From def92d1b8e9d373e2f6f27c366d578d97d8960c6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 15 May 2024 05:34:50 +0200 Subject: Merging upstream version 126.0. Signed-off-by: Daniel Baumann --- .../browser/network/browser_navigationEvents.js | 10 ++ .../actors/MarionetteCommandsChild.sys.mjs | 2 +- remote/marionette/browser.sys.mjs | 19 +-- remote/marionette/driver.sys.mjs | 17 --- remote/shared/Prompt.sys.mjs | 43 ++----- remote/shared/listeners/PromptListener.sys.mjs | 11 +- .../test/browser/browser_handle_command_retry.js | 4 +- .../browser_session_data_browser_element.js | 2 +- .../messagehandler/test/xpcshell/test_Errors.js | 2 +- remote/shared/webdriver/Capabilities.sys.mjs | 5 +- .../modules/root/browsingContext.sys.mjs | 24 ---- remote/webdriver-bidi/modules/root/network.sys.mjs | 81 +++++++++++-- remote/webdriver-bidi/modules/root/session.sys.mjs | 37 +++--- remote/webdriver-bidi/modules/root/storage.sys.mjs | 129 ++++++++++++++++----- .../modules/windowglobal/browsingContext.sys.mjs | 14 +-- 15 files changed, 229 insertions(+), 171 deletions(-) (limited to 'remote') diff --git a/remote/cdp/test/browser/network/browser_navigationEvents.js b/remote/cdp/test/browser/network/browser_navigationEvents.js index 57680c2a57..f120731158 100644 --- a/remote/cdp/test/browser/network/browser_navigationEvents.js +++ b/remote/cdp/test/browser/network/browser_navigationEvents.js @@ -76,6 +76,12 @@ add_task(async function eventsForTopFrameNavigation({ client }) { "The same loaderId is used for dependent responses (Bug 1637838)" ); is(scriptResponse.response.url, FRAMESET_JS_URL, "Got the Script response"); + + // The priority header only appears when the urgency and incremental values + // are not both default values (u=3 and i=false). In this case the scriptRequest.request.headers + // has no priority header and scriptResponse.response.requestHeaders does, hence we delete. + delete scriptResponse.response.requestHeaders.priority; + Assert.deepEqual( scriptResponse.response.requestHeaders, scriptRequest.request.headers, @@ -125,6 +131,10 @@ add_task(async function eventsForTopFrameNavigation({ client }) { subscriptResponse.loaderId === subdocRequest.loaderId, "The same loaderId is used for dependent responses (Bug 1637838)" ); + + // see comment above + delete subscriptResponse.response.requestHeaders.priority; + Assert.deepEqual( subscriptResponse.response.requestHeaders, subscriptRequest.request.headers, diff --git a/remote/marionette/actors/MarionetteCommandsChild.sys.mjs b/remote/marionette/actors/MarionetteCommandsChild.sys.mjs index 078612da56..d454e03fb0 100644 --- a/remote/marionette/actors/MarionetteCommandsChild.sys.mjs +++ b/remote/marionette/actors/MarionetteCommandsChild.sys.mjs @@ -284,7 +284,7 @@ export class MarionetteCommandsChild extends JSWindowActorChild { const accessible = await lazy.accessibility.getAccessible(elem); if (!accessible) { - return null; + return ""; } // If name is null (absent), expose the empty string. diff --git a/remote/marionette/browser.sys.mjs b/remote/marionette/browser.sys.mjs index d9a867fac5..397bb75a1e 100644 --- a/remote/marionette/browser.sys.mjs +++ b/remote/marionette/browser.sys.mjs @@ -144,23 +144,6 @@ browser.Context = class { }; } - /** - * Retrieves the current tabmodal UI object. According to the browser - * associated with the currently selected tab. - */ - getTabModal() { - let br = this.contentBrowser; - if (!br.hasAttribute("tabmodalPromptShowing")) { - return null; - } - - // The modal is a direct sibling of the browser element. - // See tabbrowser.xml's getTabModalPromptBox. - let modalElements = br.parentNode.getElementsByTagName("tabmodalprompt"); - - return br.tabModalPromptBox.getPrompt(modalElements[0]); - } - /** * Close the current window. * @@ -254,7 +237,7 @@ browser.Context = class { tab = await lazy.TabManager.addTab({ focus, window: this.window }); } else if (lazy.AppInfo.isFirefox) { const opened = new lazy.EventPromise(this.window, "TabOpen"); - this.window.BrowserOpenTab({ url: "about:blank" }); + this.window.BrowserCommands.openTab({ url: "about:blank" }); await opened; tab = this.tabBrowser.selectedTab; diff --git a/remote/marionette/driver.sys.mjs b/remote/marionette/driver.sys.mjs index f4642e756e..6b5b1cf082 100644 --- a/remote/marionette/driver.sys.mjs +++ b/remote/marionette/driver.sys.mjs @@ -3346,23 +3346,6 @@ GeckoDriver.prototype.setPermission = async function (cmd) { const { descriptor, state, oneRealm = false } = cmd.parameters; const browsingContext = lazy.assert.open(this.getBrowsingContext()); - // XXX: WPT should not have these but currently they do and we pass testing pref to - // pass them, see bug 1875837. - if ( - ["clipboard-read", "clipboard-write"].includes(descriptor.name) && - state === "granted" - ) { - if ( - Services.prefs.getBoolPref("dom.events.testing.asyncClipboard", false) - ) { - // Okay, do nothing. The clipboard module will work without permission. - return; - } - throw new lazy.error.UnsupportedOperationError( - "setPermission: expected dom.events.testing.asyncClipboard to be set" - ); - } - // XXX: We currently depend on camera/microphone tests throwing UnsupportedOperationError, // the fix is ongoing in bug 1609427. if (["camera", "microphone"].includes(descriptor.name)) { diff --git a/remote/shared/Prompt.sys.mjs b/remote/shared/Prompt.sys.mjs index bacf24c5d1..39292d4bfd 100644 --- a/remote/shared/Prompt.sys.mjs +++ b/remote/shared/Prompt.sys.mjs @@ -42,7 +42,7 @@ modal.findPrompt = function (context) { win.opener === context.window ) { lazy.logger.trace("Found open window modal prompt"); - return new modal.Dialog(() => context, win); + return new modal.Dialog(win); } } @@ -51,7 +51,7 @@ modal.findPrompt = function (context) { if (geckoViewPrompts.length) { lazy.logger.trace("Found open GeckoView prompt"); const prompt = geckoViewPrompts[0]; - return new modal.Dialog(() => context, prompt); + return new modal.Dialog(prompt); } } @@ -65,7 +65,7 @@ modal.findPrompt = function (context) { let dialogs = contentBrowser.tabDialogBox.getTabDialogManager().dialogs; if (dialogs.length) { lazy.logger.trace("Found open tab modal prompt"); - return new modal.Dialog(() => context, dialogs[0].frameContentWindow); + return new modal.Dialog(dialogs[0].frameContentWindow); } dialogs = contentBrowser.tabDialogBox.getContentDialogManager().dialogs; @@ -74,36 +74,23 @@ modal.findPrompt = function (context) { // gets lazily added. If it's not set yet, ignore the dialog for now. if (dialogs.length && dialogs[0].frameContentWindow.Dialog) { lazy.logger.trace("Found open content prompt"); - return new modal.Dialog(() => context, dialogs[0].frameContentWindow); + return new modal.Dialog(dialogs[0].frameContentWindow); } } - - // If no modal dialog has been found yet, check for old non SubDialog based - // content modal dialogs. Even with those deprecated in Firefox 89 we should - // keep supporting applications that don't have them implemented yet. - if (contentBrowser?.tabModalPromptBox) { - const prompts = contentBrowser.tabModalPromptBox.listPrompts(); - if (prompts.length) { - lazy.logger.trace("Found open old-style content prompt"); - return new modal.Dialog(() => context, null); - } - } - return null; }; /** * Represents a modal dialog. * - * @param {function(): browser.Context} curBrowserFn - * Function that returns the current |browser.Context|. * @param {DOMWindow} dialog * DOMWindow of the dialog. */ modal.Dialog = class { - constructor(curBrowserFn, dialog) { - this.curBrowserFn_ = curBrowserFn; - this.win_ = Cu.getWeakReference(dialog); + #win; + + constructor(dialog) { + this.#win = Cu.getWeakReference(dialog); } get args() { @@ -114,10 +101,6 @@ modal.Dialog = class { return tm ? tm.args : null; } - get curBrowser_() { - return this.curBrowserFn_(); - } - get isOpen() { if (lazy.AppInfo.isAndroid) { return this.window !== null; @@ -136,11 +119,7 @@ modal.Dialog = class { } get tabModal() { - let win = this.window; - if (win) { - return win.Dialog; - } - return this.curBrowser_.getTabModal(); + return this.window?.Dialog; } get promptType() { @@ -164,8 +143,8 @@ modal.Dialog = class { * it is currently attached to the DOM. */ get window() { - if (this.win_) { - let win = this.win_.get(); + if (this.#win) { + let win = this.#win.get(); if (win && (lazy.AppInfo.isAndroid || win.parent)) { return win; } diff --git a/remote/shared/listeners/PromptListener.sys.mjs b/remote/shared/listeners/PromptListener.sys.mjs index e04c766970..0e14409051 100644 --- a/remote/shared/listeners/PromptListener.sys.mjs +++ b/remote/shared/listeners/PromptListener.sys.mjs @@ -172,7 +172,7 @@ export class PromptListener { } this.emit("opened", { contentBrowser: curBrowser.contentBrowser, - prompt: new lazy.modal.Dialog(() => curBrowser, subject), + prompt: new lazy.modal.Dialog(subject), }); break; @@ -190,7 +190,6 @@ export class PromptListener { // the selected tab. const tab = tabBrowser.selectedTab; const contentBrowser = lazy.TabManager.getBrowserForTab(tab); - const window = lazy.TabManager.getWindowForTab(tab); // Do not send the event if the curBrowser is specified, // and it's different from prompt browser. @@ -200,13 +199,7 @@ export class PromptListener { this.emit("opened", { contentBrowser, - prompt: new lazy.modal.Dialog( - () => ({ - contentBrowser, - window, - }), - prompt - ), + prompt: new lazy.modal.Dialog(prompt), }); return; } diff --git a/remote/shared/messagehandler/test/browser/browser_handle_command_retry.js b/remote/shared/messagehandler/test/browser/browser_handle_command_retry.js index 1d020397e1..815990079b 100644 --- a/remote/shared/messagehandler/test/browser/browser_handle_command_retry.js +++ b/remote/shared/messagehandler/test/browser/browser_handle_command_retry.js @@ -12,8 +12,8 @@ PromiseTestUtils.allowMatchingRejectionsGlobally( ); // The tests in this file assert the retry behavior for MessageHandler commands. -// We call "blocked" commands from resources/modules/windowglobal/retry.jsm and -// then trigger reload and navigations to simulate AbortErrors and force the +// We call "blocked" commands from resources/modules/windowglobal/retry.sys.mjs +// and then trigger reload and navigations to simulate AbortErrors and force the // MessageHandler to retry the commands, when possible. // Test that without retry behavior, a pending command rejects when the diff --git a/remote/shared/messagehandler/test/browser/browser_session_data_browser_element.js b/remote/shared/messagehandler/test/browser/browser_session_data_browser_element.js index 9c15974ae6..f435ad2a91 100644 --- a/remote/shared/messagehandler/test/browser/browser_session_data_browser_element.js +++ b/remote/shared/messagehandler/test/browser/browser_session_data_browser_element.js @@ -27,7 +27,7 @@ add_task(async function test_session_data_broadcast() { const root = createRootMessageHandler("session-id-event"); - // When the windowglobal command.jsm module applies the session data + // When the windowglobal command.sys.mjs module applies the session data // browser_session_data_browser_element, it will emit an event. // Collect the events to detect which MessageHandlers have been started. info("Watch events emitted when session data is applied"); diff --git a/remote/shared/messagehandler/test/xpcshell/test_Errors.js b/remote/shared/messagehandler/test/xpcshell/test_Errors.js index 26187dac11..1a1d50e97e 100644 --- a/remote/shared/messagehandler/test/xpcshell/test_Errors.js +++ b/remote/shared/messagehandler/test/xpcshell/test_Errors.js @@ -7,7 +7,7 @@ const { error } = ChromeUtils.importESModule( ); // Note: this test file is similar to remote/shared/webdriver/test/xpcshell/test_Errors.js -// because shared/webdriver/Errors.jsm and shared/messagehandler/Errors.jsm share +// because shared/webdriver/Errors.sys.mjs and shared/messagehandler/Errors.sys.mjs share // similar helpers. add_task(function test_toJSON() { diff --git a/remote/shared/webdriver/Capabilities.sys.mjs b/remote/shared/webdriver/Capabilities.sys.mjs index 3c30ea0789..8ed9836306 100644 --- a/remote/shared/webdriver/Capabilities.sys.mjs +++ b/remote/shared/webdriver/Capabilities.sys.mjs @@ -1031,7 +1031,10 @@ export function validateCapabilities(capabilities) { */ export function processCapabilities(params) { const { capabilities } = params; - lazy.assert.object(capabilities); + lazy.assert.object( + capabilities, + lazy.pprint`Expected "capabilities" to be an object, got ${capabilities}` + ); let { alwaysMatch: requiredCapabilities = {}, diff --git a/remote/webdriver-bidi/modules/root/browsingContext.sys.mjs b/remote/webdriver-bidi/modules/root/browsingContext.sys.mjs index f2a5d5e645..8424bebf4a 100644 --- a/remote/webdriver-bidi/modules/root/browsingContext.sys.mjs +++ b/remote/webdriver-bidi/modules/root/browsingContext.sys.mjs @@ -22,7 +22,6 @@ ChromeUtils.defineESModuleGetters(lazy, { "chrome://remote/content/shared/NavigationManager.sys.mjs", NavigationListener: "chrome://remote/content/shared/listeners/NavigationListener.sys.mjs", - OwnershipModel: "chrome://remote/content/webdriver-bidi/RemoteValue.sys.mjs", PollPromise: "chrome://remote/content/shared/Sync.sys.mjs", pprint: "chrome://remote/content/shared/Format.sys.mjs", print: "chrome://remote/content/shared/PDF.sys.mjs", @@ -857,12 +856,6 @@ class BrowsingContextModule extends Module { * @param {number=} options.maxNodeCount * The maximum amount of nodes which is going to be returned. * Defaults to return all the found nodes. - * @param {OwnershipModel=} options.ownership - * The ownership model to use for the serialization - * of the DOM nodes. Defaults to `OwnershipModel.None`. - * @property {string=} sandbox - * The name of the sandbox. If the value is null or empty - * string, the default realm will be used. * @property {SerializationOptions=} serializationOptions * An object which holds the information of how the DOM nodes * should be serialized. @@ -884,8 +877,6 @@ class BrowsingContextModule extends Module { context: contextId, locator, maxNodeCount = null, - ownership = lazy.OwnershipModel.None, - sandbox = null, serializationOptions, startNodes = null, } = options; @@ -923,19 +914,6 @@ class BrowsingContextModule extends Module { }, maxNodeCountErrorMsg)(maxNodeCount); } - const ownershipTypes = Object.values(lazy.OwnershipModel); - lazy.assert.that( - ownership => ownershipTypes.includes(ownership), - `Expected "ownership" to be one of ${ownershipTypes}, got ${ownership}` - )(ownership); - - if (sandbox != null) { - lazy.assert.string( - sandbox, - `Expected "sandbox" to be a string, got ${sandbox}` - ); - } - const serializationOptionsWithDefaults = lazy.setDefaultAndAssertSerializationOptions(serializationOptions); @@ -961,8 +939,6 @@ class BrowsingContextModule extends Module { params: { locator, maxNodeCount, - resultOwnership: ownership, - sandbox, serializationOptions: serializationOptionsWithDefaults, startNodes, }, diff --git a/remote/webdriver-bidi/modules/root/network.sys.mjs b/remote/webdriver-bidi/modules/root/network.sys.mjs index 238b9f3640..6850e3f372 100644 --- a/remote/webdriver-bidi/modules/root/network.sys.mjs +++ b/remote/webdriver-bidi/modules/root/network.sys.mjs @@ -340,6 +340,9 @@ class NetworkModule extends Module { * of any intercept, the request will be suspended. * * @param {object=} options + * @param {Array=} options.contexts + * The list of browsing context ids where this intercept should be used. + * Optional, defaults to null. * @param {Array} options.phases * The phases where this intercept should be checked. * @param {Array=} options.urlPatterns @@ -353,7 +356,34 @@ class NetworkModule extends Module { * Raised if an argument is of an invalid type or value. */ addIntercept(options = {}) { - const { phases, urlPatterns = [] } = options; + const { contexts = null, phases, urlPatterns = [] } = options; + + if (contexts !== null) { + lazy.assert.array( + contexts, + `Expected "contexts" to be an array, got ${contexts}` + ); + + if (!options.contexts.length) { + throw new lazy.error.InvalidArgumentError( + `Expected "contexts" to contain at least one item, got an empty array` + ); + } + + for (const contextId of contexts) { + lazy.assert.string( + contextId, + `Expected elements of "contexts" to be a string, got ${contextId}` + ); + const context = this.#getBrowsingContext(contextId); + + if (context.parent) { + throw new lazy.error.InvalidArgumentError( + `Context with id ${contextId} is not a top-level browsing context` + ); + } + } + } lazy.assert.array( phases, @@ -386,6 +416,7 @@ class NetworkModule extends Module { const interceptId = lazy.generateUUID(); this.#interceptMap.set(interceptId, { + contexts, phases, urlPatterns: parsedPatterns, }); @@ -1122,6 +1153,23 @@ class NetworkModule extends Module { return challenges; } + #getBrowsingContext(contextId) { + const context = lazy.TabManager.getBrowsingContextById(contextId); + if (context === null) { + throw new lazy.error.NoSuchFrameError( + `Browsing Context with id ${contextId} not found` + ); + } + + if (!context.currentWindowGlobal) { + throw new lazy.error.NoSuchFrameError( + `No window found for BrowsingContext with id ${contextId}` + ); + } + + return context; + } + #getContextInfo(browsingContext) { return { contextId: browsingContext.id, @@ -1129,11 +1177,7 @@ class NetworkModule extends Module { }; } - #getSuspendMarkerText(requestData, phase) { - return `Request (id: ${requestData.request}) suspended by WebDriver BiDi in ${phase} phase`; - } - - #getNetworkIntercepts(event, requestData) { + #getNetworkIntercepts(event, requestData, contextId) { const intercepts = []; let phase; @@ -1153,8 +1197,23 @@ class NetworkModule extends Module { return intercepts; } + // Retrieve the top browsing context id for this network event. + const browsingContext = lazy.TabManager.getBrowsingContextById(contextId); + const topLevelContextId = lazy.TabManager.getIdForBrowsingContext( + browsingContext.top + ); + const url = requestData.url; for (const [interceptId, intercept] of this.#interceptMap) { + if ( + intercept.contexts !== null && + !intercept.contexts.includes(topLevelContextId) + ) { + // Skip this intercept if the event's context does not match the list + // of contexts for this intercept. + continue; + } + if (intercept.phases.includes(phase)) { const urlPatterns = intercept.urlPatterns; if ( @@ -1196,6 +1255,10 @@ class NetworkModule extends Module { return navigation ? navigation.navigationId : null; } + #getSuspendMarkerText(requestData, phase) { + return `Request (id: ${requestData.request}) suspended by WebDriver BiDi in ${phase} phase`; + } + #onAuthRequired = (name, data) => { const { authCallbacks, @@ -1580,7 +1643,11 @@ class NetworkModule extends Module { #processNetworkEvent(eventName, data) { const { contextId, navigation, redirectCount, requestData, timestamp } = data; - const intercepts = this.#getNetworkIntercepts(eventName, requestData); + const intercepts = this.#getNetworkIntercepts( + eventName, + requestData, + contextId + ); const isBlocked = !!intercepts.length; const baseParameters = { diff --git a/remote/webdriver-bidi/modules/root/session.sys.mjs b/remote/webdriver-bidi/modules/root/session.sys.mjs index a34ca514e3..8ecf7d7724 100644 --- a/remote/webdriver-bidi/modules/root/session.sys.mjs +++ b/remote/webdriver-bidi/modules/root/session.sys.mjs @@ -76,16 +76,10 @@ class SessionModule extends Module { const { events, contexts: contextIds = null } = params; // Check input types until we run schema validation. - lazy.assert.array(events, "events: array value expected"); - events.forEach(name => { - lazy.assert.string(name, `${name}: string value expected`); - }); + this.#assertNonEmptyArrayWithStrings(events, "events"); if (contextIds !== null) { - lazy.assert.array(contextIds, "contexts: array value expected"); - contextIds.forEach(contextId => { - lazy.assert.string(contextId, `${contextId}: string value expected`); - }); + this.#assertNonEmptyArrayWithStrings(contextIds, "contexts"); } const listeners = this.#updateEventMap(events, contextIds, true); @@ -113,15 +107,9 @@ class SessionModule extends Module { const { events, contexts: contextIds = null } = params; // Check input types until we run schema validation. - lazy.assert.array(events, "events: array value expected"); - events.forEach(name => { - lazy.assert.string(name, `${name}: string value expected`); - }); + this.#assertNonEmptyArrayWithStrings(events, "events"); if (contextIds !== null) { - lazy.assert.array(contextIds, "contexts: array value expected"); - contextIds.forEach(contextId => { - lazy.assert.string(contextId, `${contextId}: string value expected`); - }); + this.#assertNonEmptyArrayWithStrings(contextIds, "contexts"); } const listeners = this.#updateEventMap(events, contextIds, false); @@ -139,6 +127,23 @@ class SessionModule extends Module { } } + #assertNonEmptyArrayWithStrings(array, variableName) { + lazy.assert.array( + array, + `Expected "${variableName}" to be an array, got ${array}` + ); + lazy.assert.that( + array => !!array.length, + `Expected "${variableName}" array to have at least one item` + )(array); + array.forEach(item => { + lazy.assert.string( + item, + `Expected elements of "${variableName}" to be a string, got ${item}` + ); + }); + } + #getBrowserIdForContextId(contextId) { const context = lazy.TabManager.getBrowsingContextById(contextId); if (!context) { diff --git a/remote/webdriver-bidi/modules/root/storage.sys.mjs b/remote/webdriver-bidi/modules/root/storage.sys.mjs index 3eced2da4c..34f909aa28 100644 --- a/remote/webdriver-bidi/modules/root/storage.sys.mjs +++ b/remote/webdriver-bidi/modules/root/storage.sys.mjs @@ -16,6 +16,15 @@ ChromeUtils.defineESModuleGetters(lazy, { "chrome://remote/content/shared/UserContextManager.sys.mjs", }); +const PREF_COOKIE_BEHAVIOR = "network.cookie.cookieBehavior"; +const PREF_COOKIE_OPTIN_PARTITIONING = + "network.cookie.cookieBehavior.optInPartitioning"; + +// This is a static preference, so it cannot be modified during runtime and we can cache its value. +ChromeUtils.defineLazyGetter(lazy, "cookieBehaviorOptInPartitioning", () => + Services.prefs.getBoolPref(PREF_COOKIE_OPTIN_PARTITIONING) +); + const CookieFieldsMapping = { domain: "host", expiry: "expiry", @@ -603,10 +612,22 @@ class StorageModule extends Module { if (partitionSpec.type === PartitionType.Context) { const { context: contextId } = partitionSpec; const browsingContext = this.#getBrowsingContext(contextId); + const principal = Services.scriptSecurityManager.createContentPrincipal( + browsingContext.currentURI, + {} + ); // Define browsing context’s associated storage partition as combination of user context id - // and the origin of the document in this browsing context. + // and the origin of the document in this browsing context. We also add here `isThirdPartyURI` + // which is required to filter out third-party cookies in case they are not allowed. return { + // In case we have the browsing context of an iframe here, we perform a check + // if the URI of the top context is considered third-party to the URI of the iframe principal. + // It's considered a third-party if base domains or hosts (in case one or both base domains + // can not be determined) do not match. + isThirdPartyURI: browsingContext.parent + ? principal.isThirdPartyURI(browsingContext.top.currentURI) + : false, sourceOrigin: browsingContext.currentURI.prePath, userContext: browsingContext.originAttributes.userContextId, }; @@ -640,6 +661,9 @@ class StorageModule extends Module { ); } + // This key is not used for partitioning and was required to only filter out third-party cookies. + delete partitionKey.isThirdPartyURI; + return partitionKey; } @@ -742,44 +766,61 @@ class StorageModule extends Module { // Prepare the data in the format required for the platform API. const originAttributes = this.#getOriginAttributes(storagePartitionKey); - // In case we want to get the cookies for a certain `sourceOrigin`, - // we have to separately retrieve cookies for a hostname built from `sourceOrigin`, - // and with `partitionKey` equal an empty string to retrieve the cookies that which were set - // by this hostname but without `partitionKey`, e.g. with `document.cookie`. - if (storagePartitionKey.sourceOrigin) { - const url = new URL(storagePartitionKey.sourceOrigin); - const hostname = url.hostname; - - const principal = Services.scriptSecurityManager.createContentPrincipal( - Services.io.newURI(url), - {} + // Retrieve the cookies which exactly match a built partition attributes. + const cookiesWithOriginAttributes = + Services.cookies.getCookiesWithOriginAttributes( + JSON.stringify(originAttributes) ); - const isSecureProtocol = principal.isOriginPotentiallyTrustworthy; - - // We want to keep `userContext` id here, if it's present, - // but set the `partitionKey` to an empty string. - const cookiesMatchingHostname = - Services.cookies.getCookiesWithOriginAttributes( - JSON.stringify({ ...originAttributes, partitionKey: "" }), - hostname + + const isFirstPartyOrCrossSiteAllowed = + !storagePartitionKey.isThirdPartyURI || + this.#shouldIncludeCrossSiteCookie(); + + // Check if we accessing the first party storage or cross-site cookies are allowed. + if (isFirstPartyOrCrossSiteAllowed) { + // In case we want to get the cookies for a certain `sourceOrigin`, + // we have to separately retrieve cookies for a hostname built from `sourceOrigin`, + // and with `partitionKey` equal an empty string to retrieve the cookies that which were set + // by this hostname but without `partitionKey`, e.g. with `document.cookie`. + if (storagePartitionKey.sourceOrigin) { + const url = new URL(storagePartitionKey.sourceOrigin); + const hostname = url.hostname; + + const principal = Services.scriptSecurityManager.createContentPrincipal( + Services.io.newURI(url), + {} ); + const isSecureProtocol = principal.isOriginPotentiallyTrustworthy; + + // We want to keep `userContext` id here, if it's present, + // but set the `partitionKey` to an empty string. + const cookiesMatchingHostname = + Services.cookies.getCookiesWithOriginAttributes( + JSON.stringify({ ...originAttributes, partitionKey: "" }), + hostname + ); + for (const cookie of cookiesMatchingHostname) { + // Ignore secure cookies for non-secure protocols. + if (cookie.isSecure && !isSecureProtocol) { + continue; + } + store.push(cookie); + } + } - for (const cookie of cookiesMatchingHostname) { - // Ignore secure cookies for non-secure protocols. - if (cookie.isSecure && !isSecureProtocol) { - continue; + store = store.concat(cookiesWithOriginAttributes); + } + // If we're trying to access the store in the third party context and + // the preferences imply that we shouldn't include cross site cookies, + // but we should include partitioned cookies, add only partitioned cookies. + else if (this.#shouldIncludePartitionedCookies()) { + for (const cookie of cookiesWithOriginAttributes) { + if (cookie.isPartitioned) { + store.push(cookie); } - store.push(cookie); } } - // Add the cookies which exactly match a built partition attributes. - store = store.concat( - Services.cookies.getCookiesWithOriginAttributes( - JSON.stringify(originAttributes) - ) - ); - return store; } @@ -856,6 +897,30 @@ class StorageModule extends Module { return cookie; } + + #shouldIncludeCrossSiteCookie() { + const cookieBehavior = Services.prefs.getIntPref(PREF_COOKIE_BEHAVIOR); + + if ( + cookieBehavior === Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN || + cookieBehavior === + Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN + ) { + return false; + } + + return true; + } + + #shouldIncludePartitionedCookies() { + const cookieBehavior = Services.prefs.getIntPref(PREF_COOKIE_BEHAVIOR); + + return ( + cookieBehavior === + Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN && + lazy.cookieBehaviorOptInPartitioning + ); + } } export const storage = StorageModule; diff --git a/remote/webdriver-bidi/modules/windowglobal/browsingContext.sys.mjs b/remote/webdriver-bidi/modules/windowglobal/browsingContext.sys.mjs index adf821601d..ef61954284 100644 --- a/remote/webdriver-bidi/modules/windowglobal/browsingContext.sys.mjs +++ b/remote/webdriver-bidi/modules/windowglobal/browsingContext.sys.mjs @@ -17,6 +17,7 @@ ChromeUtils.defineESModuleGetters(lazy, { "chrome://remote/content/webdriver-bidi/modules/root/browsingContext.sys.mjs", OriginType: "chrome://remote/content/webdriver-bidi/modules/root/browsingContext.sys.mjs", + OwnershipModel: "chrome://remote/content/webdriver-bidi/RemoteValue.sys.mjs", PollPromise: "chrome://remote/content/shared/Sync.sys.mjs", }); @@ -425,16 +426,9 @@ class BrowsingContextModule extends WindowGlobalBiDiModule { } _locateNodes(params = {}) { - const { - locator, - maxNodeCount, - resultOwnership, - sandbox, - serializationOptions, - startNodes, - } = params; + const { locator, maxNodeCount, serializationOptions, startNodes } = params; - const realm = this.messageHandler.getRealm({ sandboxName: sandbox }); + const realm = this.messageHandler.getRealm(); const contextNodes = []; if (startNodes === null) { @@ -482,7 +476,7 @@ class BrowsingContextModule extends WindowGlobalBiDiModule { this.serialize( returnedNode, serializationOptions, - resultOwnership, + lazy.OwnershipModel.None, realm, { seenNodeIds } ) -- cgit v1.2.3