Bug 1648158 - Store SHIP (Session History in Parent Process) support on Android. r=geckoview-reviewers,owlish

Instead of using SessionStateAggregator.js send() to collect history data and send the event StateUpdated to the app, the new GeckoViewSessionStore is used. It is enabled when fission is true and initialized by GeckoViewStartup when Services.appinfo.sessionHistoryInParent is true. It will observe two existing topics: browsing-context-did-set-embedder, which creates the history listener, and browsing-context-discarded, which unregisters it. When the history listener is created, it will collect history from the parent. It will only collect data when the current uri is not “about:blank” and when the history count is greater than 0. It uses SessionHistory’s collectFromParent.

The new code path was manually confirmed by debugging the app history delegate to see that the state map contains all the history data.

Differential Revision: https://phabricator.services.mozilla.com/D150020
This commit is contained in:
Agi Sferro 2022-08-17 17:03:27 +00:00
Родитель 004501a470
Коммит cd000ebc03
4 изменённых файлов: 235 добавлений и 1 удалений

Просмотреть файл

@ -12,6 +12,15 @@ const { GeckoViewUtils } = ChromeUtils.import(
const { GeckoViewActorParent } = ChromeUtils.import(
"resource://gre/modules/GeckoViewActorParent.jsm"
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const lazy = {};
ChromeUtils.defineModuleGetter(
lazy,
"SessionHistory",
"resource://gre/modules/sessionstore/SessionHistory.jsm"
);
const { debug, warn } = GeckoViewUtils.initLogging("GeckoViewContentParent");
@ -21,6 +30,20 @@ class GeckoViewContentParent extends GeckoViewActorParent {
}
restoreState({ history, switchId, formdata, scrolldata }) {
if (Services.appinfo.sessionHistoryInParent) {
const { browsingContext } = this.browser;
lazy.SessionHistory.restoreFromParent(
browsingContext.sessionHistory,
history
);
// TODO Bug 1648158 this should include scroll, form history, etc
return SessionStoreUtils.initializeRestore(
browsingContext,
SessionStoreUtils.constructSessionStoreRestoreData()
);
}
// Restoring is made of two parts. First we need to restore the history
// of the tab and navigating to the current page, after the page
// navigates to the current page we need to restore the state of the
@ -35,7 +58,7 @@ class GeckoViewContentParent extends GeckoViewActorParent {
});
if (!formdata && !scrolldata) {
return;
return null;
}
const progressFilter = Cc[
@ -63,5 +86,6 @@ class GeckoViewContentParent extends GeckoViewActorParent {
const flags = Ci.nsIWebProgress.NOTIFY_LOCATION;
progressFilter.addProgressListener(progressListener, flags);
browser.addProgressListener(progressFilter, flags);
return null;
}
}

Просмотреть файл

@ -141,6 +141,16 @@ class GeckoViewStartup {
lazy.ActorManagerParent.addJSWindowActors(JSWINDOWACTORS);
lazy.ActorManagerParent.addJSProcessActors(JSPROCESSACTORS);
if (Services.appinfo.sessionHistoryInParent) {
GeckoViewUtils.addLazyGetter(this, "GeckoViewSessionStore", {
module: "resource://gre/modules/GeckoViewSessionStore.jsm",
observers: [
"browsing-context-did-set-embedder",
"browsing-context-discarded",
],
});
}
GeckoViewUtils.addLazyGetter(this, "GeckoViewWebExtension", {
module: "resource://gre/modules/GeckoViewWebExtension.jsm",
ged: [

Просмотреть файл

@ -0,0 +1,199 @@
/* 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 = ["GeckoViewSessionStore"];
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
const { GeckoViewUtils } = ChromeUtils.import(
"resource://gre/modules/GeckoViewUtils.jsm"
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const lazy = {};
ChromeUtils.defineModuleGetter(
lazy,
"SessionHistory",
"resource://gre/modules/sessionstore/SessionHistory.jsm"
);
const { debug, warn } = GeckoViewUtils.initLogging("SessionStore");
const kNoIndex = Number.MAX_SAFE_INTEGER;
const kLastIndex = Number.MAX_SAFE_INTEGER - 1;
class SHistoryListener {
constructor(browsingContext) {
this.QueryInterface = ChromeUtils.generateQI([
"nsISHistoryListener",
"nsISupportsWeakReference",
]);
this._browserId = browsingContext.browserId;
this._fromIndex = kNoIndex;
}
unregister(permanentKey) {
const bc = BrowsingContext.getCurrentTopByBrowserId(this._browserId);
bc?.sessionHistory?.removeSHistoryListener(this);
GeckoViewSessionStore._browserSHistoryListener?.delete(permanentKey);
}
collect(
permanentKey, // eslint-disable-line no-shadow
browsingContext, // eslint-disable-line no-shadow
{ collectFull = true, writeToCache = false }
) {
// Don't bother doing anything if we haven't seen any navigations.
if (!collectFull && this._fromIndex === kNoIndex) {
return null;
}
const fromIndex = collectFull ? -1 : this._fromIndex;
this._fromIndex = kNoIndex;
const historychange = lazy.SessionHistory.collectFromParent(
browsingContext.currentURI?.spec,
true, // Bug 1704574
browsingContext.sessionHistory,
fromIndex
);
if (writeToCache) {
const win =
browsingContext.embedderElement?.ownerGlobal ||
browsingContext.currentWindowGlobal?.browsingContext?.window;
GeckoViewSessionStore.onTabStateUpdate(permanentKey, win, {
data: { historychange },
});
}
return historychange;
}
collectFrom(index) {
if (this._fromIndex <= index) {
// If we already know that we need to update history from index N we
// can ignore any changes that happened with an element with index
// larger than N.
//
// Note: initially we use kNoIndex which is MAX_SAFE_INTEGER which
// means we don't ignore anything here, and in case of navigation in
// the history back and forth cases we use kLastIndex which ignores
// only the subsequent navigations, but not any new elements added.
return;
}
const bc = BrowsingContext.getCurrentTopByBrowserId(this._browserId);
if (bc?.embedderElement?.frameLoader) {
this._fromIndex = index;
// Queue a tab state update on the |browser.sessionstore.interval|
// timer. We'll call this.collect() when we receive the update.
bc.embedderElement.frameLoader.requestSHistoryUpdate();
}
}
OnHistoryNewEntry(newURI, oldIndex) {
// We use oldIndex - 1 to collect the current entry as well. This makes
// sure to collect any changes that were made to the entry while the
// document was active.
this.collectFrom(oldIndex == -1 ? oldIndex : oldIndex - 1);
}
OnHistoryGotoIndex() {
this.collectFrom(kLastIndex);
}
OnHistoryPurge() {
this.collectFrom(-1);
}
OnHistoryReload() {
this.collectFrom(-1);
return true;
}
OnHistoryReplaceEntry() {
this.collectFrom(-1);
}
}
var GeckoViewSessionStore = {
// For each <browser> element, records the SHistoryListener.
_browserSHistoryListener: new WeakMap(),
observe(aSubject, aTopic, aData) {
debug`observe ${aTopic}`;
switch (aTopic) {
case "browsing-context-did-set-embedder": {
if (
aSubject &&
aSubject === aSubject.top &&
aSubject.isContent &&
aSubject.embedderElement &&
aSubject.embedderElement.permanentKey
) {
const permanentKey = aSubject.embedderElement.permanentKey;
this._browserSHistoryListener
.get(permanentKey)
?.unregister(permanentKey);
this.getOrCreateSHistoryListener(permanentKey, aSubject, true);
}
break;
}
case "browsing-context-discarded":
const permanentKey = aSubject?.embedderElement?.permanentKey;
if (permanentKey) {
this._browserSHistoryListener
.get(permanentKey)
?.unregister(permanentKey);
}
break;
}
},
onTabStateUpdate(permanentKey, win, data) {
win.WindowEventDispatcher.sendRequest({
type: "GeckoView:StateUpdated",
data: data.data,
});
},
getOrCreateSHistoryListener(
permanentKey,
browsingContext,
collectImmediately = false
) {
if (!permanentKey || browsingContext !== browsingContext.top) {
return null;
}
const sessionHistory = browsingContext.sessionHistory;
if (!sessionHistory) {
return null;
}
let listener = this._browserSHistoryListener.get(permanentKey);
if (listener) {
return listener;
}
listener = new SHistoryListener(browsingContext);
sessionHistory.addSHistoryListener(listener);
this._browserSHistoryListener.set(permanentKey, listener);
if (
collectImmediately &&
(!(browsingContext.currentURI?.spec === "about:blank") ||
sessionHistory.count !== 0)
) {
listener.collect(permanentKey, browsingContext, { writeToCache: true });
}
return listener;
},
};

Просмотреть файл

@ -26,6 +26,7 @@ EXTRA_JS_MODULES += [
"GeckoViewPushController.jsm",
"GeckoViewRemoteDebugger.jsm",
"GeckoViewSelectionAction.jsm",
"GeckoViewSessionStore.jsm",
"GeckoViewSettings.jsm",
"GeckoViewStorageController.jsm",
"GeckoViewTab.jsm",