Bug 1507286 - Convert content-sessionStore.jsm to C++ r=mikedeboer,nika
☠☠ backed out by b08b9f22ad06 ☠ ☠
authorAlphan Chen <alchen@mozilla.com>
Fri, 25 Jan 2019 13:14:28 +0000
changeset 515431 0509a9edc58aa8f5a2a51e9684f93c913d6d55ab
parent 515430 9e7fe63b341a5a3a0479df93d6ebe2ded42f586d
child 515432 6f5c45fd0cc9dcc778cf8f6cbd7135d51b380828
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmikedeboer, nika
bugs1507286
milestone66.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 1507286 - Convert content-sessionStore.jsm to C++ r=mikedeboer,nika Test Plani: browser/components/sessionstore/test/ Differential Revision: https://phabricator.services.mozilla.com/D15845
browser/app/profile/firefox.js
browser/components/sessionstore/ContentRestore.jsm
browser/components/sessionstore/ContentSessionStore.jsm
browser/components/sessionstore/SessionStorage.jsm
browser/components/sessionstore/moz.build
dom/chrome-webidl/SessionStoreUtils.webidl
modules/libpref/init/StaticPrefList.h
toolkit/components/sessionstore/SessionStoreUtils.cpp
toolkit/components/sessionstore/SessionStoreUtils.h
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -853,18 +853,16 @@ pref("browser.sessionstore.upgradeBackup
 // End-users should not run sessionstore in debug mode
 pref("browser.sessionstore.debug", false);
 // Causes SessionStore to ignore non-final update messages from
 // browser tabs that were not caused by a flush from the parent.
 // This is a testing flag and should not be used by end-users.
 pref("browser.sessionstore.debug.no_auto_updates", false);
 // Forget closed windows/tabs after two weeks
 pref("browser.sessionstore.cleanup.forget_closed_after", 1209600000);
-// Maximum number of bytes of DOMSessionStorage data we collect per origin.
-pref("browser.sessionstore.dom_storage_limit", 2048);
 // Amount of failed SessionFile writes until we restart the worker.
 pref("browser.sessionstore.max_write_failures", 5);
 
 // Whether to warn the user when quitting, even though their tabs will be restored.
 pref("browser.sessionstore.warnOnQuit", false);
 
 // allow META refresh by default
 pref("accessibility.blockautorefresh", false);
--- a/browser/components/sessionstore/ContentRestore.jsm
+++ b/browser/components/sessionstore/ContentRestore.jsm
@@ -5,18 +5,16 @@
 "use strict";
 
 var EXPORTED_SYMBOLS = ["ContentRestore"];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm", this);
 
 ChromeUtils.defineModuleGetter(this, "SessionHistory",
   "resource://gre/modules/sessionstore/SessionHistory.jsm");
-ChromeUtils.defineModuleGetter(this, "SessionStorage",
-  "resource:///modules/sessionstore/SessionStorage.jsm");
 ChromeUtils.defineModuleGetter(this, "Utils",
   "resource://gre/modules/sessionstore/Utils.jsm");
 
 /**
  * This module implements the content side of session restoration. The chrome
  * side is handled by SessionStore.jsm. The functions in this module are called
  * by content-sessionStore.js based on messages received from SessionStore.jsm
  * (or, in one case, based on a "load" event). Each tab has its own
@@ -134,17 +132,17 @@ ContentRestoreInternal.prototype = {
     this._historyListener = listener;
 
     // Make sure to reset the capabilities and attributes in case this tab gets
     // reused.
     SessionStoreUtils.restoreDocShellCapabilities(this.docShell, tabData.disallow);
 
 
     if (tabData.storage && this.docShell instanceof Ci.nsIDocShell) {
-      SessionStorage.restore(this.docShell, tabData.storage);
+      SessionStoreUtils.restoreSessionStorage(this.docShell, tabData.storage);
       delete tabData.storage;
     }
 
     // Add a progress listener to correctly handle browser.loadURI()
     // calls from foreign code.
     this._progressListener = new ProgressListener(this.docShell, {
       onStartRequest: () => {
         // Some code called browser.loadURI() on a pending tab. It's safe to
--- a/browser/components/sessionstore/ContentSessionStore.jsm
+++ b/browser/components/sessionstore/ContentSessionStore.jsm
@@ -13,18 +13,16 @@ ChromeUtils.import("resource://gre/modul
 function debug(msg) {
   Services.console.logStringMessage("SessionStoreContent: " + msg);
 }
 
 ChromeUtils.defineModuleGetter(this, "ContentRestore",
   "resource:///modules/sessionstore/ContentRestore.jsm");
 ChromeUtils.defineModuleGetter(this, "SessionHistory",
   "resource://gre/modules/sessionstore/SessionHistory.jsm");
-ChromeUtils.defineModuleGetter(this, "SessionStorage",
-  "resource:///modules/sessionstore/SessionStorage.jsm");
 ChromeUtils.defineModuleGetter(this, "Utils",
   "resource://gre/modules/sessionstore/Utils.jsm");
 
 // A bound to the size of data to store for DOM Storage.
 const DOM_STORAGE_LIMIT_PREF = "browser.sessionstore.dom_storage_limit";
 
 // This pref controls whether or not we send updates to the parent on a timeout
 // or not, and should only be used for tests or debugging.
@@ -528,17 +526,20 @@ class SessionStorageListener extends Han
     }
 
     let {content} = this.mm;
 
     // We need the entire session storage, let's reset the pending individual change
     // messages.
     this.resetChanges();
 
-    this.messageQueue.push("storage", () => SessionStorage.collect(content));
+    this.messageQueue.push("storage", () => {
+      let data = SessionStoreUtils.collectSessionStorage(content);
+      return Object.keys(data).length ? data : null;
+    });
   }
 
   onPageLoadCompleted() {
     this.collect();
   }
 
   onPageLoadStarted() {
     this.resetEventListener();
deleted file mode 100644
--- a/browser/components/sessionstore/SessionStorage.jsm
+++ /dev/null
@@ -1,211 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
-* License, v. 2.0. If a copy of the MPL was not distributed with this file,
-* You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-var EXPORTED_SYMBOLS = ["SessionStorage"];
-
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-// A bound to the size of data to store for DOM Storage.
-const DOM_STORAGE_LIMIT_PREF = "browser.sessionstore.dom_storage_limit";
-
-// Returns the principal for a given |frame| contained in a given |docShell|.
-function getPrincipalForFrame(docShell, frame) {
-  let ssm = Services.scriptSecurityManager;
-  let uri = frame.document.documentURIObject;
-  return ssm.getDocShellCodebasePrincipal(uri, docShell);
-}
-
-var SessionStorage = Object.freeze({
-  /**
-   * Updates all sessionStorage "super cookies"
-   * @param content
-   *        A tab's global, i.e. the root frame we want to collect for.
-   * @return Returns a nested object that will have hosts as keys and per-origin
-   *         session storage data as strings. For example:
-   *         {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
-   */
-  collect(content) {
-    return SessionStorageInternal.collect(content);
-  },
-
-  /**
-   * Restores all sessionStorage "super cookies".
-   * @param aDocShell
-   *        A tab's docshell (containing the sessionStorage)
-   * @param aStorageData
-   *        A nested object with storage data to be restored that has hosts as
-   *        keys and per-origin session storage data as strings. For example:
-   *        {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
-   */
-  restore(aDocShell, aStorageData) {
-    SessionStorageInternal.restore(aDocShell, aStorageData);
-  },
-});
-
-/**
- * Calls the given callback |cb|, passing |frame| and each of its descendants.
- */
-function forEachNonDynamicChildFrame(frame, cb) {
-  // Call for current frame.
-  cb(frame);
-
-  // Call the callback recursively for each descendant.
-  SessionStoreUtils.forEachNonDynamicChildFrame(frame, subframe => {
-    return forEachNonDynamicChildFrame(subframe, cb);
-  });
-}
-
-var SessionStorageInternal = {
-  /**
-   * Reads all session storage data from the given docShell.
-   * @param content
-   *        A tab's global, i.e. the root frame we want to collect for.
-   * @return Returns a nested object that will have hosts as keys and per-origin
-   *         session storage data as strings. For example:
-   *         {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
-   */
-  collect(content) {
-    let data = {};
-    let visitedOrigins = new Set();
-    let docShell = content.docShell;
-
-    forEachNonDynamicChildFrame(content, frame => {
-      let principal = getPrincipalForFrame(docShell, frame);
-      if (!principal) {
-        return;
-      }
-
-      // Get the origin of the current history entry
-      // and use that as a key for the per-principal storage data.
-      let origin;
-      try {
-        // The origin getter may throw for about:blank iframes as of bug 1340710,
-        // but we should ignore them anyway.
-        origin = principal.origin;
-      } catch (e) {
-        return;
-      }
-      if (visitedOrigins.has(origin)) {
-        // Don't read a host twice.
-        return;
-      }
-
-      // Mark the current origin as visited.
-      visitedOrigins.add(origin);
-
-      let originData = this._readEntry(principal, docShell);
-      if (Object.keys(originData).length) {
-        data[origin] = originData;
-      }
-    });
-
-    return Object.keys(data).length ? data : null;
-  },
-
-  /**
-   * Writes session storage data to the given tab.
-   * @param aDocShell
-   *        A tab's docshell (containing the sessionStorage)
-   * @param aStorageData
-   *        A nested object with storage data to be restored that has hosts as
-   *        keys and per-origin session storage data as strings. For example:
-   *        {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
-   */
-  restore(aDocShell, aStorageData) {
-    for (let origin of Object.keys(aStorageData)) {
-      let data = aStorageData[origin];
-
-      let principal;
-
-      try {
-        // NOTE: In capture() we record the full origin for the URI which the
-        // sessionStorage is being captured for. As of bug 1235657 this code
-        // stopped parsing any origins which have originattributes correctly, as
-        // it decided to use the origin attributes from the docshell, and try to
-        // interpret the origin as a URI. Since bug 1353844 this code now correctly
-        // parses the full origin, and then discards the origin attributes, to
-        // make the behavior line up with the original intentions in bug 1235657
-        // while preserving the ability to read all session storage from
-        // previous versions. In the future, if this behavior is desired, we may
-        // want to use the spec instead of the origin as the key, and avoid
-        // transmitting origin attribute information which we then discard when
-        // restoring.
-        //
-        // If changing this logic, make sure to also change the principal
-        // computation logic in SessionStore::_sendRestoreHistory.
-        let attrs = aDocShell.getOriginAttributes();
-        let dataPrincipal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin);
-        principal = Services.scriptSecurityManager.createCodebasePrincipal(dataPrincipal.URI, attrs);
-      } catch (e) {
-        console.error(e);
-        continue;
-      }
-
-      let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
-
-      // There is no need to pass documentURI, it's only used to fill
-      // documentURI property of domstorage event, which in this case has no
-      // consumer. Prevention of events in case of missing documentURI will be
-      // solved in a followup bug to bug 600307.
-      // Null window because the current window doesn't match the principal yet
-      // and loads about:blank.
-      let storage = storageManager.createStorage(null, principal, "", aDocShell.usePrivateBrowsing);
-
-      for (let key of Object.keys(data)) {
-        try {
-          storage.setItem(key, data[key]);
-        } catch (e) {
-          // throws e.g. for URIs that can't have sessionStorage
-          console.error(e);
-        }
-      }
-    }
-  },
-
-  /**
-   * Reads an entry in the session storage data contained in a tab's history.
-   * @param aURI
-   *        That history entry uri
-   * @param aDocShell
-   *        A tab's docshell (containing the sessionStorage)
-   */
-  _readEntry(aPrincipal, aDocShell) {
-    let hostData = {};
-    let storage;
-
-    let window = aDocShell.domWindow;
-
-    try {
-      let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
-      storage = storageManager.getStorage(window, aPrincipal);
-      storage.length; // XXX: Bug 1232955 - storage.length can throw, catch that failure
-    } catch (e) {
-      // sessionStorage might throw if it's turned off, see bug 458954
-      storage = null;
-    }
-
-    if (!storage || !storage.length) {
-      return hostData;
-    }
-
-    // If the DOMSessionStorage contains too much data, ignore it.
-    let usage = window.windowUtils.getStorageUsage(storage);
-    if (usage > Services.prefs.getIntPref(DOM_STORAGE_LIMIT_PREF)) {
-      return hostData;
-    }
-
-    for (let i = 0; i < storage.length; i++) {
-      try {
-        let key = storage.key(i);
-        hostData[key] = storage.getItem(key);
-      } catch (e) {
-        // This currently throws for secured items (cf. bug 442048).
-      }
-    }
-
-    return hostData;
-  },
-};
--- a/browser/components/sessionstore/moz.build
+++ b/browser/components/sessionstore/moz.build
@@ -15,17 +15,16 @@ EXTRA_JS_MODULES.sessionstore = [
     'GlobalState.jsm',
     'RecentlyClosedTabsAndWindowsMenuUtils.jsm',
     'RunState.jsm',
     'SessionCookies.jsm',
     'SessionFile.jsm',
     'SessionMigration.jsm',
     'SessionSaver.jsm',
     'SessionStartup.jsm',
-    'SessionStorage.jsm',
     'SessionStore.jsm',
     'SessionWorker.js',
     'SessionWorker.jsm',
     'StartupPerformance.jsm',
     'TabAttributes.jsm',
     'TabState.jsm',
     'TabStateCache.jsm',
     'TabStateFlusher.jsm',
--- a/dom/chrome-webidl/SessionStoreUtils.webidl
+++ b/dom/chrome-webidl/SessionStoreUtils.webidl
@@ -107,16 +107,37 @@ namespace SessionStoreUtils {
    *
    * @param  doc
    *         DOMDocument instance to obtain form data for.
    * @return object
    *         Form data encoded in an object.
    */
   CollectedFormData collectFormData(Document document);
   boolean restoreFormData(Document document, optional CollectedFormData data);
+
+  /**
+   * Updates all sessionStorage "super cookies"
+   * @param content
+   *        A tab's global, i.e. the root frame we want to collect for.
+   * @return Returns a nested object that will have hosts as keys and per-origin
+   *         session storage data as strings. For example:
+   *         {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
+   */
+  record<DOMString, record<DOMString, DOMString>> collectSessionStorage(WindowProxy window);
+
+  /**
+   * Restores all sessionStorage "super cookies".
+   * @param aDocShell
+   *        A tab's docshell (containing the sessionStorage)
+   * @param aStorageData
+   *        A nested object with storage data to be restored that has hosts as
+   *        keys and per-origin session storage data as strings. For example:
+   *        {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
+   */
+   void restoreSessionStorage(nsIDocShell docShell, record<DOMString, record<DOMString, DOMString>> data);
 };
 
 dictionary SSScrollPositionDict {
   ByteString scroll;
 };
 
 dictionary CollectedFileListValue
 {
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -1797,16 +1797,26 @@ PREF("network.predictor.cleaned-up", boo
 // A testing flag.
 VARCACHE_PREF(
   "network.predictor.doing-tests",
    network_predictor_doing_tests,
   bool, false
 )
 
 //---------------------------------------------------------------------------
+// ContentSessionStore prefs
+//---------------------------------------------------------------------------
+// Maximum number of bytes of DOMSessionStorage data we collect per origin.
+VARCACHE_PREF(
+  "browser.sessionstore.dom_storage_limit",
+  browser_sessionstore_dom_storage_limit,
+  uint32_t, 2048
+)
+
+//---------------------------------------------------------------------------
 // Preferences prefs
 //---------------------------------------------------------------------------
 
 PREF("preferences.allow.omt-write", bool, true)
 
 //---------------------------------------------------------------------------
 // Privacy prefs
 //---------------------------------------------------------------------------
--- a/toolkit/components/sessionstore/SessionStoreUtils.cpp
+++ b/toolkit/components/sessionstore/SessionStoreUtils.cpp
@@ -967,9 +967,180 @@ MOZ_CAN_RUN_SCRIPT_BOUNDARY
       } else {
         JS::Rooted<JS::Value> object(
             aGlobal.Context(), JS::ObjectValue(*entry.mValue.GetAsObject()));
         SetElementAsObject(aGlobal.Context(), node, object);
       }
     }
   }
   return true;
