diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 4e39f9f49..707d631d8 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -952,6 +952,14 @@ "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", "description": "A paragraph in the filter issue reporter section" }, + "supportS6P2S1": { + "message": "Filter lists are updated daily. Be sure your issue has not already been addressed in the most recent filter lists.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportS6P2S2": { + "message": "Verify that the issue still exists after reloading the problematic webpage.", + "description": "A paragraph in the filter issue reporter section" + }, "supportS6URL": { "message": "Address of the web page:", "description": "Label for the URL of the page" diff --git a/src/css/support.css b/src/css/support.css index d7b014517..30e92dddf 100644 --- a/src/css/support.css +++ b/src/css/support.css @@ -1,3 +1,8 @@ +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + body { margin-bottom: 6rem; } @@ -11,6 +16,7 @@ h3 { } .supportEntry { display: flex; + margin-block: 1em; } :root.mobile .supportEntry { flex-direction: column; @@ -48,6 +54,30 @@ body.filterIssue #moreButton { display: none; } +body:not(.shouldUpdate) .shouldUpdate { + display: none; + } +body.updating { + pointer-events: none; + } +body.updating button { + filter: grayscale(1); + opacity: 0.5; + } +body.updated .shouldUpdate button { + display: none; + } +body.updating .shouldUpdate button .fa-icon svg { + animation: spin 1s linear infinite; + transform-origin: 50%; + } +body .shouldUpdate .updated { + align-self: center; + } +body:not(.updated) .shouldUpdate .updated { + display: none; + } + button { align-self: center; } diff --git a/src/js/assets.js b/src/js/assets.js index 4423f0776..84496c116 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -946,6 +946,29 @@ assets.rmrf = function() { /******************************************************************************/ +assets.getUpdateAges = async function(conditions = {}) { + const assetDict = await assets.metadata(); + const now = Date.now(); + const out = []; + for ( const [ assetKey, asset ] of Object.entries(assetDict) ) { + if ( asset.hasRemoteURL !== true ) { continue; } + const tokens = conditions[asset.content]; + if ( Array.isArray(tokens) === false ) { continue; } + if ( tokens.includes('*') === false ) { + if ( tokens.includes(assetKey) === false ) { continue; } + } + const age = now - (asset.writeTime || 0); + out.push({ + assetKey, + age, + ageNormalized: age / (asset.updateAfter * 86400000), + }); + } + return out; +}; + +/******************************************************************************/ + // Asset updater area. const updaterAssetDelayDefault = 120000; const updaterUpdated = []; diff --git a/src/js/messaging.js b/src/js/messaging.js index 898f15515..00bdf0bfe 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -589,6 +589,53 @@ const getElementCount = async function(tabId, what) { return total; }; +const launchReporter = async function(request) { + const pageStore = µb.pageStoreFromTabId(request.tabId); + if ( pageStore === null ) { return; } + if ( pageStore.hasUnprocessedRequest ) { + request.popupPanel.hasUnprocessedRequest = true; + } + + const entries = await io.getUpdateAges({ + filters: µb.selectedFilterLists.filter( + a => (/^https?:/.test(a) === false) + ) + }); + let shoudUpdateLists = false; + for ( const entry of entries ) { + if ( entry.age < (2 * 60 * 60 * 1000) ) { continue; } + io.purge(entry.assetKey); + shoudUpdateLists = true; + } + + // https://github.com/gorhill/uBlock/commit/6efd8eb#commitcomment-107523558 + // Important: for whatever reason, not using `document_start` causes the + // Promise returned by `tabs.executeScript()` to resolve only when the + // associated tab is closed. + const cosmeticSurveyResults = await vAPI.tabs.executeScript(request.tabId, { + allFrames: true, + file: '/js/scriptlets/cosmetic-report.js', + matchAboutBlank: true, + runAt: 'document_start', + }); + + const filters = cosmeticSurveyResults.reduce((a, v) => { + if ( Array.isArray(v) ) { a.push(...v); } + return a; + }, []); + if ( filters.length !== 0 ) { + request.popupPanel.cosmetic = filters; + } + + const supportURL = new URL(vAPI.getURL('support.html')); + supportURL.searchParams.set('pageURL', request.pageURL); + supportURL.searchParams.set('popupPanel', JSON.stringify(request.popupPanel)); + if ( shoudUpdateLists ) { + supportURL.searchParams.set('shouldUpdate', 1); + } + return supportURL.href; +}; + const onMessage = function(request, sender, callback) { // Async switch ( request.what ) { @@ -610,36 +657,6 @@ const onMessage = function(request, sender, callback) { }); return; - // https://github.com/gorhill/uBlock/commit/6efd8eb#commitcomment-107523558 - // Important: for whatever reason, not using `document_start` causes the - // Promise returned by `tabs.executeScript()` to resolve only when the - // associated tab is closed. - case 'launchReporter': { - const pageStore = µb.pageStoreFromTabId(request.tabId); - if ( pageStore === null ) { break; } - if ( pageStore.hasUnprocessedRequest ) { - request.popupPanel.hasUnprocessedRequest = true; - } - vAPI.tabs.executeScript(request.tabId, { - allFrames: true, - file: '/js/scriptlets/cosmetic-report.js', - matchAboutBlank: true, - runAt: 'document_start', - }).then(results => { - const filters = results.reduce((a, v) => { - if ( Array.isArray(v) ) { a.push(...v); } - return a; - }, []); - if ( filters.length !== 0 ) { - request.popupPanel.cosmetic = filters; - } - const supportURL = new URL(vAPI.getURL('support.html')); - supportURL.searchParams.set('pageURL', request.pageURL); - supportURL.searchParams.set('popupPanel', JSON.stringify(request.popupPanel)); - µb.openNewTab({ url: supportURL.href, select: true, index: -1 }); - }); - return; - } default: break; } @@ -659,6 +676,15 @@ const onMessage = function(request, sender, callback) { response = lastModified !== request.contentLastModified; break; } + + case 'launchReporter': { + launchReporter(request).then(url => { + if ( typeof url !== 'string' ) { return; } + µb.openNewTab({ url, select: true, index: -1 }); + }); + break; + } + case 'revertFirewallRules': // TODO: use Set() to message around sets of hostnames sessionFirewall.copyRules( diff --git a/src/js/scriptlets/cosmetic-report.js b/src/js/scriptlets/cosmetic-report.js index 94988a770..402b13ebd 100644 --- a/src/js/scriptlets/cosmetic-report.js +++ b/src/js/scriptlets/cosmetic-report.js @@ -30,6 +30,7 @@ if ( typeof vAPI !== 'object' ) { return; } if ( typeof vAPI.domFilterer !== 'object' ) { return; } +if ( vAPI.domFilterer === null ) { return; } /******************************************************************************/ diff --git a/src/js/storage.js b/src/js/storage.js index ccb6566bb..6fcd53e3e 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -1470,18 +1470,12 @@ self.addEventListener('hiddenSettingsChanged', ( ) => { // Respect cooldown period before launching an emergency update. const timeSinceLastEmergencyUpdate = (now - lastEmergencyUpdate) / 3600000; if ( timeSinceLastEmergencyUpdate > 1 ) { - const assetDict = await io.metadata(); - for ( const [ assetKey, asset ] of Object.entries(assetDict) ) { - if ( asset.hasRemoteURL !== true ) { continue; } - if ( asset.content === 'filters' ) { - if ( µb.selectedFilterLists.includes(assetKey) === false ) { - continue; - } - } - if ( asset.obsolete !== true ) { continue; } - const lastUpdateInDays = (now - asset.writeTime) / 86400000; - const daysSinceVeryObsolete = lastUpdateInDays - 2 * asset.updateAfter; - if ( daysSinceVeryObsolete < 0 ) { continue; } + const entries = await io.getUpdateAges({ + filters: µb.selectedFilterLists, + internal: [ '*' ], + }); + for ( const entry of entries ) { + if ( entry.ageNormalized < 2 ) { continue; } needEmergencyUpdate = true; lastEmergencyUpdate = now; break; diff --git a/src/js/support.js b/src/js/support.js index c144d6383..9bd275781 100644 --- a/src/js/support.js +++ b/src/js/support.js @@ -27,8 +27,6 @@ import { dom, qs$ } from './dom.js'; /******************************************************************************/ -let supportData; - const uselessKeys = [ 'modifiedHiddenSettings.benchmarkDatasetURL', 'modifiedHiddenSettings.blockingProfiles', @@ -138,7 +136,10 @@ function addDetailsToReportURL(id, collapse = false) { dom.attr(elem, 'data-url', url); } -function showData() { +async function showSupportData() { + const supportData = await vAPI.messaging.send('dashboard', { + what: 'getSupportData', + }); const shownData = JSON.parse(JSON.stringify(supportData)); uselessKeys.forEach(prop => { removeKey(shownData, prop); }); const redacted = true; @@ -196,6 +197,9 @@ const reportedPage = (( ) => { dom.text(option, parsedURL.href); select.append(option); } + if ( url.searchParams.get('shouldUpdate') !== null ) { + dom.cl.add(dom.body, 'shouldUpdate'); + } dom.cl.add(dom.body, 'filterIssue'); return { hostname: parsedURL.hostname.replace(/^(m|mobile|www)\./, ''), @@ -210,7 +214,7 @@ function reportSpecificFilterType() { return qs$('select[name="type"]').value; } -function reportSpecificFilterIssue(ev) { +function reportSpecificFilterIssue() { const githubURL = new URL('https://github.com/uBlockOrigin/uAssets/issues/new?template=specific_report_from_ubo.yml'); const issueType = reportSpecificFilterType(); let title = `${reportedPage.hostname}: ${issueType}`; @@ -228,9 +232,25 @@ function reportSpecificFilterIssue(ev) { what: 'gotoURL', details: { url: githubURL.href, select: true, index: -1 }, }); - ev.preventDefault(); } +async function updateFilterLists() { + dom.cl.add(dom.body, 'updating'); + vAPI.messaging.send('dashboard', { what: 'forceUpdateAssets' }); +} + +vAPI.broadcastListener.add(msg => { + switch ( msg.what ) { + case 'assetsUpdated': + showSupportData(); + dom.cl.remove(dom.body, 'updating'); + dom.cl.add(dom.body, 'updated'); + break; + default: + break; + } +}); + /******************************************************************************/ const cmEditor = new CodeMirror(qs$('#supportData'), { @@ -244,11 +264,7 @@ uBlockDashboard.patchCodeMirrorEditor(cmEditor); /******************************************************************************/ (async ( ) => { - supportData = await vAPI.messaging.send('dashboard', { - what: 'getSupportData', - }); - - showData(); + await showSupportData(); dom.on('[data-url]', 'click', ev => { const elem = ev.target.closest('[data-url]'); @@ -262,8 +278,16 @@ uBlockDashboard.patchCodeMirrorEditor(cmEditor); }); if ( reportedPage !== null ) { + if ( dom.cl.has(dom.body, 'shouldUpdate') ) { + dom.on('.shouldUpdate button', 'click', ev => { + updateFilterLists(); + ev.preventDefault(); + }); + } + dom.on('[data-i18n="supportReportSpecificButton"]', 'click', ev => { - reportSpecificFilterIssue(ev); + reportSpecificFilterIssue(); + ev.preventDefault(); }); dom.on('[data-i18n="supportFindSpecificButton"]', 'click', ev => { diff --git a/src/support.html b/src/support.html index 77faaca7b..fc7a3e70f 100644 --- a/src/support.html +++ b/src/support.html @@ -10,6 +10,7 @@ + @@ -62,34 +63,41 @@

+

+
+
+

_

+ + _ +
-
-

-

-

+
+

-
-

-
- -

-
- -

- -

+
+

+
+ +

+

+
+ +

+

+ +


@@ -109,9 +117,11 @@ + +