Bug 722254 - Add an XPCOMUtils method to generate a singleton factory.

Use the new factory in Places js services, to ensure they can't be instanced multiple times.
r=bsmedberg
This commit is contained in:
Marco Bonardo 2012-02-07 10:17:42 +01:00
parent e838bd311b
commit 3cb51695d8
8 changed files with 104 additions and 53 deletions

View File

@ -317,6 +317,32 @@ var XPCOMUtils = {
let i = uri.lastIndexOf("/");
Components.utils.import(uri.substring(0, i+1) + path, that);
},
/**
* generates a singleton nsIFactory implementation that can be used as
* the _xpcom_factory of the component.
* @param aServiceConstructor
* Constructor function of the component.
*/
generateSingletonFactory:
function XPCOMUtils_generateSingletonFactory(aServiceConstructor) {
return {
_instance: null,
createInstance: function XPCU_SF_createInstance(aOuter, aIID) {
if (aOuter !== null) {
throw Cr.NS_ERROR_NO_AGGREGATION;
}
if (this._instance === null) {
this._instance = new aServiceConstructor();
}
return this._instance.QueryInterface(aIID);
},
lockFactory: function XPCU_SF_lockFactory(aDoLock) {
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
};
},
};
/**
@ -336,4 +362,3 @@ function makeQI(interfaceNames) {
throw Cr.NS_ERROR_NO_INTERFACE;
};
}

View File

@ -50,7 +50,7 @@ const Ci = Components.interfaces;
////////////////////////////////////////////////////////////////////////////////
//// Tests
function test_generateQI_string_names()
add_test(function test_generateQI_string_names()
{
var x = {
QueryInterface: XPCOMUtils.generateQI([
@ -73,10 +73,11 @@ function test_generateQI_string_names()
x.QueryInterface(Components.interfaces.nsIDOMDocument);
do_throw("QI should not have succeeded!");
} catch(e) {}
}
run_next_test();
});
function test_generateCI()
add_test(function test_generateCI()
{
const classID = Components.ID("562dae2e-7cff-432b-995b-3d4c03fa2b89");
const classDescription = "generateCI test component";
@ -99,9 +100,10 @@ function test_generateCI()
} catch(e) {
do_throw("Classinfo for x should not be missing or broken");
}
}
run_next_test();
});
function test_defineLazyGetter()
add_test(function test_defineLazyGetter()
{
let accessCount = 0;
let obj = {
@ -124,10 +126,11 @@ function test_defineLazyGetter()
// increased.
do_check_eq(obj.foo, TEST_VALUE);
do_check_eq(accessCount, 1);
}
run_next_test();
});
function test_defineLazyServiceGetter()
add_test(function test_defineLazyServiceGetter()
{
let obj = { };
XPCOMUtils.defineLazyServiceGetter(obj, "service",
@ -142,10 +145,11 @@ function test_defineLazyServiceGetter()
do_check_true(prop in service);
for (let prop in service)
do_check_true(prop in obj.service);
}
run_next_test();
});
function test_categoryRegistration()
add_test(function test_categoryRegistration()
{
const CATEGORY_NAME = "test-cat";
const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1";
@ -203,24 +207,45 @@ function test_categoryRegistration()
}
print("Check there are no more or less than expected entries.");
do_check_eq(foundEntriesCount, EXPECTED_ENTRIES.length);
}
run_next_test();
});
add_test(function test_generateSingletonFactory()
{
const XPCCOMPONENT_CONTRACTID = "@mozilla.org/singletonComponentTest;1";
const XPCCOMPONENT_CID = Components.ID("{31031c36-5e29-4dd9-9045-333a5d719a3e}");
function XPCComponent() {}
XPCComponent.prototype = {
classID: XPCCOMPONENT_CID,
_xpcom_factory: XPCOMUtils.generateSingletonFactory(XPCComponent),
QueryInterface: XPCOMUtils.generateQI([])
};
let NSGetFactory = XPCOMUtils.generateNSGetFactory([XPCComponent]);
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
registrar.registerFactory(
XPCCOMPONENT_CID,
"XPCComponent",
XPCCOMPONENT_CONTRACTID,
NSGetFactory(XPCCOMPONENT_CID)
);
// First, try to instance the component.
let instance = Cc[XPCCOMPONENT_CONTRACTID].createInstance(Ci.nsISupports);
// Try again, check that it returns the same instance as before.
do_check_eq(instance,
Cc[XPCCOMPONENT_CONTRACTID].createInstance(Ci.nsISupports));
// Now, for sanity, check that getService is also returning the same instance.
do_check_eq(instance,
Cc[XPCCOMPONENT_CONTRACTID].getService(Ci.nsISupports));
run_next_test();
});
////////////////////////////////////////////////////////////////////////////////
//// Test Runner
let tests = [
test_generateQI_string_names,
test_generateCI,
test_defineLazyGetter,
test_defineLazyServiceGetter,
test_categoryRegistration,
];
function run_test()
{
tests.forEach(function(test) {
print("Running next test: " + test.name);
test();
});
run_next_test();
}

View File

@ -129,6 +129,8 @@ PlacesCategoriesStarter.prototype = {
classID: Components.ID("803938d5-e26d-4453-bf46-ad4b26e41114"),
_xpcom_factory: XPCOMUtils.generateSingletonFactory(PlacesCategoriesStarter),
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIObserver
, Ci.nsINavBookmarkObserver

View File

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*

View File

@ -359,9 +359,13 @@ LivemarkService.prototype = {
delete this._livemarks[aItemId];
},
// nsISupports
//////////////////////////////////////////////////////////////////////////////
//// nsISupports
classID: Components.ID("{dca61eb5-c7cd-4df1-b0fb-d0722baba251}"),
_xpcom_factory: XPCOMUtils.generateSingletonFactory(LivemarkService),
QueryInterface: XPCOMUtils.generateQI([
Ci.nsILivemarkService
, Ci.nsINavBookmarkObserver

View File

@ -1262,6 +1262,8 @@ nsPlacesAutoComplete.prototype = {
classID: Components.ID("d0272978-beab-4adc-a3d4-04b76acfa4e7"),
_xpcom_factory: XPCOMUtils.generateSingletonFactory(nsPlacesAutoComplete),
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIAutoCompleteSearch,
Ci.nsIAutoCompleteSimpleResultListener,
@ -1578,6 +1580,8 @@ urlInlineComplete.prototype = {
classID: Components.ID("c88fae2d-25cf-4338-a1f4-64a320ea7440"),
_xpcom_factory: XPCOMUtils.generateSingletonFactory(urlInlineComplete),
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIAutoCompleteSearch,
Ci.mozIStorageStatementCallback,

View File

@ -60,23 +60,6 @@ const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
////////////////////////////////////////////////////////////////////////////////
//// nsIFactory
const nsPlacesExpirationFactory = {
_instance: null,
createInstance: function(aOuter, aIID) {
if (aOuter != null)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return this._instance === null ? this._instance = new nsPlacesExpiration() :
this._instance;
},
lockFactory: function (aDoLock) {},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIFactory
]),
};
////////////////////////////////////////////////////////////////////////////////
//// Constants
@ -1031,13 +1014,13 @@ nsPlacesExpiration.prototype = {
classID: Components.ID("705a423f-2f69-42f3-b9fe-1517e0dee56f"),
_xpcom_factory: nsPlacesExpirationFactory,
_xpcom_factory: XPCOMUtils.generateSingletonFactory(nsPlacesExpiration),
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIObserver,
Ci.nsINavHistoryObserver,
Ci.nsITimerCallback,
Ci.mozIStorageStatementCallback,
Ci.nsIObserver
, Ci.nsINavHistoryObserver
, Ci.nsITimerCallback
, Ci.mozIStorageStatementCallback
])
};

View File

@ -472,9 +472,13 @@ TaggingService.prototype = {
onBeginUpdateBatch: function () {},
onEndUpdateBatch: function () {},
// nsISupports
//////////////////////////////////////////////////////////////////////////////
//// nsISupports
classID: Components.ID("{bbc23860-2553-479d-8b78-94d9038334f7}"),
_xpcom_factory: XPCOMUtils.generateSingletonFactory(TaggingService),
QueryInterface: XPCOMUtils.generateQI([
Ci.nsITaggingService
, Ci.nsINavBookmarkObserver
@ -697,12 +701,16 @@ TagAutoCompleteSearch.prototype = {
this._stopped = true;
},
// nsISupports
//////////////////////////////////////////////////////////////////////////////
//// nsISupports
classID: Components.ID("{1dcc23b0-d4cb-11dc-9ad6-479d56d89593}"),
_xpcom_factory: XPCOMUtils.generateSingletonFactory(TagAutoCompleteSearch),
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIAutoCompleteSearch
]),
classID: Components.ID("{1dcc23b0-d4cb-11dc-9ad6-479d56d89593}")
])
};
let component = [TaggingService, TagAutoCompleteSearch];