Skip to content

Commit

Permalink
Refactor BaseStore localStorage usage to fallback to an offscreen loc…
Browse files Browse the repository at this point in the history
…alStorage solution if global.localStorage does not exists
  • Loading branch information
danjm committed Nov 22, 2024
1 parent df4a0a4 commit 6d84de2
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 9 deletions.
19 changes: 10 additions & 9 deletions app/scripts/lib/Stores/BaseStore.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type Migrator from '../migrator';
import firstTimeState from '../../first-time-state';
import { generateWalletState } from '../../fixtures/generate-wallet-state';
import localStorageWithOffscreen from './localStorageWithOffscreen';

/**
* This type is a temporary type that is used to represent the state tree of
Expand Down Expand Up @@ -162,7 +163,7 @@ export abstract class BaseStore {
* does not require instantiation of the class to set that value.
*/
static optIntoRestoreOnRestart() {
global.localStorage.setItem('USER_OPTED_IN_TO_RESTORE', 'true');
localStorageWithOffscreen.setItem('USER_OPTED_IN_TO_RESTORE', 'true');
}

/**
Expand All @@ -171,7 +172,7 @@ export abstract class BaseStore {
* default state if the vault was not backed up.
*/
get hasUserOptedIntoRestart() {
return global.localStorage.getItem('USER_OPTED_IN_TO_RESTORE') === 'true';
return localStorageWithOffscreen.getItem('USER_OPTED_IN_TO_RESTORE') === 'true';
}

/**
Expand All @@ -182,9 +183,9 @@ export abstract class BaseStore {
*/
set hasUserOptedIntoRestart(value: boolean) {
if (value === true) {
global.localStorage.setItem('USER_OPTED_IN_TO_RESTORE', 'true');
localStorageWithOffscreen.setItem('USER_OPTED_IN_TO_RESTORE', 'true');
} else {
global.localStorage.removeItem('USER_OPTED_IN_TO_RESTORE');
localStorageWithOffscreen.removeItem('USER_OPTED_IN_TO_RESTORE');
}
}

