Skip to content

Commit

Permalink
[mv3] Use workaround to inject scriptlets in Firefox
Browse files Browse the repository at this point in the history
Additionally:

Use `export UBO_VERSION=local` at the console to build MV3 extension using
current version of uBO code base. By default, the version is taken from
`./platform/mv3/ubo-version' and usually set to last stable release.
  • Loading branch information
gorhill committed Aug 11, 2023
1 parent 5ec0550 commit bb41d95
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 21 deletions.
11 changes: 6 additions & 5 deletions platform/mv3/extension/js/scripting-manager.js
Expand Up @@ -417,10 +417,6 @@ function registerSpecific(context) {
/******************************************************************************/

function registerScriptlet(context, scriptletDetails) {
// https://bugzilla.mozilla.org/show_bug.cgi?id=1736575
// `MAIN` world not yet supported in Firefox
if ( isGecko ) { return; }

const { before, filteringModeDetails, rulesetsDetails } = context;

const hasBroadHostPermission =
Expand Down Expand Up @@ -476,9 +472,14 @@ function registerScriptlet(context, scriptletDetails) {
matches,
excludeMatches,
runAt: 'document_start',
world: 'MAIN',
};

// https://bugzilla.mozilla.org/show_bug.cgi?id=1736575
// `MAIN` world not yet supported in Firefox
if ( isGecko === false ) {
directive.world = 'MAIN';
}

// register
if ( registered === undefined ) {
context.toAdd.push(directive);
Expand Down
9 changes: 7 additions & 2 deletions platform/mv3/extension/js/scripting/css-procedural.js
Expand Up @@ -485,8 +485,13 @@ class PSelector {
prime(input) {
const root = input || document;
if ( this.selector === '' ) { return [ root ]; }
if ( input !== document && /^ [>+~]/.test(this.selector) ) {
return Array.from(PSelectorSpathTask.qsa(input, this.selector));
if ( input !== document ) {
const c0 = this.selector.charCodeAt(0);
if ( c0 === 0x2B /* + */ || c0 === 0x7E /* ~ */ ) {
return Array.from(PSelectorSpathTask.qsa(input, this.selector));
} else if ( c0 === 0x3E /* > */ ) {
return Array.from(input.querySelectorAll(`:scope ${this.selector}`));
}
}
return Array.from(root.querySelectorAll(this.selector));
}
Expand Down
10 changes: 7 additions & 3 deletions platform/mv3/make-rulesets.js
Expand Up @@ -53,19 +53,23 @@ const commandLineArgs = (( ) => {
return args;
})();

const platform = commandLineArgs.get('platform') || 'chromium';
const outputDir = commandLineArgs.get('output') || '.';
const cacheDir = `${outputDir}/../mv3-data`;
const rulesetDir = `${outputDir}/rulesets`;
const scriptletDir = `${rulesetDir}/scripting`;
const env = [
'chromium',
platform,
'mv3',
'native_css_has',
'ublock',
'ubol',
'user_stylesheet',
];

if ( platform !== 'firefox' ) {
env.push('native_css_has');
}

/******************************************************************************/

const jsonSetMapReplacer = (k, v) => {
Expand Down Expand Up @@ -1222,7 +1226,7 @@ async function main() {
resources: Array.from(requiredRedirectResources).map(path => `/${path}`),
matches: [ '<all_urls>' ],
};
if ( commandLineArgs.get('platform') === 'chromium' ) {
if ( platform === 'chromium' ) {
web_accessible_resources.use_dynamic_url = true;
}
manifest.web_accessible_resources = [ web_accessible_resources ];
Expand Down
2 changes: 1 addition & 1 deletion platform/mv3/make-scriptlets.js
Expand Up @@ -167,7 +167,7 @@ export async function commit(rulesetId, path, writeFn) {
content = safeReplace(content, /\$scriptletName\$/, details.name, 0);
content = safeReplace(content,
'self.$argsList$',
JSON.stringify(Array.from(details.args.keys()))
JSON.stringify(Array.from(details.args.keys()).map(a => JSON.parse(a)))
);
content = safeReplace(content,
'self.$hostnamesMap$',
Expand Down
54 changes: 52 additions & 2 deletions platform/mv3/scriptlets/scriptlet.template.js
Expand Up @@ -21,6 +21,7 @@
*/

/* jshint esversion:11 */
/* global cloneInto */

'use strict';

Expand All @@ -31,10 +32,14 @@
// Important!
// Isolate from global scope

(function uBOL_$scriptletName$() {
// Start of local scope
(( ) => {

/******************************************************************************/

// Start of injected code
const uBOL_$scriptletName$ = function() {

const scriptletGlobals = new Map(); // jshint ignore: line

const argsList = self.$argsList$;
Expand Down Expand Up @@ -109,13 +114,58 @@ if ( entitiesMap.size !== 0 ) {

// Apply scriplets
for ( const i of todoIndices ) {
try { $scriptletName$(...JSON.parse(argsList[i])); }
try { $scriptletName$(...argsList[i]); }
catch(ex) {}
}
argsList.length = 0;

/******************************************************************************/

};
// End of injected code

/******************************************************************************/

// Inject code

// https://bugzilla.mozilla.org/show_bug.cgi?id=1736575
// `MAIN` world not yet supported in Firefox, so we inject the code into
// 'MAIN' ourself when enviroment in Firefox.

// Not Firefox
if ( typeof wrappedJSObject !== 'object' ) {
return uBOL_$scriptletName$();
}

// Firefox
{
const page = self.wrappedJSObject;
let script, url;
try {
page.uBOL_$scriptletName$ = cloneInto([
[ '(', uBOL_$scriptletName$.toString(), ')();' ],
{ type: 'text/javascript; charset=utf-8' },
], self);
const blob = new page.Blob(...page.uBOL_$scriptletName$);
url = page.URL.createObjectURL(blob);
const doc = page.document;
script = doc.createElement('script');
script.async = false;
script.src = url;
(doc.head || doc.documentElement || doc).append(script);
} catch (ex) {
console.error(ex);
}
if ( url ) {
if ( script ) { script.remove(); }
page.URL.revokeObjectURL(url);
}
delete page.uBOL_$scriptletName$;
}

/******************************************************************************/

// End of local scope
})();

/******************************************************************************/
Expand Down
9 changes: 7 additions & 2 deletions src/js/contentscript-extra.js
Expand Up @@ -378,8 +378,13 @@ class PSelector {
prime(input) {
const root = input || document;
if ( this.selector === '' ) { return [ root ]; }
if ( input !== document && /^ ?[>+~]/.test(this.selector) ) {
return Array.from(PSelectorSpathTask.qsa(input, this.selector));
if ( input !== document ) {
const c0 = this.selector.charCodeAt(0);
if ( c0 === 0x2B /* + */ || c0 === 0x7E /* ~ */ ) {
return Array.from(PSelectorSpathTask.qsa(input, this.selector));
} else if ( c0 === 0x3E /* > */ ) {
return Array.from(input.querySelectorAll(`:scope ${this.selector}`));
}
}
return Array.from(root.querySelectorAll(this.selector));
}
Expand Down
15 changes: 9 additions & 6 deletions tools/make-mv3.sh
Expand Up @@ -35,7 +35,6 @@ if [ "$QUICK" != "yes" ]; then
rm -rf $DES
fi


mkdir -p $DES
cd $DES
DES=$(pwd)
Expand All @@ -45,11 +44,15 @@ mkdir -p $DES/css/fonts
mkdir -p $DES/js
mkdir -p $DES/img

UBO_DIR=$(mktemp -d)
UBO_REPO="https://github.com/gorhill/uBlock.git"
UBO_VERSION=$(cat platform/mv3/ubo-version)
echo "*** uBOLite.mv3: Fetching uBO $UBO_VERSION from $UBO_REPO into $UBO_DIR"
git clone -q --depth 1 --branch "$UBO_VERSION" "$UBO_REPO" "$UBO_DIR"
if [ "$UBO_VERSION" != "local" ]; then
UBO_VERSION=$(cat platform/mv3/ubo-version)
UBO_REPO="https://github.com/gorhill/uBlock.git"
UBO_DIR=$(mktemp -d)
echo "*** uBOLite.mv3: Fetching uBO $UBO_VERSION from $UBO_REPO into $UBO_DIR"
git clone -q --depth 1 --branch "$UBO_VERSION" "$UBO_REPO" "$UBO_DIR"
else
UBO_DIR=.
fi

echo "*** uBOLite.mv3: Copying common files"
cp -R $UBO_DIR/src/css/fonts/* $DES/css/fonts/
Expand Down

0 comments on commit bb41d95

Please sign in to comment.