Add per-site on/off switch to mv3 experimental version

This commit is contained in:
Raymond Hill 2022-09-07 10:15:36 -04:00
parent e420b75b91
commit 224410a6f5
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
13 changed files with 692 additions and 106 deletions

View File

@ -39,13 +39,17 @@ jobs:
tag_name: ${{ steps.release_info.outputs.VERSION }}
release_name: ${{ steps.release_info.outputs.VERSION }}
prerelease: true
- name: Build all packages
- name: Build MV2 packages
run: |
tools/make-chromium.sh ${{ steps.release_info.outputs.VERSION }}
tools/make-firefox.sh ${{ steps.release_info.outputs.VERSION }}
tools/make-thunderbird.sh ${{ steps.release_info.outputs.VERSION }}
tools/make-npm.sh ${{ steps.release_info.outputs.VERSION }}
tools/make-mv3.sh all
- name: Build MV3 packages
run: |
tools/make-mv3.sh
echo ::set-output name=MV3PACKAGE::$(basename $(ls dist/build/uBlock0_*.mv3.zip))
- name: Upload Chromium package
uses: actions/upload-release-asset@v1
env:
@ -88,6 +92,6 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: dist/build/uBlock0.mv3.zip
asset_name: uBlock0.mv3.zip
asset_path: dist/build/${{ env.MV3PACKAGE }}
asset_name: ${{ env.MV3PACKAGE }}
asset_content_type: application/octet-stream

View File

