Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MV3: Update service worker restart logic and keep-alive logic for dapp support #16075

Merged
merged 76 commits into from
Nov 14, 2022
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
ea21786
dapp: add debug statements
digiwand Oct 3, 2022
e860244
dapp: add retry logic [debug]
digiwand Oct 3, 2022
a8f86ad
dapp: keep SW alive on rpc request
digiwand Oct 4, 2022
9d2da57
Revert "dapp: add debug statements"
digiwand Oct 4, 2022
0eb8501
dapp: try to set up ext streams asap on reset
digiwand Oct 4, 2022
169722c
Merge branch 'develop' into mv3-dapp-sendMessage-after-SW-inactivity
digiwand Oct 4, 2022
dd2146e
dapp: apply keep alive logic to phishingPageStream
digiwand Oct 4, 2022
f60ba5f
dapp:put keep-alive logic behind isManifestV3 flag
digiwand Oct 4, 2022
d09dfca
Re-activate streams after a period of service worker in-activity
jpuri Oct 5, 2022
9e910b5
Merge branch 'mv3-dapp-sendMessage-after-SW-inactivity' of github.com…
digiwand Oct 5, 2022
09396a8
Merge branch 'mv3-dapp-sendMessage-after-SW-inactivity' of github.com…
digiwand Oct 5, 2022
69caf1a
dapp: rm extra function
digiwand Oct 5, 2022
e5ef11b
dapp: update phishing onDisconnect
digiwand Oct 5, 2022
c00fb30
dapp: fix eslint missing global chrome
digiwand Oct 5, 2022
c6f042e
Merge branch 'develop' into mv3-dapp-sendMessage-after-SW-inactivity
digiwand Oct 5, 2022
b9fdffa
add EXTENSION_MESSAGES const
digiwand Oct 7, 2022
eff5234
use EXTENSION_MESSAGES more generic comment
digiwand Oct 7, 2022
8efb02c
Merge branch 'develop' into mv3-dapp-sendMessage-after-SW-inactivity
jpuri Oct 11, 2022
073f05e
update comment
digiwand Oct 12, 2022
ae3c2df
dapp: clean timeout and interval
digiwand Oct 12, 2022
ea21f94
Merge branch 'develop' into mv3-dapp-sendMessage-after-SW-inactivity
jpuri Oct 12, 2022
db5a926
Fix DAPP action replay
jpuri Oct 12, 2022
da6aa11
execute DAPP action replay for only MV3
jpuri Oct 14, 2022
0c86b83
Merge branch 'develop' into mv3-dapp-sendMessage-after-SW-inactivity
jpuri Oct 14, 2022
3c52f65
fix
jpuri Oct 14, 2022
665b934
Merge branch 'mv3-dapp-sendMessage-after-SW-inactivity' of github.com…
jpuri Oct 14, 2022
c10bded
fix
jpuri Oct 14, 2022
9f9aad3
fix
jpuri Oct 14, 2022
00ed35f
comment out DAPP action replay code
jpuri Oct 14, 2022
acefe68
fix
jpuri Oct 21, 2022
dcd4284
fix
jpuri Oct 21, 2022
1ccb9d7
fix
jpuri Oct 21, 2022
2ab6234
scripts/background: use browser polyfill
digiwand Oct 25, 2022
3b66c2c
Revert "scripts/background: use browser polyfill"
digiwand Oct 25, 2022
0047146
scripts/background: use browser polyfill
digiwand Oct 25, 2022
bbd5cff
script/background: check lastError
digiwand Oct 25, 2022
8074e59
dapp: use EXTENSION_MESSAGES
digiwand Oct 25, 2022
b0dc2a8
scripts/background: send ready msg to all tabs
digiwand Oct 26, 2022
66107a9
dapp: update onMessage handler comment and name
digiwand Oct 26, 2022
4b68873
dapp: return values onMessage listener
digiwand Oct 26, 2022
32aa4ef
dapp: mv onMessage listener
digiwand Oct 26, 2022
3bf4dfb
dapp: add onMessage setupPhishingExtStreams
digiwand Oct 26, 2022
4bc6cfb
dapp: rn reset -> destroy streams
digiwand Oct 26, 2022
a8b6921
dapp: rn reset -> destroy for phishing streams
digiwand Oct 26, 2022
38128d0
dapp: clean comment
digiwand Oct 26, 2022
cb87438
Merge branch 'develop' into mv3-dapp-sendMessage-after-SW-inactivity
digiwand Oct 28, 2022
6b334a7
Merge branch 'develop' into mv3-dapp-sendMessage-after-SW-inactivity
digiwand Oct 28, 2022
924235b
Merge branch 'develop' into mv3-dapp-sendMessage-after-SW-inactivity
digiwand Nov 1, 2022
61ed554
Merge branch 'develop' into mv3-dapp-sendMessage-after-SW-inactivity
legobeat Nov 2, 2022
1b9e298
dapp: rm unused comments
digiwand Nov 2, 2022
bb5ba6b
dapp: onMessage return Promise|undefined
digiwand Nov 3, 2022
0f9ff27
dapp:clean: add missing undefined return type
digiwand Nov 3, 2022
de862d4
dapp: use new checkForErrorAndLog for Chrome API
digiwand Nov 3, 2022
3fd3ba3
dapp:fix: return tabs.query result
digiwand Nov 3, 2022
3d9007b
dapp:eslint: return undefined
digiwand Nov 3, 2022
d1724c7
background: do not query tabs w/out url
digiwand Nov 3, 2022
5461986
background: rm Could not establish... catch
digiwand Nov 3, 2022
b45a920
dapp:clean: rm unused checkForError... for now...
digiwand Nov 3, 2022
de1e441
dapp: prevent setupExtensionStreams called twice
digiwand Nov 4, 2022
931f511
dapp: handle onDisconnect lastError
digiwand Nov 4, 2022
b07242a
Merge branch 'develop' into mv3-dapp-sendMessage-after-SW-inactivity
digiwand Nov 4, 2022
a568420
background: update tabs.query url comment
digiwand Nov 7, 2022
f410124
background: update tabs.query url comment 2
digiwand Nov 7, 2022
bba4dba
Merge branch 'mv3-dapp-sendMessage-after-SW-inactivity' of github.com…
digiwand Nov 7, 2022
e676c71
dapp: fix SW restart for multi dapp support
digiwand Nov 9, 2022
a9aa850
dapp:clean: rm extra "." from console.warn
digiwand Nov 9, 2022
ac1c32a
Merge branch 'develop' into mv3-dapp-sendMessage-after-SW-inactivity
digiwand Nov 9, 2022
b0e5ae0
clean: comments for dapp and background
digiwand Nov 9, 2022
ba3ed91
Merge branch 'mv3-dapp-sendMessage-after-SW-inactivity' of github.com…
digiwand Nov 9, 2022
206837b
Adding catch block (#16454)
jpuri Nov 10, 2022
d979dcf
Merge branch 'develop' into mv3-dapp-sendMessage-after-SW-inactivity
digiwand Nov 10, 2022
858f5d4
fix: FireFox provider injection
digiwand Nov 11, 2022
67c5533
lib/util: fix invalid checkForErrorAndWarn export
digiwand Nov 11, 2022
76bc0f6
bg: add explanation for tabs.sendMessage catch
digiwand Nov 11, 2022
58db96f
dapp: add browser-runtime.utils
digiwand Nov 11, 2022
db28cf8
runtime.utils: add checkForLastErrorAndLog
digiwand Nov 11, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions app/scripts/background.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* global chrome */

/**
* @file The entry point for the web extension singleton process.
*/
Expand Down Expand Up @@ -102,6 +104,15 @@ const initApp = async (remotePort) => {

if (isManifestV3) {
browser.runtime.onConnect.addListener(initApp);
// Message below signals content script in DAPPS to connect to metamask background as backend is not active
// It is required to re-connect DAPPS after service worker re-activation
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
chrome.tabs.sendMessage(
tabs[0].id,
{ name: 'METAMASK_EXTENSION_READY' },
() => undefined,
);
});
} else {
// initialization flow
initialize().catch(log.error);
Expand Down
116 changes: 90 additions & 26 deletions app/scripts/contentscript.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import browser from 'webextension-polyfill';
import PortStream from 'extension-port-stream';
import { obj as createThoughStream } from 'through2';

import { MESSAGE_TYPE } from '../../shared/constants/app';
import { isManifestV3 } from '../../shared/modules/mv3.utils';
import shouldInjectProvider from '../../shared/modules/provider-injection';

Expand Down Expand Up @@ -44,9 +45,6 @@ let legacyExtMux,
legacyPagePublicConfigChannel,
notificationTransformStream;

const WORKER_KEEP_ALIVE_INTERVAL = 1000;
const WORKER_KEEP_ALIVE_MESSAGE = 'WORKER_KEEP_ALIVE_MESSAGE';

const phishingPageUrl = new URL(process.env.PHISHING_WARNING_PAGE_URL);

let phishingExtChannel,
Expand Down Expand Up @@ -82,6 +80,52 @@ function injectScript(content) {
}
}

