Skip to content

Commit

Permalink
Add ability to update lists through links with specifically crafted URLs
Browse files Browse the repository at this point in the history
As per discussion with uBO volunteers.

Volunteers offering support for uBO will be able to craft links with
specially formed URLs, which once clicked will cause uBO to automatically
force an update of specified filter lists.

The URL must be crafted as shown in the example below:

https://ublockorigin.github.io/uAssets/update-lists.html?listkeys=ublock-filters,easylist

Where the `listkeys` parameter is a comma-separated list of tokens
corresponding to filter lists. If a token does not match an enabled
filter list, it will be ignored.

The ability to update filter lists through a specially crafted link
is available only on uBO's own support sites:

- https://github.com/uBlockOrigin/
- https://reddit.com/r/uBlockOrigin/
- https://ublockorigin.github.io/

Additionally, a visual cue has been added in the "Filter lists" pane
to easily spot the filter lists which have been recently updated, where
"recently" is currently defined as less than an hour ago.
  • Loading branch information
gorhill committed Oct 14, 2023
1 parent 17d3034 commit 0325dcd
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 5 deletions.
12 changes: 12 additions & 0 deletions platform/chromium/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@
],
"run_at": "document_idle",
"all_frames": false
},
{
"matches": [
"https://github.com/uBlockOrigin/*",
"https://ublockorigin.github.io/*",
"https://*.reddit.com/r/uBlockOrigin/*"
],
"js": [
"/js/scriptlets/updater.js"
],
"run_at": "document_idle",
"all_frames": false
}
],
"content_security_policy": "script-src 'self'; object-src 'self'",
Expand Down
4 changes: 4 additions & 0 deletions src/css/3p-filters.css
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ body.working #actions button {
#lists .listEntry.checked.cached:not(.obsolete) > .detailbar .iconbar .cache {
display: inline-flex;
}
#lists .listEntry.cached.recent:not(.obsolete) > .detailbar .iconbar .cache {
color: var(--dashboard-happy-green);
fill: var(--dashboard-happy-green);
}
#lists .iconbar .obsolete {
color: var(--info2-ink);
fill: var(--info2-ink);
Expand Down
3 changes: 3 additions & 0 deletions src/css/themes/default.css
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
--green-40: 84 255 189;
--green-50: 63 225 176;
--green-60: 42 195 162;
--green-65: 21 165 149;
--green-70: 0 135 135;
--green-80: 0 94 94;
--ink-10: 57 52 115;
Expand Down Expand Up @@ -239,6 +240,8 @@
--dashboard-tab-focus-surface-rgb: var(--primary-90);
--dashboard-highlight-surface-rgb: var(--primary-90);

--dashboard-happy-green: rgb(var(--green-65));

/* popup panel */
--popup-cell-cname-ink: #0054d7; /* h260 S:100 Luv:40 */;
--popup-cell-label-mixed-surface: #c29100; /* TODO: fix */
Expand Down
14 changes: 10 additions & 4 deletions src/js/3p-filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { dom, qs$, qsa$ } from './dom.js';
const lastUpdateTemplateString = i18n$('3pLastUpdate');
const obsoleteTemplateString = i18n$('3pExternalListObsolete');
const reValidExternalList = /^[a-z-]+:\/\/(?:\S+\/\S*|\/\S+)/m;
const recentlyUpdated = 1 * 60 * 60 * 1000; // 1 hour

let listsetDetails = {};