@ -5,7 +5,7 @@ run_options := $(filter-out $@,$(MAKECMDGOALS))
compare maxcost medcost mincost modifiers record wasm
sources := $(wildcard assets/resources/* src/* src/*/* src/*/*/* src/*/*/*/*)
platform := $(wildcard platform/* platform/*/*)
platform := $(wildcard platform/* platform/*/* platform/*/*/*)
assets := $(wildcard submodules/uAssets/* \
submodules/uAssets/*/* \
submodules/uAssets/*/*/* \
@ -52,10 +52,11 @@ dig: dist/build/uBlock0.dig
dig-snfe: dig
cd dist/build/uBlock0.dig && npm run snfe $(run_options)
dist/build/uBlock0.mv3: tools/make-mv3.sh $(sources) $(platform)
tools/make-mv3.sh all
mv3: tools/make-mv3.sh $(sources) $(platform)
tools/make-mv3.sh
mv3: dist/build/uBlock0.mv3
mv3-quick: tools/make-mv3.sh $(sources) $(platform)
tools/make-mv3.sh quick
# Update submodules.
update-submodules:

View File

@ -1,65 +0,0 @@
'use strict';
import regexRulesets from '/rulesets/regexes.js';
const dnr = chrome.declarativeNetRequest;
dnr.setExtensionActionOptions({ displayActionCountAsBadgeText: true });
(async ( ) => {
const allRules = [];
const toCheck = [];
for ( const regexRuleset of regexRulesets ) {
if ( regexRuleset.enabled !== true ) { continue; }
for ( const rule of regexRuleset.rules ) {
const regex = rule.condition.regexFilter;
const isCaseSensitive = rule.condition.isUrlFilterCaseSensitive === true;
allRules.push(rule);
toCheck.push(dnr.isRegexSupported({ regex, isCaseSensitive }));
}
}
const results = await Promise.all(toCheck);
const newRules = [];
for ( let i = 0; i < allRules.length; i++ ) {
const rule = allRules[i];
const result = results[i];
if ( result instanceof Object && result.isSupported ) {
newRules.push(rule);
} else {
console.info(`${result.reason}: ${rule.condition.regexFilter}`);
}
}
const oldRules = await dnr.getDynamicRules();
const oldRuleMap = new Map(oldRules.map(rule => [ rule.id, rule ]));
const newRuleMap = new Map(newRules.map(rule => [ rule.id, rule ]));
const addRules = [];
const removeRuleIds = [];
for ( const oldRule of oldRules ) {
const newRule = newRuleMap.get(oldRule.id);
if ( newRule === undefined ) {
removeRuleIds.push(oldRule.id);
} else if ( JSON.stringify(oldRule) !== JSON.stringify(newRule) ) {
removeRuleIds.push(oldRule.id);
addRules.push(newRule);
}
}
for ( const newRule of newRuleMap.values() ) {
if ( oldRuleMap.has(newRule.id) ) { continue; }
addRules.push(newRule);
}
if ( addRules.length !== 0 || removeRuleIds.length !== 0 ) {
await dnr.updateDynamicRules({ addRules, removeRuleIds });
}
const dynamicRules = await dnr.getDynamicRules();
console.log(`Dynamic rule count: ${dynamicRules.length}`);
const enabledRulesets = await dnr.getEnabledRulesets();
console.log(`Enabled rulesets: ${enabledRulesets}`);
console.log(`Available dynamic rule count: ${dnr.MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES - dynamicRules.length}`);
dnr.getAvailableStaticRuleCount().then(count => {
console.log(`Available static rule count: ${count}`);
});
})();

View File

@ -0,0 +1,227 @@
/* External CSS values override */
.fa-icon.fa-icon-badged > .fa-icon-badge {
bottom: auto;
top: -20%;
}
/* Internal CSS values */
:root body {
overflow: hidden;
}
:root body,
:root.mobile body {
--font-size: 14px;
--popup-gap: var(--font-size);
--popup-gap-thin: calc(0.5 * var(--popup-gap));
--popup-gap-extra-thin: calc(0.25 * var(--popup-gap));
--popup-main-min-width: 18em;
--popup-firewall-min-width: 30em;
--popup-rule-cell-width: 5em;
font-size: var(--font-size);
line-height: 20px;
}
:root body.loading {
opacity: 0;
}
a {
color: var(--ink-1);
fill: var(--ink-1);
text-decoration: none;
}
:focus {
outline: 0;
}
#main {
align-self: flex-start;
max-width: 340px;
min-width: var(--popup-main-min-width);
}
:root.portrait #main {
align-self: inherit;
}
hr {
border: 0;
border-top: 1px solid var(--hr-ink);
margin: 0;
padding: 0;
}
#sticky {
background-color: var(--surface-1);
position: sticky;
top: 0;
z-index: 100;
}
#stickyTools {
align-items: stretch;
display: flex;
justify-content: space-between;
}
#switch {
color: var(--popup-power-ink);
cursor: pointer;
display: flex;
fill: var(--popup-power-ink);
flex-grow: 1;
font-size: 96px;
justify-content: center;
margin: var(--popup-gap-thin) var(--popup-gap-thin) 0;
padding: 0;
stroke: none;
stroke-width: 64;
}
body.off #switch {
fill: var(--surface-1);
stroke: var(--checkbox-ink);
}
.rulesetTools {
background-color: transparent;
border: 0;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: space-evenly;
width: 25%;
}
.rulesetTools [id] {
background-color: var(--popup-ruleset-tool-surface);
border-radius: 4px;
cursor: pointer;
fill: var(--popup-ruleset-tool-ink);
flex-grow: 1;
font-size: 2.2em;
padding: 0;
visibility: hidden;
}
.rulesetTools [id]:not(:first-of-type) {
margin-block-start: 1px;
}
.rulesetTools [id] > svg {
fill: var(--ink-4);
}
body.needReload #refresh,
body.needSave #saveRules,
body.needSave #revertRules {
visibility: visible;
}
#hostname {
margin: var(--popup-gap) var(--popup-gap-extra-thin);
text-align: center;
}
#hostname > span {
word-break: break-all;
}
#hostname > span + span {
font-weight: 600;
}
.itemRibbon {
column-gap: var(--popup-gap);
display: grid;
grid-auto-columns: 1fr;
grid-auto-flow: column;
grid-template: auto / 1fr 1fr;
margin: var(--popup-gap);
}
.itemRibbon > span + span {
text-align: end;
}
.toolRibbon {
align-items: start;
background-color: var(--popup-toolbar-surface);
display: grid;
grid-auto-columns: 1fr;
grid-auto-flow: column;
grid-template: auto / repeat(4, 1fr);
justify-items: center;
margin: 0;
white-space: normal;
}
.toolRibbon .tool {
cursor: pointer;
display: flex;
flex-direction: column;
font-size: 1.4em;
min-width: 32px;
padding: var(--popup-gap)
var(--popup-gap-thin);
unicode-bidi: embed;
visibility: hidden;
}
.toolRibbon .tool:hover {
color: var(--ink-1);
fill: var(--ink-1);
}
.toolRibbon .tool.enabled {
visibility: visible;
}
.toolRibbon .tool .caption {
font: 10px/12px sans-serif;
margin-top: 6px;
text-align: center;
}
body.mobile.no-tooltips .toolRibbon .tool {
font-size: 1.6em;
}
#basicTools {
margin-top: var(--default-gap);
}
/* configurable UI elements */
:root:not(.mobile) .toolRibbon .caption,
:root.mobile body.no-tooltips .toolRibbon .caption,
:root.mobile body[data-ui~="-captions"] .toolRibbon .caption {
display: none;
}
:root.mobile .toolRibbon .caption,
:root:not(.mobile) body[data-ui~="+captions"] .toolRibbon .caption {
display: inherit;
}
:root:not(.mobile) .toolRibbon .tool,
:root.mobile body.no-tooltips .toolRibbon .tool,
:root.mobile body[data-ui~="-captions"] .toolRibbon .tool {
padding: var(--popup-gap) var(--popup-gap-thin);
}
/* horizontally-constrained viewport */
:root.portrait body {
overflow-y: auto;
width: 100%;
}
:root.portrait #main {
max-width: unset;
}
/* mouse-driven devices */
:root.desktop {
display: flex;
justify-content: flex-end;
}
:root.desktop body {
--popup-gap: calc(var(--font-size) * 0.875);
}
:root.desktop body:not(.off) #switch:hover {
fill: rgb(var(--popup-power-ink-rgb) / 90%);
}
:root.desktop body.off #switch:hover {
stroke: var(--popup-power-ink);
}
:root.desktop .rulesetTools [id]:hover {
background-color: var(--popup-ruleset-tool-surface-hover);
}
:root.desktop .rulesetTools [id]:hover > svg {
fill: var(--ink-2);
}
:root.desktop #firewall {
direction: rtl;
line-height: 1.4;
}
:root.desktop .tool:hover {
background-color: var(--popup-toolbar-surface-hover);
}
:root.desktop #moreOrLess > span:hover {
background-color: var(--surface-2);
/* background-color: var(--popup-toolbar-surface-hover); */
}

