diff --git a/caps/OriginAttributes.cpp b/caps/OriginAttributes.cpp index bc4abe31a965..87d3bc9108d2 100644 --- a/caps/OriginAttributes.cpp +++ b/caps/OriginAttributes.cpp @@ -163,6 +163,15 @@ void OriginAttributes::CreateSuffix(nsACString& aStr) const { sanitizedFirstPartyDomain); } + if (!mGeckoViewSessionContextId.IsEmpty()) { + nsAutoString sanitizedGeckoViewUserContextId(mGeckoViewSessionContextId); + sanitizedGeckoViewUserContextId.ReplaceChar( + dom::quota::QuotaManager::kReplaceChars, '+'); + + params.Set(NS_LITERAL_STRING("geckoViewUserContextId"), + sanitizedGeckoViewUserContextId); + } + aStr.Truncate(); params.Serialize(value); @@ -258,6 +267,12 @@ class MOZ_STACK_CLASS PopulateFromSuffixIterator final return true; } + if (aName.EqualsLiteral("geckoViewUserContextId")) { + MOZ_RELEASE_ASSERT(mOriginAttributes->mGeckoViewSessionContextId.IsEmpty()); + mOriginAttributes->mGeckoViewSessionContextId.Assign(aValue); + return true; + } + // No other attributes are supported. return false; } diff --git a/caps/OriginAttributes.h b/caps/OriginAttributes.h index 582265b29006..6aed86cbcd30 100644 --- a/caps/OriginAttributes.h +++ b/caps/OriginAttributes.h @@ -50,7 +50,8 @@ class OriginAttributes : public dom::OriginAttributesDictionary { mInIsolatedMozBrowser == aOther.mInIsolatedMozBrowser && mUserContextId == aOther.mUserContextId && mPrivateBrowsingId == aOther.mPrivateBrowsingId && - mFirstPartyDomain == aOther.mFirstPartyDomain; + mFirstPartyDomain == aOther.mFirstPartyDomain && + mGeckoViewSessionContextId == aOther.mGeckoViewSessionContextId; } bool operator!=(const OriginAttributes& aOther) const { @@ -61,7 +62,8 @@ class OriginAttributes : public dom::OriginAttributesDictionary { return mAppId == aOther.mAppId && mInIsolatedMozBrowser == aOther.mInIsolatedMozBrowser && mUserContextId == aOther.mUserContextId && - mPrivateBrowsingId == aOther.mPrivateBrowsingId; + mPrivateBrowsingId == aOther.mPrivateBrowsingId && + mGeckoViewSessionContextId == aOther.mGeckoViewSessionContextId; } // Serializes/Deserializes non-default values into the suffix format, i.e. @@ -153,6 +155,11 @@ class OriginAttributesPattern : public dom::OriginAttributesPatternDictionary { return false; } + if (mGeckoViewSessionContextId.WasPassed() && + mGeckoViewSessionContextId.Value() != aAttrs.mGeckoViewSessionContextId) { + return false; + } + return true; } @@ -184,6 +191,11 @@ class OriginAttributesPattern : public dom::OriginAttributesPatternDictionary { return false; } + if (mGeckoViewSessionContextId.WasPassed() && aOther.mGeckoViewSessionContextId.WasPassed() && + mGeckoViewSessionContextId.Value() != aOther.mGeckoViewSessionContextId.Value()) { + return false; + } + return true; } }; diff --git a/dom/chrome-webidl/ChromeUtils.webidl b/dom/chrome-webidl/ChromeUtils.webidl index 3c9b6dcb6913..fb6e74b1882e 100644 --- a/dom/chrome-webidl/ChromeUtils.webidl +++ b/dom/chrome-webidl/ChromeUtils.webidl @@ -525,6 +525,7 @@ dictionary OriginAttributesDictionary { boolean inIsolatedMozBrowser = false; unsigned long privateBrowsingId = 0; DOMString firstPartyDomain = ""; + DOMString geckoViewSessionContextId = ""; }; dictionary OriginAttributesPatternDictionary { unsigned long appId; @@ -532,6 +533,7 @@ dictionary OriginAttributesPatternDictionary { boolean inIsolatedMozBrowser; unsigned long privateBrowsingId; DOMString firstPartyDomain; + DOMString geckoViewSessionContextId; }; dictionary CompileScriptOptionsDictionary { diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java index df51748a09d5..3acb16c83488 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java @@ -79,6 +79,22 @@ public final class GeckoSessionSettings implements Parcelable { return this; } + /** + * Set the session context ID for this instance. + * Setting a context ID partitions the cookie jars based on the provided + * IDs. This isolates the browser storage like cookies and localStorage + * between sessions, only sessions that share the same ID share storage + * data. + * + * @param value The custom context ID. + * The default ID is null, which removes isolation for this + * instance. + */ + public @NonNull Builder contextId(final @Nullable String value) { + mSettings.setContextId(value); + return this; + } + /** * Set whether multi-process support should be enabled. * @@ -213,6 +229,7 @@ public final class GeckoSessionSettings implements Parcelable { * width of 980 CSS px. */ public static final int VIEWPORT_MODE_MOBILE = 0; + /** * All pages will be rendered using the special desktop mode viewport, which has a width of * 980 CSS px, regardless of whether the page has a <meta> viewport tag specified or not. @@ -314,6 +331,12 @@ public final class GeckoSessionSettings implements Parcelable { private static final Key FULL_ACCESSIBILITY_TREE = new Key("fullAccessibilityTree", /* initOnly */ false, /* values */ null); + /** + * Key to specify the session context ID. + */ + private static final Key CONTEXT_ID = + new Key("sessionContextId", /* initOnly */ true, /* values */ null); + private final GeckoSession mSession; private final GeckoBundle mBundle; @@ -347,6 +370,7 @@ public final class GeckoSessionSettings implements Parcelable { mBundle.putString(USER_AGENT_OVERRIDE.name, null); mBundle.putInt(VIEWPORT_MODE.name, VIEWPORT_MODE_MOBILE); mBundle.putInt(DISPLAY_MODE.name, DISPLAY_MODE_BROWSER); + mBundle.putString(CONTEXT_ID.name, null); } /** @@ -439,6 +463,15 @@ public final class GeckoSessionSettings implements Parcelable { return getBoolean(USE_PRIVATE_MODE); } + /** + * The context ID for this session. + * + * @return The context ID for this session. + */ + public @Nullable String getContextId() { + return getString(CONTEXT_ID); + } + /** * Whether multiprocess is enabled. * @@ -597,6 +630,10 @@ public final class GeckoSessionSettings implements Parcelable { setString(USER_AGENT_OVERRIDE, value); } + private void setContextId(final @Nullable String value) { + setString(CONTEXT_ID, value); + } + private void setString(final Key key, final String value) { synchronized (mBundle) { if (valueChangedLocked(key, value)) { diff --git a/mobile/android/modules/geckoview/GeckoViewNavigation.jsm b/mobile/android/modules/geckoview/GeckoViewNavigation.jsm index d6e40a22f68f..4a06836cd6e9 100644 --- a/mobile/android/modules/geckoview/GeckoViewNavigation.jsm +++ b/mobile/android/modules/geckoview/GeckoViewNavigation.jsm @@ -13,6 +13,7 @@ XPCOMUtils.defineLazyModuleGetters(this, { E10SUtils: "resource://gre/modules/sessionstore/Utils.jsm", LoadURIDelegate: "resource://gre/modules/LoadURIDelegate.jsm", Services: "resource://gre/modules/Services.jsm", + PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm", }); // Handles navigation requests between Gecko and a GeckoView. @@ -32,6 +33,8 @@ class GeckoViewNavigation extends GeckoViewModule { } onInit() { + debug `onInit`; + this.registerListener([ "GeckoView:GoBack", "GeckoView:GoForward", @@ -42,6 +45,16 @@ class GeckoViewNavigation extends GeckoViewModule { ]); this.messageManager.addMessageListener("Browser:LoadURI", this); + + debug `sessionContextId=${this.settings.sessionContextId}`; + + if (this.settings.sessionContextId !== null) { + this.browser.webNavigation.setOriginAttributesBeforeLoading({ + geckoViewSessionContextId: this.settings.sessionContextId, + privateBrowsingId: + PrivateBrowsingUtils.isBrowserPrivate(this.browser) ? 1 : 0, + }); + } } // Bundle event handler. diff --git a/mobile/android/modules/geckoview/GeckoViewSettings.jsm b/mobile/android/modules/geckoview/GeckoViewSettings.jsm index e460a02cf165..7d88fc90135e 100644 --- a/mobile/android/modules/geckoview/GeckoViewSettings.jsm +++ b/mobile/android/modules/geckoview/GeckoViewSettings.jsm @@ -43,6 +43,7 @@ class GeckoViewSettings extends GeckoViewModule { debug `onInit`; this._userAgentMode = USER_AGENT_MODE_MOBILE; this._userAgentOverride = null; + this._sessionContextId = null; // Required for safe browsing and tracking protection. this.registerListener([ @@ -67,6 +68,7 @@ class GeckoViewSettings extends GeckoViewModule { this.displayMode = settings.displayMode; this.userAgentMode = settings.userAgentMode; this.userAgentOverride = settings.userAgentOverride; + this.sessionContextId = settings.sessionContextId; } get useMultiprocess() { @@ -112,6 +114,14 @@ class GeckoViewSettings extends GeckoViewModule { set displayMode(aMode) { this.window.docShell.displayMode = aMode; } + + set sessionContextId(aAttribute) { + this._sessionContextId = aAttribute; + } + + get sessionContextId() { + return this._sessionContextId; + } } const {debug, warn} = GeckoViewSettings.initLogging("GeckoViewSettings"); // eslint-disable-line no-unused-vars diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index 382e03decac2..d41f133bcdc9 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -3208,6 +3208,9 @@ already_AddRefed HttpBaseChannel::CloneLoadInfoForRedirect( MOZ_ASSERT( docShellAttrs.mPrivateBrowsingId == attrs.mPrivateBrowsingId, "docshell and necko should have the same privateBrowsingId attribute."); + MOZ_ASSERT( + docShellAttrs.mGeckoViewSessionContextId == attrs.mGeckoViewSessionContextId, + "docshell and necko should have the same geckoViewSessionContextId attribute"); attrs = docShellAttrs; attrs.SetFirstPartyDomain(true, newURI);