Bug 1497144 part 2 - Get rid of ScrollPosition.jsm r=JanH,nika
authorAlphan Chen <alchen@mozilla.com>
Tue, 23 Oct 2018 08:17:51 +0000
changeset 490894 50d03049245af423292d4c69621cfd1c3159401f
parent 490893 5b7afb9ada5e29dabba18a040bdcb509f3a3a315
child 490895 ac5060eb361cd23100e28128f810020f2e422878
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersJanH, nika
bugs1497144
milestone65.0a1
Bug 1497144 part 2 - Get rid of ScrollPosition.jsm r=JanH,nika Differential Revision: https://phabricator.services.mozilla.com/D8534
browser/components/sessionstore/ContentRestore.jsm
mobile/android/chrome/geckoview/GeckoViewContent.js
mobile/android/components/SessionStore.js
toolkit/modules/moz.build
toolkit/modules/sessionstore/ScrollPosition.jsm
toolkit/modules/sessionstore/Utils.jsm
--- a/browser/components/sessionstore/ContentRestore.jsm
+++ b/browser/components/sessionstore/ContentRestore.jsm
@@ -16,40 +16,16 @@ ChromeUtils.defineModuleGetter(this, "Se
   "resource:///modules/sessionstore/SessionStorage.jsm");
 ChromeUtils.defineModuleGetter(this, "Utils",
   "resource://gre/modules/sessionstore/Utils.jsm");
 
 const ssu = Cc["@mozilla.org/browser/sessionstore/utils;1"]
               .getService(Ci.nsISessionStoreUtils);
 
 /**
- * Restores frame tree |data|, starting at the given root |frame|. As the
- * function recurses into descendant frames it will call cb(frame, data) for
- * each frame it encounters, starting with the given root.
- */
-function restoreFrameTreeData(frame, data, cb) {
-  // Restore data for the root frame.
-  // The callback can abort by returning false.
-  if (cb(frame, data) === false) {
-    return;
-  }
-
-  if (!data.hasOwnProperty("children")) {
-    return;
-  }
-
-  // Recurse into child frames.
-  ssu.forEachNonDynamicChildFrame(frame, (subframe, index) => {
-    if (data.children[index]) {
-      restoreFrameTreeData(subframe, data.children[index], cb);
-    }
-  });
-}
-
-/**
  * 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
  * ContentRestore instance, constructed by content-sessionStore.js.
  *
  * In a typical restore, content-sessionStore.js will call the following based
  * on messages and events it receives:
@@ -307,25 +283,25 @@ ContentRestoreInternal.prototype = {
       return;
     }
     let {formdata, scrollPositions} = this._restoringDocument;
     this._restoringDocument = null;
 
     let window = this.docShell.domWindow;
 
     // Restore form data.
-    restoreFrameTreeData(window, formdata, (frame, data) => {
+    Utils.restoreFrameTreeData(window, formdata, (frame, data) => {
       // restore() will return false, and thus abort restoration for the
       // current |frame| and its descendants, if |data.url| is given but
       // doesn't match the loaded document's URL.
       return FormData.restore(frame, data);
     });
 
     // Restore scroll data.
-    restoreFrameTreeData(window, scrollPositions, (frame, data) => {
+    Utils.restoreFrameTreeData(window, scrollPositions, (frame, data) => {
       if (data.scroll) {
         ssu.restoreScrollPosition(frame, data.scroll);
       }
     });
   },
 
   /**
    * Cancel an ongoing restore. This function can be called any time between
--- a/mobile/android/chrome/geckoview/GeckoViewContent.js
+++ b/mobile/android/chrome/geckoview/GeckoViewContent.js
@@ -6,21 +6,23 @@
 ChromeUtils.import("resource://gre/modules/GeckoViewContentModule.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   FormData: "resource://gre/modules/FormData.jsm",
   FormLikeFactory: "resource://gre/modules/FormLikeFactory.jsm",
   GeckoViewAutoFill: "resource://gre/modules/GeckoViewAutoFill.jsm",
   PrivacyFilter: "resource://gre/modules/sessionstore/PrivacyFilter.jsm",
-  ScrollPosition: "resource://gre/modules/ScrollPosition.jsm",
   Services: "resource://gre/modules/Services.jsm",
   SessionHistory: "resource://gre/modules/sessionstore/SessionHistory.jsm",
 });
 
+const ssu = Cc["@mozilla.org/browser/sessionstore/utils;1"]
+              .getService(Ci.nsISessionStoreUtils);
+
 class GeckoViewContent extends GeckoViewContentModule {
   onInit() {
     debug `onInit`;
 
     // We don't load this in the global namespace because
     // a Utils.jsm in a11y will clobber us.
     XPCOMUtils.defineLazyModuleGetters(this, {
       Utils: "resource://gre/modules/sessionstore/Utils.jsm",
@@ -81,17 +83,17 @@ class GeckoViewContent extends GeckoView
     removeEventListener("MozDOMFullscreen:Exit", this);
     removeEventListener("MozDOMFullscreen:Exited", this);
     removeEventListener("MozDOMFullscreen:Request", this);
     removeEventListener("contextmenu", this, { capture: true });
   }
 
   collectSessionState() {
     let history = SessionHistory.collect(docShell);
-    let [formdata, scrolldata] = this.Utils.mapFrameTree(content, FormData.collect, ScrollPosition.collect);
+    let [formdata, scrolldata] = this.Utils.mapFrameTree(content, FormData.collect, ssu.collectScrollPosition.bind(ssu));
 
     // Save the current document resolution.
     let zoom = { value: 1 };
     let domWindowUtils = content.windowUtils;
     domWindowUtils.getResolution(zoom);
     scrolldata = scrolldata || {};
     scrolldata.zoom = {};
     scrolldata.zoom.resolution = zoom.value;
@@ -196,24 +198,33 @@ class GeckoViewContent extends GeckoView
         this._savedState = JSON.parse(aMsg.data.state);
 
         if (this._savedState.history) {
           let restoredHistory = SessionHistory.restore(docShell, this._savedState.history);
 
           addEventListener("load", _ => {
             const formdata = this._savedState.formdata;
             if (formdata) {
-              FormData.restoreTree(content, formdata);
+              this.Utils.restoreFrameTreeData(content, formdata, (frame, data) => {
+                // restore() will return false, and thus abort restoration for the
+                // current |frame| and its descendants, if |data.url| is given but
+                // doesn't match the loaded document's URL.
+                return FormData.restore(frame, data);
+              });
             }
           }, {capture: true, mozSystemGroup: true, once: true});
 
           addEventListener("pageshow", _ => {
             const scrolldata = this._savedState.scrolldata;
             if (scrolldata) {
-              ScrollPosition.restoreTree(content, scrolldata);
+              this.Utils.restoreFrameTreeData(content, scrolldata, (frame, data) => {
+                if (data.scroll) {
+                  ssu.restoreScrollPosition(frame, data.scroll);
+                }
+              });
             }
             delete this._savedState;
           }, {capture: true, mozSystemGroup: true, once: true});
 
           if (!this.progressFilter) {
             this.progressFilter =
               Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
               .createInstance(Ci.nsIWebProgress);
--- a/mobile/android/components/SessionStore.js
+++ b/mobile/android/components/SessionStore.js
@@ -8,26 +8,28 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   EventDispatcher: "resource://gre/modules/Messaging.jsm",
   FormData: "resource://gre/modules/FormData.jsm",
   OS: "resource://gre/modules/osfile.jsm",
   PrivacyFilter: "resource://gre/modules/sessionstore/PrivacyFilter.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
-  ScrollPosition: "resource://gre/modules/ScrollPosition.jsm",
   SessionHistory: "resource://gre/modules/sessionstore/SessionHistory.jsm",
   SharedPreferences: "resource://gre/modules/SharedPreferences.jsm",
   Task: "resource://gre/modules/Task.jsm",
   TelemetryStopwatch: "resource://gre/modules/TelemetryStopwatch.jsm",
   Utils: "resource://gre/modules/sessionstore/Utils.jsm",
 });
 
 XPCOMUtils.defineLazyModuleGetter(this, "Log", "resource://gre/modules/AndroidLog.jsm", "AndroidLog");
 
+const ssu = Cc["@mozilla.org/browser/sessionstore/utils;1"]
+              .getService(Ci.nsISessionStoreUtils);
+
 function dump(a) {
   Services.console.logStringMessage(a);
 }
 
 let loggingEnabled = false;
 
 function log(a) {
   if (!loggingEnabled) {
@@ -927,17 +929,17 @@ SessionStore.prototype = {
 
     // Neither bother if we're yet to restore the previous scroll position.
     if (aBrowser.__SS_restoreDataOnLoad || aBrowser.__SS_restoreDataOnPageshow) {
       return;
     }
 
     // Save the scroll position itself.
     let content = aBrowser.contentWindow;
-    let [scrolldata] = Utils.mapFrameTree(content, ScrollPosition.collect);
+    let [scrolldata] = Utils.mapFrameTree(content, ssu.collectScrollPosition.bind(ssu));
     scrolldata = scrolldata || {};
 
     // Save the current document resolution.
     let zoom = { value: 1 };
     content.windowUtils.getResolution(zoom);
     scrolldata.zoom = {};
     scrolldata.zoom.resolution = zoom.value;
     log("onTabScroll() zoom level: " + zoom.value);
@@ -1379,17 +1381,22 @@ SessionStore.prototype = {
   },
 
   /**
   * Takes serialized form text data and restores it into the given browser.
   */
   _restoreTextData: function ss_restoreTextData(aFormData, aBrowser) {
     if (aFormData) {
       log("_restoreTextData()");
-      FormData.restoreTree(aBrowser.contentWindow, aFormData);
+      Utils.restoreFrameTreeData(aBrowser.contentWindow, aFormData, (frame, data) => {
+        // restore() will return false, and thus abort restoration for the
+        // current |frame| and its descendants, if |data.url| is given but
+        // doesn't match the loaded document's URL.
+        return FormData.restore(frame, data);
+      });
     }
   },
 
   /**
    * Restores the zoom level of the window. This needs to be called before
    * first paint/load (whichever comes first) to take any effect.
    */
   _restoreZoom: function ss_restoreZoom(aScrollData, aBrowser) {
@@ -1406,17 +1413,21 @@ SessionStore.prototype = {
   },
 
   /**
   * Takes serialized scroll positions and restores them into the given browser.
   */
   _restoreScrollPosition: function ss_restoreScrollPosition(aScrollData, aBrowser) {
     if (aScrollData) {
       log("_restoreScrollPosition()");
-      ScrollPosition.restoreTree(aBrowser.contentWindow, aScrollData);
+      Utils.restoreFrameTreeData(aBrowser.contentWindow, aScrollData, (frame, data) => {
+        if (data.scroll) {
+          ssu.restoreScrollPosition(frame, data.scroll);
+        }
+      });
     }
   },
 
   getBrowserState: function ss_getBrowserState() {
     return this._getCurrentState();
   },
 
   _restoreWindow: function ss_restoreWindow(aData) {
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -236,17 +236,16 @@ EXTRA_JS_MODULES += [
     'RemoteSecurityUI.jsm',
     'RemoteWebProgress.jsm',
     'ResetProfile.jsm',
     'ResponsivenessMonitor.jsm',
     'SelectParentHelper.jsm',
     'ServiceRequest.jsm',
     'Services.jsm',
     'sessionstore/FormData.jsm',
-    'sessionstore/ScrollPosition.jsm',
     'ShortcutUtils.jsm',
     'Sqlite.jsm',
     'Task.jsm',
     'Timer.jsm',
     'Troubleshoot.jsm',
     'UpdateUtils.jsm',
     'WebChannel.jsm',
     'WebProgressChild.jsm',
deleted file mode 100644
--- a/toolkit/modules/sessionstore/ScrollPosition.jsm
+++ /dev/null
@@ -1,104 +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 = ["ScrollPosition"];
-
-/**
- * It provides methods to collect scroll positions from single frames and to
- * restore scroll positions for frame trees.
- *
- * This is a child process module.
- */
-var ScrollPosition = Object.freeze({
-  collect(frame) {
-    return ScrollPositionInternal.collect(frame);
-  },
-
-  restore(frame, value) {
-    ScrollPositionInternal.restore(frame, value);
-  },
-
-  restoreTree(root, data) {
-    ScrollPositionInternal.restoreTree(root, data);
-  },
-});
-
-/**
- * This module's internal API.
- */
-var ScrollPositionInternal = {
-  /**
-   * Collects scroll position data for any given |frame| in the frame hierarchy.
-   *
-   * @param frame (DOMWindow)
-   *
-   * @return {scroll: "x,y"} e.g. {scroll: "100,200"}
-   *         Returns null when there is no scroll data we want to store for the
-   *         given |frame|.
-   */
-  collect(frame) {
-    let utils = frame.windowUtils;
-    let scrollX = {}, scrollY = {};
-    utils.getScrollXY(false /* no layout flush */, scrollX, scrollY);
-
-    if (scrollX.value || scrollY.value) {
-      return {scroll: scrollX.value + "," + scrollY.value};
-    }
-
-    return null;
-  },
-
-  /**
-   * Restores scroll position data for any given |frame| in the frame hierarchy.
-   *
-   * @param frame (DOMWindow)
-   * @param value (object, see collect())
-   */
-  restore(frame, value) {
-    let match;
-
-    if (value && (match = /(\d+),(\d+)/.exec(value))) {
-      frame.scrollTo(match[1], match[2]);
-    }
-  },
-
-  /**
-   * Restores scroll position data for the current frame hierarchy starting at
-   * |root| using the given scroll position |data|.
-   *
-   * If the given |root| frame's hierarchy doesn't match that of the given
-   * |data| object we will silently discard data for unreachable frames. We
-   * may as well assign scroll positions to the wrong frames if some were
-   * reordered or removed.
-   *
-   * @param root (DOMWindow)
-   * @param data (object)
-   *        {
-   *          scroll: "100,200",
-   *          children: [
-   *            {scroll: "100,200"},
-   *            null,
-   *            {scroll: "200,300", children: [ ... ]}
-   *          ]
-   *        }
-   */
-  restoreTree(root, data) {
-    if (data.hasOwnProperty("scroll")) {
-      this.restore(root, data.scroll);
-    }
-
-    if (!data.hasOwnProperty("children")) {
-      return;
-    }
-
-    let frames = root.frames;
-    data.children.forEach((child, index) => {
-      if (child && index < frames.length) {
-        this.restoreTree(frames[index], child);
-      }
-    });
-  },
-};
--- a/toolkit/modules/sessionstore/Utils.jsm
+++ b/toolkit/modules/sessionstore/Utils.jsm
@@ -194,9 +194,33 @@ var Utils = Object.freeze({
       if (!children[i].length) {
         continue;
       }
       objs[i].children = children[i];
     }
 
     return objs.map((obj) => Object.getOwnPropertyNames(obj).length ? obj : null);
   },
+
+  /**
+   * Restores frame tree |data|, starting at the given root |frame|. As the
+   * function recurses into descendant frames it will call cb(frame, data) for
+   * each frame it encounters, starting with the given root.
+   */
+  restoreFrameTreeData(frame, data, cb) {
+    // Restore data for the root frame.
+    // The callback can abort by returning false.
+    if (cb(frame, data) === false) {
+      return;
+    }
+
+    if (!data.hasOwnProperty("children")) {
+      return;
+    }
+
+    // Recurse into child frames.
+    ssu.forEachNonDynamicChildFrame(frame, (subframe, index) => {
+      if (data.children[index]) {
+        this.restoreFrameTreeData(subframe, data.children[index], cb);
+      }
+    });
+  },
 });