-}
\ No newline at end of file
+}
+
+/* Read entries in the session storage data contained in a tab's history. */
+static void ReadAllEntriesFromStorage(
+    nsPIDOMWindowOuter* aWindow,
+    nsTHashtable<nsCStringHashKey>& aVisitedOrigins,
+    Record<nsString, Record<nsString, nsString>>& aRetVal) {
+  nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
+  if (!docShell) {
+    return;
+  }
+
+  Document* doc = aWindow->GetDoc();
+  if (!doc) {
+    return;
+  }
+  nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
+  if (!principal) {
+    return;
+  }
+
+  nsAutoCString origin;
+  nsresult rv = principal->GetOrigin(origin);
+  if (NS_FAILED(rv) || aVisitedOrigins.Contains(origin)) {
+    // Don't read a host twice.
+    return;
+  }
+
+  /* Completed checking for recursion and is about to read storage*/
+  nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(docShell);
+  if (!storageManager) {
+    return;
+  }
+  RefPtr<Storage> storage;
+  storageManager->GetStorage(aWindow->GetCurrentInnerWindow(), principal, false,
+                             getter_AddRefs(storage));
+  if (!storage) {
+    return;
+  }
+  mozilla::IgnoredErrorResult result;
+  uint32_t len = storage->GetLength(*principal, result);
+  if (result.Failed() || len == 0) {
+    return;
+  }
+  int64_t storageUsage = storage->GetOriginQuotaUsage();
+  if (storageUsage > StaticPrefs::browser_sessionstore_dom_storage_limit()) {
+    return;
+  }
+
+  Record<nsString, Record<nsString, nsString>>::EntryType* recordEntry = nullptr;
+  for (uint32_t i = 0; i < len; i++) {
+    Record<nsString, nsString>::EntryType entry;
+    mozilla::IgnoredErrorResult res;
+    storage->Key(i, entry.mKey, *principal, res);
+    if (res.Failed()) {
+      continue;
+    }
+
+    storage->GetItem(entry.mKey, entry.mValue, *principal, res);
+    if (res.Failed()) {
+      continue;
+    }
+
+    if (!recordEntry) {
+      recordEntry = aRetVal.Entries().AppendElement();
+      recordEntry->mKey = NS_ConvertUTF8toUTF16(origin);
+      aVisitedOrigins.PutEntry(origin);
+    }
+    recordEntry->mValue.Entries().AppendElement(std::move(entry));
+  }
+}
+
+/* Collect Collect session storage from current frame and all child frame */
+static void CollectedSessionStorageInternal(
+    JSContext* aCx, BrowsingContext* aBrowsingContext,
+    nsTHashtable<nsCStringHashKey>& aVisitedOrigins,
+    Record<nsString, Record<nsString, nsString>>& aRetVal) {
+  /* Collect session store from current frame */
+  nsPIDOMWindowOuter* window = aBrowsingContext->GetDOMWindow();
+  if (!window) {
+    return;
+  }
+  ReadAllEntriesFromStorage(window, aVisitedOrigins, aRetVal);
+
+  /* Collect session storage from all child frame */
+  nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
+  if (!docShell) {
+    return;
+  }
+  int32_t length;
+  nsresult rv = docShell->GetChildCount(&length);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+  for (int32_t i = 0; i < length; ++i) {
+    nsCOMPtr<nsIDocShellTreeItem> item;
+    docShell->GetChildAt(i, getter_AddRefs(item));
+    if (!item) {
+      return;
+    }
+    nsCOMPtr<nsIDocShell> childDocShell(do_QueryInterface(item));
+    if (!childDocShell) {
+      return;
+    }
+    bool isDynamic = false;
+    rv = childDocShell->GetCreatedDynamically(&isDynamic);
+    if (NS_SUCCEEDED(rv) && isDynamic) {
+      continue;
+    }
+    CollectedSessionStorageInternal(
+        aCx, nsDocShell::Cast(childDocShell)->GetBrowsingContext(),
+        aVisitedOrigins, aRetVal);
+  }
+}
+
+/* static */ void SessionStoreUtils::CollectSessionStorage(
+    const GlobalObject& aGlobal, WindowProxyHolder& aWindow,
+    Record<nsString, Record<nsString, nsString>>& aRetVal) {
+  nsTHashtable<nsCStringHashKey> visitedOrigins;
+  CollectedSessionStorageInternal(aGlobal.Context(), aWindow.get(),
+                                  visitedOrigins, aRetVal);
+}
+
+/* static */ void SessionStoreUtils::RestoreSessionStorage(
+    const GlobalObject& aGlobal, nsIDocShell* aDocShell,
+    const Record<nsString, Record<nsString, nsString>>& aData) {
+  for (auto& entry : aData.Entries()) {
+    // NOTE: In capture() we record the full origin for the URI which the
+    // sessionStorage is being captured for. As of bug 1235657 this code
+    // stopped parsing any origins which have originattributes correctly, as
+    // it decided to use the origin attributes from the docshell, and try to
+    // interpret the origin as a URI. Since bug 1353844 this code now correctly
+    // parses the full origin, and then discards the origin attributes, to
+    // make the behavior line up with the original intentions in bug 1235657
+    // while preserving the ability to read all session storage from
+    // previous versions. In the future, if this behavior is desired, we may
+    // want to use the spec instead of the origin as the key, and avoid
+    // transmitting origin attribute information which we then discard when
+    // restoring.
+    //
+    // If changing this logic, make sure to also change the principal
+    // computation logic in SessionStore::_sendRestoreHistory.
+
+    // OriginAttributes are always after a '^' character
+    int32_t pos = entry.mKey.RFindChar('^');
+    nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateCodebasePrincipal(
+        NS_ConvertUTF16toUTF8(Substring(entry.mKey, 0, pos)));
+    nsresult rv;
+    nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(aDocShell, &rv);
+    if (NS_FAILED(rv)) {
+      return;
+    }
+    RefPtr<Storage> storage;
+    // There is no need to pass documentURI, it's only used to fill documentURI
+    // property of domstorage event, which in this case has no consumer.
+    // Prevention of events in case of missing documentURI will be solved in a
+    // followup bug to bug 600307.
+    // Null window because the current window doesn't match the principal yet
+    // and loads about:blank.
+    storageManager->CreateStorage(nullptr, principal, EmptyString(), false, getter_AddRefs(storage));
+    if (!storage) {
+      continue;
+    }
+    for (auto& InnerEntry : entry.mValue.Entries()) {
+      IgnoredErrorResult result;
+      storage->SetItem(InnerEntry.mKey, InnerEntry.mValue, *principal, result);
+      if (result.Failed()) {
+        NS_WARNING("storage set item failed!");
+      }
+    }
+  }
+}
--- a/toolkit/components/sessionstore/SessionStoreUtils.h
+++ b/toolkit/components/sessionstore/SessionStoreUtils.h
@@ -53,14 +53,22 @@ class SessionStoreUtils {
                                     const SSScrollPositionDict& data);
 
   static void CollectFormData(const GlobalObject& aGlobal, Document& aDocument,
                               CollectedFormData& aRetVal);
 
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   static bool RestoreFormData(const GlobalObject& aGlobal, Document& aDocument,
                               const CollectedFormData& aData);
+
+  static void CollectSessionStorage(
+      const GlobalObject& aGlobal, WindowProxyHolder& aWindow,
+      Record<nsString, Record<nsString, nsString>>& aRetVal);
+
+  static void RestoreSessionStorage(
+      const GlobalObject& aGlobal, nsIDocShell* aDocShell,
+      const Record<nsString, Record<nsString, nsString>>& aData);
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_SessionStoreUtils_h