Create a MVP version of uBOLite for Firefox

What does not work at the time of commit:

Cosmetic filtering does not work:

The content scripts responsible for cosmetic filtering fail when
trying to inject the stylesheets through document.adoptedStyleSheets,
with the following error message:

  XrayWrapper denied access to property Symbol.iterator
  (reason: object is not safely Xrayable).
  See https://developer.mozilla.org/en-US/docs/Xray_vision for more
  information. ... css-declarative.js:106:8

A possible solution is to inject those content scripts in the
MAIN world. However Firefox scripting API does not support MAIN
world injection at the moment.

Scriptlet-filtering does not work:

Because scriptlet code needs to be injected in the MAIN world,
and this is currently not supported by Firefox's scripting API,
see https://bugzilla.mozilla.org/show_bug.cgi?id=1736575

There is no count badge on the toolbar icon in Firefox, as it
currently does not support the `DNR.setExtensionActionOptions`
method.

Other than the above issues, it does appear uBO is blocking
properly with no error reported in the dev console.

The adoptedStyleSheets issue though is worrisome, as the
cosmetic filtering content scripts were designed with ISOLATED
world injection in mind. Being forced to inject in MAIN world
(when available) make things a bit more complicated as uBO
has to ensure it's global variables do not leak into the page.
This commit is contained in:
Raymond Hill 2023-04-07 10:19:43 -04:00
parent 8b5774a83d
commit cbfd2ad942
No known key found for this signature in database
GPG key ID: 25E1490B761470C2
12 changed files with 124 additions and 17 deletions

View file

@ -5,7 +5,7 @@ run_options := $(filter-out $@,$(MAKECMDGOALS))
compare maxcost medcost mincost modifiers record wasm compare maxcost medcost mincost modifiers record wasm
sources := $(wildcard assets/* assets/*/* dist/version src/* src/*/* src/*/*/* src/*/*/*/*) sources := $(wildcard assets/* assets/*/* dist/version src/* src/*/* src/*/*/* src/*/*/*/*)
platform := $(wildcard platform/* platform/*/* platform/*/*/* platform/*/*/*/*) platform := $(wildcard platform/* platform/*/* platform/*/*/* platform/*/*/*/* platform/*/*/*/*/*)
assets := dist/build/uAssets assets := dist/build/uAssets
all: chromium firefox npm all: chromium firefox npm
@ -55,8 +55,11 @@ dig: dist/build/uBlock0.dig
dig-snfe: dig dig-snfe: dig
cd dist/build/uBlock0.dig && npm run snfe $(run_options) cd dist/build/uBlock0.dig && npm run snfe $(run_options)
mv3: tools/make-mv3.sh $(sources) $(platform) mv3-chromium: tools/make-mv3.sh $(sources) $(platform)
tools/make-mv3.sh tools/make-mv3.sh chromium
mv3-firefox: tools/make-mv3.sh $(sources) $(platform)
tools/make-mv3.sh firefox
mv3-quick: tools/make-mv3.sh $(sources) $(platform) mv3-quick: tools/make-mv3.sh $(sources) $(platform)
tools/make-mv3.sh quick tools/make-mv3.sh quick

View file