View File

@ -0,0 +1,188 @@
'use strict';
import regexRulesets from '/rulesets/regexes.js';
/******************************************************************************/
const dnr = chrome.declarativeNetRequest;
const TRUSTED_DIRECTIVE_BASE_RULE_ID = 1000000;
const dynamicRuleMap = new Map();
/******************************************************************************/
async function updateRegexRules() {
const allRules = [];
const toCheck = [];
for ( const regexRuleset of regexRulesets ) {
if ( regexRuleset.enabled !== true ) { continue; }
for ( const rule of regexRuleset.rules ) {
const regex = rule.condition.regexFilter;
const isCaseSensitive = rule.condition.isUrlFilterCaseSensitive === true;
allRules.push(rule);
toCheck.push(dnr.isRegexSupported({ regex, isCaseSensitive }));
}
}
const results = await Promise.all(toCheck);
const newRules = [];
for ( let i = 0; i < allRules.length; i++ ) {
const rule = allRules[i];
const result = results[i];
if ( result instanceof Object && result.isSupported ) {
newRules.push(rule);
} else {
console.info(`${result.reason}: ${rule.condition.regexFilter}`);
}
}
const newRuleMap = new Map(newRules.map(rule => [ rule.id, rule ]));
const addRules = [];
const removeRuleIds = [];
for ( const oldRule of dynamicRuleMap.values() ) {
if ( oldRule.id >= TRUSTED_DIRECTIVE_BASE_RULE_ID ) { continue; }
const newRule = newRuleMap.get(oldRule.id);
if ( newRule === undefined ) {
removeRuleIds.push(oldRule.id);
dynamicRuleMap.delete(oldRule.id);
} else if ( JSON.stringify(oldRule) !== JSON.stringify(newRule) ) {
removeRuleIds.push(oldRule.id);
addRules.push(newRule);
dynamicRuleMap.set(oldRule.id, newRule);
}
}
for ( const newRule of newRuleMap.values() ) {
if ( dynamicRuleMap.has(newRule.id) ) { continue; }
addRules.push(newRule);
dynamicRuleMap.set(newRule.id, newRule);
}
if ( addRules.length !== 0 || removeRuleIds.length !== 0 ) {
return dnr.updateDynamicRules({ addRules, removeRuleIds });
}
}
/******************************************************************************/
async function matchesTrustedSiteDirective(details) {
const url = new URL(details.origin);
let rule = dynamicRuleMap.get(TRUSTED_DIRECTIVE_BASE_RULE_ID);
if ( rule === undefined ) { return false; }
const domainSet = new Set(rule.condition.requestDomains);
let hostname = url.hostname;
for (;;) {
if ( domainSet.has(hostname) ) { return true; }
const pos = hostname.indexOf('.');
if ( pos === -1 ) { break; }
hostname = hostname.slice(pos+1);
}
return false;
}
async function addTrustedSiteDirective(details) {
const url = new URL(details.origin);
let rule = dynamicRuleMap.get(TRUSTED_DIRECTIVE_BASE_RULE_ID);
if ( rule !== undefined ) {
rule.condition.initiatorDomains = undefined;
if ( Array.isArray(rule.condition.requestDomains) === false ) {
rule.condition.requestDomains = [];
}
}
if ( rule === undefined ) {
rule = {
id: TRUSTED_DIRECTIVE_BASE_RULE_ID,
action: {
type: 'allowAllRequests',
},
condition: {
requestDomains: [ url.hostname ],
resourceTypes: [ 'main_frame' ],
},
priority: TRUSTED_DIRECTIVE_BASE_RULE_ID,
};
dynamicRuleMap.set(TRUSTED_DIRECTIVE_BASE_RULE_ID, rule);
} else if ( rule.condition.requestDomains.includes(url.hostname) === false ) {
rule.condition.requestDomains.push(url.hostname);
}
await dnr.updateDynamicRules({
addRules: [ rule ],
removeRuleIds: [ TRUSTED_DIRECTIVE_BASE_RULE_ID ],
});
return true;
}
async function removeTrustedSiteDirective(details) {
const url = new URL(details.origin);
let rule = dynamicRuleMap.get(TRUSTED_DIRECTIVE_BASE_RULE_ID);
if ( rule === undefined ) { return false; }
rule.condition.initiatorDomains = undefined;
if ( Array.isArray(rule.condition.requestDomains) === false ) {
rule.condition.requestDomains = [];
}
const domainSet = new Set(rule.condition.requestDomains);
const beforeCount = domainSet.size;
let hostname = url.hostname;
for (;;) {
domainSet.delete(hostname);
const pos = hostname.indexOf('.');
if ( pos === -1 ) { break; }
hostname = hostname.slice(pos+1);
}
if ( domainSet.size === beforeCount ) { return false; }
if ( domainSet.size === 0 ) {
dynamicRuleMap.delete(TRUSTED_DIRECTIVE_BASE_RULE_ID);
await dnr.updateDynamicRules({
removeRuleIds: [ TRUSTED_DIRECTIVE_BASE_RULE_ID ]
});
return false;
}
rule.condition.requestDomains = Array.from(domainSet);
await dnr.updateDynamicRules({
addRules: [ rule ],
removeRuleIds: [ TRUSTED_DIRECTIVE_BASE_RULE_ID ],
});
return false;
}
async function toggleTrustedSiteDirective(details) {
return details.state
? removeTrustedSiteDirective(details)
: addTrustedSiteDirective(details);
}
/******************************************************************************/
(async ( ) => {
const dynamicRules = await dnr.getDynamicRules();
for ( const rule of dynamicRules ) {
dynamicRuleMap.set(rule.id, rule);
}
await updateRegexRules();
console.log(`Dynamic rule count: ${dynamicRuleMap.size}`);
const enabledRulesets = await dnr.getEnabledRulesets();
console.log(`Enabled rulesets: ${enabledRulesets}`);
console.log(`Available dynamic rule count: ${dnr.MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES - dynamicRuleMap.size}`);
dnr.getAvailableStaticRuleCount().then(count => {
console.log(`Available static rule count: ${count}`);
});
dnr.setExtensionActionOptions({ displayActionCountAsBadgeText: true });
chrome.runtime.onMessage.addListener((request, sender, callback) => {
switch ( request.what ) {
case 'matchesTrustedSiteDirective':
matchesTrustedSiteDirective(request).then(response => {
callback(response);
});
return true;
case 'toggleTrustedSiteDirective':
toggleTrustedSiteDirective(request).then(response => {
callback(response);
});
return true;
default:
break;
}
});
})();

