-
Notifications
You must be signed in to change notification settings - Fork 5.1k
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
Rename provider streams, notify provider of stream failures #10006
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,16 +16,13 @@ const inpageContent = fs.readFileSync( | |
const inpageSuffix = `//# sourceURL=${extension.runtime.getURL('inpage.js')}\n` | ||
const inpageBundle = inpageContent + inpageSuffix | ||
|
||
// Eventually this streaming injection could be replaced with: | ||
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Language_Bindings/Components.utils.exportFunction | ||
// | ||
// But for now that is only Firefox | ||
// If we create a FireFox-only code path using that API, | ||
// MetaMask will be much faster loading and performant on Firefox. | ||
const CONTENT_SCRIPT = 'metamask-contentscript' | ||
const INPAGE = 'metamask-inpage' | ||
const PROVIDER = 'metamask-provider' | ||
|
||
if (shouldInjectProvider()) { | ||
injectScript(inpageBundle) | ||
start() | ||
setupStreams() | ||
} | ||
|
||
/** | ||
|
@@ -46,15 +43,6 @@ function injectScript(content) { | |
} | ||
} | ||
|
||
/** | ||
* Sets up the stream communication and submits site metadata | ||
* | ||
*/ | ||
async function start() { | ||
await setupStreams() | ||
await domIsReady() | ||
} | ||
|
||
/** | ||
* Sets up two-way communication streams between the | ||
* browser extension and local per-page browser context. | ||
|
@@ -63,10 +51,10 @@ async function start() { | |
async function setupStreams() { | ||
// the transport-specific streams for communication between inpage and background | ||
const pageStream = new LocalMessageDuplexStream({ | ||
name: 'contentscript', | ||
target: 'inpage', | ||
name: CONTENT_SCRIPT, | ||
target: INPAGE, | ||
}) | ||
const extensionPort = extension.runtime.connect({ name: 'contentscript' }) | ||
const extensionPort = extension.runtime.connect({ name: CONTENT_SCRIPT }) | ||
const extensionStream = new PortStream(extensionPort) | ||
|
||
// create and connect channel muxers | ||
|
@@ -79,25 +67,26 @@ async function setupStreams() { | |
pump(pageMux, pageStream, pageMux, (err) => | ||
logStreamDisconnectWarning('MetaMask Inpage Multiplex', err), | ||
) | ||
pump(extensionMux, extensionStream, extensionMux, (err) => | ||
logStreamDisconnectWarning('MetaMask Background Multiplex', err), | ||
) | ||
pump(extensionMux, extensionStream, extensionMux, (err) => { | ||
logStreamDisconnectWarning('MetaMask Background Multiplex', err) | ||
notifyInpageOfStreamFailure() | ||
}) | ||
|
||
// forward communication across inpage-background for these channels only | ||
forwardTrafficBetweenMuxers('provider', pageMux, extensionMux) | ||
forwardTrafficBetweenMuxes(PROVIDER, pageMux, extensionMux) | ||
|
||
// connect "phishing" channel to warning system | ||
const phishingStream = extensionMux.createStream('phishing') | ||
phishingStream.once('data', redirectToPhishingWarning) | ||
} | ||
|
||
function forwardTrafficBetweenMuxers(channelName, muxA, muxB) { | ||
function forwardTrafficBetweenMuxes(channelName, muxA, muxB) { | ||
const channelA = muxA.createStream(channelName) | ||
const channelB = muxB.createStream(channelName) | ||
pump(channelA, channelB, channelA, (err) => | ||
logStreamDisconnectWarning( | ||
pump(channelA, channelB, channelA, (error) => | ||
console.debug( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Q: Was this change made simply to clean up the log output (Not prefixing it with the connection lost message?) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah; I just decided that these were cluttering the user's console with warnings for little benefit, and so downgraded them to debugs. |
||
`MetaMask muxed traffic for channel "${channelName}" failed.`, | ||
err, | ||
error, | ||
), | ||
) | ||
} | ||
|
@@ -106,14 +95,35 @@ function forwardTrafficBetweenMuxers(channelName, muxA, muxB) { | |
* Error handler for page to extension stream disconnections | ||
* | ||
* @param {string} remoteLabel - Remote stream name | ||
* @param {Error} err - Stream connection error | ||
* @param {Error} error - Stream connection error | ||
*/ | ||
function logStreamDisconnectWarning(remoteLabel, err) { | ||
let warningMsg = `MetamaskContentscript - lost connection to ${remoteLabel}` | ||
if (err) { | ||
warningMsg += `\n${err.stack}` | ||
} | ||
console.warn(warningMsg) | ||
function logStreamDisconnectWarning(remoteLabel, error) { | ||
console.debug( | ||
`MetaMask Contentscript: Lost connection to "${remoteLabel}".`, | ||
error, | ||
) | ||
} | ||
|
||
/** | ||
* This function must ONLY be called in pump destruction/close callbacks. | ||
* Notifies the inpage context that streams have failed, via window.postMessage. | ||
* Relies on obj-multiplex and post-message-stream implementation details. | ||
*/ | ||
function notifyInpageOfStreamFailure() { | ||
window.postMessage( | ||
{ | ||
target: INPAGE, // the post-message-stream "target" | ||
data: { | ||
// this object gets passed to obj-multiplex | ||
name: PROVIDER, // the obj-multiplex channel name | ||
data: { | ||
jsonrpc: '2.0', | ||
method: 'METAMASK_STREAM_FAILURE', | ||
}, | ||
}, | ||
}, | ||
window.location.origin, | ||
) | ||
Comment on lines
+112
to
+126
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We use |
||
} | ||
|
||
/** | ||
|
@@ -220,17 +230,3 @@ function redirectToPhishingWarning() { | |
href: window.location.href, | ||
})}` | ||
} | ||
|
||
/** | ||
* Returns a promise that resolves when the DOM is loaded (does not wait for images to load) | ||
*/ | ||
async function domIsReady() { | ||
// already loaded | ||
if (['interactive', 'complete'].includes(document.readyState)) { | ||
return undefined | ||
} | ||
// wait for load | ||
return new Promise((resolve) => | ||
window.addEventListener('DOMContentLoaded', resolve, { once: true }), | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we no longer need to know that the dom is ready?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we needed it in the past because we pulled metadata in this script instead of in the inpage provider. Now, we were just calling
start
, which first set up the streams, and then waited for the DOM to be ready to do, nothing.