Bug 771980: provider must be re-usable, r=jaws

--HG--
extra : transplant_source : s%94%84%C2%7C%27%A3%B0%DB%5B%7F%40%96%A4S%21%FFcSA
This commit is contained in:
Gavin Sharp 2012-07-11 10:43:56 -07:00
parent 2c31655232
commit ed5fdc5f5a
7 changed files with 168 additions and 28 deletions

View File

@ -3621,3 +3621,5 @@ pref("memory.low_memory_notification_interval_ms", 10000);
// likely leak)? This should be longer than it usually takes for an eligible // likely leak)? This should be longer than it usually takes for an eligible
// window to be collected via the GC/CC. // window to be collected via the GC/CC.
pref("memory.ghost_window_timeout_seconds", 60); pref("memory.ghost_window_timeout_seconds", 60);
pref("social.enabled", false);

View File

@ -18,8 +18,9 @@ const EXPORTED_SYMBOLS = ["SocialProvider"];
* *
* @constructor * @constructor
* @param {jsobj} object representing the manifest file describing this provider * @param {jsobj} object representing the manifest file describing this provider
* @param {bool} whether the provider should be initially enabled (defaults to true)
*/ */
function SocialProvider(input) { function SocialProvider(input, enabled) {
if (!input.name) if (!input.name)
throw new Error("SocialProvider must be passed a name"); throw new Error("SocialProvider must be passed a name");
if (!input.origin) if (!input.origin)
@ -29,16 +30,53 @@ function SocialProvider(input) {
this.workerURL = input.workerURL; this.workerURL = input.workerURL;
this.origin = input.origin; this.origin = input.origin;
let workerAPIPort = this.getWorkerPort(); // If enabled is |undefined|, default to true.
if (workerAPIPort) this._enabled = !(enabled == false);
this.workerAPI = new WorkerAPI(workerAPIPort); if (this._enabled)
this._activate();
} }
SocialProvider.prototype = { SocialProvider.prototype = {
/** // Provider enabled/disabled state. Disabled providers do not have active
* Terminates the provider's FrameWorker, if it has one. // connections to their FrameWorkers.
*/ _enabled: true,
terminate: function terminate() { get enabled() {
return this._enabled;
},
set enabled(val) {
let enable = !!val;
if (enable == this._enabled)
return;
this._enabled = enable;
if (enable) {
this._activate();
} else {
this._terminate();
}
},
// Active port to the provider's FrameWorker. Null if the provider has no
// FrameWorker, or is disabled.
port: null,
// Reference to a workerAPI object for this provider. Null if the provider has
// no FrameWorker, or is disabled.
workerAPI: null,
// Internal helper methods
_activate: function _activate() {
// Initialize the workerAPI and its port first, so that its initialization
// occurs before any other messages are processed by other ports.
let workerAPIPort = this._getWorkerPort();
if (workerAPIPort)
this.workerAPI = new WorkerAPI(workerAPIPort);
this.port = this._getWorkerPort();
},
_terminate: function _terminate() {
if (this.workerURL) { if (this.workerURL) {
try { try {
getFrameWorkerHandle(this.workerURL, null).terminate(); getFrameWorkerHandle(this.workerURL, null).terminate();
@ -46,18 +84,20 @@ SocialProvider.prototype = {
Cu.reportError("SocialProvider FrameWorker termination failed: " + e); Cu.reportError("SocialProvider FrameWorker termination failed: " + e);
} }
} }
this.port = null;
this.workerAPI = null;
}, },
/** /**
* Instantiates a FrameWorker for the provider if one doesn't exist, and * Instantiates a FrameWorker for the provider if one doesn't exist, and
* returns a reference to a port to that FrameWorker. * returns a reference to a new port to that FrameWorker.
* *
* Returns null if this provider has no workerURL. * Returns null if this provider has no workerURL, or is disabled.
* *
* @param {DOMWindow} window (optional) * @param {DOMWindow} window (optional)
*/ */
getWorkerPort: function getWorkerPort(window) { _getWorkerPort: function _getWorkerPort(window) {
if (!this.workerURL) if (!this.workerURL || !this.enabled)
return null; return null;
try { try {
return getFrameWorkerHandle(this.workerURL, window).port; return getFrameWorkerHandle(this.workerURL, window).port;

View File

@ -10,30 +10,68 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/SocialProvider.jsm"); Cu.import("resource://gre/modules/SocialProvider.jsm");
const MANIFEST_PREFS = Services.prefs.getBranch("social.manifest."); // Internal helper methods and state
let SocialServiceInternal = {
let SocialServiceInternal = {}; enabled: Services.prefs.getBoolPref("social.enabled"),
get providerArray() {
return [p for ([, p] of Iterator(this.providers))];
}
};
XPCOMUtils.defineLazyGetter(SocialServiceInternal, "providers", function () { XPCOMUtils.defineLazyGetter(SocialServiceInternal, "providers", function () {
// Initialize the service (add a pref observer)
function prefObserver(subject, topic, data) {
SocialService._setEnabled(Services.prefs.getBoolPref(data));
}
Services.prefs.addObserver("social.enabled", prefObserver, false);
Services.obs.addObserver(function xpcomShutdown() {
Services.obs.removeObserver(xpcomShutdown, "xpcom-shutdown");
Services.prefs.removeObserver("social.enabled", prefObserver);
}, "xpcom-shutdown", false);
// Now retrieve the providers
let providers = {}; let providers = {};
let MANIFEST_PREFS = Services.prefs.getBranch("social.manifest.");
let prefs = MANIFEST_PREFS.getChildList("", {}); let prefs = MANIFEST_PREFS.getChildList("", {});
prefs.forEach(function (pref) { prefs.forEach(function (pref) {
try { try {
var manifest = JSON.parse(MANIFEST_PREFS.getCharPref(pref)); var manifest = JSON.parse(MANIFEST_PREFS.getCharPref(pref));
if (manifest && typeof(manifest) == "object") { if (manifest && typeof(manifest) == "object") {
let provider = new SocialProvider(manifest); let provider = new SocialProvider(manifest, SocialServiceInternal.enabled);
providers[provider.origin] = provider; providers[provider.origin] = provider;
} }
} catch (err) { } catch (err) {
Cu.reportError("SocialService: failed to load provider: " + pref + Cu.reportError("SocialService: failed to load provider: " + pref +
", exception: " + err); ", exception: " + err);
} }
}, this); });
return providers; return providers;
}); });
function schedule(callback) {
Services.tm.mainThread.dispatch(callback, Ci.nsIThread.DISPATCH_NORMAL);
}
// Public API
const SocialService = { const SocialService = {
get enabled() {
return SocialServiceInternal.enabled;
},
set enabled(val) {
let enable = !!val;
if (enable == SocialServiceInternal.enabled)
return;
Services.prefs.setBoolPref("social.enabled", enable);
this._setEnabled(enable);
},
_setEnabled: function _setEnabled(enable) {
SocialServiceInternal.providerArray.forEach(function (p) p.enabled = enable);
SocialServiceInternal.enabled = enable;
},
// Returns a single provider object with the specified origin.
getProvider: function getProvider(origin, onDone) { getProvider: function getProvider(origin, onDone) {
schedule((function () { schedule((function () {
onDone(SocialServiceInternal.providers[origin] || null); onDone(SocialServiceInternal.providers[origin] || null);
@ -42,13 +80,8 @@ const SocialService = {
// Returns an array of installed provider origins. // Returns an array of installed provider origins.
getProviderList: function getProviderList(onDone) { getProviderList: function getProviderList(onDone) {
let providers = [p for each (p in SocialServiceInternal.providers)]; schedule(function () {
schedule((function () { onDone(SocialServiceInternal.providerArray);
onDone(providers); });
}).bind(this));
} }
}; };
function schedule(callback) {
Services.tm.mainThread.dispatch(callback, Ci.nsIThread.DISPATCH_NORMAL);
}

View File

@ -21,6 +21,7 @@ MOCHITEST_BROWSER_FILES = \
relative_import.js \ relative_import.js \
browser_workerAPI.js \ browser_workerAPI.js \
worker_social.js \ worker_social.js \
browser_SocialProvider.js \
$(NULL) $(NULL)
include $(topsrcdir)/config/rules.mk include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,35 @@
/* 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/. */
let SocialProvider = Components.utils.import("resource://gre/modules/SocialProvider.jsm", {}).SocialProvider;
function test() {
// This test creates a SocialProvider object directly - it would be nicer to
// go through the SocialService, but adding a test provider before the service
// has been initialized can be tricky.
let provider = new SocialProvider({
origin: 'http://example.com',
name: "Example Provider",
workerURL: "http://example.com/browser/toolkit/components/social/test/browser/worker_social.js"
});
ok(provider.enabled, "provider is initially enabled");
ok(provider.port, "should be able to get a port from enabled provider");
ok(provider.workerAPI, "should be able to get a workerAPI from enabled provider");
provider.enabled = false;
ok(!provider.enabled, "provider is now disabled");
ok(!provider.port, "shouldn't be able to get a port from disabled provider");
ok(!provider.workerAPI, "shouldn't be able to get a workerAPI from disabled provider");
provider.enabled = true;
ok(provider.enabled, "provider is re-enabled");
ok(provider.port, "should be able to get a port from re-enabled provider");
ok(provider.workerAPI, "should be able to get a workerAPI from re-enabled provider");
// Terminate the provider
provider.enabled = false;
}

View File

@ -19,14 +19,15 @@ function test() {
ok(provider.workerAPI, "provider has a workerAPI"); ok(provider.workerAPI, "provider has a workerAPI");
is(provider.workerAPI.initialized, false, "workerAPI is not yet initialized"); is(provider.workerAPI.initialized, false, "workerAPI is not yet initialized");
let port = provider.getWorkerPort(); let port = provider.port;
ok(port, "should be able to get a port from the provider"); ok(port, "should be able to get a port from the provider");
port.onmessage = function onMessage(event) { port.onmessage = function onMessage(event) {
let {topic, data} = event.data; let {topic, data} = event.data;
if (topic == "test-initialization-complete") { if (topic == "test-initialization-complete") {
is(provider.workerAPI.initialized, true, "workerAPI is now initialized"); is(provider.workerAPI.initialized, true, "workerAPI is now initialized");
provider.terminate(); // Terminate the provider
provider.enabled = false;
finish(); finish();
} }
} }

View File

@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file, * 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/. */ * You can obtain one at http://mozilla.org/MPL/2.0/. */
Cu.import("resource://gre/modules/Services.jsm");
function run_test() { function run_test() {
let manifests = [ let manifests = [
{ // normal provider { // normal provider
@ -18,14 +20,16 @@ function run_test() {
manifests.forEach(function (manifest) { manifests.forEach(function (manifest) {
MANIFEST_PREFS.setCharPref(manifest.origin, JSON.stringify(manifest)); MANIFEST_PREFS.setCharPref(manifest.origin, JSON.stringify(manifest));
}); });
do_register_cleanup(function () MANIFEST_PREFS.deleteBranch(""));
// Enable the service for this test
Services.prefs.setBoolPref("social.enabled", true);
Cu.import("resource://gre/modules/SocialService.jsm"); Cu.import("resource://gre/modules/SocialService.jsm");
let runner = new AsyncRunner(); let runner = new AsyncRunner();
let next = runner.next.bind(runner); let next = runner.next.bind(runner);
runner.appendIterator(testGetProvider(manifests, next)); runner.appendIterator(testGetProvider(manifests, next));
runner.appendIterator(testGetProviderList(manifests, next)); runner.appendIterator(testGetProviderList(manifests, next));
runner.appendIterator(testEnabled(manifests, next));
runner.next(); runner.next();
} }
@ -48,7 +52,31 @@ function testGetProviderList(manifests, next) {
let providerIdx = providers.map(function (p) p.origin).indexOf(manifests[i].origin); let providerIdx = providers.map(function (p) p.origin).indexOf(manifests[i].origin);
let provider = providers[providerIdx]; let provider = providers[providerIdx];
do_check_true(!!provider); do_check_true(!!provider);
do_check_true(provider.enabled);
do_check_eq(provider.workerURL, manifests[i].workerURL); do_check_eq(provider.workerURL, manifests[i].workerURL);
do_check_eq(provider.name, manifests[i].name); do_check_eq(provider.name, manifests[i].name);
} }
} }
function testEnabled(manifests, next) {
let providers = yield SocialService.getProviderList(next);
do_check_true(providers.length >= manifests.length);
do_check_true(SocialService.enabled);
providers.forEach(function (provider) {
do_check_true(provider.enabled);
});
SocialService.enabled = false;
do_check_true(!Services.prefs.getBoolPref("social.enabled"));
do_check_true(!SocialService.enabled);
providers.forEach(function (provider) {
do_check_true(!provider.enabled);
});
// Check that setting the pref directly updates things accordingly
Services.prefs.setBoolPref("social.enabled", true);
do_check_true(SocialService.enabled);
providers.forEach(function (provider) {
do_check_true(provider.enabled);
});
}