/**
* SERVICE WORKER LOGIC
*/

const WORKER_KEEP_ALIVE_INTERVAL = 1000;
const WORKER_KEEP_ALIVE_MESSAGE = 'WORKER_KEEP_ALIVE_MESSAGE';
const TIME_45_MIN_IN_MS = 45 * 60 * 1000;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should keep service worker alive even if account is not connected on DAPP I think. There are some queries that user can make even in disconnected state.

Copy link
Contributor

@legobeat legobeat Nov 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edit: Addressed in linked thread

Slightly similar point to #16075 (comment), something to keep in mind when settling the above:

If the user is interacting with MetaMask on a dapp on one page, this should not result in events/change of behavior readable from an unauthorized (disconnected) tab/page as this would both leak entropy facilitating fingerprinting as well as help in detecting user activity (e.g distinguishing from "visiting browser has MetaMask installed" vs "visiting user has opened MetaMask during this session".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jpuri

There are some queries that user can make even in a disconnected state.
I'm not sure I know what queries you mean here. Is this something you can elaborate on?

The keep-alive logic starts for any dapp interaction, whether or not accounts are connected. On initial load, we only trigger the keep-alive logic if the dapp connects or if the dapp sends requests. Initially, I don't think there is a way to distinguish a dapp from a non-dapp page aside from connected accounts... unless I'm missing something. I actually prefer to avoid running the keep-alive logic on non-dapps or when the provider is not in use on a page to save CPU and memory on the browser.

@legobeat getting to these comments after I run some more tests sending messages to/from our content script and extension

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@legobeat I added a reply here #16075 (comment)

/**
* Don't run the keep worker alive logic for initial JSON RPC methods.
* We should not run keep-alive logic for non-dapp pages.
*/
const IGNORE_INIT_METHODS_FOR_KEEP_ALIVE = [
MESSAGE_TYPE.GET_PROVIDER_STATE,
MESSAGE_TYPE.SEND_METADATA,
];

let keepAliveInterval;
let keepAliveTimer;

/**
* Running this method will ensure the service worker is kept alive for 45 minutes
* First message is sent immediately, subsequent messages are sent at interval of WORKER_KEEP_ALIVE_INTERVAL
*/
const runWorkerKeepAliveInterval = () => {
if (keepAliveTimer) {
clearTimeout(keepAliveTimer);
}

keepAliveTimer = setTimeout(() => {
clearInterval(keepAliveInterval);
}, TIME_45_MIN_IN_MS);

if (keepAliveInterval) {
clearInterval(keepAliveInterval);
}

browser.runtime.sendMessage({ name: WORKER_KEEP_ALIVE_MESSAGE });

keepAliveInterval = setInterval(() => {
if (browser.runtime.id) {
browser.runtime.sendMessage({ name: WORKER_KEEP_ALIVE_MESSAGE });
}
}, WORKER_KEEP_ALIVE_INTERVAL);
};

/**
* PHISHING STREAM LOGIC
*/
Expand All @@ -93,6 +137,14 @@ function setupPhishingPageStreams() {
target: PHISHING_WARNING_PAGE,
});

