mirror of
https://github.com/gorhill/uBlock.git
synced 2024-10-06 09:37:12 +02:00
Refactoring: Replace DOM events with broadcast channels
Broadcast channels are more suited to uBO than DOM events to dispatch notifications to different parts of uBO. DOM events can only be dispatched to local context, broadcast channels dispatch to all contexts (i.e. background process, workers, auxiliary pages) -- this last behavior is better suited to uBO to communicate internal changes to all potential listeners, not just those in the local context. Additionally, broadcasting to content scripts is now done through tabs.sendMessage() instead of through potentially opened message ports, this simplifies broadcasting to content scripts, and this doesn't require to have long-lived message ports in content scripts.
This commit is contained in:
parent
c4de5552e5
commit
67fb969572
@ -30,13 +30,9 @@
|
|||||||
(( ) => {
|
(( ) => {
|
||||||
// >>>>>>>> start of private namespace
|
// >>>>>>>> start of private namespace
|
||||||
|
|
||||||
if (
|
if ( typeof vAPI !== 'object' ) { return; }
|
||||||
typeof vAPI !== 'object' ||
|
if ( vAPI.messaging instanceof Object === false ) { return; }
|
||||||
vAPI.messaging instanceof Object === false ||
|
if ( vAPI.MessagingConnection instanceof Function ) { return; }
|
||||||
vAPI.MessagingConnection instanceof Function
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const listeners = new Set();
|
const listeners = new Set();
|
||||||
const connections = new Map();
|
const connections = new Map();
|
||||||
@ -247,50 +243,6 @@ vAPI.messaging.extensions.push(vAPI.MessagingConnection);
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
// Broadcast listening ability
|
|
||||||
|
|
||||||
(( ) => {
|
|
||||||
// >>>>>>>> start of private namespace
|
|
||||||
|
|
||||||
if (
|
|
||||||
typeof vAPI !== 'object' ||
|
|
||||||
vAPI.messaging instanceof Object === false ||
|
|
||||||
vAPI.broadcastListener instanceof Object
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const listeners = new Set();
|
|
||||||
|
|
||||||
vAPI.broadcastListener = {
|
|
||||||
add: function(listener) {
|
|
||||||
listeners.add(listener);
|
|
||||||
vAPI.messaging.getPort();
|
|
||||||
},
|
|
||||||
remove: function(listener) {
|
|
||||||
listeners.delete(listener);
|
|
||||||
},
|
|
||||||
canDestroyPort() {
|
|
||||||
return listeners.size === 0;
|
|
||||||
},
|
|
||||||
mustDestroyPort() {
|
|
||||||
listeners.clear();
|
|
||||||
},
|
|
||||||
canProcessMessage(details) {
|
|
||||||
if ( details.broadcast === false ) { return; }
|
|
||||||
for ( const listener of listeners ) {
|
|
||||||
listener(details.msg);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
vAPI.messaging.extensions.push(vAPI.broadcastListener);
|
|
||||||
|
|
||||||
// <<<<<<<< end of private namespace
|
|
||||||
})();
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
|
|
||||||
// For non-background page
|
// For non-background page
|
||||||
|
|
||||||
|
/* globals browser */
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -23,8 +23,9 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { i18n$ } from './i18n.js';
|
import { onBroadcast } from './broadcast.js';
|
||||||
import { dom, qs$ } from './dom.js';
|
import { dom, qs$ } from './dom.js';
|
||||||
|
import { i18n$ } from './i18n.js';
|
||||||
import './codemirror/ubo-static-filtering.js';
|
import './codemirror/ubo-static-filtering.js';
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
@ -310,7 +311,7 @@ dom.on('#userFiltersRevert', 'click', revertChanges);
|
|||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/3704
|
// https://github.com/gorhill/uBlock/issues/3704
|
||||||
// Merge changes to user filters occurring in the background
|
// Merge changes to user filters occurring in the background
|
||||||
vAPI.broadcastListener.add(msg => {
|
onBroadcast(msg => {
|
||||||
switch ( msg.what ) {
|
switch ( msg.what ) {
|
||||||
case 'userFiltersUpdated': {
|
case 'userFiltersUpdated': {
|
||||||
cmEditor.startOperation();
|
cmEditor.startOperation();
|
||||||
|
@ -21,8 +21,9 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { i18n, i18n$ } from './i18n.js';
|
import { onBroadcast } from './broadcast.js';
|
||||||
import { dom, qs$, qsa$ } from './dom.js';
|
import { dom, qs$, qsa$ } from './dom.js';
|
||||||
|
import { i18n, i18n$ } from './i18n.js';
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
@ -35,9 +36,7 @@ let listsetDetails = {};
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const messaging = vAPI.messaging;
|
onBroadcast(msg => {
|
||||||
|
|
||||||
vAPI.broadcastListener.add(msg => {
|
|
||||||
switch ( msg.what ) {
|
switch ( msg.what ) {
|
||||||
case 'assetUpdated':
|
case 'assetUpdated':
|
||||||
updateAssetStatus(msg);
|
updateAssetStatus(msg);
|
||||||
@ -277,7 +276,7 @@ const renderFilterLists = ( ) => {
|
|||||||
renderWidgets();
|
renderWidgets();
|
||||||
};
|
};
|
||||||
|
|
||||||
messaging.send('dashboard', {
|
vAPI.messaging.send('dashboard', {
|
||||||
what: 'getLists',
|
what: 'getLists',
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
onListsReceived(response);
|
onListsReceived(response);
|
||||||
@ -510,7 +509,7 @@ const onPurgeClicked = ev => {
|
|||||||
dom.cl.remove(listLeaf, 'cached');
|
dom.cl.remove(listLeaf, 'cached');
|
||||||
}
|
}
|
||||||
|
|
||||||
messaging.send('dashboard', {
|
vAPI.messaging.send('dashboard', {
|
||||||
what: 'purgeCaches',
|
what: 'purgeCaches',
|
||||||
assetKeys,
|
assetKeys,
|
||||||
});
|
});
|
||||||
@ -534,7 +533,7 @@ dom.on('#lists', 'click', 'span.cache', onPurgeClicked);
|
|||||||
const selectFilterLists = async ( ) => {
|
const selectFilterLists = async ( ) => {
|
||||||
// Cosmetic filtering switch
|
// Cosmetic filtering switch
|
||||||
let checked = qs$('#parseCosmeticFilters').checked;
|
let checked = qs$('#parseCosmeticFilters').checked;
|
||||||
messaging.send('dashboard', {
|
vAPI.messaging.send('dashboard', {
|
||||||
what: 'userSettings',
|
what: 'userSettings',
|
||||||
name: 'parseAllABPHideFilters',
|
name: 'parseAllABPHideFilters',
|
||||||
value: checked,
|
value: checked,
|
||||||
@ -542,7 +541,7 @@ const selectFilterLists = async ( ) => {
|
|||||||
listsetDetails.parseCosmeticFilters = checked;
|
listsetDetails.parseCosmeticFilters = checked;
|
||||||
|
|
||||||
checked = qs$('#ignoreGenericCosmeticFilters').checked;
|
checked = qs$('#ignoreGenericCosmeticFilters').checked;
|
||||||
messaging.send('dashboard', {
|
vAPI.messaging.send('dashboard', {
|
||||||
what: 'userSettings',
|
what: 'userSettings',
|
||||||
name: 'ignoreGenericCosmeticFilters',
|
name: 'ignoreGenericCosmeticFilters',
|
||||||
value: checked,
|
value: checked,
|
||||||
@ -581,7 +580,7 @@ const selectFilterLists = async ( ) => {
|
|||||||
|
|
||||||
hashFromListsetDetails();
|
hashFromListsetDetails();
|
||||||
|
|
||||||
await messaging.send('dashboard', {
|
await vAPI.messaging.send('dashboard', {
|
||||||
what: 'applyFilterListSelection',
|
what: 'applyFilterListSelection',
|
||||||
toSelect,
|
toSelect,
|
||||||
toImport,
|
toImport,
|
||||||
@ -596,7 +595,7 @@ const buttonApplyHandler = async ( ) => {
|
|||||||
dom.cl.add(dom.body, 'working');
|
dom.cl.add(dom.body, 'working');
|
||||||
dom.cl.remove('#lists .listEntry.stickied', 'stickied');
|
dom.cl.remove('#lists .listEntry.stickied', 'stickied');
|
||||||
renderWidgets();
|
renderWidgets();
|
||||||
await messaging.send('dashboard', { what: 'reloadAllFilters' });
|
await vAPI.messaging.send('dashboard', { what: 'reloadAllFilters' });
|
||||||
dom.cl.remove(dom.body, 'working');
|
dom.cl.remove(dom.body, 'working');
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -609,7 +608,7 @@ const buttonUpdateHandler = async ( ) => {
|
|||||||
await selectFilterLists();
|
await selectFilterLists();
|
||||||
dom.cl.add(dom.body, 'updating');
|
dom.cl.add(dom.body, 'updating');
|
||||||
renderWidgets();
|
renderWidgets();
|
||||||
messaging.send('dashboard', { what: 'forceUpdateAssets' });
|
vAPI.messaging.send('dashboard', { what: 'forceUpdateAssets' });
|
||||||
};
|
};
|
||||||
|
|
||||||
dom.on('#buttonUpdate', 'click', ( ) => { buttonUpdateHandler(); });
|
dom.on('#buttonUpdate', 'click', ( ) => { buttonUpdateHandler(); });
|
||||||
@ -617,7 +616,7 @@ dom.on('#buttonUpdate', 'click', ( ) => { buttonUpdateHandler(); });
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const buttonPurgeAllHandler = async hard => {
|
const buttonPurgeAllHandler = async hard => {
|
||||||
await messaging.send('dashboard', {
|
await vAPI.messaging.send('dashboard', {
|
||||||
what: 'purgeAllCaches',
|
what: 'purgeAllCaches',
|
||||||
hard,
|
hard,
|
||||||
});
|
});
|
||||||
@ -630,7 +629,7 @@ dom.on('#buttonPurgeAll', 'click', ev => { buttonPurgeAllHandler(ev.shiftKey); }
|
|||||||
|
|
||||||
const userSettingCheckboxChanged = ( ) => {
|
const userSettingCheckboxChanged = ( ) => {
|
||||||
const target = event.target;
|
const target = event.target;
|
||||||
messaging.send('dashboard', {
|
vAPI.messaging.send('dashboard', {
|
||||||
what: 'userSettings',
|
what: 'userSettings',
|
||||||
name: target.id,
|
name: target.id,
|
||||||
value: target.checked,
|
value: target.checked,
|
||||||
|
@ -23,11 +23,12 @@
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
import cacheStorage from './cachestorage.js';
|
|
||||||
import logger from './logger.js';
|
|
||||||
import µb from './background.js';
|
import µb from './background.js';
|
||||||
|
import { broadcast } from './broadcast.js';
|
||||||
|
import cacheStorage from './cachestorage.js';
|
||||||
import { ubolog } from './console.js';
|
import { ubolog } from './console.js';
|
||||||
import { i18n$ } from './i18n.js';
|
import { i18n$ } from './i18n.js';
|
||||||
|
import logger from './logger.js';
|
||||||
import * as sfp from './static-filtering-parser.js';
|
import * as sfp from './static-filtering-parser.js';
|
||||||
import { orphanizeString, } from './text-utils.js';
|
import { orphanizeString, } from './text-utils.js';
|
||||||
|
|
||||||
@ -1234,7 +1235,7 @@ async function diffUpdater() {
|
|||||||
assetCacheSetDetails(data.assetKey, metadata);
|
assetCacheSetDetails(data.assetKey, metadata);
|
||||||
};
|
};
|
||||||
bc.onmessage = ev => {
|
bc.onmessage = ev => {
|
||||||
const data = ev.data;
|
const data = ev.data || {};
|
||||||
if ( data.what === 'ready' ) {
|
if ( data.what === 'ready' ) {
|
||||||
ubolog('Diff updater: hard updating', toHardUpdate.map(v => v.assetKey).join());
|
ubolog('Diff updater: hard updating', toHardUpdate.map(v => v.assetKey).join());
|
||||||
while ( toHardUpdate.length !== 0 ) {
|
while ( toHardUpdate.length !== 0 ) {
|
||||||
@ -1279,7 +1280,7 @@ async function diffUpdater() {
|
|||||||
} else if ( data.status === 'nopatch-yet' || data.status === 'nodiff' ) {
|
} else if ( data.status === 'nopatch-yet' || data.status === 'nodiff' ) {
|
||||||
ubolog(`Diff updater: skip update of ${data.assetKey} using ${data.patchPath}\n\treason: ${data.status}`);
|
ubolog(`Diff updater: skip update of ${data.assetKey} using ${data.patchPath}\n\treason: ${data.status}`);
|
||||||
assetCacheSetDetails(data.assetKey, { writeTime: data.writeTime });
|
assetCacheSetDetails(data.assetKey, { writeTime: data.writeTime });
|
||||||
vAPI.messaging.broadcast({
|
broadcast({
|
||||||
what: 'assetUpdated',
|
what: 'assetUpdated',
|
||||||
key: data.assetKey,
|
key: data.assetKey,
|
||||||
cached: true,
|
cached: true,
|
||||||
|
75
src/js/broadcast.js
Normal file
75
src/js/broadcast.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* globals browser */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Broadcast a message to all uBO contexts
|
||||||
|
|
||||||
|
let broadcastChannel;
|
||||||
|
|
||||||
|
export function broadcast(message) {
|
||||||
|
if ( broadcastChannel === undefined ) {
|
||||||
|
broadcastChannel = new self.BroadcastChannel('uBO');
|
||||||
|
}
|
||||||
|
broadcastChannel.postMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Broadcast a message to all uBO contexts and all uBO's content scripts
|
||||||
|
|
||||||
|
export async function broadcastToAll(message) {
|
||||||
|
broadcast(message);
|
||||||
|
const tabs = await vAPI.tabs.query({
|
||||||
|
discarded: false,
|
||||||
|
});
|
||||||
|
const bcmessage = Object.assign({ broadcast: true }, message);
|
||||||
|
for ( const tab of tabs ) {
|
||||||
|
browser.tabs.sendMessage(tab.id, bcmessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
export function onBroadcast(listener) {
|
||||||
|
const bc = new self.BroadcastChannel('uBO');
|
||||||
|
bc.onmessage = ev => listener(ev.data || {});
|
||||||
|
return bc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
export function filteringBehaviorChanged(details = {}) {
|
||||||
|
if ( typeof details.direction !== 'number' || details.direction >= 0 ) {
|
||||||
|
filteringBehaviorChanged.throttle.offon(727);
|
||||||
|
}
|
||||||
|
broadcast(Object.assign({ what: 'filteringBehaviorChanged' }, details));
|
||||||
|
}
|
||||||
|
|
||||||
|
filteringBehaviorChanged.throttle = vAPI.defer.create(( ) => {
|
||||||
|
vAPI.net.handlerBehaviorChanged();
|
||||||
|
});
|
||||||
|
|
||||||
|
/******************************************************************************/
|
@ -271,7 +271,7 @@ async function fetchAndApplyAllPatches(assetDetails) {
|
|||||||
const bc = new globalThis.BroadcastChannel('diffUpdater');
|
const bc = new globalThis.BroadcastChannel('diffUpdater');
|
||||||
|
|
||||||
bc.onmessage = ev => {
|
bc.onmessage = ev => {
|
||||||
const message = ev.data;
|
const message = ev.data || {};
|
||||||
switch ( message.what ) {
|
switch ( message.what ) {
|
||||||
case 'update':
|
case 'update':
|
||||||
fetchAndApplyAllPatches(message).then(response => {
|
fetchAndApplyAllPatches(message).then(response => {
|
||||||
|
@ -23,6 +23,10 @@
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
import { broadcastToAll } from './broadcast.js';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
let buffer = null;
|
let buffer = null;
|
||||||
let lastReadTime = 0;
|
let lastReadTime = 0;
|
||||||
let writePtr = 0;
|
let writePtr = 0;
|
||||||
@ -40,7 +44,7 @@ const janitorTimer = vAPI.defer.create(( ) => {
|
|||||||
buffer = null;
|
buffer = null;
|
||||||
writePtr = 0;
|
writePtr = 0;
|
||||||
logger.ownerId = undefined;
|
logger.ownerId = undefined;
|
||||||
vAPI.messaging.broadcast({ what: 'loggerDisabled' });
|
broadcastToAll({ what: 'loggerDisabled' });
|
||||||
});
|
});
|
||||||
|
|
||||||
const boxEntry = function(details) {
|
const boxEntry = function(details) {
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
import publicSuffixList from '../lib/publicsuffixlist/publicsuffixlist.js';
|
import publicSuffixList from '../lib/publicsuffixlist/publicsuffixlist.js';
|
||||||
import punycode from '../lib/punycode.js';
|
import punycode from '../lib/punycode.js';
|
||||||
|
|
||||||
|
import { filteringBehaviorChanged } from './broadcast.js';
|
||||||
import cacheStorage from './cachestorage.js';
|
import cacheStorage from './cachestorage.js';
|
||||||
import cosmeticFilteringEngine from './cosmetic-filtering.js';
|
import cosmeticFilteringEngine from './cosmetic-filtering.js';
|
||||||
import htmlFilteringEngine from './html-filtering.js';
|
import htmlFilteringEngine from './html-filtering.js';
|
||||||
@ -346,7 +347,7 @@ const onMessage = function(request, sender, callback) {
|
|||||||
case 'setWhitelist':
|
case 'setWhitelist':
|
||||||
µb.netWhitelist = µb.whitelistFromString(request.whitelist);
|
µb.netWhitelist = µb.whitelistFromString(request.whitelist);
|
||||||
µb.saveWhitelist();
|
µb.saveWhitelist();
|
||||||
µb.filteringBehaviorChanged();
|
filteringBehaviorChanged();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'toggleHostnameSwitch':
|
case 'toggleHostnameSwitch':
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
import µb from './background.js';
|
import µb from './background.js';
|
||||||
|
import { onBroadcast } from './broadcast.js';
|
||||||
import { redirectEngine as reng } from './redirect-engine.js';
|
import { redirectEngine as reng } from './redirect-engine.js';
|
||||||
import { sessionFirewall } from './filtering-engines.js';
|
import { sessionFirewall } from './filtering-engines.js';
|
||||||
import { StaticExtFilteringHostnameDB } from './static-ext-filtering-db.js';
|
import { StaticExtFilteringHostnameDB } from './static-ext-filtering-db.js';
|
||||||
@ -67,12 +68,10 @@ const contentScriptRegisterer = new (class {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.hostnameToDetails = new Map();
|
this.hostnameToDetails = new Map();
|
||||||
if ( browser.contentScripts === undefined ) { return; }
|
if ( browser.contentScripts === undefined ) { return; }
|
||||||
µb.onEvent('filteringBehaviorChanged', ev => {
|
onBroadcast(msg => {
|
||||||
const details = ev.detail;
|
if ( msg.what !== 'filteringBehaviorChanged' ) { return; }
|
||||||
if ( details instanceof Object ) {
|
if ( msg.direction > 0 ) { return; }
|
||||||
if ( details.direction > 0 ) { return; }
|
if ( msg.hostname ) { return this.flush(msg.hostname); }
|
||||||
if ( details.hostname ) { return this.flush(details.hostname); }
|
|
||||||
}
|
|
||||||
this.reset();
|
this.reset();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
Home: https://github.com/gorhill/uBlock
|
Home: https://github.com/gorhill/uBlock
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* globals browser */
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
@ -28,12 +30,8 @@
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
if (
|
if ( typeof vAPI !== 'object' ) { return; }
|
||||||
typeof vAPI !== 'object' ||
|
if ( vAPI.domWatcher instanceof Object === false ) { return; }
|
||||||
vAPI.domWatcher instanceof Object === false
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const reHasCSSCombinators = /[ >+~]/;
|
const reHasCSSCombinators = /[ >+~]/;
|
||||||
const simpleDeclarativeSet = new Set();
|
const simpleDeclarativeSet = new Set();
|
||||||
@ -51,16 +49,16 @@ const loggedSelectors = new Set();
|
|||||||
|
|
||||||
const rePseudoElements = /:(?::?after|:?before|:[a-z-]+)$/;
|
const rePseudoElements = /:(?::?after|:?before|:[a-z-]+)$/;
|
||||||
|
|
||||||
const hasSelector = function(selector, context = document) {
|
function hasSelector(selector, context = document) {
|
||||||
try {
|
try {
|
||||||
return context.querySelector(selector) !== null;
|
return context.querySelector(selector) !== null;
|
||||||
}
|
}
|
||||||
catch(ex) {
|
catch(ex) {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
}
|
||||||
|
|
||||||
const safeMatchSelector = function(selector, context) {
|
function safeMatchSelector(selector, context) {
|
||||||
const safeSelector = rePseudoElements.test(selector)
|
const safeSelector = rePseudoElements.test(selector)
|
||||||
? selector.replace(rePseudoElements, '')
|
? selector.replace(rePseudoElements, '')
|
||||||
: selector;
|
: selector;
|
||||||
@ -70,9 +68,9 @@ const safeMatchSelector = function(selector, context) {
|
|||||||
catch(ex) {
|
catch(ex) {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
}
|
||||||
|
|
||||||
const safeQuerySelector = function(selector, context = document) {
|
function safeQuerySelector(selector, context = document) {
|
||||||
const safeSelector = rePseudoElements.test(selector)
|
const safeSelector = rePseudoElements.test(selector)
|
||||||
? selector.replace(rePseudoElements, '')
|
? selector.replace(rePseudoElements, '')
|
||||||
: selector;
|
: selector;
|
||||||
@ -82,9 +80,9 @@ const safeQuerySelector = function(selector, context = document) {
|
|||||||
catch(ex) {
|
catch(ex) {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
}
|
||||||
|
|
||||||
const safeGroupSelectors = function(selectors) {
|
function safeGroupSelectors(selectors) {
|
||||||
const arr = Array.isArray(selectors)
|
const arr = Array.isArray(selectors)
|
||||||
? selectors
|
? selectors
|
||||||
: Array.from(selectors);
|
: Array.from(selectors);
|
||||||
@ -93,11 +91,11 @@ const safeGroupSelectors = function(selectors) {
|
|||||||
? s.replace(rePseudoElements, '')
|
? s.replace(rePseudoElements, '')
|
||||||
: s;
|
: s;
|
||||||
}).join(',\n');
|
}).join(',\n');
|
||||||
};
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const processDeclarativeSimple = function(node, out) {
|
function processDeclarativeSimple(node, out) {
|
||||||
if ( simpleDeclarativeSet.size === 0 ) { return; }
|
if ( simpleDeclarativeSet.size === 0 ) { return; }
|
||||||
if ( simpleDeclarativeStr === undefined ) {
|
if ( simpleDeclarativeStr === undefined ) {
|
||||||
simpleDeclarativeStr = safeGroupSelectors(simpleDeclarativeSet);
|
simpleDeclarativeStr = safeGroupSelectors(simpleDeclarativeSet);
|
||||||
@ -120,11 +118,11 @@ const processDeclarativeSimple = function(node, out) {
|
|||||||
simpleDeclarativeStr = undefined;
|
simpleDeclarativeStr = undefined;
|
||||||
loggedSelectors.add(selector);
|
loggedSelectors.add(selector);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const processDeclarativeComplex = function(out) {
|
function processDeclarativeComplex(out) {
|
||||||
if ( complexDeclarativeSet.size === 0 ) { return; }
|
if ( complexDeclarativeSet.size === 0 ) { return; }
|
||||||
if ( complexDeclarativeStr === undefined ) {
|
if ( complexDeclarativeStr === undefined ) {
|
||||||
complexDeclarativeStr = safeGroupSelectors(complexDeclarativeSet);
|
complexDeclarativeStr = safeGroupSelectors(complexDeclarativeSet);
|
||||||
@ -137,7 +135,7 @@ const processDeclarativeComplex = function(out) {
|
|||||||
complexDeclarativeStr = undefined;
|
complexDeclarativeStr = undefined;
|
||||||
loggedSelectors.add(selector);
|
loggedSelectors.add(selector);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
@ -156,7 +154,7 @@ function processProcedural(out) {
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const processExceptions = function(out) {
|
function processExceptions(out) {
|
||||||
if ( exceptionDict.size === 0 ) { return; }
|
if ( exceptionDict.size === 0 ) { return; }
|
||||||
if ( exceptionStr === undefined ) {
|
if ( exceptionStr === undefined ) {
|
||||||
exceptionStr = safeGroupSelectors(exceptionDict.keys());
|
exceptionStr = safeGroupSelectors(exceptionDict.keys());
|
||||||
@ -169,18 +167,18 @@ const processExceptions = function(out) {
|
|||||||
exceptionStr = undefined;
|
exceptionStr = undefined;
|
||||||
loggedSelectors.add(raw);
|
loggedSelectors.add(raw);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const processProceduralExceptions = function(out) {
|
function processProceduralExceptions(out) {
|
||||||
if ( proceduralExceptionDict.size === 0 ) { return; }
|
if ( proceduralExceptionDict.size === 0 ) { return; }
|
||||||
for ( const exception of proceduralExceptionDict.values() ) {
|
for ( const exception of proceduralExceptionDict.values() ) {
|
||||||
if ( exception.test() === false ) { continue; }
|
if ( exception.test() === false ) { continue; }
|
||||||
out.push(`#@#${exception.raw}`);
|
out.push(`#@#${exception.raw}`);
|
||||||
proceduralExceptionDict.delete(exception.raw);
|
proceduralExceptionDict.delete(exception.raw);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
@ -313,9 +311,22 @@ const handlers = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
vAPI.domWatcher.addListener(handlers);
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const shutdown = function() {
|
const broadcastHandler = msg => {
|
||||||
|
if ( msg.what === 'loggerDisabled' ) {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
browser.runtime.onMessage.addListener(broadcastHandler);
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
function shutdown() {
|
||||||
|
browser.runtime.onMessage.removeListener(broadcastHandler);
|
||||||
processTimer.clear();
|
processTimer.clear();
|
||||||
attributeObserver.disconnect();
|
attributeObserver.disconnect();
|
||||||
if ( typeof vAPI !== 'object' ) { return; }
|
if ( typeof vAPI !== 'object' ) { return; }
|
||||||
@ -325,29 +336,7 @@ const shutdown = function() {
|
|||||||
if ( vAPI.domWatcher instanceof Object ) {
|
if ( vAPI.domWatcher instanceof Object ) {
|
||||||
vAPI.domWatcher.removeListener(handlers);
|
vAPI.domWatcher.removeListener(handlers);
|
||||||
}
|
}
|
||||||
if ( vAPI.broadcastListener instanceof Object ) {
|
}
|
||||||
vAPI.broadcastListener.remove(broadcastListener);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
const broadcastListener = msg => {
|
|
||||||
if ( msg.what === 'loggerDisabled' ) {
|
|
||||||
shutdown();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
vAPI.messaging.extend().then(extended => {
|
|
||||||
if ( extended !== true ) {
|
|
||||||
return shutdown();
|
|
||||||
}
|
|
||||||
vAPI.broadcastListener.add(broadcastListener);
|
|
||||||
});
|
|
||||||
|
|
||||||
vAPI.domWatcher.addListener(handlers);
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
@ -39,17 +39,18 @@ import './tab.js';
|
|||||||
import './ublock.js';
|
import './ublock.js';
|
||||||
import './utils.js';
|
import './utils.js';
|
||||||
|
|
||||||
import cacheStorage from './cachestorage.js';
|
|
||||||
import contextMenu from './contextmenu.js';
|
|
||||||
import io from './assets.js';
|
import io from './assets.js';
|
||||||
import lz4Codec from './lz4.js';
|
|
||||||
import staticExtFilteringEngine from './static-ext-filtering.js';
|
|
||||||
import staticFilteringReverseLookup from './reverselookup.js';
|
|
||||||
import staticNetFilteringEngine from './static-net-filtering.js';
|
|
||||||
import µb from './background.js';
|
import µb from './background.js';
|
||||||
import webRequest from './traffic.js';
|
import { filteringBehaviorChanged } from './broadcast.js';
|
||||||
import { redirectEngine } from './redirect-engine.js';
|
import cacheStorage from './cachestorage.js';
|
||||||
import { ubolog } from './console.js';
|
import { ubolog } from './console.js';
|
||||||
|
import contextMenu from './contextmenu.js';
|
||||||
|
import lz4Codec from './lz4.js';
|
||||||
|
import { redirectEngine } from './redirect-engine.js';
|
||||||
|
import staticFilteringReverseLookup from './reverselookup.js';
|
||||||
|
import staticExtFilteringEngine from './static-ext-filtering.js';
|
||||||
|
import staticNetFilteringEngine from './static-net-filtering.js';
|
||||||
|
import webRequest from './traffic.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
permanentFirewall,
|
permanentFirewall,
|
||||||
@ -453,7 +454,7 @@ if ( selfieIsValid !== true ) {
|
|||||||
|
|
||||||
// Flush memory cache -- unsure whether the browser does this internally
|
// Flush memory cache -- unsure whether the browser does this internally
|
||||||
// when loading a new extension.
|
// when loading a new extension.
|
||||||
µb.filteringBehaviorChanged();
|
filteringBehaviorChanged();
|
||||||
|
|
||||||
// Final initialization steps after all needed assets are in memory.
|
// Final initialization steps after all needed assets are in memory.
|
||||||
|
|
||||||
|
@ -28,8 +28,9 @@
|
|||||||
import publicSuffixList from '../lib/publicsuffixlist/publicsuffixlist.js';
|
import publicSuffixList from '../lib/publicsuffixlist/publicsuffixlist.js';
|
||||||
import punycode from '../lib/punycode.js';
|
import punycode from '../lib/punycode.js';
|
||||||
|
|
||||||
import cosmeticFilteringEngine from './cosmetic-filtering.js';
|
|
||||||
import io from './assets.js';
|
import io from './assets.js';
|
||||||
|
import { broadcast, filteringBehaviorChanged, onBroadcast } from './broadcast.js';
|
||||||
|
import cosmeticFilteringEngine from './cosmetic-filtering.js';
|
||||||
import logger from './logger.js';
|
import logger from './logger.js';
|
||||||
import lz4Codec from './lz4.js';
|
import lz4Codec from './lz4.js';
|
||||||
import staticExtFilteringEngine from './static-ext-filtering.js';
|
import staticExtFilteringEngine from './static-ext-filtering.js';
|
||||||
@ -243,7 +244,7 @@ import {
|
|||||||
if ( typeof hs[key] !== typeof hsDefault[key] ) { continue; }
|
if ( typeof hs[key] !== typeof hsDefault[key] ) { continue; }
|
||||||
this.hiddenSettings[key] = hs[key];
|
this.hiddenSettings[key] = hs[key];
|
||||||
}
|
}
|
||||||
this.fireEvent('hiddenSettingsChanged');
|
broadcast({ what: 'hiddenSettingsChanged' });
|
||||||
};
|
};
|
||||||
|
|
||||||
// Note: Save only the settings which values differ from the default ones.
|
// Note: Save only the settings which values differ from the default ones.
|
||||||
@ -259,7 +260,8 @@ import {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
µb.onEvent('hiddenSettingsChanged', ( ) => {
|
onBroadcast(msg => {
|
||||||
|
if ( msg.what !== 'hiddenSettingsChanged' ) { return; }
|
||||||
const µbhs = µb.hiddenSettings;
|
const µbhs = µb.hiddenSettings;
|
||||||
ubologSet(µbhs.consoleLogLevel === 'info');
|
ubologSet(µbhs.consoleLogLevel === 'info');
|
||||||
vAPI.net.setOptions({
|
vAPI.net.setOptions({
|
||||||
@ -391,7 +393,8 @@ import {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
µb.onEvent('hiddenSettingsChanged', ( ) => {
|
onBroadcast(msg => {
|
||||||
|
if ( msg.what !== 'hiddenSettingsChanged' ) { return; }
|
||||||
µb.parsedTrustedListPrefixes = [];
|
µb.parsedTrustedListPrefixes = [];
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -614,9 +617,8 @@ import {
|
|||||||
|
|
||||||
// https://www.reddit.com/r/uBlockOrigin/comments/cj7g7m/
|
// https://www.reddit.com/r/uBlockOrigin/comments/cj7g7m/
|
||||||
// https://www.reddit.com/r/uBlockOrigin/comments/cnq0bi/
|
// https://www.reddit.com/r/uBlockOrigin/comments/cnq0bi/
|
||||||
µb.filteringBehaviorChanged();
|
filteringBehaviorChanged();
|
||||||
|
broadcast({ what: 'userFiltersUpdated' });
|
||||||
vAPI.messaging.broadcast({ what: 'userFiltersUpdated' });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
µb.createUserFilters = function(details) {
|
µb.createUserFilters = function(details) {
|
||||||
@ -852,7 +854,7 @@ import {
|
|||||||
staticExtFilteringEngine.freeze();
|
staticExtFilteringEngine.freeze();
|
||||||
redirectEngine.freeze();
|
redirectEngine.freeze();
|
||||||
vAPI.net.unsuspend();
|
vAPI.net.unsuspend();
|
||||||
µb.filteringBehaviorChanged();
|
filteringBehaviorChanged();
|
||||||
|
|
||||||
vAPI.storage.set({ 'availableFilterLists': µb.availableFilterLists });
|
vAPI.storage.set({ 'availableFilterLists': µb.availableFilterLists });
|
||||||
|
|
||||||
@ -862,7 +864,7 @@ import {
|
|||||||
text: 'Reloading all filter lists: done'
|
text: 'Reloading all filter lists: done'
|
||||||
});
|
});
|
||||||
|
|
||||||
vAPI.messaging.broadcast({
|
broadcast({
|
||||||
what: 'staticFilteringDataChanged',
|
what: 'staticFilteringDataChanged',
|
||||||
parseCosmeticFilters: µb.userSettings.parseAllABPHideFilters,
|
parseCosmeticFilters: µb.userSettings.parseAllABPHideFilters,
|
||||||
ignoreGenericCosmeticFilters: µb.userSettings.ignoreGenericCosmeticFilters,
|
ignoreGenericCosmeticFilters: µb.userSettings.ignoreGenericCosmeticFilters,
|
||||||
@ -1584,10 +1586,10 @@ import {
|
|||||||
} else if ( details.assetKey === 'ublock-badlists' ) {
|
} else if ( details.assetKey === 'ublock-badlists' ) {
|
||||||
this.badLists = new Map();
|
this.badLists = new Map();
|
||||||
}
|
}
|
||||||
vAPI.messaging.broadcast({
|
broadcast({
|
||||||
what: 'assetUpdated',
|
what: 'assetUpdated',
|
||||||
key: details.assetKey,
|
key: details.assetKey,
|
||||||
cached: cached
|
cached,
|
||||||
});
|
});
|
||||||
// https://github.com/gorhill/uBlock/issues/2585
|
// https://github.com/gorhill/uBlock/issues/2585
|
||||||
// Whenever an asset is overwritten, the current selfie is quite
|
// Whenever an asset is overwritten, the current selfie is quite
|
||||||
@ -1598,10 +1600,10 @@ import {
|
|||||||
|
|
||||||
// Update failed.
|
// Update failed.
|
||||||
if ( topic === 'asset-update-failed' ) {
|
if ( topic === 'asset-update-failed' ) {
|
||||||
vAPI.messaging.broadcast({
|
broadcast({
|
||||||
what: 'assetUpdated',
|
what: 'assetUpdated',
|
||||||
key: details.assetKey,
|
key: details.assetKey,
|
||||||
failed: true
|
failed: true,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1625,7 +1627,7 @@ import {
|
|||||||
} else {
|
} else {
|
||||||
this.scheduleAssetUpdater(0);
|
this.scheduleAssetUpdater(0);
|
||||||
}
|
}
|
||||||
vAPI.messaging.broadcast({
|
broadcast({
|
||||||
what: 'assetsUpdated',
|
what: 'assetsUpdated',
|
||||||
assetKeys: details.assetKeys
|
assetKeys: details.assetKeys
|
||||||
});
|
});
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
import { onBroadcast } from './broadcast.js';
|
||||||
import { dom, qs$ } from './dom.js';
|
import { dom, qs$ } from './dom.js';
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
@ -316,7 +317,7 @@ uBlockDashboard.patchCodeMirrorEditor(cmEditor);
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
vAPI.broadcastListener.add(msg => {
|
onBroadcast(msg => {
|
||||||
if ( msg.what === 'assetsUpdated' ) {
|
if ( msg.what === 'assetsUpdated' ) {
|
||||||
dom.cl.remove(dom.body, 'updating');
|
dom.cl.remove(dom.body, 'updating');
|
||||||
dom.cl.add(dom.body, 'updated');
|
dom.cl.add(dom.body, 'updated');
|
||||||
|
@ -23,12 +23,13 @@
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
import contextMenu from './contextmenu.js';
|
|
||||||
import cosmeticFilteringEngine from './cosmetic-filtering.js';
|
|
||||||
import io from './assets.js';
|
import io from './assets.js';
|
||||||
import µb from './background.js';
|
import µb from './background.js';
|
||||||
import { hostnameFromURI } from './uri-utils.js';
|
import { broadcast, filteringBehaviorChanged, onBroadcast } from './broadcast.js';
|
||||||
|
import contextMenu from './contextmenu.js';
|
||||||
|
import cosmeticFilteringEngine from './cosmetic-filtering.js';
|
||||||
import { redirectEngine } from './redirect-engine.js';
|
import { redirectEngine } from './redirect-engine.js';
|
||||||
|
import { hostnameFromURI } from './uri-utils.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
permanentFirewall,
|
permanentFirewall,
|
||||||
@ -147,7 +148,7 @@ const matchBucket = function(url, hostname, bucket, start) {
|
|||||||
}
|
}
|
||||||
bucket.push(directive);
|
bucket.push(directive);
|
||||||
this.saveWhitelist();
|
this.saveWhitelist();
|
||||||
µb.filteringBehaviorChanged({ hostname: targetHostname });
|
filteringBehaviorChanged({ hostname: targetHostname });
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +189,7 @@ const matchBucket = function(url, hostname, bucket, start) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.saveWhitelist();
|
this.saveWhitelist();
|
||||||
µb.filteringBehaviorChanged({ direction: 1 });
|
filteringBehaviorChanged({ direction: 1 });
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -423,7 +424,7 @@ const matchBucket = function(url, hostname, bucket, start) {
|
|||||||
redirectEngine.invalidateResourcesSelfie(io);
|
redirectEngine.invalidateResourcesSelfie(io);
|
||||||
this.loadRedirectResources();
|
this.loadRedirectResources();
|
||||||
}
|
}
|
||||||
this.fireEvent('hiddenSettingsChanged');
|
broadcast({ what: 'hiddenSettingsChanged' });
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
@ -524,7 +525,7 @@ const matchBucket = function(url, hostname, bucket, start) {
|
|||||||
cosmeticFilteringEngine.removeFromSelectorCache(srcHostname, 'net');
|
cosmeticFilteringEngine.removeFromSelectorCache(srcHostname, 'net');
|
||||||
|
|
||||||
// Flush caches
|
// Flush caches
|
||||||
µb.filteringBehaviorChanged({
|
filteringBehaviorChanged({
|
||||||
direction: action === 1 ? 1 : 0,
|
direction: action === 1 ? 1 : 0,
|
||||||
hostname: srcHostname,
|
hostname: srcHostname,
|
||||||
});
|
});
|
||||||
@ -613,7 +614,7 @@ const matchBucket = function(url, hostname, bucket, start) {
|
|||||||
switch ( details.name ) {
|
switch ( details.name ) {
|
||||||
case 'no-scripting':
|
case 'no-scripting':
|
||||||
case 'no-remote-fonts':
|
case 'no-remote-fonts':
|
||||||
µb.filteringBehaviorChanged({
|
filteringBehaviorChanged({
|
||||||
direction: details.state ? 1 : 0,
|
direction: details.state ? 1 : 0,
|
||||||
hostname: details.hostname,
|
hostname: details.hostname,
|
||||||
});
|
});
|
||||||
@ -677,7 +678,10 @@ const matchBucket = function(url, hostname, bucket, start) {
|
|||||||
|
|
||||||
parse();
|
parse();
|
||||||
|
|
||||||
µb.onEvent('hiddenSettingsChanged', ( ) => { parse(); });
|
onBroadcast(msg => {
|
||||||
|
if ( msg.what !== 'hiddenSettingsChanged' ) { return; }
|
||||||
|
parse();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -134,36 +134,6 @@ import µb from './background.js';
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
µb.fireEvent = function(name, details = undefined) {
|
|
||||||
if (
|
|
||||||
self instanceof Object &&
|
|
||||||
self.dispatchEvent instanceof Function &&
|
|
||||||
self.CustomEvent instanceof Function
|
|
||||||
) {
|
|
||||||
self.dispatchEvent(new CustomEvent(name, { detail: details }));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
µb.onEvent = function(name, fn) {
|
|
||||||
if (
|
|
||||||
self instanceof Object &&
|
|
||||||
self.addEventListener instanceof Function
|
|
||||||
) {
|
|
||||||
self.addEventListener(name, fn);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
µb.filteringBehaviorChanged = function(details = {}) {
|
|
||||||
if ( typeof details.direction !== 'number' || details.direction >= 0 ) {
|
|
||||||
vAPI.net.handlerBehaviorChanged();
|
|
||||||
}
|
|
||||||
this.fireEvent('filteringBehaviorChanged', details);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
// TODO: properly compare arrays
|
// TODO: properly compare arrays
|
||||||
|
|
||||||
µb.getModifiedSettings = function(edit, orig = {}) {
|
µb.getModifiedSettings = function(edit, orig = {}) {
|
||||||
|
Loading…
Reference in New Issue
Block a user