@ -169,7 +169,9 @@ async function onPermissionsRemoved() {
function onMessage(request, sender, callback) { function onMessage(request, sender, callback) {
if ( sender.origin !== UBOL_ORIGIN ) { return; } // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/MessageSender
// Firefox API does not set `sender.origin`
if ( sender.origin !== undefined && sender.origin !== UBOL_ORIGIN ) { return; }
switch ( request.what ) { switch ( request.what ) {
@ -304,7 +306,11 @@ async function start() {
console.log(`Available static rule count: ${count}`); console.log(`Available static rule count: ${count}`);
}); });
dnr.setExtensionActionOptions({ displayActionCountAsBadgeText: true }); // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/declarativeNetRequest
// Firefox API does not support `dnr.setExtensionActionOptions`
if ( dnr.setExtensionActionOptions ) {
dnr.setExtensionActionOptions({ displayActionCountAsBadgeText: true });
}
} }
(async ( ) => { (async ( ) => {

View file

@ -353,6 +353,10 @@ function registerDeclarative(context, declarativeDetails) {
/******************************************************************************/ /******************************************************************************/
function registerScriptlet(context, scriptletDetails) { function registerScriptlet(context, scriptletDetails) {
// https://bugzilla.mozilla.org/show_bug.cgi?id=1736575
// `MAIN` world not yet supported in Firefox
if ( navigator && navigator.product === 'Gecko' ) { return; }
const { before, filteringModeDetails, rulesetsDetails } = context; const { before, filteringModeDetails, rulesetsDetails } = context;
const hasBroadHostPermission = const hasBroadHostPermission =
@ -427,6 +431,10 @@ function registerScriptlet(context, scriptletDetails) {
/******************************************************************************/ /******************************************************************************/
function registerScriptletEntity(context) { function registerScriptletEntity(context) {
// https://bugzilla.mozilla.org/show_bug.cgi?id=1736575
// `MAIN` world not yet supported in Firefox
if ( navigator && navigator.product === 'Gecko' ) { return; }
const { before, filteringModeDetails, rulesetsDetails } = context; const { before, filteringModeDetails, rulesetsDetails } = context;
const js = []; const js = [];

View file

@ -32,6 +32,7 @@
/******************************************************************************/ /******************************************************************************/
const declarativeImports = self.declarativeImports || []; const declarativeImports = self.declarativeImports || [];
delete self.declarativeImports;
const lookupSelectors = (hn, out) => { const lookupSelectors = (hn, out) => {
for ( const { argsList, hostnamesMap } of declarativeImports ) { for ( const { argsList, hostnamesMap } of declarativeImports ) {

View file

@ -30,9 +30,9 @@
(function uBOL_cssGeneric() { (function uBOL_cssGeneric() {
const genericSelectorMap = self.genericSelectorMap || new Map(); const genericSelectorMap = self.genericSelectorMap || new Map();
if ( genericSelectorMap.size === 0 ) { return; } delete self.genericSelectorMap;
self.genericSelectorMap = undefined; if ( genericSelectorMap.size === 0 ) { return; }
/******************************************************************************/ /******************************************************************************/

View file

@ -659,6 +659,7 @@ class ProceduralFilterer {
/******************************************************************************/ /******************************************************************************/
const proceduralImports = self.proceduralImports || []; const proceduralImports = self.proceduralImports || [];
delete self.proceduralImports;
const lookupSelectors = (hn, out) => { const lookupSelectors = (hn, out) => {
for ( const { argsList, hostnamesMap } of proceduralImports ) { for ( const { argsList, hostnamesMap } of proceduralImports ) {

View file

@ -34,6 +34,7 @@
// $rulesetId$ // $rulesetId$
const specificEntityImports = self.specificEntityImports || []; const specificEntityImports = self.specificEntityImports || [];
delete self.specificEntityImports;
/******************************************************************************/ /******************************************************************************/
@ -65,8 +66,6 @@ for ( let i = 0; i < hnpartslen; i++ ) {
} }
} }
self.specificEntityImports = undefined;
if ( selectors.length === 0 ) { return; } if ( selectors.length === 0 ) { return; }
try { try {

View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>uBlock Origin Background Page</title>
</head>
<body>
<script src="js/background.js" type="module"></script>
</body>
</html>

View file

@ -0,0 +1,48 @@
{
"action": {
"default_icon": {
"16": "img/icon_16.png",
"32": "img/icon_32.png",
"64": "img/icon_64.png"
},
"default_popup": "popup.html"
},
"author": "Raymond Hill",
"background": {
"page": "background.html"
},
"browser_specific_settings": {
"gecko": {
"id": "uBOLite@raymondhill.net",
"strict_min_version": "113a1"
}
},
"declarative_net_request": {
"rule_resources": [
]
},
"default_locale": "en",
"description": "__MSG_extShortDesc__",
"icons": {
"16": "img/icon_16.png",
"32": "img/icon_32.png",
"64": "img/icon_64.png",
"128": "img/icon_128.png"
},
"manifest_version": 3,
"name": "__MSG_extName__",
"options_ui": {
"page": "dashboard.html"
},
"optional_permissions": [
"<all_urls>"
],
"permissions": [
"activeTab",
"declarativeNetRequest",
"scripting"
],
"short_name": "uBO Lite",
"version": "0.1",
"web_accessible_resources": []
}

View file

@ -1418,11 +1418,15 @@ async function main() {
// Patch declarative_net_request key // Patch declarative_net_request key
manifest.declarative_net_request = { rule_resources: ruleResources }; manifest.declarative_net_request = { rule_resources: ruleResources };
// Patch web_accessible_resources key // Patch web_accessible_resources key
manifest.web_accessible_resources = [{ const web_accessible_resources = {
resources: Array.from(requiredRedirectResources).map(path => `/${path}`), resources: Array.from(requiredRedirectResources).map(path => `/${path}`),
matches: [ '<all_urls>' ], matches: [ '<all_urls>' ],
use_dynamic_url: true, };
}]; if ( commandLineArgs.get('platform') === 'chromium' ) {
web_accessible_resources.use_dynamic_url = true;
}
manifest.web_accessible_resources = [ web_accessible_resources ];
// Patch version key // Patch version key
const now = new Date(); const now = new Date();
const yearPart = now.getUTCFullYear() - 2000; const yearPart = now.getUTCFullYear() - 2000;

View file

@ -6,12 +6,32 @@ set -e
echo "*** uBOLite.mv3: Creating extension" echo "*** uBOLite.mv3: Creating extension"
DES="dist/build/uBOLite.mv3" PLATFORM="chromium"
if [ "$1" != "quick" ]; then for i in "$@"; do
case $i in
quick)
QUICK="yes"
shift # past argument=value
;;
firefox)
PLATFORM="firefox"
shift # past argument=value
;;
chromium)
PLATFORM="chromium"
shift # past argument=value
;;
esac
done
DES="dist/build/uBOLite.$PLATFORM"
if [ "$QUICK" != "yes" ]; then
rm -rf $DES rm -rf $DES
fi fi
mkdir -p $DES mkdir -p $DES
cd $DES cd $DES
DES=$(pwd) DES=$(pwd)
@ -35,17 +55,24 @@ cp src/js/i18n.js $DES/js/
cp LICENSE.txt $DES/ cp LICENSE.txt $DES/
echo "*** uBOLite.mv3: Copying mv3-specific files" echo "*** uBOLite.mv3: Copying mv3-specific files"
if [ "$PLATFORM" = "firefox" ]; then
cp platform/mv3/firefox/background.html $DES/
fi
cp platform/mv3/extension/*.html $DES/ cp platform/mv3/extension/*.html $DES/
cp platform/mv3/extension/css/* $DES/css/ cp platform/mv3/extension/css/* $DES/css/
cp -R platform/mv3/extension/js/* $DES/js/ cp -R platform/mv3/extension/js/* $DES/js/
cp platform/mv3/extension/img/* $DES/img/ cp platform/mv3/extension/img/* $DES/img/
cp -R platform/mv3/extension/_locales $DES/ cp -R platform/mv3/extension/_locales $DES/
if [ "$1" != "quick" ]; then if [ "$QUICK" != "yes" ]; then
echo "*** uBOLite.mv3: Generating rulesets" echo "*** uBOLite.mv3: Generating rulesets"
TMPDIR=$(mktemp -d) TMPDIR=$(mktemp -d)
mkdir -p $TMPDIR mkdir -p $TMPDIR
cp platform/mv3/extension/manifest.json $DES/ if [ "$PLATFORM" = "chromium" ]; then
cp platform/mv3/chromium/manifest.json $DES/
elif [ "$PLATFORM" = "firefox" ]; then
cp platform/mv3/firefox/manifest.json $DES/
fi
./tools/make-nodejs.sh $TMPDIR ./tools/make-nodejs.sh $TMPDIR
cp platform/mv3/package.json $TMPDIR/ cp platform/mv3/package.json $TMPDIR/
cp platform/mv3/*.js $TMPDIR/ cp platform/mv3/*.js $TMPDIR/
@ -55,7 +82,7 @@ if [ "$1" != "quick" ]; then
mkdir -p $TMPDIR/web_accessible_resources mkdir -p $TMPDIR/web_accessible_resources
cp src/web_accessible_resources/* $TMPDIR/web_accessible_resources/ cp src/web_accessible_resources/* $TMPDIR/web_accessible_resources/
cd $TMPDIR cd $TMPDIR
node --no-warnings make-rulesets.js output=$DES node --no-warnings make-rulesets.js output=$DES platform="$PLATFORM"
cd - > /dev/null cd - > /dev/null
rm -rf $TMPDIR rm -rf $TMPDIR
fi fi