View File

@ -0,0 +1,114 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2014-present Raymond Hill
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/uBlock
*/
'use strict';
/******************************************************************************/
let currentTab = {};
let originalTrustedState = false;
/******************************************************************************/
async function toggleTrustedSiteDirective() {
let url;
try {
url = new URL(currentTab.url);
} catch(ex) {
return;
}
if ( url instanceof URL === false ) { return; }
const targetTrustedState = document.body.classList.contains('off');
const newTrustedState = await chrome.runtime.sendMessage({
what: 'toggleTrustedSiteDirective',
origin: url.origin,
state: targetTrustedState,
tabId: currentTab.id,
}).catch(( ) => targetTrustedState === false);
document.body.classList.toggle('off', newTrustedState === true);
document.body.classList.toggle(
'needReload',
newTrustedState !== originalTrustedState
);
}
/******************************************************************************/
function reloadTab(ev) {
chrome.tabs.reload(currentTab.id, {
bypassCache: ev.ctrlKey || ev.metaKey || ev.shiftKey,
});
document.body.classList.remove('needReload');
originalTrustedState = document.body.classList.contains('off');
}
/******************************************************************************/
async function init() {
const [ tab ] = await chrome.tabs.query({ active: true });
if ( tab instanceof Object === false ) { return true; }
currentTab = tab;
let url;
try {
url = new URL(currentTab.url);
} catch(ex) {
}
if ( url !== undefined ) {
originalTrustedState = await chrome.runtime.sendMessage({
what: 'matchesTrustedSiteDirective',
origin: url.origin,
}) === true;
}
const body = document.body;
body.classList.toggle('off', originalTrustedState);
const elemHn = document.querySelector('#hostname');
elemHn.textContent = url && url.hostname || '';
document.querySelector('#switch').addEventListener(
'click',
toggleTrustedSiteDirective
);
document.querySelector('#refresh').addEventListener(
'click',
reloadTab
);
document.body.classList.remove('loading');
return true;
}
async function tryInit() {
try {
await init();
} catch(ex) {
setTimeout(tryInit, 100);
}
}
tryInit();
/******************************************************************************/

