Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acc4ef2eed |
@@ -1494,11 +1494,12 @@ wrote last, the record says which build's shape it is in.
|
|||||||
Each write reads the record, changes it and writes it back, holding a lock from
|
Each write reads the record, changes it and writes it back, holding a lock from
|
||||||
the read to the write: the browser's own (`navigator.locks`), taken through
|
the read to the write: the browser's own (`navigator.locks`), taken through
|
||||||
`withLock()` in `src/shared/browserApi.js`. Every page of the extension and its
|
`withLock()` in `src/shared/browserApi.js`. Every page of the extension and its
|
||||||
background share it, except a page in a Firefox private window, which Firefox
|
background share it. Firefox gives a page in a private window locks of its own.
|
||||||
gives locks of its own (see https://git.eeqj.de/sneak/AutistMask/issues/646).
|
`manifest/firefox.json` sets `"incognito": "not_allowed"`, so such a page cannot
|
||||||
The popup's `saveState()`, the background's `updateState()` and the
|
read or write extension storage. In Chrome, a page opened from an incognito
|
||||||
StateRecovery reset all take it, so among the pages and the background that
|
window still runs outside incognito and shares the lock. The popup's
|
||||||
share it, no write lands between another write's read and its write, where it
|
`saveState()`, the background's `updateState()` and the StateRecovery reset all
|
||||||
|
take it, so no write lands between another write's read and its write, where it
|
||||||
would be lost. Chrome has it from version 69, older than any Chrome that runs a
|
would be lost. Chrome has it from version 69, older than any Chrome that runs a
|
||||||
Manifest V3 extension, in pages and in the service worker; Firefox from version
|
Manifest V3 extension, in pages and in the service worker; Firefox from version
|
||||||
96, older than the 115 `manifest/firefox.json` declares. Where it is missing, a
|
96, older than the 115 `manifest/firefox.json` declares. Where it is missing, a
|
||||||
|
|||||||
@@ -208,6 +208,8 @@ password.
|
|||||||
3. Click "Load Temporary Add-on".
|
3. Click "Load Temporary Add-on".
|
||||||
4. Select `dist/firefox/manifest.json`.
|
4. Select `dist/firefox/manifest.json`.
|
||||||
|
|
||||||
|
AutistMask does not work in Firefox private windows.
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
### Creating a New Wallet
|
### Creating a New Wallet
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "Minimal Ethereum wallet for Firefox",
|
"description": "Minimal Ethereum wallet for Firefox",
|
||||||
"permissions": ["storage", "activeTab", "alarms", "<all_urls>"],
|
"permissions": ["storage", "activeTab", "alarms", "<all_urls>"],
|
||||||
|
"incognito": "not_allowed",
|
||||||
"content_security_policy": "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; object-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self' https: http:; frame-src 'none'; form-action 'none'; base-uri 'none'",
|
"content_security_policy": "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; object-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self' https: http:; frame-src 'none'; form-action 'none'; base-uri 'none'",
|
||||||
"icons": {
|
"icons": {
|
||||||
"16": "icons/icon16.png",
|
"16": "icons/icon16.png",
|
||||||
|
|||||||
@@ -18,11 +18,10 @@
|
|||||||
// updateState(fn) read fresh, apply fn to that fresh record, write it
|
// updateState(fn) read fresh, apply fn to that fresh record, write it
|
||||||
// back — all holding the lock every write of the stored
|
// back — all holding the lock every write of the stored
|
||||||
// record takes, so no other write lands in between, by
|
// record takes, so no other write lands in between, by
|
||||||
// the background or by any page except a page in a
|
// the background or by any page (README, "Stored state
|
||||||
// Firefox private window (README, "Stored state and its
|
// and its version"). The read is one storage round trip
|
||||||
// version"). The read is one storage round trip ahead of
|
// ahead of the write rather than a page lifetime ahead of
|
||||||
// the write rather than a page lifetime ahead of it
|
// it (which is what made the popup's saveState() need a
|
||||||
// (which is what made the popup's saveState() need a
|
|
||||||
// per-field merge against a baseline at all).
|
// per-field merge against a baseline at all).
|
||||||
//
|
//
|
||||||
// A handler that must both read and write therefore does its network work
|
// A handler that must both read and write therefore does its network work
|
||||||
@@ -73,8 +72,7 @@ async function updateStateOnce(mutate) {
|
|||||||
// src/shared/browserApi.js) is held from the read to the write: two
|
// src/shared/browserApi.js) is held from the read to the write: two
|
||||||
// background turns, or a turn and a page's save, never interleave
|
// background turns, or a turn and a page's save, never interleave
|
||||||
// (https://git.eeqj.de/sneak/AutistMask/issues/641). Every page's save waits
|
// (https://git.eeqj.de/sneak/AutistMask/issues/641). Every page's save waits
|
||||||
// while it is held, except a page in a Firefox private window, which Firefox
|
// while it is held (README, "Stored state and its version").
|
||||||
// gives locks of its own (README, "Stored state and its version").
|
|
||||||
//
|
//
|
||||||
// `mutate` must also not call updateState() itself, directly or through
|
// `mutate` must also not call updateState() itself, directly or through
|
||||||
// anything it awaits: the inner turn waits for the lock the outer one holds,
|
// anything it awaits: the inner turn waits for the lock the outer one holds,
|
||||||
|
|||||||
@@ -143,8 +143,6 @@ async function resetProfile() {
|
|||||||
try {
|
try {
|
||||||
// Under the lock every write of the stored record takes, so the erase
|
// Under the lock every write of the stored record takes, so the erase
|
||||||
// cannot land between another write's read and its write and be undone.
|
// cannot land between another write's read and its write and be undone.
|
||||||
// A page in a Firefox private window is the exception: Firefox gives
|
|
||||||
// it locks of its own (README, "Stored state and its version").
|
|
||||||
await withLock("autistmask", () => storageRemove("autistmask"));
|
await withLock("autistmask", () => storageRemove("autistmask"));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.errorf("state recovery: reset failed:", e);
|
log.errorf("state recovery: reset failed:", e);
|
||||||
|
|||||||
+1
-3
@@ -581,9 +581,7 @@ function reportSaveFailure(err) {
|
|||||||
// Each save runs start to finish holding the lock every write of the stored
|
// Each save runs start to finish holding the lock every write of the stored
|
||||||
// record takes (withLock() in src/shared/browserApi.js), so no write by
|
// record takes (withLock() in src/shared/browserApi.js), so no write by
|
||||||
// another page or the background lands between its read and its write and is
|
// another page or the background lands between its read and its write and is
|
||||||
// lost (https://git.eeqj.de/sneak/AutistMask/issues/641). A page in a Firefox
|
// lost (https://git.eeqj.de/sneak/AutistMask/issues/641).
|
||||||
// private window is the exception: Firefox gives it locks of its own (README,
|
|
||||||
// "Stored state and its version").
|
|
||||||
//
|
//
|
||||||
// The same lock orders this page's own saves. showView() calls saveState() on
|
// The same lock orders this page's own saves. showView() calls saveState() on
|
||||||
// every navigation without awaiting it, so two saves from one page can be
|
// every navigation without awaiting it, so two saves from one page can be
|
||||||
|
|||||||
@@ -165,6 +165,15 @@ describe("declared icons", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Firefox gives a page in a private window locks of its own, so that page's
|
||||||
|
// save could land between the background's read and its write (README,
|
||||||
|
// "Stored state and its version").
|
||||||
|
describe("private windows", () => {
|
||||||
|
test("a page in a firefox private window cannot read or write extension storage", () => {
|
||||||
|
expect(readManifest("firefox").incognito).toBe("not_allowed");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("shipped Content Security Policy", () => {
|
describe("shipped Content Security Policy", () => {
|
||||||
// MV3 takes an object and applies extension_pages to the popup and the
|
// MV3 takes an object and applies extension_pages to the popup and the
|
||||||
// background service worker, which is where libsodium runs.
|
// background service worker, which is where libsodium runs.
|
||||||
|
|||||||
@@ -6,9 +6,8 @@
|
|||||||
// now holds a lock from its read to its write.
|
// now holds a lock from its read to its write.
|
||||||
//
|
//
|
||||||
// The popup and the background run here as the real modules over one shared
|
// The popup and the background run here as the real modules over one shared
|
||||||
// storage stub, and share the lock the way the background and a page outside
|
// storage stub, and share the lock the way the background and its pages share
|
||||||
// a Firefox private window share the browser's
|
// the browser's (tests/support/navigatorLocks.js).
|
||||||
// (tests/support/navigatorLocks.js).
|
|
||||||
|
|
||||||
const { makeStorageStub } = require("./support/storageStub");
|
const { makeStorageStub } = require("./support/storageStub");
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,8 @@
|
|||||||
// the callback settles, whether it resolved or rejected. That is what the
|
// the callback settles, whether it resolved or rejected. That is what the
|
||||||
// browser's own does for the one call the extension makes,
|
// browser's own does for the one call the extension makes,
|
||||||
// `navigator.locks.request(name, callback)`. Every module a test file loads
|
// `navigator.locks.request(name, callback)`. Every module a test file loads
|
||||||
// shares it, as the extension's background and its pages share the browser's,
|
// shares it, as the extension's background and its pages share the browser's
|
||||||
// except a page in a Firefox private window (README, "Stored state and its
|
// (README, "Stored state and its version").
|
||||||
// version").
|
|
||||||
//
|
//
|
||||||
// Run before each test file by `setupFiles` in package.json.
|
// Run before each test file by `setupFiles` in package.json.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user