Bug 1501108 - [1.2] Add GeckoView Session Context ID support. r=snorp,baku,mayhemer
☠☠ backed out by 095d253f97be ☠ ☠
authorEugen Sawin <esawin@mozilla.com>
Mon, 15 Apr 2019 20:58:30 +0000
changeset 469568 cad2b29b79d2a086c96feb0326cf61e53bb105da
parent 469567 27f0cd20a8b010d5753295df6246cde37fbada1e
child 469569 de36c9fb8c65a6b78e7a98a3b3a06b60ffd3102b
push id35874
push userccoroiu@mozilla.com
push dateTue, 16 Apr 2019 04:04:58 +0000
treeherdermozilla-central@be3f40425b52 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp, baku, mayhemer
bugs1501108
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1501108 - [1.2] Add GeckoView Session Context ID support. r=snorp,baku,mayhemer Differential Revision: https://phabricator.services.mozilla.com/D19182
caps/OriginAttributes.cpp
caps/OriginAttributes.h
dom/chrome-webidl/ChromeUtils.webidl
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java
mobile/android/modules/geckoview/GeckoViewNavigation.jsm
mobile/android/modules/geckoview/GeckoViewSettings.jsm
netwerk/protocol/http/HttpBaseChannel.cpp
--- a/caps/OriginAttributes.cpp
+++ b/caps/OriginAttributes.cpp
@@ -158,16 +158,25 @@ void OriginAttributes::CreateSuffix(nsAC
     nsAutoString sanitizedFirstPartyDomain(mFirstPartyDomain);
     sanitizedFirstPartyDomain.ReplaceChar(
         dom::quota::QuotaManager::kReplaceChars, '+');
 
     params.Set(NS_LITERAL_STRING("firstPartyDomain"),
                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);
   if (!value.IsEmpty()) {
     aStr.AppendLiteral("^");
     aStr.Append(NS_ConvertUTF16toUTF8(value));
   }
 
@@ -253,16 +262,22 @@ class MOZ_STACK_CLASS PopulateFromSuffix
     }
 
     if (aName.EqualsLiteral("firstPartyDomain")) {
       MOZ_RELEASE_ASSERT(mOriginAttributes->mFirstPartyDomain.IsEmpty());
       mOriginAttributes->mFirstPartyDomain.Assign(aValue);
       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;
   }
 
  private:
   OriginAttributes* mOriginAttributes;
 };
 
--- a/caps/OriginAttributes.h
+++ b/caps/OriginAttributes.h
@@ -45,28 +45,30 @@ class OriginAttributes : public dom::Ori
     }
   }
 
   bool operator==(const OriginAttributes& aOther) const {
     return mAppId == aOther.mAppId &&
            mInIsolatedMozBrowser == aOther.mInIsolatedMozBrowser &&
            mUserContextId == aOther.mUserContextId &&
            mPrivateBrowsingId == aOther.mPrivateBrowsingId &&
-           mFirstPartyDomain == aOther.mFirstPartyDomain;
+           mFirstPartyDomain == aOther.mFirstPartyDomain &&
+           mGeckoViewSessionContextId == aOther.mGeckoViewSessionContextId;
   }
 
   bool operator!=(const OriginAttributes& aOther) const {
     return !(*this == aOther);
   }
 
   MOZ_MUST_USE bool EqualsIgnoringFPD(const OriginAttributes& aOther) const {
     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.
   // |!key1=value1&key2=value2|. If there are no non-default attributes, this
   // returns an empty string.
   void CreateSuffix(nsACString& aStr) const;
 
   // Don't use this method for anything else than debugging!
@@ -148,16 +150,21 @@ class OriginAttributesPattern : public d
       return false;
     }
 
     if (mFirstPartyDomain.WasPassed() &&
         mFirstPartyDomain.Value() != aAttrs.mFirstPartyDomain) {
       return false;
     }
 
+    if (mGeckoViewSessionContextId.WasPassed() &&
+        mGeckoViewSessionContextId.Value() != aAttrs.mGeckoViewSessionContextId) {
+      return false;
+    }
+
     return true;
   }
 
   bool Overlaps(const OriginAttributesPattern& aOther) const {
     if (mAppId.WasPassed() && aOther.mAppId.WasPassed() &&
         mAppId.Value() != aOther.mAppId.Value()) {
       return false;
     }
@@ -179,15 +186,20 @@ class OriginAttributesPattern : public d
       return false;
     }
 
     if (mFirstPartyDomain.WasPassed() && aOther.mFirstPartyDomain.WasPassed() &&
         mFirstPartyDomain.Value() != aOther.mFirstPartyDomain.Value()) {
       return false;
     }
 
