-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
…7494) ## **Description** Currently the inpage injected scripts used in the in-app browser (for making the wallet provider object available to the dapp) are currently built in a separate `@metamask/mobile-provider` package. This PR brings the build process into the mobile repo itself. This is needed in order to support passing build time variables to the provider initialization (for EIP6963 support). ## **Manual testing steps** 1. Pull in branch 2. `yarn setup` 3. `yarn watch` 4. `yarn start:ios` 5. Go to in-app browser 6. Visit test dapp 7. Verify that buttons are still functional 8. Optionally: Open safari debug console on the test dapp webview 9. check that `window.ethereum` is defined and `window.ethereum.chainId` logs a deprecation warning ## **Screenshots/Recordings** Note the deprecation warnings which indicates that provider v13.0.0 is now being used ![image](https://github.com/MetaMask/metamask-mobile/assets/918701/143a5f56-29d4-4b7c-8192-d4595689ebba) ## **Related issues** See: MetaMask/MetaMask-planning#869 ## **Pre-merge author checklist** - [ ] I’ve followed [MetaMask Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've clearly explained: - [x] What problem this PR is solving. - [x] How this problem was solved. - [x] How reviewers can test my changes. - [x] I’ve indicated what issue this PR is linked to: Fixes #??? - [ ] I’ve included tests if applicable. - [x] I’ve documented any added code. - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). - [x] I’ve properly set the pull request status: - [x] In case it's not yet "ready for review", I've set it to "draft". - [x] In case it's "ready for review", I've changed it from "draft" to "non-draft". ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: Cal Leung <cleun007@gmail.com>
- Loading branch information
Showing
16 changed files
with
740 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
/scripts/inpage-bridge | ||
/app/core/InpageBridgeWeb3.js | ||
/app/util/blockies.js | ||
__snapshots__ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#!/bin/bash | ||
set -euo pipefail | ||
|
||
rm -f app/core/InpageBridgeWeb3.js | ||
mkdir -p scripts/inpage-bridge/dist && rm -rf scripts/inpage-bridge/dist/* | ||
cd scripts/inpage-bridge/inpage | ||
../../../node_modules/.bin/webpack --config webpack.config.js | ||
cd .. | ||
node content-script/build.js | ||
cat dist/inpage-bundle.js content-script/index.js > dist/index-raw.js | ||
../../node_modules/.bin/webpack --config webpack.config.js | ||
cd ../.. | ||
cp scripts/inpage-bridge/dist/index.js app/core/InpageBridgeWeb3.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
|
||
const distPath = path.join(__dirname, '..', '/', 'dist'); | ||
|
||
const inpageContent = fs | ||
.readFileSync(path.join(distPath, 'inpage-content.js')) | ||
.toString(); | ||
|
||
// wrap the inpage content in a variable declaration | ||
const code = `const inpageBundle = ${JSON.stringify(inpageContent)}`; | ||
|
||
fs.writeFileSync(path.join(distPath, 'inpage-bundle.js'), code, 'ascii'); | ||
console.log('content-script.js generated succesfully'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
/* global inpageBundle */ | ||
|
||
if (shouldInject()) { | ||
injectScript(inpageBundle); | ||
start(); | ||
} | ||
|
||
// Functions | ||
|
||
/** | ||
* Sets up the stream communication and submits site metadata | ||
* | ||
*/ | ||
async function start() { | ||
await domIsReady(); | ||
window._metamaskSetupProvider(); | ||
} | ||
|
||
/** | ||
* Injects a script tag into the current document | ||
* | ||
* @param {string} content - Code to be executed in the current document | ||
*/ | ||
function injectScript(content) { | ||
try { | ||
const container = document.head || document.documentElement; | ||
|
||
// synchronously execute script in page context | ||
const scriptTag = document.createElement('script'); | ||
scriptTag.setAttribute('async', false); | ||
scriptTag.textContent = content; | ||
container.insertBefore(scriptTag, container.children[0]); | ||
|
||
// script executed; remove script element from DOM | ||
container.removeChild(scriptTag); | ||
} catch (err) { | ||
console.error('MetaMask script injection failed', err); | ||
} | ||
} | ||
|
||
/** | ||
* Determines if the provider should be injected. | ||
* | ||
* @returns {boolean} {@code true} if the provider should be injected. | ||
*/ | ||
function shouldInject() { | ||
return ( | ||
doctypeCheck() && | ||
suffixCheck() && | ||
documentElementCheck() && | ||
!blockedDomainCheck() | ||
); | ||
} | ||
|
||
/** | ||
* Checks the doctype of the current document if it exists | ||
* | ||
* @returns {boolean} {@code true} if the doctype is html or if none exists | ||
*/ | ||
function doctypeCheck() { | ||
const { doctype } = window.document; | ||
if (doctype) { | ||
return doctype.name === 'html'; | ||
} | ||
return true; | ||
} | ||
|
||
/** | ||
* Returns whether or not the extension (suffix) of the current document is | ||
* prohibited. | ||
* | ||
* This checks {@code window.location.pathname} against a set of file extensions | ||
* that should not have the provider injected into them. This check is indifferent | ||
* of query parameters in the location. | ||
* | ||
* @returns {boolean} whether or not the extension of the current document is prohibited | ||
*/ | ||
function suffixCheck() { | ||
const prohibitedTypes = [/\\.xml$/u, /\\.pdf$/u]; | ||
const currentUrl = window.location.pathname; | ||
for (let i = 0; i < prohibitedTypes.length; i++) { | ||
if (prohibitedTypes[i].test(currentUrl)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
/** | ||
* Checks the documentElement of the current document | ||
* | ||
* @returns {boolean} {@code true} if the documentElement is an html node or if none exists | ||
*/ | ||
function documentElementCheck() { | ||
const documentElement = document.documentElement.nodeName; | ||
if (documentElement) { | ||
return documentElement.toLowerCase() === 'html'; | ||
} | ||
return true; | ||
} | ||
|
||
/** | ||
* Checks if the current domain is blocked | ||
* | ||
* @returns {boolean} {@code true} if the current domain is blocked | ||
*/ | ||
function blockedDomainCheck() { | ||
const blockedDomains = [ | ||
'uscourts.gov', | ||
'dropbox.com', | ||
'webbyawards.com', | ||
'cdn.shopify.com/s/javascripts/tricorder/xtld-read-only-frame.html', | ||
'adyen.com', | ||
'gravityforms.com', | ||
'harbourair.com', | ||
'ani.gamer.com.tw', | ||
'blueskybooking.com', | ||
'sharefile.com', | ||
]; | ||
const currentUrl = window.location.href; | ||
let currentRegex; | ||
for (let i = 0; i < blockedDomains.length; i++) { | ||
const blockedDomain = blockedDomains[i].replace('.', '\\.'); | ||
currentRegex = new RegExp( | ||
`(?:https?:\\/\\/)(?:(?!${blockedDomain}).)*$`, | ||
'u', | ||
); | ||
if (!currentRegex.test(currentUrl)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
/** | ||
* 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; | ||
} | ||
// wait for load | ||
await new Promise((resolve) => | ||
window.addEventListener('DOMContentLoaded', resolve, { once: true }), | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
const { inherits } = require('util'); | ||
const { Duplex } = require('readable-stream'); | ||
|
||
const noop = () => undefined; | ||
|
||
module.exports = MobilePortStream; | ||
|
||
inherits(MobilePortStream, Duplex); | ||
|
||
/** | ||
* Creates a stream that's both readable and writable. | ||
* The stream supports arbitrary objects. | ||
* | ||
* @class | ||
* @param {Object} port Remote Port object | ||
*/ | ||
function MobilePortStream(port) { | ||
Duplex.call(this, { | ||
objectMode: true, | ||
}); | ||
this._name = port.name; | ||
this._targetWindow = window; | ||
this._port = port; | ||
this._origin = location.origin; | ||
window.addEventListener('message', this._onMessage.bind(this), false); | ||
} | ||
|
||
/** | ||
* Callback triggered when a message is received from | ||
* the remote Port associated with this Stream. | ||
* | ||
* @private | ||
* @param {Object} msg - Payload from the onMessage listener of Port | ||
*/ | ||
MobilePortStream.prototype._onMessage = function (event) { | ||
const msg = event.data; | ||
|
||
// validate message | ||
if (this._origin !== '*' && event.origin !== this._origin) { | ||
return; | ||
} | ||
if (!msg || typeof msg !== 'object') { | ||
return; | ||
} | ||
if (!msg.data || typeof msg.data !== 'object') { | ||
return; | ||
} | ||
if (msg.target && msg.target !== this._name) { | ||
return; | ||
} | ||
// Filter outgoing messages | ||
if (msg.data.data && msg.data.data.toNative) { | ||
return; | ||
} | ||
|
||
if (Buffer.isBuffer(msg)) { | ||
delete msg._isBuffer; | ||
const data = Buffer.from(msg); | ||
this.push(data); | ||
} else { | ||
this.push(msg); | ||
} | ||
}; | ||
|
||
/** | ||
* Callback triggered when the remote Port | ||
* associated with this Stream disconnects. | ||
* | ||
* @private | ||
*/ | ||
MobilePortStream.prototype._onDisconnect = function () { | ||
this.destroy(); | ||
}; | ||
|
||
/** | ||
* Explicitly sets read operations to a no-op | ||
*/ | ||
MobilePortStream.prototype._read = noop; | ||
|
||
/** | ||
* Called internally when data should be written to | ||
* this writable stream. | ||
* | ||
* @private | ||
* @param {*} msg Arbitrary object to write | ||
* @param {string} encoding Encoding to use when writing payload | ||
* @param {Function} cb Called when writing is complete or an error occurs | ||
*/ | ||
MobilePortStream.prototype._write = function (msg, _encoding, cb) { | ||
try { | ||
if (Buffer.isBuffer(msg)) { | ||
const data = msg.toJSON(); | ||
data._isBuffer = true; | ||
window.ReactNativeWebView.postMessage( | ||
JSON.stringify({ ...data, origin: window.location.href }), | ||
); | ||
} else { | ||
if (msg.data) { | ||
msg.data.toNative = true; | ||
} | ||
window.ReactNativeWebView.postMessage( | ||
JSON.stringify({ ...msg, origin: window.location.href }), | ||
); | ||
} | ||
} catch (err) { | ||
return cb(new Error('MobilePortStream - disconnected')); | ||
} | ||
return cb(); | ||
}; |
Oops, something went wrong.