Bug 1458327 - 2. Let ModuleManager manage more aspects of modules; r=esawin

Let ModuleManager keep the current settings and enabled states for
modules. It initializes settings and module states from the window
init-data, and then listens to changes in them.

Having ModuleManager manage these states helps keeping things
consistent, and makes it possible for future optimizations like
delay-loading modules.

MozReview-Commit-ID: AM6lAxnUGhd

--HG--
extra : rebase_source : 2751990bec6054cc36104957690d8fce6d3da399
This commit is contained in:
Jim Chen 2018-05-04 21:08:10 -04:00
Родитель ad471d48f6
Коммит 46997650b0
3 изменённых файлов: 219 добавлений и 106 удалений

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

@ -15,39 +15,172 @@ XPCOMUtils.defineLazyModuleGetters(this, {
XPCOMUtils.defineLazyGetter(this, "WindowEventDispatcher",
() => EventDispatcher.for(window));
// Creates and manages GeckoView modules.
// A module must extend GeckoViewModule.
// Instantiate a module by calling
// add(<resource path>, <type name>)
// and remove by calling
// remove(<type name>)
/**
* ModuleManager creates and manages GeckoView modules. Each GeckoView module
* normally consists of a JSM file with an optional frame script file. The JSM
* file contains a class that extends GeckoViewModule. A module usually pairs
* with a particular GeckoSessionHandler or delegate on the Java side, and
* automatically receives module lifetime events such as initialization, change
* in enabled state, and change in settings.
*/
var ModuleManager = {
init: function(aBrowser) {
this.browser = aBrowser;
this.modules = new Map();
get _initData() {
return window.arguments[0].QueryInterface(Ci.nsIAndroidView).initData;
},
add: function(aResource, aType, ...aArgs) {
this.remove(aType);
init(aBrowser, aModules) {
const initData = this._initData;
this._browser = aBrowser;
this._settings = initData.settings;
this._frozenSettings = Object.freeze(Object.assign({}, this._settings));
const self = this;
this._modules = new Map((function* () {
for (const module of aModules) {
yield [
module.name,
new ModuleInfo({
manager: self,
...module,
}),
];
}
})());
WindowEventDispatcher.registerListener(this, [
"GeckoView:EnableModule",
"GeckoView:UpdateInitData",
"GeckoView:UpdateSettings",
]);
},
get window() {
return window;
},
get browser() {
return this._browser;
},
get messageManager() {
return this._browser.messageManager;
},
get eventDispatcher() {
return WindowEventDispatcher;
},
get settings() {
return this._frozenSettings;
},
forEach(aCallback) {
this._modules.forEach(aCallback, this);
},
_updateSettings(aSettings) {
Object.assign(this._settings, aSettings);
this._frozenSettings = Object.freeze(Object.assign({}, this._settings));
this.forEach(module => {
if (!module.enabled) {
return;
}
module.impl.onSettingsUpdate();
this._browser.messageManager.sendAsyncMessage("GeckoView:UpdateSettings",
this._settings);
});
},
onEvent(aEvent, aData, aCallback) {
switch (aEvent) {
case "GeckoView:EnableModule": {
const module = this._modules.get(aData.module);
if (module) {
module.enabled = aData.enabled;
}
break;
}
case "GeckoView:UpdateInitData": {
// Replace all sett_onSettingsUpdateings during a transfer.
const initData = this._initData;
this._updateSettings(initData.settings);
break;
}
case "GeckoView:UpdateSettings": {
this._updateSettings(aData);
break;
}
}
},
};
/**
* ModuleInfo is the structure used by ModuleManager to represent individual
* modules. It is responsible for loading the module JSM file if necessary,
* and it acts as the intermediary between ModuleManager and the module
* object that extends GeckoViewModule.
*/
class ModuleInfo {
constructor({manager, name, resource}) {
this._manager = manager;
this._name = name;
const scope = {};
const global = ChromeUtils.import(aResource, scope);
const tag = aType.replace("GeckoView", "GeckoView.");
const global = ChromeUtils.import(resource, scope);
const tag = name.replace("GeckoView", "GeckoView.");
GeckoViewUtils.initLogging(tag, global);
this.modules.set(aType, new scope[aType](
aType, window, this.browser, WindowEventDispatcher, ...aArgs
));
},
remove: function(aType) {
this.modules.delete(aType);
},
forEach: function(aCallback) {
this.modules.forEach(aCallback, this);
this._impl = new scope[name](this);
this._enabled = false;
}
};
onInit() {
this._impl.onInit();
}
get manager() {
return this._manager;
}
get name() {
return this._name;
}
get impl() {
return this._impl;
}
get enabled() {
return this._enabled;
}
set enabled(aEnabled) {
if (aEnabled === this._enabled) {
return;
}
if (!aEnabled) {
this._manager.messageManager.sendAsyncMessage("GeckoView:Unregister", {
module: this._name,
});
this._impl.onDisable();
}
this._enabled = aEnabled;
if (aEnabled) {
this._impl.onEnable();
this._impl.onSettingsUpdate();
this._manager.messageManager.sendAsyncMessage("GeckoView:Register", {
module: this._name,
settings: this._manager.settings,
});
}
}
}
function createBrowser() {
const browser = window.browser = document.createElement("browser");
@ -61,32 +194,39 @@ function startup() {
GeckoViewUtils.initLogging("GeckoView.XUL", window);
const browser = createBrowser();
ModuleManager.init(browser);
ModuleManager.add("resource://gre/modules/GeckoViewNavigation.jsm",
"GeckoViewNavigation");
ModuleManager.add("resource://gre/modules/GeckoViewSettings.jsm",
"GeckoViewSettings");
ModuleManager.add("resource://gre/modules/GeckoViewContent.jsm",
"GeckoViewContent");
ModuleManager.add("resource://gre/modules/GeckoViewProgress.jsm",
"GeckoViewProgress");
ModuleManager.add("resource://gre/modules/GeckoViewScroll.jsm",
"GeckoViewScroll");
ModuleManager.add("resource://gre/modules/GeckoViewTab.jsm",
"GeckoViewTab");
ModuleManager.add("resource://gre/modules/GeckoViewTrackingProtection.jsm",
"GeckoViewTrackingProtection");
ModuleManager.add("resource://gre/modules/GeckoViewSelectionAction.jsm",
"GeckoViewSelectionAction");
ModuleManager.add("resource://gre/modules/GeckoViewAccessibility.jsm",
"GeckoViewAccessibility");
ModuleManager.init(browser, [{
name: "GeckoViewAccessibility",
resource: "resource://gre/modules/GeckoViewAccessibility.jsm",
}, {
name: "GeckoViewContent",
resource: "resource://gre/modules/GeckoViewContent.jsm",
}, {
name: "GeckoViewNavigation",
resource: "resource://gre/modules/GeckoViewNavigation.jsm",
}, {
name: "GeckoViewProgress",
resource: "resource://gre/modules/GeckoViewProgress.jsm",
}, {
name: "GeckoViewScroll",
resource: "resource://gre/modules/GeckoViewScroll.jsm",
}, {
name: "GeckoViewSelectionAction",
resource: "resource://gre/modules/GeckoViewSelectionAction.jsm",
}, {
name: "GeckoViewSettings",
resource: "resource://gre/modules/GeckoViewSettings.jsm",
}, {
name: "GeckoViewTab",
resource: "resource://gre/modules/GeckoViewTab.jsm",
}, {
name: "GeckoViewTrackingProtection",
resource: "resource://gre/modules/GeckoViewTrackingProtection.jsm",
}]);
window.document.documentElement.appendChild(browser);
ModuleManager.forEach(module => {
module.onInit();
module.onSettingsUpdate();
});
// Move focus to the content window at the end of startup,

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

@ -12,45 +12,43 @@ ChromeUtils.import("resource://gre/modules/GeckoViewUtils.jsm");
GeckoViewUtils.initLogging("GeckoView.Module", this);
class GeckoViewModule {
constructor(aModuleName, aWindow, aBrowser, aEventDispatcher) {
this.isRegistered = false;
this.window = aWindow;
this.browser = aBrowser;
this.eventDispatcher = aEventDispatcher;
this.moduleName = aModuleName;
constructor(aModuleInfo) {
this._info = aModuleInfo;
this._isContentLoaded = false;
this._eventProxy = new EventProxy(this, this.eventDispatcher);
this.eventDispatcher.registerListener(
(aEvent, aData, aCallback) => {
this.messageManager.sendAsyncMessage("GeckoView:UpdateSettings",
this.settings);
this.onSettingsUpdate();
}, "GeckoView:UpdateSettings"
);
this.eventDispatcher.registerListener(
(aEvent, aData, aCallback) => {
if (aData.module == this.moduleName) {
this._register();
aData.settings = this.settings;
this.messageManager.sendAsyncMessage("GeckoView:Register", aData);
}
}, "GeckoView:Register"
);
this.eventDispatcher.registerListener(
(aEvent, aData, aCallback) => {
if (aData.module == this.moduleName) {
this.messageManager.sendAsyncMessage("GeckoView:Unregister", aData);
this._unregister();
}
}, "GeckoView:Unregister"
);
this.onInitBrowser();
}
get name() {
return this._info.name;
}
get enabled() {
return this._info.enabled;
}
get window() {
return this._info.manager.window;
}
get browser() {
return this._info.manager.browser;
}
get messageManager() {
return this._info.manager.messageManager;
}
get eventDispatcher() {
return this._info.manager.eventDispatcher;
}
get settings() {
return this._info.manager.settings;
}
// Override to initialize the browser before it is bound to the window.
onInitBrowser() {}
@ -66,22 +64,6 @@ class GeckoViewModule {
// Override to disable module after clearing the Java delegate.
onDisable() {}
_register() {
if (this.isRegistered) {
return;
}
this.onEnable();
this.isRegistered = true;
}
_unregister() {
if (!this.isRegistered) {
return;
}
this.onDisable();
this.isRegistered = false;
}
registerContent(aUri) {
if (this._isContentLoaded) {
return;
@ -92,7 +74,7 @@ class GeckoViewModule {
let self = this;
this.messageManager.addMessageListener("GeckoView:ContentRegistered",
function listener(aMsg) {
if (aMsg.data.module !== self.moduleName) {
if (aMsg.data.module !== self.name) {
return;
}
self.messageManager.removeMessageListener("GeckoView:ContentRegistered",
@ -110,15 +92,6 @@ class GeckoViewModule {
unregisterListener() {
this._eventProxy.unregisterListener();
}
get settings() {
let view = this.window.arguments[0].QueryInterface(Ci.nsIAndroidView);
return Object.freeze(view.settings);
}
get messageManager() {
return this.browser.messageManager;
}
}
class EventProxy {

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

@ -123,7 +123,7 @@ class GeckoViewNavigation extends GeckoViewModule {
debug `handleNewSession: uri=${aUri && aUri.spec}
where=${aWhere} flags=${aFlags}`;
if (!this.isRegistered) {
if (!this.enabled) {
return null;
}