diff --git a/.prettierignore b/.prettierignore index 161611c702..7aa3708cf6 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,5 +7,6 @@ injected/src/features/Scriptlets **/*.json **/*.md **/*.html +!injected/integration-test/test-pages/infra/pages/conditional-matching.html **/*.har **/*.css diff --git a/injected/README.md b/injected/README.md index f9d3cd5486..1d584d4b0d 100644 --- a/injected/README.md +++ b/injected/README.md @@ -188,6 +188,30 @@ Everything within `integration-test` is integration tests controlled by Playwrig npm run test-int ``` +**Running specific tests:** + +To run a specific test or test suite, you can use the `--grep` flag to filter tests by name: + +```shell +# Run tests containing "Test infra" in their name +npx playwright test pages.spec.js --grep "Test infra" + +# Run tests containing "Conditional frame matching" in their name +npx playwright test pages.spec.js --grep "Conditional frame matching" + +# Run tests in headed mode (shows browser window) +npx playwright test pages.spec.js --grep "Test infra" --headed +``` + +**Debugging tests:** + +For debugging, you can run tests in headed mode and add debugging output: + +```shell +# Run with browser visible and debugging enabled +npx playwright test pages.spec.js --grep "Test infra" --headed --debug +``` + #### Feature Build process To produce all artefacts that are used by platforms, just run the `npm run build` command. diff --git a/injected/entry-points/integration.js b/injected/entry-points/integration.js index c7aaf755dd..90cbb3c618 100644 --- a/injected/entry-points/integration.js +++ b/injected/entry-points/integration.js @@ -1,21 +1,9 @@ import { load, init } from '../src/content-scope-features.js'; import { TestTransportConfig } from '../../messaging/index.js'; -function getTopLevelURL() { - try { - // FROM: https://stackoverflow.com/a/7739035/73479 - // FIX: Better capturing of top level URL so that trackers in embedded documents are not considered first party - if (window.location !== window.parent.location) { - return new URL(window.location.href !== 'about:blank' ? document.referrer : window.parent.location.href); - } else { - return new URL(window.location.href); - } - } catch (error) { - return new URL(location.href); - } -} +import { getTabUrl } from '../src/utils.js'; function generateConfig() { - const topLevelUrl = getTopLevelURL(); + const topLevelUrl = getTabUrl(); return { debug: false, sessionKey: 'randomVal', @@ -35,8 +23,8 @@ function generateConfig() { }, ], site: { - domain: topLevelUrl.hostname, - url: topLevelUrl.href, + domain: topLevelUrl?.hostname || '', + url: topLevelUrl?.href || '', isBroken: false, allowlisted: false, enabledFeatures: [ @@ -86,7 +74,7 @@ function mergeDeep(target, ...sources) { } async function initCode() { - const topLevelUrl = getTopLevelURL(); + const topLevelUrl = getTabUrl(); const processedConfig = generateConfig(); // mock Messaging and allow for tests to intercept them @@ -116,7 +104,7 @@ async function initCode() { // mark this phase as loaded setStatus('loaded'); - if (!topLevelUrl.searchParams.has('wait-for-init-args')) { + if (!topLevelUrl?.searchParams.has('wait-for-init-args')) { await init(processedConfig); setStatus('initialized'); return; @@ -128,11 +116,14 @@ async function initCode() { async (evt) => { // @ts-expect-error https://app.asana.com/0/1201614831475344/1203979574128023/f const merged = mergeDeep(processedConfig, evt.detail); + // @ts-expect-error https://app.asana.com/0/1201614831475344/1203979574128023/f + window.__testContentScopeArgs = merged; // init features await init(merged); // set status to initialized so that tests can resume setStatus('initialized'); + document.dispatchEvent(new CustomEvent('content-scope-init-completed')); }, { once: true }, ); diff --git a/injected/integration-test/test-pages/infra/config/conditional-matching.json b/injected/integration-test/test-pages/infra/config/conditional-matching.json index 00a588de61..8827b2b573 100644 --- a/injected/integration-test/test-pages/infra/config/conditional-matching.json +++ b/injected/integration-test/test-pages/infra/config/conditional-matching.json @@ -13,6 +13,22 @@ "type": "number", "value": 222 } + }, + "testApi1": { + "type": "descriptor", + "getterValue": { + "type": "number", + "value": 100 + }, + "define": true + }, + "testApi2": { + "type": "descriptor", + "getterValue": { + "type": "number", + "value": 200 + }, + "define": true } }, "conditionalChanges": [ @@ -27,11 +43,39 @@ "value": 333 } ] + }, + { + "condition": { + "context": { + "top": true + } + }, + "patchSettings": [ + { + "op": "replace", + "path": "/apiChanges/testApi1/getterValue/value", + "value": 43339 + } + ] + }, + { + "condition": { + "context": { + "frame": true + } + }, + "patchSettings": [ + { + "op": "replace", + "path": "/apiChanges/testApi1/getterValue/value", + "value": 43338 + } + ] } ] } } }, "unprotectedTemporary": [] - } +} \ No newline at end of file diff --git a/injected/integration-test/test-pages/infra/pages/conditional-matching.html b/injected/integration-test/test-pages/infra/pages/conditional-matching.html index 7b11d615c3..b4b6182942 100644 --- a/injected/integration-test/test-pages/infra/pages/conditional-matching.html +++ b/injected/integration-test/test-pages/infra/pages/conditional-matching.html @@ -1,64 +1,204 @@ - + - - - - Conditional Matching - - - - -

[Infra]

- -

This page verifies that APIs get modified

- - +

[Infra]

+ +

This page verifies that APIs get modified

+ + - + + // eslint-disable-next-line no-undef + renderResults(); + + diff --git a/injected/src/config-feature.js b/injected/src/config-feature.js index 06fd6bee4a..9057173c48 100644 --- a/injected/src/config-feature.js +++ b/injected/src/config-feature.js @@ -119,6 +119,9 @@ export default class ConfigFeature { * @property {object} [experiment] * @property {string} [experiment.experimentName] * @property {string} [experiment.cohort] + * @property {object} [context] + * @property {boolean} [context.frame] - true if the condition applies to frames + * @property {boolean} [context.top] - true if the condition applies to the top frame */ /** @@ -144,6 +147,7 @@ export default class ConfigFeature { /** @type {Record boolean>} */ const conditionChecks = { domain: this._matchDomainConditional, + context: this._matchContextConditional, urlPattern: this._matchUrlPatternConditional, experiment: this._matchExperimentConditional, minSupportedVersion: this._matchMinSupportedVersion, @@ -208,6 +212,23 @@ export default class ConfigFeature { }); } + /** + * Takes a condition block and returns true if the current context matches the context. + * @param {ConditionBlock} conditionBlock + * @returns {boolean} + */ + _matchContextConditional(conditionBlock) { + if (!conditionBlock.context) return false; + const isFrame = window.self !== window.top; + if (conditionBlock.context.frame && isFrame) { + return true; + } + if (conditionBlock.context.top && !isFrame) { + return true; + } + return false; + } + /** * Takes a condtion block and returns true if the current url matches the urlPattern. * @param {ConditionBlock} conditionBlock diff --git a/injected/src/features.js b/injected/src/features.js index f156d62f27..76ad8fc383 100644 --- a/injected/src/features.js +++ b/injected/src/features.js @@ -59,6 +59,6 @@ export const platformSupport = { ], firefox: ['cookie', ...baseFeatures, 'clickToLoad'], chrome: ['cookie', ...baseFeatures, 'clickToLoad'], - 'chrome-mv3': ['cookie', ...baseFeatures, 'clickToLoad'], + 'chrome-mv3': ['cookie', ...baseFeatures, 'clickToLoad', 'webCompat'], integration: [...baseFeatures, ...otherFeatures], }; diff --git a/injected/src/features/web-compat.js b/injected/src/features/web-compat.js index 6741b44b22..8e68a7706c 100644 --- a/injected/src/features/web-compat.js +++ b/injected/src/features/web-compat.js @@ -126,7 +126,7 @@ export class WebCompat extends ContentFeature { if (this.getFeatureSettingEnabled('modifyCookies')) { this.modifyCookies(); } - if (this.getFeatureSettingEnabled('disableDeviceEnumeration') || this.getFeatureSettingEnabled('disableDeviceEnumerationFrames')) { + if (this.getFeatureSettingEnabled('disableDeviceEnumeration')) { this.preventDeviceEnumeration(); } } @@ -761,21 +761,12 @@ export class WebCompat extends ContentFeature { if (!window.MediaDevices) { return; } - let disableDeviceEnumeration = false; - const isFrame = window.self !== window.top; - if (isFrame) { - disableDeviceEnumeration = this.getFeatureSettingEnabled('disableDeviceEnumerationFrames'); - } else { - disableDeviceEnumeration = this.getFeatureSettingEnabled('disableDeviceEnumeration'); - } - if (disableDeviceEnumeration) { - const enumerateDevicesProxy = new DDGProxy(this, MediaDevices.prototype, 'enumerateDevices', { - apply() { - return Promise.resolve([]); - }, - }); - enumerateDevicesProxy.overload(); - } + const enumerateDevicesProxy = new DDGProxy(this, MediaDevices.prototype, 'enumerateDevices', { + apply() { + return Promise.resolve([]); + }, + }); + enumerateDevicesProxy.overload(); } }