From 3c73f70f122af394a695f7c3b58feb1adf7749d4 Mon Sep 17 00:00:00 2001 From: Dave Vandyke Date: Thu, 19 Dec 2024 19:37:27 +0000 Subject: [PATCH] Add getBreakageFormOptions message handler The privacy-dashboard is going to be sending a new message "getBreakageFormOptions", which is very similar to "getToggleReportOptions" except for use by the standard breakage report UI flow. Let's add that now, taking care to share the logic between those message handlers. --- shared/js/background/broken-site-report.js | 74 ++++++++++++++++++- .../background/components/toggle-reports.js | 67 +---------------- shared/js/background/message-handlers.js | 3 +- unit-test/background/broken-site-report.js | 59 +++++++++++++++ 4 files changed, 135 insertions(+), 68 deletions(-) create mode 100644 unit-test/background/broken-site-report.js diff --git a/shared/js/background/broken-site-report.js b/shared/js/background/broken-site-report.js index 3e4e3f524..69532f952 100644 --- a/shared/js/background/broken-site-report.js +++ b/shared/js/background/broken-site-report.js @@ -5,6 +5,9 @@ * tooling for those anonymous broken site reports. * * Learn more at: https://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/#remotely-configured-exceptions + * + * @typedef {import('@duckduckgo/privacy-dashboard/schema/__generated__/schema.types').ToggleReportScreen} DisclosureDetails + * @typedef {import('@duckduckgo/privacy-dashboard/schema/__generated__/schema.types').DataItemId} DisclosureParamId */ const browser = require('webextension-polyfill'); @@ -12,12 +15,49 @@ const load = require('./load'); const browserWrapper = require('./wrapper'); const settings = require('./settings'); const parseUserAgentString = require('../shared-utils/parse-user-agent-string'); -const { getURLWithoutQueryString } = require('./utils'); +const { getCurrentTab, getURLWithoutQueryString } = require('./utils'); const { getURL } = require('./pixels'); const tdsStorage = require('./storage/tds').default; const tabManager = require('./tab-manager'); const maxPixelLength = 7000; +/** + * When the user clicks to see what a breakage report will include, the details + * displayed are based on these param IDs. + * + * Notes: + * - The naming system is similar, but not quite the same as the breakage + * report parameter names themselves. See the docs[1] for a list of all the + * possible values. + * - Take care to update this list as the privacy-dashboard dependency is + * updated, and when breakage parameters are added/removed. + * - The UI displays the parameters in the order the IDs are listed here, so + * consider the ordering when adjusting the array. + * + * TODO: In the future, it would be better for the UI to accept all of the + * actual parameter names instead. Needing to update the list here + * manually seems error-prone. Likewise with the ordering, it would be + * better for the UI to decide the display order for the parameters, + * to ensure they are displayed consistently between platforms. + * + * 1 - https://duckduckgo.github.io/privacy-dashboard/documents/Guides.Toggle_Report.html#md:appendix-data-disclosure-item-ids-and-their-meanings + * + * @type {DisclosureParamId[]} + */ +const PARAM_IDS = [ + 'siteUrl', + 'atb', + 'errorDescriptions', + 'extensionVersion', + 'features', + 'httpErrorCodes', + 'jsPerformance', + 'locale', + 'openerContext', + 'requests', + 'userRefreshCount', +]; + /** * * Fire a pixel @@ -225,9 +265,8 @@ export async function breakageReportForTab({ const jsPerformance = pageParams.jsPerformance ? pageParams.jsPerformance : undefined; const locale = tab.locale; - // Note: Take care to update the `ToggleReports.PARAM_IDS` array (see - // './components/toggle-reports.js') when adding/removing breakage - // parameters! + // Note: Take care to update the `PARAM_IDS` array (see above) when + // adding/removing breakage parameters! const brokenSiteParams = new URLSearchParams({ siteUrl, tds, @@ -306,3 +345,30 @@ export async function sendBreakageReportForCurrentTab({ pixelName, currentTab, c reportFlow, }); } + +/** + * Returns the breakage report details as expected by the + * "getBreakageFormOptions" and "getToggleReportOptions" messages. + * + * @returns {Promise} + */ +export async function getDisclosureDetails() { + let siteUrl = null; + const currentTabUrl = (await getCurrentTab())?.url; + if (currentTabUrl) { + siteUrl = getURLWithoutQueryString(currentTabUrl); + } + + /** @type {DisclosureDetails} */ + const response = { data: [] }; + + for (const paramId of PARAM_IDS) { + if (paramId === 'siteUrl' && siteUrl) { + response.data.push({ id: 'siteUrl', additional: { url: siteUrl } }); + } else { + response.data.push({ id: paramId }); + } + } + + return response; +} diff --git a/shared/js/background/components/toggle-reports.js b/shared/js/background/components/toggle-reports.js index b6fe7b2fd..4586517ff 100644 --- a/shared/js/background/components/toggle-reports.js +++ b/shared/js/background/components/toggle-reports.js @@ -1,14 +1,9 @@ -/** - * @typedef {import('@duckduckgo/privacy-dashboard/schema/__generated__/schema.types').ToggleReportScreen} ToggleReportOptions - * @typedef {import('@duckduckgo/privacy-dashboard/schema/__generated__/schema.types').DataItemId} ToggleReportParamId - */ - import browser from 'webextension-polyfill'; import { registerMessageHandler } from '../message-handlers'; import { postPopupMessage } from '../popupMessaging'; import settings from '../settings'; -import { getCurrentTab, getFeatureSettings, getURLWithoutQueryString, reloadCurrentTab, resolveAfterDelay } from '../utils'; -import { sendBreakageReportForCurrentTab } from '../broken-site-report'; +import { getFeatureSettings, reloadCurrentTab, resolveAfterDelay } from '../utils'; +import { getDisclosureDetails, sendBreakageReportForCurrentTab } from '../broken-site-report'; import { createAlarm } from '../wrapper'; import tabManager from '../tab-manager'; @@ -28,43 +23,6 @@ import tabManager from '../tab-manager'; export default class ToggleReports { static ALARM_NAME = 'toggleReportsClearExpired'; - /** - * When prompting the user to submit a breakage report, context is shown to - * explain what will be included in the report. This is to help the user - * make an informed decision. That context is based on this list of - * parameter IDs. The naming system is similar, but not quite the same as - * the breakage report parameter names themselves. See the docs[1] for a - * list of all the possible values. Take care to update this list as the - * privacy-dashboard dependency is updated, and when breakage parameters are - * added/removed (see '../broken-site-report.js'). - * - * Note: The UI displays the parameters in the order the IDs are listed - * here, so consider the ordering when adjusting the array. - * - * TODO: In the future, it would be better for the UI to accept all of the - * actual parameter names instead. Needing to update the list here - * manually seems error-prone. Likewise with the ordering, it would be - * better for the UI to decide the display order for the parameters, - * to ensure they are displayed consistently between platforms. - * - * 1 - https://duckduckgo.github.io/privacy-dashboard/modules/Toggle_Report.html - * - * @type {ToggleReportParamId[]} - */ - static PARAM_IDS = [ - 'siteUrl', - 'atb', - 'errorDescriptions', - 'extensionVersion', - 'features', - 'httpErrorCodes', - 'jsPerformance', - 'locale', - 'openerContext', - 'requests', - 'userRefreshCount', - ]; - constructor() { this.onDisconnect = this.toggleReportFinished.bind(this, false); @@ -90,7 +48,7 @@ export default class ToggleReports { * UI flow begins. * * @param {browser.Runtime.Port} sender - * @returns {Promise} + * @returns {Promise} */ async toggleReportStarted(sender) { // If the browser closes the popup UI during the "toggle reports" flow @@ -98,24 +56,7 @@ export default class ToggleReports { // this event will fire. sender?.onDisconnect?.addListener(this.onDisconnect); - let siteUrl = null; - const currentTabUrl = (await getCurrentTab())?.url; - if (currentTabUrl) { - siteUrl = getURLWithoutQueryString(currentTabUrl); - } - - /** @type {ToggleReportOptions} */ - const response = { data: [] }; - - for (const paramId of ToggleReports.PARAM_IDS) { - if (paramId === 'siteUrl' && siteUrl) { - response.data.push({ id: 'siteUrl', additional: { url: siteUrl } }); - } else { - response.data.push({ id: paramId }); - } - } - - return response; + return getDisclosureDetails(); } /** diff --git a/shared/js/background/message-handlers.js b/shared/js/background/message-handlers.js index 8dee923ea..25accfbbe 100644 --- a/shared/js/background/message-handlers.js +++ b/shared/js/background/message-handlers.js @@ -1,6 +1,6 @@ import browser from 'webextension-polyfill'; import { dashboardDataFromTab } from './classes/privacy-dashboard-data'; -import { sendBreakageReportForCurrentTab } from './broken-site-report'; +import { getDisclosureDetails, sendBreakageReportForCurrentTab } from './broken-site-report'; import parseUserAgentString from '../shared-utils/parse-user-agent-string'; import { getExtensionURL } from './wrapper'; import { isFeatureEnabled, reloadCurrentTab } from './utils'; @@ -345,6 +345,7 @@ const messageHandlers = { getBrowser, openOptions, submitBrokenSiteReport, + getBreakageFormOptions: getDisclosureDetails, getPrivacyDashboardData, getTopBlockedByPages, getClickToLoadState, diff --git a/unit-test/background/broken-site-report.js b/unit-test/background/broken-site-report.js new file mode 100644 index 000000000..800617591 --- /dev/null +++ b/unit-test/background/broken-site-report.js @@ -0,0 +1,59 @@ +import browser from 'webextension-polyfill'; +import { getDisclosureDetails } from '../../shared/js/background/broken-site-report'; + +describe('broke-site-report', () => { + let currentTabDetails = null; + + beforeAll(async () => { + // Stub the necessary browser.tabs.* APIs. + spyOn(browser.tabs, 'query').and.callFake(() => { + const result = []; + + if (currentTabDetails) { + result.push(currentTabDetails); + } + + return Promise.resolve(result); + }); + }); + + beforeEach(() => { + currentTabDetails = null; + }); + + it('getDisclosureDetails()', async () => { + expect(await getDisclosureDetails()).toEqual({ + data: [ + { id: 'siteUrl' }, + { id: 'atb' }, + { id: 'errorDescriptions' }, + { id: 'extensionVersion' }, + { id: 'features' }, + { id: 'httpErrorCodes' }, + { id: 'jsPerformance' }, + { id: 'locale' }, + { id: 'openerContext' }, + { id: 'requests' }, + { id: 'userRefreshCount' }, + ], + }); + + currentTabDetails = { url: 'https://domain.example/path?param=value' }; + + expect(await getDisclosureDetails()).toEqual({ + data: [ + { id: 'siteUrl', additional: { url: 'https://domain.example/path' } }, + { id: 'atb' }, + { id: 'errorDescriptions' }, + { id: 'extensionVersion' }, + { id: 'features' }, + { id: 'httpErrorCodes' }, + { id: 'jsPerformance' }, + { id: 'locale' }, + { id: 'openerContext' }, + { id: 'requests' }, + { id: 'userRefreshCount' }, + ], + }); + }); +});