if (isManifestV3) {
phishingPageStream.on('data', ({ data: { method } }) => {
if (!IGNORE_INIT_METHODS_FOR_KEEP_ALIVE.includes(method)) {
runWorkerKeepAliveInterval();
}
});
}

// create and connect channel muxers
// so we can handle the channels individually
phishingPageMux = new ObjectMultiplex();
Expand Down Expand Up @@ -142,6 +194,9 @@ const setupPhishingExtStreams = () => {
error,
),
);

// eslint-disable-next-line no-use-before-define
phishingExtPort.onDisconnect.addListener(resetPhishingStreamAndListeners);
};

/** Destroys all of the phishing extension streams */
Expand All @@ -164,8 +219,6 @@ const resetPhishingStreamAndListeners = () => {

destroyPhishingExtStreams();
setupPhishingExtStreams();

phishingExtPort.onDisconnect.addListener(resetPhishingStreamAndListeners);
};

/**
Expand All @@ -176,8 +229,6 @@ const resetPhishingStreamAndListeners = () => {
const initPhishingStreams = () => {
setupPhishingPageStreams();
setupPhishingExtStreams();

phishingExtPort.onDisconnect.addListener(resetPhishingStreamAndListeners);
};

/**
Expand All @@ -191,6 +242,14 @@ const setupPageStreams = () => {
target: INPAGE,
});

if (isManifestV3) {
pageStream.on('data', ({ data: { method } }) => {
if (!IGNORE_INIT_METHODS_FOR_KEEP_ALIVE.includes(method)) {
runWorkerKeepAliveInterval();
}
});
}

// create and connect channel muxers
// so we can handle the channels individually
pageMux = new ObjectMultiplex();
Expand Down Expand Up @@ -231,6 +290,8 @@ const setupExtensionStreams = () => {
extensionPhishingStream = extensionMux.createStream('phishing');
extensionPhishingStream.once('data', redirectToPhishingWarning);

// eslint-disable-next-line no-use-before-define
extensionPort.onDisconnect.addListener(resetStreamAndListeners);
notifyInpageOfExtensionStreamConnect();
};

Expand All @@ -247,6 +308,7 @@ const destroyExtensionStreams = () => {

/**
* LEGACY STREAM LOGIC
* TODO:LegacyProvider: Delete
*/