View File

@ -1,7 +1,15 @@
{
"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": {
"service_worker": "background.js",
"background": {
"service_worker": "/js/background.js",
"type": "module"
},
"declarative_net_request": {
@ -19,7 +27,8 @@
"minimum_chrome_version": "101.0",
"name": "uBO Minus (MV3)",
"permissions": [
"activeTab",
"declarativeNetRequest"
],
"version": "0.1.0"
"version": "0.1"
}

View File

@ -0,0 +1,57 @@
<!DOCTYPE html>
<html id="uBO-popup-panel" class="desktop">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/default.css">
<link rel="stylesheet" href="css/common.css">
<link rel="stylesheet" href="css/fa-icons.css">
<link rel="stylesheet" href="css/popup.css">
<title data-i18n="extName"></title>
</head>
<body class="loading">
<div id="main">
<div id="sticky">
<div id="stickyTools">
<div class="rulesetTools">
<span id="saveRules" class="fa-icon" data-i18n-title="popupTipSaveRules">lock</span>
<span id="revertRules" class="fa-icon" data-i18n-title="popupTipRevertRules">eraser</span>
</div>
<div id="switch" role="button" aria-label tabindex="0" title>
<span class="fa-icon"><!--
Power button taken from Font Awesome v4.7.0 by Dave Gandy.
Unlike other FA icons, the power button is inlined here so
that we can use a clip-path in order to ensure that the stroke
does not "bleed" outside the fill area.
--><svg viewBox="0 0 1536 1664">
<defs>
<path id="power-off-path" d="m 1536,896 q 0,156 -61,298 -61,142 -164,245 -103,103 -245,164 -142,61 -298,61 -156,0 -298,-61 Q 328,1542 225,1439 122,1336 61,1194 0,1052 0,896 0,714 80.5,553 161,392 307,283 q 43,-32 95.5,-25 52.5,7 83.5,50 32,42 24.5,94.5 Q 503,455 461,487 363,561 309.5,668 256,775 256,896 q 0,104 40.5,198.5 40.5,94.5 109.5,163.5 69,69 163.5,109.5 94.5,40.5 198.5,40.5 104,0 198.5,-40.5 Q 1061,1327 1130,1258 1199,1189 1239.5,1094.5 1280,1000 1280,896 1280,775 1226.5,668 1173,561 1075,487 1033,455 1025.5,402.5 1018,350 1050,308 q 31,-43 84,-50 53,-7 95,25 146,109 226.5,270 80.5,161 80.5,343 z m -640,-768 0,640 q 0,52 -38,90 -38,38 -90,38 -52,0 -90,-38 -38,-38 -38,-90 l 0,-640 q 0,-52 38,-90 38,-38 90,-38 52,0 90,38 38,38 38,90 z"/>
<clipPath id="power-off-clip"><use href="#power-off-path"/></clipPath>
</defs>
<use href="#power-off-path" clip-path="url(#power-off-clip)"/>
</svg><!--
--></span>
</div>
<div class="rulesetTools">
<span id="refresh" class="fa-icon">refresh</span>
</div>
</div>
<div id="hostname"><span></span>&shy;<span></span></div>
</div>
<div id="basicTools" class="toolRibbon">
<span></span>
<span></span>
<span></span>
<span></span>
<a href="dashboard.html" class="fa-icon tool" target="uBODashboard" tabindex="0" data-i18n-title="popupTipDashboard">cogs<span class="caption" data-i18n="popupTipDashboard"></span></a>
</div>
</div>
<script src="js/fa-icons.js"></script>
<script src="js/popup.js" type="module"></script>
</body>
</html>

View File

@ -137,6 +137,7 @@ async function main() {
for ( const ruleset of rulesetConfigs ) {
const lists = [];
log('============================');
log(`Listset for '${ruleset.id}':`);
if ( Array.isArray(ruleset.paths) ) {
@ -152,11 +153,14 @@ async function main() {
}
}
const rules = await dnrRulesetFromRawLists(lists, {
const details = await dnrRulesetFromRawLists(lists, {
env: [ 'chromium' ],
});
log(`Ruleset size for '${ruleset.id}': ${rules.length}`);
const { ruleset: rules } = details;
log(`Input filter count: ${details.filterCount}`);
log(`\tAccepted filter count: ${details.acceptedFilterCount}`);
log(`\tRejected filter count: ${details.rejectedFilterCount}`);
log(`Output rule count: ${rules.length}`);
const good = rules.filter(rule => isGood(rule) && isRegex(rule) === false);
log(`\tGood: ${good.length}`);
@ -227,11 +231,19 @@ async function main() {
log(`Total regex rules count: ${maybeGoodTotalCount}`);
// Patch manifest
const manifest = await fs.readFile(`${outputDir}/manifest.json`, { encoding: 'utf8' })
.then(text => JSON.parse(text));
const manifest = await fs.readFile(
`${outputDir}/manifest.json`,
{ encoding: 'utf8' }
).then(text =>
JSON.parse(text)
);
manifest.declarative_net_request = { rule_resources: ruleResources };
const now = new Date();
manifest.version = `0.1.${now.getUTCFullYear() - 2000}.${now.getUTCMonth() * 100 + now.getUTCDate()}`;
const yearPart = now.getUTCFullYear() - 2000;
const monthPart = (now.getUTCMonth() + 1) * 1000;
const dayPart = now.getUTCDate() * 10;
const hourPart = Math.floor(now.getUTCHours() / 3) + 1;
manifest.version = manifest.version + `.${yearPart}.${monthPart + dayPart + hourPart}`;
await fs.writeFile(
`${outputDir}/manifest.json`,
JSON.stringify(manifest, null, 2) + '\n'

View File

@ -160,7 +160,7 @@ const onMessage = function(request, sender, callback) {
env: vAPI.webextFlavor.env,
};
const t0 = Date.now();
dnrRulesetFromRawLists(listPromises, options).then(ruleset => {
dnrRulesetFromRawLists(listPromises, options).then(details => {
const replacer = (k, v) => {
if ( k.startsWith('__') ) { return; }
if ( Array.isArray(v) ) {
@ -192,9 +192,14 @@ const onMessage = function(request, sender, callback) {
rule.action.type === 'redirect' &&
rule.action.redirect.transform !== undefined;
const runtime = Date.now() - t0;
const { ruleset } = details;
const out = [
`dnrRulesetFromRawLists(${JSON.stringify(listNames, null, 2)})`,
`Run time: ${runtime} ms`,
`Filters count: ${details.filterCount}`,
`Accepted filter count: ${details.acceptedFilterCount}`,
`Rejected filter count: ${details.rejectedFilterCount}`,
`Resulting DNR rule count: ${ruleset.length}`,
];
const good = ruleset.filter(rule =>
isUnsupported(rule) === false &&

View File

@ -46,7 +46,6 @@ function addToDNR(context, list) {
const compiler = staticNetFilteringEngine.createCompiler(parser);
writer.properties.set('name', list.name);
parser.setMaxTokenLength(staticNetFilteringEngine.MAX_TOKEN_LENGTH);
compiler.start(writer);
while ( lineIter.eot() === false ) {
@ -95,8 +94,7 @@ async function dnrRulesetFromRawLists(lists, options = {}) {
toLoad.push(list.then(list => toDNR(context, list)));
}
await Promise.all(toLoad);
const ruleset = staticNetFilteringEngine.dnrFromCompiled('end', context);
return ruleset;
return staticNetFilteringEngine.dnrFromCompiled('end', context);
}
/******************************************************************************/

View File

@ -3852,6 +3852,9 @@ FilterContainer.prototype.dnrFromCompiled = function(op, context, ...args) {
good: new Set(),
bad: new Set(),
invalid: new Set(),
filterCount: 0,
acceptedFilterCount: 0,
rejectedFilterCount: 0,
};
}
@ -3859,6 +3862,7 @@ FilterContainer.prototype.dnrFromCompiled = function(op, context, ...args) {
const reader = args[0];
reader.select('NETWORK_FILTERS:GOOD');
while ( reader.next() ) {
context.filterCount += 1;
if ( context.good.has(reader.line) === false ) {
context.good.add(reader.line);
}
@ -3878,8 +3882,10 @@ FilterContainer.prototype.dnrFromCompiled = function(op, context, ...args) {
for ( const line of good ) {
if ( bad.has(line) ) {
context.rejectedFilterCount += 1;
continue;
}
context.acceptedFilterCount += 1;
const args = unserialize(line);
const bits = args[0];
@ -4201,7 +4207,12 @@ FilterContainer.prototype.dnrFromCompiled = function(op, context, ...args) {
}
}
return Array.from(rulesetMap.values());
return {
ruleset: Array.from(rulesetMap.values()),
filterCount: context.filterCount,
acceptedFilterCount: context.acceptedFilterCount,
rejectedFilterCount: context.rejectedFilterCount,
};
};
/******************************************************************************/

View File

@ -7,36 +7,61 @@ set -e
echo "*** uBlock0.mv3: Creating extension"
DES="dist/build/uBlock0.mv3"
rm -rf $DES
if [ "$1" != "quick" ]; then
rm -rf $DES
fi
mkdir -p $DES
cd $DES
DES=$(pwd)
cd - > /dev/null
TMPDIR=$(mktemp -d)
mkdir -p $TMPDIR
echo "*** uBlock0.mv3: Copying mv3-specific files"
cp -R platform/mv3/extension/* $DES/
mkdir -p $DES/css/fonts
mkdir -p $DES/js
mkdir -p $DES/img
echo "*** uBlock0.mv3: Copying common files"
cp -R src/css/fonts/* $DES/css/fonts/
cp src/css/themes/default.css $DES/css/
cp src/css/common.css $DES/css/
cp src/css/fa-icons.css $DES/css/
cp src/js/fa-icons.js $DES/js/
cp LICENSE.txt $DES/
echo "*** uBlock0.mv3: Generating rulesets"
./tools/make-nodejs.sh $TMPDIR
cp platform/mv3/package.json $TMPDIR/
cp platform/mv3/*.js $TMPDIR/
cd $TMPDIR
node --no-warnings make-rulesets.js output=$DES
cd - > /dev/null
rm -rf $TMPDIR
echo "*** uBlock0.mv3: Copying mv3-specific files"
cp platform/mv3/extension/*.html $DES/
cp platform/mv3/extension/css/* $DES/css/
cp platform/mv3/extension/js/* $DES/js/
cp platform/mv3/extension/img/* $DES/img/
if [ "$1" != "quick" ]; then
echo "*** uBlock0.mv3: Generating rulesets"
TMPDIR=$(mktemp -d)
mkdir -p $TMPDIR
cp platform/mv3/extension/manifest.json $DES/
./tools/make-nodejs.sh $TMPDIR
cp platform/mv3/package.json $TMPDIR/
cp platform/mv3/*.js $TMPDIR/
cd $TMPDIR
node --no-warnings make-rulesets.js output=$DES quick=$QUICK
cd - > /dev/null
rm -rf $TMPDIR
fi
echo "*** uBlock0.mv3: extension ready"
echo "Extension location: $DES/"
if [ "$1" = all ]; then
echo "*** uBlock0.mv3: Creating webstore package..."
pushd $(dirname $DES/) > /dev/null
zip uBlock0.mv3.zip -qr $(basename $DES/)/*
echo "Package location: $(pwd)/uBlock0.mv3.zip"
popd > /dev/null
fi
echo "*** uBlock0.mv3: Creating webstore package..."
PACKAGENAME=uBlock0_$(jq -r .version $DES/manifest.json).mv3.zip
TMPDIR=$(mktemp -d)
mkdir -p $TMPDIR
cp -R $DES/* $TMPDIR/
cd $TMPDIR > /dev/null
rm log.txt
zip $PACKAGENAME -qr ./*
cp $PACKAGENAME $(dirname $DES/)/
cd - > /dev/null
rm -rf $TMPDIR
echo "Package location: $(pwd)/$PACKAGENAME"