diff --git a/caps/OriginAttributes.cpp b/caps/OriginAttributes.cpp index 7f40e6549310..166fd148a1fa 100644 --- a/caps/OriginAttributes.cpp +++ b/caps/OriginAttributes.cpp @@ -158,6 +158,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); @@ -243,6 +252,13 @@ 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 9b36b797f0a2..18520aaec6c1 100644 --- a/caps/OriginAttributes.h +++ b/caps/OriginAttributes.h @@ -48,7 +48,8 @@ class OriginAttributes : public dom::OriginAttributesDictionary { return mInIsolatedMozBrowser == aOther.mInIsolatedMozBrowser && mUserContextId == aOther.mUserContextId && mPrivateBrowsingId == aOther.mPrivateBrowsingId && - mFirstPartyDomain == aOther.mFirstPartyDomain; + mFirstPartyDomain == aOther.mFirstPartyDomain && + mGeckoViewSessionContextId == aOther.mGeckoViewSessionContextId; } bool operator!=(const OriginAttributes& aOther) const { @@ -58,7 +59,8 @@ class OriginAttributes : public dom::OriginAttributesDictionary { MOZ_MUST_USE bool EqualsIgnoringFPD(const OriginAttributes& aOther) const { return 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. @@ -146,6 +148,12 @@ class OriginAttributesPattern : public dom::OriginAttributesPatternDictionary { return false; } + if (mGeckoViewSessionContextId.WasPassed() && + mGeckoViewSessionContextId.Value() != + aAttrs.mGeckoViewSessionContextId) { + return false; + } + return true; } @@ -172,6 +180,13 @@ 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 5c9c42dff9b4..b3c1c957fc4b 100644 --- a/dom/chrome-webidl/ChromeUtils.webidl +++ b/dom/chrome-webidl/ChromeUtils.webidl @@ -547,12 +547,14 @@ dictionary OriginAttributesDictionary { boolean inIsolatedMozBrowser = false; unsigned long privateBrowsingId = 0; DOMString firstPartyDomain = ""; + DOMString geckoViewSessionContextId = ""; }; dictionary OriginAttributesPatternDictionary { unsigned long userContextId; boolean inIsolatedMozBrowser; unsigned long privateBrowsingId; DOMString firstPartyDomain; + DOMString geckoViewSessionContextId; }; dictionary CompileScriptOptionsDictionary { diff --git a/dom/quota/SerializationHelpers.h b/dom/quota/SerializationHelpers.h index 68e2c613cc05..2e18064ad206 100644 --- a/dom/quota/SerializationHelpers.h +++ b/dom/quota/SerializationHelpers.h @@ -37,6 +37,7 @@ struct ParamTraits { WriteParam(aMsg, aParam.mInIsolatedMozBrowser); WriteParam(aMsg, aParam.mPrivateBrowsingId); WriteParam(aMsg, aParam.mUserContextId); + WriteParam(aMsg, aParam.mGeckoViewSessionContextId); } static bool Read(const Message* aMsg, PickleIterator* aIter, @@ -44,7 +45,8 @@ struct ParamTraits { return ReadParam(aMsg, aIter, &aResult->mFirstPartyDomain) && ReadParam(aMsg, aIter, &aResult->mInIsolatedMozBrowser) && ReadParam(aMsg, aIter, &aResult->mPrivateBrowsingId) && - ReadParam(aMsg, aIter, &aResult->mUserContextId); + ReadParam(aMsg, aIter, &aResult->mUserContextId) && + ReadParam(aMsg, aIter, &aResult->mGeckoViewSessionContextId); } }; diff --git a/mobile/android/geckoview/api.txt b/mobile/android/geckoview/api.txt index b3cace262943..8f9159ee1fcf 100644 --- a/mobile/android/geckoview/api.txt +++ b/mobile/android/geckoview/api.txt @@ -797,6 +797,7 @@ package org.mozilla.geckoview { ctor public GeckoSessionSettings(@NonNull GeckoSessionSettings); method public boolean getAllowJavascript(); method @Nullable public String getChromeUri(); + method @Nullable public String getContextId(); method public int getDisplayMode(); method public boolean getFullAccessibilityTree(); method public int getScreenId(); @@ -834,6 +835,7 @@ package org.mozilla.geckoview { method @NonNull public GeckoSessionSettings.Builder allowJavascript(boolean); method @NonNull public GeckoSessionSettings build(); method @NonNull public GeckoSessionSettings.Builder chromeUri(@NonNull String); + method @NonNull public GeckoSessionSettings.Builder contextId(@Nullable String); method @NonNull public GeckoSessionSettings.Builder displayMode(int); method @NonNull public GeckoSessionSettings.Builder fullAccessibilityTree(boolean); method @NonNull public GeckoSessionSettings.Builder screenId(int); 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 8ddeef6e2598..da3dc3a5456f 100644 --- a/mobile/android/modules/geckoview/GeckoViewNavigation.jsm +++ b/mobile/android/modules/geckoview/GeckoViewNavigation.jsm @@ -17,6 +17,7 @@ XPCOMUtils.defineLazyModuleGetters(this, { E10SUtils: "resource://gre/modules/E10SUtils.jsm", LoadURIDelegate: "resource://gre/modules/LoadURIDelegate.jsm", Services: "resource://gre/modules/Services.jsm", + PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm", }); XPCOMUtils.defineLazyGetter(this, "ReferrerInfo", () => @@ -58,6 +59,8 @@ class GeckoViewNavigation extends GeckoViewModule { } onInit() { + debug`onInit`; + this.registerListener([ "GeckoView:GoBack", "GeckoView:GoForward", @@ -69,6 +72,17 @@ class GeckoViewNavigation extends GeckoViewModule { this.messageManager.addMessageListener("Browser:LoadURI", this); this._initialAboutBlank = true; + + 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 d159f5f766ac..c8a6a0e5a074 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(["GeckoView:GetUserAgent"]); @@ -65,6 +66,7 @@ class GeckoViewSettings extends GeckoViewModule { this.displayMode = settings.displayMode; this.userAgentMode = settings.userAgentMode; this.userAgentOverride = settings.userAgentOverride; + this.sessionContextId = settings.sessionContextId; } get useMultiprocess() { @@ -110,6 +112,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/mobile/android/modules/geckoview/GeckoViewStorageController.jsm b/mobile/android/modules/geckoview/GeckoViewStorageController.jsm index 48052f1ad160..c2754a286163 100644 --- a/mobile/android/modules/geckoview/GeckoViewStorageController.jsm +++ b/mobile/android/modules/geckoview/GeckoViewStorageController.jsm @@ -125,4 +125,13 @@ const GeckoViewStorageController = { aCallback.onSuccess(); }); }, + + clearSessionContextData(aContextId) { + const pattern = { geckoViewSessionContextId: aContextId }; + debug`clearSessionContextData ${pattern}`; + Services.clearData.deleteDataFromOriginAttributesPattern(pattern); + // Call QMS explicitly to work around bug 1537882. + Services.qms.clearStoragesForOriginAttributesPattern( + JSON.stringify(pattern)); + }, }; diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index a815679e1516..c796aba2d1c7 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -2911,6 +2911,10 @@ 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);