From ed5fdc5f5aa26127eacd45f627a21c91302c4e23 Mon Sep 17 00:00:00 2001 From: Gavin Sharp Date: Wed, 11 Jul 2012 10:43:56 -0700 Subject: [PATCH] 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 --- modules/libpref/src/init/all.js | 2 + toolkit/components/social/SocialProvider.jsm | 64 +++++++++++++++---- toolkit/components/social/SocialService.jsm | 59 +++++++++++++---- .../social/test/browser/Makefile.in | 1 + .../test/browser/browser_SocialProvider.js | 35 ++++++++++ .../social/test/browser/browser_workerAPI.js | 5 +- .../test/xpcshell/test_SocialService.js | 30 ++++++++- 7 files changed, 168 insertions(+), 28 deletions(-) create mode 100644 toolkit/components/social/test/browser/browser_SocialProvider.js diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 549d3554f6ee..acfc40d77ce2 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -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 // window to be collected via the GC/CC. pref("memory.ghost_window_timeout_seconds", 60); + +pref("social.enabled", false); diff --git a/toolkit/components/social/SocialProvider.jsm b/toolkit/components/social/SocialProvider.jsm index 426e2006877a..759e91a63cc3 100644 --- a/toolkit/components/social/SocialProvider.jsm +++ b/toolkit/components/social/SocialProvider.jsm @@ -18,8 +18,9 @@ const EXPORTED_SYMBOLS = ["SocialProvider"]; * * @constructor * @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) throw new Error("SocialProvider must be passed a name"); if (!input.origin) @@ -29,16 +30,53 @@ function SocialProvider(input) { this.workerURL = input.workerURL; this.origin = input.origin; - let workerAPIPort = this.getWorkerPort(); - if (workerAPIPort) - this.workerAPI = new WorkerAPI(workerAPIPort); + // If enabled is |undefined|, default to true. + this._enabled = !(enabled == false); + if (this._enabled) + this._activate(); } SocialProvider.prototype = { - /** - * Terminates the provider's FrameWorker, if it has one. - */ - terminate: function terminate() { + // Provider enabled/disabled state. Disabled providers do not have active + // connections to their FrameWorkers. + _enabled: true, + 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) { try { getFrameWorkerHandle(this.workerURL, null).terminate(); @@ -46,18 +84,20 @@ SocialProvider.prototype = { 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 - * 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) */ - getWorkerPort: function getWorkerPort(window) { - if (!this.workerURL) + _getWorkerPort: function _getWorkerPort(window) { + if (!this.workerURL || !this.enabled) return null; try { return getFrameWorkerHandle(this.workerURL, window).port; diff --git a/toolkit/components/social/SocialService.jsm b/toolkit/components/social/SocialService.jsm index 31dfb5afd391..a8af9eb9d40c 100644 --- a/toolkit/components/social/SocialService.jsm +++ b/toolkit/components/social/SocialService.jsm @@ -10,30 +10,68 @@ Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/SocialProvider.jsm"); -const MANIFEST_PREFS = Services.prefs.getBranch("social.manifest."); - -let SocialServiceInternal = {}; +// Internal helper methods and state +let SocialServiceInternal = { + enabled: Services.prefs.getBoolPref("social.enabled"), + get providerArray() { + return [p for ([, p] of Iterator(this.providers))]; + } +}; 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 MANIFEST_PREFS = Services.prefs.getBranch("social.manifest."); let prefs = MANIFEST_PREFS.getChildList("", {}); prefs.forEach(function (pref) { try { var manifest = JSON.parse(MANIFEST_PREFS.getCharPref(pref)); if (manifest && typeof(manifest) == "object") { - let provider = new SocialProvider(manifest); + let provider = new SocialProvider(manifest, SocialServiceInternal.enabled); providers[provider.origin] = provider; } } catch (err) { Cu.reportError("SocialService: failed to load provider: " + pref + ", exception: " + err); } - }, this); + }); return providers; }); +function schedule(callback) { + Services.tm.mainThread.dispatch(callback, Ci.nsIThread.DISPATCH_NORMAL); +} + +// Public API 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) { schedule((function () { onDone(SocialServiceInternal.providers[origin] || null); @@ -42,13 +80,8 @@ const SocialService = { // Returns an array of installed provider origins. getProviderList: function getProviderList(onDone) { - let providers = [p for each (p in SocialServiceInternal.providers)]; - schedule((function () { - onDone(providers); - }).bind(this)); + schedule(function () { + onDone(SocialServiceInternal.providerArray); + }); } }; - -function schedule(callback) { - Services.tm.mainThread.dispatch(callback, Ci.nsIThread.DISPATCH_NORMAL); -} diff --git a/toolkit/components/social/test/browser/Makefile.in b/toolkit/components/social/test/browser/Makefile.in index f7b02167aa93..ea4ae941e5bc 100644 --- a/toolkit/components/social/test/browser/Makefile.in +++ b/toolkit/components/social/test/browser/Makefile.in @@ -21,6 +21,7 @@ MOCHITEST_BROWSER_FILES = \ relative_import.js \ browser_workerAPI.js \ worker_social.js \ + browser_SocialProvider.js \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/toolkit/components/social/test/browser/browser_SocialProvider.js b/toolkit/components/social/test/browser/browser_SocialProvider.js new file mode 100644 index 000000000000..e3afbf913cdc --- /dev/null +++ b/toolkit/components/social/test/browser/browser_SocialProvider.js @@ -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; +} diff --git a/toolkit/components/social/test/browser/browser_workerAPI.js b/toolkit/components/social/test/browser/browser_workerAPI.js index dfabb9970672..765648b04ffe 100644 --- a/toolkit/components/social/test/browser/browser_workerAPI.js +++ b/toolkit/components/social/test/browser/browser_workerAPI.js @@ -19,14 +19,15 @@ function test() { ok(provider.workerAPI, "provider has a workerAPI"); 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"); port.onmessage = function onMessage(event) { let {topic, data} = event.data; if (topic == "test-initialization-complete") { is(provider.workerAPI.initialized, true, "workerAPI is now initialized"); - provider.terminate(); + // Terminate the provider + provider.enabled = false; finish(); } } diff --git a/toolkit/components/social/test/xpcshell/test_SocialService.js b/toolkit/components/social/test/xpcshell/test_SocialService.js index a9108cf44759..de5b57608de7 100644 --- a/toolkit/components/social/test/xpcshell/test_SocialService.js +++ b/toolkit/components/social/test/xpcshell/test_SocialService.js @@ -2,6 +2,8 @@ * 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/. */ +Cu.import("resource://gre/modules/Services.jsm"); + function run_test() { let manifests = [ { // normal provider @@ -18,14 +20,16 @@ function run_test() { manifests.forEach(function (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"); let runner = new AsyncRunner(); let next = runner.next.bind(runner); runner.appendIterator(testGetProvider(manifests, next)); runner.appendIterator(testGetProviderList(manifests, next)); + runner.appendIterator(testEnabled(manifests, next)); runner.next(); } @@ -48,7 +52,31 @@ function testGetProviderList(manifests, next) { let providerIdx = providers.map(function (p) p.origin).indexOf(manifests[i].origin); let provider = providers[providerIdx]; do_check_true(!!provider); + do_check_true(provider.enabled); do_check_eq(provider.workerURL, manifests[i].workerURL); 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); + }); +}