+    if (mGeckoViewSessionContextId.WasPassed() && aOther.mGeckoViewSessionContextId.WasPassed() &&
+        mGeckoViewSessionContextId.Value() != aOther.mGeckoViewSessionContextId.Value()) {
+      return false;
+    }
+
     return true;
   }
 };
 
 }  // namespace mozilla
 
 #endif /* mozilla_OriginAttributes_h */
--- a/dom/chrome-webidl/ChromeUtils.webidl
+++ b/dom/chrome-webidl/ChromeUtils.webidl
@@ -520,23 +520,25 @@ dictionary IOActivityDataDictionary {
  * (3) Update the methods on mozilla::OriginAttributesPattern, including matching.
  */
 dictionary OriginAttributesDictionary {
   unsigned long appId = 0;
   unsigned long userContextId = 0;
   boolean inIsolatedMozBrowser = false;
   unsigned long privateBrowsingId = 0;
   DOMString firstPartyDomain = "";
+  DOMString geckoViewSessionContextId = "";
 };
 dictionary OriginAttributesPatternDictionary {
   unsigned long appId;
   unsigned long userContextId;
   boolean inIsolatedMozBrowser;
   unsigned long privateBrowsingId;
   DOMString firstPartyDomain;
+  DOMString geckoViewSessionContextId;
 };
 
 dictionary CompileScriptOptionsDictionary {
   /**
    * The character set from which to decode the script.
    */
   DOMString charset = "utf-8";
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java
@@ -75,16 +75,32 @@ public final class GeckoSessionSettings 
          * @return This Builder instance.
          */
         public @NonNull Builder usePrivateMode(final boolean flag) {
             mSettings.setUsePrivateMode(flag);
             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.
          *
          * @param flag A flag determining whether multi-process should be enabled.
          *             Default is false.
          * @return This Builder instance.
          */
         public @NonNull Builder useMultiprocess(final boolean flag) {
             mSettings.setUseMultiprocess(flag);
@@ -208,16 +224,17 @@ public final class GeckoSessionSettings 
 
     // This needs to match GeckoViewSettingsChild.js
     /**
      * Mobile-friendly pages will be rendered using a viewport based on their &lt;meta&gt; viewport
      * tag. All other pages will be rendered using a special desktop mode viewport, which has a
      * 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 &lt;meta&gt; viewport tag specified or not.
      */
     public static final int VIEWPORT_MODE_DESKTOP = 1;
 
     public static class Key<T> {
         /* package */ final String name;
@@ -309,16 +326,22 @@ public final class GeckoSessionSettings 
     private static final Key<Boolean> ALLOW_JAVASCRIPT =
             new Key<Boolean>("allowJavascript", /* initOnly */ false, /* values */ null);
     /**
      * Key to specify if entire accessible tree should be exposed with no caching.
      */
     private static final Key<Boolean> FULL_ACCESSIBILITY_TREE =
             new Key<Boolean>("fullAccessibilityTree", /* initOnly */ false, /* values */ null);
 
+    /**
+     * Key to specify the session context ID.
+     */
+    private static final Key<String> CONTEXT_ID =
+        new Key<String>("sessionContextId", /* initOnly */ true, /* values */ null);
+
     private final GeckoSession mSession;
     private final GeckoBundle mBundle;
 
     public GeckoSessionSettings() {
         this(null, null);
     }
 
     public GeckoSessionSettings(final @NonNull GeckoSessionSettings settings) {
@@ -342,16 +365,17 @@ public final class GeckoSessionSettings 
         mBundle.putBoolean(USE_MULTIPROCESS.name, true);
         mBundle.putBoolean(SUSPEND_MEDIA_WHEN_INACTIVE.name, false);
         mBundle.putBoolean(ALLOW_JAVASCRIPT.name, true);
         mBundle.putBoolean(FULL_ACCESSIBILITY_TREE.name, false);
         mBundle.putInt(USER_AGENT_MODE.name, USER_AGENT_MODE_MOBILE);
         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);
     }
 
     /**
      * Set whether tracking protection should be enabled.
      *
      * @param value A flag determining whether tracking protection should be enabled.
      *             Default is false.
      */
@@ -435,16 +459,25 @@ public final class GeckoSessionSettings 
      *
      * @return true if private mode is enabled, false if not.
      */
     public boolean getUsePrivateMode() {
         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.
      *
      * @return true if multiprocess is enabled, false if not.
      */
     public boolean getUseMultiprocess() {
         return getBoolean(USE_MULTIPROCESS);
     }
 
@@ -592,16 +625,20 @@ public final class GeckoSessionSettings 
      * Specify the user agent override string.
      * Set value to null to use the user agent specified by USER_AGENT_MODE.
      * @param value The string to override the user agent with.
      */
     public void setUserAgentOverride(final @Nullable String value) {
         setString(USER_AGENT_OVERRIDE, value);
     }
 
+    private void setContextId(final @Nullable String value) {
+        setString(CONTEXT_ID, value);
+    }
+
     private void setString(final Key<String> key, final String value) {
         synchronized (mBundle) {
             if (valueChangedLocked(key, value)) {
                 mBundle.putString(key.name, value);
                 dispatchUpdate();
             }
         }
     }
--- a/mobile/android/modules/geckoview/GeckoViewNavigation.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewNavigation.jsm
@@ -8,16 +8,17 @@ var EXPORTED_SYMBOLS = ["GeckoViewNaviga
 
 const {GeckoViewModule} = ChromeUtils.import("resource://gre/modules/GeckoViewModule.jsm");
 const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 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.
 // Handles GeckoView:GoBack and :GoForward requests dispatched by
 // GeckoView.goBack and .goForward.
 // Dispatches GeckoView:LocationChange to the GeckoView on location change when
 // active.
 // Implements nsIBrowserDOMWindow.
@@ -27,26 +28,38 @@ class GeckoViewNavigation extends GeckoV
 
     // There may be a GeckoViewNavigation module in another window waiting for
     // us to create a browser so it can call presetOpenerWindow(), so allow them
     // to do that now.
     Services.obs.notifyObservers(this.window, "geckoview-window-created");
   }
 
   onInit() {
+    debug `onInit`;
+
     this.registerListener([
       "GeckoView:GoBack",
       "GeckoView:GoForward",
       "GeckoView:GotoHistoryIndex",
       "GeckoView:LoadUri",
       "GeckoView:Reload",
       "GeckoView:Stop",
     ]);
 
     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.
   onEvent(aEvent, aData, aCallback) {
     debug `onEvent: event=${aEvent}, data=${aData}`;
 
     switch (aEvent) {
       case "GeckoView:GoBack":
--- a/mobile/android/modules/geckoview/GeckoViewSettings.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewSettings.jsm
@@ -38,16 +38,17 @@ const USER_AGENT_MODE_VR = 2;
 // Handles GeckoView settings including:
 // * multiprocess
 // * user agent override
 class GeckoViewSettings extends GeckoViewModule {
   onInit() {
     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",
     ]);
   }
 
   onEvent(aEvent, aData, aCallback) {
@@ -62,16 +63,17 @@ class GeckoViewSettings extends GeckoVie
 
   onSettingsUpdate() {
     const settings = this.settings;
     debug `onSettingsUpdate: ${settings}`;
 
     this.displayMode = settings.displayMode;
     this.userAgentMode = settings.userAgentMode;
     this.userAgentOverride = settings.userAgentOverride;
+    this.sessionContextId = settings.sessionContextId;
   }
 
   get useMultiprocess() {
     return this.browser.isRemoteBrowser;
   }
 
   get userAgent() {
     if (this.userAgentOverride !== null) {
@@ -107,11 +109,19 @@ class GeckoViewSettings extends GeckoVie
 
   get displayMode() {
     return this.window.docShell.displayMode;
   }
 
   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
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -3200,16 +3200,19 @@ already_AddRefed<nsILoadInfo> HttpBaseCh
         "docshell and necko should have the same userContextId attribute.");
     MOZ_ASSERT(
         docShellAttrs.mInIsolatedMozBrowser == attrs.mInIsolatedMozBrowser,
         "docshell and necko should have the same inIsolatedMozBrowser "
         "attribute.");
     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);
     newLoadInfo->SetOriginAttributes(attrs);
   }
 
   // Leave empty, we want a 'clean ground' when creating the new channel.
   // This will be ensured to be either set by the protocol handler or set