// TODO:LegacyProvider: Delete
Expand All @@ -256,6 +318,14 @@ const setupLegacyPageStreams = () => {
target: LEGACY_INPAGE,
});

if (isManifestV3) {
legacyPageStream.on('data', ({ data: { method } }) => {
if (!IGNORE_INIT_METHODS_FOR_KEEP_ALIVE.includes(method)) {
runWorkerKeepAliveInterval();
}
});
}

legacyPageMux = new ObjectMultiplex();
legacyPageMux.setMaxListeners(25);

Expand Down Expand Up @@ -330,6 +400,16 @@ const destroyLegacyExtensionStreams = () => {
legacyExtPublicConfigChannel.destroy();
};

// When extension background is loaded it sends message 'METAMASK_EXTENSION_READY' to browser tabs
// Function below helps to setup streams after service worker in-activity
const activateStreams = (msg) => {
if (msg.name === 'METAMASK_EXTENSION_READY') {
setupExtensionStreams();
setupLegacyExtensionStreams();
}
};
browser.runtime.onMessage.addListener(activateStreams);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @digiwand : I know it is complex, but could you test phishing stream and legacy stream changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @seaona for taking this head on! As discussed earlier, we will handle the support for the phishing stream logic in a separate PR (see #15987)

@seaona has found that terminating the service worker breaks the phishing warning page logic

/**
* Resets the extension stream with new streams to channel with the in page streams,
* and creates a new event listener to the reestablished extension port.
Expand All @@ -338,12 +418,7 @@ const resetStreamAndListeners = () => {
extensionPort.onDisconnect.removeListener(resetStreamAndListeners);

destroyExtensionStreams();
setupExtensionStreams();

destroyLegacyExtensionStreams();
setupLegacyExtensionStreams();

extensionPort.onDisconnect.addListener(resetStreamAndListeners);
};

/**
Expand All @@ -353,13 +428,10 @@ const resetStreamAndListeners = () => {
*/
const initStreams = () => {
setupPageStreams();
setupExtensionStreams();

// TODO:LegacyProvider: Delete
setupLegacyPageStreams();
setupLegacyExtensionStreams();

extensionPort.onDisconnect.addListener(resetStreamAndListeners);
setupExtensionStreams();
setupLegacyExtensionStreams();
};

// TODO:LegacyProvider: Delete
Expand Down Expand Up @@ -446,12 +518,6 @@ function redirectToPhishingWarning(data = {}) {
window.location.href = `${baseUrl}#${querystring}`;
}

const initKeepWorkerAlive = () => {
setInterval(() => {
browser.runtime.sendMessage({ name: WORKER_KEEP_ALIVE_MESSAGE });
}, WORKER_KEEP_ALIVE_INTERVAL);
};

const start = () => {
const isDetectedPhishingSite =
window.location.origin === phishingPageUrl.origin &&
Expand All @@ -463,9 +529,7 @@ const start = () => {
}

if (shouldInjectProvider()) {
if (isManifestV3) {
initKeepWorkerAlive();
} else {
if (!isManifestV3) {
injectScript(inpageBundle);
}
initStreams();
Expand Down