Skip to content

Commit

Permalink
Create a dashboard messaging component to handle dashboard data and b…
Browse files Browse the repository at this point in the history
…roken site report messages
  • Loading branch information
sammacbeth committed Feb 6, 2025
1 parent 53504de commit fbd3cb3
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 99 deletions.
7 changes: 6 additions & 1 deletion shared/js/background/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import DebuggerConnection from './components/debugger-connection';
import Devtools from './components/devtools';
import DNRListeners from './components/dnr-listeners';
import RemoteConfig from './components/remote-config';
import DashboardMessaging from './components/dashboard-messaging';
import initDebugBuild from './devbuild';
import initReloader from './devbuild-reloader';
import tabManager from './tab-manager';
Expand All @@ -51,14 +52,17 @@ const remoteConfig = new RemoteConfig({ settings });
const abnMetrics = BUILD_TARGET !== 'firefox' ? new AbnExperimentMetrics({ remoteConfig }) : null;
const tds = new TDSStorage({ settings, remoteConfig, abnMetrics });
const devtools = new Devtools({ tds });
const dashboardMessaging = new DashboardMessaging({ settings, tds, tabManager })
/**
* @type {{
* autofill: EmailAutofill;
* dashboardMessaging: DashboardMessaging
* omnibox: OmniboxSearch;
* fireButton?: FireButton;
* internalUser: InternalUserDetector;
* tds: TDSStorage;
* tabTracking: TabTracker;
* toggleReports: ToggleReports;
* trackers: TrackersGlobal;
* remoteConfig: RemoteConfig;
* abnMetrics: AbnExperimentMetrics?;
Expand All @@ -67,11 +71,12 @@ const devtools = new Devtools({ tds });
*/
const components = {
autofill: new EmailAutofill({ settings }),
dashboardMessaging,
omnibox: new OmniboxSearch(),
internalUser: new InternalUserDetector({ settings }),
tabTracking: new TabTracker({ tabManager, devtools }),
tds,
toggleReports: new ToggleReports(),
toggleReports: new ToggleReports({ dashboardMessaging }),
trackers: new TrackersGlobal({ tds }),
debugger: new DebuggerConnection({ tds, devtools }),
devtools,
Expand Down
44 changes: 0 additions & 44 deletions shared/js/background/broken-site-report.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,12 @@
* @typedef {import('@duckduckgo/privacy-dashboard/schema/__generated__/schema.types').DataItemId} DisclosureParamId
*/

const browser = require('webextension-polyfill');
const load = require('./load');
const browserWrapper = require('./wrapper');
const settings = require('./settings');
const parseUserAgentString = require('../shared-utils/parse-user-agent-string');
const { getCurrentTab, getURLWithoutQueryString } = require('./utils');
const { getURL } = require('./pixels');
const tdsStorage = require('./storage/tds').default;
const tabManager = require('./tab-manager');
const maxPixelLength = 7000;

/**
Expand Down Expand Up @@ -307,47 +304,6 @@ export async function breakageReportForTab({
return fire(pixelName, brokenSiteParams.toString());
}

/**
* Attempt to send a breakage report for the currently focused tab.
*
* @param {Object} arg
* @prop {string} pixelName
* @prop {import("./classes/tab") | undefined} arg.currentTab
* @prop {string | undefined} arg.category
* @prop {string | undefined} arg.description
* @prop {string | undefined} arg.reportFlow
* String detailing the UI flow that this breakage report came from.
*/
export async function sendBreakageReportForCurrentTab({ pixelName, currentTab, category, description, reportFlow }) {
await settings.ready();
await tdsStorage.ready('config');
// wait for onload callbacks (to ensure that config has been correctly processed)
await tdsStorage.config.allLoadingFinished

const tab = currentTab || (await tabManager.getOrRestoreCurrentTab());
if (!tab) {
return;
}

const pageParams = (await browser.tabs.sendMessage(tab.id, { getBreakagePageParams: true })) || {};

const tds = settings.getSetting('tds-etag');
const remoteConfigEtag = settings.getSetting('config-etag');
const remoteConfigVersion = tdsStorage.config.version;

return await breakageReportForTab({
pixelName,
tab,
tds,
remoteConfigEtag,
remoteConfigVersion,
category,
description,
pageParams,
reportFlow,
});
}

/**
* Returns the breakage report details as expected by the
* "getBreakageFormOptions" and "getToggleReportOptions" messages.
Expand Down
92 changes: 92 additions & 0 deletions shared/js/background/components/dashboard-messaging.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import browser from 'webextension-polyfill';
import { breakageReportForTab, getDisclosureDetails } from '../broken-site-report';
import { dashboardDataFromTab } from '../classes/privacy-dashboard-data';
import { registerMessageHandler } from '../message-handlers';
import { getCurrentTab } from '../utils';
import { isFireButtonEnabled } from './fire-button';

/**
* Message handlers for communication from the dashboard to the extension background.
*
* Note, additional message handles for toggle reports is in the separate ToggleReports component.
*/
export default class DashboardMessaging {
/**
* @param {{
* settings: import('../settings.js');
* tds: import('./tds').default;
* tabManager: import('../tab-manager.js');
* }} args
*/
constructor({ settings, tds, tabManager }) {
this.settings = settings;
this.tds = tds;
this.tabManager = tabManager;

registerMessageHandler('submitBrokenSiteReport', (report) => this.submitBrokenSiteReport(report));
registerMessageHandler('getPrivacyDashboardData', this.getPrivacyDashboardData.bind(this));
registerMessageHandler('getBreakageFormOptions', getDisclosureDetails);
}

/**
* Only the dashboard sends this message, so we import the types from there.
* @param {import('@duckduckgo/privacy-dashboard/schema/__generated__/schema.types').BreakageReportRequest} breakageReport
* @param {string} [pixelName]
* @param {string} [reportFlow]
* @returns {Promise<void>}
*/
async submitBrokenSiteReport(breakageReport, pixelName = 'epbf', reportFlow = undefined) {
// wait for config and TDS so we can get etags and config version
await Promise.all([this.tds.remoteConfig.allLoadingFinished, this.tds.tds.ready]);
const { category, description } = breakageReport;
const tab = await this.tabManager.getOrRestoreCurrentTab();
if (!tab) {
return;
}
const pageParams = (await browser.tabs.sendMessage(tab.id, { getBreakagePageParams: true })) || {};
const tds = this.tds.tds.etag;
const remoteConfigEtag = this.tds.remoteConfig.etag;
const remoteConfigVersion = this.tds.remoteConfig.config?.version;
return breakageReportForTab({
pixelName,
tab,
tds,
remoteConfigEtag,
remoteConfigVersion,
category,
description,
pageParams,
reportFlow,
});
}

/**
* This message is here to ensure the privacy dashboard can render
* from a single call to the extension.
*
* Currently, it will collect data for the current tab and email protection
* user data.
*/
async getPrivacyDashboardData(options) {
let { tabId } = options;
if (tabId === null) {
const currentTab = await getCurrentTab();
if (!currentTab?.id) {
throw new Error('could not get the current tab...');
}
tabId = currentTab?.id;
}

// Await for storage to be ready; this happens on service worker closing mostly.
await this.settings.ready();
await this.tds.config.ready;

const tab = await this.tabManager.getOrRestoreTab(tabId);
if (!tab) throw new Error('unreachable - cannot access current tab with ID ' + tabId);
const userData = this.settings.getSetting('userData');
const fireButtonData = {
enabled: isFireButtonEnabled,
};
return dashboardDataFromTab(tab, userData, fireButtonData);
}
}
20 changes: 14 additions & 6 deletions shared/js/background/components/toggle-reports.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { registerMessageHandler } from '../message-handlers';
import { postPopupMessage } from '../popup-messaging';
import settings from '../settings';
import { getFeatureSettings, reloadCurrentTab, resolveAfterDelay } from '../utils';
import { getDisclosureDetails, sendBreakageReportForCurrentTab } from '../broken-site-report';
import { getDisclosureDetails } from '../broken-site-report';
import { createAlarm } from '../wrapper';
import tabManager from '../tab-manager';

Expand All @@ -23,7 +23,14 @@ import tabManager from '../tab-manager';
export default class ToggleReports {
static ALARM_NAME = 'toggleReportsClearExpired';

constructor() {
/**
*
* @param {{
* dashboardMessaging: import('./dashboard-messaging').default
* }} args
*/
constructor({ dashboardMessaging }) {
this.dashboardMessaging = dashboardMessaging;
this.onDisconnect = this.toggleReportFinished.bind(this, false);

registerMessageHandler('getToggleReportOptions', (_, sender) => this.toggleReportStarted(sender));
Expand Down Expand Up @@ -81,10 +88,11 @@ export default class ToggleReports {
try {
// Send the breakage report before reloading the page, to ensure
// the correct page details are sent with the report.
await sendBreakageReportForCurrentTab({
pixelName: 'protection-toggled-off-breakage-report',
reportFlow: 'on_protections_off_dashboard_main',
});
await this.dashboardMessaging.submitBrokenSiteReport(
{},
'protection-toggled-off-breakage-report',
'on_protections_off_dashboard_main',
);
} catch (e) {
// Catch this, mostly to ensure the page is still reloaded if
// sending the breakage report fails.
Expand Down
47 changes: 0 additions & 47 deletions shared/js/background/message-handlers.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import browser from 'webextension-polyfill';
import { dashboardDataFromTab } from './classes/privacy-dashboard-data';
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';
import { ensureClickToLoadRuleActionDisabled } from './dnr-click-to-load';
import tdsStorage from './storage/tds';
import { getArgumentsObject } from './helpers/arguments-object';
import { isFireButtonEnabled } from './components/fire-button';
import { postPopupMessage } from './popup-messaging';
import ToggleReports from './components/toggle-reports';
const utils = require('./utils');
Expand Down Expand Up @@ -99,47 +96,6 @@ export function openOptions() {
}
}

/**
* Only the dashboard sends this message, so we import the types from there.
* @param {import('@duckduckgo/privacy-dashboard/schema/__generated__/schema.types').BreakageReportRequest} breakageReport
* @returns {Promise<void>}
*/
export function submitBrokenSiteReport(breakageReport) {
const pixelName = 'epbf';
const { category, description } = breakageReport;
return sendBreakageReportForCurrentTab({ pixelName, category, description });
}

/**
* This message is here to ensure the privacy dashboard can render
* from a single call to the extension.
*
* Currently, it will collect data for the current tab and email protection
* user data.
*/
export async function getPrivacyDashboardData(options) {
let { tabId } = options;
if (tabId === null) {
const currentTab = await utils.getCurrentTab();
if (!currentTab?.id) {
throw new Error('could not get the current tab...');
}
tabId = currentTab?.id;
}

// Await for storage to be ready; this happens on service worker closing mostly.
await settings.ready();
await tdsStorage.ready('config');

const tab = await tabManager.getOrRestoreTab(tabId);
if (!tab) throw new Error('unreachable - cannot access current tab with ID ' + tabId);
const userData = settings.getSetting('userData');
const fireButtonData = {
enabled: isFireButtonEnabled,
};
return dashboardDataFromTab(tab, userData, fireButtonData);
}

export function getTopBlockedByPages(options) {
return Companies.getTopBlockedByPages(options);
}
Expand Down Expand Up @@ -344,9 +300,6 @@ const messageHandlers = {
allowlistOptIn,
getBrowser,
openOptions,
submitBrokenSiteReport,
getBreakageFormOptions: getDisclosureDetails,
getPrivacyDashboardData,
getTopBlockedByPages,
getClickToLoadState,
getYouTubeVideoDetails,
Expand Down
3 changes: 2 additions & 1 deletion shared/js/background/storage/tds.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default {
_config: { features: {} },
_tds: { entities: {}, trackers: {}, domains: {}, cnames: {} },
_surrogates: '',
/** @type {import('@duckduckgo/privacy-configuration/schema/config').GenericV4Config} */
get config() {
return globalThis.components?.remoteConfig.config || this._config;
},
Expand Down Expand Up @@ -60,7 +61,7 @@ export default {
return Promise.resolve();
}
if (configName && listNames.includes(configName)) {
return tdsStorage[configName].ready;
return tdsStorage[configName].allLoadingFinished;
}
return Promise.all(listNames.map((n) => tdsStorage[n].ready));
},
Expand Down

0 comments on commit fbd3cb3

Please sign in to comment.