Bug 722332: add an asynchronous initialization API to nsIBrowserSearchService, with a fallback to synchronous initialization for backwards compatibility, r=gavin

This commit is contained in:
David Rajchenbach-Teller 2012-05-03 16:56:33 +02:00
Родитель 79d98e1dd2
Коммит 83974652ed
2 изменённых файлов: 153 добавлений и 10 удалений

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

@ -128,9 +128,48 @@ interface nsISearchEngine : nsISupports
};
[scriptable, uuid(8307b8f2-08ea-45b8-96bf-b1dc7688fe3b)]
/**
* Callback for asynchronous initialization of nsIBrowserSearchService
*/
[scriptable, function, uuid(02256156-16e4-47f1-9979-76ff98ceb590)]
interface nsIBrowserSearchInitObserver : nsISupports
{
/**
* Called once initialization of the browser search service is complete.
*
* @param aStatus The status of that service.
*/
void onInitComplete(in nsresult aStatus);
};
[scriptable, uuid(a676eb70-6987-4429-a668-3e253b6f7c7c)]
interface nsIBrowserSearchService : nsISupports
{
/**
* Start asynchronous initialization.
*
* The callback is triggered once initialization is complete, which may be
* immediately, if initialization has already been completed by some previous
* call to this method. The callback is always invoked asynchronously.
*
* @param aObserver An optional object observing the end of initialization.
*/
void init([optional] in nsIBrowserSearchInitObserver aObserver);
/**
* Determine whether initialization has been completed.
*
* Clients of the service can use this attribute to quickly determine whether
* initialization is complete, and decide to trigger some immediate treatment,
* to launch asynchronous initialization or to bailout.
*
* Note that this attribute does not indicate that initialization has succeeded.
*
* @return |true| if the search service is now initialized, |false| if
* initialization has not been triggered yet.
*/
readonly attribute bool isInitialized;
/**
* Adds a new search engine from the file at the supplied URI, optionally
* asking the user for confirmation first. If a confirmation dialog is

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

@ -734,9 +734,9 @@ function getMozParamPref(prefName)
*
* @see nsIBrowserSearchService.idl
*/
let gEnginesLoaded = false;
let gInitialized = false;
function notifyAction(aEngine, aVerb) {
if (gEnginesLoaded) {
if (gInitialized) {
LOG("NOTIFY: Engine: \"" + aEngine.name + "\"; Verb: \"" + aVerb + "\"");
Services.obs.notifyObservers(aEngine, SEARCH_ENGINE_TOPIC, aVerb);
}
@ -2466,23 +2466,85 @@ Submission.prototype = {
}
}
function executeSoon(func) {
Services.tm.mainThread.dispatch(func, Ci.nsIThread.DISPATCH_NORMAL);
}
// nsIBrowserSearchService
function SearchService() {
// Replace empty LOG function with the useful one if the log pref is set.
if (getBoolPref(BROWSER_SEARCH_PREF + "log", false))
LOG = DO_LOG;
try {
this._loadEngines();
} catch (ex) {
LOG("_init: failure loading engines: " + ex);
}
gEnginesLoaded = true;
this._addObservers();
/**
* If initialization is not complete yet, an array of
* |nsIBrowserSearchInitObserver| expecting the result of the end of
* initialization.
* Once initialization is complete, |null|.
*/
this._initObservers = [];
}
SearchService.prototype = {
classID: Components.ID("{7319788a-fe93-4db3-9f39-818cf08f4256}"),
// The current status of initialization. Note that it does not determine if
// initialization is complete, only if an error has been encountered so far.
_initRV: Cr.NS_OK,
// If initialization has not been completed yet, perform synchronous
// initialization.
// Throws in case of initialization error.
_ensureInitialized: function SRCH_SVC__ensureInitialized() {
if (gInitialized) {
if (!Components.isSuccessCode(this._initRV)) {
throw this._initRV;
}
// Ensure that the following calls to |_ensureInitialized| can be inlined
// to a noop. Note that we could do this at the end of both |_init| and
// |_syncInit|, to save one call to a non-empty |_ensureInitialized|, but
// this would complicate code.
delete this._ensureInitialized;
this._ensureInitialized = function SRCH_SVC__ensureInitializedDone() { };
return;
}
let warning = "Search service falling back to synchronous initialization at " + new Error().stack;
Components.utils.reportError(warning);
LOG(warning);
this._syncInit();
if (!Components.isSuccessCode(this._initRV)) {
throw this._initRV;
}
},
// Synchronous implementation of the initializer.
// Used as by |_ensureInitialized| as a fallback if initialization is not
// complete. In this implementation, it is also used by |init|.
_syncInit: function SRCH_SVC__syncInit() {
try {
this._loadEngines();
} catch (ex) {
this._initRV = Cr.NS_ERROR_FAILURE;
LOG("_syncInit: failure loading engines: " + ex);
}
this._addObservers();
gInitialized = true;
// Notify all of the init observers
this._initObservers.forEach(function (observer) {
try {
observer.onInitComplete(this._initRV);
} catch (x) {
LOG("nsIBrowserInitObserver failed with error " + x);
}
}, this);
this._initObservers = null;
},
_engines: { },
__sortedEngines: null,
get _sortedEngines() {
@ -3151,7 +3213,36 @@ SearchService.prototype = {
},
// nsIBrowserSearchService
init: function SRCH_SVC_init(observer) {
if (gInitialized) {
if (observer) {
executeSoon(function () {
observer.onInitComplete(this._initRV);
});
}
return;
}
if (observer)
this._initObservers.push(observer);
if (!this._initStarted) {
executeSoon((function () {
// Someone may have since called syncInit via ensureInitialized - if so,
// nothing to do here.
if (!gInitialized)
this._syncInit();
}).bind(this));
this._initStarted = true;
}
},
get isInitialized() {
return gInitialized;
},
getEngines: function SRCH_SVC_getEngines(aCount) {
this._ensureInitialized();
LOG("getEngines: getting all engines");
var engines = this._getSortedEngines(true);
aCount.value = engines.length;
@ -3159,6 +3250,7 @@ SearchService.prototype = {
},
getVisibleEngines: function SRCH_SVC_getVisible(aCount) {
this._ensureInitialized();
LOG("getVisibleEngines: getting all visible engines");
var engines = this._getSortedEngines(false);
aCount.value = engines.length;
@ -3166,6 +3258,7 @@ SearchService.prototype = {
},
getDefaultEngines: function SRCH_SVC_getDefault(aCount) {
this._ensureInitialized();
function isDefault(engine) {
return engine._isDefault;
};
@ -3224,10 +3317,12 @@ SearchService.prototype = {
},
getEngineByName: function SRCH_SVC_getEngineByName(aEngineName) {
this._ensureInitialized();
return this._engines[aEngineName] || null;
},
getEngineByAlias: function SRCH_SVC_getEngineByAlias(aAlias) {
this._ensureInitialized();
for (var engineName in this._engines) {
var engine = this._engines[engineName];
if (engine && engine.alias == aAlias)
@ -3239,6 +3334,7 @@ SearchService.prototype = {
addEngineWithDetails: function SRCH_SVC_addEWD(aName, aIconURL, aAlias,
aDescription, aMethod,
aTemplate) {
this._ensureInitialized();
if (!aName)
FAIL("Invalid name passed to addEngineWithDetails!");
if (!aMethod)
@ -3258,6 +3354,7 @@ SearchService.prototype = {
addEngine: function SRCH_SVC_addEngine(aEngineURL, aDataType, aIconURL,
aConfirm) {
LOG("addEngine: Adding \"" + aEngineURL + "\".");
this._ensureInitialized();
try {
var uri = makeURI(aEngineURL);
var engine = new Engine(uri, aDataType, false);
@ -3270,6 +3367,7 @@ SearchService.prototype = {
},
removeEngine: function SRCH_SVC_removeEngine(aEngine) {
this._ensureInitialized();
if (!aEngine)
FAIL("no engine passed to removeEngine!");
@ -3317,6 +3415,7 @@ SearchService.prototype = {
},
moveEngine: function SRCH_SVC_moveEngine(aEngine, aNewIndex) {
this._ensureInitialized();
if ((aNewIndex > this._sortedEngines.length) || (aNewIndex < 0))
FAIL("SRCH_SVC_moveEngine: Index out of bounds!");
if (!(aEngine instanceof Ci.nsISearchEngine))
@ -3366,6 +3465,7 @@ SearchService.prototype = {
},
restoreDefaultEngines: function SRCH_SVC_resetDefaultEngines() {
this._ensureInitialized();
for each (var e in this._engines) {
// Unhide all default engines
if (e.hidden && e._isDefault)
@ -3374,11 +3474,13 @@ SearchService.prototype = {
},
get originalDefaultEngine() {
this._ensureInitialized();
const defPref = BROWSER_SEARCH_PREF + "defaultenginename";
return this.getEngineByName(getLocalizedPref(defPref, ""));
},
get defaultEngine() {
this._ensureInitialized();
let defaultEngine = this.originalDefaultEngine;
if (!defaultEngine || defaultEngine.hidden)
defaultEngine = this._getSortedEngines(false)[0] || null;
@ -3386,6 +3488,7 @@ SearchService.prototype = {
},
get currentEngine() {
this._ensureInitialized();
if (!this._currentEngine) {
let selectedEngine = getLocalizedPref(BROWSER_SEARCH_PREF +
"selectedEngine");
@ -3397,6 +3500,7 @@ SearchService.prototype = {
return this._currentEngine;
},
set currentEngine(val) {
this._ensureInitialized();
if (!(val instanceof Ci.nsISearchEngine))
FAIL("Invalid argument passed to currentEngine setter");