Skip to content

Commit

Permalink
[mv3] Fix procedural operator matches-media()
Browse files Browse the repository at this point in the history
The failure was caused by the fact that there is no
window.matchMedia() API available in Nodejs. The validation
is now done using cssTree.
  • Loading branch information
gorhill committed Sep 27, 2022
1 parent ec83127 commit 51c2e22
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 161 deletions.
22 changes: 9 additions & 13 deletions platform/mv3/extension/js/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ async function loadRulesetConfig() {
return;
}

const match = /^\|\|example.invalid\/([^\/]+)\/(?:([^\/]+)\/)?/.exec(
const match = /^\|\|(?:example|ubolite)\.invalid\/([^\/]+)\/(?:([^\/]+)\/)?/.exec(
configRule.condition.urlFilter
);
if ( match === null ) { return; }
Expand Down Expand Up @@ -101,7 +101,7 @@ async function saveRulesetConfig() {

const version = rulesetConfig.version;
const enabledRulesets = encodeURIComponent(rulesetConfig.enabledRulesets.join(' '));
const urlFilter = `||example.invalid/${version}/${enabledRulesets}/`;
const urlFilter = `||ubolite.invalid/${version}/${enabledRulesets}/`;
if ( urlFilter === configRule.condition.urlFilter ) { return; }
configRule.condition.urlFilter = urlFilter;

Expand All @@ -115,26 +115,26 @@ async function saveRulesetConfig() {

async function hasGreatPowers(origin) {
return browser.permissions.contains({
origins: [ `${origin}/*` ]
origins: [ `${origin}/*` ],
});
}

function grantGreatPowers(hostname) {
return browser.permissions.request({
origins: [
`*://${hostname}/*`,
]
origins: [ `*://${hostname}/*` ],
});
}

function revokeGreatPowers(hostname) {
return browser.permissions.remove({
origins: [
`*://${hostname}/*`,
]
origins: [ `*://${hostname}/*` ],
});
}

function onPermissionsChanged() {
registerInjectable();
}

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

function onMessage(request, sender, callback) {
Expand Down Expand Up @@ -213,10 +213,6 @@ function onMessage(request, sender, callback) {
}
}

async function onPermissionsChanged() {
await registerInjectable();
}

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

async function start() {
Expand Down
32 changes: 24 additions & 8 deletions platform/mv3/extension/js/ruleset-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,26 +205,42 @@ async function defaultRulesetsFromLanguage() {
async function enableRulesets(ids) {
const afterIds = new Set(ids);
const beforeIds = new Set(await dnr.getEnabledRulesets());
const enableRulesetIds = [];
const disableRulesetIds = [];
const enableRulesetSet = new Set();
const disableRulesetSet = new Set();
for ( const id of afterIds ) {
if ( beforeIds.has(id) ) { continue; }
enableRulesetIds.push(id);
enableRulesetSet.add(id);
}
for ( const id of beforeIds ) {
if ( afterIds.has(id) ) { continue; }
disableRulesetIds.push(id);
disableRulesetSet.add(id);
}


if ( enableRulesetSet.size === 0 && disableRulesetSet.size === 0 ) {
return;
}

// Be sure the rulesets to enable/disable do exist in the current version,
// otherwise the API throws.
const rulesetDetails = await getRulesetDetails();
for ( const id of enableRulesetSet ) {
if ( rulesetDetails.has(id) ) { continue; }
enableRulesetSet.delete(id);
}
for ( const id of disableRulesetSet ) {
if ( rulesetDetails.has(id) ) { continue; }
disableRulesetSet.delete(id);
}
const enableRulesetIds = Array.from(enableRulesetSet);
const disableRulesetIds = Array.from(disableRulesetSet);

if ( enableRulesetIds.length !== 0 ) {
console.info(`Enable rulesets: ${enableRulesetIds}`);
}
if ( disableRulesetIds.length !== 0 ) {
console.info(`Disable ruleset: ${disableRulesetIds}`);
}
if ( enableRulesetIds.length !== 0 || disableRulesetIds.length !== 0 ) {
return dnr.updateEnabledRulesets({ enableRulesetIds, disableRulesetIds });
}
return dnr.updateEnabledRulesets({ enableRulesetIds, disableRulesetIds });
}

/******************************************************************************/
Expand Down
40 changes: 19 additions & 21 deletions platform/mv3/extension/js/scripting-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

import { browser, dnr } from './ext.js';
import { fetchJSON } from './fetch.js';
import { matchesTrustedSiteDirective } from './trusted-sites.js';
import { getAllTrustedSiteDirectives } from './trusted-sites.js';

import {
parsedURLromOrigin,
Expand Down Expand Up @@ -94,7 +94,6 @@ const arrayEq = (a, b) => {
const toRegisterable = (fname, entry) => {
const directive = {
id: fname,
allFrames: true,
};
if ( entry.matches ) {
directive.matches = matchesFromHostnames(entry.matches);
Expand Down Expand Up @@ -173,37 +172,36 @@ async function getInjectableCount(origin) {

async function registerInjectable() {

if ( browser.scripting === undefined ) { return false; }

const [
hostnames,
trustedSites,
rulesetIds,
registered,
scriptingDetails,
] = await Promise.all([
browser.permissions.getAll(),
getAllTrustedSiteDirectives(),
dnr.getEnabledRulesets(),
browser.scripting.getRegisteredContentScripts(),
getScriptingDetails(),
]).then(results => {
results[0] = new Map(
hostnamesFromMatches(results[0].origins).map(hn => [ hn, false ])
);
results[0] = new Set(hostnamesFromMatches(results[0].origins));
results[1] = new Set(results[1]);
return results;
});

if ( hostnames.has('*') && hostnames.size > 1 ) {
hostnames.clear();
hostnames.set('*', false);
}

await Promise.all(
Array.from(hostnames.keys()).map(
hn => matchesTrustedSiteDirective({ hostname: hn })
.then(trusted => hostnames.set(hn, trusted))
)
);

const toRegister = new Map();

const isTrustedHostname = hn => {
while ( hn ) {
if ( trustedSites.has(hn) ) { return true; }
hn = toBroaderHostname(hn);
}
return false;
};

const checkMatches = (details, hn) => {
let fids = details.matches?.get(hn);
if ( fids === undefined ) { return; }
Expand All @@ -222,9 +220,9 @@ async function registerInjectable() {
for ( const rulesetId of rulesetIds ) {
const details = scriptingDetails.get(rulesetId);
if ( details === undefined ) { continue; }
for ( let [ hn, trusted ] of hostnames ) {
if ( trusted ) { continue; }
while ( hn !== '' ) {
for ( let hn of hostnames ) {
if ( isTrustedHostname(hn) ) { continue; }
while ( hn ) {
checkMatches(details, hn);
hn = toBroaderHostname(hn);
}
Expand All @@ -251,7 +249,7 @@ async function registerInjectable() {
const details = scriptingDetails.get(rulesetId);
if ( details === undefined ) { continue; }
for ( let hn of hostnames.keys() ) {
while ( hn !== '' ) {
while ( hn ) {
checkExcludeMatches(details, hn);
hn = toBroaderHostname(hn);
}
Expand Down
12 changes: 11 additions & 1 deletion platform/mv3/extension/js/trusted-sites.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ import {

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

async function getAllTrustedSiteDirectives() {
const dynamicRuleMap = await getDynamicRules();
const rule = dynamicRuleMap.get(TRUSTED_DIRECTIVE_BASE_RULE_ID);
if ( rule === undefined ) { return []; }
return rule.condition.requestDomains;
}

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

async function matchesTrustedSiteDirective(details) {
const hostname =
details.hostname ||
Expand All @@ -47,7 +56,7 @@ async function matchesTrustedSiteDirective(details) {
if ( hostname === undefined ) { return false; }

const dynamicRuleMap = await getDynamicRules();
let rule = dynamicRuleMap.get(TRUSTED_DIRECTIVE_BASE_RULE_ID);
const rule = dynamicRuleMap.get(TRUSTED_DIRECTIVE_BASE_RULE_ID);
if ( rule === undefined ) { return false; }

const domainSet = new Set(rule.condition.requestDomains);
Expand Down Expand Up @@ -155,6 +164,7 @@ async function toggleTrustedSiteDirective(details) {
/******************************************************************************/

export {
getAllTrustedSiteDirectives,
matchesTrustedSiteDirective,
toggleTrustedSiteDirective,
};
62 changes: 32 additions & 30 deletions platform/mv3/make-rulesets.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,35 +74,6 @@ const uidint32 = (s) => {

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

const isUnsupported = rule =>
rule._error !== undefined;

const isRegex = rule =>
rule.condition !== undefined &&
rule.condition.regexFilter !== undefined;

const isRedirect = rule =>
rule.action !== undefined &&
rule.action.type === 'redirect' &&
rule.action.redirect.extensionPath !== undefined;

const isCsp = rule =>
rule.action !== undefined &&
rule.action.type === 'modifyHeaders';

const isRemoveparam = rule =>
rule.action !== undefined &&
rule.action.type === 'redirect' &&
rule.action.redirect.transform !== undefined;

const isGood = rule =>
isUnsupported(rule) === false &&
isRedirect(rule) === false &&
isCsp(rule) === false &&
isRemoveparam(rule) === false;

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

const stdOutput = [];

const log = (text, silent = false) => {
Expand Down Expand Up @@ -217,6 +188,35 @@ async function fetchAsset(assetDetails) {

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

const isUnsupported = rule =>
rule._error !== undefined;

const isRegex = rule =>
rule.condition !== undefined &&
rule.condition.regexFilter !== undefined;

const isRedirect = rule =>
rule.action !== undefined &&
rule.action.type === 'redirect' &&
rule.action.redirect.extensionPath !== undefined;

const isCsp = rule =>
rule.action !== undefined &&
rule.action.type === 'modifyHeaders';

const isRemoveparam = rule =>
rule.action !== undefined &&
rule.action.type === 'redirect' &&
rule.action.redirect.transform !== undefined;

const isGood = rule =>
isUnsupported(rule) === false &&
isRedirect(rule) === false &&
isCsp(rule) === false &&
isRemoveparam(rule) === false;

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

async function processNetworkFilters(assetDetails, network) {
const replacer = (k, v) => {
if ( k.startsWith('__') ) { return; }
Expand Down Expand Up @@ -993,7 +993,9 @@ async function main() {
);

// Log results
await fs.writeFile(`${outputDir}/log.txt`, stdOutput.join('\n') + '\n');
const logContent = stdOutput.join('\n') + '\n';
await fs.writeFile(`${outputDir}/log.txt`, logContent);
await fs.writeFile(`${cacheDir}/log.txt`, logContent);
}

main();
Expand Down
27 changes: 25 additions & 2 deletions platform/mv3/scriptlets/css-specific-procedural.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@ class PSelectorTask {
}
}

class PSelectorVoidTask extends PSelectorTask {
constructor(task) {
super();
console.info(`uBO: :${task[0]}() operator does not exist`);
}
transpose() {
}
}

class PSelectorHasTextTask extends PSelectorTask {
constructor(task) {
super();
Expand Down Expand Up @@ -120,6 +129,19 @@ class PSelectorMatchesCSSTask extends PSelectorTask {
}
}
}
class PSelectorMatchesCSSAfterTask extends PSelectorMatchesCSSTask {
constructor(task) {
super(task);
this.pseudo = '::after';
}
}

class PSelectorMatchesCSSBeforeTask extends PSelectorMatchesCSSTask {
constructor(task) {
super(task);
this.pseudo = '::before';
}
}

class PSelectorMatchesMediaTask extends PSelectorTask {
constructor(task) {
Expand Down Expand Up @@ -370,6 +392,8 @@ class PSelector {
[ 'if', PSelectorIfTask ],
[ 'if-not', PSelectorIfNotTask ],
[ 'matches-css', PSelectorMatchesCSSTask ],
[ 'matches-css-after', PSelectorMatchesCSSAfterTask ],
[ 'matches-css-before', PSelectorMatchesCSSBeforeTask ],
[ 'matches-media', PSelectorMatchesMediaTask ],
[ 'matches-path', PSelectorMatchesPathTask ],
[ 'min-text-length', PSelectorMinTextLengthTask ],
Expand All @@ -387,8 +411,7 @@ class PSelector {
const tasks = [];
if ( Array.isArray(o.tasks) === false ) { return; }
for ( const task of o.tasks ) {
const ctor = this.operatorToTaskMap.get(task[0]);
if ( ctor === undefined ) { return; }
const ctor = this.operatorToTaskMap.get(task[0]) || PSelectorVoidTask;
tasks.push(new ctor(task));
}
// Initialize only after all tasks have been successfully instantiated
Expand Down
Loading

0 comments on commit 51c2e22

Please sign in to comment.