Expand All @@ -194,12 +195,12 @@ export abstract class BaseStore {
* This is useful in detecting
*/
set lastGoodMigrationVersion(version: number) {
global.localStorage.setItem('lastGoodMigrationVersion', version.toString());
localStorageWithOffscreen.setItem('lastGoodMigrationVersion', version.toString());
}

get lastGoodMigrationVersion() {
return parseInt(
global.localStorage.getItem('lastGoodMigrationVersion') ?? '0',
localStorageWithOffscreen.getItem('lastGoodMigrationVersion') ?? '0',
10,
);
}
Expand All @@ -217,7 +218,7 @@ export abstract class BaseStore {
* current one.
*/
doesMigrationNumberHaveMismatch(currentMigrationNumber: number) {
if (global.localStorage.getItem('lastGoodMigrationVersion') === null) {
if (localStorageWithOffscreen.getItem('lastGoodMigrationVersion') === null) {
return false;
}
return this.lastGoodMigrationVersion !== currentMigrationNumber;
Expand All @@ -230,7 +231,7 @@ export abstract class BaseStore {
* corruption has occurred.
*/
recordStateExistence() {
global.localStorage.setItem('MMStateExisted', Date.now().toString());
localStorageWithOffscreen.setItem('MMStateExisted', Date.now().toString());
}

/**
Expand All @@ -240,7 +241,7 @@ export abstract class BaseStore {
* determining if corruption has occurred.
*/
get hasStateExisted() {
return global.localStorage.getItem('MMStateExisted') !== null;
return localStorageWithOffscreen.getItem('MMStateExisted') !== null;
}

/**
Expand Down
63 changes: 63 additions & 0 deletions app/scripts/lib/Stores/localStorageWithOffscreen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import browser from 'webextension-polyfill';
import { awaitOffscreenDocumentCreation } from '../../offscreen';
import {
OffscreenCommunicationTarget,
} from '../../../../shared/constants/offscreen-communication';

const offscreenSupported = browser.offscreen;

const browserRuntimeSendMessageToAwait = async (params) => {
await awaitOffscreenDocumentCreation();
return new Promise((resolve, reject) => {
browser.runtime.sendMessage(
params,
(response) => {
if (response.error) {
reject(response.error);
}

resolve(response.value);
}
);
})
}

const localStorageWithOffscreen = {
getItem: (key) => {
if (offscreenSupported) {
await awaitOffscreenDocumentCreation();
return await browserRuntimeSendMessageToAwait({
target: OffscreenCommunicationTarget.localStorageOffscreen,
action: 'getItem',
key,
});
} else {
return window.localStorage.getItem(key);
}
},
setItem: (key, value) => {
if (offscreenSupported) {
await awaitOffscreenDocumentCreation();
return await browserRuntimeSendMessageToAwait({
target: OffscreenCommunicationTarget.localStorageOffscreen,
action: 'setItem',
key,
value,
});
} else {
return window.localStorage.setItem(key, value);
}
},
removeItem: (key) => {
if (offscreenSupported) {
await awaitOffscreenDocumentCreation();
return await browserRuntimeSendMessageToAwait({
target: OffscreenCommunicationTarget.localStorageOffscreen,
action: 'removeItem',
key,
});
} else {
return window.localStorage.removeItem(key);
}
},
};
10 changes: 10 additions & 0 deletions app/scripts/offscreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ async function hasOffscreenDocument() {
return matchedClients.some((client) => client.url === url);
}

export async function awaitOffscreenDocumentCreation () {
const offscreenExists = await hasOffscreenDocument();
if (offscreenExists) {
return;
} else if () {
await new Promise((resolve) => setTimeout(resolve, 10));
return await awaitOffscreenDocumentCreation();
}
}

/**
* Creates an offscreen document that can be used to load additional scripts
* and iframes that can communicate with the extension through the chrome
Expand Down
40 changes: 40 additions & 0 deletions offscreen/scripts/localStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {
OffscreenCommunicationEvents,
OffscreenCommunicationTarget,
} from '../../shared/constants/offscreen-communication';
import { isObject } from '@metamask/utils';

export function setupLocalStorageMessageListeners() {
chrome.runtime.onMessage.addListener(
(
msg: {
target: string;
action: string;
key: string;
value: string;
},
_sender,
sendResponse,
) => {
if (
message &&
isObject(message) &&
message.action &&
message.key &&
message.target === OffscreenCommunicationTarget.localStorageOffscreen
) {
if (message.action === 'getItem') {
const storedValue = window.localStorage.getItem(key);
chrome.runtime.sendMessage({
target: OffscreenCommunicationTarget.extensionLocalStorage,
value: storedValue,
});
} else if (message.action === 'setItem') {
window.localStorage.getItem(key, value);
} else if (message.action === 'removeItem') {
window.localStorage.removeItem(key);
}
}
},
);
}
2 changes: 2 additions & 0 deletions offscreen/scripts/offscreen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import initLedger from './ledger';
import initTrezor from './trezor';
import initLattice from './lattice';
import setupLocalStorageMessageListeners from './localStorage';

/**
* Initialize a post message stream with the parent window that is initialized
Expand All @@ -34,6 +35,7 @@ async function init(): Promise<void> {
initializePostMessageStream();
initTrezor();
initLattice();
setupLocalStorageMessageListeners();

try {
const ledgerInitTimeout = new Promise((_, reject) => {
Expand Down
2 changes: 2 additions & 0 deletions shared/constants/offscreen-communication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export enum OffscreenCommunicationTarget {
ledgerOffscreen = 'ledger-offscreen',
latticeOffscreen = 'lattice-offscreen',
extension = 'extension-offscreen',
localStorageOffScreen = 'local-storage-offscreen',
extensionLocalStorage = 'extension-local-storage',
extensionMain = 'extension',
}

Expand Down

0 comments on commit 6d84de2

Please sign in to comment.