Expand Down Expand Up @@ -154,6 +155,8 @@ const renderFilterLists = ( ) => {
if ( asset.cached === true ) {
dom.cl.add(listEntry, 'cached');
dom.attr(qs$(listEntry, ':scope > .detailbar .status.cache'), 'title', lastUpdateString);
const timeSinceLastUpdate = Date.now() - asset.writeTime;
dom.cl.toggle(listEntry, 'recent', timeSinceLastUpdate < recentlyUpdated);
} else {
dom.cl.remove(listEntry, 'cached');
}
Expand Down Expand Up @@ -308,7 +311,7 @@ const updateAssetStatus = details => {
dom.attr(qs$(listEntry, '.status.cache'), 'title',
lastUpdateTemplateString.replace('{{ago}}', i18n.renderElapsedTimeToString(Date.now()))
);

dom.cl.add(listEntry, 'recent');
}
updateAncestorListNodes(listEntry, ancestor => {
updateListNode(ancestor);
Expand Down Expand Up @@ -413,7 +416,8 @@ const updateListNode = listNode => {
let totalFilterCount = 0;
let isCached = false;
let isObsolete = false;
let writeTime = 0;
let latestWriteTime = 0;
let oldestWriteTime = Number.MAX_SAFE_INTEGER;
for ( const listLeaf of checkedListLeaves ) {
const listkey = listLeaf.dataset.key;
const listDetails = listsetDetails.available[listkey];
Expand All @@ -422,7 +426,8 @@ const updateListNode = listNode => {
const assetCache = listsetDetails.cache[listkey] || {};
isCached = isCached || dom.cl.has(listLeaf, 'cached');
isObsolete = isObsolete || dom.cl.has(listLeaf, 'obsolete');
writeTime = Math.max(writeTime, assetCache.writeTime || 0);
latestWriteTime = Math.max(latestWriteTime, assetCache.writeTime || 0);
oldestWriteTime = Math.min(oldestWriteTime, assetCache.writeTime || Number.MAX_SAFE_INTEGER);
}
dom.cl.toggle(listNode, 'checked', checkedListLeaves.length !== 0);
dom.cl.toggle(qs$(listNode, ':scope > .detailbar .checkbox'),
Expand All @@ -449,8 +454,9 @@ const updateListNode = listNode => {
dom.cl.toggle(listNode, 'obsolete', isObsolete);
if ( isCached ) {
dom.attr(qs$(listNode, ':scope > .detailbar .cache'), 'title',
lastUpdateTemplateString.replace('{{ago}}', i18n.renderElapsedTimeToString(writeTime))
lastUpdateTemplateString.replace('{{ago}}', i18n.renderElapsedTimeToString(latestWriteTime))
);
dom.cl.toggle(listNode, 'recent', (Date.now() - oldestWriteTime) < recentlyUpdated);
}
if ( qs$(listNode, '.listEntry.isDefault') !== null ) {
dom.cl.add(listNode, 'isDefault');
Expand Down
10 changes: 9 additions & 1 deletion src/js/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,18 @@ if ( self.location.hash.slice(1) === 'no-dashboard.html' ) {
dom.on('.tabButton', 'click', onTabClickHandler);

// https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
dom.on(window, 'beforeunload', ( ) => {
dom.on(self, 'beforeunload', ( ) => {
if ( discardUnsavedData(true) ) { return; }
event.preventDefault();
event.returnValue = '';
});

// https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
dom.on(self, 'hashchange', ( ) => {
const pane = self.location.hash.slice(1);
if ( pane === '' ) { return; }
loadDashboardPanel(pane);
});

}
})();
15 changes: 15 additions & 0 deletions src/js/messaging.js
Original file line number Diff line number Diff line change
Expand Up @@ -2057,6 +2057,21 @@ const onMessage = function(request, sender, callback) {
});
break;

case 'updateLists':
const listkeys = request.listkeys.split(',').filter(s => s !== '');
if ( listkeys.length === 0 ) { return; }
for ( const listkey of listkeys ) {
io.purge(listkey);
io.remove(`compiled/${listkey}`);
}
µb.scheduleAssetUpdater(0);
µb.openNewTab({
url: 'dashboard.html#3p-filters.html',
select: true,
});
io.updateStart({ delay: 100 });
break;

default:
return vAPI.messaging.UNHANDLED;
}
Expand Down
99 changes: 99 additions & 0 deletions src/js/scriptlets/updater.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*******************************************************************************
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
*/

/* global HTMLDocument */

'use strict';

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

// Injected into specific webpages, those which have been pre-selected
// because they are known to contain `https://ublockorigin.github.io/update-lists?` links.

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

(( ) => {
// >>>>> start of local scope

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

if ( document instanceof HTMLDocument === false ) { return; }

// Maybe uBO has gone away meanwhile.
if ( typeof vAPI !== 'object' || vAPI === null ) { return; }

function updateStockLists(target) {
if ( vAPI instanceof Object === false ) {
document.removeEventListener('click', updateStockLists);
return;
}
try {
const updateURL = new URL(target.href);
if ( updateURL.hostname !== 'ublockorigin.github.io') { return; }
if ( updateURL.pathname !== '/uAssets/update-lists.html') { return; }
const listkeys = updateURL.searchParams.get('listkeys') || '';
if ( listkeys === '' ) { return true; }
vAPI.messaging.send('scriptlets', {
what: 'updateLists',
listkeys,
});
return true;
} catch (_) {
}
}

// https://github.com/easylist/EasyListHebrew/issues/89
// Ensure trusted events only.

document.addEventListener('click', ev => {
if ( ev.button !== 0 || ev.isTrusted === false ) { return; }
const target = ev.target.closest('a');
if ( target instanceof HTMLAnchorElement === false ) { return; }
if ( updateStockLists(target) === true ) {
ev.stopPropagation();
ev.preventDefault();
}
});

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

// <<<<< end of local scope
})();








/*******************************************************************************
DO NOT:
- Remove the following code
- Add code beyond the following code
Reason:
- https://github.com/gorhill/uBlock/pull/3721
- uBO never uses the return value from injected content scripts
**/

void 0;

0 comments on commit 0325dcd

Please sign in to comment.