From 4f0df069bb31aaf8f0555b796376b1e129e1f89c Mon Sep 17 00:00:00 2001 From: chaithanya Date: Wed, 27 Jan 2016 09:57:00 +0100 Subject: [PATCH 01/28] Bug 1184458- TelemetryEnvironment needs to shut down properly r=gfritzsche --- .../telemetry/TelemetryController.jsm | 1 + .../telemetry/TelemetryEnvironment.jsm | 9 +++++++ .../tests/unit/test_TelemetryEnvironment.js | 25 +++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/toolkit/components/telemetry/TelemetryController.jsm b/toolkit/components/telemetry/TelemetryController.jsm index 61afcf8a6ae..a408ec1acc0 100644 --- a/toolkit/components/telemetry/TelemetryController.jsm +++ b/toolkit/components/telemetry/TelemetryController.jsm @@ -795,6 +795,7 @@ var Impl = { try { // Stop the datachoices infobar display. TelemetryReportingPolicy.shutdown(); + TelemetryEnvironment.shutdown(); // Stop any ping sending. yield TelemetrySend.shutdown(); diff --git a/toolkit/components/telemetry/TelemetryEnvironment.jsm b/toolkit/components/telemetry/TelemetryEnvironment.jsm index 109baa36e91..0b1f07ad004 100644 --- a/toolkit/components/telemetry/TelemetryEnvironment.jsm +++ b/toolkit/components/telemetry/TelemetryEnvironment.jsm @@ -77,6 +77,10 @@ this.TelemetryEnvironment = { return getGlobal().unregisterChangeListener(name); }, + shutdown: function() { + return getGlobal().shutdown(); + }, + // Policy to use when saving preferences. Exported for using them in tests. RECORD_PREF_STATE: 1, // Don't record the preference value RECORD_PREF_VALUE: 2, // We only record user-set prefs. @@ -799,6 +803,11 @@ EnvironmentCache.prototype = { this._changeListeners.delete(name); }, + shutdown: function() { + this._log.trace("shutdown"); + this._shutdown = true; + }, + /** * Only used in tests, set the preferences to watch. * @param aPreferences A map of preferences names and their recording policy. diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js index 4d744924595..2b4e472a772 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js @@ -1372,6 +1372,31 @@ add_task(function* test_defaultSearchEngine() { Assert.equal(data.settings.searchCohort, "testcohort"); }); +add_task(function* test_environmentShutdown() { + // Define and reset the test preference. + const PREF_TEST = "toolkit.telemetry.test.pref1"; + const PREFS_TO_WATCH = new Map([ + [PREF_TEST, {what: TelemetryEnvironment.RECORD_PREF_STATE}], + ]); + Preferences.reset(PREF_TEST); + gNow = futureDate(gNow, 10 * MILLISECONDS_PER_MINUTE); + fakeNow(gNow); + + // Set up the preferences and listener, then the trigger shutdown + TelemetryEnvironment._watchPreferences(PREFS_TO_WATCH); + TelemetryEnvironment.registerChangeListener("test_environmentShutdownChange", () => { + // Register a new change listener that asserts if change is propogated + Assert.ok(false, "No change should be propagated after shutdown."); + }); + TelemetryEnvironment.shutdown(); + + // Flipping the test preference after shutdown should not trigger the listener + Preferences.set(PREF_TEST, 1); + + // Unregister the listener. + TelemetryEnvironment.unregisterChangeListener("test_environmentShutdownChange"); +}); + add_task(function*() { do_test_finished(); }); From 838f29d82794191a20ffaeb4b15a3c394303d060 Mon Sep 17 00:00:00 2001 From: brendan Date: Sun, 24 Jan 2016 22:33:52 -0500 Subject: [PATCH 02/28] Bug 1237983 - Investigate and remove the Bagheera Client Implementation. r=gfritzsche --- services/common/bagheeraclient.js | 278 ---------------- .../common/modules-testing/bagheeraserver.js | 296 ------------------ services/common/moz.build | 8 - services/common/tests/mach_commands.py | 6 - services/common/tests/run_bagheera_server.js | 26 -- .../common/tests/unit/test_bagheera_client.js | 161 ---------- .../common/tests/unit/test_bagheera_server.js | 30 -- .../common/tests/unit/test_load_modules.js | 14 - services/common/tests/unit/xpcshell.ini | 6 - 9 files changed, 825 deletions(-) delete mode 100644 services/common/bagheeraclient.js delete mode 100644 services/common/modules-testing/bagheeraserver.js delete mode 100644 services/common/tests/run_bagheera_server.js delete mode 100644 services/common/tests/unit/test_bagheera_client.js delete mode 100644 services/common/tests/unit/test_bagheera_server.js diff --git a/services/common/bagheeraclient.js b/services/common/bagheeraclient.js deleted file mode 100644 index dd41f65dcf9..00000000000 --- a/services/common/bagheeraclient.js +++ /dev/null @@ -1,278 +0,0 @@ -/* 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/. */ - -/** - * This file contains a client API for the Bagheera data storage service. - * - * Information about Bagheera is available at - * https://github.com/mozilla-metrics/bagheera - */ - -#ifndef MERGED_COMPARTMENT - -"use strict"; - -this.EXPORTED_SYMBOLS = [ - "BagheeraClient", - "BagheeraClientRequestResult", -]; - -var {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -#endif - -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://services-common/rest.js"); -Cu.import("resource://services-common/utils.js"); - -/** - * Represents the result of a Bagheera request. - */ -this.BagheeraClientRequestResult = function BagheeraClientRequestResult() { - this.transportSuccess = false; - this.serverSuccess = false; - this.request = null; -}; - -Object.freeze(BagheeraClientRequestResult.prototype); - - -/** - * Wrapper around RESTRequest so logging is sane. - */ -function BagheeraRequest(uri) { - RESTRequest.call(this, uri); - - this._log = Log.repository.getLogger("Services.BagheeraClient"); - this._log.level = Log.Level.Debug; -} - -BagheeraRequest.prototype = Object.freeze({ - __proto__: RESTRequest.prototype, -}); - - -/** - * Create a new Bagheera client instance. - * - * Each client is associated with a specific Bagheera HTTP URI endpoint. - * - * @param baseURI - * (string) The base URI of the Bagheera HTTP endpoint. - */ -this.BagheeraClient = function BagheeraClient(baseURI) { - if (!baseURI) { - throw new Error("baseURI argument must be defined."); - } - - this._log = Log.repository.getLogger("Services.BagheeraClient"); - this._log.level = Log.Level.Debug; - - this.baseURI = baseURI; - - if (!baseURI.endsWith("/")) { - this.baseURI += "/"; - } -}; - -BagheeraClient.prototype = Object.freeze({ - /** - * Channel load flags for all requests. - * - * Caching is not applicable, so we bypass and disable it. We also - * ignore any cookies that may be present for the domain because - * Bagheera does not utilize cookies and the release of cookies may - * inadvertantly constitute unncessary information disclosure. - */ - _loadFlags: Ci.nsIRequest.LOAD_BYPASS_CACHE | - Ci.nsIRequest.INHIBIT_CACHING | - Ci.nsIRequest.LOAD_ANONYMOUS, - - DEFAULT_TIMEOUT_MSEC: 5 * 60 * 1000, // 5 minutes. - - _RE_URI_IDENTIFIER: /^[a-zA-Z0-9_-]+$/, - - /** - * Upload a JSON payload to the server. - * - * The return value is a Promise which will be resolved with a - * BagheeraClientRequestResult when the request has finished. - * - * @param namespace - * (string) The namespace to post this data to. - * @param id - * (string) The ID of the document being uploaded. This is typically - * a UUID in hex form. - * @param payload - * (string|object) Data to upload. Can be specified as a string (which - * is assumed to be JSON) or an object. If an object, it will be fed into - * JSON.stringify() for serialization. - * @param options - * (object) Extra options to control behavior. Recognized properties: - * - * deleteIDs -- (array) Old document IDs to delete as part of - * upload. If not specified, no old documents will be deleted as - * part of upload. The array values are typically UUIDs in hex - * form. - * - * telemetryCompressed -- (string) Telemetry histogram to record - * compressed size of payload under. If not defined, no telemetry - * data for the compressed size will be recorded. - * - * @return Promise - */ - uploadJSON: function uploadJSON(namespace, id, payload, options={}) { - if (!namespace) { - throw new Error("namespace argument must be defined."); - } - - if (!id) { - throw new Error("id argument must be defined."); - } - - if (!payload) { - throw new Error("payload argument must be defined."); - } - - if (options && typeof(options) != "object") { - throw new Error("Unexpected type for options argument. Expected object. " + - "Got: " + typeof(options)); - } - - let uri = this._submitURI(namespace, id); - - let data = payload; - - if (typeof(payload) == "object") { - data = JSON.stringify(payload); - } - - if (typeof(data) != "string") { - throw new Error("Unknown type for payload: " + typeof(data)); - } - - this._log.info("Uploading data to " + uri); - - let request = new BagheeraRequest(uri); - request.loadFlags = this._loadFlags; - request.timeout = this.DEFAULT_TIMEOUT_MSEC; - - // Since API changed, throw on old API usage. - if ("deleteID" in options) { - throw new Error("API has changed, use (array) deleteIDs instead"); - } - - let deleteIDs; - if (options.deleteIDs && options.deleteIDs.length > 0) { - deleteIDs = options.deleteIDs; - this._log.debug("Will delete " + deleteIDs.join(", ")); - request.setHeader("X-Obsolete-Document", deleteIDs.join(",")); - } - - let deferred = Promise.defer(); - - // The string converter service used by CommonUtils.convertString() - // silently throws away high bytes. We need to convert the string to - // consist of only low bytes first. - data = CommonUtils.encodeUTF8(data); - data = CommonUtils.convertString(data, "uncompressed", "deflate"); - if (options.telemetryCompressed) { - try { - let h = Services.telemetry.getHistogramById(options.telemetryCompressed); - h.add(data.length); - } catch (ex) { - this._log.warn("Unable to record telemetry for compressed payload size", ex); - } - } - - // TODO proper header per bug 807134. - request.setHeader("Content-Type", "application/json+zlib; charset=utf-8"); - - this._log.info("Request body length: " + data.length); - - let result = new BagheeraClientRequestResult(); - result.namespace = namespace; - result.id = id; - result.deleteIDs = deleteIDs ? deleteIDs.slice(0) : null; - - request.onComplete = this._onComplete.bind(this, request, deferred, result); - request.post(data); - - return deferred.promise; - }, - - /** - * Delete the specified document. - * - * @param namespace - * (string) Namespace from which to delete the document. - * @param id - * (string) ID of document to delete. - * - * @return Promise - */ - deleteDocument: function deleteDocument(namespace, id) { - let uri = this._submitURI(namespace, id); - - let request = new BagheeraRequest(uri); - request.loadFlags = this._loadFlags; - request.timeout = this.DEFAULT_TIMEOUT_MSEC; - - let result = new BagheeraClientRequestResult(); - result.namespace = namespace; - result.id = id; - let deferred = Promise.defer(); - - request.onComplete = this._onComplete.bind(this, request, deferred, result); - request.delete(); - - return deferred.promise; - }, - - _submitURI: function _submitURI(namespace, id) { - if (!this._RE_URI_IDENTIFIER.test(namespace)) { - throw new Error("Illegal namespace name. Must be alphanumeric + [_-]: " + - namespace); - } - - if (!this._RE_URI_IDENTIFIER.test(id)) { - throw new Error("Illegal id value. Must be alphanumeric + [_-]: " + id); - } - - return this.baseURI + "1.0/submit/" + namespace + "/" + id; - }, - - _onComplete: function _onComplete(request, deferred, result, error) { - result.request = request; - - if (error) { - this._log.info("Transport failure on request", error); - result.transportSuccess = false; - deferred.resolve(result); - return; - } - - result.transportSuccess = true; - - let response = request.response; - - switch (response.status) { - case 200: - case 201: - result.serverSuccess = true; - break; - - default: - result.serverSuccess = false; - - this._log.info("Received unexpected status code: " + response.status); - this._log.debug("Response body: " + response.body); - } - - deferred.resolve(result); - }, -}); - diff --git a/services/common/modules-testing/bagheeraserver.js b/services/common/modules-testing/bagheeraserver.js deleted file mode 100644 index 1e51b92a9f9..00000000000 --- a/services/common/modules-testing/bagheeraserver.js +++ /dev/null @@ -1,296 +0,0 @@ -/* 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/. */ - -"use strict"; - -var {utils: Cu} = Components; - -this.EXPORTED_SYMBOLS = ["BagheeraServer"]; - -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://services-common/utils.js"); -Cu.import("resource://testing-common/httpd.js"); - -/** - * This is an implementation of the Bagheera server. - * - * The purpose of the server is to facilitate testing of the Bagheera - * client and the Firefox Health report. It is *not* meant to be a - * production grade server. - * - * The Bagheera server is essentially a glorified document store. - */ -this.BagheeraServer = function BagheeraServer() { - this._log = Log.repository.getLogger("metrics.BagheeraServer"); - - this.server = new HttpServer(); - this.namespaces = {}; - - this.allowAllNamespaces = false; -} - -BagheeraServer.prototype = { - /** - * Whether this server has a namespace defined. - * - * @param ns - * (string) Namepsace whose existence to query for. - * @return bool - */ - hasNamespace: function hasNamespace(ns) { - return ns in this.namespaces; - }, - - /** - * Whether this server has an ID in a particular namespace. - * - * @param ns - * (string) Namespace to look for item in. - * @param id - * (string) ID of object to look for. - * @return bool - */ - hasDocument: function hasDocument(ns, id) { - let namespace = this.namespaces[ns]; - - if (!namespace) { - return false; - } - - return id in namespace; - }, - - /** - * Obtain a document from the server. - * - * @param ns - * (string) Namespace to retrieve document from. - * @param id - * (string) ID of document to retrieve. - * - * @return string The content of the document or null if the document - * does not exist. - */ - getDocument: function getDocument(ns, id) { - let namespace = this.namespaces[ns]; - - if (!namespace) { - return null; - } - - return namespace[id]; - }, - - /** - * Set the contents of a document in the server. - * - * @param ns - * (string) Namespace to add document to. - * @param id - * (string) ID of document being added. - * @param payload - * (string) The content of the document. - */ - setDocument: function setDocument(ns, id, payload) { - let namespace = this.namespaces[ns]; - - if (!namespace) { - if (!this.allowAllNamespaces) { - throw new Error("Namespace does not exist: " + ns); - } - - this.createNamespace(ns); - namespace = this.namespaces[ns]; - } - - namespace[id] = payload; - }, - - /** - * Create a namespace in the server. - * - * The namespace will initially be empty. - * - * @param ns - * (string) The name of the namespace to create. - */ - createNamespace: function createNamespace(ns) { - if (ns in this.namespaces) { - throw new Error("Namespace already exists: " + ns); - } - - this.namespaces[ns] = {}; - }, - - start: function start(port=-1) { - this.server.registerPrefixHandler("/", this._handleRequest.bind(this)); - this.server.start(port); - let i = this.server.identity; - - this.serverURI = i.primaryScheme + "://" + i.primaryHost + ":" + - i.primaryPort + "/"; - this.port = i.primaryPort; - }, - - stop: function stop(cb) { - let handler = {onStopped: cb}; - - this.server.stop(handler); - }, - - /** - * Our root path handler. - */ - _handleRequest: function _handleRequest(request, response) { - let path = request.path; - this._log.info("Received request: " + request.method + " " + path + " " + - "HTTP/" + request.httpVersion); - - try { - if (path.startsWith("/1.0/submit/")) { - return this._handleV1Submit(request, response, - path.substr("/1.0/submit/".length)); - } else { - throw HTTP_404; - } - } catch (ex) { - if (ex instanceof HttpError) { - this._log.info("HttpError thrown: " + ex.code + " " + ex.description); - } else { - this._log.warn("Exception processing request", ex); - } - - throw ex; - } - }, - - /** - * Handles requests to /submit/*. - */ - _handleV1Submit: function _handleV1Submit(request, response, rest) { - if (!rest.length) { - throw HTTP_404; - } - - let namespace; - let index = rest.indexOf("/"); - if (index == -1) { - namespace = rest; - rest = ""; - } else { - namespace = rest.substr(0, index); - rest = rest.substr(index + 1); - } - - this._handleNamespaceSubmit(namespace, rest, request, response); - }, - - _handleNamespaceSubmit: function _handleNamespaceSubmit(namespace, rest, - request, response) { - if (!this.hasNamespace(namespace)) { - if (!this.allowAllNamespaces) { - this._log.info("Request to unknown namespace: " + namespace); - throw HTTP_404; - } - - this.createNamespace(namespace); - } - - if (!rest) { - this._log.info("No ID defined."); - throw HTTP_404; - } - - let id = rest; - if (id.includes("/")) { - this._log.info("URI has too many components."); - throw HTTP_404; - } - - if (request.method == "POST") { - return this._handleNamespaceSubmitPost(namespace, id, request, response); - } - - if (request.method == "DELETE") { - return this._handleNamespaceSubmitDelete(namespace, id, request, response); - } - - this._log.info("Unsupported HTTP method on namespace handler: " + - request.method); - response.setHeader("Allow", "POST,DELETE"); - throw HTTP_405; - }, - - _handleNamespaceSubmitPost: - function _handleNamespaceSubmitPost(namespace, id, request, response) { - - this._log.info("Handling data upload for " + namespace + ":" + id); - - let requestBody = CommonUtils.readBytesFromInputStream(request.bodyInputStream); - this._log.info("Raw body length: " + requestBody.length); - - if (!request.hasHeader("Content-Type")) { - this._log.info("Request does not have Content-Type header."); - throw HTTP_400; - } - - const ALLOWED_TYPES = [ - // TODO proper content types from bug 807134. - "application/json; charset=utf-8", - "application/json+zlib; charset=utf-8", - ]; - - let ct = request.getHeader("Content-Type"); - if (ALLOWED_TYPES.indexOf(ct) == -1) { - this._log.info("Unknown media type: " + ct); - // Should generate proper HTTP response headers for this error. - throw HTTP_415; - } - - if (ct.startsWith("application/json+zlib")) { - this._log.debug("Uncompressing entity body with deflate."); - requestBody = CommonUtils.convertString(requestBody, "deflate", - "uncompressed"); - } - - requestBody = CommonUtils.decodeUTF8(requestBody); - - this._log.debug("HTTP request body: " + requestBody); - - let doc; - try { - doc = JSON.parse(requestBody); - } catch(ex) { - this._log.info("JSON parse error."); - throw HTTP_400; - } - - this.namespaces[namespace][id] = doc; - - if (request.hasHeader("X-Obsolete-Document")) { - let obsolete = request.getHeader("X-Obsolete-Document"); - this._log.info("Deleting from X-Obsolete-Document header: " + obsolete); - for (let obsolete_id of obsolete.split(",")) { - delete this.namespaces[namespace][obsolete_id]; - } - } - - response.setStatusLine(request.httpVersion, 201, "Created"); - response.setHeader("Content-Type", "text/plain"); - - let body = id; - response.bodyOutputStream.write(body, body.length); - }, - - _handleNamespaceSubmitDelete: - function _handleNamespaceSubmitDelete(namespace, id, request, response) { - - delete this.namespaces[namespace][id]; - - let body = id; - response.bodyOutputStream.write(body, body.length); - }, -}; - -Object.freeze(BagheeraServer.prototype); diff --git a/services/common/moz.build b/services/common/moz.build index ec96079c346..37e1aa64bdd 100644 --- a/services/common/moz.build +++ b/services/common/moz.build @@ -31,14 +31,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android' or CONFIG['MOZ_B2GDROID']: 'modules-testing/storageserver.js', ] - if CONFIG['MOZ_SERVICES_HEALTHREPORT']: - EXTRA_PP_JS_MODULES['services-common'] += [ - 'bagheeraclient.js', - ] - TESTING_JS_MODULES.services.common += [ - 'modules-testing/bagheeraserver.js', - ] - EXTRA_PP_JS_MODULES['services-common'] += [ 'async.js', 'observers.js', diff --git a/services/common/tests/mach_commands.py b/services/common/tests/mach_commands.py index 6777b81e4ce..b57fa3aa26b 100644 --- a/services/common/tests/mach_commands.py +++ b/services/common/tests/mach_commands.py @@ -34,7 +34,6 @@ DEFAULT_HOSTNAME = 'localhost' SRCDIR = mozpath.abspath(mozpath.dirname(__file__)) STORAGE_SERVER_SCRIPT = mozpath.join(SRCDIR, 'run_storage_server.js') -BAGHEERA_SERVER_SCRIPT = mozpath.join(SRCDIR, 'run_bagheera_server.js') def SyncStorageCommand(func): """Decorator that adds shared command arguments to services commands.""" @@ -110,8 +109,3 @@ class SyncTestCommands(MachCommandBase): @SyncStorageCommand def run_storage_server(self, port=DEFAULT_PORT, address=DEFAULT_HOSTNAME): exit(self.run_server(STORAGE_SERVER_SCRIPT, address, port)) - - @Command('bagheera-server', category='services', - description='Run a bagheera server.') - def run_bagheera_server(self, port=DEFAULT_PORT, address=DEFAULT_HOSTNAME): - exit(self.run_server(BAGHEERA_SERVER_SCRIPT, address, port)) diff --git a/services/common/tests/run_bagheera_server.js b/services/common/tests/run_bagheera_server.js deleted file mode 100644 index a97eb11eab6..00000000000 --- a/services/common/tests/run_bagheera_server.js +++ /dev/null @@ -1,26 +0,0 @@ -/* 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/. */ - -/** - * This file runs a stub Bagheera server. - * - * It is meant to be executed with an xpcshell. - * - * The Makefile in this directory contains a target to run it: - * - * $ make bagheera-server - */ - -Cu.import("resource://testing-common/services/common/bagheeraserver.js"); - -initTestLogging(); - -var server = new BagheeraServer(); -server.allowAllNamespaces = true; -server.start(SERVER_PORT); -_("Bagheera server started on port " + SERVER_PORT); - -// Launch the thread manager. -_do_main(); - diff --git a/services/common/tests/unit/test_bagheera_client.js b/services/common/tests/unit/test_bagheera_client.js deleted file mode 100644 index 2cd65f5c1d7..00000000000 --- a/services/common/tests/unit/test_bagheera_client.js +++ /dev/null @@ -1,161 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -Cu.import("resource://services-common/bagheeraclient.js"); -Cu.import("resource://services-common/rest.js"); -Cu.import("resource://testing-common/services/common/bagheeraserver.js"); -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); - -function getClientAndServer() { - let server = new BagheeraServer(); - server.start(); - - let client = new BagheeraClient(server.serverURI); - - return [client, server]; -} - -function run_test() { - initTestLogging("Trace"); - run_next_test(); -} - -add_test(function test_constructor() { - let client = new BagheeraClient("http://localhost:1234/"); - - run_next_test(); -}); - -add_test(function test_post_json_transport_failure() { - let client = new BagheeraClient("http://localhost:1234/"); - - client.uploadJSON("foo", "bar", {}).then(function onResult(result) { - do_check_false(result.transportSuccess); - - run_next_test(); - }); -}); - -add_test(function test_post_json_simple() { - let [client, server] = getClientAndServer(); - - server.createNamespace("foo"); - let promise = client.uploadJSON("foo", "bar", {foo: "bar", biz: "baz"}); - - promise.then(function onSuccess(result) { - do_check_true(result instanceof BagheeraClientRequestResult); - do_check_true(result.request instanceof RESTRequest); - do_check_true(result.transportSuccess); - do_check_true(result.serverSuccess); - - server.stop(run_next_test); - }, do_check_null); -}); - -add_test(function test_post_json_bad_data() { - let [client, server] = getClientAndServer(); - - server.createNamespace("foo"); - - client.uploadJSON("foo", "bar", "{this is invalid json}").then( - function onResult(result) { - do_check_true(result.transportSuccess); - do_check_false(result.serverSuccess); - - server.stop(run_next_test); - }); -}); - -add_task(function* test_unicode_payload() { - let [client, server] = getClientAndServer(); - server.createNamespace("foo"); - - const EXPECTED = "πόλλ' οἶδ' ἀλώπηξ, ἀλλ' ἐχῖνος ἓν μέγα"; - - let result = yield client.uploadJSON("foo", "bar", {test: EXPECTED}); - Assert.ok(result.transportSuccess); - Assert.ok(result.serverSuccess); - - let p = server.getDocument("foo", "bar"); - Assert.equal(p.test, EXPECTED); - - result = yield client.uploadJSON("foo", "baz", JSON.stringify({test: EXPECTED})); - Assert.ok(result.transportSuccess); - Assert.ok(result.serverSuccess); - p = server.getDocument("foo", "baz"); - Assert.equal(p.test, EXPECTED); - - let deferred = Promise.defer(); - server.stop(() => deferred.resolve()); - yield deferred.promise; -}); - -add_task(function test_post_delete_multiple_obsolete_documents () { - let [client, server] = getClientAndServer(); - let namespace = "foo"; - let documents = [ - [namespace, "one", "{v:1}"], - [namespace, "two", "{v:2}"], - [namespace, "three", "{v:3}"], - [namespace, "four", "{v:4}"], - ]; - - try { - // create initial documents - server.createNamespace(namespace); - for (let [ns, id, payload] of documents) { - server.setDocument(ns, id, payload); - do_check_true(server.hasDocument(ns, id)); - } - - // Test uploading with deleting some documents. - let deleteIDs = [0, 1].map((no) => { return documents[no][1]; }); - let result = yield client.uploadJSON(namespace, "new-1", {foo: "bar"}, {deleteIDs: deleteIDs}); - do_check_true(result.transportSuccess); - do_check_true(result.serverSuccess); - do_check_true(server.hasDocument(namespace, "new-1")); - for (let id of deleteIDs) { - do_check_false(server.hasDocument(namespace, id)); - } - // Check if the documents that were not staged for deletion are still there. - for (let [,id,] of documents) { - if (deleteIDs.indexOf(id) == -1) { - do_check_true(server.hasDocument(namespace, id)); - } - } - - // Test upload without deleting documents. - let ids = Object.keys(server.namespaces[namespace]); - result = yield client.uploadJSON(namespace, "new-2", {foo: "bar"}); - do_check_true(result.transportSuccess); - do_check_true(result.serverSuccess); - do_check_true(server.hasDocument(namespace, "new-2")); - // Check to see if all the original documents are still there. - for (let id of ids) { - do_check_true(deleteIDs.indexOf(id) !== -1 || server.hasDocument(namespace, id)); - } - } finally { - let deferred = Promise.defer(); - server.stop(deferred.resolve.bind(deferred)); - yield deferred.promise; - } -}); - -add_test(function test_delete_document() { - let [client, server] = getClientAndServer(); - - server.createNamespace("foo"); - server.setDocument("foo", "bar", "{}"); - - client.deleteDocument("foo", "bar").then(function onResult(result) { - do_check_true(result.transportSuccess); - do_check_true(result.serverSuccess); - - do_check_null(server.getDocument("foo", "bar")); - - server.stop(run_next_test); - }); -}); diff --git a/services/common/tests/unit/test_bagheera_server.js b/services/common/tests/unit/test_bagheera_server.js deleted file mode 100644 index 52d234b7c86..00000000000 --- a/services/common/tests/unit/test_bagheera_server.js +++ /dev/null @@ -1,30 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -Cu.import("resource://testing-common/services/common/bagheeraserver.js"); - -function run_test() { - run_next_test(); -} - -add_test(function test_server_empty() { - let server = new BagheeraServer(); - - do_check_false(server.hasNamespace("foo")); - do_check_false(server.hasDocument("foo", "bar")); - do_check_null(server.getDocument("foo", "bar")); - - server.createNamespace("foo"); - do_check_true(server.hasNamespace("foo")); - - run_next_test(); -}); - -add_test(function test_server_start() { - let server = new BagheeraServer(); - server.start(); - server.stop(run_next_test); -}); - diff --git a/services/common/tests/unit/test_load_modules.js b/services/common/tests/unit/test_load_modules.js index b549c93ef09..66ecf0734ac 100644 --- a/services/common/tests/unit/test_load_modules.js +++ b/services/common/tests/unit/test_load_modules.js @@ -16,10 +16,6 @@ const non_android_modules = [ "tokenserverclient.js", ]; -const non_android_healthreport_modules = [ - "bagheeraclient.js", -]; - const TEST_BASE = "resource://testing-common/services/common/"; const shared_test_modules = [ "logging.js", @@ -29,10 +25,6 @@ const non_android_test_modules = [ "storageserver.js", ]; -const non_android_healthreport_test_modules = [ - "bagheeraserver.js", -]; - function expectImportsToSucceed(mm, base=MODULE_BASE) { for (let m of mm) { let resource = base + m; @@ -70,14 +62,8 @@ function run_test() { if (AppConstants.platform != "android") { expectImportsToSucceed(non_android_modules); expectImportsToSucceed(non_android_test_modules, TEST_BASE); - if (AppConstants.MOZ_SERVICES_HEALTHREPORT) { - expectImportsToSucceed(non_android_healthreport_modules); - expectImportsToSucceed(non_android_healthreport_test_modules, TEST_BASE); - } } else { expectImportsToFail(non_android_modules); expectImportsToFail(non_android_test_modules, TEST_BASE); - expectImportsToFail(non_android_healthreport_modules); - expectImportsToFail(non_android_healthreport_test_modules, TEST_BASE); } } diff --git a/services/common/tests/unit/xpcshell.ini b/services/common/tests/unit/xpcshell.ini index 2cdbe79be36..3288abc7651 100644 --- a/services/common/tests/unit/xpcshell.ini +++ b/services/common/tests/unit/xpcshell.ini @@ -29,12 +29,6 @@ support-files = [test_async_chain.js] [test_async_querySpinningly.js] -[test_bagheera_server.js] -skip-if = (os == "android" || !healthreport) - -[test_bagheera_client.js] -skip-if = (os == "android" || !healthreport) - [test_hawkclient.js] skip-if = os == "android" [test_hawkrequest.js] From 2d798a512b25d2cb9e60782e80ca831434f29f29 Mon Sep 17 00:00:00 2001 From: Alessio Placitelli Date: Wed, 27 Jan 2016 01:03:00 +0100 Subject: [PATCH 03/28] Bug 1236580 - Remove the IS_UNIFIED_TELEMETRY constant and the related preferences. r=gfritzsche --- browser/app/profile/firefox.js | 2 - .../content/abouthealthreport/abouthealth.js | 10 +-- .../mozbase/mozprofile/mozprofile/profile.py | 3 - testing/profiles/prefs_general.js | 6 +- .../telemetry/TelemetryController.jsm | 78 +------------------ .../telemetry/TelemetryEnvironment.jsm | 1 - .../telemetry/healthreport-prefs.js | 1 - 7 files changed, 6 insertions(+), 95 deletions(-) diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index b6bfff53abe..585e1917c6f 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1537,8 +1537,6 @@ pref("browser.translation.engine", "bing"); // Telemetry settings. // Determines if Telemetry pings can be archived locally. pref("toolkit.telemetry.archive.enabled", true); -// Whether we enable opt-out Telemetry for a sample of the release population. -pref("toolkit.telemetry.optoutSample", true); // Telemetry experiments settings. pref("experiments.enabled", true); diff --git a/browser/base/content/abouthealthreport/abouthealth.js b/browser/base/content/abouthealthreport/abouthealth.js index f90f6742d5d..199f72738ce 100644 --- a/browser/base/content/abouthealthreport/abouthealth.js +++ b/browser/base/content/abouthealthreport/abouthealth.js @@ -12,11 +12,7 @@ Cu.import("resource://gre/modules/Services.jsm"); const prefs = new Preferences("datareporting.healthreport."); const PREF_UNIFIED = "toolkit.telemetry.unified"; -const PREF_UNIFIED_OPTIN = "toolkit.telemetry.unifiedIsOptIn"; - -// Whether v4 behavior is enabled, i.e. unified Telemetry features are on by default. -const IS_V4 = Preferences.get(PREF_UNIFIED, false) && - !Preferences.get(PREF_UNIFIED_OPTIN, false); +const PREF_REPORTING_URL = "datareporting.healthreport.about.reportUrl"; var healthReportWrapper = { init: function () { @@ -31,9 +27,7 @@ var healthReportWrapper = { }, _getReportURI: function () { - const pref = IS_V4 ? "datareporting.healthreport.about.reportUrl" - : "datareporting.healthreport.about.reportUrlUnified"; - let url = Services.urlFormatter.formatURLPref(pref); + let url = Services.urlFormatter.formatURLPref(PREF_REPORTING_URL); return Services.io.newURI(url, null, null); }, diff --git a/testing/mozbase/mozprofile/mozprofile/profile.py b/testing/mozbase/mozprofile/mozprofile/profile.py index 2dddebc5d31..4390b9b26b9 100644 --- a/testing/mozbase/mozprofile/mozprofile/profile.py +++ b/testing/mozbase/mozprofile/mozprofile/profile.py @@ -385,9 +385,6 @@ class FirefoxProfile(Profile): # Don't send Telemetry reports to the production server. This is # needed as Telemetry sends pings also if FHR upload is enabled. 'toolkit.telemetry.server' : 'http://%(server)s/telemetry-dummy/', - # Our current tests expect the unified Telemetry feature to be opt-out, - # which is not true while we hold back shipping it. - 'toolkit.telemetry.unifiedIsOptIn': True, } class MetroFirefoxProfile(Profile): diff --git a/testing/profiles/prefs_general.js b/testing/profiles/prefs_general.js index 52ed5e37745..6ef760008a5 100644 --- a/testing/profiles/prefs_general.js +++ b/testing/profiles/prefs_general.js @@ -145,8 +145,7 @@ user_pref("datareporting.policy.dataSubmissionPolicyBypassNotification", true); // Point Firefox Health Report at a local server. We don't care if it actually // works. It just can't hit the default production endpoint. user_pref("datareporting.healthreport.documentServerURI", "http://%(server)s/healthreport/"); -user_pref("datareporting.healthreport.about.reportUrl", "http://%(server)s/abouthealthreport/"); -user_pref("datareporting.healthreport.about.reportUrlUnified", "http://%(server)s/abouthealthreport/v4/"); +user_pref("datareporting.healthreport.about.reportUrl", "http://%(server)s/abouthealthreport/v4/"); // Make sure CSS error reporting is enabled for tests user_pref("layout.css.report_errors", true); @@ -233,9 +232,6 @@ user_pref('browser.contentHandlers.types.5.uri', 'http://test1.example.org/rss?u // We want to collect telemetry, but we don't want to send in the results. user_pref('toolkit.telemetry.server', 'https://%(server)s/telemetry-dummy/'); -// Our current tests expect the unified Telemetry feature to be opt-out, -// which is not true while we hold back shipping it. -user_pref('toolkit.telemetry.unifiedIsOptIn', false); // A couple of preferences with default values to test that telemetry preference // watching is working. diff --git a/toolkit/components/telemetry/TelemetryController.jsm b/toolkit/components/telemetry/TelemetryController.jsm index a408ec1acc0..2c0972368f9 100644 --- a/toolkit/components/telemetry/TelemetryController.jsm +++ b/toolkit/components/telemetry/TelemetryController.jsm @@ -39,15 +39,10 @@ const PREF_CACHED_CLIENTID = PREF_BRANCH + "cachedClientID"; const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled"; const PREF_SESSIONS_BRANCH = "datareporting.sessions."; const PREF_UNIFIED = PREF_BRANCH + "unified"; -const PREF_UNIFIED_OPTIN = PREF_BRANCH + "unifiedIsOptIn"; -const PREF_OPTOUT_SAMPLE = PREF_BRANCH + "optoutSample"; // Whether the FHR/Telemetry unification features are enabled. // Changing this pref requires a restart. const IS_UNIFIED_TELEMETRY = Preferences.get(PREF_UNIFIED, false); -// This preference allows to leave unified Telemetry behavior on only for people that -// opted into Telemetry. Changing this pref requires a restart. -const IS_UNIFIED_OPTIN = Preferences.get(PREF_UNIFIED_OPTIN, false); const PING_FORMAT_VERSION = 4; @@ -90,29 +85,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySend", XPCOMUtils.defineLazyModuleGetter(this, "TelemetryReportingPolicy", "resource://gre/modules/TelemetryReportingPolicy.jsm"); -XPCOMUtils.defineLazyGetter(this, "gCrcTable", function() { - let c; - let table = []; - for (let n = 0; n < 256; n++) { - c = n; - for (let k =0; k < 8; k++) { - c = ((c&1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); - } - table[n] = c; - } - return table; -}); - -function crc32(str) { - let crc = 0 ^ (-1); - - for (let i = 0; i < str.length; i++ ) { - crc = (crc >>> 8) ^ gCrcTable[(crc ^ str.charCodeAt(i)) & 0xFF]; - } - - return (crc ^ (-1)) >>> 0; -} - /** * Setup Telemetry logging. This function also gets called when loggin related * preferences change. @@ -153,7 +125,6 @@ var Policy = { now: () => new Date(), generatePingId: () => Utils.generateUUID(), getCachedClientID: () => ClientID.getCachedClientID(), - isUnifiedOptin: () => IS_UNIFIED_OPTIN, } this.EXPORTED_SYMBOLS = ["TelemetryController"]; @@ -327,15 +298,6 @@ this.TelemetryController = Object.freeze({ return Impl.clientID; }, - /** - * Whether this client is part of a sample that gets opt-out Telemetry. - * - * @return {Boolean} Whether the client is part of the opt-out sample. - */ - get isInOptoutSample() { - return Impl.isInOptoutSample; - }, - /** * The AsyncShutdown.Barrier to synchronize with TelemetryController shutdown. */ @@ -619,34 +581,6 @@ var Impl = { return TelemetryStorage.removeAbortedSessionPing(); }, - /** - * - */ - _isInOptoutSample: function() { - if (!Preferences.get(PREF_OPTOUT_SAMPLE, false)) { - this._log.config("_sampleForOptoutTelemetry - optout sampling is disabled"); - return false; - } - - const clientId = Policy.getCachedClientID(); - if (!clientId) { - this._log.config("_sampleForOptoutTelemetry - no cached client id available") - return false; - } - - // This mimics the server-side 1% sampling, so that we can get matching populations. - // The server samples on ((crc32(clientId) % 100) == 42), we match 42+X here to get - // a bigger sample. - const sample = crc32(clientId) % 100; - const offset = 42; - const range = 5; // sampling from 5% - - const optout = (sample >= offset && sample < (offset + range)); - this._log.config("_sampleForOptoutTelemetry - sampling for optout Telemetry - " + - "offset: " + offset + ", range: " + range + ", sample: " + sample); - return optout; - }, - /** * Perform telemetry initialization for either chrome or content process. * @return {Boolean} True if Telemetry is allowed to record at least base (FHR) data, @@ -664,12 +598,10 @@ var Impl = { } // Configure base Telemetry recording. - // Unified Telemetry makes it opt-out unless the unifedOptin pref is set. - // Additionally, we make Telemetry opt-out for a 5% sample. - // If extended Telemetry is enabled, base recording is always on as well. + // Unified Telemetry makes it opt-out. If extended Telemetry is enabled, base recording + // is always on as well. const enabled = Utils.isTelemetryEnabled; - const isOptout = IS_UNIFIED_TELEMETRY && (!Policy.isUnifiedOptin() || this._isInOptoutSample()); - Telemetry.canRecordBase = enabled || isOptout; + Telemetry.canRecordBase = enabled || IS_UNIFIED_TELEMETRY; Telemetry.canRecordExtended = enabled; this._log.config("enableTelemetryRecording - canRecordBase:" + Telemetry.canRecordBase + @@ -868,10 +800,6 @@ var Impl = { return this._clientID; }, - get isInOptoutSample() { - return this._isInOptoutSample(); - }, - /** * Get an object describing the current state of this module for AsyncShutdown diagnostics. */ diff --git a/toolkit/components/telemetry/TelemetryEnvironment.jsm b/toolkit/components/telemetry/TelemetryEnvironment.jsm index 0b1f07ad004..32da55af206 100644 --- a/toolkit/components/telemetry/TelemetryEnvironment.jsm +++ b/toolkit/components/telemetry/TelemetryEnvironment.jsm @@ -1097,7 +1097,6 @@ EnvironmentCache.prototype = { blocklistEnabled: Preferences.get(PREF_BLOCKLIST_ENABLED, true), e10sEnabled: Services.appinfo.browserTabsRemoteAutostart, telemetryEnabled: Utils.isTelemetryEnabled, - isInOptoutSample: TelemetryController.isInOptoutSample, locale: getBrowserLocale(), update: { channel: updateChannel, diff --git a/toolkit/components/telemetry/healthreport-prefs.js b/toolkit/components/telemetry/healthreport-prefs.js index 168cca0c5b6..021028e1caf 100644 --- a/toolkit/components/telemetry/healthreport-prefs.js +++ b/toolkit/components/telemetry/healthreport-prefs.js @@ -8,4 +8,3 @@ pref("datareporting.healthreport.infoURL", "https://www.mozilla.org/legal/privac pref("datareporting.healthreport.uploadEnabled", true); pref("datareporting.healthreport.about.reportUrl", "https://fhr.cdn.mozilla.net/%LOCALE%/v4/"); -pref("datareporting.healthreport.about.reportUrlUnified", "https://fhr.cdn.mozilla.net/%LOCALE%/v4/"); From f54dc2d4f5c1f10f426b97d0cb86f868db9ec2d1 Mon Sep 17 00:00:00 2001 From: Alessio Placitelli Date: Wed, 27 Jan 2016 07:09:00 +0100 Subject: [PATCH 04/28] Bug 1236580 - Fix the tests and update the documentation. r=gfritzsche --- .../test/general/browser_aboutHealthReport.js | 4 -- .../components/telemetry/docs/environment.rst | 1 - .../components/telemetry/docs/preferences.rst | 14 ----- .../components/telemetry/tests/unit/head.js | 5 -- .../tests/unit/test_TelemetryController.js | 59 ------------------- .../tests/unit/test_TelemetryEnvironment.js | 1 - 6 files changed, 84 deletions(-) diff --git a/browser/base/content/test/general/browser_aboutHealthReport.js b/browser/base/content/test/general/browser_aboutHealthReport.js index 33d1a1c5840..47f57d71689 100644 --- a/browser/base/content/test/general/browser_aboutHealthReport.js +++ b/browser/base/content/test/general/browser_aboutHealthReport.js @@ -14,7 +14,6 @@ const TELEMETRY_LOG_PREF = "toolkit.telemetry.log.level"; const telemetryOriginalLogPref = Preferences.get(TELEMETRY_LOG_PREF, null); const originalReportUrl = Services.prefs.getCharPref("datareporting.healthreport.about.reportUrl"); -const originalReportUrlUnified = Services.prefs.getCharPref("datareporting.healthreport.about.reportUrlUnified"); registerCleanupFunction(function() { // Ensure we don't pollute prefs for next tests. @@ -26,7 +25,6 @@ registerCleanupFunction(function() { try { Services.prefs.setCharPref("datareporting.healthreport.about.reportUrl", originalReportUrl); - Services.prefs.setCharPref("datareporting.healthreport.about.reportUrlUnified", originalReportUrlUnified); Services.prefs.setBoolPref("datareporting.healthreport.uploadEnabled", true); } catch (ex) {} }); @@ -69,8 +67,6 @@ var gTests = [ yield setupPingArchive(); Preferences.set("datareporting.healthreport.about.reportUrl", HTTPS_BASE + "healthreport_testRemoteCommands.html"); - Preferences.set("datareporting.healthreport.about.reportUrlUnified", - HTTPS_BASE + "healthreport_testRemoteCommands.html"); }), run: function (iframe) { diff --git a/toolkit/components/telemetry/docs/environment.rst b/toolkit/components/telemetry/docs/environment.rst index d09d4287570..441e68b2ab4 100644 --- a/toolkit/components/telemetry/docs/environment.rst +++ b/toolkit/components/telemetry/docs/environment.rst @@ -44,7 +44,6 @@ Structure:: searchCohort: , // optional, contains an identifier for any active search A/B experiments e10sEnabled: , // whether e10s is on, i.e. browser tabs open by default in a different process telemetryEnabled: , // false on failure - isInOptoutSample: , // whether this client is part of the opt-out sample locale: , // e.g. "it", null on failure update: { channel: , // e.g. "release", null on failure diff --git a/toolkit/components/telemetry/docs/preferences.rst b/toolkit/components/telemetry/docs/preferences.rst index 19b8fc3cdeb..177c61ce80e 100644 --- a/toolkit/components/telemetry/docs/preferences.rst +++ b/toolkit/components/telemetry/docs/preferences.rst @@ -19,16 +19,6 @@ Preferences * Telemetry is always enabled and recording *base* data. * Telemetry will send additional ``main`` pings. -``toolkit.telemetry.unifiedIsOptIn`` - - When true, we enable the Telemetry system only for people that opted into Telemetry, even if unified Telemetry is enabled. - Defaults to false & requires a restart. - -``toolkit.telemetry.optoutSample`` - - When true, we enable Telemetry opt-out for a sample of users when ``.unified`` and ``unifiedIsOptIn`` are on. - Defaults to false. - ``toolkit.telemetry.enabled`` If ``unified`` is off, this controls whether the Telemetry module is enabled. @@ -71,10 +61,6 @@ Data-choices notification This is the data submission master kill switch. If disabled, no policy is shown or upload takes place, ever. -``datareporting.policy.dataSubmissionEnabled.v2`` - - If disabled, FHR v2 data will not be sent to Mozilla servers. Telemetry v4 data submission will not be affected. This is like ``datareporting.policy.dataSubmissionEnabled``, but only affects FHR - Telemetry upload will not be disabled. - ``datareporting.policy.dataSubmissionPolicyNotifiedTime`` Records the date user was shown the policy. This preference is also used on Android. diff --git a/toolkit/components/telemetry/tests/unit/head.js b/toolkit/components/telemetry/tests/unit/head.js index baaa16be7ba..58dfdfe4457 100644 --- a/toolkit/components/telemetry/tests/unit/head.js +++ b/toolkit/components/telemetry/tests/unit/head.js @@ -284,11 +284,6 @@ function fakeCachedClientId(uuid) { module.Policy.getCachedClientID = () => uuid; } -function fakeIsUnifiedOptin(isOptin) { - let module = Cu.import("resource://gre/modules/TelemetryController.jsm"); - module.Policy.isUnifiedOptin = () => isOptin; -} - // Return a date that is |offset| ms in the future from |date|. function futureDate(date, offset) { return new Date(date.getTime() + offset); diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryController.js b/toolkit/components/telemetry/tests/unit/test_TelemetryController.js index 7746408efbc..fdf4d59e19c 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryController.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryController.js @@ -32,7 +32,6 @@ const PREF_ENABLED = PREF_BRANCH + "enabled"; const PREF_ARCHIVE_ENABLED = PREF_BRANCH + "archive.enabled"; const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled"; const PREF_UNIFIED = PREF_BRANCH + "unified"; -const PREF_OPTOUT_SAMPLE = PREF_BRANCH + "optoutSample"; var gClientID = null; @@ -373,64 +372,6 @@ add_task(function* test_changePingAfterSubmission() { "The payload must not be changed after being submitted."); }); -add_task(function* test_optoutSampling() { - if (!Preferences.get(PREF_UNIFIED, false)) { - dump("Unified Telemetry is disabled, skipping.\n"); - return; - } - - const DATA = [ - {uuid: null, sampled: false}, // not to be sampled - {uuid: "3d38d821-14a4-3d45-ab0b-02a9fb5a7505", sampled: false}, // samples to 0 - {uuid: "1331255e-7eb5-aa4f-b04e-494a0c6da282", sampled: false}, // samples to 41 - {uuid: "35393e78-a363-ea4e-9fc9-9f9abbee2077", sampled: true }, // samples to 42 - {uuid: "4dc81df6-db03-a34e-ba79-3e877afd22c4", sampled: true }, // samples to 43 - {uuid: "79e15be6-4884-8d4f-98e5-f94790251e5f", sampled: true }, // samples to 44 - {uuid: "c3841566-e39e-384d-826f-508ab6387b21", sampled: true }, // samples to 45 - {uuid: "cc7498a4-2cde-da47-89b3-f3ce5dd7c6fc", sampled: true }, // samples to 46 - {uuid: "0750d8ed-5969-3a4f-90ba-2e85f9074309", sampled: false}, // samples to 47 - {uuid: "0dfcbce7-d82b-b144-8d77-eb15935c9a8e", sampled: false}, // samples to 99 - ]; - - // Test that the opt-out pref enables us sampling on 5% of release. - Preferences.set(PREF_ENABLED, false); - Preferences.set(PREF_OPTOUT_SAMPLE, true); - fakeIsUnifiedOptin(true); - - for (let d of DATA) { - dump("Testing sampling for uuid: " + d.uuid + "\n"); - fakeCachedClientId(d.uuid); - yield TelemetryController.reset(); - Assert.equal(TelemetryController.isInOptoutSample, d.sampled, - "Opt-out sampling should behave as expected"); - Assert.equal(Telemetry.canRecordBase, d.sampled, - "Base recording setting should be correct"); - } - - // If we disable opt-out sampling Telemetry, have the opt-in setting on and extended Telemetry off, - // we should not enable anything. - Preferences.set(PREF_OPTOUT_SAMPLE, false); - fakeIsUnifiedOptin(true); - for (let d of DATA) { - dump("Testing sampling for uuid: " + d.uuid + "\n"); - fakeCachedClientId(d.uuid); - yield TelemetryController.reset(); - Assert.equal(Telemetry.canRecordBase, false, - "Sampling should not override the default opt-out behavior"); - } - - // If we fully enable opt-out Telemetry on release, the sampling should not override that. - Preferences.set(PREF_OPTOUT_SAMPLE, true); - fakeIsUnifiedOptin(false); - for (let d of DATA) { - dump("Testing sampling for uuid: " + d.uuid + "\n"); - fakeCachedClientId(d.uuid); - yield TelemetryController.reset(); - Assert.equal(Telemetry.canRecordBase, true, - "Sampling should not override the default opt-out behavior"); - } -}); - add_task(function* test_telemetryEnabledUnexpectedValue(){ // Remove the default value for toolkit.telemetry.enabled from the default prefs. // Otherwise, we wouldn't be able to set the pref to a string. diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js index 2b4e472a772..63ce2a8595e 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js @@ -338,7 +338,6 @@ function checkSettingsSection(data) { blocklistEnabled: "boolean", e10sEnabled: "boolean", telemetryEnabled: "boolean", - isInOptoutSample: "boolean", locale: "string", update: "object", userPrefs: "object", From a38e08e093011cfa29daecd7eb51b56fbe7ed11d Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 28 Jan 2016 09:36:00 +0100 Subject: [PATCH 05/28] Backed out changeset 1686b901daa4 (bug 1237983) for bustage --- services/common/bagheeraclient.js | 278 ++++++++++++++++ .../common/modules-testing/bagheeraserver.js | 296 ++++++++++++++++++ services/common/moz.build | 8 + services/common/tests/mach_commands.py | 6 + services/common/tests/run_bagheera_server.js | 26 ++ .../common/tests/unit/test_bagheera_client.js | 161 ++++++++++ .../common/tests/unit/test_bagheera_server.js | 30 ++ .../common/tests/unit/test_load_modules.js | 14 + services/common/tests/unit/xpcshell.ini | 6 + 9 files changed, 825 insertions(+) create mode 100644 services/common/bagheeraclient.js create mode 100644 services/common/modules-testing/bagheeraserver.js create mode 100644 services/common/tests/run_bagheera_server.js create mode 100644 services/common/tests/unit/test_bagheera_client.js create mode 100644 services/common/tests/unit/test_bagheera_server.js diff --git a/services/common/bagheeraclient.js b/services/common/bagheeraclient.js new file mode 100644 index 00000000000..dd41f65dcf9 --- /dev/null +++ b/services/common/bagheeraclient.js @@ -0,0 +1,278 @@ +/* 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/. */ + +/** + * This file contains a client API for the Bagheera data storage service. + * + * Information about Bagheera is available at + * https://github.com/mozilla-metrics/bagheera + */ + +#ifndef MERGED_COMPARTMENT + +"use strict"; + +this.EXPORTED_SYMBOLS = [ + "BagheeraClient", + "BagheeraClientRequestResult", +]; + +var {classes: Cc, interfaces: Ci, utils: Cu} = Components; + +#endif + +Cu.import("resource://gre/modules/Promise.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Log.jsm"); +Cu.import("resource://services-common/rest.js"); +Cu.import("resource://services-common/utils.js"); + +/** + * Represents the result of a Bagheera request. + */ +this.BagheeraClientRequestResult = function BagheeraClientRequestResult() { + this.transportSuccess = false; + this.serverSuccess = false; + this.request = null; +}; + +Object.freeze(BagheeraClientRequestResult.prototype); + + +/** + * Wrapper around RESTRequest so logging is sane. + */ +function BagheeraRequest(uri) { + RESTRequest.call(this, uri); + + this._log = Log.repository.getLogger("Services.BagheeraClient"); + this._log.level = Log.Level.Debug; +} + +BagheeraRequest.prototype = Object.freeze({ + __proto__: RESTRequest.prototype, +}); + + +/** + * Create a new Bagheera client instance. + * + * Each client is associated with a specific Bagheera HTTP URI endpoint. + * + * @param baseURI + * (string) The base URI of the Bagheera HTTP endpoint. + */ +this.BagheeraClient = function BagheeraClient(baseURI) { + if (!baseURI) { + throw new Error("baseURI argument must be defined."); + } + + this._log = Log.repository.getLogger("Services.BagheeraClient"); + this._log.level = Log.Level.Debug; + + this.baseURI = baseURI; + + if (!baseURI.endsWith("/")) { + this.baseURI += "/"; + } +}; + +BagheeraClient.prototype = Object.freeze({ + /** + * Channel load flags for all requests. + * + * Caching is not applicable, so we bypass and disable it. We also + * ignore any cookies that may be present for the domain because + * Bagheera does not utilize cookies and the release of cookies may + * inadvertantly constitute unncessary information disclosure. + */ + _loadFlags: Ci.nsIRequest.LOAD_BYPASS_CACHE | + Ci.nsIRequest.INHIBIT_CACHING | + Ci.nsIRequest.LOAD_ANONYMOUS, + + DEFAULT_TIMEOUT_MSEC: 5 * 60 * 1000, // 5 minutes. + + _RE_URI_IDENTIFIER: /^[a-zA-Z0-9_-]+$/, + + /** + * Upload a JSON payload to the server. + * + * The return value is a Promise which will be resolved with a + * BagheeraClientRequestResult when the request has finished. + * + * @param namespace + * (string) The namespace to post this data to. + * @param id + * (string) The ID of the document being uploaded. This is typically + * a UUID in hex form. + * @param payload + * (string|object) Data to upload. Can be specified as a string (which + * is assumed to be JSON) or an object. If an object, it will be fed into + * JSON.stringify() for serialization. + * @param options + * (object) Extra options to control behavior. Recognized properties: + * + * deleteIDs -- (array) Old document IDs to delete as part of + * upload. If not specified, no old documents will be deleted as + * part of upload. The array values are typically UUIDs in hex + * form. + * + * telemetryCompressed -- (string) Telemetry histogram to record + * compressed size of payload under. If not defined, no telemetry + * data for the compressed size will be recorded. + * + * @return Promise + */ + uploadJSON: function uploadJSON(namespace, id, payload, options={}) { + if (!namespace) { + throw new Error("namespace argument must be defined."); + } + + if (!id) { + throw new Error("id argument must be defined."); + } + + if (!payload) { + throw new Error("payload argument must be defined."); + } + + if (options && typeof(options) != "object") { + throw new Error("Unexpected type for options argument. Expected object. " + + "Got: " + typeof(options)); + } + + let uri = this._submitURI(namespace, id); + + let data = payload; + + if (typeof(payload) == "object") { + data = JSON.stringify(payload); + } + + if (typeof(data) != "string") { + throw new Error("Unknown type for payload: " + typeof(data)); + } + + this._log.info("Uploading data to " + uri); + + let request = new BagheeraRequest(uri); + request.loadFlags = this._loadFlags; + request.timeout = this.DEFAULT_TIMEOUT_MSEC; + + // Since API changed, throw on old API usage. + if ("deleteID" in options) { + throw new Error("API has changed, use (array) deleteIDs instead"); + } + + let deleteIDs; + if (options.deleteIDs && options.deleteIDs.length > 0) { + deleteIDs = options.deleteIDs; + this._log.debug("Will delete " + deleteIDs.join(", ")); + request.setHeader("X-Obsolete-Document", deleteIDs.join(",")); + } + + let deferred = Promise.defer(); + + // The string converter service used by CommonUtils.convertString() + // silently throws away high bytes. We need to convert the string to + // consist of only low bytes first. + data = CommonUtils.encodeUTF8(data); + data = CommonUtils.convertString(data, "uncompressed", "deflate"); + if (options.telemetryCompressed) { + try { + let h = Services.telemetry.getHistogramById(options.telemetryCompressed); + h.add(data.length); + } catch (ex) { + this._log.warn("Unable to record telemetry for compressed payload size", ex); + } + } + + // TODO proper header per bug 807134. + request.setHeader("Content-Type", "application/json+zlib; charset=utf-8"); + + this._log.info("Request body length: " + data.length); + + let result = new BagheeraClientRequestResult(); + result.namespace = namespace; + result.id = id; + result.deleteIDs = deleteIDs ? deleteIDs.slice(0) : null; + + request.onComplete = this._onComplete.bind(this, request, deferred, result); + request.post(data); + + return deferred.promise; + }, + + /** + * Delete the specified document. + * + * @param namespace + * (string) Namespace from which to delete the document. + * @param id + * (string) ID of document to delete. + * + * @return Promise + */ + deleteDocument: function deleteDocument(namespace, id) { + let uri = this._submitURI(namespace, id); + + let request = new BagheeraRequest(uri); + request.loadFlags = this._loadFlags; + request.timeout = this.DEFAULT_TIMEOUT_MSEC; + + let result = new BagheeraClientRequestResult(); + result.namespace = namespace; + result.id = id; + let deferred = Promise.defer(); + + request.onComplete = this._onComplete.bind(this, request, deferred, result); + request.delete(); + + return deferred.promise; + }, + + _submitURI: function _submitURI(namespace, id) { + if (!this._RE_URI_IDENTIFIER.test(namespace)) { + throw new Error("Illegal namespace name. Must be alphanumeric + [_-]: " + + namespace); + } + + if (!this._RE_URI_IDENTIFIER.test(id)) { + throw new Error("Illegal id value. Must be alphanumeric + [_-]: " + id); + } + + return this.baseURI + "1.0/submit/" + namespace + "/" + id; + }, + + _onComplete: function _onComplete(request, deferred, result, error) { + result.request = request; + + if (error) { + this._log.info("Transport failure on request", error); + result.transportSuccess = false; + deferred.resolve(result); + return; + } + + result.transportSuccess = true; + + let response = request.response; + + switch (response.status) { + case 200: + case 201: + result.serverSuccess = true; + break; + + default: + result.serverSuccess = false; + + this._log.info("Received unexpected status code: " + response.status); + this._log.debug("Response body: " + response.body); + } + + deferred.resolve(result); + }, +}); + diff --git a/services/common/modules-testing/bagheeraserver.js b/services/common/modules-testing/bagheeraserver.js new file mode 100644 index 00000000000..1e51b92a9f9 --- /dev/null +++ b/services/common/modules-testing/bagheeraserver.js @@ -0,0 +1,296 @@ +/* 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/. */ + +"use strict"; + +var {utils: Cu} = Components; + +this.EXPORTED_SYMBOLS = ["BagheeraServer"]; + +Cu.import("resource://gre/modules/Log.jsm"); +Cu.import("resource://services-common/utils.js"); +Cu.import("resource://testing-common/httpd.js"); + +/** + * This is an implementation of the Bagheera server. + * + * The purpose of the server is to facilitate testing of the Bagheera + * client and the Firefox Health report. It is *not* meant to be a + * production grade server. + * + * The Bagheera server is essentially a glorified document store. + */ +this.BagheeraServer = function BagheeraServer() { + this._log = Log.repository.getLogger("metrics.BagheeraServer"); + + this.server = new HttpServer(); + this.namespaces = {}; + + this.allowAllNamespaces = false; +} + +BagheeraServer.prototype = { + /** + * Whether this server has a namespace defined. + * + * @param ns + * (string) Namepsace whose existence to query for. + * @return bool + */ + hasNamespace: function hasNamespace(ns) { + return ns in this.namespaces; + }, + + /** + * Whether this server has an ID in a particular namespace. + * + * @param ns + * (string) Namespace to look for item in. + * @param id + * (string) ID of object to look for. + * @return bool + */ + hasDocument: function hasDocument(ns, id) { + let namespace = this.namespaces[ns]; + + if (!namespace) { + return false; + } + + return id in namespace; + }, + + /** + * Obtain a document from the server. + * + * @param ns + * (string) Namespace to retrieve document from. + * @param id + * (string) ID of document to retrieve. + * + * @return string The content of the document or null if the document + * does not exist. + */ + getDocument: function getDocument(ns, id) { + let namespace = this.namespaces[ns]; + + if (!namespace) { + return null; + } + + return namespace[id]; + }, + + /** + * Set the contents of a document in the server. + * + * @param ns + * (string) Namespace to add document to. + * @param id + * (string) ID of document being added. + * @param payload + * (string) The content of the document. + */ + setDocument: function setDocument(ns, id, payload) { + let namespace = this.namespaces[ns]; + + if (!namespace) { + if (!this.allowAllNamespaces) { + throw new Error("Namespace does not exist: " + ns); + } + + this.createNamespace(ns); + namespace = this.namespaces[ns]; + } + + namespace[id] = payload; + }, + + /** + * Create a namespace in the server. + * + * The namespace will initially be empty. + * + * @param ns + * (string) The name of the namespace to create. + */ + createNamespace: function createNamespace(ns) { + if (ns in this.namespaces) { + throw new Error("Namespace already exists: " + ns); + } + + this.namespaces[ns] = {}; + }, + + start: function start(port=-1) { + this.server.registerPrefixHandler("/", this._handleRequest.bind(this)); + this.server.start(port); + let i = this.server.identity; + + this.serverURI = i.primaryScheme + "://" + i.primaryHost + ":" + + i.primaryPort + "/"; + this.port = i.primaryPort; + }, + + stop: function stop(cb) { + let handler = {onStopped: cb}; + + this.server.stop(handler); + }, + + /** + * Our root path handler. + */ + _handleRequest: function _handleRequest(request, response) { + let path = request.path; + this._log.info("Received request: " + request.method + " " + path + " " + + "HTTP/" + request.httpVersion); + + try { + if (path.startsWith("/1.0/submit/")) { + return this._handleV1Submit(request, response, + path.substr("/1.0/submit/".length)); + } else { + throw HTTP_404; + } + } catch (ex) { + if (ex instanceof HttpError) { + this._log.info("HttpError thrown: " + ex.code + " " + ex.description); + } else { + this._log.warn("Exception processing request", ex); + } + + throw ex; + } + }, + + /** + * Handles requests to /submit/*. + */ + _handleV1Submit: function _handleV1Submit(request, response, rest) { + if (!rest.length) { + throw HTTP_404; + } + + let namespace; + let index = rest.indexOf("/"); + if (index == -1) { + namespace = rest; + rest = ""; + } else { + namespace = rest.substr(0, index); + rest = rest.substr(index + 1); + } + + this._handleNamespaceSubmit(namespace, rest, request, response); + }, + + _handleNamespaceSubmit: function _handleNamespaceSubmit(namespace, rest, + request, response) { + if (!this.hasNamespace(namespace)) { + if (!this.allowAllNamespaces) { + this._log.info("Request to unknown namespace: " + namespace); + throw HTTP_404; + } + + this.createNamespace(namespace); + } + + if (!rest) { + this._log.info("No ID defined."); + throw HTTP_404; + } + + let id = rest; + if (id.includes("/")) { + this._log.info("URI has too many components."); + throw HTTP_404; + } + + if (request.method == "POST") { + return this._handleNamespaceSubmitPost(namespace, id, request, response); + } + + if (request.method == "DELETE") { + return this._handleNamespaceSubmitDelete(namespace, id, request, response); + } + + this._log.info("Unsupported HTTP method on namespace handler: " + + request.method); + response.setHeader("Allow", "POST,DELETE"); + throw HTTP_405; + }, + + _handleNamespaceSubmitPost: + function _handleNamespaceSubmitPost(namespace, id, request, response) { + + this._log.info("Handling data upload for " + namespace + ":" + id); + + let requestBody = CommonUtils.readBytesFromInputStream(request.bodyInputStream); + this._log.info("Raw body length: " + requestBody.length); + + if (!request.hasHeader("Content-Type")) { + this._log.info("Request does not have Content-Type header."); + throw HTTP_400; + } + + const ALLOWED_TYPES = [ + // TODO proper content types from bug 807134. + "application/json; charset=utf-8", + "application/json+zlib; charset=utf-8", + ]; + + let ct = request.getHeader("Content-Type"); + if (ALLOWED_TYPES.indexOf(ct) == -1) { + this._log.info("Unknown media type: " + ct); + // Should generate proper HTTP response headers for this error. + throw HTTP_415; + } + + if (ct.startsWith("application/json+zlib")) { + this._log.debug("Uncompressing entity body with deflate."); + requestBody = CommonUtils.convertString(requestBody, "deflate", + "uncompressed"); + } + + requestBody = CommonUtils.decodeUTF8(requestBody); + + this._log.debug("HTTP request body: " + requestBody); + + let doc; + try { + doc = JSON.parse(requestBody); + } catch(ex) { + this._log.info("JSON parse error."); + throw HTTP_400; + } + + this.namespaces[namespace][id] = doc; + + if (request.hasHeader("X-Obsolete-Document")) { + let obsolete = request.getHeader("X-Obsolete-Document"); + this._log.info("Deleting from X-Obsolete-Document header: " + obsolete); + for (let obsolete_id of obsolete.split(",")) { + delete this.namespaces[namespace][obsolete_id]; + } + } + + response.setStatusLine(request.httpVersion, 201, "Created"); + response.setHeader("Content-Type", "text/plain"); + + let body = id; + response.bodyOutputStream.write(body, body.length); + }, + + _handleNamespaceSubmitDelete: + function _handleNamespaceSubmitDelete(namespace, id, request, response) { + + delete this.namespaces[namespace][id]; + + let body = id; + response.bodyOutputStream.write(body, body.length); + }, +}; + +Object.freeze(BagheeraServer.prototype); diff --git a/services/common/moz.build b/services/common/moz.build index 37e1aa64bdd..ec96079c346 100644 --- a/services/common/moz.build +++ b/services/common/moz.build @@ -31,6 +31,14 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android' or CONFIG['MOZ_B2GDROID']: 'modules-testing/storageserver.js', ] + if CONFIG['MOZ_SERVICES_HEALTHREPORT']: + EXTRA_PP_JS_MODULES['services-common'] += [ + 'bagheeraclient.js', + ] + TESTING_JS_MODULES.services.common += [ + 'modules-testing/bagheeraserver.js', + ] + EXTRA_PP_JS_MODULES['services-common'] += [ 'async.js', 'observers.js', diff --git a/services/common/tests/mach_commands.py b/services/common/tests/mach_commands.py index b57fa3aa26b..6777b81e4ce 100644 --- a/services/common/tests/mach_commands.py +++ b/services/common/tests/mach_commands.py @@ -34,6 +34,7 @@ DEFAULT_HOSTNAME = 'localhost' SRCDIR = mozpath.abspath(mozpath.dirname(__file__)) STORAGE_SERVER_SCRIPT = mozpath.join(SRCDIR, 'run_storage_server.js') +BAGHEERA_SERVER_SCRIPT = mozpath.join(SRCDIR, 'run_bagheera_server.js') def SyncStorageCommand(func): """Decorator that adds shared command arguments to services commands.""" @@ -109,3 +110,8 @@ class SyncTestCommands(MachCommandBase): @SyncStorageCommand def run_storage_server(self, port=DEFAULT_PORT, address=DEFAULT_HOSTNAME): exit(self.run_server(STORAGE_SERVER_SCRIPT, address, port)) + + @Command('bagheera-server', category='services', + description='Run a bagheera server.') + def run_bagheera_server(self, port=DEFAULT_PORT, address=DEFAULT_HOSTNAME): + exit(self.run_server(BAGHEERA_SERVER_SCRIPT, address, port)) diff --git a/services/common/tests/run_bagheera_server.js b/services/common/tests/run_bagheera_server.js new file mode 100644 index 00000000000..a97eb11eab6 --- /dev/null +++ b/services/common/tests/run_bagheera_server.js @@ -0,0 +1,26 @@ +/* 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/. */ + +/** + * This file runs a stub Bagheera server. + * + * It is meant to be executed with an xpcshell. + * + * The Makefile in this directory contains a target to run it: + * + * $ make bagheera-server + */ + +Cu.import("resource://testing-common/services/common/bagheeraserver.js"); + +initTestLogging(); + +var server = new BagheeraServer(); +server.allowAllNamespaces = true; +server.start(SERVER_PORT); +_("Bagheera server started on port " + SERVER_PORT); + +// Launch the thread manager. +_do_main(); + diff --git a/services/common/tests/unit/test_bagheera_client.js b/services/common/tests/unit/test_bagheera_client.js new file mode 100644 index 00000000000..2cd65f5c1d7 --- /dev/null +++ b/services/common/tests/unit/test_bagheera_client.js @@ -0,0 +1,161 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +Cu.import("resource://services-common/bagheeraclient.js"); +Cu.import("resource://services-common/rest.js"); +Cu.import("resource://testing-common/services/common/bagheeraserver.js"); +Cu.import("resource://gre/modules/Promise.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); + +function getClientAndServer() { + let server = new BagheeraServer(); + server.start(); + + let client = new BagheeraClient(server.serverURI); + + return [client, server]; +} + +function run_test() { + initTestLogging("Trace"); + run_next_test(); +} + +add_test(function test_constructor() { + let client = new BagheeraClient("http://localhost:1234/"); + + run_next_test(); +}); + +add_test(function test_post_json_transport_failure() { + let client = new BagheeraClient("http://localhost:1234/"); + + client.uploadJSON("foo", "bar", {}).then(function onResult(result) { + do_check_false(result.transportSuccess); + + run_next_test(); + }); +}); + +add_test(function test_post_json_simple() { + let [client, server] = getClientAndServer(); + + server.createNamespace("foo"); + let promise = client.uploadJSON("foo", "bar", {foo: "bar", biz: "baz"}); + + promise.then(function onSuccess(result) { + do_check_true(result instanceof BagheeraClientRequestResult); + do_check_true(result.request instanceof RESTRequest); + do_check_true(result.transportSuccess); + do_check_true(result.serverSuccess); + + server.stop(run_next_test); + }, do_check_null); +}); + +add_test(function test_post_json_bad_data() { + let [client, server] = getClientAndServer(); + + server.createNamespace("foo"); + + client.uploadJSON("foo", "bar", "{this is invalid json}").then( + function onResult(result) { + do_check_true(result.transportSuccess); + do_check_false(result.serverSuccess); + + server.stop(run_next_test); + }); +}); + +add_task(function* test_unicode_payload() { + let [client, server] = getClientAndServer(); + server.createNamespace("foo"); + + const EXPECTED = "πόλλ' οἶδ' ἀλώπηξ, ἀλλ' ἐχῖνος ἓν μέγα"; + + let result = yield client.uploadJSON("foo", "bar", {test: EXPECTED}); + Assert.ok(result.transportSuccess); + Assert.ok(result.serverSuccess); + + let p = server.getDocument("foo", "bar"); + Assert.equal(p.test, EXPECTED); + + result = yield client.uploadJSON("foo", "baz", JSON.stringify({test: EXPECTED})); + Assert.ok(result.transportSuccess); + Assert.ok(result.serverSuccess); + p = server.getDocument("foo", "baz"); + Assert.equal(p.test, EXPECTED); + + let deferred = Promise.defer(); + server.stop(() => deferred.resolve()); + yield deferred.promise; +}); + +add_task(function test_post_delete_multiple_obsolete_documents () { + let [client, server] = getClientAndServer(); + let namespace = "foo"; + let documents = [ + [namespace, "one", "{v:1}"], + [namespace, "two", "{v:2}"], + [namespace, "three", "{v:3}"], + [namespace, "four", "{v:4}"], + ]; + + try { + // create initial documents + server.createNamespace(namespace); + for (let [ns, id, payload] of documents) { + server.setDocument(ns, id, payload); + do_check_true(server.hasDocument(ns, id)); + } + + // Test uploading with deleting some documents. + let deleteIDs = [0, 1].map((no) => { return documents[no][1]; }); + let result = yield client.uploadJSON(namespace, "new-1", {foo: "bar"}, {deleteIDs: deleteIDs}); + do_check_true(result.transportSuccess); + do_check_true(result.serverSuccess); + do_check_true(server.hasDocument(namespace, "new-1")); + for (let id of deleteIDs) { + do_check_false(server.hasDocument(namespace, id)); + } + // Check if the documents that were not staged for deletion are still there. + for (let [,id,] of documents) { + if (deleteIDs.indexOf(id) == -1) { + do_check_true(server.hasDocument(namespace, id)); + } + } + + // Test upload without deleting documents. + let ids = Object.keys(server.namespaces[namespace]); + result = yield client.uploadJSON(namespace, "new-2", {foo: "bar"}); + do_check_true(result.transportSuccess); + do_check_true(result.serverSuccess); + do_check_true(server.hasDocument(namespace, "new-2")); + // Check to see if all the original documents are still there. + for (let id of ids) { + do_check_true(deleteIDs.indexOf(id) !== -1 || server.hasDocument(namespace, id)); + } + } finally { + let deferred = Promise.defer(); + server.stop(deferred.resolve.bind(deferred)); + yield deferred.promise; + } +}); + +add_test(function test_delete_document() { + let [client, server] = getClientAndServer(); + + server.createNamespace("foo"); + server.setDocument("foo", "bar", "{}"); + + client.deleteDocument("foo", "bar").then(function onResult(result) { + do_check_true(result.transportSuccess); + do_check_true(result.serverSuccess); + + do_check_null(server.getDocument("foo", "bar")); + + server.stop(run_next_test); + }); +}); diff --git a/services/common/tests/unit/test_bagheera_server.js b/services/common/tests/unit/test_bagheera_server.js new file mode 100644 index 00000000000..52d234b7c86 --- /dev/null +++ b/services/common/tests/unit/test_bagheera_server.js @@ -0,0 +1,30 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +Cu.import("resource://testing-common/services/common/bagheeraserver.js"); + +function run_test() { + run_next_test(); +} + +add_test(function test_server_empty() { + let server = new BagheeraServer(); + + do_check_false(server.hasNamespace("foo")); + do_check_false(server.hasDocument("foo", "bar")); + do_check_null(server.getDocument("foo", "bar")); + + server.createNamespace("foo"); + do_check_true(server.hasNamespace("foo")); + + run_next_test(); +}); + +add_test(function test_server_start() { + let server = new BagheeraServer(); + server.start(); + server.stop(run_next_test); +}); + diff --git a/services/common/tests/unit/test_load_modules.js b/services/common/tests/unit/test_load_modules.js index 66ecf0734ac..b549c93ef09 100644 --- a/services/common/tests/unit/test_load_modules.js +++ b/services/common/tests/unit/test_load_modules.js @@ -16,6 +16,10 @@ const non_android_modules = [ "tokenserverclient.js", ]; +const non_android_healthreport_modules = [ + "bagheeraclient.js", +]; + const TEST_BASE = "resource://testing-common/services/common/"; const shared_test_modules = [ "logging.js", @@ -25,6 +29,10 @@ const non_android_test_modules = [ "storageserver.js", ]; +const non_android_healthreport_test_modules = [ + "bagheeraserver.js", +]; + function expectImportsToSucceed(mm, base=MODULE_BASE) { for (let m of mm) { let resource = base + m; @@ -62,8 +70,14 @@ function run_test() { if (AppConstants.platform != "android") { expectImportsToSucceed(non_android_modules); expectImportsToSucceed(non_android_test_modules, TEST_BASE); + if (AppConstants.MOZ_SERVICES_HEALTHREPORT) { + expectImportsToSucceed(non_android_healthreport_modules); + expectImportsToSucceed(non_android_healthreport_test_modules, TEST_BASE); + } } else { expectImportsToFail(non_android_modules); expectImportsToFail(non_android_test_modules, TEST_BASE); + expectImportsToFail(non_android_healthreport_modules); + expectImportsToFail(non_android_healthreport_test_modules, TEST_BASE); } } diff --git a/services/common/tests/unit/xpcshell.ini b/services/common/tests/unit/xpcshell.ini index 3288abc7651..2cdbe79be36 100644 --- a/services/common/tests/unit/xpcshell.ini +++ b/services/common/tests/unit/xpcshell.ini @@ -29,6 +29,12 @@ support-files = [test_async_chain.js] [test_async_querySpinningly.js] +[test_bagheera_server.js] +skip-if = (os == "android" || !healthreport) + +[test_bagheera_client.js] +skip-if = (os == "android" || !healthreport) + [test_hawkclient.js] skip-if = os == "android" [test_hawkrequest.js] From 4418c2d7872342b568d986fb979199c9e82020c7 Mon Sep 17 00:00:00 2001 From: Jordan Santell Date: Wed, 27 Jan 2016 08:54:00 +0100 Subject: [PATCH 06/28] Bug 1204174 - Reduce test iterations to reduce intermittent timeouts. r=vp --- .../client/performance/test/browser_perf-overview-render-02.js | 3 ++- .../client/performance/test/browser_perf-overview-render-03.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/devtools/client/performance/test/browser_perf-overview-render-02.js b/devtools/client/performance/test/browser_perf-overview-render-02.js index 26a9426b38a..3670917dc7e 100644 --- a/devtools/client/performance/test/browser_perf-overview-render-02.js +++ b/devtools/client/performance/test/browser_perf-overview-render-02.js @@ -5,6 +5,7 @@ * Tests that the overview graphs cannot be selected during recording * and that they're cleared upon rerecording. */ +const TIMES_TO_UPDATE = 2; function* spawnTest() { // This test seems to take a long time to cleanup on Ubuntu VMs. requestLongerTimeout(2); @@ -51,7 +52,7 @@ function* spawnTest() { let updated = 0; OverviewView.on(EVENTS.OVERVIEW_RENDERED, () => updated++); - ok((yield waitUntil(() => updated > 10)), + ok((yield waitUntil(() => updated > TIMES_TO_UPDATE)), "The overviews were updated several times."); ok("selectionEnabled" in framerate, diff --git a/devtools/client/performance/test/browser_perf-overview-render-03.js b/devtools/client/performance/test/browser_perf-overview-render-03.js index a203af705ec..8fc50ef291c 100644 --- a/devtools/client/performance/test/browser_perf-overview-render-03.js +++ b/devtools/client/performance/test/browser_perf-overview-render-03.js @@ -4,6 +4,7 @@ /** * Tests that the overview graphs share the exact same width and scaling. */ +const TIMES_TO_UPDATE = 2; function* spawnTest() { // This test seems to take a long time to cleanup on Ubuntu VMs. requestLongerTimeout(2); @@ -23,7 +24,7 @@ function* spawnTest() { yield waitUntil(() => PerformanceController.getCurrentRecording().getMarkers().length); yield waitUntil(() => PerformanceController.getCurrentRecording().getMemory().length); yield waitUntil(() => PerformanceController.getCurrentRecording().getTicks().length); - yield waitUntil(() => updated > 10); + yield waitUntil(() => updated > TIMES_TO_UPDATE); yield stopRecording(panel); From 6624598d3155f2bb8ce919baf8022ff5ebba1530 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Wed, 27 Jan 2016 14:27:15 -0600 Subject: [PATCH 07/28] Bug 1229763 - Update a11y + e10s support url. r=mconley --- browser/app/profile/firefox.js | 3 +++ browser/components/nsBrowserGlue.js | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 585e1917c6f..2315fdbaae4 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1119,6 +1119,9 @@ pref("toolkit.crashreporter.infoURL", // base URL for web-based support pages pref("app.support.baseURL", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/"); +// a11y conflicts with e10s support page +pref("app.support.e10sAccessibilityUrl", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/accessibility-ppt"); + // base url for web-based feedback pages #ifdef MOZ_DEV_EDITION pref("app.feedback.baseURL", "https://input.mozilla.org/%LOCALE%/feedback/firefoxdev/%VERSION%/"); diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index 4e7201e63b1..4fe37c617ae 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -3215,7 +3215,7 @@ var E10SAccessibilityCheck = { }]; let options = { popupIconURL: "chrome://browser/skin/e10s-64@2x.png", - learnMoreURL: "https://support.mozilla.org/kb/accessibility-and-ppt", + learnMoreURL: Services.urlFormatter.formatURLPref("app.support.e10sAccessibilityUrl"), persistWhileVisible: true, hideNotNow: true, }; From 3db979ff61967dc5243c0ad48589a42044a7968f Mon Sep 17 00:00:00 2001 From: Patrick Brosset Date: Thu, 28 Jan 2016 10:33:27 +0100 Subject: [PATCH 08/28] Bug 1243682 - Register the new mdn gcli command in moz.build; r=me --- devtools/shared/gcli/commands/moz.build | 1 + 1 file changed, 1 insertion(+) diff --git a/devtools/shared/gcli/commands/moz.build b/devtools/shared/gcli/commands/moz.build index 4cc8c103774..eb3fb38a8ed 100644 --- a/devtools/shared/gcli/commands/moz.build +++ b/devtools/shared/gcli/commands/moz.build @@ -17,6 +17,7 @@ DevToolsModules( 'inject.js', 'jsb.js', 'listen.js', + 'mdn.js', 'measure.js', 'media.js', 'pagemod.js', From 3323f8749602d76e2c3ac7202cacc012a54dc6ac Mon Sep 17 00:00:00 2001 From: brendan Date: Sun, 24 Jan 2016 22:33:52 -0500 Subject: [PATCH 09/28] Bug 1237983 - Investigate and remove the Bagheera Client Implementation. r=gfritzsche --- CLOBBER | 2 +- services/common/bagheeraclient.js | 278 ---------------- .../common/modules-testing/bagheeraserver.js | 296 ------------------ services/common/moz.build | 8 - services/common/tests/mach_commands.py | 6 - services/common/tests/run_bagheera_server.js | 26 -- .../common/tests/unit/test_bagheera_client.js | 161 ---------- .../common/tests/unit/test_bagheera_server.js | 30 -- .../common/tests/unit/test_load_modules.js | 14 - services/common/tests/unit/xpcshell.ini | 6 - 10 files changed, 1 insertion(+), 826 deletions(-) delete mode 100644 services/common/bagheeraclient.js delete mode 100644 services/common/modules-testing/bagheeraserver.js delete mode 100644 services/common/tests/run_bagheera_server.js delete mode 100644 services/common/tests/unit/test_bagheera_client.js delete mode 100644 services/common/tests/unit/test_bagheera_server.js diff --git a/CLOBBER b/CLOBBER index 6cfd8022318..b78ee44b58a 100644 --- a/CLOBBER +++ b/CLOBBER @@ -22,5 +22,5 @@ # changes to stick? As of bug 928195, this shouldn't be necessary! Please # don't change CLOBBER for WebIDL changes any more. -to fix another bustage from bug 1069556 landing; +Bug 1237983 - Investigate and remove the Bagheera Client implementation. diff --git a/services/common/bagheeraclient.js b/services/common/bagheeraclient.js deleted file mode 100644 index dd41f65dcf9..00000000000 --- a/services/common/bagheeraclient.js +++ /dev/null @@ -1,278 +0,0 @@ -/* 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/. */ - -/** - * This file contains a client API for the Bagheera data storage service. - * - * Information about Bagheera is available at - * https://github.com/mozilla-metrics/bagheera - */ - -#ifndef MERGED_COMPARTMENT - -"use strict"; - -this.EXPORTED_SYMBOLS = [ - "BagheeraClient", - "BagheeraClientRequestResult", -]; - -var {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -#endif - -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://services-common/rest.js"); -Cu.import("resource://services-common/utils.js"); - -/** - * Represents the result of a Bagheera request. - */ -this.BagheeraClientRequestResult = function BagheeraClientRequestResult() { - this.transportSuccess = false; - this.serverSuccess = false; - this.request = null; -}; - -Object.freeze(BagheeraClientRequestResult.prototype); - - -/** - * Wrapper around RESTRequest so logging is sane. - */ -function BagheeraRequest(uri) { - RESTRequest.call(this, uri); - - this._log = Log.repository.getLogger("Services.BagheeraClient"); - this._log.level = Log.Level.Debug; -} - -BagheeraRequest.prototype = Object.freeze({ - __proto__: RESTRequest.prototype, -}); - - -/** - * Create a new Bagheera client instance. - * - * Each client is associated with a specific Bagheera HTTP URI endpoint. - * - * @param baseURI - * (string) The base URI of the Bagheera HTTP endpoint. - */ -this.BagheeraClient = function BagheeraClient(baseURI) { - if (!baseURI) { - throw new Error("baseURI argument must be defined."); - } - - this._log = Log.repository.getLogger("Services.BagheeraClient"); - this._log.level = Log.Level.Debug; - - this.baseURI = baseURI; - - if (!baseURI.endsWith("/")) { - this.baseURI += "/"; - } -}; - -BagheeraClient.prototype = Object.freeze({ - /** - * Channel load flags for all requests. - * - * Caching is not applicable, so we bypass and disable it. We also - * ignore any cookies that may be present for the domain because - * Bagheera does not utilize cookies and the release of cookies may - * inadvertantly constitute unncessary information disclosure. - */ - _loadFlags: Ci.nsIRequest.LOAD_BYPASS_CACHE | - Ci.nsIRequest.INHIBIT_CACHING | - Ci.nsIRequest.LOAD_ANONYMOUS, - - DEFAULT_TIMEOUT_MSEC: 5 * 60 * 1000, // 5 minutes. - - _RE_URI_IDENTIFIER: /^[a-zA-Z0-9_-]+$/, - - /** - * Upload a JSON payload to the server. - * - * The return value is a Promise which will be resolved with a - * BagheeraClientRequestResult when the request has finished. - * - * @param namespace - * (string) The namespace to post this data to. - * @param id - * (string) The ID of the document being uploaded. This is typically - * a UUID in hex form. - * @param payload - * (string|object) Data to upload. Can be specified as a string (which - * is assumed to be JSON) or an object. If an object, it will be fed into - * JSON.stringify() for serialization. - * @param options - * (object) Extra options to control behavior. Recognized properties: - * - * deleteIDs -- (array) Old document IDs to delete as part of - * upload. If not specified, no old documents will be deleted as - * part of upload. The array values are typically UUIDs in hex - * form. - * - * telemetryCompressed -- (string) Telemetry histogram to record - * compressed size of payload under. If not defined, no telemetry - * data for the compressed size will be recorded. - * - * @return Promise - */ - uploadJSON: function uploadJSON(namespace, id, payload, options={}) { - if (!namespace) { - throw new Error("namespace argument must be defined."); - } - - if (!id) { - throw new Error("id argument must be defined."); - } - - if (!payload) { - throw new Error("payload argument must be defined."); - } - - if (options && typeof(options) != "object") { - throw new Error("Unexpected type for options argument. Expected object. " + - "Got: " + typeof(options)); - } - - let uri = this._submitURI(namespace, id); - - let data = payload; - - if (typeof(payload) == "object") { - data = JSON.stringify(payload); - } - - if (typeof(data) != "string") { - throw new Error("Unknown type for payload: " + typeof(data)); - } - - this._log.info("Uploading data to " + uri); - - let request = new BagheeraRequest(uri); - request.loadFlags = this._loadFlags; - request.timeout = this.DEFAULT_TIMEOUT_MSEC; - - // Since API changed, throw on old API usage. - if ("deleteID" in options) { - throw new Error("API has changed, use (array) deleteIDs instead"); - } - - let deleteIDs; - if (options.deleteIDs && options.deleteIDs.length > 0) { - deleteIDs = options.deleteIDs; - this._log.debug("Will delete " + deleteIDs.join(", ")); - request.setHeader("X-Obsolete-Document", deleteIDs.join(",")); - } - - let deferred = Promise.defer(); - - // The string converter service used by CommonUtils.convertString() - // silently throws away high bytes. We need to convert the string to - // consist of only low bytes first. - data = CommonUtils.encodeUTF8(data); - data = CommonUtils.convertString(data, "uncompressed", "deflate"); - if (options.telemetryCompressed) { - try { - let h = Services.telemetry.getHistogramById(options.telemetryCompressed); - h.add(data.length); - } catch (ex) { - this._log.warn("Unable to record telemetry for compressed payload size", ex); - } - } - - // TODO proper header per bug 807134. - request.setHeader("Content-Type", "application/json+zlib; charset=utf-8"); - - this._log.info("Request body length: " + data.length); - - let result = new BagheeraClientRequestResult(); - result.namespace = namespace; - result.id = id; - result.deleteIDs = deleteIDs ? deleteIDs.slice(0) : null; - - request.onComplete = this._onComplete.bind(this, request, deferred, result); - request.post(data); - - return deferred.promise; - }, - - /** - * Delete the specified document. - * - * @param namespace - * (string) Namespace from which to delete the document. - * @param id - * (string) ID of document to delete. - * - * @return Promise - */ - deleteDocument: function deleteDocument(namespace, id) { - let uri = this._submitURI(namespace, id); - - let request = new BagheeraRequest(uri); - request.loadFlags = this._loadFlags; - request.timeout = this.DEFAULT_TIMEOUT_MSEC; - - let result = new BagheeraClientRequestResult(); - result.namespace = namespace; - result.id = id; - let deferred = Promise.defer(); - - request.onComplete = this._onComplete.bind(this, request, deferred, result); - request.delete(); - - return deferred.promise; - }, - - _submitURI: function _submitURI(namespace, id) { - if (!this._RE_URI_IDENTIFIER.test(namespace)) { - throw new Error("Illegal namespace name. Must be alphanumeric + [_-]: " + - namespace); - } - - if (!this._RE_URI_IDENTIFIER.test(id)) { - throw new Error("Illegal id value. Must be alphanumeric + [_-]: " + id); - } - - return this.baseURI + "1.0/submit/" + namespace + "/" + id; - }, - - _onComplete: function _onComplete(request, deferred, result, error) { - result.request = request; - - if (error) { - this._log.info("Transport failure on request", error); - result.transportSuccess = false; - deferred.resolve(result); - return; - } - - result.transportSuccess = true; - - let response = request.response; - - switch (response.status) { - case 200: - case 201: - result.serverSuccess = true; - break; - - default: - result.serverSuccess = false; - - this._log.info("Received unexpected status code: " + response.status); - this._log.debug("Response body: " + response.body); - } - - deferred.resolve(result); - }, -}); - diff --git a/services/common/modules-testing/bagheeraserver.js b/services/common/modules-testing/bagheeraserver.js deleted file mode 100644 index 1e51b92a9f9..00000000000 --- a/services/common/modules-testing/bagheeraserver.js +++ /dev/null @@ -1,296 +0,0 @@ -/* 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/. */ - -"use strict"; - -var {utils: Cu} = Components; - -this.EXPORTED_SYMBOLS = ["BagheeraServer"]; - -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://services-common/utils.js"); -Cu.import("resource://testing-common/httpd.js"); - -/** - * This is an implementation of the Bagheera server. - * - * The purpose of the server is to facilitate testing of the Bagheera - * client and the Firefox Health report. It is *not* meant to be a - * production grade server. - * - * The Bagheera server is essentially a glorified document store. - */ -this.BagheeraServer = function BagheeraServer() { - this._log = Log.repository.getLogger("metrics.BagheeraServer"); - - this.server = new HttpServer(); - this.namespaces = {}; - - this.allowAllNamespaces = false; -} - -BagheeraServer.prototype = { - /** - * Whether this server has a namespace defined. - * - * @param ns - * (string) Namepsace whose existence to query for. - * @return bool - */ - hasNamespace: function hasNamespace(ns) { - return ns in this.namespaces; - }, - - /** - * Whether this server has an ID in a particular namespace. - * - * @param ns - * (string) Namespace to look for item in. - * @param id - * (string) ID of object to look for. - * @return bool - */ - hasDocument: function hasDocument(ns, id) { - let namespace = this.namespaces[ns]; - - if (!namespace) { - return false; - } - - return id in namespace; - }, - - /** - * Obtain a document from the server. - * - * @param ns - * (string) Namespace to retrieve document from. - * @param id - * (string) ID of document to retrieve. - * - * @return string The content of the document or null if the document - * does not exist. - */ - getDocument: function getDocument(ns, id) { - let namespace = this.namespaces[ns]; - - if (!namespace) { - return null; - } - - return namespace[id]; - }, - - /** - * Set the contents of a document in the server. - * - * @param ns - * (string) Namespace to add document to. - * @param id - * (string) ID of document being added. - * @param payload - * (string) The content of the document. - */ - setDocument: function setDocument(ns, id, payload) { - let namespace = this.namespaces[ns]; - - if (!namespace) { - if (!this.allowAllNamespaces) { - throw new Error("Namespace does not exist: " + ns); - } - - this.createNamespace(ns); - namespace = this.namespaces[ns]; - } - - namespace[id] = payload; - }, - - /** - * Create a namespace in the server. - * - * The namespace will initially be empty. - * - * @param ns - * (string) The name of the namespace to create. - */ - createNamespace: function createNamespace(ns) { - if (ns in this.namespaces) { - throw new Error("Namespace already exists: " + ns); - } - - this.namespaces[ns] = {}; - }, - - start: function start(port=-1) { - this.server.registerPrefixHandler("/", this._handleRequest.bind(this)); - this.server.start(port); - let i = this.server.identity; - - this.serverURI = i.primaryScheme + "://" + i.primaryHost + ":" + - i.primaryPort + "/"; - this.port = i.primaryPort; - }, - - stop: function stop(cb) { - let handler = {onStopped: cb}; - - this.server.stop(handler); - }, - - /** - * Our root path handler. - */ - _handleRequest: function _handleRequest(request, response) { - let path = request.path; - this._log.info("Received request: " + request.method + " " + path + " " + - "HTTP/" + request.httpVersion); - - try { - if (path.startsWith("/1.0/submit/")) { - return this._handleV1Submit(request, response, - path.substr("/1.0/submit/".length)); - } else { - throw HTTP_404; - } - } catch (ex) { - if (ex instanceof HttpError) { - this._log.info("HttpError thrown: " + ex.code + " " + ex.description); - } else { - this._log.warn("Exception processing request", ex); - } - - throw ex; - } - }, - - /** - * Handles requests to /submit/*. - */ - _handleV1Submit: function _handleV1Submit(request, response, rest) { - if (!rest.length) { - throw HTTP_404; - } - - let namespace; - let index = rest.indexOf("/"); - if (index == -1) { - namespace = rest; - rest = ""; - } else { - namespace = rest.substr(0, index); - rest = rest.substr(index + 1); - } - - this._handleNamespaceSubmit(namespace, rest, request, response); - }, - - _handleNamespaceSubmit: function _handleNamespaceSubmit(namespace, rest, - request, response) { - if (!this.hasNamespace(namespace)) { - if (!this.allowAllNamespaces) { - this._log.info("Request to unknown namespace: " + namespace); - throw HTTP_404; - } - - this.createNamespace(namespace); - } - - if (!rest) { - this._log.info("No ID defined."); - throw HTTP_404; - } - - let id = rest; - if (id.includes("/")) { - this._log.info("URI has too many components."); - throw HTTP_404; - } - - if (request.method == "POST") { - return this._handleNamespaceSubmitPost(namespace, id, request, response); - } - - if (request.method == "DELETE") { - return this._handleNamespaceSubmitDelete(namespace, id, request, response); - } - - this._log.info("Unsupported HTTP method on namespace handler: " + - request.method); - response.setHeader("Allow", "POST,DELETE"); - throw HTTP_405; - }, - - _handleNamespaceSubmitPost: - function _handleNamespaceSubmitPost(namespace, id, request, response) { - - this._log.info("Handling data upload for " + namespace + ":" + id); - - let requestBody = CommonUtils.readBytesFromInputStream(request.bodyInputStream); - this._log.info("Raw body length: " + requestBody.length); - - if (!request.hasHeader("Content-Type")) { - this._log.info("Request does not have Content-Type header."); - throw HTTP_400; - } - - const ALLOWED_TYPES = [ - // TODO proper content types from bug 807134. - "application/json; charset=utf-8", - "application/json+zlib; charset=utf-8", - ]; - - let ct = request.getHeader("Content-Type"); - if (ALLOWED_TYPES.indexOf(ct) == -1) { - this._log.info("Unknown media type: " + ct); - // Should generate proper HTTP response headers for this error. - throw HTTP_415; - } - - if (ct.startsWith("application/json+zlib")) { - this._log.debug("Uncompressing entity body with deflate."); - requestBody = CommonUtils.convertString(requestBody, "deflate", - "uncompressed"); - } - - requestBody = CommonUtils.decodeUTF8(requestBody); - - this._log.debug("HTTP request body: " + requestBody); - - let doc; - try { - doc = JSON.parse(requestBody); - } catch(ex) { - this._log.info("JSON parse error."); - throw HTTP_400; - } - - this.namespaces[namespace][id] = doc; - - if (request.hasHeader("X-Obsolete-Document")) { - let obsolete = request.getHeader("X-Obsolete-Document"); - this._log.info("Deleting from X-Obsolete-Document header: " + obsolete); - for (let obsolete_id of obsolete.split(",")) { - delete this.namespaces[namespace][obsolete_id]; - } - } - - response.setStatusLine(request.httpVersion, 201, "Created"); - response.setHeader("Content-Type", "text/plain"); - - let body = id; - response.bodyOutputStream.write(body, body.length); - }, - - _handleNamespaceSubmitDelete: - function _handleNamespaceSubmitDelete(namespace, id, request, response) { - - delete this.namespaces[namespace][id]; - - let body = id; - response.bodyOutputStream.write(body, body.length); - }, -}; - -Object.freeze(BagheeraServer.prototype); diff --git a/services/common/moz.build b/services/common/moz.build index ec96079c346..37e1aa64bdd 100644 --- a/services/common/moz.build +++ b/services/common/moz.build @@ -31,14 +31,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android' or CONFIG['MOZ_B2GDROID']: 'modules-testing/storageserver.js', ] - if CONFIG['MOZ_SERVICES_HEALTHREPORT']: - EXTRA_PP_JS_MODULES['services-common'] += [ - 'bagheeraclient.js', - ] - TESTING_JS_MODULES.services.common += [ - 'modules-testing/bagheeraserver.js', - ] - EXTRA_PP_JS_MODULES['services-common'] += [ 'async.js', 'observers.js', diff --git a/services/common/tests/mach_commands.py b/services/common/tests/mach_commands.py index 6777b81e4ce..b57fa3aa26b 100644 --- a/services/common/tests/mach_commands.py +++ b/services/common/tests/mach_commands.py @@ -34,7 +34,6 @@ DEFAULT_HOSTNAME = 'localhost' SRCDIR = mozpath.abspath(mozpath.dirname(__file__)) STORAGE_SERVER_SCRIPT = mozpath.join(SRCDIR, 'run_storage_server.js') -BAGHEERA_SERVER_SCRIPT = mozpath.join(SRCDIR, 'run_bagheera_server.js') def SyncStorageCommand(func): """Decorator that adds shared command arguments to services commands.""" @@ -110,8 +109,3 @@ class SyncTestCommands(MachCommandBase): @SyncStorageCommand def run_storage_server(self, port=DEFAULT_PORT, address=DEFAULT_HOSTNAME): exit(self.run_server(STORAGE_SERVER_SCRIPT, address, port)) - - @Command('bagheera-server', category='services', - description='Run a bagheera server.') - def run_bagheera_server(self, port=DEFAULT_PORT, address=DEFAULT_HOSTNAME): - exit(self.run_server(BAGHEERA_SERVER_SCRIPT, address, port)) diff --git a/services/common/tests/run_bagheera_server.js b/services/common/tests/run_bagheera_server.js deleted file mode 100644 index a97eb11eab6..00000000000 --- a/services/common/tests/run_bagheera_server.js +++ /dev/null @@ -1,26 +0,0 @@ -/* 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/. */ - -/** - * This file runs a stub Bagheera server. - * - * It is meant to be executed with an xpcshell. - * - * The Makefile in this directory contains a target to run it: - * - * $ make bagheera-server - */ - -Cu.import("resource://testing-common/services/common/bagheeraserver.js"); - -initTestLogging(); - -var server = new BagheeraServer(); -server.allowAllNamespaces = true; -server.start(SERVER_PORT); -_("Bagheera server started on port " + SERVER_PORT); - -// Launch the thread manager. -_do_main(); - diff --git a/services/common/tests/unit/test_bagheera_client.js b/services/common/tests/unit/test_bagheera_client.js deleted file mode 100644 index 2cd65f5c1d7..00000000000 --- a/services/common/tests/unit/test_bagheera_client.js +++ /dev/null @@ -1,161 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -Cu.import("resource://services-common/bagheeraclient.js"); -Cu.import("resource://services-common/rest.js"); -Cu.import("resource://testing-common/services/common/bagheeraserver.js"); -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); - -function getClientAndServer() { - let server = new BagheeraServer(); - server.start(); - - let client = new BagheeraClient(server.serverURI); - - return [client, server]; -} - -function run_test() { - initTestLogging("Trace"); - run_next_test(); -} - -add_test(function test_constructor() { - let client = new BagheeraClient("http://localhost:1234/"); - - run_next_test(); -}); - -add_test(function test_post_json_transport_failure() { - let client = new BagheeraClient("http://localhost:1234/"); - - client.uploadJSON("foo", "bar", {}).then(function onResult(result) { - do_check_false(result.transportSuccess); - - run_next_test(); - }); -}); - -add_test(function test_post_json_simple() { - let [client, server] = getClientAndServer(); - - server.createNamespace("foo"); - let promise = client.uploadJSON("foo", "bar", {foo: "bar", biz: "baz"}); - - promise.then(function onSuccess(result) { - do_check_true(result instanceof BagheeraClientRequestResult); - do_check_true(result.request instanceof RESTRequest); - do_check_true(result.transportSuccess); - do_check_true(result.serverSuccess); - - server.stop(run_next_test); - }, do_check_null); -}); - -add_test(function test_post_json_bad_data() { - let [client, server] = getClientAndServer(); - - server.createNamespace("foo"); - - client.uploadJSON("foo", "bar", "{this is invalid json}").then( - function onResult(result) { - do_check_true(result.transportSuccess); - do_check_false(result.serverSuccess); - - server.stop(run_next_test); - }); -}); - -add_task(function* test_unicode_payload() { - let [client, server] = getClientAndServer(); - server.createNamespace("foo"); - - const EXPECTED = "πόλλ' οἶδ' ἀλώπηξ, ἀλλ' ἐχῖνος ἓν μέγα"; - - let result = yield client.uploadJSON("foo", "bar", {test: EXPECTED}); - Assert.ok(result.transportSuccess); - Assert.ok(result.serverSuccess); - - let p = server.getDocument("foo", "bar"); - Assert.equal(p.test, EXPECTED); - - result = yield client.uploadJSON("foo", "baz", JSON.stringify({test: EXPECTED})); - Assert.ok(result.transportSuccess); - Assert.ok(result.serverSuccess); - p = server.getDocument("foo", "baz"); - Assert.equal(p.test, EXPECTED); - - let deferred = Promise.defer(); - server.stop(() => deferred.resolve()); - yield deferred.promise; -}); - -add_task(function test_post_delete_multiple_obsolete_documents () { - let [client, server] = getClientAndServer(); - let namespace = "foo"; - let documents = [ - [namespace, "one", "{v:1}"], - [namespace, "two", "{v:2}"], - [namespace, "three", "{v:3}"], - [namespace, "four", "{v:4}"], - ]; - - try { - // create initial documents - server.createNamespace(namespace); - for (let [ns, id, payload] of documents) { - server.setDocument(ns, id, payload); - do_check_true(server.hasDocument(ns, id)); - } - - // Test uploading with deleting some documents. - let deleteIDs = [0, 1].map((no) => { return documents[no][1]; }); - let result = yield client.uploadJSON(namespace, "new-1", {foo: "bar"}, {deleteIDs: deleteIDs}); - do_check_true(result.transportSuccess); - do_check_true(result.serverSuccess); - do_check_true(server.hasDocument(namespace, "new-1")); - for (let id of deleteIDs) { - do_check_false(server.hasDocument(namespace, id)); - } - // Check if the documents that were not staged for deletion are still there. - for (let [,id,] of documents) { - if (deleteIDs.indexOf(id) == -1) { - do_check_true(server.hasDocument(namespace, id)); - } - } - - // Test upload without deleting documents. - let ids = Object.keys(server.namespaces[namespace]); - result = yield client.uploadJSON(namespace, "new-2", {foo: "bar"}); - do_check_true(result.transportSuccess); - do_check_true(result.serverSuccess); - do_check_true(server.hasDocument(namespace, "new-2")); - // Check to see if all the original documents are still there. - for (let id of ids) { - do_check_true(deleteIDs.indexOf(id) !== -1 || server.hasDocument(namespace, id)); - } - } finally { - let deferred = Promise.defer(); - server.stop(deferred.resolve.bind(deferred)); - yield deferred.promise; - } -}); - -add_test(function test_delete_document() { - let [client, server] = getClientAndServer(); - - server.createNamespace("foo"); - server.setDocument("foo", "bar", "{}"); - - client.deleteDocument("foo", "bar").then(function onResult(result) { - do_check_true(result.transportSuccess); - do_check_true(result.serverSuccess); - - do_check_null(server.getDocument("foo", "bar")); - - server.stop(run_next_test); - }); -}); diff --git a/services/common/tests/unit/test_bagheera_server.js b/services/common/tests/unit/test_bagheera_server.js deleted file mode 100644 index 52d234b7c86..00000000000 --- a/services/common/tests/unit/test_bagheera_server.js +++ /dev/null @@ -1,30 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -Cu.import("resource://testing-common/services/common/bagheeraserver.js"); - -function run_test() { - run_next_test(); -} - -add_test(function test_server_empty() { - let server = new BagheeraServer(); - - do_check_false(server.hasNamespace("foo")); - do_check_false(server.hasDocument("foo", "bar")); - do_check_null(server.getDocument("foo", "bar")); - - server.createNamespace("foo"); - do_check_true(server.hasNamespace("foo")); - - run_next_test(); -}); - -add_test(function test_server_start() { - let server = new BagheeraServer(); - server.start(); - server.stop(run_next_test); -}); - diff --git a/services/common/tests/unit/test_load_modules.js b/services/common/tests/unit/test_load_modules.js index b549c93ef09..66ecf0734ac 100644 --- a/services/common/tests/unit/test_load_modules.js +++ b/services/common/tests/unit/test_load_modules.js @@ -16,10 +16,6 @@ const non_android_modules = [ "tokenserverclient.js", ]; -const non_android_healthreport_modules = [ - "bagheeraclient.js", -]; - const TEST_BASE = "resource://testing-common/services/common/"; const shared_test_modules = [ "logging.js", @@ -29,10 +25,6 @@ const non_android_test_modules = [ "storageserver.js", ]; -const non_android_healthreport_test_modules = [ - "bagheeraserver.js", -]; - function expectImportsToSucceed(mm, base=MODULE_BASE) { for (let m of mm) { let resource = base + m; @@ -70,14 +62,8 @@ function run_test() { if (AppConstants.platform != "android") { expectImportsToSucceed(non_android_modules); expectImportsToSucceed(non_android_test_modules, TEST_BASE); - if (AppConstants.MOZ_SERVICES_HEALTHREPORT) { - expectImportsToSucceed(non_android_healthreport_modules); - expectImportsToSucceed(non_android_healthreport_test_modules, TEST_BASE); - } } else { expectImportsToFail(non_android_modules); expectImportsToFail(non_android_test_modules, TEST_BASE); - expectImportsToFail(non_android_healthreport_modules); - expectImportsToFail(non_android_healthreport_test_modules, TEST_BASE); } } diff --git a/services/common/tests/unit/xpcshell.ini b/services/common/tests/unit/xpcshell.ini index 2cdbe79be36..3288abc7651 100644 --- a/services/common/tests/unit/xpcshell.ini +++ b/services/common/tests/unit/xpcshell.ini @@ -29,12 +29,6 @@ support-files = [test_async_chain.js] [test_async_querySpinningly.js] -[test_bagheera_server.js] -skip-if = (os == "android" || !healthreport) - -[test_bagheera_client.js] -skip-if = (os == "android" || !healthreport) - [test_hawkclient.js] skip-if = os == "android" [test_hawkrequest.js] From d5ff8fca4b9adbec0dada263e1da02339f153c15 Mon Sep 17 00:00:00 2001 From: chaithanya Date: Thu, 28 Jan 2016 16:47:26 +0100 Subject: [PATCH 10/28] Bug 1090880 - Remove FUEL.r=mak --- .eslintignore | 1 - b2g/installer/package-manifest.in | 3 - browser/fuel/fuelApplication.js | 823 ------------------ browser/fuel/fuelApplication.manifest | 3 - browser/fuel/fuelIApplication.idl | 347 -------- browser/fuel/moz.build | 21 - browser/fuel/test/.eslintrc | 5 - browser/fuel/test/ContentA.html | 12 - browser/fuel/test/ContentB.html | 12 - browser/fuel/test/ContentWithFrames.html | 12 - browser/fuel/test/browser.ini | 12 - browser/fuel/test/browser_Application.js | 79 -- browser/fuel/test/browser_ApplicationPrefs.js | 179 ---- .../fuel/test/browser_ApplicationQuitting.js | 17 - .../fuel/test/browser_ApplicationStorage.js | 30 - browser/fuel/test/browser_Bookmarks.js | 253 ------ browser/fuel/test/browser_Browser.js | 148 ---- browser/installer/package-manifest.in | 3 - browser/moz.build | 1 - .../b2gdroid/installer/package-manifest.in | 3 - 20 files changed, 1964 deletions(-) delete mode 100644 browser/fuel/fuelApplication.js delete mode 100644 browser/fuel/fuelApplication.manifest delete mode 100644 browser/fuel/fuelIApplication.idl delete mode 100644 browser/fuel/moz.build delete mode 100644 browser/fuel/test/.eslintrc delete mode 100644 browser/fuel/test/ContentA.html delete mode 100644 browser/fuel/test/ContentB.html delete mode 100644 browser/fuel/test/ContentWithFrames.html delete mode 100644 browser/fuel/test/browser.ini delete mode 100644 browser/fuel/test/browser_Application.js delete mode 100644 browser/fuel/test/browser_ApplicationPrefs.js delete mode 100644 browser/fuel/test/browser_ApplicationQuitting.js delete mode 100644 browser/fuel/test/browser_ApplicationStorage.js delete mode 100644 browser/fuel/test/browser_Bookmarks.js delete mode 100644 browser/fuel/test/browser_Browser.js diff --git a/.eslintignore b/.eslintignore index 57b01695e3c..29606a5d89f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -76,7 +76,6 @@ browser/components/translation/** browser/extensions/pdfjs/** browser/extensions/pocket/content/panels/js/vendor/** browser/extensions/shumway/** -browser/fuel/** browser/locales/** # Ignore all of loop since it is imported from github and checked at source. diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index bd224e4b66a..3a2a2d3d92b 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -247,7 +247,6 @@ @RESPATH@/components/filepicker.xpt #endif @RESPATH@/components/find.xpt -@RESPATH@/components/fuel.xpt @RESPATH@/components/gfx.xpt @RESPATH@/components/hal.xpt @RESPATH@/components/html5.xpt @@ -400,8 +399,6 @@ @RESPATH@/components/BrowserFeeds.manifest @RESPATH@/components/FeedConverter.js @RESPATH@/components/FeedWriter.js -@RESPATH@/components/fuelApplication.manifest -@RESPATH@/components/fuelApplication.js @RESPATH@/components/WebContentConverter.js @RESPATH@/components/BrowserComponents.manifest @RESPATH@/components/nsBrowserContentHandler.js diff --git a/browser/fuel/fuelApplication.js b/browser/fuel/fuelApplication.js deleted file mode 100644 index e726efc5d97..00000000000 --- a/browser/fuel/fuelApplication.js +++ /dev/null @@ -1,823 +0,0 @@ -/* 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/. */ - -const Ci = Components.interfaces; -const Cc = Components.classes; - -Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Deprecated", - "resource://gre/modules/Deprecated.jsm"); - -const APPLICATION_CID = Components.ID("fe74cf80-aa2d-11db-abbd-0800200c9a66"); -const APPLICATION_CONTRACTID = "@mozilla.org/fuel/application;1"; - -//================================================= -// Singleton that holds services and utilities -var Utilities = { - get bookmarks() { - let bookmarks = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. - getService(Ci.nsINavBookmarksService); - this.__defineGetter__("bookmarks", () => bookmarks); - return this.bookmarks; - }, - - get bookmarksObserver() { - let bookmarksObserver = new BookmarksObserver(); - this.__defineGetter__("bookmarksObserver", () => bookmarksObserver); - return this.bookmarksObserver; - }, - - get annotations() { - let annotations = Cc["@mozilla.org/browser/annotation-service;1"]. - getService(Ci.nsIAnnotationService); - this.__defineGetter__("annotations", () => annotations); - return this.annotations; - }, - - get history() { - let history = Cc["@mozilla.org/browser/nav-history-service;1"]. - getService(Ci.nsINavHistoryService); - this.__defineGetter__("history", () => history); - return this.history; - }, - - get windowMediator() { - let windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"]. - getService(Ci.nsIWindowMediator); - this.__defineGetter__("windowMediator", () => windowMediator); - return this.windowMediator; - }, - - makeURI: function fuelutil_makeURI(aSpec) { - if (!aSpec) - return null; - var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); - return ios.newURI(aSpec, null, null); - }, - - free: function fuelutil_free() { - delete this.bookmarks; - delete this.bookmarksObserver; - delete this.annotations; - delete this.history; - delete this.windowMediator; - } -}; - - -//================================================= -// Window implementation - -var fuelWindowMap = new WeakMap(); -function getWindow(aWindow) { - let fuelWindow = fuelWindowMap.get(aWindow); - if (!fuelWindow) { - fuelWindow = new Window(aWindow); - fuelWindowMap.set(aWindow, fuelWindow); - } - return fuelWindow; -} - -// Don't call new Window() directly; use getWindow instead. -function Window(aWindow) { - this._window = aWindow; - this._events = new Events(); - - this._watch("TabOpen"); - this._watch("TabMove"); - this._watch("TabClose"); - this._watch("TabSelect"); -} - -Window.prototype = { - get events() { - return this._events; - }, - - get _tabbrowser() { - return this._window.gBrowser; - }, - - /* - * Helper used to setup event handlers on the XBL element. Note that the events - * are actually dispatched to tabs, so we capture them. - */ - _watch: function win_watch(aType) { - this._tabbrowser.tabContainer.addEventListener(aType, this, - /* useCapture = */ true); - }, - - handleEvent: function win_handleEvent(aEvent) { - this._events.dispatch(aEvent.type, getBrowserTab(this, aEvent.originalTarget.linkedBrowser)); - }, - - get tabs() { - var tabs = []; - var browsers = this._tabbrowser.browsers; - for (var i=0; i getWindow(Utilities.windowMediator.getMostRecentWindow("navigator:browser")), - enumerable: true, - configurable: true - }); - -}; - -// set the proto, defined in extApplication.js -ApplicationPrototype.prototype = extApplication.prototype; - -Application.prototype = new ApplicationPrototype(); - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Application]); - diff --git a/browser/fuel/fuelApplication.manifest b/browser/fuel/fuelApplication.manifest deleted file mode 100644 index 67e6d0fe6a5..00000000000 --- a/browser/fuel/fuelApplication.manifest +++ /dev/null @@ -1,3 +0,0 @@ -component {fe74cf80-aa2d-11db-abbd-0800200c9a66} fuelApplication.js -contract @mozilla.org/fuel/application;1 {fe74cf80-aa2d-11db-abbd-0800200c9a66} -category JavaScript-global-privileged-property Application @mozilla.org/fuel/application;1 diff --git a/browser/fuel/fuelIApplication.idl b/browser/fuel/fuelIApplication.idl deleted file mode 100644 index 69b51b0f5e8..00000000000 --- a/browser/fuel/fuelIApplication.idl +++ /dev/null @@ -1,347 +0,0 @@ -/* 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/. */ - -#include "nsISupports.idl" -#include "extIApplication.idl" - -interface nsIVariant; -interface nsIURI; -interface nsIDOMHTMLDocument; - -interface fuelIBookmarkFolder; -interface fuelIBrowserTab; - -/** - * Interface representing a collection of annotations associated - * with a bookmark or bookmark folder. - */ -[scriptable, uuid(335c9292-91a1-4ca0-ad0b-07d5f63ed6cd)] -interface fuelIAnnotations : nsISupports -{ - /** - * Array of the annotation names associated with the owning item - */ - readonly attribute nsIVariant names; - - /** - * Determines if an annotation exists with the given name. - * @param aName - * The name of the annotation - * @returns true if an annotation exists with the given name, - * false otherwise. - */ - boolean has(in AString aName); - - /** - * Gets the value of an annotation with the given name. - * @param aName - * The name of the annotation - * @returns A variant containing the value of the annotation. Supports - * string, boolean and number. - */ - nsIVariant get(in AString aName); - - /** - * Sets the value of an annotation with the given name. - * @param aName - * The name of the annotation - * @param aValue - * The new value of the annotation. Supports string, boolean - * and number - * @param aExpiration - * The expiration policy for the annotation. - * See nsIAnnotationService. - */ - void set(in AString aName, in nsIVariant aValue, in int32_t aExpiration); - - /** - * Removes the named annotation from the owner item. - * @param aName - * The name of annotation. - */ - void remove(in AString aName); -}; - - -/** - * Interface representing a bookmark item. - */ -[scriptable, uuid(808585b6-7568-4b26-8c62-545221bf2b8c)] -interface fuelIBookmark : nsISupports -{ - /** - * The id of the bookmark. - */ - readonly attribute long long id; - - /** - * The title of the bookmark. - */ - attribute AString title; - - /** - * The uri of the bookmark. - */ - attribute nsIURI uri; - - /** - * The description of the bookmark. - */ - attribute AString description; - - /** - * The keyword associated with the bookmark. - */ - attribute AString keyword; - - /** - * The type of the bookmark. - * values: "bookmark", "separator" - */ - readonly attribute AString type; - - /** - * The parent folder of the bookmark. - */ - attribute fuelIBookmarkFolder parent; - - /** - * The annotations object for the bookmark. - */ - readonly attribute fuelIAnnotations annotations; - - /** - * The events object for the bookmark. - * supports: "remove", "change", "visit", "move" - */ - readonly attribute extIEvents events; - - /** - * Removes the item from the parent folder. Used to - * delete a bookmark or separator - */ - void remove(); -}; - - -/** - * Interface representing a bookmark folder. Folders - * can hold bookmarks, separators and other folders. - */ -[scriptable, uuid(9f42fe20-52de-4a55-8632-a459c7716aa0)] -interface fuelIBookmarkFolder : nsISupports -{ - /** - * The id of the folder. - */ - readonly attribute long long id; - - /** - * The title of the folder. - */ - attribute AString title; - - /** - * The description of the folder. - */ - attribute AString description; - - /** - * The type of the folder. - * values: "folder" - */ - readonly attribute AString type; - - /** - * The parent folder of the folder. - */ - attribute fuelIBookmarkFolder parent; - - /** - * The annotations object for the folder. - */ - readonly attribute fuelIAnnotations annotations; - - /** - * The events object for the folder. - * supports: "add", "addchild", "remove", "removechild", "change", "move" - */ - readonly attribute extIEvents events; - - /** - * Array of all bookmarks, separators and folders contained - * in this folder. - */ - readonly attribute nsIVariant children; - - /** - * Adds a new child bookmark to this folder. - * @param aTitle - * The title of bookmark. - * @param aURI - * The uri of bookmark. - */ - fuelIBookmark addBookmark(in AString aTitle, in nsIURI aURI); - - /** - * Adds a new child separator to this folder. - */ - fuelIBookmark addSeparator(); - - /** - * Adds a new child folder to this folder. - * @param aTitle - * The title of folder. - */ - fuelIBookmarkFolder addFolder(in AString aTitle); - - /** - * Removes the folder from the parent folder. - */ - void remove(); -}; - -/** - * Interface representing a container for bookmark roots. Roots - * are the top level parents for the various types of bookmarks in the system. - */ -[scriptable, uuid(c9a80870-eb3c-11dc-95ff-0800200c9a66)] -interface fuelIBookmarkRoots : nsISupports -{ - /** - * The folder for the 'bookmarks menu' root. - */ - readonly attribute fuelIBookmarkFolder menu; - - /** - * The folder for the 'personal toolbar' root. - */ - readonly attribute fuelIBookmarkFolder toolbar; - - /** - * The folder for the 'tags' root. - */ - readonly attribute fuelIBookmarkFolder tags; - - /** - * The folder for the 'unfiled bookmarks' root. - */ - readonly attribute fuelIBookmarkFolder unfiled; -}; - -/** - * Interface representing a browser window. - */ -[scriptable, uuid(207edb28-eb5e-424e-a862-b0e97C8de866)] -interface fuelIWindow : nsISupports -{ - /** - * A collection of browser tabs within the browser window. - */ - readonly attribute nsIVariant tabs; - - /** - * The currently-active tab within the browser window. - */ - readonly attribute fuelIBrowserTab activeTab; - - /** - * Open a new browser tab, pointing to the specified URI. - * @param aURI - * The uri to open the browser tab to - */ - fuelIBrowserTab open(in nsIURI aURI); - - /** - * The events object for the browser window. - * supports: "TabOpen", "TabClose", "TabMove", "TabSelect" - */ - readonly attribute extIEvents events; -}; - -/** - * Interface representing a browser tab. - */ -[scriptable, uuid(3073ceff-777c-41ce-9ace-ab37268147c1)] -interface fuelIBrowserTab : nsISupports -{ - /** - * The current uri of this tab. - */ - readonly attribute nsIURI uri; - - /** - * The current index of this tab in the browser window. - */ - readonly attribute int32_t index; - - /** - * The browser window that is holding the tab. - */ - readonly attribute fuelIWindow window; - - /** - * The content document of the browser tab. - */ - readonly attribute nsIDOMHTMLDocument document; - - /** - * The events object for the browser tab. - * supports: "load" - */ - readonly attribute extIEvents events; - - /** - * Load a new URI into this browser tab. - * @param aURI - * The uri to load into the browser tab - */ - void load(in nsIURI aURI); - - /** - * Give focus to this browser tab, and bring it to the front. - */ - void focus(); - - /** - * Close the browser tab. This may not actually close the tab - * as script may abort the close operation. - */ - void close(); - - /** - * Moves this browser tab before another browser tab within the window. - * @param aBefore - * The tab before which the target tab will be moved - */ - void moveBefore(in fuelIBrowserTab aBefore); - - /** - * Move this browser tab to the last tab within the window. - */ - void moveToEnd(); -}; - -/** - * Interface for managing and accessing the applications systems - */ -[scriptable, uuid(fe74cf80-aa2d-11db-abbd-0800200c9a66)] -interface fuelIApplication : extIApplication -{ - /** - * The root bookmarks object for the application. - * Contains all the bookmark roots in the system. - */ - readonly attribute fuelIBookmarkRoots bookmarks; - - /** - * An array of browser windows within the application. - */ - readonly attribute nsIVariant windows; - - /** - * The currently active browser window. - */ - readonly attribute fuelIWindow activeWindow; -}; diff --git a/browser/fuel/moz.build b/browser/fuel/moz.build deleted file mode 100644 index 10562b0f81e..00000000000 --- a/browser/fuel/moz.build +++ /dev/null @@ -1,21 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -BROWSER_CHROME_MANIFESTS += ['test/browser.ini'] - -XPIDL_SOURCES += [ - 'fuelIApplication.idl', -] - -XPIDL_MODULE = 'fuel' - -EXTRA_COMPONENTS += [ - 'fuelApplication.manifest', -] - -EXTRA_PP_COMPONENTS += [ - 'fuelApplication.js', -] diff --git a/browser/fuel/test/.eslintrc b/browser/fuel/test/.eslintrc deleted file mode 100644 index a9848783e32..00000000000 --- a/browser/fuel/test/.eslintrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": [ - "../../../testing/mochitest/browser.eslintrc" - ] -} diff --git a/browser/fuel/test/ContentA.html b/browser/fuel/test/ContentA.html deleted file mode 100644 index 67cbadf47ee..00000000000 --- a/browser/fuel/test/ContentA.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - Content Page A - - -

Content Page A

-
This is a simple content page used for testing FUEL Browser API
-
A
-
B
- - diff --git a/browser/fuel/test/ContentB.html b/browser/fuel/test/ContentB.html deleted file mode 100644 index c5d3b37ebf5..00000000000 --- a/browser/fuel/test/ContentB.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - Content Page B - - -

Content Page B

-
This is a simple content page used for testing FUEL Browser API
-
1
-
2
- - diff --git a/browser/fuel/test/ContentWithFrames.html b/browser/fuel/test/ContentWithFrames.html deleted file mode 100644 index a66873ad816..00000000000 --- a/browser/fuel/test/ContentWithFrames.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - Content Page with Frames - - -

Content Page with Frames

-
This is a simple framed content page used for testing FUEL Browser API
- - - - diff --git a/browser/fuel/test/browser.ini b/browser/fuel/test/browser.ini deleted file mode 100644 index 3081e059b25..00000000000 --- a/browser/fuel/test/browser.ini +++ /dev/null @@ -1,12 +0,0 @@ -[DEFAULT] -support-files = - ContentA.html - ContentB.html - ContentWithFrames.html - -[browser_Application.js] -[browser_ApplicationPrefs.js] -[browser_ApplicationQuitting.js] -[browser_ApplicationStorage.js] -[browser_Bookmarks.js] -[browser_Browser.js] diff --git a/browser/fuel/test/browser_Application.js b/browser/fuel/test/browser_Application.js deleted file mode 100644 index b2590d6a776..00000000000 --- a/browser/fuel/test/browser_Application.js +++ /dev/null @@ -1,79 +0,0 @@ -// This listens for the next opened window and checks it is of the right url. -// opencallback is called when the new window is fully loaded -// closecallback is called when the window is closed -function WindowOpenListener(url, opencallback, closecallback) { - this.url = url; - this.opencallback = opencallback; - this.closecallback = closecallback; - - Services.wm.addListener(this); -} - -WindowOpenListener.prototype = { - url: null, - opencallback: null, - closecallback: null, - window: null, - domwindow: null, - - handleEvent: function(event) { - is(this.domwindow.document.location.href, this.url, "Should have opened the correct window"); - - this.domwindow.removeEventListener("load", this, false); - // Allow any other load handlers to execute - var self = this; - executeSoon(function() { self.opencallback(self.domwindow); } ); - }, - - onWindowTitleChange: function(window, title) { - }, - - onOpenWindow: function(window) { - if (this.window) - return; - - this.window = window; - this.domwindow = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow); - this.domwindow.addEventListener("load", this, false); - }, - - onCloseWindow: function(window) { - if (this.window != window) - return; - - Services.wm.removeListener(this); - this.opencallback = null; - this.window = null; - this.domwindow = null; - - // Let the window close complete - executeSoon(this.closecallback); - this.closecallback = null; - } -}; - -function test() { - ok(Application, "Check global access to Application"); - - // I'd test these against a specific value, but that is bound to flucuate - ok(Application.id, "Check to see if an ID exists for the Application"); - ok(Application.name, "Check to see if a name exists for the Application"); - ok(Application.version, "Check to see if a version exists for the Application"); - - var wMediator = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator); - var console = wMediator.getMostRecentWindow("global:console"); - waitForExplicitFinish(); - ok(!console, "Console should not already be open"); - - new WindowOpenListener("chrome://global/content/console.xul", consoleOpened, consoleClosed); - Application.console.open(); -} - -function consoleOpened(win) { - win.close(); -} - -function consoleClosed() { - finish(); -} diff --git a/browser/fuel/test/browser_ApplicationPrefs.js b/browser/fuel/test/browser_ApplicationPrefs.js deleted file mode 100644 index 6c84b5dbcc4..00000000000 --- a/browser/fuel/test/browser_ApplicationPrefs.js +++ /dev/null @@ -1,179 +0,0 @@ -// The various properties that we'll be testing -var testdata = { - missing: "fuel.fuel-test-missing", - dummy: "fuel.fuel-test", - string: "browser.active_color", - integer: "permissions.default.image", - boolean: "browser.underline_anchors" -}; - -function test() { - // test getting nonexistent values - var itemValue = Application.prefs.getValue(testdata.missing, "default"); - is(itemValue, "default", "Check 'Application.prefs.getValue' for nonexistent item"); - - is(Application.prefs.get(testdata.missing), null, "Check 'Application.prefs.get' for nonexistent item"); - - // test setting and getting a value - Application.prefs.setValue(testdata.dummy, "dummy"); - itemValue = Application.prefs.getValue(testdata.dummy, "default"); - is(itemValue, "dummy", "Check 'Application.prefs.getValue' for existing item"); - - // test for overwriting an existing value - Application.prefs.setValue(testdata.dummy, "smarty"); - itemValue = Application.prefs.getValue(testdata.dummy, "default"); - is(itemValue, "smarty", "Check 'Application.prefs.getValue' for overwritten item"); - - // test setting and getting a value - Application.prefs.get(testdata.dummy).value = "dummy2"; - itemValue = Application.prefs.get(testdata.dummy).value; - is(itemValue, "dummy2", "Check 'Application.prefs.get().value' for existing item"); - - // test resetting a pref [since there is no default value, the pref should disappear] - Application.prefs.get(testdata.dummy).reset(); - itemValue = Application.prefs.getValue(testdata.dummy, "default"); - is(itemValue, "default", "Check 'Application.prefs.getValue' for reset pref"); - - // test to see if a non-existant property exists - ok(!Application.prefs.has(testdata.dummy), "Check non-existant property for existence"); - - // PREF: string browser.active_color == #EE0000 - - // test to see if an existing string property exists - ok(Application.prefs.has(testdata.string), "Check existing string property for existence"); - - // test accessing a non-existant string property - var val = Application.prefs.getValue(testdata.dummy, "default"); - is(val, "default", "Check non-existant string property for expected value"); - - // test accessing an existing string property - var val = Application.prefs.getValue(testdata.string, "default"); - is(val, "#EE0000", "Check existing string property for expected value"); - - // test manipulating an existing string property - Application.prefs.setValue(testdata.string, "#EF0000"); - var val = Application.prefs.getValue(testdata.string, "default"); - is(val, "#EF0000", "Set existing string property"); - - // test getting the type of an existing string property - var type = Application.prefs.get(testdata.string).type; - is(type, "String", "Check 'Application.prefs.get().type' for string pref"); - - // test resetting an existing string property - Application.prefs.get(testdata.string).reset(); - var val = Application.prefs.getValue(testdata.string, "default"); - is(val, "#EE0000", "Reset existing string property"); - - // PREF: integer permissions.default.image == 1 - - // test to see if an existing integer property exists - ok(Application.prefs.has(testdata.integer), "Check existing integer property for existence"); - - // test accessing a non-existant integer property - var val = Application.prefs.getValue(testdata.dummy, 0); - is(val, 0, "Check non-existant integer property for expected value"); - - // test accessing an existing integer property - var val = Application.prefs.getValue(testdata.integer, 0); - is(val, 1, "Check existing integer property for expected value"); - - // test manipulating an existing integer property - Application.prefs.setValue(testdata.integer, 0); - var val = Application.prefs.getValue(testdata.integer, 1); - is(val, 0, "Set existing integer property"); - - // test getting the type of an existing integer property - var type = Application.prefs.get(testdata.integer).type; - is(type, "Number", "Check 'Application.prefs.get().type' for integer pref"); - - // test resetting an existing integer property - Application.prefs.get(testdata.integer).reset(); - var val = Application.prefs.getValue(testdata.integer, 0); - is(val, 1, "Reset existing integer property"); - - // PREF: boolean browser.underline_anchors == true - - // test to see if an existing boolean property exists - ok(Application.prefs.has(testdata.boolean), "Check existing boolean property for existence"); - - // test accessing a non-existant boolean property - var val = Application.prefs.getValue(testdata.dummy, true); - ok(val, "Check non-existant boolean property for expected value"); - - // test accessing an existing boolean property - var val = Application.prefs.getValue(testdata.boolean, false); - ok(val, "Check existing boolean property for expected value"); - - // test manipulating an existing boolean property - Application.prefs.setValue(testdata.boolean, false); - var val = Application.prefs.getValue(testdata.boolean, true); - ok(!val, "Set existing boolean property"); - - // test getting the type of an existing boolean property - var type = Application.prefs.get(testdata.boolean).type; - is(type, "Boolean", "Check 'Application.prefs.get().type' for boolean pref"); - - // test resetting an existing boolean property - Application.prefs.get(testdata.boolean).reset(); - var val = Application.prefs.getValue(testdata.boolean, false); - ok(val, "Reset existing string property for expected value"); - - // test getting all preferences - var allPrefs = Application.prefs.all; - ok(allPrefs.length >= 800, "Check 'Application.prefs.all' for the right number of preferences"); - ok(allPrefs[0].name.length > 0, "Check 'Application.prefs.all' for a valid name in the starting preference"); - - // test the value of the preference root - is(Application.prefs.root, "", "Check the Application preference root"); - - // test for user changed preferences - ok(Application.prefs.get("browser.dom.window.dump.enabled").modified, "A single preference is marked as modified."); - ok(!Application.prefs.get(testdata.string).modified, "A single preference is marked as not modified."); - - // test for a locked preference - var pref = Application.prefs.get(testdata.string); - ok(!pref.locked, "A single preference should not be locked."); - - pref.locked = true; - ok(pref.locked, "A single preference should be locked."); - - try { - prev.value = "test value"; - - ok(false, "A locked preference could be modified."); - } catch(e){ - ok(true, "A locked preference should not be able to be modified."); - } - - pref.locked = false; - ok(!pref.locked, "A single preference is unlocked."); - - // check for change event when setting a value - waitForExplicitFinish(); - Application.prefs.events.addListener("change", onPrefChange); - Application.prefs.setValue("fuel.fuel-test", "change event"); -} - -function onPrefChange(evt) { - is(evt.data, testdata.dummy, "Check 'Application.prefs.setValue' fired a change event"); - Application.prefs.events.removeListener("change", onPrefChange); - - // We are removing the old listener after adding the new listener so we can test that - // removing a listener does not remove all listeners - Application.prefs.get("fuel.fuel-test").events.addListener("change", onPrefChangeDummy); - Application.prefs.get("fuel.fuel-test").events.addListener("change", onPrefChange2); - Application.prefs.get("fuel.fuel-test").events.removeListener("change", onPrefChangeDummy); - - Application.prefs.setValue("fuel.fuel-test", "change event2"); -} - -function onPrefChange2(evt) { - is(evt.data, testdata.dummy, "Check 'Application.prefs.setValue' fired a change event for a single preference"); - Application.prefs.events.removeListener("change", onPrefChange2); - - finish(); -} - -function onPrefChangeDummy(evt) { - ok(false, "onPrefChangeDummy shouldn't be invoked!"); -} diff --git a/browser/fuel/test/browser_ApplicationQuitting.js b/browser/fuel/test/browser_ApplicationQuitting.js deleted file mode 100644 index 2a3535fc9e2..00000000000 --- a/browser/fuel/test/browser_ApplicationQuitting.js +++ /dev/null @@ -1,17 +0,0 @@ -function test() { - function quitRequestObserver(aSubject, aTopic, aData) { - ok(aTopic == "quit-application-requested" && - aSubject instanceof Components.interfaces.nsISupportsPRBool, - "Received a quit request we're going to deny"); - aSubject.data = true; - } - - // ensure that we don't accidentally quit - Services.obs.addObserver(quitRequestObserver, "quit-application-requested", false); - - ok(!Application.quit(), "Tried to quit - and didn't succeed"); - ok(!Application.restart(), "Tried to restart - and didn't succeed"); - - // clean up - Services.obs.removeObserver(quitRequestObserver, "quit-application-requested"); -} diff --git a/browser/fuel/test/browser_ApplicationStorage.js b/browser/fuel/test/browser_ApplicationStorage.js deleted file mode 100644 index ab19f783943..00000000000 --- a/browser/fuel/test/browser_ApplicationStorage.js +++ /dev/null @@ -1,30 +0,0 @@ -function test() { - // test for existence of values - var hasItem = Application.storage.has("fuel-test-missing"); - is(hasItem, false, "Check 'Application.storage.has' for nonexistent item"); - Application.storage.set("fuel-test", "dummy"); - hasItem = Application.storage.has("fuel-test"); - is(hasItem, true, "Check 'Application.storage.has' for existing item"); - - // test getting nonexistent and existing values - var itemValue = Application.storage.get("fuel-test-missing", "default"); - is(itemValue, "default", "Check 'Application.storage.get' for nonexistent item"); - itemValue = Application.storage.get("fuel-test", "default"); - is(itemValue, "dummy", "Check 'Application.storage.get' for existing item"); - - // test for overwriting an existing value - Application.storage.set("fuel-test", "smarty"); - itemValue = Application.storage.get("fuel-test", "default"); - is(itemValue, "smarty", "Check 'Application.storage.get' for overwritten item"); - - // check for change event when setting a value - waitForExplicitFinish(); - Application.storage.events.addListener("change", onStorageChange); - Application.storage.set("fuel-test", "change event"); -} - -function onStorageChange(evt) { - is(evt.data, "fuel-test", "Check 'Application.storage.set' fired a change event"); - Application.storage.events.removeListener("change", onStorageChange); - finish(); -} diff --git a/browser/fuel/test/browser_Bookmarks.js b/browser/fuel/test/browser_Bookmarks.js deleted file mode 100644 index 803ede63b3b..00000000000 --- a/browser/fuel/test/browser_Bookmarks.js +++ /dev/null @@ -1,253 +0,0 @@ -var gLastFolderAction = ""; -var gLastBookmarkAction = ""; -var gLastRootAction = ""; - -function url(spec) { - return Services.io.newURI(spec, null, null); -} - -function test() { - // Some very basic tests on the tags root - var tags = Application.bookmarks.tags; - ok(tags, "Check access to bookmark tags root"); - ok(!tags.parent, "Check tags parent (should be null)"); - - //---------------------------------------------- - - // Some very basic tests on the unfiled root - var unfiled = Application.bookmarks.unfiled; - ok(unfiled, "Check access to bookmark unfiled root"); - ok(!unfiled.parent, "Check unfiled parent (should be null)"); - - //---------------------------------------------- - - // Some basic tests on the toolbar root - var toolbar = Application.bookmarks.toolbar; - ok(toolbar, "Check access to bookmark toolbar root"); - ok(!toolbar.parent, "Check toolbar parent (should be null)"); - - var toolbarKidCount = toolbar.children.length; - - // test adding folders - var testFolderToolbar = toolbar.addFolder("FUEL in Toolbar"); - ok(testFolderToolbar, "Check folder creation"); - is(testFolderToolbar.type, "folder", "Check 'folder.type' after creation"); - ok(testFolderToolbar.parent, "Check parent after folder creation"); - - toolbarKidCount++; - is(toolbar.children.length, toolbarKidCount, "Check toolbar folder child count after adding a child folder"); - - //---------------------------------------------- - - // Main testing is done on the bookmarks menu root - var root = Application.bookmarks.menu; - ok(root, "Check access to bookmark root"); - ok(!root.parent, "Check root parent (should be null)"); - - var rootKidCount = root.children.length; - - // test adding folders - var testFolder = root.addFolder("FUEL"); - ok(testFolder, "Check folder creation"); - is(testFolder.type, "folder", "Check 'folder.type' after creation"); - ok(testFolder.parent, "Check parent after folder creation"); - - rootKidCount++; - is(root.children.length, rootKidCount, "Check root folder child count after adding a child folder"); - - // test modifying a folder - testFolder.events.addListener("change", onFolderChange); - testFolder.description = "FUEL folder"; - is(testFolder.description, "FUEL folder", "Check setting 'folder.description'"); - is(gLastFolderAction, "bookmarkProperties/description", "Check event handler for setting 'folder.description'"); - - testFolder.title = "fuel-is-cool"; - is(testFolder.title, "fuel-is-cool", "Check setting 'folder.title'"); - is(gLastFolderAction, "title", "Check event handler for setting 'folder.title'"); - - testFolder.annotations.set("testing/folder", "annotate-this", 0); - ok(testFolder.annotations.has("testing/folder"), "Checking existence of added annotation"); - is(gLastFolderAction, "testing/folder", "Check event handler for setting annotation"); - gLastFolderAction = ""; - is(testFolder.annotations.get("testing/folder"), "annotate-this", "Checking existence of added annotation"); - testFolder.annotations.remove("testing/folder"); - ok(!testFolder.annotations.has("testing/folder"), "Checking existence of removed annotation"); - is(gLastFolderAction, "testing/folder", "Check event handler for removing annotation"); - - testFolder.events.addListener("addchild", onFolderAddChild); - testFolder.events.addListener("removechild", onFolderRemoveChild); - - // test adding a bookmark - var testBookmark = testFolder.addBookmark("Mozilla", url("https://www.mozilla.org/")); - ok(testBookmark, "Check bookmark creation"); - ok(testBookmark.parent, "Check parent after bookmark creation"); - is(gLastFolderAction, "addchild", "Check event handler for adding a child to a folder"); - is(testBookmark.type, "bookmark", "Check 'bookmark.type' after creation"); - is(testBookmark.title, "Mozilla", "Check 'bookmark.title' after creation"); - is(testBookmark.uri.spec, "https://www.mozilla.org/", "Check 'bookmark.uri' after creation"); - - is(testFolder.children.length, 1, "Check test folder child count after adding a child bookmark"); - - // test modifying a bookmark - testBookmark.events.addListener("change", onBookmarkChange); - testBookmark.description = "mozcorp"; - is(testBookmark.description, "mozcorp", "Check setting 'bookmark.description'"); - is(gLastBookmarkAction, "bookmarkProperties/description", "Check event handler for setting 'bookmark.description'"); - - testBookmark.keyword = "moz" - is(testBookmark.keyword, "moz", "Check setting 'bookmark.keyword'"); - is(gLastBookmarkAction, "keyword", "Check event handler for setting 'bookmark.keyword'"); - - testBookmark.title = "MozCorp" - is(testBookmark.title, "MozCorp", "Check setting 'bookmark.title'"); - is(gLastBookmarkAction, "title", "Check event handler for setting 'bookmark.title'"); - - testBookmark.uri = url("http://www.mozilla.org/"); - is(testBookmark.uri.spec, "http://www.mozilla.org/", "Check setting 'bookmark.uri'"); - is(gLastBookmarkAction, "uri", "Check event handler for setting 'bookmark.uri'"); - - // test adding and removing a bookmark annotation - testBookmark.annotations.set("testing/bookmark", "annotate-this", 0); - ok(testBookmark.annotations.has("testing/bookmark"), "Checking existence of added annotation"); - is(gLastBookmarkAction, "testing/bookmark", "Check event handler for setting annotation"); - gLastBookmarkAction = ""; - is(testBookmark.annotations.get("testing/bookmark"), "annotate-this", "Checking existence of added annotation"); - testBookmark.annotations.remove("testing/bookmark"); - ok(!testBookmark.annotations.has("testing/bookmark"), "Checking existence of removed annotation"); - is(gLastBookmarkAction, "testing/bookmark", "Check event handler for removing annotation"); - is(testBookmark.annotations.get("testing/bookmark"), null, "Check existence of a missing annotation"); - - // quick annotation type tests - testBookmark.annotations.set("testing/bookmark/string", "annotate-this", 0); - ok(testBookmark.annotations.has("testing/bookmark/string"), "Checking existence of added string annotation"); - is(testBookmark.annotations.get("testing/bookmark/string"), "annotate-this", "Checking value of added string annotation"); - is(gLastBookmarkAction, "testing/bookmark/string", "Check event handler for setting annotation"); - gLastBookmarkAction = ""; - testBookmark.annotations.set("testing/bookmark/int", 100, 0); - ok(testBookmark.annotations.has("testing/bookmark/int"), "Checking existence of added integer annotation"); - is(testBookmark.annotations.get("testing/bookmark/int"), 100, "Checking value of added integer annotation"); - is(gLastBookmarkAction, "testing/bookmark/int", "Check event handler for setting annotation"); - gLastBookmarkAction = ""; - testBookmark.annotations.set("testing/bookmark/double", 3.333, 0); - ok(testBookmark.annotations.has("testing/bookmark/double"), "Checking existence of added double annotation"); - is(testBookmark.annotations.get("testing/bookmark/double"), 3.333, "Checking value of added double annotation"); - is(gLastBookmarkAction, "testing/bookmark/double", "Check event handler for setting annotation"); - gLastBookmarkAction = ""; - - // test names array - NOTE: "bookmarkProperties/description" is an annotation too - var names = testBookmark.annotations.names; - ok(names.some(f => f == "bookmarkProperties/description"), "Checking for description annotation"); - ok(names.some(f => f == "testing/bookmark/string"), "Checking for string test annotation"); - ok(names.some(f => f == "testing/bookmark/int"), "Checking for int test annotation"); - ok(names.some(f => f == "testing/bookmark/double"), "Checking for double test annotation"); - - // test adding a separator - var testSeparator = testFolder.addSeparator(); - ok(testSeparator, "Check bookmark creation"); - ok(testSeparator.parent, "Check parent after separator creation"); - is(gLastFolderAction, "addchild", "Check event handler for adding a child separator to a folder"); - is(testSeparator.type, "separator", "Check 'bookmark.type' after separator creation"); - - is(testFolder.children.length, 2, "Check test folder child count after adding a child separator"); - - // test removing separator - testSeparator.events.addListener("remove", onBookmarkRemove); - testSeparator.remove(); - is(gLastBookmarkAction, "remove", "Check event handler for removing separator"); - is(gLastFolderAction, "removechild", "Check event handler for removing a child separator from a folder"); - is(testFolder.children.length, 1, "Check test folder child count after removing a child separator"); - - // test removing bookmark - testBookmark.events.addListener("remove", onBookmarkRemove); - testBookmark.remove(); - is(gLastBookmarkAction, "remove", "Check event handler for removing bookmark"); - is(gLastFolderAction, "removechild", "Check event handler for removing a child from a folder"); - is(testFolder.children.length, 0, "Check test folder child count after removing a child bookmark"); - - // test removing a folder - testFolder.events.addListener("remove", onFolderRemove); - testFolder.remove(); - is(gLastFolderAction, "remove", "Check event handler for removing child folder"); - rootKidCount--; - is(root.children.length, rootKidCount, "Check root folder child count after removing a child folder"); - - // test moving between folders - var testFolderA = root.addFolder("folder-a"); - var testFolderB = root.addFolder("folder-b"); - - var testMove = testFolderA.addBookmark("Mozilla", url("https://www.mozilla.org/")); - testMove.events.addListener("move", onBookmarkMove); - is(testMove.parent.title, "folder-a", "Checking for new parent before moving bookmark"); - - testMove.parent = testFolderB; - is(testMove.parent.title, "folder-b", "Checking for new parent after moving bookmark"); - is(gLastBookmarkAction, "move", "Checking for event handler after moving bookmark"); - - // test moving a folder - testFolderA.events.addListener("move", onFolderMove); - testFolderA.parent = testFolderB; - is(testFolderA.parent.title, "folder-b", "Checking for new parent after moving folder"); - is(gLastFolderAction, "move", "Checking for event handler after moving folder"); - - // test events on the root - root.events.addListener("add", onRootAdd); - root.events.addListener("remove", onRootRemove); - root.events.addListener("change", onRootChange); - var testFolderC = root.addFolder("folder-c"); - is(gLastRootAction, "add"); - - root.events.removeListener("add", onRootAdd); - gLastRootAction = ""; - var testFolderD = root.addFolder("folder-d"); - is(gLastRootAction, ""); - - testFolderC.remove(); - is(gLastRootAction, "remove"); - - testFolderD.description = "Foo"; - is(gLastRootAction, "bookmarkProperties/description"); -} - -function onFolderChange(evt) { - gLastFolderAction = evt.data; -} - -function onFolderRemove(evt) { - gLastFolderAction = evt.type; -} - -function onFolderAddChild(evt) { - gLastFolderAction = evt.type; -} - -function onFolderRemoveChild(evt) { - gLastFolderAction = evt.type; -} - -function onFolderMove(evt) { - gLastFolderAction = evt.type; -} - -function onBookmarkChange(evt) { - gLastBookmarkAction = evt.data; -} - -function onBookmarkRemove(evt) { - gLastBookmarkAction = evt.type; -} - -function onBookmarkMove(evt) { - gLastBookmarkAction = evt.type; -} - -function onRootAdd(evt) { - gLastRootAction = evt.type; -} - -function onRootRemove(evt) { - gLastRootAction = evt.type; -} - -function onRootChange(evt) { - gLastRootAction = evt.data; -} diff --git a/browser/fuel/test/browser_Browser.js b/browser/fuel/test/browser_Browser.js deleted file mode 100644 index 2e705120175..00000000000 --- a/browser/fuel/test/browser_Browser.js +++ /dev/null @@ -1,148 +0,0 @@ -var gPageA = null; -var gPageB = null; - -// cached data from events -var gTabOpenPageA = null; -var gTabOpenPageB = null; -var gTabOpenCount = 0; -var gTabCloseCount = 0; -var gTabMoveCount = 0; -var gPageLoadCount = 0; - -var rootDir = getRootDirectory(gTestPath); -const CHROMEROOT = rootDir; - -function test() { - waitForExplicitFinish(); - - var windows = Application.windows; - ok(windows, "Check access to browser windows"); - is(windows.length, 1, "There should be one browser window open"); - - var activeWin = Application.activeWindow; - activeWin.events.addListener("TabOpen", onTabOpen); - activeWin.events.addListener("TabClose", onTabClose); - activeWin.events.addListener("TabMove", onTabMove); - - gPageA = activeWin.open(makeURI(CHROMEROOT + "ContentA.html")); - gPageA.events.addListener("load", onPageAFirstLoad); - - is(activeWin.tabs.length, 2, "Checking length of 'Browser.tabs' after opening 1 additional tab"); - - function onPageAFirstLoad(event) { - gPageA.events.removeListener("load", onPageAFirstLoad); - is(gPageA.uri.spec, event.data.uri.spec, "Checking event browser tab is equal to page A"); - - gPageB = activeWin.open(makeURI(CHROMEROOT + "ContentB.html")); - gPageB.events.addListener("load", delayAfterOpen); - gPageB.focus(); - - is(activeWin.tabs.length, 3, "Checking length of 'Browser.tabs' after opening a second additional tab"); - is(activeWin.activeTab.index, gPageB.index, "Checking 'Browser.activeTab' after setting focus"); - } - - function delayAfterOpen() { - executeSoon(afterOpen); - } - - // need to wait for the url's to be refreshed during the load - function afterOpen(event) { - gPageB.events.removeListener("load", delayAfterOpen); - // check actuals - is(gPageA.uri.spec, CHROMEROOT + "ContentA.html", "Checking 'BrowserTab.uri' after opening"); - is(gPageB.uri.spec, CHROMEROOT + "ContentB.html", "Checking 'BrowserTab.uri' after opening"); - - // check event - is(gTabOpenCount, 2, "Checking event handler for tab open"); - // check cached values from TabOpen event - is(gPageA.uri.spec, gTabOpenPageA.uri.spec, "Checking first browser tab open is equal to page A"); - is(gPageB.uri.spec, gTabOpenPageB.uri.spec, "Checking second browser tab open is equal to page B"); - - // test document access - var test1 = gPageA.document.getElementById("test1"); - ok(test1, "Checking existence of element in content DOM"); - is(test1.innerHTML, "A", "Checking content of element in content DOM"); - - // test moving tab - is(gTabMoveCount, 0, "Checking initial tab move count"); - - // move the tab - gPageA.moveToEnd(); - is(gPageA.index, 2, "Checking index after moving tab"); - - // check event - is(gTabMoveCount, 1, "Checking event handler for tab move"); - - gBrowser.addProgressListener({ - onStateChange: function (webProgress, request, stateFlags, status) { - info("onStateChange: " + stateFlags); - - const complete = Ci.nsIWebProgressListener.STATE_IS_WINDOW + - Ci.nsIWebProgressListener.STATE_IS_NETWORK + - Ci.nsIWebProgressListener.STATE_STOP; - if ((stateFlags & complete) == complete) { - gBrowser.removeProgressListener(this); - onPageBLoadComplete(); - } - }, - onLocationChange: () => 0, - onProgressChange: () => 0, - onStatusChange: () => 0, - onSecurityChange: () => 0 - }); - - // test loading new content with a frame into a tab - // the event will be checked in onPageBLoadComplete - gPageB.events.addListener("load", onPageBLoadWithFrames); - gPageB.load(makeURI(CHROMEROOT + "ContentWithFrames.html")); - } - - function onPageBLoadWithFrames(event) { - gPageLoadCount++; - info("onPageBLoadWithFrames: " + gPageLoadCount); - } - - function onPageBLoadComplete() { - gPageB.events.removeListener("load", onPageBLoadWithFrames); - // check page load with frame event - is(gPageLoadCount, 1, "Checking load count after loading new content with a frame"); - - // test loading new content into a tab - // the event will be checked in onPageASecondLoad - gPageA.events.addListener("load", onPageASecondLoad); - gPageA.load(makeURI(CHROMEROOT + "ContentB.html")); - } - - function onPageASecondLoad(event) { - gPageA.events.removeListener("load", onPageASecondLoad); - is(gPageA.uri.spec, CHROMEROOT + "ContentB.html", "Checking 'BrowserTab.uri' after loading new content"); - - // start testing closing tabs - // the event will be checked in afterClose - // use executeSoon so the onPageASecondLoad - // has a chance to finish first - gPageA.close(); - gPageB.close(); - - is(gTabCloseCount, 2, "Checking that tabs closed"); - is(activeWin.tabs.length, 1, "Checking length of 'Browser.tabs' after closing 2 tabs"); - finish(); - } -} -function onTabOpen(event) { - gTabOpenCount++; - - // cache these values so we can check them later (after loading completes) - if (gTabOpenCount == 1) - gTabOpenPageA = event.data; - - if (gTabOpenCount == 2) - gTabOpenPageB = event.data; -} -function onTabClose(event) { - gTabCloseCount++; -} - -function onTabMove(event) { - gTabMoveCount++; -} diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index f19aa058b9e..631a68c9ada 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -248,7 +248,6 @@ @RESPATH@/components/filepicker.xpt #endif @RESPATH@/components/find.xpt -@RESPATH@/browser/components/fuel.xpt @RESPATH@/components/gfx.xpt @RESPATH@/components/html5.xpt @RESPATH@/components/htmlparser.xpt @@ -373,8 +372,6 @@ @RESPATH@/browser/components/BrowserFeeds.manifest @RESPATH@/browser/components/FeedConverter.js @RESPATH@/browser/components/FeedWriter.js -@RESPATH@/browser/components/fuelApplication.manifest -@RESPATH@/browser/components/fuelApplication.js @RESPATH@/browser/components/WebContentConverter.js @RESPATH@/browser/components/BrowserComponents.manifest @RESPATH@/browser/components/nsBrowserContentHandler.js diff --git a/browser/moz.build b/browser/moz.build index 115e2469dee..c196909fce0 100644 --- a/browser/moz.build +++ b/browser/moz.build @@ -12,7 +12,6 @@ DIRS += [ 'base', 'components', 'experiments', - 'fuel', 'locales', 'modules', 'themes', diff --git a/mobile/android/b2gdroid/installer/package-manifest.in b/mobile/android/b2gdroid/installer/package-manifest.in index 8fe3593b3b8..2f214ad0771 100644 --- a/mobile/android/b2gdroid/installer/package-manifest.in +++ b/mobile/android/b2gdroid/installer/package-manifest.in @@ -185,7 +185,6 @@ @BINPATH@/components/fastfind.xpt @BINPATH@/components/feeds.xpt @BINPATH@/components/find.xpt -@BINPATH@/components/fuel.xpt @BINPATH@/components/gfx.xpt @BINPATH@/components/html5.xpt @BINPATH@/components/htmlparser.xpt @@ -318,8 +317,6 @@ @BINPATH@/components/PermissionSettings.manifest @BINPATH@/components/PermissionPromptService.js @BINPATH@/components/PermissionPromptService.manifest -@BINPATH@/components/fuelApplication.manifest -@BINPATH@/components/fuelApplication.js @BINPATH@/components/WebContentConverter.js @BINPATH@/components/BrowserComponents.manifest @BINPATH@/components/nsBrowserContentHandler.js From 39748ca73a89331b64e86ac7947b028da6cea926 Mon Sep 17 00:00:00 2001 From: Shane Tomlinson Date: Mon, 21 Dec 2015 10:56:19 +0000 Subject: [PATCH 11/28] Bug 1234165 - Load FxA's settings page with context and service query params. r=markh If a user must re-authenticate with FxA to view the settings page, a `login` message needs to be sent to the browser with the updated credentials. This does not occur for the settings page because `context` and `service` are not specified. --- browser/app/profile/firefox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 2315fdbaae4..3c65a2f0b99 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1469,7 +1469,7 @@ pref("identity.fxaccounts.remote.webchannel.uri", "https://accounts.firefox.com/ // The URL we take the user to when they opt to "manage" their Firefox Account. // Note that this will always need to be in the same TLD as the // "identity.fxaccounts.remote.signup.uri" pref. -pref("identity.fxaccounts.settings.uri", "https://accounts.firefox.com/settings"); +pref("identity.fxaccounts.settings.uri", "https://accounts.firefox.com/settings?service=sync&context=fx_desktop_v3"); // The remote URL of the FxA Profile Server pref("identity.fxaccounts.remote.profile.uri", "https://profile.accounts.firefox.com/v1"); From ba52d3f7c0602f652b5ad8c93bab3a398477e83d Mon Sep 17 00:00:00 2001 From: Julian Descottes Date: Wed, 20 Jan 2016 23:21:54 +0100 Subject: [PATCH 12/28] Bug 1241070 - refresh dominatortreeitem if expand state changed;r=fitzgen In DominatorTreeItem check props.expanded in shouldComponentUpdate. Added integration/sanity test to check that a node can be collapsed and expanded using mouse events. Added a DominatorTree component unit test to check the expanded nodes are correctly updated. --- .../memory/components/dominator-tree-item.js | 1 + .../memory/components/dominator-tree.js | 1 + .../client/memory/test/browser/browser.ini | 1 + .../browser_memory_dominator_trees_02.js | 64 ++++++++++++++++ devtools/client/memory/test/chrome/chrome.ini | 1 + devtools/client/memory/test/chrome/head.js | 16 ++-- .../test/chrome/test_DominatorTree_03.html | 75 +++++++++++++++++++ 7 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 devtools/client/memory/test/browser/browser_memory_dominator_trees_02.js create mode 100644 devtools/client/memory/test/chrome/test_DominatorTree_03.html diff --git a/devtools/client/memory/components/dominator-tree-item.js b/devtools/client/memory/components/dominator-tree-item.js index d3e98fbfffc..e96faa13a32 100644 --- a/devtools/client/memory/components/dominator-tree-item.js +++ b/devtools/client/memory/components/dominator-tree-item.js @@ -31,6 +31,7 @@ const DominatorTreeItem = module.exports = createClass({ shouldComponentUpdate(nextProps, nextState) { return this.props.item != nextProps.item || this.props.depth != nextProps.depth + || this.props.expanded != nextProps.expanded || this.props.focused != nextProps.focused; }, diff --git a/devtools/client/memory/components/dominator-tree.js b/devtools/client/memory/components/dominator-tree.js index b96ca32cb36..a7bfb34f171 100644 --- a/devtools/client/memory/components/dominator-tree.js +++ b/devtools/client/memory/components/dominator-tree.js @@ -201,6 +201,7 @@ const DominatorTree = module.exports = createClass({ depth, focused, arrow, + expanded, getPercentSize: size => (size / dominatorTree.root.retainedSize) * 100, onViewSourceInDebugger, }) diff --git a/devtools/client/memory/test/browser/browser.ini b/devtools/client/memory/test/browser/browser.ini index 86b011a5997..474630ba2d4 100644 --- a/devtools/client/memory/test/browser/browser.ini +++ b/devtools/client/memory/test/browser/browser.ini @@ -12,6 +12,7 @@ support-files = [browser_memory_diff_01.js] skip-if = debug # bug 1219554 [browser_memory_dominator_trees_01.js] +[browser_memory_dominator_trees_02.js] [browser_memory_filter_01.js] skip-if = debug # bug 1219554 [browser_memory_no_allocation_stacks.js] diff --git a/devtools/client/memory/test/browser/browser_memory_dominator_trees_02.js b/devtools/client/memory/test/browser/browser_memory_dominator_trees_02.js new file mode 100644 index 00000000000..72bc52567f0 --- /dev/null +++ b/devtools/client/memory/test/browser/browser_memory_dominator_trees_02.js @@ -0,0 +1,64 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Integration test for mouse interaction in the dominator tree + +"use strict"; + +const { + dominatorTreeState, + viewState, +} = require("devtools/client/memory/constants"); +const { changeView } = require("devtools/client/memory/actions/view"); + +const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html"; + +function clickOnNodeArrow(node, panel) { + EventUtils.synthesizeMouseAtCenter(node.querySelector(".arrow"), + {}, panel.panelWin); +} + +this.test = makeMemoryTest(TEST_URL, function* ({ panel }) { + // Taking snapshots and computing dominator trees is slow :-/ + requestLongerTimeout(4); + + const store = panel.panelWin.gStore; + const { getState, dispatch } = store; + const doc = panel.panelWin.document; + + dispatch(changeView(viewState.DOMINATOR_TREE)); + + // Take a snapshot. + const takeSnapshotButton = doc.getElementById("take-snapshot"); + EventUtils.synthesizeMouseAtCenter(takeSnapshotButton, {}, panel.panelWin); + + // Wait for the dominator tree to be computed and fetched. + yield waitUntilState(store, state => + state.snapshots[0] && + state.snapshots[0].dominatorTree && + state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED); + ok(true, "Computed and fetched the dominator tree."); + + const root = getState().snapshots[0].dominatorTree.root; + ok(getState().snapshots[0].dominatorTree.expanded.has(root.nodeId), + "Root node is expanded by default"); + + // Click on root arrow to collapse the root element + const rootNode = doc.querySelector(`.node-${root.nodeId}`); + clickOnNodeArrow(rootNode, panel); + + yield waitUntilState(store, state => + state.snapshots[0] && + state.snapshots[0].dominatorTree && + !state.snapshots[0].dominatorTree.expanded.has(root.nodeId)); + ok(true, "Root node collapsed"); + + // Click on root arrow to expand it again + clickOnNodeArrow(rootNode, panel); + + yield waitUntilState(store, state => + state.snapshots[0] && + state.snapshots[0].dominatorTree && + state.snapshots[0].dominatorTree.expanded.has(root.nodeId)); + ok(true, "Root node is expanded again"); +}); diff --git a/devtools/client/memory/test/chrome/chrome.ini b/devtools/client/memory/test/chrome/chrome.ini index 501b0d885dc..553a534d5c0 100644 --- a/devtools/client/memory/test/chrome/chrome.ini +++ b/devtools/client/memory/test/chrome/chrome.ini @@ -4,6 +4,7 @@ support-files = [test_DominatorTree_01.html] [test_DominatorTree_02.html] +[test_DominatorTree_03.html] [test_DominatorTreeItem_01.html] [test_Heap_01.html] [test_Heap_02.html] diff --git a/devtools/client/memory/test/chrome/head.js b/devtools/client/memory/test/chrome/head.js index a0b2fb7798a..efedf600350 100644 --- a/devtools/client/memory/test/chrome/head.js +++ b/devtools/client/memory/test/chrome/head.js @@ -174,12 +174,18 @@ function onNextAnimationFrame(fn) { requestAnimationFrame(fn)); } -function renderComponent(component, container) { +/** + * Render the provided ReactElement in the provided HTML container. + * Returns a Promise that will resolve the rendered element as a React + * component. + */ +function renderComponent(element, container) { return new Promise(resolve => { - ReactDOM.render(component, container, onNextAnimationFrame(() => { - dumpn("Rendered = " + container.innerHTML); - resolve(); - })); + let component = ReactDOM.render(element, container, + onNextAnimationFrame(() => { + dumpn("Rendered = " + container.innerHTML); + resolve(component); + })); }); } diff --git a/devtools/client/memory/test/chrome/test_DominatorTree_03.html b/devtools/client/memory/test/chrome/test_DominatorTree_03.html new file mode 100644 index 00000000000..e9656dad84f --- /dev/null +++ b/devtools/client/memory/test/chrome/test_DominatorTree_03.html @@ -0,0 +1,75 @@ + + + + + + Tree component test + + + + + +
+ +
+        
+        
+    
+ + From eab0e329eb7b4e0b869e377773c68931b6b87057 Mon Sep 17 00:00:00 2001 From: Gijs Kruitbosch Date: Fri, 11 Dec 2015 08:17:56 -0500 Subject: [PATCH 13/28] Bug 1172165 - test changes: remove broken bit of test for view-source linking now that it is completely disallowed, and fix html parser's view-source test, r=bz --- docshell/test/test_bug369814.html | 12 ----- parser/htmlparser/moz.build | 1 + parser/htmlparser/tests/mochitest/browser.ini | 7 +++ .../tests/mochitest/browser_viewsource.js | 22 ++++++++ .../tests/mochitest/file_viewsource.html | 18 +++++++ .../htmlparser/tests/mochitest/mochitest.ini | 1 - .../tests/mochitest/test_viewsource.html | 50 ------------------- 7 files changed, 48 insertions(+), 63 deletions(-) create mode 100644 parser/htmlparser/tests/mochitest/browser.ini create mode 100644 parser/htmlparser/tests/mochitest/browser_viewsource.js create mode 100644 parser/htmlparser/tests/mochitest/file_viewsource.html delete mode 100644 parser/htmlparser/tests/mochitest/test_viewsource.html diff --git a/docshell/test/test_bug369814.html b/docshell/test/test_bug369814.html index 27068b4a092..8667710555d 100644 --- a/docshell/test/test_bug369814.html +++ b/docshell/test/test_bug369814.html @@ -164,18 +164,6 @@ var gTests = [ "pokes" : { }, "func" : anchorTest, }, - { "name" : "iframes.html loaded from view-source jar type, pref disabled", - "url" : "jar:view-source:http://mochi.test:8888/tests/docshell/test/bug369814.jar!/iframes.html", - "pref" : false, - "pokes" : { }, - "func" : loadErrorTest - }, - { "name" : "iframes.html loaded from view-source jar type, pref enabled", - "url" : "jar:view-source:http://mochi.test:8888/tests/docshell/test/bug369814.jar!/iframes.html", - "pref" : true, - "pokes" : { }, - "func" : loadErrorTest - }, ]; var gNextTest = 0; diff --git a/parser/htmlparser/moz.build b/parser/htmlparser/moz.build index d0b5eb0fd40..740f3e6ea5f 100644 --- a/parser/htmlparser/moz.build +++ b/parser/htmlparser/moz.build @@ -5,6 +5,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini'] +BROWSER_CHROME_MANIFESTS += ['tests/mochitest/browser.ini'] XPIDL_SOURCES += [ 'nsIExpatSink.idl', diff --git a/parser/htmlparser/tests/mochitest/browser.ini b/parser/htmlparser/tests/mochitest/browser.ini new file mode 100644 index 00000000000..c3f493ebd62 --- /dev/null +++ b/parser/htmlparser/tests/mochitest/browser.ini @@ -0,0 +1,7 @@ +[DEFAULT] +skip-if = buildapp == 'b2g' + +[browser_viewsource.js] +support-files = + file_viewsource.html + diff --git a/parser/htmlparser/tests/mochitest/browser_viewsource.js b/parser/htmlparser/tests/mochitest/browser_viewsource.js new file mode 100644 index 00000000000..2e45e81a9d5 --- /dev/null +++ b/parser/htmlparser/tests/mochitest/browser_viewsource.js @@ -0,0 +1,22 @@ +"use strict"; + +add_task(function*() { + const PAGE_URL = getRootDirectory(gTestPath) + "file_viewsource.html"; + let viewSourceTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "view-source:" + PAGE_URL); + + let xhrPromise = new Promise(resolve => { + let xhr = new XMLHttpRequest(); + xhr.open("GET", PAGE_URL, true); + xhr.onload = event => resolve(event.target.responseText); + xhr.send(); + }); + + let viewSourceContentPromise = ContentTask.spawn(viewSourceTab.linkedBrowser, null, function*() { + return content.document.body.textContent; + }); + + let results = yield Promise.all([viewSourceContentPromise, xhrPromise]); + is(results[0], results[1], "Sources should match"); + yield BrowserTestUtils.removeTab(viewSourceTab); +}); + diff --git a/parser/htmlparser/tests/mochitest/file_viewsource.html b/parser/htmlparser/tests/mochitest/file_viewsource.html new file mode 100644 index 00000000000..3ed00150a78 --- /dev/null +++ b/parser/htmlparser/tests/mochitest/file_viewsource.html @@ -0,0 +1,18 @@ + + + + Test for view source + + + + + + + + + + diff --git a/parser/htmlparser/tests/mochitest/mochitest.ini b/parser/htmlparser/tests/mochitest/mochitest.ini index fd8f652a87b..fdef5aa6766 100644 --- a/parser/htmlparser/tests/mochitest/mochitest.ini +++ b/parser/htmlparser/tests/mochitest/mochitest.ini @@ -140,7 +140,6 @@ skip-if = toolkit == 'android' #TIMED_OUT [test_html5_tree_construction_part2.html] skip-if = toolkit == 'android' #TIMED_OUT [test_img_picture_preload.html] -[test_viewsource.html] [test_xml_mislabeled.html] # Disabled test due to orange on Linux # test_bug568470.html diff --git a/parser/htmlparser/tests/mochitest/test_viewsource.html b/parser/htmlparser/tests/mochitest/test_viewsource.html deleted file mode 100644 index 42721954a46..00000000000 --- a/parser/htmlparser/tests/mochitest/test_viewsource.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - Test for view source - - - - - - - - - - - - From 5364db3fb370d27f194e49ce005b8f920972c296 Mon Sep 17 00:00:00 2001 From: Gijs Kruitbosch Date: Fri, 11 Dec 2015 08:06:41 -0500 Subject: [PATCH 14/28] Bug 1172165 - check all nested URI schemes in CAPS. Make view-source dangerous to load, and about: URIs use per-URI flags so they keep working, r=bz Also, add an opt-out for crashtest/reftest for the view-source thing so they don't all break, r=bz --- caps/nsScriptSecurityManager.cpp | 61 +++++++++++++++++-- layout/tools/reftest/reftest-preferences.js | 4 ++ modules/libpref/init/all.js | 4 ++ .../protocol/about/nsAboutProtocolHandler.cpp | 30 ++++++++- .../protocol/about/nsAboutProtocolHandler.h | 4 +- .../viewsource/nsViewSourceHandler.cpp | 2 +- netwerk/test/mochitests/mochitest.ini | 1 + .../test_viewsource_unlinkable.html | 27 ++++++++ 8 files changed, 126 insertions(+), 7 deletions(-) create mode 100644 netwerk/test/mochitests/test_viewsource_unlinkable.html diff --git a/caps/nsScriptSecurityManager.cpp b/caps/nsScriptSecurityManager.cpp index 49fb4e7601b..febd4de1222 100644 --- a/caps/nsScriptSecurityManager.cpp +++ b/caps/nsScriptSecurityManager.cpp @@ -629,6 +629,42 @@ EqualOrSubdomain(nsIURI* aProbeArg, nsIURI* aBase) } } +static bool +AllSchemesMatch(nsIURI* aURI, nsIURI* aOtherURI) +{ + nsCOMPtr nestedURI = do_QueryInterface(aURI); + nsCOMPtr nestedOtherURI = do_QueryInterface(aOtherURI); + auto stringComparator = nsCaseInsensitiveCStringComparator(); + if (!nestedURI && !nestedOtherURI) { + // Neither of the URIs is nested, compare their schemes directly: + nsAutoCString scheme, otherScheme; + aURI->GetScheme(scheme); + aOtherURI->GetScheme(otherScheme); + return scheme.Equals(otherScheme, stringComparator); + } + while (nestedURI && nestedOtherURI) { + nsCOMPtr currentURI = do_QueryInterface(nestedURI); + nsCOMPtr currentOtherURI = do_QueryInterface(nestedOtherURI); + nsAutoCString scheme, otherScheme; + currentURI->GetScheme(scheme); + currentOtherURI->GetScheme(otherScheme); + if (!scheme.Equals(otherScheme, stringComparator)) { + return false; + } + + nestedURI->GetInnerURI(getter_AddRefs(currentURI)); + nestedOtherURI->GetInnerURI(getter_AddRefs(currentOtherURI)); + nestedURI = do_QueryInterface(currentURI); + nestedOtherURI = do_QueryInterface(currentOtherURI); + } + if (!!nestedURI != !!nestedOtherURI) { + // If only one of the scheme chains runs out at one point, clearly the chains + // aren't of the same length, so we bail: + return false; + } + return true; +} + NS_IMETHODIMP nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal, nsIURI *aTargetURI, @@ -729,14 +765,30 @@ nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal, rv = sourceBaseURI->GetScheme(sourceScheme); if (NS_FAILED(rv)) return rv; + // When comparing schemes, if the relevant pref is set, view-source URIs + // are reachable from same-protocol (so e.g. file: can link to + // view-source:file). This is required for reftests. + static bool sViewSourceReachableFromInner = false; + static bool sCachedViewSourcePref = false; + if (!sCachedViewSourcePref) { + sCachedViewSourcePref = true; + mozilla::Preferences::AddBoolVarCache(&sViewSourceReachableFromInner, + "security.view-source.reachable-from-inner-protocol"); + } + + bool targetIsViewSource = false; + if (sourceScheme.LowerCaseEqualsLiteral(NS_NULLPRINCIPAL_SCHEME)) { // A null principal can target its own URI. if (sourceURI == aTargetURI) { return NS_OK; } } - else if (targetScheme.Equals(sourceScheme, - nsCaseInsensitiveCStringComparator())) + else if (AllSchemesMatch(sourceURI, aTargetURI) || + (sViewSourceReachableFromInner && + sourceScheme.EqualsIgnoreCase(targetScheme.get()) && + NS_SUCCEEDED(aTargetURI->SchemeIs("view-source", &targetIsViewSource)) && + targetIsViewSource)) { // every scheme can access another URI from the same scheme, // as long as they don't represent null principals... @@ -785,7 +837,7 @@ nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal, // at the flags for our one URI. // Check for system target URI - rv = DenyAccessIfURIHasFlags(targetBaseURI, + rv = DenyAccessIfURIHasFlags(aTargetURI, nsIProtocolHandler::URI_DANGEROUS_TO_LOAD); if (NS_FAILED(rv)) { // Deny access, since the origin principal is not system @@ -853,7 +905,7 @@ nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal, } // Check for target URI pointing to a file - rv = NS_URIChainHasFlags(targetBaseURI, + rv = NS_URIChainHasFlags(aTargetURI, nsIProtocolHandler::URI_IS_LOCAL_FILE, &hasFlags); NS_ENSURE_SUCCESS(rv, rv); @@ -1639,3 +1691,4 @@ nsScriptSecurityManager::PolicyAllowsScript(nsIURI* aURI, bool *aRv) return NS_OK; } + diff --git a/layout/tools/reftest/reftest-preferences.js b/layout/tools/reftest/reftest-preferences.js index ecf2ab111fe..bfc1569ae9f 100644 --- a/layout/tools/reftest/reftest-preferences.js +++ b/layout/tools/reftest/reftest-preferences.js @@ -64,3 +64,7 @@ // Allow XUL and XBL files to be opened from file:// URIs branch.setBoolPref("dom.allow_XUL_XBL_for_file", true); + + // Allow view-source URIs to be opened from URIs that share + // their protocol with the inner URI of the view-source URI + branch.setBoolPref("security.view-source.reachable-from-inner-protocol", true); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 8f8c822188e..274ac474d18 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -2025,6 +2025,10 @@ pref("security.cert_pinning.enforcement_level", 0); // for tests. pref("security.cert_pinning.process_headers_from_non_builtin_roots", false); +// If set to true, allow view-source URIs to be opened from URIs that share +// their protocol with the inner URI of the view-source URI +pref("security.view-source.reachable-from-inner-protocol", false); + // Modifier key prefs: default to Windows settings, // menu access key = alt, accelerator key = control. // Use 17 for Ctrl, 18 for Alt, 224 for Meta, 91 for Win, 0 for none. Mac settings in macprefs.js diff --git a/netwerk/protocol/about/nsAboutProtocolHandler.cpp b/netwerk/protocol/about/nsAboutProtocolHandler.cpp index 70536345a12..91664952e0c 100644 --- a/netwerk/protocol/about/nsAboutProtocolHandler.cpp +++ b/netwerk/protocol/about/nsAboutProtocolHandler.cpp @@ -39,7 +39,8 @@ static bool IsSafeToLinkForUntrustedContent(nsIAboutModule *aModule, nsIURI *aUR } //////////////////////////////////////////////////////////////////////////////// -NS_IMPL_ISUPPORTS(nsAboutProtocolHandler, nsIProtocolHandler, nsISupportsWeakReference) +NS_IMPL_ISUPPORTS(nsAboutProtocolHandler, nsIProtocolHandler, + nsIProtocolHandlerWithDynamicFlags, nsISupportsWeakReference) //////////////////////////////////////////////////////////////////////////////// // nsIProtocolHandler methods: @@ -65,6 +66,33 @@ nsAboutProtocolHandler::GetProtocolFlags(uint32_t *result) return NS_OK; } +NS_IMETHODIMP +nsAboutProtocolHandler::GetFlagsForURI(nsIURI* aURI, uint32_t* aFlags) +{ + // First use the default (which is "unsafe for content"): + GetProtocolFlags(aFlags); + + // Now try to see if this URI overrides the default: + nsCOMPtr aboutMod; + nsresult rv = NS_GetAboutModule(aURI, getter_AddRefs(aboutMod)); + if (NS_FAILED(rv)) { + // Swallow this and just tell the consumer the default: + return NS_OK; + } + uint32_t aboutModuleFlags = 0; + rv = aboutMod->GetURIFlags(aURI, &aboutModuleFlags); + // This should never happen, so pass back the error: + NS_ENSURE_SUCCESS(rv, rv); + + // If marked as safe, and not marked unlinkable, pass 'safe' flags. + if ((aboutModuleFlags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT) && + !(aboutModuleFlags & nsIAboutModule::MAKE_UNLINKABLE)) { + *aFlags = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE | + URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT; + } + return NS_OK; +} + NS_IMETHODIMP nsAboutProtocolHandler::NewURI(const nsACString &aSpec, const char *aCharset, // ignore charset info diff --git a/netwerk/protocol/about/nsAboutProtocolHandler.h b/netwerk/protocol/about/nsAboutProtocolHandler.h index 007975ad46d..f3e9e19291a 100644 --- a/netwerk/protocol/about/nsAboutProtocolHandler.h +++ b/netwerk/protocol/about/nsAboutProtocolHandler.h @@ -13,7 +13,8 @@ class nsIURI; -class nsAboutProtocolHandler : public nsIProtocolHandler +class nsAboutProtocolHandler : public nsIProtocolHandlerWithDynamicFlags + , public nsIProtocolHandler , public nsSupportsWeakReference { public: @@ -21,6 +22,7 @@ public: // nsIProtocolHandler methods: NS_DECL_NSIPROTOCOLHANDLER + NS_DECL_NSIPROTOCOLHANDLERWITHDYNAMICFLAGS // nsAboutProtocolHandler methods: nsAboutProtocolHandler() {} diff --git a/netwerk/protocol/viewsource/nsViewSourceHandler.cpp b/netwerk/protocol/viewsource/nsViewSourceHandler.cpp index 5f8932b50e8..b346c2e900f 100644 --- a/netwerk/protocol/viewsource/nsViewSourceHandler.cpp +++ b/netwerk/protocol/viewsource/nsViewSourceHandler.cpp @@ -35,7 +35,7 @@ nsViewSourceHandler::GetDefaultPort(int32_t *result) NS_IMETHODIMP nsViewSourceHandler::GetProtocolFlags(uint32_t *result) { - *result = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE | + *result = URI_NORELATIVE | URI_NOAUTH | URI_DANGEROUS_TO_LOAD | URI_NON_PERSISTABLE; return NS_OK; } diff --git a/netwerk/test/mochitests/mochitest.ini b/netwerk/test/mochitests/mochitest.ini index da5a4f8acb7..d7e984618ee 100644 --- a/netwerk/test/mochitests/mochitest.ini +++ b/netwerk/test/mochitests/mochitest.ini @@ -25,6 +25,7 @@ skip-if = e10s [test_user_agent_updates.html] skip-if = e10s [test_user_agent_updates_reset.html] +[test_viewsource_unlinkable.html] [test_xhr_method_case.html] [test_signed_web_packaged_app.html] skip-if = e10s || buildapp != 'browser' diff --git a/netwerk/test/mochitests/test_viewsource_unlinkable.html b/netwerk/test/mochitests/test_viewsource_unlinkable.html new file mode 100644 index 00000000000..9a1a35d682d --- /dev/null +++ b/netwerk/test/mochitests/test_viewsource_unlinkable.html @@ -0,0 +1,27 @@ + + + + + Test for view-source linkability + + + + + + +

+ +
+
+ + + + From b618c233947124708327e4c377db0fde2e58d892 Mon Sep 17 00:00:00 2001 From: vivek Date: Thu, 28 Jan 2016 20:02:20 +0200 Subject: [PATCH 15/28] Bug 1241846 - Remove unused sync xml resources. --- .../src/main/res/layout/sync_account.xml | 80 ------------ .../src/main/res/layout/sync_list_item.xml | 34 ----- .../res/layout/sync_redirect_to_setup.xml | 34 ----- .../src/main/res/layout/sync_send_tab.xml | 51 -------- .../src/main/res/layout/sync_setup.xml | 94 -------------- .../main/res/layout/sync_setup_failure.xml | 48 ------- .../res/layout/sync_setup_jpake_waiting.xml | 43 ------ .../main/res/layout/sync_setup_nointernet.xml | 31 ----- .../src/main/res/layout/sync_setup_pair.xml | 85 ------------ .../main/res/layout/sync_setup_success.xml | 39 ------ .../main/res/layout/sync_setup_webview.xml | 11 -- .../main/res/values-large-v11/sync_styles.xml | 17 --- .../src/main/res/values-v11/sync_styles.xml | 31 ----- .../src/main/res/values/sync_styles.xml | 122 ------------------ 14 files changed, 720 deletions(-) delete mode 100644 mobile/android/services/src/main/res/layout/sync_account.xml delete mode 100644 mobile/android/services/src/main/res/layout/sync_list_item.xml delete mode 100644 mobile/android/services/src/main/res/layout/sync_redirect_to_setup.xml delete mode 100644 mobile/android/services/src/main/res/layout/sync_send_tab.xml delete mode 100644 mobile/android/services/src/main/res/layout/sync_setup.xml delete mode 100644 mobile/android/services/src/main/res/layout/sync_setup_failure.xml delete mode 100644 mobile/android/services/src/main/res/layout/sync_setup_jpake_waiting.xml delete mode 100644 mobile/android/services/src/main/res/layout/sync_setup_nointernet.xml delete mode 100644 mobile/android/services/src/main/res/layout/sync_setup_pair.xml delete mode 100644 mobile/android/services/src/main/res/layout/sync_setup_success.xml delete mode 100644 mobile/android/services/src/main/res/layout/sync_setup_webview.xml delete mode 100644 mobile/android/services/src/main/res/values-large-v11/sync_styles.xml delete mode 100644 mobile/android/services/src/main/res/values-v11/sync_styles.xml delete mode 100644 mobile/android/services/src/main/res/values/sync_styles.xml diff --git a/mobile/android/services/src/main/res/layout/sync_account.xml b/mobile/android/services/src/main/res/layout/sync_account.xml deleted file mode 100644 index 121dcaff3f4..00000000000 --- a/mobile/android/services/src/main/res/layout/sync_account.xml +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -