mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
Bug 1389443 - Load handlers.json asynchronously r=florian,Paolo
Asynchronously load handlers.json for nsHandlerService-json.js in order to avoid blocking IO early on. MozReview-Commit-ID: F3THSxvXR7I --HG-- extra : rebase_source : 95d5ccb545cd7d8a09f80586fc2bf2dcf1ea5ab7
This commit is contained in:
parent
a4a6ffdd99
commit
21ef26ac40
@ -483,6 +483,16 @@ BrowserGlue.prototype = {
|
||||
case "sync-ui-state:update":
|
||||
this._updateFxaBadges();
|
||||
break;
|
||||
case "handlersvc-store-initialized":
|
||||
// Initialize PdfJs when running in-process and remote. This only
|
||||
// happens once since PdfJs registers global hooks. If the PdfJs
|
||||
// extension is installed the init method below will be overridden
|
||||
// leaving initialization to the extension.
|
||||
// parent only: configure default prefs, set up pref observers, register
|
||||
// pdf content handler, and initializes parent side message manager
|
||||
// shim for privileged api access.
|
||||
PdfJs.init(true);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
@ -518,6 +528,7 @@ BrowserGlue.prototype = {
|
||||
os.addObserver(this, "flash-plugin-hang");
|
||||
os.addObserver(this, "xpi-signature-changed");
|
||||
os.addObserver(this, "sync-ui-state:update");
|
||||
os.addObserver(this, "handlersvc-store-initialized");
|
||||
|
||||
this._flashHangCount = 0;
|
||||
this._firstWindowReady = new Promise(resolve => this._firstWindowLoaded = resolve);
|
||||
@ -857,20 +868,12 @@ BrowserGlue.prototype = {
|
||||
|
||||
// the first browser window has finished initializing
|
||||
_onFirstWindowLoaded: function BG__onFirstWindowLoaded(aWindow) {
|
||||
// Initialize PdfJs when running in-process and remote. This only
|
||||
// happens once since PdfJs registers global hooks. If the PdfJs
|
||||
// extension is installed the init method below will be overridden
|
||||
// leaving initialization to the extension.
|
||||
// parent only: configure default prefs, set up pref observers, register
|
||||
// pdf content handler, and initializes parent side message manager
|
||||
// shim for privileged api access.
|
||||
PdfJs.init(true);
|
||||
// child only: similar to the call above for parent - register content
|
||||
// handler and init message manager child shim for privileged api access.
|
||||
// With older versions of the extension installed, this load will fail
|
||||
// passively.
|
||||
// Set up listeners and, if PdfJs is enabled, register the PDF stream converter.
|
||||
// We delay all of the parent's initialization other than stream converter
|
||||
// registration, because it requires file IO from nsHandlerService-json.js
|
||||
Services.ppmm.loadProcessScript("resource://pdf.js/pdfjschildbootstrap.js", true);
|
||||
if (PdfJs.enabled) {
|
||||
PdfJs.ensureRegistered();
|
||||
Services.ppmm.loadProcessScript("resource://pdf.js/pdfjschildbootstrap-enabled.js", true);
|
||||
}
|
||||
|
||||
@ -1151,6 +1154,12 @@ BrowserGlue.prototype = {
|
||||
Services.tm.idleDispatchToMainThread(Services.startup.trackStartupCrashEnd);
|
||||
}, STARTUP_CRASHES_END_DELAY_MS);
|
||||
});
|
||||
|
||||
Services.tm.idleDispatchToMainThread(() => {
|
||||
let handlerService = Cc["@mozilla.org/uriloader/handler-service;1"].
|
||||
getService(Ci.nsIHandlerService);
|
||||
handlerService.asyncInit();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -30,6 +30,10 @@ const PREF_PREVIOUS_ACTION = PREF_PREFIX + ".previousHandler.preferredAction";
|
||||
const PREF_PREVIOUS_ASK = PREF_PREFIX +
|
||||
".previousHandler.alwaysAskBeforeHandling";
|
||||
const PREF_DISABLED_PLUGIN_TYPES = "plugin.disable_full_page_plugin_for_types";
|
||||
const PREF_ENABLED_CACHE_STATE = PREF_PREFIX + ".enabledCache.state";
|
||||
const PREF_ENABLED_CACHE_INITIALIZED = PREF_PREFIX +
|
||||
".enabledCache.initialized";
|
||||
const PREF_APP_UPDATE_POSTUPDATE = "app.update.postupdate";
|
||||
const TOPIC_PDFJS_HANDLER_CHANGED = "pdfjs:handlerChanged";
|
||||
const TOPIC_PLUGINS_LIST_UPDATED = "plugins-list-updated";
|
||||
const TOPIC_PLUGIN_INFO_UPDATED = "plugin-info-updated";
|
||||
@ -190,7 +194,7 @@ var PdfJs = {
|
||||
},
|
||||
|
||||
updateRegistration: function updateRegistration() {
|
||||
if (this.enabled) {
|
||||
if (this.checkEnabled()) {
|
||||
this.ensureRegistered();
|
||||
} else {
|
||||
this.ensureUnregistered();
|
||||
@ -269,26 +273,7 @@ var PdfJs = {
|
||||
false);
|
||||
},
|
||||
|
||||
// nsIObserver
|
||||
observe: function observe(aSubject, aTopic, aData) {
|
||||
if (Services.appinfo.processType !==
|
||||
Services.appinfo.PROCESS_TYPE_DEFAULT) {
|
||||
throw new Error("Only the parent process should be observing PDF " +
|
||||
"handler changes.");
|
||||
}
|
||||
|
||||
this.updateRegistration();
|
||||
let jsm = "resource://pdf.js/PdfjsChromeUtils.jsm";
|
||||
let PdfjsChromeUtils = Components.utils.import(jsm, {}).PdfjsChromeUtils;
|
||||
PdfjsChromeUtils.notifyChildOfSettingsChange(this.enabled);
|
||||
},
|
||||
|
||||
/**
|
||||
* pdf.js is only enabled if it is both selected as the pdf viewer and if the
|
||||
* global switch enabling it is true.
|
||||
* @return {boolean} Whether or not it's enabled.
|
||||
*/
|
||||
get enabled() {
|
||||
_isEnabled: function _isEnabled() {
|
||||
var disabled = getBoolPref(PREF_DISABLED, true);
|
||||
if (disabled) {
|
||||
return false;
|
||||
@ -328,6 +313,47 @@ var PdfJs = {
|
||||
return !enabledPluginFound;
|
||||
},
|
||||
|
||||
checkEnabled: function checkEnabled() {
|
||||
let isEnabled = this._isEnabled();
|
||||
// This will be updated any time we observe a dependency changing, since
|
||||
// updateRegistration internally calls enabled.
|
||||
Services.prefs.setBoolPref(PREF_ENABLED_CACHE_STATE, isEnabled);
|
||||
return isEnabled;
|
||||
},
|
||||
|
||||
// nsIObserver
|
||||
observe: function observe(aSubject, aTopic, aData) {
|
||||
if (Services.appinfo.processType !==
|
||||
Services.appinfo.PROCESS_TYPE_DEFAULT) {
|
||||
throw new Error("Only the parent process should be observing PDF " +
|
||||
"handler changes.");
|
||||
}
|
||||
|
||||
this.updateRegistration();
|
||||
let jsm = "resource://pdf.js/PdfjsChromeUtils.jsm";
|
||||
let PdfjsChromeUtils = Components.utils.import(jsm, {}).PdfjsChromeUtils;
|
||||
PdfjsChromeUtils.notifyChildOfSettingsChange(this.enabled);
|
||||
},
|
||||
|
||||
/**
|
||||
* pdf.js is only enabled if it is both selected as the pdf viewer and if the
|
||||
* global switch enabling it is true.
|
||||
* @return {boolean} Whether or not it's enabled.
|
||||
*/
|
||||
get enabled() {
|
||||
if (!Services.prefs.getBoolPref(PREF_ENABLED_CACHE_INITIALIZED, false)) {
|
||||
// If we just updated, and the cache hasn't been initialized, then we
|
||||
// can't assume a default state, and need to synchronously initialize
|
||||
// PdfJs
|
||||
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_POSTUPDATE)) {
|
||||
this.checkEnabled();
|
||||
}
|
||||
|
||||
Services.prefs.setBoolPref(PREF_ENABLED_CACHE_INITIALIZED, true);
|
||||
}
|
||||
return Services.prefs.getBoolPref(PREF_ENABLED_CACHE_STATE, true);
|
||||
},
|
||||
|
||||
ensureRegistered: function ensureRegistered() {
|
||||
if (this._registered) {
|
||||
return;
|
||||
@ -350,4 +376,3 @@ var PdfJs = {
|
||||
this._registered = false;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -117,6 +117,11 @@ ContentHandlerService::~ContentHandlerService()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP ContentHandlerService::AsyncInit()
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP ContentHandlerService::Enumerate(nsISimpleEnumerator * *_retval)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
|
@ -44,14 +44,27 @@ HandlerService.prototype = {
|
||||
path: OS.Path.join(OS.Constants.Path.profileDir, "handlers.json"),
|
||||
dataPostProcessor: this._dataPostProcessor.bind(this),
|
||||
});
|
||||
}
|
||||
|
||||
// Always call this even if this.__store was set, since it may have been
|
||||
// set by asyncInit, which might not have completed yet.
|
||||
this._ensureStoreInitialized();
|
||||
return this.__store;
|
||||
},
|
||||
|
||||
__storeInitialized: false,
|
||||
_ensureStoreInitialized() {
|
||||
if (!this.__storeInitialized) {
|
||||
this.__storeInitialized = true;
|
||||
this.__store.ensureDataReady();
|
||||
|
||||
// We have to inject new default protocol handlers only if we haven't
|
||||
// already done this when migrating data from the RDF back-end.
|
||||
let alreadyInjected = this._migrateFromRDFIfNeeded();
|
||||
this._injectDefaultProtocolHandlersIfNeeded(alreadyInjected);
|
||||
|
||||
Services.obs.notifyObservers(null, "handlersvc-store-initialized");
|
||||
}
|
||||
return this.__store;
|
||||
},
|
||||
|
||||
_dataPostProcessor(data) {
|
||||
@ -218,6 +231,7 @@ HandlerService.prototype = {
|
||||
await this.__store.finalize();
|
||||
}
|
||||
this.__store = null;
|
||||
this.__storeInitialized = false;
|
||||
})().catch(Cu.reportError);
|
||||
},
|
||||
|
||||
@ -232,6 +246,22 @@ HandlerService.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
// nsIHandlerService
|
||||
asyncInit() {
|
||||
if (!this.__store) {
|
||||
this.__store = new JSONFile({
|
||||
path: OS.Path.join(OS.Constants.Path.profileDir, "handlers.json"),
|
||||
dataPostProcessor: this._dataPostProcessor.bind(this),
|
||||
});
|
||||
this.__store.load().then(() => {
|
||||
// __store can be null if we called _onDBChange in the mean time.
|
||||
if (this.__store) {
|
||||
this._ensureStoreInitialized();
|
||||
}
|
||||
}).catch(Cu.reportError);
|
||||
}
|
||||
},
|
||||
|
||||
// nsIHandlerService
|
||||
enumerate() {
|
||||
let handlers = Cc["@mozilla.org/array;1"]
|
||||
|
@ -302,6 +302,9 @@ HandlerService.prototype = {
|
||||
|
||||
//**************************************************************************//
|
||||
// nsIHandlerService
|
||||
asyncInit: function HS_asyncInit() {
|
||||
// noop
|
||||
},
|
||||
|
||||
enumerate: function HS_enumerate() {
|
||||
var handlers = Cc["@mozilla.org/array;1"].
|
||||
|
@ -10,6 +10,12 @@ interface nsISimpleEnumerator;
|
||||
[scriptable, uuid(53f0ad17-ec62-46a1-adbc-efccc06babcd)]
|
||||
interface nsIHandlerService : nsISupports
|
||||
{
|
||||
/**
|
||||
* Asynchronously performs any IO that the nsIHandlerService needs to do
|
||||
* before it can be of use.
|
||||
*/
|
||||
void asyncInit();
|
||||
|
||||
/**
|
||||
* Retrieve a list of all handlers in the datastore. This list is not
|
||||
* guaranteed to be in any particular order, and callers should not assume
|
||||
|
@ -32,6 +32,44 @@ add_task(async function test_store_keeps_unknown_properties() {
|
||||
"preserved");
|
||||
});
|
||||
|
||||
/**
|
||||
* Runs the asyncInit method, ensuring that it successfully inits the store
|
||||
* and calls the handlersvc-store-initialized topic.
|
||||
*/
|
||||
add_task(async function test_async_init() {
|
||||
await deleteHandlerStore();
|
||||
await copyTestDataToHandlerStore();
|
||||
gHandlerService.asyncInit();
|
||||
await TestUtils.topicObserved("handlersvc-store-initialized");
|
||||
await assertAllHandlerInfosMatchTestData();
|
||||
|
||||
await unloadHandlerStore();
|
||||
});
|
||||
|
||||
/**
|
||||
* Races the asyncInit method against the sync init (implicit in enumerate),
|
||||
* to ensure that the store will be synchronously initialized without any
|
||||
* ill effects.
|
||||
*/
|
||||
add_task(async function test_race_async_init() {
|
||||
await deleteHandlerStore();
|
||||
await copyTestDataToHandlerStore();
|
||||
let storeInitialized = false;
|
||||
// Pass a callback to synchronously observe the topic, as a promise would
|
||||
// resolve asynchronously
|
||||
TestUtils.topicObserved("handlersvc-store-initialized", () => {
|
||||
storeInitialized = true;
|
||||
return true;
|
||||
});
|
||||
gHandlerService.asyncInit();
|
||||
do_check_false(storeInitialized);
|
||||
gHandlerService.enumerate();
|
||||
do_check_true(storeInitialized);
|
||||
await assertAllHandlerInfosMatchTestData();
|
||||
|
||||
await unloadHandlerStore();
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests the migration from an existing RDF data source.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user