From 4a92f96206c2fba138c7323c3c4c9524ef71fa26 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 23 Apr 2023 13:45:11 -0400 Subject: [PATCH] Offer opportunity to update filter lists before reporting issue Related discussion: - https://github.com/uBlockOrigin/uBlock-issues/discussions/2582 If there exist any built-in filter list which last update time is older than 2 hours, the "Report a filter issue" page will ask the user to update their filter lists then verify that the issue still exists. Once filter lists are updated, the troubleshooting information will reflect the change in update time. --- src/_locales/en/messages.json | 8 +++ src/css/support.css | 30 ++++++++++ src/js/assets.js | 23 ++++++++ src/js/messaging.js | 86 ++++++++++++++++++---------- src/js/scriptlets/cosmetic-report.js | 1 + src/js/storage.js | 18 ++---- src/js/support.js | 46 +++++++++++---- src/support.html | 58 +++++++++++-------- 8 files changed, 193 insertions(+), 77 deletions(-) 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 @@ + +