From 10e97429b9d177d0b1755b012239ca891dcc4729 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Thu, 5 Apr 2012 23:26:06 -0700 Subject: [PATCH 1/6] Bug 731494 - Refactor generic code from services/sync into services/common; r=rnewman --HG-- rename : services/sync/modules/async.js => services/common/async.js rename : services/sync/modules/log4moz.js => services/common/log4moz.js rename : services/sync/modules/ext/Observers.js => services/common/observers.js rename : services/sync/modules/ext/Preferences.js => services/common/preferences.js rename : services/sync/modules/ext/StringBundle.js => services/common/stringbundle.js rename : services/sync/tests/unit/test_async_chain.js => services/common/tests/unit/test_async_chain.js rename : services/sync/tests/unit/test_async_querySpinningly.js => services/common/tests/unit/test_async_querySpinningly.js rename : services/sync/tests/unit/test_log4moz.js => services/common/tests/unit/test_log4moz.js rename : services/sync/tests/unit/test_Observers.js => services/common/tests/unit/test_observers.js rename : services/sync/tests/unit/test_Preferences.js => services/common/tests/unit/test_preferences.js rename : services/sync/tests/unit/test_restrequest.js => services/common/tests/unit/test_restrequest.js rename : services/sync/tests/unit/test_utils_makeURI.js => services/common/tests/unit/test_utils_makeURI.js rename : services/sync/tests/unit/test_utils_namedTimer.js => services/common/tests/unit/test_utils_namedTimer.js rename : services/sync/tests/unit/test_utils_stackTrace.js => services/common/tests/unit/test_utils_stackTrace.js --- browser/base/content/sync/notification.xml | 4 +- browser/installer/removed-files.in | 7 + services/Makefile.in | 2 +- services/common/Makefile.in | 33 + services/{sync/modules => common}/async.js | 50 +- services/{sync/modules => common}/log4moz.js | 19 +- .../ext/Observers.js => common/observers.js} | 0 .../Preferences.js => common/preferences.js} | 0 services/common/rest.js | 578 ++++++++++++++++++ services/common/services-common.js | 5 + .../stringbundle.js} | 7 +- services/common/tests/Makefile.in | 16 + services/common/tests/unit/head_global.js | 54 ++ services/common/tests/unit/head_helpers.js | 173 ++++++ .../tests/unit/test_async_chain.js | 5 +- .../tests/unit/test_async_querySpinningly.js | 17 +- .../common/tests/unit/test_load_modules.js | 18 + .../tests/unit/test_log4moz.js | 6 +- .../tests/unit/test_observers.js} | 31 +- .../tests/unit/test_preferences.js} | 183 +++--- .../tests/unit/test_restrequest.js | 22 +- .../common/tests/unit/test_utils_makeURI.js | 66 ++ .../tests/unit/test_utils_namedTimer.js | 18 +- .../tests/unit/test_utils_stackTrace.js | 13 +- services/common/tests/unit/xpcshell.ini | 17 + services/common/utils.js | 125 ++++ services/makefiles.sh | 14 +- services/sync/SyncComponents.manifest | 1 + services/sync/modules/addonsreconciler.js | 2 +- services/sync/modules/engines.js | 6 +- services/sync/modules/engines/addons.js | 4 +- services/sync/modules/engines/bookmarks.js | 2 +- services/sync/modules/engines/clients.js | 2 +- services/sync/modules/engines/forms.js | 4 +- services/sync/modules/engines/history.js | 4 +- services/sync/modules/engines/prefs.js | 2 +- services/sync/modules/engines/tabs.js | 2 +- services/sync/modules/identity.js | 2 +- services/sync/modules/jpakeclient.js | 4 +- services/sync/modules/keys.js | 2 +- services/sync/modules/notifications.js | 4 +- services/sync/modules/policies.js | 2 +- services/sync/modules/record.js | 2 +- services/sync/modules/resource.js | 8 +- services/sync/modules/rest.js | 571 +---------------- services/sync/modules/service.js | 4 +- services/sync/modules/status.js | 2 +- services/sync/modules/util.js | 139 +---- services/sync/tests/unit/head_appinfo.js | 10 +- services/sync/tests/unit/head_helpers.js | 81 +-- services/sync/tests/unit/head_http_server.js | 98 +-- .../sync/tests/unit/test_addons_engine.js | 4 +- services/sync/tests/unit/test_addons_store.js | 2 +- .../sync/tests/unit/test_bookmark_engine.js | 4 +- ..._bookmark_legacy_microsummaries_support.js | 2 +- .../tests/unit/test_bookmark_livemarks.js | 2 +- .../sync/tests/unit/test_bookmark_record.js | 2 +- .../unit/test_bookmark_smart_bookmarks.js | 2 +- services/sync/tests/unit/test_corrupt_keys.js | 2 +- services/sync/tests/unit/test_engine.js | 2 +- .../tests/unit/test_errorhandler_filelog.js | 2 +- .../sync/tests/unit/test_forms_tracker.js | 2 +- .../sync/tests/unit/test_history_store.js | 2 +- .../sync/tests/unit/test_httpd_sync_server.js | 2 +- services/sync/tests/unit/test_jpakeclient.js | 5 +- services/sync/tests/unit/test_load_modules.js | 4 - .../sync/tests/unit/test_node_reassignment.js | 4 +- .../tests/unit/test_places_guid_downgrade.js | 2 +- services/sync/tests/unit/test_prefs_store.js | 2 +- .../sync/tests/unit/test_prefs_tracker.js | 2 +- .../sync/tests/unit/test_records_crypto.js | 2 +- services/sync/tests/unit/test_resource.js | 4 +- .../sync/tests/unit/test_resource_async.js | 4 +- .../tests/unit/test_service_changePassword.js | 2 +- .../tests/unit/test_service_detect_upgrade.js | 2 +- .../tests/unit/test_service_getStorageInfo.js | 2 +- .../sync/tests/unit/test_service_login.js | 2 +- .../tests/unit/test_service_migratePrefs.js | 2 +- .../sync/tests/unit/test_service_startup.js | 2 +- .../unit/test_service_sync_remoteSetup.js | 2 +- .../tests/unit/test_service_verifyLogin.js | 2 +- .../tests/unit/test_syncstoragerequest.js | 2 +- .../sync/tests/unit/test_utils_lazyStrings.js | 5 +- .../sync/tests/unit/test_utils_makeURI.js | 66 -- services/sync/tests/unit/xpcshell.ini | 13 +- .../tps/extensions/tps/modules/addons.jsm | 2 +- .../tps/extensions/tps/modules/bookmarks.jsm | 2 +- .../tps/extensions/tps/modules/history.jsm | 2 +- .../sync/tps/extensions/tps/modules/tps.jsm | 2 +- testing/xpcshell/xpcshell.ini | 1 + 90 files changed, 1403 insertions(+), 1208 deletions(-) create mode 100644 services/common/Makefile.in rename services/{sync/modules => common}/async.js (75%) rename services/{sync/modules => common}/log4moz.js (98%) rename services/{sync/modules/ext/Observers.js => common/observers.js} (100%) rename services/{sync/modules/ext/Preferences.js => common/preferences.js} (100%) create mode 100644 services/common/rest.js create mode 100644 services/common/services-common.js rename services/{sync/modules/ext/StringBundle.js => common/stringbundle.js} (97%) create mode 100644 services/common/tests/Makefile.in create mode 100644 services/common/tests/unit/head_global.js create mode 100644 services/common/tests/unit/head_helpers.js rename services/{sync => common}/tests/unit/test_async_chain.js (80%) rename services/{sync => common}/tests/unit/test_async_querySpinningly.js (85%) create mode 100644 services/common/tests/unit/test_load_modules.js rename services/{sync => common}/tests/unit/test_log4moz.js (96%) rename services/{sync/tests/unit/test_Observers.js => common/tests/unit/test_observers.js} (79%) rename services/{sync/tests/unit/test_Preferences.js => common/tests/unit/test_preferences.js} (84%) rename services/{sync => common}/tests/unit/test_restrequest.js (97%) create mode 100644 services/common/tests/unit/test_utils_makeURI.js rename services/{sync => common}/tests/unit/test_utils_namedTimer.js (76%) rename services/{sync => common}/tests/unit/test_utils_stackTrace.js (63%) create mode 100644 services/common/tests/unit/xpcshell.ini create mode 100644 services/common/utils.js delete mode 100644 services/sync/tests/unit/test_utils_makeURI.js diff --git a/browser/base/content/sync/notification.xml b/browser/base/content/sync/notification.xml index c7d37e579cd..d1868c345f1 100644 --- a/browser/base/content/sync/notification.xml +++ b/browser/base/content/sync/notification.xml @@ -57,7 +57,7 @@ diff --git a/browser/installer/removed-files.in b/browser/installer/removed-files.in index 6143cd5730c..fac098a8b82 100644 --- a/browser/installer/removed-files.in +++ b/browser/installer/removed-files.in @@ -1031,6 +1031,13 @@ xpicleanup@BIN_SUFFIX@ modules/PropertyPanel.jsm modules/reflect.jsm modules/Services.jsm + modules/services-common/async.js + modules/services-common/log4moz.js + modules/services-common/observers.js + modules/services-common/preferences.js + modules/services-common/rest.js + modules/services-common/stringbundle.js + modules/services-common/utils.js modules/services-sync/auth.js modules/services-sync/base_records/collection.js modules/services-sync/base_records/crypto.js diff --git a/services/Makefile.in b/services/Makefile.in index 538aa50e544..22523acedce 100644 --- a/services/Makefile.in +++ b/services/Makefile.in @@ -43,7 +43,7 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk ifdef MOZ_SERVICES_SYNC -PARALLEL_DIRS += crypto sync +PARALLEL_DIRS += common crypto sync endif include $(topsrcdir)/config/rules.mk diff --git a/services/common/Makefile.in b/services/common/Makefile.in new file mode 100644 index 00000000000..7a42949fdfb --- /dev/null +++ b/services/common/Makefile.in @@ -0,0 +1,33 @@ +# 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/. + +DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +PREF_JS_EXPORTS = $(srcdir)/services-common.js + +modules := \ + async.js \ + log4moz.js \ + observers.js \ + preferences.js \ + rest.js \ + stringbundle.js \ + utils.js \ + $(NULL) + +source_modules = $(foreach module,$(modules),$(srcdir)/$(module)) +module_dir = $(FINAL_TARGET)/modules/services-common + +libs:: + $(NSINSTALL) -D $(module_dir) + $(NSINSTALL) -l $(source_modules) $(module_dir) + +TEST_DIRS += tests + +include $(topsrcdir)/config/rules.mk diff --git a/services/sync/modules/async.js b/services/common/async.js similarity index 75% rename from services/sync/modules/async.js rename to services/common/async.js index 82ab9b04ed5..f2047a31b69 100644 --- a/services/sync/modules/async.js +++ b/services/common/async.js @@ -1,46 +1,10 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Firefox Sync. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Richard Newman - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ +/* 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 EXPORTED_SYMBOLS = ['Async']; +const EXPORTED_SYMBOLS = ["Async"]; -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; -const Cu = Components.utils; +const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components; // Constants for makeSyncCallback, waitForSyncCallback. const CB_READY = {}; @@ -61,10 +25,10 @@ let Async = { * other, passing the callback arguments on to the next one. All functions * must take a callback function as their last argument. The 'this' object * will be whatever chain()'s is. - * + * * @usage this._chain = Async.chain; * this._chain(this.foo, this.bar, this.baz)(args, for, foo) - * + * * This is equivalent to: * * let self = this; diff --git a/services/sync/modules/log4moz.js b/services/common/log4moz.js similarity index 98% rename from services/sync/modules/log4moz.js rename to services/common/log4moz.js index a2f2149ae96..1bce7cc2de1 100644 --- a/services/sync/modules/log4moz.js +++ b/services/common/log4moz.js @@ -39,10 +39,7 @@ const EXPORTED_SYMBOLS = ['Log4Moz']; -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; -const Cu = Components.utils; +const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components; const ONE_BYTE = 1; const ONE_KILOBYTE = 1024 * ONE_BYTE; @@ -373,7 +370,7 @@ BasicFormatter.prototype = { __proto__: Formatter.prototype, format: function BF_format(message) { - return message.time + "\t" + message.loggerName + "\t" + message.levelDesc + return message.time + "\t" + message.loggerName + "\t" + message.levelDesc + "\t" + message.message + "\n"; } }; @@ -442,7 +439,7 @@ ConsoleAppender.prototype = { /** * Base implementation for stream based appenders. - * + * * Caution: This writes to the output stream synchronously, thus logging calls * block as the data is written to the stream. This can have negligible impact * for in-memory streams, but should be taken into account for I/O streams @@ -460,7 +457,7 @@ BlockingStreamAppender.prototype = { /** * Output stream to write to. - * + * * This will automatically open the stream if it doesn't exist yet by * calling newOutputStream. The resulting raw stream is wrapped in a * nsIConverterOutputStream to ensure text is written as UTF-8. @@ -481,7 +478,7 @@ BlockingStreamAppender.prototype = { } this._converterStream.init( this._outputStream, "UTF-8", STREAM_SEGMENT_SIZE, - Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); + Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); } return this._converterStream; }, @@ -521,7 +518,7 @@ BlockingStreamAppender.prototype = { /** * Append to an nsIStorageStream - * + * * This writes logging output to an in-memory stream which can later be read * back as an nsIInputStream. It can be used to avoid expensive I/O operations * during logging. Instead, one can periodically consume the input stream and @@ -531,7 +528,7 @@ function StorageStreamAppender(formatter) { this._name = "StorageStreamAppender"; BlockingStreamAppender.call(this, formatter); } -StorageStreamAppender.prototype = { +StorageStreamAppender.prototype = { __proto__: BlockingStreamAppender.prototype, _ss: null, @@ -590,7 +587,7 @@ FileAppender.prototype = { /** * Rotating file appender (discouraged) - * + * * Similar to FileAppender, but rotates logs when they become too large. */ function RotatingFileAppender(file, formatter, maxSize, maxBackups) { diff --git a/services/sync/modules/ext/Observers.js b/services/common/observers.js similarity index 100% rename from services/sync/modules/ext/Observers.js rename to services/common/observers.js diff --git a/services/sync/modules/ext/Preferences.js b/services/common/preferences.js similarity index 100% rename from services/sync/modules/ext/Preferences.js rename to services/common/preferences.js diff --git a/services/common/rest.js b/services/common/rest.js new file mode 100644 index 00000000000..0dec0c6fee9 --- /dev/null +++ b/services/common/rest.js @@ -0,0 +1,578 @@ +/* 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 {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +const EXPORTED_SYMBOLS = ["RESTRequest"]; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://services-common/log4moz.js"); +Cu.import("resource://services-common/preferences.js"); +Cu.import("resource://services-common/utils.js"); + +const Prefs = new Preferences("services.common.rest."); + +/** + * Single use HTTP requests to RESTish resources. + * + * @param uri + * URI for the request. This can be an nsIURI object or a string + * that can be used to create one. An exception will be thrown if + * the string is not a valid URI. + * + * Examples: + * + * (1) Quick GET request: + * + * new RESTRequest("http://server/rest/resource").get(function (error) { + * if (error) { + * // Deal with a network error. + * processNetworkErrorCode(error.result); + * return; + * } + * if (!this.response.success) { + * // Bail out if we're not getting an HTTP 2xx code. + * processHTTPError(this.response.status); + * return; + * } + * processData(this.response.body); + * }); + * + * (2) Quick PUT request (non-string data is automatically JSONified) + * + * new RESTRequest("http://server/rest/resource").put(data, function (error) { + * ... + * }); + * + * (3) Streaming GET + * + * let request = new RESTRequest("http://server/rest/resource"); + * request.setHeader("Accept", "application/newlines"); + * request.onComplete = function (error) { + * if (error) { + * // Deal with a network error. + * processNetworkErrorCode(error.result); + * return; + * } + * callbackAfterRequestHasCompleted() + * }); + * request.onProgress = function () { + * if (!this.response.success) { + * // Bail out if we're not getting an HTTP 2xx code. + * return; + * } + * // Process body data and reset it so we don't process the same data twice. + * processIncrementalData(this.response.body); + * this.response.body = ""; + * }); + * request.get(); + */ +function RESTRequest(uri) { + this.status = this.NOT_SENT; + + // If we don't have an nsIURI object yet, make one. This will throw if + // 'uri' isn't a valid URI string. + if (!(uri instanceof Ci.nsIURI)) { + uri = Services.io.newURI(uri, null, null); + } + this.uri = uri; + + this._headers = {}; + this._log = Log4Moz.repository.getLogger(this._logName); + this._log.level = + Log4Moz.Level[Prefs.get("log.logger.rest.request")]; +} +RESTRequest.prototype = { + + _logName: "Services.Common.RESTRequest", + + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsIBadCertListener2, + Ci.nsIInterfaceRequestor, + Ci.nsIChannelEventSink + ]), + + /*** Public API: ***/ + + /** + * URI for the request (an nsIURI object). + */ + uri: null, + + /** + * HTTP method (e.g. "GET") + */ + method: null, + + /** + * RESTResponse object + */ + response: null, + + /** + * nsIRequest load flags. Don't do any caching by default. + */ + loadFlags: Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING, + + /** + * nsIHttpChannel + */ + channel: null, + + /** + * Flag to indicate the status of the request. + * + * One of NOT_SENT, SENT, IN_PROGRESS, COMPLETED, ABORTED. + */ + status: null, + + NOT_SENT: 0, + SENT: 1, + IN_PROGRESS: 2, + COMPLETED: 4, + ABORTED: 8, + + /** + * Request timeout (in seconds, though decimal values can be used for + * up to millisecond granularity.) + * + * 0 for no timeout. + */ + timeout: null, + + /** + * Called when the request has been completed, including failures and + * timeouts. + * + * @param error + * Error that occurred while making the request, null if there + * was no error. + */ + onComplete: function onComplete(error) { + }, + + /** + * Called whenever data is being received on the channel. If this throws an + * exception, the request is aborted and the exception is passed as the + * error to onComplete(). + */ + onProgress: function onProgress() { + }, + + /** + * Set a request header. + */ + setHeader: function setHeader(name, value) { + this._headers[name.toLowerCase()] = value; + }, + + /** + * Perform an HTTP GET. + * + * @param onComplete + * Short-circuit way to set the 'onComplete' method. Optional. + * @param onProgress + * Short-circuit way to set the 'onProgress' method. Optional. + * + * @return the request object. + */ + get: function get(onComplete, onProgress) { + return this.dispatch("GET", null, onComplete, onProgress); + }, + + /** + * Perform an HTTP PUT. + * + * @param data + * Data to be used as the request body. If this isn't a string + * it will be JSONified automatically. + * @param onComplete + * Short-circuit way to set the 'onComplete' method. Optional. + * @param onProgress + * Short-circuit way to set the 'onProgress' method. Optional. + * + * @return the request object. + */ + put: function put(data, onComplete, onProgress) { + return this.dispatch("PUT", data, onComplete, onProgress); + }, + + /** + * Perform an HTTP POST. + * + * @param data + * Data to be used as the request body. If this isn't a string + * it will be JSONified automatically. + * @param onComplete + * Short-circuit way to set the 'onComplete' method. Optional. + * @param onProgress + * Short-circuit way to set the 'onProgress' method. Optional. + * + * @return the request object. + */ + post: function post(data, onComplete, onProgress) { + return this.dispatch("POST", data, onComplete, onProgress); + }, + + /** + * Perform an HTTP DELETE. + * + * @param onComplete + * Short-circuit way to set the 'onComplete' method. Optional. + * @param onProgress + * Short-circuit way to set the 'onProgress' method. Optional. + * + * @return the request object. + */ + delete: function delete_(onComplete, onProgress) { + return this.dispatch("DELETE", null, onComplete, onProgress); + }, + + /** + * Abort an active request. + */ + abort: function abort() { + if (this.status != this.SENT && this.status != this.IN_PROGRESS) { + throw "Can only abort a request that has been sent."; + } + + this.status = this.ABORTED; + this.channel.cancel(Cr.NS_BINDING_ABORTED); + + if (this.timeoutTimer) { + // Clear the abort timer now that the channel is done. + this.timeoutTimer.clear(); + } + }, + + /*** Implementation stuff ***/ + + dispatch: function dispatch(method, data, onComplete, onProgress) { + if (this.status != this.NOT_SENT) { + throw "Request has already been sent!"; + } + + this.method = method; + if (onComplete) { + this.onComplete = onComplete; + } + if (onProgress) { + this.onProgress = onProgress; + } + + // Create and initialize HTTP channel. + let channel = Services.io.newChannelFromURI(this.uri, null, null) + .QueryInterface(Ci.nsIRequest) + .QueryInterface(Ci.nsIHttpChannel); + this.channel = channel; + channel.loadFlags |= this.loadFlags; + channel.notificationCallbacks = this; + + // Set request headers. + let headers = this._headers; + for (let key in headers) { + if (key == 'authorization') { + this._log.trace("HTTP Header " + key + ": ***** (suppressed)"); + } else { + this._log.trace("HTTP Header " + key + ": " + headers[key]); + } + channel.setRequestHeader(key, headers[key], false); + } + + // Set HTTP request body. + if (method == "PUT" || method == "POST") { + // Convert non-string bodies into JSON. + if (typeof data != "string") { + data = JSON.stringify(data); + } + + this._log.debug(method + " Length: " + data.length); + if (this._log.level <= Log4Moz.Level.Trace) { + this._log.trace(method + " Body: " + data); + } + + let stream = Cc["@mozilla.org/io/string-input-stream;1"] + .createInstance(Ci.nsIStringInputStream); + stream.setData(data, data.length); + + let type = headers["content-type"] || "text/plain"; + channel.QueryInterface(Ci.nsIUploadChannel); + channel.setUploadStream(stream, type, data.length); + } + // We must set this after setting the upload stream, otherwise it + // will always be 'PUT'. Yeah, I know. + channel.requestMethod = method; + + // Blast off! + channel.asyncOpen(this, null); + this.status = this.SENT; + this.delayTimeout(); + return this; + }, + + /** + * Create or push back the abort timer that kills this request. + */ + delayTimeout: function delayTimeout() { + if (this.timeout) { + CommonUtils.namedTimer(this.abortTimeout, this.timeout * 1000, this, + "timeoutTimer"); + } + }, + + /** + * Abort the request based on a timeout. + */ + abortTimeout: function abortTimeout() { + this.abort(); + let error = Components.Exception("Aborting due to channel inactivity.", + Cr.NS_ERROR_NET_TIMEOUT); + if (!this.onComplete) { + this._log.error("Unexpected error: onComplete not defined in " + + "abortTimeout.") + return; + } + this.onComplete(error); + }, + + /*** nsIStreamListener ***/ + + onStartRequest: function onStartRequest(channel) { + if (this.status == this.ABORTED) { + this._log.trace("Not proceeding with onStartRequest, request was aborted."); + return; + } + + try { + channel.QueryInterface(Ci.nsIHttpChannel); + } catch (ex) { + this._log.error("Unexpected error: channel is not a nsIHttpChannel!"); + this.status = this.ABORTED; + channel.cancel(Cr.NS_BINDING_ABORTED); + return; + } + + this.status = this.IN_PROGRESS; + + this._log.trace("onStartRequest: " + channel.requestMethod + " " + + channel.URI.spec); + + // Create a response object and fill it with some data. + let response = this.response = new RESTResponse(); + response.request = this; + response.body = ""; + + // Define this here so that we don't have make a new one each time + // onDataAvailable() gets called. + this._inputStream = Cc["@mozilla.org/scriptableinputstream;1"] + .createInstance(Ci.nsIScriptableInputStream); + + this.delayTimeout(); + }, + + onStopRequest: function onStopRequest(channel, context, statusCode) { + if (this.timeoutTimer) { + // Clear the abort timer now that the channel is done. + this.timeoutTimer.clear(); + } + + // We don't want to do anything for a request that's already been aborted. + if (this.status == this.ABORTED) { + this._log.trace("Not proceeding with onStopRequest, request was aborted."); + return; + } + + try { + channel.QueryInterface(Ci.nsIHttpChannel); + } catch (ex) { + this._log.error("Unexpected error: channel not nsIHttpChannel!"); + this.status = this.ABORTED; + return; + } + this.status = this.COMPLETED; + + let statusSuccess = Components.isSuccessCode(statusCode); + let uri = channel && channel.URI && channel.URI.spec || ""; + this._log.trace("Channel for " + channel.requestMethod + " " + uri + + " returned status code " + statusCode); + + if (!this.onComplete) { + this._log.error("Unexpected error: onComplete not defined in " + + "abortRequest."); + this.onProgress = null; + return; + } + + // Throw the failure code and stop execution. Use Components.Exception() + // instead of Error() so the exception is QI-able and can be passed across + // XPCOM borders while preserving the status code. + if (!statusSuccess) { + let message = Components.Exception("", statusCode).name; + let error = Components.Exception(message, statusCode); + this.onComplete(error); + this.onComplete = this.onProgress = null; + return; + } + + this._log.debug(this.method + " " + uri + " " + this.response.status); + + // Additionally give the full response body when Trace logging. + if (this._log.level <= Log4Moz.Level.Trace) { + this._log.trace(this.method + " body: " + this.response.body); + } + + delete this._inputStream; + + this.onComplete(null); + this.onComplete = this.onProgress = null; + }, + + onDataAvailable: function onDataAvailable(req, cb, stream, off, count) { + this._inputStream.init(stream); + try { + this.response.body += this._inputStream.read(count); + } catch (ex) { + this._log.warn("Exception thrown reading " + count + + " bytes from the channel."); + this._log.debug(CommonUtils.exceptionStr(ex)); + throw ex; + } + + try { + this.onProgress(); + } catch (ex) { + this._log.warn("Got exception calling onProgress handler, aborting " + + this.method + " " + req.URI.spec); + this._log.debug("Exception: " + CommonUtils.exceptionStr(ex)); + this.abort(); + + if (!this.onComplete) { + this._log.error("Unexpected error: onComplete not defined in " + + "onDataAvailable."); + this.onProgress = null; + return; + } + + this.onComplete(ex); + this.onComplete = this.onProgress = null; + return; + } + + this.delayTimeout(); + }, + + /*** nsIInterfaceRequestor ***/ + + getInterface: function(aIID) { + return this.QueryInterface(aIID); + }, + + /*** nsIBadCertListener2 ***/ + + notifyCertProblem: function notifyCertProblem(socketInfo, sslStatus, targetHost) { + this._log.warn("Invalid HTTPS certificate encountered!"); + // Suppress invalid HTTPS certificate warnings in the UI. + // (The request will still fail.) + return true; + }, + + /*** nsIChannelEventSink ***/ + asyncOnChannelRedirect: + function asyncOnChannelRedirect(oldChannel, newChannel, flags, callback) { + + try { + newChannel.QueryInterface(Ci.nsIHttpChannel); + } catch (ex) { + this._log.error("Unexpected error: channel not nsIHttpChannel!"); + callback.onRedirectVerifyCallback(Cr.NS_ERROR_NO_INTERFACE); + return; + } + + this.channel = newChannel; + + // We let all redirects proceed. + callback.onRedirectVerifyCallback(Cr.NS_OK); + } +}; + +/** + * Response object for a RESTRequest. This will be created automatically by + * the RESTRequest. + */ +function RESTResponse() { + this._log = Log4Moz.repository.getLogger(this._logName); + this._log.level = + Log4Moz.Level[Prefs.get("log.logger.rest.response")]; +} +RESTResponse.prototype = { + + _logName: "Sync.RESTResponse", + + /** + * Corresponding REST request + */ + request: null, + + /** + * HTTP status code + */ + get status() { + let status; + try { + let channel = this.request.channel.QueryInterface(Ci.nsIHttpChannel); + status = channel.responseStatus; + } catch (ex) { + this._log.debug("Caught exception fetching HTTP status code:" + + CommonUtils.exceptionStr(ex)); + return null; + } + delete this.status; + return this.status = status; + }, + + /** + * Boolean flag that indicates whether the HTTP status code is 2xx or not. + */ + get success() { + let success; + try { + let channel = this.request.channel.QueryInterface(Ci.nsIHttpChannel); + success = channel.requestSucceeded; + } catch (ex) { + this._log.debug("Caught exception fetching HTTP success flag:" + + CommonUtils.exceptionStr(ex)); + return null; + } + delete this.success; + return this.success = success; + }, + + /** + * Object containing HTTP headers (keyed as lower case) + */ + get headers() { + let headers = {}; + try { + this._log.trace("Processing response headers."); + let channel = this.request.channel.QueryInterface(Ci.nsIHttpChannel); + channel.visitResponseHeaders(function (header, value) { + headers[header.toLowerCase()] = value; + }); + } catch (ex) { + this._log.debug("Caught exception processing response headers:" + + CommonUtils.exceptionStr(ex)); + return null; + } + + delete this.headers; + return this.headers = headers; + }, + + /** + * HTTP body (string) + */ + body: null + +}; diff --git a/services/common/services-common.js b/services/common/services-common.js new file mode 100644 index 00000000000..17394f04b0f --- /dev/null +++ b/services/common/services-common.js @@ -0,0 +1,5 @@ +// This file contains default preference values for components in +// services-common. + +pref("services.common.log.logger.rest.request", "Debug"); +pref("services.common.log.logger.rest.response", "Debug"); diff --git a/services/sync/modules/ext/StringBundle.js b/services/common/stringbundle.js similarity index 97% rename from services/sync/modules/ext/StringBundle.js rename to services/common/stringbundle.js index 74f463eefcf..d485c29d426 100644 --- a/services/sync/modules/ext/StringBundle.js +++ b/services/common/stringbundle.js @@ -34,12 +34,9 @@ * * ***** END LICENSE BLOCK ***** */ -let EXPORTED_SYMBOLS = ["StringBundle"]; +const EXPORTED_SYMBOLS = ["StringBundle"]; -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; -const Cu = Components.utils; +const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components; /** * A string bundle. diff --git a/services/common/tests/Makefile.in b/services/common/tests/Makefile.in new file mode 100644 index 00000000000..2639e774adb --- /dev/null +++ b/services/common/tests/Makefile.in @@ -0,0 +1,16 @@ +# 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/. + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +relativesrcdir = services/common/tests + +include $(DEPTH)/config/autoconf.mk + +MODULE = test_services_common +XPCSHELL_TESTS = unit + +include $(topsrcdir)/config/rules.mk diff --git a/services/common/tests/unit/head_global.js b/services/common/tests/unit/head_global.js new file mode 100644 index 00000000000..e7ca4439bde --- /dev/null +++ b/services/common/tests/unit/head_global.js @@ -0,0 +1,54 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Where to bind test HTTP servers to. +const TEST_SERVER_URL = "http://localhost:8080/"; + +// This has the side-effect of populating Cc, Ci, Cu, Cr. It's best not to +// ask questions and just accept it. +do_load_httpd_js(); +const Cm = Components.manager; + +let gSyncProfile = do_get_profile(); + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +let XULAppInfo = { + vendor: "Mozilla", + name: "XPCShell", + ID: "xpcshell@tests.mozilla.org", + version: "1", + appBuildID: "20100621", + platformVersion: "", + platformBuildID: "20100621", + inSafeMode: false, + logConsoleErrors: true, + OS: "XPCShell", + XPCOMABI: "noarch-spidermonkey", + QueryInterface: XPCOMUtils.generateQI([Ci.nsIXULAppInfo, Ci.nsIXULRuntime]), + invalidateCachesOnRestart: function invalidateCachesOnRestart() { } +}; + +let XULAppInfoFactory = { + createInstance: function (outer, iid) { + if (outer != null) + throw Cr.NS_ERROR_NO_AGGREGATION; + return XULAppInfo.QueryInterface(iid); + } +}; + +let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); +registrar.registerFactory(Components.ID("{fbfae60b-64a4-44ef-a911-08ceb70b9f31}"), + "XULAppInfo", "@mozilla.org/xre/app-info;1", + XULAppInfoFactory); + +function addResourceAlias() { + Cu.import("resource://gre/modules/Services.jsm"); + const handler = Services.io.getProtocolHandler("resource") + .QueryInterface(Ci.nsIResProtocolHandler); + + let uri = Services.io.newURI("resource:///modules/services-common/", null, + null); + handler.setSubstitution("services-common", uri); +} +addResourceAlias(); diff --git a/services/common/tests/unit/head_helpers.js b/services/common/tests/unit/head_helpers.js new file mode 100644 index 00000000000..79232314257 --- /dev/null +++ b/services/common/tests/unit/head_helpers.js @@ -0,0 +1,173 @@ +/* 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/. */ + +Cu.import("resource://services-common/log4moz.js"); + +function do_check_empty(obj) { + do_check_attribute_count(obj, 0); +} + +function do_check_attribute_count(obj, c) { + do_check_eq(c, Object.keys(obj).length); +} + +function do_check_throws(aFunc, aResult, aStack) { + if (!aStack) { + try { + // We might not have a 'Components' object. + aStack = Components.stack.caller; + } catch (e) {} + } + + try { + aFunc(); + } catch (e) { + do_check_eq(e.result, aResult, aStack); + return; + } + do_throw("Expected result " + aResult + ", none thrown.", aStack); +} + +/** + * Print some debug message to the console. All arguments will be printed, + * separated by spaces. + * + * @param [arg0, arg1, arg2, ...] + * Any number of arguments to print out + * @usage _("Hello World") -> prints "Hello World" + * @usage _(1, 2, 3) -> prints "1 2 3" + */ +let _ = function(some, debug, text, to) print(Array.slice(arguments).join(" ")); + +function initTestLogging(level) { + function LogStats() { + this.errorsLogged = 0; + } + LogStats.prototype = { + format: function BF_format(message) { + if (message.level == Log4Moz.Level.Error) + this.errorsLogged += 1; + return message.loggerName + "\t" + message.levelDesc + "\t" + + message.message + "\n"; + } + }; + LogStats.prototype.__proto__ = new Log4Moz.Formatter(); + + var log = Log4Moz.repository.rootLogger; + var logStats = new LogStats(); + var appender = new Log4Moz.DumpAppender(logStats); + + if (typeof(level) == "undefined") + level = "Debug"; + getTestLogger().level = Log4Moz.Level[level]; + + log.level = Log4Moz.Level.Trace; + appender.level = Log4Moz.Level.Trace; + // Overwrite any other appenders (e.g. from previous incarnations) + log.ownAppenders = [appender]; + log.updateAppenders(); + + return logStats; +} + +function getTestLogger(component) { + return Log4Moz.repository.getLogger("Testing"); +} + +function httpd_setup (handlers, port) { + let port = port || 8080; + let server = new nsHttpServer(); + for (let path in handlers) { + server.registerPathHandler(path, handlers[path]); + } + try { + server.start(port); + } catch (ex) { + _("=========================================="); + _("Got exception starting HTTP server on port " + port); + _("Error: " + Utils.exceptionStr(ex)); + _("Is there a process already listening on port " + port + "?"); + _("=========================================="); + do_throw(ex); + } + + return server; +} + +function httpd_handler(statusCode, status, body) { + return function handler(request, response) { + _("Processing request"); + // Allow test functions to inspect the request. + request.body = readBytesFromInputStream(request.bodyInputStream); + handler.request = request; + + response.setStatusLine(request.httpVersion, statusCode, status); + if (body) { + response.bodyOutputStream.write(body, body.length); + } + }; +} + +/* + * Read bytes string from an nsIInputStream. If 'count' is omitted, + * all available input is read. + */ +function readBytesFromInputStream(inputStream, count) { + var BinaryInputStream = Components.Constructor( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream"); + if (!count) { + count = inputStream.available(); + } + return new BinaryInputStream(inputStream).readBytes(count); +} + +/** + * Proxy auth helpers. + */ + +/** + * Fake a PAC to prompt a channel replacement. + */ +let PACSystemSettings = { + CID: Components.ID("{5645d2c1-d6d8-4091-b117-fe7ee4027db7}"), + contractID: "@mozilla.org/system-proxy-settings;1", + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory, + Ci.nsISystemProxySettings]), + + createInstance: function createInstance(outer, iid) { + if (outer) { + throw Cr.NS_ERROR_NO_AGGREGATION; + } + return this.QueryInterface(iid); + }, + + lockFactory: function lockFactory(lock) { + throw Cr.NS_ERROR_NOT_IMPLEMENTED; + }, + + // Replace this URI for each test to avoid caching. We want to ensure that + // each test gets a completely fresh setup. + PACURI: null, + getProxyForURI: function getProxyForURI(aURI) { + throw Cr.NS_ERROR_NOT_IMPLEMENTED; + } +}; + +function installFakePAC() { + _("Installing fake PAC."); + Cm.nsIComponentRegistrar + .registerFactory(PACSystemSettings.CID, + "Fake system proxy-settings", + PACSystemSettings.contractID, + PACSystemSettings); +} + +function uninstallFakePAC() { + _("Uninstalling fake PAC."); + let CID = PACSystemSettings.CID; + Cm.nsIComponentRegistrar.unregisterFactory(CID, PACSystemSettings); +} diff --git a/services/sync/tests/unit/test_async_chain.js b/services/common/tests/unit/test_async_chain.js similarity index 80% rename from services/sync/tests/unit/test_async_chain.js rename to services/common/tests/unit/test_async_chain.js index 56393986e5e..c3abef2962a 100644 --- a/services/sync/tests/unit/test_async_chain.js +++ b/services/common/tests/unit/test_async_chain.js @@ -1,4 +1,7 @@ -Cu.import("resource://services-sync/async.js"); +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +Cu.import("resource://services-common/async.js"); function run_test() { _("Chain a few async methods, making sure the 'this' object is correct."); diff --git a/services/sync/tests/unit/test_async_querySpinningly.js b/services/common/tests/unit/test_async_querySpinningly.js similarity index 85% rename from services/sync/tests/unit/test_async_querySpinningly.js rename to services/common/tests/unit/test_async_querySpinningly.js index f08ba2b47f0..9904c0124e6 100644 --- a/services/sync/tests/unit/test_async_querySpinningly.js +++ b/services/common/tests/unit/test_async_querySpinningly.js @@ -1,12 +1,23 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://services-common/async.js"); +Cu.import("resource://services-common/utils.js"); + _("Make sure querySpinningly will synchronously fetch rows for a query asyncly"); -Cu.import("resource://services-sync/async.js"); const SQLITE_CONSTRAINT_VIOLATION = 19; // http://www.sqlite.org/c3ref/c_abort.html +let Svc = {}; +XPCOMUtils.defineLazyServiceGetter(Svc, "Form", + "@mozilla.org/satchel/form-history;1", + "nsIFormHistory2"); + function querySpinningly(query, names) { let q = Svc.Form.DBConnection.createStatement(query); let r = Async.querySpinningly(q, names); - q.finalize(); + q.finalize(); return r; } @@ -15,7 +26,7 @@ function run_test() { _("Make sure the call is async and allows other events to process"); let isAsync = false; - Utils.nextTick(function() { isAsync = true; }); + CommonUtils.nextTick(function() { isAsync = true; }); do_check_false(isAsync); _("Empty out the formhistory table"); diff --git a/services/common/tests/unit/test_load_modules.js b/services/common/tests/unit/test_load_modules.js new file mode 100644 index 00000000000..e1104972600 --- /dev/null +++ b/services/common/tests/unit/test_load_modules.js @@ -0,0 +1,18 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const modules = [ + "async.js", + "log4moz.js", + "preferences.js", + "rest.js", + "stringbundle.js", + "utils.js", +]; + +function run_test() { + for each (let m in modules) { + let resource = "resource://services-common/" + m; + Components.utils.import(resource, {}); + } +} diff --git a/services/sync/tests/unit/test_log4moz.js b/services/common/tests/unit/test_log4moz.js similarity index 96% rename from services/sync/tests/unit/test_log4moz.js rename to services/common/tests/unit/test_log4moz.js index 03a44fbe1bd..ae480e66c14 100644 --- a/services/sync/tests/unit/test_log4moz.js +++ b/services/common/tests/unit/test_log4moz.js @@ -1,8 +1,10 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -Components.utils.import("resource://services-sync/log4moz.js"); -Components.utils.import("resource://gre/modules/FileUtils.jsm"); +Cu.import("resource://gre/modules/FileUtils.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); + +Cu.import("resource://services-common/log4moz.js"); let testFormatter = { format: function format(message) { diff --git a/services/sync/tests/unit/test_Observers.js b/services/common/tests/unit/test_observers.js similarity index 79% rename from services/sync/tests/unit/test_Observers.js rename to services/common/tests/unit/test_observers.js index c85eacf019c..25dbcca648d 100644 --- a/services/sync/tests/unit/test_Observers.js +++ b/services/common/tests/unit/test_observers.js @@ -1,8 +1,15 @@ -Components.utils.import("resource://services-sync/ext/Observers.js"); +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +Components.utils.import("resource://services-common/observers.js"); let gSubject = {}; -function test_function_observer() { +function run_test() { + run_next_test(); +} + +add_test(function test_function_observer() { let foo = false; let onFoo = function(subject, data) { @@ -22,9 +29,11 @@ function test_function_observer() { // The observer was not notified after being removed. do_check_true(foo); -} -function test_method_observer() { + run_next_test(); +}); + +add_test(function test_method_observer() { let obj = { foo: false, onFoo: function(subject, data) { @@ -43,9 +52,11 @@ function test_method_observer() { Observers.remove("foo", obj.onFoo, obj); Observers.notify("foo"); do_check_true(obj.foo); -} -function test_object_observer() { + run_next_test(); +}); + +add_test(function test_object_observer() { let obj = { foo: false, observe: function(subject, topic, data) { @@ -68,10 +79,6 @@ function test_object_observer() { // The observer is not notified after being removed. do_check_true(obj.foo); -} -function run_test() { - test_function_observer(); - test_method_observer(); - test_object_observer(); -} + run_next_test(); +}); diff --git a/services/sync/tests/unit/test_Preferences.js b/services/common/tests/unit/test_preferences.js similarity index 84% rename from services/sync/tests/unit/test_Preferences.js rename to services/common/tests/unit/test_preferences.js index 234f62f61a9..16172d90cf7 100644 --- a/services/sync/tests/unit/test_Preferences.js +++ b/services/common/tests/unit/test_preferences.js @@ -1,6 +1,13 @@ -Components.utils.import("resource://services-sync/ext/Preferences.js"); +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ -function test_set_get_pref() { +Cu.import("resource://services-common/preferences.js"); + +function run_test() { + run_next_test(); +} + +add_test(function test_set_get_pref() { Preferences.set("test_set_get_pref.integer", 1); do_check_eq(Preferences.get("test_set_get_pref.integer"), 1); @@ -12,9 +19,11 @@ function test_set_get_pref() { // Clean up. Preferences.resetBranch("test_set_get_pref."); -} -function test_set_get_branch_pref() { + run_next_test(); +}); + +add_test(function test_set_get_branch_pref() { let prefs = new Preferences("test_set_get_branch_pref."); prefs.set("something", 1); @@ -23,9 +32,11 @@ function test_set_get_branch_pref() { // Clean up. prefs.reset("something"); -} -function test_set_get_multiple_prefs() { + run_next_test(); +}); + +add_test(function test_set_get_multiple_prefs() { Preferences.set({ "test_set_get_multiple_prefs.integer": 1, "test_set_get_multiple_prefs.string": "foo", "test_set_get_multiple_prefs.boolean": true }); @@ -40,9 +51,11 @@ function test_set_get_multiple_prefs() { // Clean up. Preferences.resetBranch("test_set_get_multiple_prefs."); -} -function test_get_multiple_prefs_with_default_value() { + run_next_test(); +}); + +add_test(function test_get_multiple_prefs_with_default_value() { Preferences.set({ "test_get_multiple_prefs_with_default_value.a": 1, "test_get_multiple_prefs_with_default_value.b": 2 }); @@ -57,47 +70,57 @@ function test_get_multiple_prefs_with_default_value() { // Clean up. Preferences.resetBranch("test_get_multiple_prefs_with_default_value."); -} -function test_set_get_unicode_pref() { + run_next_test(); +}); + +add_test(function test_set_get_unicode_pref() { Preferences.set("test_set_get_unicode_pref", String.fromCharCode(960)); do_check_eq(Preferences.get("test_set_get_unicode_pref"), String.fromCharCode(960)); // Clean up. Preferences.reset("test_set_get_unicode_pref"); -} -function test_set_null_pref() { + run_next_test(); +}); + +add_test(function test_set_null_pref() { try { Preferences.set("test_set_null_pref", null); // We expect this to throw, so the test is designed to fail if it doesn't. do_check_true(false); } catch(ex) {} -} -function test_set_undefined_pref() { + run_next_test(); +}); + +add_test(function test_set_undefined_pref() { try { Preferences.set("test_set_undefined_pref"); // We expect this to throw, so the test is designed to fail if it doesn't. do_check_true(false); } catch(ex) {} -} -function test_set_unsupported_pref() { + run_next_test(); +}); + +add_test(function test_set_unsupported_pref() { try { Preferences.set("test_set_unsupported_pref", new Array()); // We expect this to throw, so the test is designed to fail if it doesn't. do_check_true(false); } catch(ex) {} -} + + run_next_test(); +}); // Make sure that we can get a string pref that we didn't set ourselves // (i.e. that the way we get a string pref using getComplexValue doesn't // hork us getting a string pref that wasn't set using setComplexValue). -function test_get_string_pref() { +add_test(function test_get_string_pref() { let svc = Cc["@mozilla.org/preferences-service;1"]. getService(Ci.nsIPrefService). getBranch(""); @@ -106,9 +129,11 @@ function test_get_string_pref() { // Clean up. Preferences.reset("test_get_string_pref"); -} -function test_set_get_number_pref() { + run_next_test(); +}); + +add_test(function test_set_get_number_pref() { Preferences.set("test_set_get_number_pref", 5); do_check_eq(Preferences.get("test_set_get_number_pref"), 5); @@ -126,35 +151,45 @@ function test_set_get_number_pref() { // Clean up. Preferences.reset("test_set_get_number_pref"); -} -function test_reset_pref() { + run_next_test(); +}); + +add_test(function test_reset_pref() { Preferences.set("test_reset_pref", 1); Preferences.reset("test_reset_pref"); do_check_eq(Preferences.get("test_reset_pref"), undefined); -} -function test_reset_pref_branch() { + run_next_test(); +}); + +add_test(function test_reset_pref_branch() { Preferences.set("test_reset_pref_branch.foo", 1); Preferences.set("test_reset_pref_branch.bar", 2); Preferences.resetBranch("test_reset_pref_branch."); do_check_eq(Preferences.get("test_reset_pref_branch.foo"), undefined); do_check_eq(Preferences.get("test_reset_pref_branch.bar"), undefined); -} + + run_next_test(); +}); // Make sure the module doesn't throw an exception when asked to reset // a nonexistent pref. -function test_reset_nonexistent_pref() { +add_test(function test_reset_nonexistent_pref() { Preferences.reset("test_reset_nonexistent_pref"); -} + + run_next_test(); +}); // Make sure the module doesn't throw an exception when asked to reset // a nonexistent pref branch. -function test_reset_nonexistent_pref_branch() { +add_test(function test_reset_nonexistent_pref_branch() { Preferences.resetBranch("test_reset_nonexistent_pref_branch."); -} -function test_observe_prefs_function() { + run_next_test(); +}); + +add_test(function test_observe_prefs_function() { let observed = false; let observer = function() { observed = !observed }; @@ -168,9 +203,11 @@ function test_observe_prefs_function() { // Clean up. Preferences.reset("test_observe_prefs_function"); -} -function test_observe_prefs_object() { + run_next_test(); +}); + +add_test(function test_observe_prefs_object() { let observer = { observed: false, observe: function() { @@ -188,9 +225,11 @@ function test_observe_prefs_object() { // Clean up. Preferences.reset("test_observe_prefs_object"); -} -function test_observe_prefs_nsIObserver() { + run_next_test(); +}); + +add_test(function test_observe_prefs_nsIObserver() { let observer = { observed: false, observe: function(subject, topic, data) { @@ -211,9 +250,12 @@ function test_observe_prefs_nsIObserver() { // Clean up. Preferences.reset("test_observe_prefs_nsIObserver"); -} -function test_observe_exact_pref() { + run_next_test(); +}); + +/* +add_test(function test_observe_exact_pref() { let observed = false; let observer = function() { observed = !observed }; @@ -224,9 +266,12 @@ function test_observe_exact_pref() { // Clean up. Preferences.ignore("test_observe_exact_pref", observer); Preferences.reset("test_observe_exact_pref.sub-pref"); -} -function test_observe_value_of_set_pref() { + run_next_test(); +}); +*/ + +add_test(function test_observe_value_of_set_pref() { let observer = function(newVal) { do_check_eq(newVal, "something") }; Preferences.observe("test_observe_value_of_set_pref", observer); @@ -235,9 +280,11 @@ function test_observe_value_of_set_pref() { // Clean up. Preferences.ignore("test_observe_value_of_set_pref", observer); Preferences.reset("test_observe_value_of_set_pref"); -} -function test_observe_value_of_reset_pref() { + run_next_test(); +}); + +add_test(function test_observe_value_of_reset_pref() { let observer = function(newVal) { do_check_true(typeof newVal == "undefined") }; Preferences.set("test_observe_value_of_reset_pref", "something"); @@ -246,9 +293,11 @@ function test_observe_value_of_reset_pref() { // Clean up. Preferences.ignore("test_observe_value_of_reset_pref", observer); -} -function test_has_pref() { + run_next_test(); +}); + +add_test(function test_has_pref() { do_check_false(Preferences.has("test_has_pref")); Preferences.set("test_has_pref", "foo"); do_check_true(Preferences.has("test_has_pref")); @@ -264,9 +313,11 @@ function test_has_pref() { // Clean up. Preferences.resetBranch("test_has_pref"); -} -function test_isSet_pref() { + run_next_test(); +}); + +add_test(function test_isSet_pref() { // Use a pref that we know has a default value but no user-set value. // This feels dangerous; perhaps we should create some other default prefs // that we can use for testing. @@ -276,9 +327,12 @@ function test_isSet_pref() { // Clean up. Preferences.reset("toolkit.defaultChromeURI"); -} -function test_lock_prefs() { + run_next_test(); +}); + +/* +add_test(function test_lock_prefs() { // Use a pref that we know has a default value. // This feels dangerous; perhaps we should create some other default prefs // that we can use for testing. @@ -298,9 +352,12 @@ function test_lock_prefs() { // Clean up. Preferences.reset("toolkit.defaultChromeURI"); -} -function test_site_prefs() { + run_next_test(); +}); +*/ + +add_test(function test_site_prefs() { let prefs = Preferences.site("www.example.com"); prefs.set("test_site_prefs.integer", 1); @@ -323,32 +380,6 @@ function test_site_prefs() { do_check_false(Preferences.has("test_site_prefs.boolean")); prefs.reset("test_site_prefs.boolean"); do_check_false(prefs.has("test_site_prefs.boolean")); -} - -function run_test() { - test_set_get_pref(); - test_set_get_branch_pref(); - test_set_get_multiple_prefs(); - test_get_multiple_prefs_with_default_value(); - test_set_get_unicode_pref(); - test_set_null_pref(); - test_set_undefined_pref(); - test_set_unsupported_pref(); - test_get_string_pref(); - test_set_get_number_pref(); - test_reset_pref(); - test_reset_pref_branch(); - test_reset_nonexistent_pref(); - test_reset_nonexistent_pref_branch(); - test_observe_prefs_function(); - test_observe_prefs_object(); - test_observe_prefs_nsIObserver(); - //test_observe_exact_pref(); - test_observe_value_of_set_pref(); - test_observe_value_of_reset_pref(); - test_has_pref(); - test_isSet_pref(); - //test_lock_prefs(); - test_site_prefs(); -} + run_next_test(); +}); diff --git a/services/sync/tests/unit/test_restrequest.js b/services/common/tests/unit/test_restrequest.js similarity index 97% rename from services/sync/tests/unit/test_restrequest.js rename to services/common/tests/unit/test_restrequest.js index 15d2e5f180d..eee6fad3f02 100644 --- a/services/sync/tests/unit/test_restrequest.js +++ b/services/common/tests/unit/test_restrequest.js @@ -1,14 +1,18 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -Cu.import("resource://services-sync/rest.js"); -Cu.import("resource://services-sync/log4moz.js"); Cu.import("resource://gre/modules/NetUtil.jsm"); +Cu.import("resource://services-common/log4moz.js"); +Cu.import("resource://services-common/rest.js"); +Cu.import("resource://services-common/utils.js"); const TEST_RESOURCE_URL = TEST_SERVER_URL + "resource"; +//DEBUG = true; + function run_test() { - Log4Moz.repository.getLogger("Sync.RESTRequest").level = Log4Moz.Level.Trace; + Log4Moz.repository.getLogger("Services.Common.RESTRequest").level = + Log4Moz.Level.Trace; initTestLogging(); run_next_test(); @@ -140,7 +144,7 @@ add_test(function test_get() { do_check_eq(handler.request.method, "GET"); do_check_true(onProgress_called); - Utils.nextTick(function () { + CommonUtils.nextTick(function () { do_check_eq(request.onComplete, null); do_check_eq(request.onProgress, null); server.stop(run_next_test); @@ -189,7 +193,7 @@ add_test(function test_put() { do_check_eq(handler.request.getHeader("Content-Type"), "text/plain"); do_check_true(onProgress_called); - Utils.nextTick(function () { + CommonUtils.nextTick(function () { do_check_eq(request.onComplete, null); do_check_eq(request.onProgress, null); server.stop(run_next_test); @@ -238,7 +242,7 @@ add_test(function test_post() { do_check_eq(handler.request.getHeader("Content-Type"), "text/plain"); do_check_true(onProgress_called); - Utils.nextTick(function () { + CommonUtils.nextTick(function () { do_check_eq(request.onComplete, null); do_check_eq(request.onProgress, null); server.stop(run_next_test); @@ -284,7 +288,7 @@ add_test(function test_delete() { do_check_eq(handler.request.method, "DELETE"); do_check_true(onProgress_called); - Utils.nextTick(function () { + CommonUtils.nextTick(function () { do_check_eq(request.onComplete, null); do_check_eq(request.onProgress, null); server.stop(run_next_test); @@ -467,7 +471,7 @@ add_test(function test_changing_uri() { let server = httpd_setup({"/resource": handler}); let request = new RESTRequest("http://localhost:8080/the-wrong-resource"); - request.uri = Utils.makeURI(TEST_RESOURCE_URL); + request.uri = CommonUtils.makeURI(TEST_RESOURCE_URL); request.get(function (error) { do_check_eq(error, null); do_check_eq(this.response.status, 200); @@ -572,7 +576,7 @@ add_test(function test_abort() { }); do_check_eq(request.status, request.ABORTED); - Utils.nextTick(function () { + CommonUtils.nextTick(function () { server.stop(run_next_test); }); }); diff --git a/services/common/tests/unit/test_utils_makeURI.js b/services/common/tests/unit/test_utils_makeURI.js new file mode 100644 index 00000000000..4b2b9bf7198 --- /dev/null +++ b/services/common/tests/unit/test_utils_makeURI.js @@ -0,0 +1,66 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +_("Make sure uri strings are converted to nsIURIs"); +Cu.import("resource://services-common/utils.js"); + +function run_test() { + _test_makeURI(); +} + +function _test_makeURI() { + _("Check http uris"); + let uri1 = "http://mozillalabs.com/"; + do_check_eq(CommonUtils.makeURI(uri1).spec, uri1); + let uri2 = "http://www.mozillalabs.com/"; + do_check_eq(CommonUtils.makeURI(uri2).spec, uri2); + let uri3 = "http://mozillalabs.com/path"; + do_check_eq(CommonUtils.makeURI(uri3).spec, uri3); + let uri4 = "http://mozillalabs.com/multi/path"; + do_check_eq(CommonUtils.makeURI(uri4).spec, uri4); + let uri5 = "http://mozillalabs.com/?query"; + do_check_eq(CommonUtils.makeURI(uri5).spec, uri5); + let uri6 = "http://mozillalabs.com/#hash"; + do_check_eq(CommonUtils.makeURI(uri6).spec, uri6); + + _("Check https uris"); + let uris1 = "https://mozillalabs.com/"; + do_check_eq(CommonUtils.makeURI(uris1).spec, uris1); + let uris2 = "https://www.mozillalabs.com/"; + do_check_eq(CommonUtils.makeURI(uris2).spec, uris2); + let uris3 = "https://mozillalabs.com/path"; + do_check_eq(CommonUtils.makeURI(uris3).spec, uris3); + let uris4 = "https://mozillalabs.com/multi/path"; + do_check_eq(CommonUtils.makeURI(uris4).spec, uris4); + let uris5 = "https://mozillalabs.com/?query"; + do_check_eq(CommonUtils.makeURI(uris5).spec, uris5); + let uris6 = "https://mozillalabs.com/#hash"; + do_check_eq(CommonUtils.makeURI(uris6).spec, uris6); + + _("Check chrome uris"); + let uric1 = "chrome://browser/content/browser.xul"; + do_check_eq(CommonUtils.makeURI(uric1).spec, uric1); + let uric2 = "chrome://browser/skin/browser.css"; + do_check_eq(CommonUtils.makeURI(uric2).spec, uric2); + let uric3 = "chrome://browser/locale/browser.dtd"; + do_check_eq(CommonUtils.makeURI(uric3).spec, uric3); + + _("Check about uris"); + let uria1 = "about:weave"; + do_check_eq(CommonUtils.makeURI(uria1).spec, uria1); + let uria2 = "about:weave/"; + do_check_eq(CommonUtils.makeURI(uria2).spec, uria2); + let uria3 = "about:weave/path"; + do_check_eq(CommonUtils.makeURI(uria3).spec, uria3); + let uria4 = "about:weave/multi/path"; + do_check_eq(CommonUtils.makeURI(uria4).spec, uria4); + let uria5 = "about:weave/?query"; + do_check_eq(CommonUtils.makeURI(uria5).spec, uria5); + let uria6 = "about:weave/#hash"; + do_check_eq(CommonUtils.makeURI(uria6).spec, uria6); + + _("Invalid uris are undefined"); + do_check_eq(CommonUtils.makeURI("mozillalabs.com"), undefined); + do_check_eq(CommonUtils.makeURI("chrome://badstuff"), undefined); + do_check_eq(CommonUtils.makeURI("this is a test"), undefined); +} diff --git a/services/sync/tests/unit/test_utils_namedTimer.js b/services/common/tests/unit/test_utils_namedTimer.js similarity index 76% rename from services/sync/tests/unit/test_utils_namedTimer.js rename to services/common/tests/unit/test_utils_namedTimer.js index 34601360f7a..61a65e26037 100644 --- a/services/sync/tests/unit/test_utils_namedTimer.js +++ b/services/common/tests/unit/test_utils_namedTimer.js @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -Cu.import("resource://services-sync/util.js"); +Cu.import("resource://services-common/utils.js"); function run_test() { run_next_test(); @@ -9,7 +9,7 @@ function run_test() { add_test(function test_required_args() { try { - Utils.namedTimer(function callback() { + CommonUtils.namedTimer(function callback() { do_throw("Shouldn't fire."); }, 0); do_throw("Should have thrown!"); @@ -19,12 +19,12 @@ add_test(function test_required_args() { }); add_test(function test_simple() { - _("Test basic properties of Utils.namedTimer."); + _("Test basic properties of CommonUtils.namedTimer."); const delay = 200; let that = {}; let t0 = Date.now(); - Utils.namedTimer(function callback(timer) { + CommonUtils.namedTimer(function callback(timer) { do_check_eq(this, that); do_check_eq(this._zetimer, null); do_check_true(timer instanceof Ci.nsITimer); @@ -37,7 +37,7 @@ add_test(function test_simple() { add_test(function test_delay() { _("Test delaying a timer that hasn't fired yet."); - + const delay = 100; let that = {}; let t0 = Date.now(); @@ -47,8 +47,8 @@ add_test(function test_delay() { do_check_true((Date.now() - t0) > delay); run_next_test(); } - Utils.namedTimer(callback, delay, that, "_zetimer"); - Utils.namedTimer(callback, 2 * delay, that, "_zetimer"); + CommonUtils.namedTimer(callback, delay, that, "_zetimer"); + CommonUtils.namedTimer(callback, 2 * delay, that, "_zetimer"); run_next_test(); }); @@ -57,13 +57,13 @@ add_test(function test_clear() { const delay = 0; let that = {}; - Utils.namedTimer(function callback(timer) { + CommonUtils.namedTimer(function callback(timer) { do_throw("Shouldn't fire!"); }, delay, that, "_zetimer"); that._zetimer.clear(); do_check_eq(that._zetimer, null); - Utils.nextTick(run_next_test); + CommonUtils.nextTick(run_next_test); run_next_test(); }); diff --git a/services/sync/tests/unit/test_utils_stackTrace.js b/services/common/tests/unit/test_utils_stackTrace.js similarity index 63% rename from services/sync/tests/unit/test_utils_stackTrace.js rename to services/common/tests/unit/test_utils_stackTrace.js index daf6658713d..1c537fae431 100644 --- a/services/sync/tests/unit/test_utils_stackTrace.js +++ b/services/common/tests/unit/test_utils_stackTrace.js @@ -1,10 +1,13 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + _("Define some functions in well defined line positions for the test"); function foo(v) bar(v + 1); // line 2 function bar(v) baz(v + 1); // line 3 function baz(v) { throw new Error(v + 1); } // line 4 _("Make sure lazy constructor calling/assignment works"); -Cu.import("resource://services-sync/util.js"); +Cu.import("resource://services-common/utils.js"); function run_test() { _("Make sure functions, arguments, files are pretty printed in the trace"); @@ -13,14 +16,14 @@ function run_test() { foo(0); } catch(ex) { - trace = Utils.stackTrace(ex); + trace = CommonUtils.stackTrace(ex); } _("Got trace:", trace); do_check_neq(trace, ""); - let bazPos = trace.indexOf("baz(2)@test_utils_stackTrace.js:4"); - let barPos = trace.indexOf("bar(1)@test_utils_stackTrace.js:3"); - let fooPos = trace.indexOf("foo(0)@test_utils_stackTrace.js:2"); + let bazPos = trace.indexOf("baz(2)@test_utils_stackTrace.js:7"); + let barPos = trace.indexOf("bar(1)@test_utils_stackTrace.js:6"); + let fooPos = trace.indexOf("foo(0)@test_utils_stackTrace.js:5"); _("String positions:", bazPos, barPos, fooPos); _("Make sure the desired messages show up"); diff --git a/services/common/tests/unit/xpcshell.ini b/services/common/tests/unit/xpcshell.ini new file mode 100644 index 00000000000..788bf3a77ff --- /dev/null +++ b/services/common/tests/unit/xpcshell.ini @@ -0,0 +1,17 @@ +[DEFAULT] +head = head_global.js head_helpers.js +tail = + +# Test load modules first so syntax failures are caught early. +[test_load_modules.js] + +[test_utils_makeURI.js] +[test_utils_namedTimer.js] +[test_utils_stackTrace.js] + +[test_async_chain.js] +[test_async_querySpinningly.js] +[test_log4moz.js] +[test_observers.js] +[test_preferences.js] +[test_restrequest.js] diff --git a/services/common/utils.js b/services/common/utils.js new file mode 100644 index 00000000000..1083bfb5c92 --- /dev/null +++ b/services/common/utils.js @@ -0,0 +1,125 @@ +/* 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 {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +const EXPORTED_SYMBOLS = ["CommonUtils"]; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://services-common/log4moz.js"); + +let CommonUtils = { + exceptionStr: function exceptionStr(e) { + let message = e.message ? e.message : e; + return message + " " + CommonUtils.stackTrace(e); + }, + + stackTrace: function stackTrace(e) { + // Wrapped nsIException + if (e.location) { + let frame = e.location; + let output = []; + while (frame) { + // Works on frames or exceptions, munges file:// URIs to shorten the paths + // FIXME: filename munging is sort of hackish, might be confusing if + // there are multiple extensions with similar filenames + let str = ""; + + let file = frame.filename || frame.fileName; + if (file){ + str = file.replace(/^(?:chrome|file):.*?([^\/\.]+\.\w+)$/, "$1"); + } + + if (frame.lineNumber){ + str += ":" + frame.lineNumber; + } + if (frame.name){ + str = frame.name + "()@" + str; + } + + if (str){ + output.push(str); + } + frame = frame.caller; + } + return "Stack trace: " + output.join(" < "); + } + // Standard JS exception + if (e.stack){ + return "JS Stack trace: " + e.stack.trim().replace(/\n/g, " < "). + replace(/@[^@]*?([^\/\.]+\.\w+:)/g, "@$1"); + } + + return "No traceback available"; + }, + + /** + * Create a nsIURI instance from a string. + */ + makeURI: function makeURI(URIString) { + if (!URIString) + return null; + try { + return Services.io.newURI(URIString, null, null); + } catch (e) { + let log = Log4Moz.repository.getLogger("Common.Utils"); + log.debug("Could not create URI: " + CommonUtils.exceptionStr(e)); + return null; + } + }, + + /** + * Execute a function on the next event loop tick. + * + * @param callback + * Function to invoke. + * @param thisObj [optional] + * Object to bind the callback to. + */ + nextTick: function nextTick(callback, thisObj) { + if (thisObj) { + callback = callback.bind(thisObj); + } + Services.tm.currentThread.dispatch(callback, Ci.nsIThread.DISPATCH_NORMAL); + }, + + /** + * Return a timer that is scheduled to call the callback after waiting the + * provided time or as soon as possible. The timer will be set as a property + * of the provided object with the given timer name. + */ + namedTimer: function namedTimer(callback, wait, thisObj, name) { + if (!thisObj || !name) { + throw "You must provide both an object and a property name for the timer!"; + } + + // Delay an existing timer if it exists + if (name in thisObj && thisObj[name] instanceof Ci.nsITimer) { + thisObj[name].delay = wait; + return; + } + + // Create a special timer that we can add extra properties + let timer = {}; + timer.__proto__ = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + + // Provide an easy way to clear out the timer + timer.clear = function() { + thisObj[name] = null; + timer.cancel(); + }; + + // Initialize the timer with a smart callback + timer.initWithCallback({ + notify: function notify() { + // Clear out the timer once it's been triggered + timer.clear(); + callback.call(thisObj, timer); + } + }, wait, timer.TYPE_ONE_SHOT); + + return thisObj[name] = timer; + }, + +}; diff --git a/services/makefiles.sh b/services/makefiles.sh index f4789d96fa3..a53b16dfdb8 100644 --- a/services/makefiles.sh +++ b/services/makefiles.sh @@ -35,24 +35,18 @@ # # ***** END LICENSE BLOCK ***** -MAKEFILES_crypto=" +add_makefiles " + services/Makefile + services/common/Makefile services/crypto/Makefile services/crypto/component/Makefile -" - -MAKEFILES_sync=" services/sync/Makefile services/sync/locales/Makefile " -add_makefiles " - services/Makefile - $MAKEFILES_crypto - $MAKEFILES_sync -" - if [ "$ENABLE_TESTS" ]; then add_makefiles " + services/common/tests/Makefile services/crypto/tests/Makefile services/sync/tests/Makefile " diff --git a/services/sync/SyncComponents.manifest b/services/sync/SyncComponents.manifest index e6f9975853e..1f1cca84cf9 100644 --- a/services/sync/SyncComponents.manifest +++ b/services/sync/SyncComponents.manifest @@ -6,4 +6,5 @@ component {d28f8a0b-95da-48f4-b712-caf37097be41} Weave.js contract @mozilla.org/network/protocol/about;1?what=sync-log {d28f8a0b-95da-48f4-b712-caf37097be41} # Register resource aliases resource services-sync resource:///modules/services-sync/ +resource services-common resource:///modules/services-common/ resource services-crypto resource:///modules/services-crypto/ diff --git a/services/sync/modules/addonsreconciler.js b/services/sync/modules/addonsreconciler.js index ff45ed1413c..967dbf5e3ca 100644 --- a/services/sync/modules/addonsreconciler.js +++ b/services/sync/modules/addonsreconciler.js @@ -52,7 +52,7 @@ const Cu = Components.utils; -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://gre/modules/AddonManager.jsm"); diff --git a/services/sync/modules/engines.js b/services/sync/modules/engines.js index c33a1556a7f..29a6a386215 100644 --- a/services/sync/modules/engines.js +++ b/services/sync/modules/engines.js @@ -46,12 +46,12 @@ const Ci = Components.interfaces; const Cr = Components.results; const Cu = Components.utils; -Cu.import("resource://services-sync/async.js"); +Cu.import("resource://services-common/async.js"); Cu.import("resource://services-sync/record.js"); Cu.import("resource://services-sync/constants.js"); -Cu.import("resource://services-sync/ext/Observers.js"); +Cu.import("resource://services-common/observers.js"); Cu.import("resource://services-sync/identity.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); Cu.import("resource://services-sync/resource.js"); Cu.import("resource://services-sync/util.js"); diff --git a/services/sync/modules/engines/addons.js b/services/sync/modules/engines/addons.js index 1221ed91cec..2e83d219ae0 100644 --- a/services/sync/modules/engines/addons.js +++ b/services/sync/modules/engines/addons.js @@ -72,8 +72,8 @@ Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/record.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-sync/constants.js"); -Cu.import("resource://services-sync/async.js"); -Cu.import("resource://services-sync/ext/Preferences.js"); +Cu.import("resource://services-common/async.js"); +Cu.import("resource://services-common/preferences.js"); Cu.import("resource://gre/modules/AddonManager.jsm"); Cu.import("resource://gre/modules/AddonRepository.jsm"); diff --git a/services/sync/modules/engines/bookmarks.js b/services/sync/modules/engines/bookmarks.js index 7a4ffbc8c3c..48983cf0196 100644 --- a/services/sync/modules/engines/bookmarks.js +++ b/services/sync/modules/engines/bookmarks.js @@ -51,7 +51,7 @@ Cu.import("resource://gre/modules/PlacesUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/record.js"); -Cu.import("resource://services-sync/async.js"); +Cu.import("resource://services-common/async.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-sync/constants.js"); diff --git a/services/sync/modules/engines/clients.js b/services/sync/modules/engines/clients.js index c2999360a9f..2b30d429325 100644 --- a/services/sync/modules/engines/clients.js +++ b/services/sync/modules/engines/clients.js @@ -42,9 +42,9 @@ const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; +Cu.import("resource://services-common/stringbundle.js"); Cu.import("resource://services-sync/constants.js"); Cu.import("resource://services-sync/engines.js"); -Cu.import("resource://services-sync/ext/StringBundle.js"); Cu.import("resource://services-sync/record.js"); Cu.import("resource://services-sync/resource.js"); Cu.import("resource://services-sync/util.js"); diff --git a/services/sync/modules/engines/forms.js b/services/sync/modules/engines/forms.js index 03610ef5c0d..999cd19345e 100644 --- a/services/sync/modules/engines/forms.js +++ b/services/sync/modules/engines/forms.js @@ -43,10 +43,10 @@ const Cu = Components.utils; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/record.js"); -Cu.import("resource://services-sync/async.js"); +Cu.import("resource://services-common/async.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-sync/constants.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); const FORMS_TTL = 5184000; // 60 days diff --git a/services/sync/modules/engines/history.js b/services/sync/modules/engines/history.js index 8f15c5ee2fc..aa5e3914740 100644 --- a/services/sync/modules/engines/history.js +++ b/services/sync/modules/engines/history.js @@ -51,9 +51,9 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://services-sync/constants.js"); Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/record.js"); -Cu.import("resource://services-sync/async.js"); +Cu.import("resource://services-common/async.js"); Cu.import("resource://services-sync/util.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); function HistoryRec(collection, id) { CryptoWrapper.call(this, collection, id); diff --git a/services/sync/modules/engines/prefs.js b/services/sync/modules/engines/prefs.js index 58fe8b92ffd..0b4c6e72439 100644 --- a/services/sync/modules/engines/prefs.js +++ b/services/sync/modules/engines/prefs.js @@ -47,7 +47,7 @@ Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/record.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-sync/constants.js"); -Cu.import("resource://services-sync/ext/Preferences.js"); +Cu.import("resource://services-common/preferences.js"); Cu.import("resource://gre/modules/LightweightThemeManager.jsm"); const PREFS_GUID = Utils.encodeBase64url(Services.appinfo.ID); diff --git a/services/sync/modules/engines/tabs.js b/services/sync/modules/engines/tabs.js index d200c6994f4..7fa7a68f9a1 100644 --- a/services/sync/modules/engines/tabs.js +++ b/services/sync/modules/engines/tabs.js @@ -51,7 +51,7 @@ Cu.import("resource://services-sync/record.js"); Cu.import("resource://services-sync/resource.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-sync/constants.js"); -Cu.import("resource://services-sync/ext/Preferences.js"); +Cu.import("resource://services-common/preferences.js"); // It is safer to inspect the private browsing preferences rather than // the flags of nsIPrivateBrowsingService. The user may have turned on diff --git a/services/sync/modules/identity.js b/services/sync/modules/identity.js index 75092975226..731cfefd03a 100644 --- a/services/sync/modules/identity.js +++ b/services/sync/modules/identity.js @@ -10,7 +10,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import("resource://services-sync/constants.js"); Cu.import("resource://services-sync/keys.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); Cu.import("resource://services-sync/util.js"); XPCOMUtils.defineLazyGetter(this, "Identity", function() { diff --git a/services/sync/modules/jpakeclient.js b/services/sync/modules/jpakeclient.js index 627763e3649..fce73808159 100644 --- a/services/sync/modules/jpakeclient.js +++ b/services/sync/modules/jpakeclient.js @@ -40,8 +40,8 @@ const Ci = Components.interfaces; const Cr = Components.results; const Cu = Components.utils; -Cu.import("resource://services-sync/log4moz.js"); -Cu.import("resource://services-sync/rest.js"); +Cu.import("resource://services-common/log4moz.js"); +Cu.import("resource://services-common/rest.js"); Cu.import("resource://services-sync/constants.js"); Cu.import("resource://services-sync/util.js"); diff --git a/services/sync/modules/keys.js b/services/sync/modules/keys.js index d178ed70726..1f775235063 100644 --- a/services/sync/modules/keys.js +++ b/services/sync/modules/keys.js @@ -12,7 +12,7 @@ const EXPORTED_SYMBOLS = [ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import("resource://services-sync/constants.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); Cu.import("resource://services-sync/util.js"); /** diff --git a/services/sync/modules/notifications.js b/services/sync/modules/notifications.js index e28fcd84fe3..b978452a80b 100644 --- a/services/sync/modules/notifications.js +++ b/services/sync/modules/notifications.js @@ -41,8 +41,8 @@ const Ci = Components.interfaces; const Cr = Components.results; const Cu = Components.utils; -Cu.import("resource://services-sync/ext/Observers.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/observers.js"); +Cu.import("resource://services-common/log4moz.js"); Cu.import("resource://services-sync/util.js"); let Notifications = { diff --git a/services/sync/modules/policies.js b/services/sync/modules/policies.js index e17c15cdc25..c38a52bfc51 100644 --- a/services/sync/modules/policies.js +++ b/services/sync/modules/policies.js @@ -45,7 +45,7 @@ const EXPORTED_SYMBOLS = ["SyncScheduler", const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import("resource://services-sync/constants.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/engines/clients.js"); diff --git a/services/sync/modules/record.js b/services/sync/modules/record.js index aae0eb41ce4..aab0f0d63a6 100644 --- a/services/sync/modules/record.js +++ b/services/sync/modules/record.js @@ -51,7 +51,7 @@ const KEYS_WBO = "keys"; Cu.import("resource://services-sync/constants.js"); Cu.import("resource://services-sync/identity.js"); Cu.import("resource://services-sync/keys.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); Cu.import("resource://services-sync/resource.js"); Cu.import("resource://services-sync/util.js"); diff --git a/services/sync/modules/resource.js b/services/sync/modules/resource.js index ab7e299c4cd..6f832155ef4 100644 --- a/services/sync/modules/resource.js +++ b/services/sync/modules/resource.js @@ -47,12 +47,12 @@ const Ci = Components.interfaces; const Cr = Components.results; const Cu = Components.utils; -Cu.import("resource://services-sync/async.js"); +Cu.import("resource://services-common/async.js"); Cu.import("resource://services-sync/constants.js"); -Cu.import("resource://services-sync/ext/Observers.js"); -Cu.import("resource://services-sync/ext/Preferences.js"); +Cu.import("resource://services-common/observers.js"); +Cu.import("resource://services-common/preferences.js"); Cu.import("resource://services-sync/identity.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); Cu.import("resource://services-sync/util.js"); /* diff --git a/services/sync/modules/rest.js b/services/sync/modules/rest.js index dac142a804a..ca7ed293fd7 100644 --- a/services/sync/modules/rest.js +++ b/services/sync/modules/rest.js @@ -40,581 +40,16 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); +Cu.import("resource://services-common/rest.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-sync/identity.js"); Cu.import("resource://services-sync/constants.js"); -const EXPORTED_SYMBOLS = ["RESTRequest", "SyncStorageRequest"]; +const EXPORTED_SYMBOLS = ["SyncStorageRequest"]; const STORAGE_REQUEST_TIMEOUT = 5 * 60; // 5 minutes -/** - * Single use HTTP requests to RESTish resources. - * - * @param uri - * URI for the request. This can be an nsIURI object or a string - * that can be used to create one. An exception will be thrown if - * the string is not a valid URI. - * - * Examples: - * - * (1) Quick GET request: - * - * new RESTRequest("http://server/rest/resource").get(function (error) { - * if (error) { - * // Deal with a network error. - * processNetworkErrorCode(error.result); - * return; - * } - * if (!this.response.success) { - - * * // Bail out if we're not getting an HTTP 2xx code. - * processHTTPError(this.response.status); - * return; - * } - * processData(this.response.body); - * }); - * - * (2) Quick PUT request (non-string data is automatically JSONified) - * - * new RESTRequest("http://server/rest/resource").put(data, function (error) { - * ... - * }); - * - * (3) Streaming GET - * - * let request = new RESTRequest("http://server/rest/resource"); - * request.setHeader("Accept", "application/newlines"); - * request.onComplete = function (error) { - * if (error) { - * // Deal with a network error. - * processNetworkErrorCode(error.result); - * return; - * } - * callbackAfterRequestHasCompleted() - * }); - * request.onProgress = function () { - * if (!this.response.success) { - * // Bail out if we're not getting an HTTP 2xx code. - * return; - * } - * // Process body data and reset it so we don't process the same data twice. - * processIncrementalData(this.response.body); - * this.response.body = ""; - * }); - * request.get(); - */ -function RESTRequest(uri) { - this.status = this.NOT_SENT; - - // If we don't have an nsIURI object yet, make one. This will throw if - // 'uri' isn't a valid URI string. - if (!(uri instanceof Ci.nsIURI)) { - uri = Services.io.newURI(uri, null, null); - } - this.uri = uri; - - this._headers = {}; - this._log = Log4Moz.repository.getLogger(this._logName); - this._log.level = - Log4Moz.Level[Svc.Prefs.get("log.logger.network.resources")]; -} -RESTRequest.prototype = { - - _logName: "Sync.RESTRequest", - - QueryInterface: XPCOMUtils.generateQI([ - Ci.nsIBadCertListener2, - Ci.nsIInterfaceRequestor, - Ci.nsIChannelEventSink - ]), - - /*** Public API: ***/ - - /** - * URI for the request (an nsIURI object). - */ - uri: null, - - /** - * HTTP method (e.g. "GET") - */ - method: null, - - /** - * RESTResponse object - */ - response: null, - - /** - * nsIRequest load flags. Don't do any caching by default. - */ - loadFlags: Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING, - - /** - * nsIHttpChannel - */ - channel: null, - - /** - * Flag to indicate the status of the request. - * - * One of NOT_SENT, SENT, IN_PROGRESS, COMPLETED, ABORTED. - */ - status: null, - - NOT_SENT: 0, - SENT: 1, - IN_PROGRESS: 2, - COMPLETED: 4, - ABORTED: 8, - - /** - * Request timeout (in seconds, though decimal values can be used for - * up to millisecond granularity.) - * - * 0 for no timeout. - */ - timeout: null, - - /** - * Called when the request has been completed, including failures and - * timeouts. - * - * @param error - * Error that occurred while making the request, null if there - * was no error. - */ - onComplete: function onComplete(error) { - }, - - /** - * Called whenever data is being received on the channel. If this throws an - * exception, the request is aborted and the exception is passed as the - * error to onComplete(). - */ - onProgress: function onProgress() { - }, - - /** - * Set a request header. - */ - setHeader: function setHeader(name, value) { - this._headers[name.toLowerCase()] = value; - }, - - /** - * Perform an HTTP GET. - * - * @param onComplete - * Short-circuit way to set the 'onComplete' method. Optional. - * @param onProgress - * Short-circuit way to set the 'onProgress' method. Optional. - * - * @return the request object. - */ - get: function get(onComplete, onProgress) { - return this.dispatch("GET", null, onComplete, onProgress); - }, - - /** - * Perform an HTTP PUT. - * - * @param data - * Data to be used as the request body. If this isn't a string - * it will be JSONified automatically. - * @param onComplete - * Short-circuit way to set the 'onComplete' method. Optional. - * @param onProgress - * Short-circuit way to set the 'onProgress' method. Optional. - * - * @return the request object. - */ - put: function put(data, onComplete, onProgress) { - return this.dispatch("PUT", data, onComplete, onProgress); - }, - - /** - * Perform an HTTP POST. - * - * @param data - * Data to be used as the request body. If this isn't a string - * it will be JSONified automatically. - * @param onComplete - * Short-circuit way to set the 'onComplete' method. Optional. - * @param onProgress - * Short-circuit way to set the 'onProgress' method. Optional. - * - * @return the request object. - */ - post: function post(data, onComplete, onProgress) { - return this.dispatch("POST", data, onComplete, onProgress); - }, - - /** - * Perform an HTTP DELETE. - * - * @param onComplete - * Short-circuit way to set the 'onComplete' method. Optional. - * @param onProgress - * Short-circuit way to set the 'onProgress' method. Optional. - * - * @return the request object. - */ - delete: function delete_(onComplete, onProgress) { - return this.dispatch("DELETE", null, onComplete, onProgress); - }, - - /** - * Abort an active request. - */ - abort: function abort() { - if (this.status != this.SENT && this.status != this.IN_PROGRESS) { - throw "Can only abort a request that has been sent."; - } - - this.status = this.ABORTED; - this.channel.cancel(Cr.NS_BINDING_ABORTED); - - if (this.timeoutTimer) { - // Clear the abort timer now that the channel is done. - this.timeoutTimer.clear(); - } - }, - - /*** Implementation stuff ***/ - - dispatch: function dispatch(method, data, onComplete, onProgress) { - if (this.status != this.NOT_SENT) { - throw "Request has already been sent!"; - } - - this.method = method; - if (onComplete) { - this.onComplete = onComplete; - } - if (onProgress) { - this.onProgress = onProgress; - } - - // Create and initialize HTTP channel. - let channel = Services.io.newChannelFromURI(this.uri, null, null) - .QueryInterface(Ci.nsIRequest) - .QueryInterface(Ci.nsIHttpChannel); - this.channel = channel; - channel.loadFlags |= this.loadFlags; - channel.notificationCallbacks = this; - - // Set request headers. - let headers = this._headers; - for (let key in headers) { - if (key == 'authorization') { - this._log.trace("HTTP Header " + key + ": ***** (suppressed)"); - } else { - this._log.trace("HTTP Header " + key + ": " + headers[key]); - } - channel.setRequestHeader(key, headers[key], false); - } - - // Set HTTP request body. - if (method == "PUT" || method == "POST") { - // Convert non-string bodies into JSON. - if (typeof data != "string") { - data = JSON.stringify(data); - } - - this._log.debug(method + " Length: " + data.length); - if (this._log.level <= Log4Moz.Level.Trace) { - this._log.trace(method + " Body: " + data); - } - - let stream = Cc["@mozilla.org/io/string-input-stream;1"] - .createInstance(Ci.nsIStringInputStream); - stream.setData(data, data.length); - - let type = headers["content-type"] || "text/plain"; - channel.QueryInterface(Ci.nsIUploadChannel); - channel.setUploadStream(stream, type, data.length); - } - // We must set this after setting the upload stream, otherwise it - // will always be 'PUT'. Yeah, I know. - channel.requestMethod = method; - - // Blast off! - channel.asyncOpen(this, null); - this.status = this.SENT; - this.delayTimeout(); - return this; - }, - - /** - * Create or push back the abort timer that kills this request. - */ - delayTimeout: function delayTimeout() { - if (this.timeout) { - Utils.namedTimer(this.abortTimeout, this.timeout * 1000, this, - "timeoutTimer"); - } - }, - - /** - * Abort the request based on a timeout. - */ - abortTimeout: function abortTimeout() { - this.abort(); - let error = Components.Exception("Aborting due to channel inactivity.", - Cr.NS_ERROR_NET_TIMEOUT); - if (!this.onComplete) { - this._log.error("Unexpected error: onComplete not defined in " + - "abortTimeout.") - return; - } - this.onComplete(error); - }, - - /*** nsIStreamListener ***/ - - onStartRequest: function onStartRequest(channel) { - if (this.status == this.ABORTED) { - this._log.trace("Not proceeding with onStartRequest, request was aborted."); - return; - } - - try { - channel.QueryInterface(Ci.nsIHttpChannel); - } catch (ex) { - this._log.error("Unexpected error: channel is not a nsIHttpChannel!"); - this.status = this.ABORTED; - channel.cancel(Cr.NS_BINDING_ABORTED); - return; - } - - this.status = this.IN_PROGRESS; - - this._log.trace("onStartRequest: " + channel.requestMethod + " " + - channel.URI.spec); - - // Create a response object and fill it with some data. - let response = this.response = new RESTResponse(); - response.request = this; - response.body = ""; - - // Define this here so that we don't have make a new one each time - // onDataAvailable() gets called. - this._inputStream = Cc["@mozilla.org/scriptableinputstream;1"] - .createInstance(Ci.nsIScriptableInputStream); - - this.delayTimeout(); - }, - - onStopRequest: function onStopRequest(channel, context, statusCode) { - if (this.timeoutTimer) { - // Clear the abort timer now that the channel is done. - this.timeoutTimer.clear(); - } - - // We don't want to do anything for a request that's already been aborted. - if (this.status == this.ABORTED) { - this._log.trace("Not proceeding with onStopRequest, request was aborted."); - return; - } - - try { - channel.QueryInterface(Ci.nsIHttpChannel); - } catch (ex) { - this._log.error("Unexpected error: channel not nsIHttpChannel!"); - this.status = this.ABORTED; - return; - } - this.status = this.COMPLETED; - - let statusSuccess = Components.isSuccessCode(statusCode); - let uri = channel && channel.URI && channel.URI.spec || ""; - this._log.trace("Channel for " + channel.requestMethod + " " + uri + - " returned status code " + statusCode); - - if (!this.onComplete) { - this._log.error("Unexpected error: onComplete not defined in " + - "abortRequest."); - this.onProgress = null; - return; - } - - // Throw the failure code and stop execution. Use Components.Exception() - // instead of Error() so the exception is QI-able and can be passed across - // XPCOM borders while preserving the status code. - if (!statusSuccess) { - let message = Components.Exception("", statusCode).name; - let error = Components.Exception(message, statusCode); - this.onComplete(error); - this.onComplete = this.onProgress = null; - return; - } - - this._log.debug(this.method + " " + uri + " " + this.response.status); - - // Additionally give the full response body when Trace logging. - if (this._log.level <= Log4Moz.Level.Trace) { - this._log.trace(this.method + " body: " + this.response.body); - } - - delete this._inputStream; - - this.onComplete(null); - this.onComplete = this.onProgress = null; - }, - - onDataAvailable: function onDataAvailable(req, cb, stream, off, count) { - this._inputStream.init(stream); - try { - this.response.body += this._inputStream.read(count); - } catch (ex) { - this._log.warn("Exception thrown reading " + count + - " bytes from the channel."); - this._log.debug(Utils.exceptionStr(ex)); - throw ex; - } - - try { - this.onProgress(); - } catch (ex) { - this._log.warn("Got exception calling onProgress handler, aborting " + - this.method + " " + req.URI.spec); - this._log.debug("Exception: " + Utils.exceptionStr(ex)); - this.abort(); - - if (!this.onComplete) { - this._log.error("Unexpected error: onComplete not defined in " + - "onDataAvailable."); - this.onProgress = null; - return; - } - - this.onComplete(ex); - this.onComplete = this.onProgress = null; - return; - } - - this.delayTimeout(); - }, - - /*** nsIInterfaceRequestor ***/ - - getInterface: function(aIID) { - return this.QueryInterface(aIID); - }, - - /*** nsIBadCertListener2 ***/ - - notifyCertProblem: function notifyCertProblem(socketInfo, sslStatus, targetHost) { - this._log.warn("Invalid HTTPS certificate encountered!"); - // Suppress invalid HTTPS certificate warnings in the UI. - // (The request will still fail.) - return true; - }, - - /*** nsIChannelEventSink ***/ - asyncOnChannelRedirect: - function asyncOnChannelRedirect(oldChannel, newChannel, flags, callback) { - - try { - newChannel.QueryInterface(Ci.nsIHttpChannel); - } catch (ex) { - this._log.error("Unexpected error: channel not nsIHttpChannel!"); - callback.onRedirectVerifyCallback(Cr.NS_ERROR_NO_INTERFACE); - return; - } - - this.channel = newChannel; - - // We let all redirects proceed. - callback.onRedirectVerifyCallback(Cr.NS_OK); - } -}; - - -/** - * Response object for a RESTRequest. This will be created automatically by - * the RESTRequest. - */ -function RESTResponse() { - this._log = Log4Moz.repository.getLogger(this._logName); - this._log.level = - Log4Moz.Level[Svc.Prefs.get("log.logger.network.resources")]; -} -RESTResponse.prototype = { - - _logName: "Sync.RESTResponse", - - /** - * Corresponding REST request - */ - request: null, - - /** - * HTTP status code - */ - get status() { - let status; - try { - let channel = this.request.channel.QueryInterface(Ci.nsIHttpChannel); - status = channel.responseStatus; - } catch (ex) { - this._log.debug("Caught exception fetching HTTP status code:" + - Utils.exceptionStr(ex)); - return null; - } - delete this.status; - return this.status = status; - }, - - /** - * Boolean flag that indicates whether the HTTP status code is 2xx or not. - */ - get success() { - let success; - try { - let channel = this.request.channel.QueryInterface(Ci.nsIHttpChannel); - success = channel.requestSucceeded; - } catch (ex) { - this._log.debug("Caught exception fetching HTTP success flag:" + - Utils.exceptionStr(ex)); - return null; - } - delete this.success; - return this.success = success; - }, - - /** - * Object containing HTTP headers (keyed as lower case) - */ - get headers() { - let headers = {}; - try { - this._log.trace("Processing response headers."); - let channel = this.request.channel.QueryInterface(Ci.nsIHttpChannel); - channel.visitResponseHeaders(function (header, value) { - headers[header.toLowerCase()] = value; - }); - } catch (ex) { - this._log.debug("Caught exception processing response headers:" + - Utils.exceptionStr(ex)); - return null; - } - - delete this.headers; - return this.headers = headers; - }, - - /** - * HTTP body (string) - */ - body: null - -}; - - /** * RESTRequest variant for use against a Sync storage server. */ diff --git a/services/sync/modules/service.js b/services/sync/modules/service.js index 73f0c6b98c4..f930ad99a5c 100644 --- a/services/sync/modules/service.js +++ b/services/sync/modules/service.js @@ -63,9 +63,9 @@ Cu.import("resource://services-sync/record.js"); Cu.import("resource://services-sync/constants.js"); Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/engines/clients.js"); -Cu.import("resource://services-sync/ext/Preferences.js"); +Cu.import("resource://services-common/preferences.js"); Cu.import("resource://services-sync/identity.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); Cu.import("resource://services-sync/resource.js"); Cu.import("resource://services-sync/rest.js"); Cu.import("resource://services-sync/status.js"); diff --git a/services/sync/modules/status.js b/services/sync/modules/status.js index a6b8b2dbfab..15a92c3cb3e 100644 --- a/services/sync/modules/status.js +++ b/services/sync/modules/status.js @@ -41,7 +41,7 @@ const Cr = Components.results; const Cu = Components.utils; Cu.import("resource://services-sync/constants.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); Cu.import("resource://services-sync/identity.js"); Cu.import("resource://gre/modules/Services.jsm"); diff --git a/services/sync/modules/util.js b/services/sync/modules/util.js index 9d696169253..6ab2d44a9bb 100644 --- a/services/sync/modules/util.js +++ b/services/sync/modules/util.js @@ -38,17 +38,15 @@ const EXPORTED_SYMBOLS = ["XPCOMUtils", "Services", "NetUtil", "PlacesUtils", "FileUtils", "Utils", "Async", "Svc", "Str"]; -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; -const Cu = Components.utils; +const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components; -Cu.import("resource://services-sync/async.js"); +Cu.import("resource://services-common/log4moz.js"); +Cu.import("resource://services-common/preferences.js"); +Cu.import("resource://services-common/stringbundle.js"); +Cu.import("resource://services-common/utils.js"); +Cu.import("resource://services-common/async.js"); Cu.import("resource://services-sync/constants.js"); -Cu.import("resource://services-sync/ext/Observers.js"); -Cu.import("resource://services-sync/ext/Preferences.js"); -Cu.import("resource://services-sync/ext/StringBundle.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/observers.js"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/PlacesUtils.jsm"); @@ -60,6 +58,14 @@ Cu.import("resource://gre/modules/FileUtils.jsm"); */ let Utils = { + // Alias in functions from CommonUtils. These previously were defined here. + // In the ideal world, references to these would be removed. + nextTick: CommonUtils.nextTick, + namedTimer: CommonUtils.namedTimer, + exceptionStr: CommonUtils.exceptionStr, + stackTrace: CommonUtils.stackTrace, + makeURI: CommonUtils.makeURI, + /** * Wrap a function to catch all exceptions and log them * @@ -269,62 +275,18 @@ let Utils = { return true; }, - exceptionStr: function Weave_exceptionStr(e) { - let message = e.message ? e.message : e; - return message + " " + Utils.stackTrace(e); - }, - - stackTrace: function Weave_stackTrace(e) { - // Wrapped nsIException - if (e.location){ - let frame = e.location; - let output = []; - while (frame) { - // Works on frames or exceptions, munges file:// URIs to shorten the paths - // FIXME: filename munging is sort of hackish, might be confusing if - // there are multiple extensions with similar filenames - let str = ""; - - let file = frame.filename || frame.fileName; - if (file){ - str = file.replace(/^(?:chrome|file):.*?([^\/\.]+\.\w+)$/, "$1"); - } - - if (frame.lineNumber){ - str += ":" + frame.lineNumber; - } - if (frame.name){ - str = frame.name + "()@" + str; - } - - if (str){ - output.push(str); - } - frame = frame.caller; - } - return "Stack trace: " + output.join(" < "); - } - // Standard JS exception - if (e.stack){ - return "JS Stack trace: " + e.stack.trim().replace(/\n/g, " < "). - replace(/@[^@]*?([^\/\.]+\.\w+:)/g, "@$1"); - } - - return "No traceback available"; - }, - // Generator and discriminator for HMAC exceptions. - // Split these out in case we want to make them richer in future, and to + // Split these out in case we want to make them richer in future, and to // avoid inevitable confusion if the message changes. throwHMACMismatch: function throwHMACMismatch(shouldBe, is) { throw "Record SHA256 HMAC mismatch: should be " + shouldBe + ", is " + is; }, - + isHMACMismatch: function isHMACMismatch(ex) { const hmacFail = "Record SHA256 HMAC mismatch: "; return ex && ex.indexOf && (ex.indexOf(hmacFail) == 0); }, - + /** * UTF8-encode a message and hash it with the given hasher. Returns a * string containing bytes. The hasher is reset if it's an HMAC hasher. @@ -832,18 +794,6 @@ let Utils = { return header += ', ext="' + ext +'"'; }, - makeURI: function Weave_makeURI(URIString) { - if (!URIString) - return null; - try { - return Services.io.newURI(URIString, null, null); - } catch (e) { - let log = Log4Moz.repository.getLogger("Sync.Utils"); - log.debug("Could not create URI: " + Utils.exceptionStr(e)); - return null; - } - }, - /** * Load a json object from disk * @@ -917,59 +867,6 @@ let Utils = { }); }, - /** - * Execute a function on the next event loop tick. - * - * @param callback - * Function to invoke. - * @param thisObj [optional] - * Object to bind the callback to. - */ - nextTick: function nextTick(callback, thisObj) { - if (thisObj) { - callback = callback.bind(thisObj); - } - Services.tm.currentThread.dispatch(callback, Ci.nsIThread.DISPATCH_NORMAL); - }, - - /** - * Return a timer that is scheduled to call the callback after waiting the - * provided time or as soon as possible. The timer will be set as a property - * of the provided object with the given timer name. - */ - namedTimer: function delay(callback, wait, thisObj, name) { - if (!thisObj || !name) { - throw "You must provide both an object and a property name for the timer!"; - } - - // Delay an existing timer if it exists - if (name in thisObj && thisObj[name] instanceof Ci.nsITimer) { - thisObj[name].delay = wait; - return; - } - - // Create a special timer that we can add extra properties - let timer = {}; - timer.__proto__ = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - - // Provide an easy way to clear out the timer - timer.clear = function() { - thisObj[name] = null; - timer.cancel(); - }; - - // Initialize the timer with a smart callback - timer.initWithCallback({ - notify: function notify() { - // Clear out the timer once it's been triggered - timer.clear(); - callback.call(thisObj, timer); - } - }, wait, timer.TYPE_ONE_SHOT); - - return thisObj[name] = timer; - }, - getIcon: function(iconUri, defaultIcon) { try { let iconURI = Utils.makeURI(iconUri); diff --git a/services/sync/tests/unit/head_appinfo.js b/services/sync/tests/unit/head_appinfo.js index ad5fa3683e4..5cad84a406b 100644 --- a/services/sync/tests/unit/head_appinfo.js +++ b/services/sync/tests/unit/head_appinfo.js @@ -49,10 +49,10 @@ function addResourceAlias() { Cu.import("resource://gre/modules/Services.jsm"); const resProt = Services.io.getProtocolHandler("resource") .QueryInterface(Ci.nsIResProtocolHandler); - let uri; - uri = Services.io.newURI("resource:///modules/services-sync/", null, null); - resProt.setSubstitution("services-sync", uri); - uri = Services.io.newURI("resource:///modules/services-crypto/", null, null); - resProt.setSubstitution("services-crypto", uri); + for each (let s in ["common", "sync", "crypto"]) { + let uri = Services.io.newURI("resource:///modules/services-" + s + "/", null, + null); + resProt.setSubstitution("services-" + s, uri); + } } addResourceAlias(); diff --git a/services/sync/tests/unit/head_helpers.js b/services/sync/tests/unit/head_helpers.js index f2ad151bdbd..27ae2ae0868 100644 --- a/services/sync/tests/unit/head_helpers.js +++ b/services/sync/tests/unit/head_helpers.js @@ -1,7 +1,8 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -Cu.import("resource://services-sync/async.js"); +Cu.import("resource://services-common/utils.js"); +Cu.import("resource://services-common/async.js"); Cu.import("resource://services-sync/identity.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-sync/record.js"); @@ -40,42 +41,8 @@ function waitForZeroTimer(callback) { timer = Utils.namedTimer(wait, 150, {}, "timer"); } -btoa = Cu.import("resource://services-sync/log4moz.js").btoa; -atob = Cu.import("resource://services-sync/log4moz.js").atob; -function getTestLogger(component) { - return Log4Moz.repository.getLogger("Testing"); -} - -function initTestLogging(level) { - function LogStats() { - this.errorsLogged = 0; - } - LogStats.prototype = { - format: function BF_format(message) { - if (message.level == Log4Moz.Level.Error) - this.errorsLogged += 1; - return message.loggerName + "\t" + message.levelDesc + "\t" + - message.message + "\n"; - } - }; - LogStats.prototype.__proto__ = new Log4Moz.Formatter(); - - var log = Log4Moz.repository.rootLogger; - var logStats = new LogStats(); - var appender = new Log4Moz.DumpAppender(logStats); - - if (typeof(level) == "undefined") - level = "Debug"; - getTestLogger().level = Log4Moz.Level[level]; - - log.level = Log4Moz.Level.Trace; - appender.level = Log4Moz.Level.Trace; - // Overwrite any other appenders (e.g. from previous incarnations) - log.ownAppenders = [appender]; - log.updateAppenders(); - - return logStats; -} +btoa = Cu.import("resource://services-common/log4moz.js").btoa; +atob = Cu.import("resource://services-common/log4moz.js").atob; // This is needed for loadAddonTestFunctions(). let gGlobalScope = this; @@ -298,21 +265,9 @@ function ensureThrows(func) { } -/** - * Print some debug message to the console. All arguments will be printed, - * separated by spaces. - * - * @param [arg0, arg1, arg2, ...] - * Any number of arguments to print out - * @usage _("Hello World") -> prints "Hello World" - * @usage _(1, 2, 3) -> prints "1 2 3" - */ -let _ = function(some, debug, text, to) print(Array.slice(arguments).join(" ")); - _("Setting the identity for passphrase"); Cu.import("resource://services-sync/identity.js"); - /* * Test setup helpers. */ @@ -334,32 +289,6 @@ function generateNewKeys(collections) { CollectionKeys.setContents(wbo.cleartext, modified); } -function do_check_empty(obj) { - do_check_attribute_count(obj, 0); -} - -function do_check_attribute_count(obj, c) { - do_check_eq(c, Object.keys(obj).length); -} - -function do_check_throws(aFunc, aResult, aStack) -{ - if (!aStack) { - try { - // We might not have a 'Components' object. - aStack = Components.stack.caller; - } catch (e) {} - } - - try { - aFunc(); - } catch (e) { - do_check_eq(e.result, aResult, aStack); - return; - } - do_throw("Expected result " + aResult + ", none thrown.", aStack); -} - /* * A fake engine implementation. * This is used all over the place. @@ -487,4 +416,4 @@ deepCopy: function deepCopy(thing, noSort) { } return ret; -}; \ No newline at end of file +}; diff --git a/services/sync/tests/unit/head_http_server.js b/services/sync/tests/unit/head_http_server.js index fd8c773e841..b506586ce30 100644 --- a/services/sync/tests/unit/head_http_server.js +++ b/services/sync/tests/unit/head_http_server.js @@ -4,7 +4,7 @@ const TEST_CLUSTER_URL = "http://localhost:8080/"; const TEST_SERVER_URL = "http://localhost:8080/"; // Shared logging for all HTTP server functions. -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); const SYNC_HTTP_LOGGER = "Sync.Test.Server"; const SYNC_API_VERSION = "1.1"; @@ -26,39 +26,6 @@ function return_timestamp(request, response, timestamp) { return timestamp; } -function httpd_setup (handlers, port) { - let port = port || 8080; - let server = new nsHttpServer(); - for (let path in handlers) { - server.registerPathHandler(path, handlers[path]); - } - try { - server.start(port); - } catch (ex) { - _("=========================================="); - _("Got exception starting HTTP server on port " + port); - _("Error: " + Utils.exceptionStr(ex)); - _("Is there a process already listening on port " + port + "?"); - _("=========================================="); - do_throw(ex); - } - - return server; -} - -function httpd_handler(statusCode, status, body) { - return function handler(request, response) { - // Allow test functions to inspect the request. - request.body = readBytesFromInputStream(request.bodyInputStream); - handler.request = request; - - response.setStatusLine(request.httpVersion, statusCode, status); - if (body) { - response.bodyOutputStream.write(body, body.length); - } - }; -} - function basic_auth_header(user, password) { return "Basic " + btoa(user + ":" + Utils.encodeUTF8(password)); } @@ -84,21 +51,6 @@ function httpd_basic_auth_handler(body, metadata, response) { response.bodyOutputStream.write(body, body.length); } -/* - * Read bytes string from an nsIInputStream. If 'count' is omitted, - * all available input is read. - */ -function readBytesFromInputStream(inputStream, count) { - var BinaryInputStream = Components.Constructor( - "@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); - if (!count) { - count = inputStream.available(); - } - return new BinaryInputStream(inputStream).readBytes(count); -} - /* * Represent a WBO on the server */ @@ -1044,51 +996,3 @@ function serverForUsers(users, contents, callback) { server.start(); return server; } - -/** - * Proxy auth helpers. - */ - -/** - * Fake a PAC to prompt a channel replacement. - */ -let PACSystemSettings = { - CID: Components.ID("{5645d2c1-d6d8-4091-b117-fe7ee4027db7}"), - contractID: "@mozilla.org/system-proxy-settings;1", - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory, - Ci.nsISystemProxySettings]), - - createInstance: function createInstance(outer, iid) { - if (outer) { - throw Cr.NS_ERROR_NO_AGGREGATION; - } - return this.QueryInterface(iid); - }, - - lockFactory: function lockFactory(lock) { - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - - // Replace this URI for each test to avoid caching. We want to ensure that - // each test gets a completely fresh setup. - PACURI: null, - getProxyForURI: function getProxyForURI(aURI) { - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - } -}; - -function installFakePAC() { - _("Installing fake PAC."); - Cm.nsIComponentRegistrar - .registerFactory(PACSystemSettings.CID, - "Fake system proxy-settings", - PACSystemSettings.contractID, - PACSystemSettings); -} - -function uninstallFakePAC() { - _("Uninstalling fake PAC."); - let CID = PACSystemSettings.CID; - Cm.nsIComponentRegistrar.unregisterFactory(CID, PACSystemSettings); -} diff --git a/services/sync/tests/unit/test_addons_engine.js b/services/sync/tests/unit/test_addons_engine.js index 7c704218def..a491afe40f4 100644 --- a/services/sync/tests/unit/test_addons_engine.js +++ b/services/sync/tests/unit/test_addons_engine.js @@ -6,9 +6,9 @@ Cu.import("resource://gre/modules/AddonManager.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://services-sync/addonsreconciler.js"); -Cu.import("resource://services-sync/async.js"); +Cu.import("resource://services-common/async.js"); Cu.import("resource://services-sync/engines/addons.js"); -Cu.import("resource://services-sync/ext/Preferences.js"); +Cu.import("resource://services-common/preferences.js"); Cu.import("resource://services-sync/service.js"); let prefs = new Preferences(); diff --git a/services/sync/tests/unit/test_addons_store.js b/services/sync/tests/unit/test_addons_store.js index 2fbccf19d89..1f5ebbbfac9 100644 --- a/services/sync/tests/unit/test_addons_store.js +++ b/services/sync/tests/unit/test_addons_store.js @@ -4,7 +4,7 @@ "use strict"; Cu.import("resource://services-sync/engines/addons.js"); -Cu.import("resource://services-sync/ext/Preferences.js"); +Cu.import("resource://services-common/preferences.js"); const HTTP_PORT = 8888; diff --git a/services/sync/tests/unit/test_bookmark_engine.js b/services/sync/tests/unit/test_bookmark_engine.js index 51a9e8bf8ab..d52208f3230 100644 --- a/services/sync/tests/unit/test_bookmark_engine.js +++ b/services/sync/tests/unit/test_bookmark_engine.js @@ -1,8 +1,8 @@ Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/engines/bookmarks.js"); Cu.import("resource://services-sync/record.js"); -Cu.import("resource://services-sync/log4moz.js"); -Cu.import("resource://services-sync/async.js"); +Cu.import("resource://services-common/log4moz.js"); +Cu.import("resource://services-common/async.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-sync/service.js"); diff --git a/services/sync/tests/unit/test_bookmark_legacy_microsummaries_support.js b/services/sync/tests/unit/test_bookmark_legacy_microsummaries_support.js index 1581d73fa6d..a3e47f5f97f 100644 --- a/services/sync/tests/unit/test_bookmark_legacy_microsummaries_support.js +++ b/services/sync/tests/unit/test_bookmark_legacy_microsummaries_support.js @@ -6,7 +6,7 @@ Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/engines/bookmarks.js"); Cu.import("resource://services-sync/record.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://gre/modules/Services.jsm"); diff --git a/services/sync/tests/unit/test_bookmark_livemarks.js b/services/sync/tests/unit/test_bookmark_livemarks.js index db69d50fe35..989dfc8847c 100644 --- a/services/sync/tests/unit/test_bookmark_livemarks.js +++ b/services/sync/tests/unit/test_bookmark_livemarks.js @@ -1,4 +1,4 @@ -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); Cu.import("resource://services-sync/record.js"); Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/engines/bookmarks.js"); diff --git a/services/sync/tests/unit/test_bookmark_record.js b/services/sync/tests/unit/test_bookmark_record.js index be530bc67ba..3e542475675 100644 --- a/services/sync/tests/unit/test_bookmark_record.js +++ b/services/sync/tests/unit/test_bookmark_record.js @@ -5,7 +5,7 @@ Cu.import("resource://services-sync/identity.js"); Cu.import("resource://services-sync/keys.js"); Cu.import("resource://services-sync/record.js"); Cu.import("resource://services-sync/engines/bookmarks.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); Cu.import("resource://services-sync/util.js"); function prepareBookmarkItem(collection, id) { diff --git a/services/sync/tests/unit/test_bookmark_smart_bookmarks.js b/services/sync/tests/unit/test_bookmark_smart_bookmarks.js index 217c64b82fd..dae8e98f085 100644 --- a/services/sync/tests/unit/test_bookmark_smart_bookmarks.js +++ b/services/sync/tests/unit/test_bookmark_smart_bookmarks.js @@ -1,7 +1,7 @@ Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/engines/bookmarks.js"); Cu.import("resource://services-sync/record.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-sync/service.js"); diff --git a/services/sync/tests/unit/test_corrupt_keys.js b/services/sync/tests/unit/test_corrupt_keys.js index 7c8e6004873..d5a06db54f2 100644 --- a/services/sync/tests/unit/test_corrupt_keys.js +++ b/services/sync/tests/unit/test_corrupt_keys.js @@ -7,7 +7,7 @@ Cu.import("resource://services-sync/constants.js"); Cu.import("resource://services-sync/record.js"); Cu.import("resource://services-sync/engines/tabs.js"); Cu.import("resource://services-sync/engines/history.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); add_test(function test_locally_changed_keys() { let passphrase = "abcdeabcdeabcdeabcdeabcdea"; diff --git a/services/sync/tests/unit/test_engine.js b/services/sync/tests/unit/test_engine.js index 14557c07d20..eaf05ad19d2 100644 --- a/services/sync/tests/unit/test_engine.js +++ b/services/sync/tests/unit/test_engine.js @@ -1,5 +1,5 @@ Cu.import("resource://services-sync/engines.js"); -Cu.import("resource://services-sync/ext/Observers.js"); +Cu.import("resource://services-common/observers.js"); Cu.import("resource://services-sync/util.js"); diff --git a/services/sync/tests/unit/test_errorhandler_filelog.js b/services/sync/tests/unit/test_errorhandler_filelog.js index 305cce76798..6be93b2e72d 100644 --- a/services/sync/tests/unit/test_errorhandler_filelog.js +++ b/services/sync/tests/unit/test_errorhandler_filelog.js @@ -4,7 +4,7 @@ Cu.import("resource://services-sync/service.js"); Cu.import("resource://services-sync/policies.js"); Cu.import("resource://services-sync/util.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); const logsdir = FileUtils.getDir("ProfD", ["weave", "logs"], true); const LOG_PREFIX_SUCCESS = "success-"; diff --git a/services/sync/tests/unit/test_forms_tracker.js b/services/sync/tests/unit/test_forms_tracker.js index 5506fcb6af4..9175ac3298b 100644 --- a/services/sync/tests/unit/test_forms_tracker.js +++ b/services/sync/tests/unit/test_forms_tracker.js @@ -1,6 +1,6 @@ Cu.import("resource://services-sync/engines/forms.js"); Cu.import("resource://services-sync/util.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); function run_test() { _("Verify we've got an empty tracker to work with."); diff --git a/services/sync/tests/unit/test_history_store.js b/services/sync/tests/unit/test_history_store.js index 24dbb7398bd..52b1a089302 100644 --- a/services/sync/tests/unit/test_history_store.js +++ b/services/sync/tests/unit/test_history_store.js @@ -1,6 +1,6 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://services-sync/engines/history.js"); -Cu.import("resource://services-sync/async.js"); +Cu.import("resource://services-common/async.js"); Cu.import("resource://services-sync/util.js"); const TIMESTAMP1 = (Date.now() - 103406528) * 1000; diff --git a/services/sync/tests/unit/test_httpd_sync_server.js b/services/sync/tests/unit/test_httpd_sync_server.js index d2bada23510..88fd90f6700 100644 --- a/services/sync/tests/unit/test_httpd_sync_server.js +++ b/services/sync/tests/unit/test_httpd_sync_server.js @@ -59,7 +59,7 @@ add_test(function test_url_parsing() { run_next_test(); }); -Cu.import("resource://services-sync/rest.js"); +Cu.import("resource://services-common/rest.js"); function localRequest(path) { _("localRequest: " + path); let url = "http://127.0.0.1:8080" + path; diff --git a/services/sync/tests/unit/test_jpakeclient.js b/services/sync/tests/unit/test_jpakeclient.js index 05cfb3c98aa..240a0ed1356 100644 --- a/services/sync/tests/unit/test_jpakeclient.js +++ b/services/sync/tests/unit/test_jpakeclient.js @@ -1,4 +1,4 @@ -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); Cu.import("resource://services-sync/identity.js"); Cu.import("resource://services-sync/jpakeclient.js"); Cu.import("resource://services-sync/constants.js"); @@ -190,7 +190,8 @@ function run_test() { initTestLogging("Trace"); Log4Moz.repository.getLogger("Sync.JPAKEClient").level = Log4Moz.Level.Trace; - Log4Moz.repository.getLogger("Sync.RESTRequest").level = Log4Moz.Level.Trace; + Log4Moz.repository.getLogger("Common.RESTRequest").level = + Log4Moz.Level.Trace; run_next_test(); } diff --git a/services/sync/tests/unit/test_load_modules.js b/services/sync/tests/unit/test_load_modules.js index 0a9fff57740..f277358c0e5 100644 --- a/services/sync/tests/unit/test_load_modules.js +++ b/services/sync/tests/unit/test_load_modules.js @@ -1,6 +1,5 @@ const modules = [ "addonsreconciler.js", - "async.js", "constants.js", "engines/addons.js", "engines/bookmarks.js", @@ -11,12 +10,9 @@ const modules = [ "engines/prefs.js", "engines/tabs.js", "engines.js", - "ext/Observers.js", - "ext/Preferences.js", "identity.js", "jpakeclient.js", "keys.js", - "log4moz.js", "main.js", "notifications.js", "policies.js", diff --git a/services/sync/tests/unit/test_node_reassignment.js b/services/sync/tests/unit/test_node_reassignment.js index 571ac0d02c3..db678724573 100644 --- a/services/sync/tests/unit/test_node_reassignment.js +++ b/services/sync/tests/unit/test_node_reassignment.js @@ -7,12 +7,12 @@ _("Test that node reassignment responses are respected on all kinds of " + // Don't sync any engines by default. Svc.DefaultPrefs.set("registerEngines", "") +Cu.import("resource://services-common/rest.js"); Cu.import("resource://services-sync/constants.js"); Cu.import("resource://services-sync/policies.js"); -Cu.import("resource://services-sync/rest.js"); Cu.import("resource://services-sync/service.js"); Cu.import("resource://services-sync/status.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); function run_test() { Log4Moz.repository.getLogger("Sync.AsyncResource").level = Log4Moz.Level.Trace; diff --git a/services/sync/tests/unit/test_places_guid_downgrade.js b/services/sync/tests/unit/test_places_guid_downgrade.js index 0e88415b905..16ef6f1176a 100644 --- a/services/sync/tests/unit/test_places_guid_downgrade.js +++ b/services/sync/tests/unit/test_places_guid_downgrade.js @@ -1,4 +1,4 @@ -Cu.import("resource://services-sync/async.js"); +Cu.import("resource://services-common/async.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/engines/history.js"); diff --git a/services/sync/tests/unit/test_prefs_store.js b/services/sync/tests/unit/test_prefs_store.js index c29c6995e71..25360992667 100644 --- a/services/sync/tests/unit/test_prefs_store.js +++ b/services/sync/tests/unit/test_prefs_store.js @@ -1,6 +1,6 @@ Cu.import("resource://services-sync/engines/prefs.js"); Cu.import("resource://services-sync/util.js"); -Cu.import("resource://services-sync/ext/Preferences.js"); +Cu.import("resource://services-common/preferences.js"); Cu.import("resource://gre/modules/LightweightThemeManager.jsm"); Cu.import("resource://gre/modules/Services.jsm"); diff --git a/services/sync/tests/unit/test_prefs_tracker.js b/services/sync/tests/unit/test_prefs_tracker.js index d0353d16543..ea4ee28d99d 100644 --- a/services/sync/tests/unit/test_prefs_tracker.js +++ b/services/sync/tests/unit/test_prefs_tracker.js @@ -1,7 +1,7 @@ Cu.import("resource://services-sync/engines/prefs.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-sync/constants.js"); -Cu.import("resource://services-sync/ext/Preferences.js"); +Cu.import("resource://services-common/preferences.js"); function run_test() { let engine = new PrefsEngine(); diff --git a/services/sync/tests/unit/test_records_crypto.js b/services/sync/tests/unit/test_records_crypto.js index 0927a2711fd..62e85b98da6 100644 --- a/services/sync/tests/unit/test_records_crypto.js +++ b/services/sync/tests/unit/test_records_crypto.js @@ -6,7 +6,7 @@ Cu.import("resource://services-sync/identity.js"); Cu.import("resource://services-sync/keys.js"); Cu.import("resource://services-sync/record.js"); Cu.import("resource://services-sync/resource.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); Cu.import("resource://services-sync/util.js"); let cryptoWrap; diff --git a/services/sync/tests/unit/test_resource.js b/services/sync/tests/unit/test_resource.js index 65bb7fb175a..559cdabc965 100644 --- a/services/sync/tests/unit/test_resource.js +++ b/services/sync/tests/unit/test_resource.js @@ -1,9 +1,9 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -Cu.import("resource://services-sync/ext/Observers.js"); +Cu.import("resource://services-common/observers.js"); Cu.import("resource://services-sync/identity.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); Cu.import("resource://services-sync/resource.js"); Cu.import("resource://services-sync/util.js"); diff --git a/services/sync/tests/unit/test_resource_async.js b/services/sync/tests/unit/test_resource_async.js index 432cb192e18..18e401b1172 100644 --- a/services/sync/tests/unit/test_resource_async.js +++ b/services/sync/tests/unit/test_resource_async.js @@ -1,9 +1,9 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -Cu.import("resource://services-sync/ext/Observers.js"); +Cu.import("resource://services-common/observers.js"); Cu.import("resource://services-sync/identity.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); Cu.import("resource://services-sync/resource.js"); Cu.import("resource://services-sync/util.js"); diff --git a/services/sync/tests/unit/test_service_changePassword.js b/services/sync/tests/unit/test_service_changePassword.js index 89d66a8fa21..0c7774563ea 100644 --- a/services/sync/tests/unit/test_service_changePassword.js +++ b/services/sync/tests/unit/test_service_changePassword.js @@ -6,7 +6,7 @@ Cu.import("resource://services-sync/main.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-sync/constants.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); function run_test() { initTestLogging("Trace"); diff --git a/services/sync/tests/unit/test_service_detect_upgrade.js b/services/sync/tests/unit/test_service_detect_upgrade.js index a045fef31c7..09904cf8e31 100644 --- a/services/sync/tests/unit/test_service_detect_upgrade.js +++ b/services/sync/tests/unit/test_service_detect_upgrade.js @@ -10,7 +10,7 @@ Cu.import("resource://services-sync/constants.js"); Cu.import("resource://services-sync/record.js"); Cu.import("resource://services-sync/keys.js"); Cu.import("resource://services-sync/engines/tabs.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); Engines.register(TabEngine); diff --git a/services/sync/tests/unit/test_service_getStorageInfo.js b/services/sync/tests/unit/test_service_getStorageInfo.js index f442443bb2e..b6347fd52e1 100644 --- a/services/sync/tests/unit/test_service_getStorageInfo.js +++ b/services/sync/tests/unit/test_service_getStorageInfo.js @@ -1,8 +1,8 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ +Cu.import("resource://services-common/rest.js"); Cu.import("resource://services-sync/service.js"); -Cu.import("resource://services-sync/rest.js"); Cu.import("resource://services-sync/constants.js"); Cu.import("resource://services-sync/util.js"); diff --git a/services/sync/tests/unit/test_service_login.js b/services/sync/tests/unit/test_service_login.js index 483d8b870bf..204324f9865 100644 --- a/services/sync/tests/unit/test_service_login.js +++ b/services/sync/tests/unit/test_service_login.js @@ -1,5 +1,5 @@ Cu.import("resource://services-sync/constants.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); Cu.import("resource://services-sync/service.js"); Cu.import("resource://services-sync/status.js"); Cu.import("resource://services-sync/util.js"); diff --git a/services/sync/tests/unit/test_service_migratePrefs.js b/services/sync/tests/unit/test_service_migratePrefs.js index d403faff759..3fc7c28eb1e 100644 --- a/services/sync/tests/unit/test_service_migratePrefs.js +++ b/services/sync/tests/unit/test_service_migratePrefs.js @@ -1,4 +1,4 @@ -Cu.import("resource://services-sync/ext/Preferences.js"); +Cu.import("resource://services-common/preferences.js"); function test_migrate_logging() { _("Testing log pref migration."); diff --git a/services/sync/tests/unit/test_service_startup.js b/services/sync/tests/unit/test_service_startup.js index 880bba11014..dd2e853d4d9 100644 --- a/services/sync/tests/unit/test_service_startup.js +++ b/services/sync/tests/unit/test_service_startup.js @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -Cu.import("resource://services-sync/ext/Observers.js"); +Cu.import("resource://services-common/observers.js"); Cu.import("resource://services-sync/status.js"); Cu.import("resource://services-sync/identity.js"); Cu.import("resource://services-sync/util.js"); diff --git a/services/sync/tests/unit/test_service_sync_remoteSetup.js b/services/sync/tests/unit/test_service_sync_remoteSetup.js index 494fb6a35e3..bc00d64a509 100644 --- a/services/sync/tests/unit/test_service_sync_remoteSetup.js +++ b/services/sync/tests/unit/test_service_sync_remoteSetup.js @@ -6,7 +6,7 @@ Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-sync/status.js"); Cu.import("resource://services-sync/constants.js"); Cu.import("resource://services-sync/keys.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); function run_test() { let logger = Log4Moz.repository.rootLogger; diff --git a/services/sync/tests/unit/test_service_verifyLogin.js b/services/sync/tests/unit/test_service_verifyLogin.js index a6a99ba2f33..f074dcbf0b1 100644 --- a/services/sync/tests/unit/test_service_verifyLogin.js +++ b/services/sync/tests/unit/test_service_verifyLogin.js @@ -1,5 +1,5 @@ Cu.import("resource://services-sync/constants.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); Cu.import("resource://services-sync/service.js"); Cu.import("resource://services-sync/status.js"); Cu.import("resource://services-sync/util.js"); diff --git a/services/sync/tests/unit/test_syncstoragerequest.js b/services/sync/tests/unit/test_syncstoragerequest.js index 083f86eda3a..8ccb9846171 100644 --- a/services/sync/tests/unit/test_syncstoragerequest.js +++ b/services/sync/tests/unit/test_syncstoragerequest.js @@ -5,7 +5,7 @@ Cu.import("resource://services-sync/rest.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-sync/identity.js"); Cu.import("resource://services-sync/constants.js"); -Cu.import("resource://services-sync/log4moz.js"); +Cu.import("resource://services-common/log4moz.js"); const STORAGE_REQUEST_RESOURCE_URL = TEST_SERVER_URL + "resource"; diff --git a/services/sync/tests/unit/test_utils_lazyStrings.js b/services/sync/tests/unit/test_utils_lazyStrings.js index 2a4b8ec45b8..68f9b35747e 100644 --- a/services/sync/tests/unit/test_utils_lazyStrings.js +++ b/services/sync/tests/unit/test_utils_lazyStrings.js @@ -1,5 +1,8 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +Cu.import("resource://services-common/stringbundle.js"); Cu.import("resource://services-sync/util.js"); -Cu.import("resource://services-sync/ext/StringBundle.js"); function run_test() { let fn = Utils.lazyStrings("sync"); diff --git a/services/sync/tests/unit/test_utils_makeURI.js b/services/sync/tests/unit/test_utils_makeURI.js deleted file mode 100644 index d4789d09a7c..00000000000 --- a/services/sync/tests/unit/test_utils_makeURI.js +++ /dev/null @@ -1,66 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -_("Make sure uri strings are converted to nsIURIs"); -Cu.import("resource://services-sync/util.js"); - -function run_test() { - _test_makeURI(); -} - -function _test_makeURI() { - _("Check http uris"); - let uri1 = "http://mozillalabs.com/"; - do_check_eq(Utils.makeURI(uri1).spec, uri1); - let uri2 = "http://www.mozillalabs.com/"; - do_check_eq(Utils.makeURI(uri2).spec, uri2); - let uri3 = "http://mozillalabs.com/path"; - do_check_eq(Utils.makeURI(uri3).spec, uri3); - let uri4 = "http://mozillalabs.com/multi/path"; - do_check_eq(Utils.makeURI(uri4).spec, uri4); - let uri5 = "http://mozillalabs.com/?query"; - do_check_eq(Utils.makeURI(uri5).spec, uri5); - let uri6 = "http://mozillalabs.com/#hash"; - do_check_eq(Utils.makeURI(uri6).spec, uri6); - - _("Check https uris"); - let uris1 = "https://mozillalabs.com/"; - do_check_eq(Utils.makeURI(uris1).spec, uris1); - let uris2 = "https://www.mozillalabs.com/"; - do_check_eq(Utils.makeURI(uris2).spec, uris2); - let uris3 = "https://mozillalabs.com/path"; - do_check_eq(Utils.makeURI(uris3).spec, uris3); - let uris4 = "https://mozillalabs.com/multi/path"; - do_check_eq(Utils.makeURI(uris4).spec, uris4); - let uris5 = "https://mozillalabs.com/?query"; - do_check_eq(Utils.makeURI(uris5).spec, uris5); - let uris6 = "https://mozillalabs.com/#hash"; - do_check_eq(Utils.makeURI(uris6).spec, uris6); - - _("Check chrome uris"); - let uric1 = "chrome://browser/content/browser.xul"; - do_check_eq(Utils.makeURI(uric1).spec, uric1); - let uric2 = "chrome://browser/skin/browser.css"; - do_check_eq(Utils.makeURI(uric2).spec, uric2); - let uric3 = "chrome://browser/locale/browser.dtd"; - do_check_eq(Utils.makeURI(uric3).spec, uric3); - - _("Check about uris"); - let uria1 = "about:weave"; - do_check_eq(Utils.makeURI(uria1).spec, uria1); - let uria2 = "about:weave/"; - do_check_eq(Utils.makeURI(uria2).spec, uria2); - let uria3 = "about:weave/path"; - do_check_eq(Utils.makeURI(uria3).spec, uria3); - let uria4 = "about:weave/multi/path"; - do_check_eq(Utils.makeURI(uria4).spec, uria4); - let uria5 = "about:weave/?query"; - do_check_eq(Utils.makeURI(uria5).spec, uria5); - let uria6 = "about:weave/#hash"; - do_check_eq(Utils.makeURI(uria6).spec, uria6); - - _("Invalid uris are undefined"); - do_check_eq(Utils.makeURI("mozillalabs.com"), undefined); - do_check_eq(Utils.makeURI("chrome://badstuff"), undefined); - do_check_eq(Utils.makeURI("this is a test"), undefined); -} diff --git a/services/sync/tests/unit/xpcshell.ini b/services/sync/tests/unit/xpcshell.ini index 83f4614f493..12c165be46c 100644 --- a/services/sync/tests/unit/xpcshell.ini +++ b/services/sync/tests/unit/xpcshell.ini @@ -1,9 +1,7 @@ [DEFAULT] -head = head_appinfo.js head_helpers.js head_http_server.js +head = head_appinfo.js ../../../common/tests/unit/head_helpers.js head_helpers.js head_http_server.js tail = -[test_load_modules.js] - # The manifest is roughly ordered from low-level to high-level. When making # systemic sweeping changes, this makes it easier to identify errors closer to # the source. @@ -27,23 +25,14 @@ tail = [test_utils_lazyStrings.js] [test_utils_lock.js] [test_utils_makeGUID.js] -[test_utils_makeURI.js] -[test_utils_namedTimer.js] [test_utils_notify.js] [test_utils_passphrase.js] [test_utils_pbkdf2.js] [test_utils_sha1.js] -[test_utils_stackTrace.js] [test_utils_utf8.js] # We have a number of other libraries that are pretty much standalone. -[test_Observers.js] -[test_Preferences.js] -[test_async_chain.js] -[test_async_querySpinningly.js] [test_httpd_sync_server.js] -[test_log4moz.js] -[test_restrequest.js] [test_jpakeclient.js] # Bug 618233: this test produces random failures on Windows 7. # Bug 676978: test hangs on Android (see also testing/xpcshell/xpcshell.ini) diff --git a/services/sync/tps/extensions/tps/modules/addons.jsm b/services/sync/tps/extensions/tps/modules/addons.jsm index 2a5ad0c3338..fcb0f72a194 100644 --- a/services/sync/tps/extensions/tps/modules/addons.jsm +++ b/services/sync/tps/extensions/tps/modules/addons.jsm @@ -42,7 +42,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components; Cu.import("resource://gre/modules/AddonManager.jsm"); Cu.import("resource://gre/modules/AddonRepository.jsm"); Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://services-sync/async.js"); +Cu.import("resource://services-common/async.js"); Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://tps/logger.jsm"); diff --git a/services/sync/tps/extensions/tps/modules/bookmarks.jsm b/services/sync/tps/extensions/tps/modules/bookmarks.jsm index 2eb065b3db2..2e2e1457457 100644 --- a/services/sync/tps/extensions/tps/modules/bookmarks.jsm +++ b/services/sync/tps/extensions/tps/modules/bookmarks.jsm @@ -50,7 +50,7 @@ const CU = Components.utils; CU.import("resource://tps/logger.jsm"); CU.import("resource://gre/modules/Services.jsm"); CU.import("resource://gre/modules/PlacesUtils.jsm"); -CU.import("resource://services-sync/async.js"); +CU.import("resource://services-common/async.js"); var DumpBookmarks = function TPS_Bookmarks__DumpBookmarks() { let writer = { diff --git a/services/sync/tps/extensions/tps/modules/history.jsm b/services/sync/tps/extensions/tps/modules/history.jsm index 31eadc67cb1..4fd1249a1b2 100644 --- a/services/sync/tps/extensions/tps/modules/history.jsm +++ b/services/sync/tps/extensions/tps/modules/history.jsm @@ -49,7 +49,7 @@ const CU = Components.utils; CU.import("resource://gre/modules/Services.jsm"); CU.import("resource://gre/modules/PlacesUtils.jsm"); CU.import("resource://tps/logger.jsm"); -CU.import("resource://services-sync/async.js"); +CU.import("resource://services-common/async.js"); var DumpHistory = function TPS_History__DumpHistory() { let writer = { diff --git a/services/sync/tps/extensions/tps/modules/tps.jsm b/services/sync/tps/extensions/tps/modules/tps.jsm index d65af3daacf..3420a2e51eb 100644 --- a/services/sync/tps/extensions/tps/modules/tps.jsm +++ b/services/sync/tps/extensions/tps/modules/tps.jsm @@ -47,7 +47,7 @@ const {classes: CC, interfaces: CI, utils: CU} = Components; CU.import("resource://services-sync/service.js"); CU.import("resource://services-sync/constants.js"); CU.import("resource://services-sync/engines.js"); -CU.import("resource://services-sync/async.js"); +CU.import("resource://services-common/async.js"); CU.import("resource://services-sync/util.js"); CU.import("resource://gre/modules/XPCOMUtils.jsm"); CU.import("resource://gre/modules/Services.jsm"); diff --git a/testing/xpcshell/xpcshell.ini b/testing/xpcshell/xpcshell.ini index 8c36df500e7..9483e6754ca 100644 --- a/testing/xpcshell/xpcshell.ini +++ b/testing/xpcshell/xpcshell.ini @@ -67,6 +67,7 @@ skip-if = os == "android" [include:content/base/test/unit/xpcshell.ini] [include:content/test/unit/xpcshell.ini] [include:toolkit/components/url-classifier/tests/unit/xpcshell.ini] +[include:services/common/tests/unit/xpcshell.ini] [include:services/crypto/tests/unit/xpcshell.ini] [include:services/crypto/components/tests/unit/xpcshell.ini] [include:services/sync/tests/unit/xpcshell.ini] From ccb6375f4f4f50d865f317dcc3c47a8a4bc86af0 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Mon, 9 Apr 2012 15:39:59 -0700 Subject: [PATCH 2/6] Bug 743413 - Move some utility functions from sync to common; r=rnewman --HG-- rename : services/sync/tests/unit/test_utils_atob.js => services/common/tests/unit/test_utils_atob.js rename : services/sync/tests/unit/test_utils_utf8.js => services/common/tests/unit/test_utils_utf8.js --- services/common/tests/unit/test_utils_atob.js | 11 ++++++ services/common/tests/unit/test_utils_utf8.js | 11 ++++++ services/common/tests/unit/xpcshell.ini | 2 ++ services/common/utils.js | 36 +++++++++++++++++++ services/sync/modules/util.js | 30 ++-------------- services/sync/tests/unit/test_utils_atob.js | 8 ----- services/sync/tests/unit/test_utils_utf8.js | 8 ----- services/sync/tests/unit/xpcshell.ini | 2 -- 8 files changed, 63 insertions(+), 45 deletions(-) create mode 100644 services/common/tests/unit/test_utils_atob.js create mode 100644 services/common/tests/unit/test_utils_utf8.js delete mode 100644 services/sync/tests/unit/test_utils_atob.js delete mode 100644 services/sync/tests/unit/test_utils_utf8.js diff --git a/services/common/tests/unit/test_utils_atob.js b/services/common/tests/unit/test_utils_atob.js new file mode 100644 index 00000000000..422fcab2004 --- /dev/null +++ b/services/common/tests/unit/test_utils_atob.js @@ -0,0 +1,11 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +Cu.import("resource://services-common/utils.js"); + +function run_test() { + let data = ["Zm9vYmE=", "Zm9vYmE==", "Zm9vYmE==="]; + for (let d in data) { + do_check_eq(CommonUtils.safeAtoB(data[d]), "fooba"); + } +} diff --git a/services/common/tests/unit/test_utils_utf8.js b/services/common/tests/unit/test_utils_utf8.js new file mode 100644 index 00000000000..b0fd540f5d9 --- /dev/null +++ b/services/common/tests/unit/test_utils_utf8.js @@ -0,0 +1,11 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +Cu.import("resource://services-common/utils.js"); + +function run_test() { + let str = "Umlaute: \u00FC \u00E4\n"; // Umlaute: ü ä + let encoded = CommonUtils.encodeUTF8(str); + let decoded = CommonUtils.decodeUTF8(encoded); + do_check_eq(decoded, str); +} diff --git a/services/common/tests/unit/xpcshell.ini b/services/common/tests/unit/xpcshell.ini index 788bf3a77ff..bf16ef42c6a 100644 --- a/services/common/tests/unit/xpcshell.ini +++ b/services/common/tests/unit/xpcshell.ini @@ -5,9 +5,11 @@ tail = # Test load modules first so syntax failures are caught early. [test_load_modules.js] +[test_utils_atob.js] [test_utils_makeURI.js] [test_utils_namedTimer.js] [test_utils_stackTrace.js] +[test_utils_utf8.js] [test_async_chain.js] [test_async_querySpinningly.js] diff --git a/services/common/utils.js b/services/common/utils.js index 1083bfb5c92..ca71bb8965c 100644 --- a/services/common/utils.js +++ b/services/common/utils.js @@ -7,6 +7,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; const EXPORTED_SYMBOLS = ["CommonUtils"]; Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://services-common/log4moz.js"); let CommonUtils = { @@ -122,4 +123,39 @@ let CommonUtils = { return thisObj[name] = timer; }, + encodeUTF8: function encodeUTF8(str) { + try { + str = this._utf8Converter.ConvertFromUnicode(str); + return str + this._utf8Converter.Finish(); + } catch (ex) { + return null; + } + }, + + decodeUTF8: function decodeUTF8(str) { + try { + str = this._utf8Converter.ConvertToUnicode(str); + return str + this._utf8Converter.Finish(); + } catch (ex) { + return null; + } + }, + + /** + * Trim excess padding from a Base64 string and atob(). + * + * See bug 562431 comment 4. + */ + safeAtoB: function safeAtoB(b64) { + let len = b64.length; + let over = len % 4; + return over ? atob(b64.substr(0, len - over)) : atob(b64); + }, }; + +XPCOMUtils.defineLazyGetter(CommonUtils, "_utf8Converter", function() { + let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"] + .createInstance(Ci.nsIScriptableUnicodeConverter); + converter.charset = "UTF-8"; + return converter; +}); diff --git a/services/sync/modules/util.js b/services/sync/modules/util.js index 6ab2d44a9bb..e2265763ad8 100644 --- a/services/sync/modules/util.js +++ b/services/sync/modules/util.js @@ -65,6 +65,9 @@ let Utils = { exceptionStr: CommonUtils.exceptionStr, stackTrace: CommonUtils.stackTrace, makeURI: CommonUtils.makeURI, + encodeUTF8: CommonUtils.encodeUTF8, + decodeUTF8: CommonUtils.decodeUTF8, + safeAtoB: CommonUtils.safeAtoB, /** * Wrap a function to catch all exceptions and log them @@ -887,24 +890,6 @@ let Utils = { return Str.errors.get("error.reason.unknown"); }, - encodeUTF8: function(str) { - try { - str = this._utf8Converter.ConvertFromUnicode(str); - return str + this._utf8Converter.Finish(); - } catch(ex) { - return null; - } - }, - - decodeUTF8: function(str) { - try { - str = this._utf8Converter.ConvertToUnicode(str); - return str + this._utf8Converter.Finish(); - } catch(ex) { - return null; - } - }, - /** * Generate 26 characters. */ @@ -1005,15 +990,6 @@ let Utils = { return acc.trim(); }, - // WeaveCrypto returns bad base64 strings. Truncate excess padding - // and decode. - // See Bug 562431, comment 4. - safeAtoB: function safeAtoB(b64) { - let len = b64.length; - let over = len % 4; - return over ? atob(b64.substr(0, len - over)) : atob(b64); - }, - /** * Create an array like the first but without elements of the second. Reuse * arrays if possible. diff --git a/services/sync/tests/unit/test_utils_atob.js b/services/sync/tests/unit/test_utils_atob.js deleted file mode 100644 index fd97dd1b3bc..00000000000 --- a/services/sync/tests/unit/test_utils_atob.js +++ /dev/null @@ -1,8 +0,0 @@ -Cu.import("resource://services-sync/util.js"); - -function run_test() { - let data = ["Zm9vYmE=", "Zm9vYmE==", "Zm9vYmE==="]; - for (let d in data) { - do_check_eq(Utils.safeAtoB(data[d]), "fooba"); - } -} diff --git a/services/sync/tests/unit/test_utils_utf8.js b/services/sync/tests/unit/test_utils_utf8.js deleted file mode 100644 index cddbe8c423e..00000000000 --- a/services/sync/tests/unit/test_utils_utf8.js +++ /dev/null @@ -1,8 +0,0 @@ -Cu.import("resource://services-sync/util.js"); - -function run_test() { - let str = "Umlaute: \u00FC \u00E4\n"; // Umlaute: ü ä - let encoded = Utils.encodeUTF8(str); - let decoded = Utils.decodeUTF8(encoded); - do_check_eq(decoded, str); -} diff --git a/services/sync/tests/unit/xpcshell.ini b/services/sync/tests/unit/xpcshell.ini index 12c165be46c..ef163c14a58 100644 --- a/services/sync/tests/unit/xpcshell.ini +++ b/services/sync/tests/unit/xpcshell.ini @@ -10,7 +10,6 @@ tail = [test_load_modules.js] # util contains a bunch of functionality used throughout. -[test_utils_atob.js] [test_utils_catch.js] [test_utils_deepCopy.js] [test_utils_deepEquals.js] @@ -29,7 +28,6 @@ tail = [test_utils_passphrase.js] [test_utils_pbkdf2.js] [test_utils_sha1.js] -[test_utils_utf8.js] # We have a number of other libraries that are pretty much standalone. [test_httpd_sync_server.js] From 0cb6812bd8f849fc084a1503c853662fc60aced9 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Mon, 9 Apr 2012 15:40:06 -0700 Subject: [PATCH 3/6] Bug 727210 - Implement client for Services' token server; r=rnewman --- services/common/Makefile.in | 1 + services/common/rest.js | 2 +- services/common/services-common.js | 2 + .../common/tests/unit/test_load_modules.js | 1 + .../tests/unit/test_tokenserverclient.js | 166 ++++++++++++ services/common/tests/unit/xpcshell.ini | 1 + services/common/tokenserverclient.js | 238 ++++++++++++++++++ 7 files changed, 410 insertions(+), 1 deletion(-) create mode 100644 services/common/tests/unit/test_tokenserverclient.js create mode 100644 services/common/tokenserverclient.js diff --git a/services/common/Makefile.in b/services/common/Makefile.in index 7a42949fdfb..25d537e903e 100644 --- a/services/common/Makefile.in +++ b/services/common/Makefile.in @@ -18,6 +18,7 @@ modules := \ preferences.js \ rest.js \ stringbundle.js \ + tokenserverclient.js \ utils.js \ $(NULL) diff --git a/services/common/rest.js b/services/common/rest.js index 0dec0c6fee9..acf7825113d 100644 --- a/services/common/rest.js +++ b/services/common/rest.js @@ -4,7 +4,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; -const EXPORTED_SYMBOLS = ["RESTRequest"]; +const EXPORTED_SYMBOLS = ["RESTRequest", "RESTResponse"]; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); diff --git a/services/common/services-common.js b/services/common/services-common.js index 17394f04b0f..26b514bcd48 100644 --- a/services/common/services-common.js +++ b/services/common/services-common.js @@ -3,3 +3,5 @@ pref("services.common.log.logger.rest.request", "Debug"); pref("services.common.log.logger.rest.response", "Debug"); + +pref("services.common.tokenserverclient.logger.level", "Info"); diff --git a/services/common/tests/unit/test_load_modules.js b/services/common/tests/unit/test_load_modules.js index e1104972600..7685126efaf 100644 --- a/services/common/tests/unit/test_load_modules.js +++ b/services/common/tests/unit/test_load_modules.js @@ -7,6 +7,7 @@ const modules = [ "preferences.js", "rest.js", "stringbundle.js", + "tokenserverclient.js", "utils.js", ]; diff --git a/services/common/tests/unit/test_tokenserverclient.js b/services/common/tests/unit/test_tokenserverclient.js new file mode 100644 index 00000000000..5d50d10f897 --- /dev/null +++ b/services/common/tests/unit/test_tokenserverclient.js @@ -0,0 +1,166 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +Cu.import("resource://services-common/async.js"); +Cu.import("resource://services-common/tokenserverclient.js"); + +function run_test() { + initTestLogging("Trace"); + + run_next_test(); +} + +add_test(function test_working_bid_exchange() { + _("Ensure that working BrowserID token exchange works as expected."); + + let service = "http://example.com/foo"; + + let server = httpd_setup({ + "/1.0/foo/1.0": function(request, response) { + do_check_true(request.hasHeader("accept")); + do_check_eq("application/json", request.getHeader("accept")); + + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "application/json"); + + let body = JSON.stringify({ + id: "id", + secret: "key", + api_endpoint: service, + uid: "uid", + }); + response.bodyOutputStream.write(body, body.length); + } + }); + + let client = new TokenServerClient(); + let cb = Async.makeSpinningCallback(); + let url = TEST_SERVER_URL + "1.0/foo/1.0"; + client.getTokenFromBrowserIDAssertion(url, "assertion", cb); + let result = cb.wait(); + do_check_eq("object", typeof(result)); + do_check_attribute_count(result, 4); + do_check_eq(service, result.endpoint); + do_check_eq("id", result.id); + do_check_eq("key", result.key); + do_check_eq("uid", result.uid); + + server.stop(run_next_test); +}); + +add_test(function test_invalid_arguments() { + _("Ensure invalid arguments to APIs are rejected."); + + let args = [ + [null, "assertion", function() {}], + ["http://example.com/", null, function() {}], + ["http://example.com/", "assertion", null] + ]; + + for each (let arg in args) { + try { + let client = new TokenServerClient(); + client.getTokenFromBrowserIDAssertion(arg[0], arg[1], arg[2]); + do_throw("Should never get here."); + } catch (ex) { + do_check_true(ex instanceof TokenServerClientError); + } + } + + run_next_test(); +}); + +add_test(function test_error_404() { + _("Ensure that 404 responses result in error."); + + let server = httpd_setup(); + + let client = new TokenServerClient(); + let url = TEST_SERVER_URL + "foo"; + client.getTokenFromBrowserIDAssertion(url, "assertion", function(error, r) { + do_check_neq(null, error); + do_check_eq("TokenServerClientServerError", error.name); + do_check_neq(null, error.response); + do_check_eq(null, r); + + server.stop(run_next_test); + }); +}); + +add_test(function test_bad_json() { + _("Ensure that malformed JSON is handled properly."); + + let server = httpd_setup({ + "/1.0/foo/1.0": function(request, response) { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "application/json"); + + let body = '{"id": "id", baz}' + response.bodyOutputStream.write(body, body.length); + } + }); + + let client = new TokenServerClient(); + let url = TEST_SERVER_URL + "1.0/foo/1.0"; + client.getTokenFromBrowserIDAssertion(url, "assertion", function(error, r) { + _(error); + do_check_neq(null, error); + do_check_eq("TokenServerClientServerError", error.name); + do_check_neq(null, error.response); + do_check_eq(null, r); + + server.stop(run_next_test); + }); +}); + +add_test(function test_unhandled_media_type() { + _("Ensure that unhandled media types throw an error."); + + let server = httpd_setup({ + "/1.0/foo/1.0": function(request, response) { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/plain"); + + let body = "hello, world"; + response.bodyOutputStream.write(body, body.length); + } + }); + + let url = TEST_SERVER_URL + "1.0/foo/1.0"; + let client = new TokenServerClient(); + client.getTokenFromBrowserIDAssertion(url, "assertion", function(error, r) { + do_check_neq(null, error); + do_check_eq("TokenServerClientError", error.name); + do_check_neq(null, error.response); + do_check_eq(null, r); + + server.stop(run_next_test); + }); +}); + +add_test(function test_rich_media_types() { + _("Ensure that extra tokens in the media type aren't rejected."); + + let server = httpd_setup({ + "/foo": function(request, response) { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "application/json; foo=bar; bar=foo"); + + let body = JSON.stringify({ + id: "id", + secret: "key", + api_endpoint: "foo", + uid: "uid", + }); + response.bodyOutputStream.write(body, body.length); + } + }); + + let url = TEST_SERVER_URL + "foo"; + let client = new TokenServerClient(); + client.getTokenFromBrowserIDAssertion(url, "assertion", function(error, r) { + do_check_eq(null, error); + + server.stop(run_next_test); + }); +}); diff --git a/services/common/tests/unit/xpcshell.ini b/services/common/tests/unit/xpcshell.ini index bf16ef42c6a..39cb5caa960 100644 --- a/services/common/tests/unit/xpcshell.ini +++ b/services/common/tests/unit/xpcshell.ini @@ -17,3 +17,4 @@ tail = [test_observers.js] [test_preferences.js] [test_restrequest.js] +[test_tokenserverclient.js] diff --git a/services/common/tokenserverclient.js b/services/common/tokenserverclient.js new file mode 100644 index 00000000000..b71d8d4f393 --- /dev/null +++ b/services/common/tokenserverclient.js @@ -0,0 +1,238 @@ +/* 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"; + +const EXPORTED_SYMBOLS = [ + "TokenServerClient", + "TokenServerClientError", + "TokenServerClientNetworkError", + "TokenServerClientServerError" +]; + +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +Cu.import("resource://services-common/log4moz.js"); +Cu.import("resource://services-common/preferences.js"); +Cu.import("resource://services-common/rest.js"); + +const Prefs = new Preferences("services.common.tokenserverclient."); + +/** + * Represents a TokenServerClient error that occurred on the client. + * + * This is the base type for all errors raised by client operations. + * + * @param message + * (string) Error message. + */ +function TokenServerClientError(message) { + this.name = "TokenServerClientError"; + this.message = message || "Client error."; +} +TokenServerClientError.prototype = new Error(); +TokenServerClientError.prototype.constructor = TokenServerClientError; + +/** + * Represents a TokenServerClient error that occurred in the network layer. + * + * @param error + * The underlying error thrown by the network layer. + */ +function TokenServerClientNetworkError(error) { + this.name = "TokenServerClientNetworkError"; + this.error = error; +} +TokenServerClientNetworkError.prototype = new TokenServerClientError(); +TokenServerClientNetworkError.prototype.constructor = + TokenServerClientNetworkError; + +/** + * Represents a TokenServerClient error that occurred on the server. + * + * This type will be encountered for all non-200 response codes from the + * server. + * + * @param message + * (string) Error message. + */ +function TokenServerClientServerError(message) { + this.name = "TokenServerClientServerError"; + this.message = message || "Server error."; +} +TokenServerClientServerError.prototype = new TokenServerClientError(); +TokenServerClientServerError.prototype.constructor = + TokenServerClientServerError; + +/** + * Represents a client to the Token Server. + * + * http://docs.services.mozilla.com/token/index.html + * + * The Token Server supports obtaining tokens for arbitrary apps by + * constructing URI paths of the form /. However, the service + * discovery mechanism emphasizes the use of full URIs and tries to not force + * the client to manipulate URIs. This client currently enforces this practice + * by not implementing an API which would perform URI manipulation. + * + * If you are tempted to implement this API in the future, consider this your + * warning that you may be doing it wrong and that you should store full URIs + * instead. + * + * Areas to Improve: + * + * - The server sends a JSON response on error. The client does not currently + * parse this. It might be convenient if it did. + * - Currently all non-200 status codes are rolled into one error type. It + * might be helpful if callers had a richer API that communicated who was + * at fault (e.g. differentiating a 503 from a 401). + */ +function TokenServerClient() { + this._log = Log4Moz.repository.getLogger("Common.TokenServerClient"); + this._log.level = Log4Moz.Level[Prefs.get("logger.level")]; +} +TokenServerClient.prototype = { + /** + * Logger instance. + */ + _log: null, + + /** + * Obtain a token from a BrowserID assertion against a specific URL. + * + * This asynchronously obtains the token. The callback receives 2 arguments. + * The first signifies an error and is a TokenServerClientError (or derived) + * type when an error occurs. If an HTTP response was seen, a RESTResponse + * instance will be stored in the "response" property of this object. + * + * The second argument to the callback is a map containing the results from + * the server. This map has the following keys: + * + * id (string) HTTP MAC public key identifier. + * key (string) HTTP MAC shared symmetric key. + * endpoint (string) URL where service can be connected to. + * uid (string) user ID for requested service. + * + * e.g. + * + * let client = new TokenServerClient(); + * let assertion = getBrowserIDAssertionFromSomewhere(); + * let url = "https://token.services.mozilla.com/1.0/sync/2.0"; + * + * client.getTokenFromBrowserIDAssertion(url, assertion, + * function(error, result) { + * if (error) { + * // Do error handling. + * return; + * } + * + * let {id: id, key: key, uid: uid, endpoint: endpoint} = result; + * // Do stuff with data and carry on. + * }); + * + * @param url + * (string) URL to fetch token from. + * @param assertion + * (string) BrowserID assertion to exchange token for. + * @param cb + * (function) Callback to be invoked with result of operation. + */ + getTokenFromBrowserIDAssertion: + function getTokenFromBrowserIDAssertion(url, assertion, cb) { + if (!url) { + throw new TokenServerClientError("url argument is not valid."); + } + + if (!assertion) { + throw new TokenServerClientError("assertion argument is not valid."); + } + + if (!cb) { + throw new TokenServerClientError("cb argument is not valid."); + } + + this._log.debug("Beginning BID assertion exchange: " + url); + + let req = new RESTRequest(url); + req.setHeader("accept", "application/json"); + req.setHeader("authorization", "Browser-ID " + assertion); + let client = this; + req.get(function onResponse(error) { + if (error) { + cb(new TokenServerClientNetworkError(error), null); + return; + } + + try { + client._processTokenResponse(this.response, cb); + } catch (ex) { + let error = new TokenServerClientError(ex); + error.response = this.response; + cb(error, null); + return; + } + }); + }, + + /** + * Handler to process token request responses. + * + * @param response + * RESTResponse from token HTTP request. + * @param cb + * The original callback passed to the public API. + */ + _processTokenResponse: function processTokenResponse(response, cb) { + this._log.debug("Got token response."); + + if (!response.success) { + this._log.info("Non-200 response code to token request: " + + response.status); + this._log.debug("Response body: " + response.body); + let error = new TokenServerClientServerError("Non 200 response code: " + + response.status); + error.response = response; + cb(error, null); + return; + } + + let ct = response.headers["content-type"]; + if (ct != "application/json" && ct.indexOf("application/json;") != 0) { + let error = new TokenServerClientError("Unsupported media type: " + ct); + error.response = response; + cb(error, null); + return; + } + + let result; + try { + result = JSON.parse(response.body); + } catch (ex) { + let error = new TokenServerClientServerError("Invalid JSON returned " + + "from server."); + error.response = response; + cb(error, null); + return; + } + + for each (let k in ["id", "secret", "api_endpoint", "uid"]) { + if (!(k in result)) { + let error = new TokenServerClientServerError("Expected key not " + + " present in result: " + + k); + error.response = response; + cb(error, null); + return; + } + } + + this._log.debug("Successful token response: " + result.id); + cb(null, { + id: result.id, + key: result.secret, + endpoint: result.api_endpoint, + uid: result.uid, + }); + } +}; From 4b97f724d0b5c074ecf26de349c7edd87fdc9aed Mon Sep 17 00:00:00 2001 From: Jonathan Griffin Date: Mon, 9 Apr 2012 17:09:20 -0700 Subject: [PATCH 4/6] Bug 743872 - update TPS to use latest pulsebuildmonitor, a=testonly, DONTBUILD, npotb --- testing/tps/setup.py | 2 +- testing/tps/tps/pulse.py | 19 ++++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/testing/tps/setup.py b/testing/tps/setup.py index 2d406d174b0..35f26c6315a 100644 --- a/testing/tps/setup.py +++ b/testing/tps/setup.py @@ -40,7 +40,7 @@ from setuptools import setup, find_packages version = '0.2.40' -deps = ['pulsebuildmonitor >= 0.2', 'MozillaPulse == .4', +deps = ['pulsebuildmonitor >= 0.62', 'MozillaPulse == 0.61', 'mozinfo == 0.3.1', 'mozprofile == 0.1t', 'mozprocess == 0.1a', 'mozrunner == 3.0a', 'mozregression == 0.3', 'mozautolog >= 0.2.1'] diff --git a/testing/tps/tps/pulse.py b/testing/tps/tps/pulse.py index b70474dae35..335b89cb198 100644 --- a/testing/tps/tps/pulse.py +++ b/testing/tps/tps/pulse.py @@ -51,7 +51,7 @@ class TPSPulseMonitor(PulseBuildMonitor): def __init__(self, extensionDir, platform='linux', config=None, autolog=False, emailresults=False, testfile=None, logfile=None, rlock=None, **kwargs): - self.buildtype = 'opt' + self.buildtype = ['opt'] self.autolog = autolog self.emailresults = emailresults self.testfile = testfile @@ -59,8 +59,8 @@ class TPSPulseMonitor(PulseBuildMonitor): self.rlock = rlock self.extensionDir = extensionDir self.config = config - self.tree = self.config.get('tree', ['services-central', 'places']) - self.platform = self.config.get('platform', 'linux') + self.tree = self.config.get('tree', ['services-central']) + self.platform = [self.config.get('platform', 'linux')] self.label=('crossweave@mozilla.com|tps_build_monitor_' + socket.gethostname()) @@ -70,10 +70,12 @@ class TPSPulseMonitor(PulseBuildMonitor): self.logger.addHandler(handler) PulseBuildMonitor.__init__(self, - tree=self.tree, + trees=self.tree, label=self.label, - mobile=False, logger=self.logger, + platforms=self.platform, + buildtypes=self.buildtype, + builds=True, **kwargs) def onPulseMessage(self, data): @@ -84,12 +86,7 @@ class TPSPulseMonitor(PulseBuildMonitor): print "=================================================================" print json.dumps(builddata) print "=================================================================" - try: - if not (builddata['platform'] == self.platform and - builddata['buildtype'] == self.buildtype): - return - except KeyError: - return + thread = TPSTestThread(self.extensionDir, builddata=builddata, emailresults=self.emailresults, From b5b7b0c4b3ee3e0501f49a771d703c96382246d5 Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Wed, 11 Apr 2012 08:24:18 +0300 Subject: [PATCH 5/6] Bug 742482 - Add support for MutationObserver.takeRecords(), r=sicking --- content/base/src/nsDOMMutationObserver.cpp | 47 +++++++++++++------ content/base/src/nsDOMMutationObserver.h | 2 + content/base/test/test_mutationobservers.html | 40 +++++++++++++++- .../core/nsIDOMMutationObserver.idl | 3 +- 4 files changed, 75 insertions(+), 17 deletions(-) diff --git a/content/base/src/nsDOMMutationObserver.cpp b/content/base/src/nsDOMMutationObserver.cpp index 6ad99fcb8d8..e0602fa40d6 100644 --- a/content/base/src/nsDOMMutationObserver.cpp +++ b/content/base/src/nsDOMMutationObserver.cpp @@ -585,6 +585,36 @@ nsDOMMutationObserver::Disconnect() return NS_OK; } +NS_IMETHODIMP +nsDOMMutationObserver::TakeRecords(nsIVariant** aRetVal) +{ + *aRetVal = TakeRecords().get(); + return NS_OK; +} + +already_AddRefed +nsDOMMutationObserver::TakeRecords() +{ + nsCOMPtr mutations = + do_CreateInstance("@mozilla.org/variant;1"); + PRInt32 len = mPendingMutations.Count(); + if (len == 0) { + mutations->SetAsEmptyArray(); + } else { + nsTArray mods(len); + for (PRInt32 i = 0; i < len; ++i) { + mods.AppendElement(mPendingMutations[i]); + } + + mutations->SetAsArray(nsIDataType::VTYPE_INTERFACE, + &NS_GET_IID(nsIDOMMutationRecord), + mods.Length(), + const_cast( + static_cast(mods.Elements()))); + mPendingMutations.Clear(); + } + return mutations.forget(); +} NS_IMETHODIMP nsDOMMutationObserver::Initialize(nsISupports* aOwner, JSContext* cx, @@ -639,21 +669,8 @@ nsDOMMutationObserver::HandleMutation() mPendingMutations.Clear(); return; } - - PRInt32 len = mPendingMutations.Count(); - nsTArray mods(len); - for (PRInt32 i = 0; i < len; ++i) { - mods.AppendElement(mPendingMutations[i]); - } - - nsCOMPtr mutations = - do_CreateInstance("@mozilla.org/variant;1"); - mutations->SetAsArray(nsIDataType::VTYPE_INTERFACE, - &NS_GET_IID(nsIDOMMutationRecord), - mods.Length(), - const_cast( - static_cast(mods.Elements()))); - mPendingMutations.Clear(); + + nsCOMPtr mutations = TakeRecords(); nsAutoMicroTask mt; sCurrentObserver = this; // For 'this' handling. mCallback->HandleMutations(mutations, this); diff --git a/content/base/src/nsDOMMutationObserver.h b/content/base/src/nsDOMMutationObserver.h index 4c276b26981..4bd78d02f53 100644 --- a/content/base/src/nsDOMMutationObserver.h +++ b/content/base/src/nsDOMMutationObserver.h @@ -320,6 +320,8 @@ protected: nsMutationReceiver* GetReceiverFor(nsINode* aNode, bool aMayCreate); void RemoveReceiver(nsMutationReceiver* aReceiver); + already_AddRefed TakeRecords(); + void GetAllSubtreeObserversFor(nsINode* aNode, nsTArray& aObservers); void ScheduleForRun(); diff --git a/content/base/test/test_mutationobservers.html b/content/base/test/test_mutationobservers.html index 1b8031455d2..8bf20e2a52e 100644 --- a/content/base/test/test_mutationobservers.html +++ b/content/base/test/test_mutationobservers.html @@ -477,7 +477,45 @@ function testModalDialog() { div.innerHTML = "foo"; window.showModalDialog("mutationobserver_dialog.html"); ok(didHandleCallback, "Should have called the callback while showing modal dialog!"); - then(); + then(testTakeRecords); +} + +function testTakeRecords() { + var s = "12"; + div.innerHTML = s; + var takenRecords; + m = new M(function(records, observer) { + is(records.length, 3, "Should have got 3 records"); + + is(records[0].type, "attributes", "Should have got attributes"); + is(records[0].attributeName, "foo", ""); + is(records[1].type, "childList", "Should have got childList"); + is(records[1].removedNodes.length, 2, "Should have got removedNodes"); + is(records[1].addedNodes.length, 2, "Should have got addedNodes"); + is(records[2].type, "attributes", "Should have got attributes"); + is(records[2].attributeName, "foo", ""); + + is(records.length, takenRecords.length, "Should have had similar mutations"); + is(records[0].type, takenRecords[0].type, "Should have had similar mutations"); + is(records[1].type, takenRecords[1].type, "Should have had similar mutations"); + is(records[2].type, takenRecords[2].type, "Should have had similar mutations"); + + is(records[1].removedNodes.length, takenRecords[1].removedNodes.length, "Should have had similar mutations"); + is(records[1].addedNodes.length, takenRecords[1].addedNodes.length, "Should have had similar mutations"); + + is(m.takeRecords().length, 0, "Shouldn't have any records"); + observer.disconnect(); + then(); + m = null; + }); + m.observe(div, { childList: true, attributes: true }); + div.setAttribute("foo", "bar"); + div.innerHTML = s; + div.removeAttribute("foo"); + takenRecords = m.takeRecords(); + div.setAttribute("foo", "bar"); + div.innerHTML = s; + div.removeAttribute("foo"); } SimpleTest.waitForExplicitFinish(); diff --git a/dom/interfaces/core/nsIDOMMutationObserver.idl b/dom/interfaces/core/nsIDOMMutationObserver.idl index 5cf7401f8e3..238f2223537 100644 --- a/dom/interfaces/core/nsIDOMMutationObserver.idl +++ b/dom/interfaces/core/nsIDOMMutationObserver.idl @@ -56,12 +56,13 @@ dictionary MutationObserverInit }; //[Constructor(in nsIMutationCallback aDoneCallback)] -[scriptable, builtinclass, uuid(daeba265-9aa7-45ab-8de2-b6b039c13ced)] +[scriptable, builtinclass, uuid(156e2ce4-e44a-45f3-92c2-e6611f391dae)] interface nsIDOMMozMutationObserver : nsISupports { [implicit_jscontext] void observe(in nsIDOMNode aTarget, in jsval aOptions); void disconnect(); + nsIVariant takeRecords(); }; [scriptable, function, uuid(fb539590-b088-4d07-96ff-2cefbc90a198)] From abeb0a1a6e30f254aeea1c5af970c35e5e277638 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Wed, 11 Apr 2012 11:27:25 -0700 Subject: [PATCH 6/6] Bug 744498: Fix some java warnings. r=kats --- mobile/android/base/db/BrowserProvider.java.in | 4 ++-- mobile/android/base/sqlite/SQLiteBridge.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mobile/android/base/db/BrowserProvider.java.in b/mobile/android/base/db/BrowserProvider.java.in index 5cc48bae294..46505fef187 100644 --- a/mobile/android/base/db/BrowserProvider.java.in +++ b/mobile/android/base/db/BrowserProvider.java.in @@ -455,7 +455,7 @@ public class BrowserProvider extends ContentProvider { } private void createDefaultBookmarks(SQLiteDatabase db, String pattern) { - Class stringsClass = R.string.class; + Class stringsClass = R.string.class; Field[] fields = stringsClass.getFields(); Pattern p = Pattern.compile(pattern); @@ -498,7 +498,7 @@ public class BrowserProvider extends ContentProvider { } private void setDefaultFavicon(SQLiteDatabase db, String name, String url) { - Class drawablesClass = R.drawable.class; + Class drawablesClass = R.drawable.class; ByteArrayOutputStream stream = null; try { // Look for a drawable with the id R.drawable.bookmarkdefaults_favicon_* diff --git a/mobile/android/base/sqlite/SQLiteBridge.java b/mobile/android/base/sqlite/SQLiteBridge.java index 601a79d9bc2..f631aa235c0 100644 --- a/mobile/android/base/sqlite/SQLiteBridge.java +++ b/mobile/android/base/sqlite/SQLiteBridge.java @@ -252,7 +252,7 @@ public class SQLiteBridge { SQLiteBridge bridge = null; try { bridge = new SQLiteBridge(path); - bridge.mDbPointer = bridge.openDatabase(path); + bridge.mDbPointer = SQLiteBridge.openDatabase(path); } catch(SQLiteBridgeException ex) { // catch and rethrow as a SQLiteException to match SQLiteDatabase throw new SQLiteException(ex.getMessage());