Merge services-central into mozilla-central

This commit is contained in:
Gregory Szorc 2012-04-11 11:46:19 -07:00
commit 9d66d1498f
98 changed files with 1884 additions and 1265 deletions

View File

@ -57,7 +57,7 @@
<implementation>
<constructor><![CDATA[
let temp = {};
Cu.import("resource://services-sync/ext/Observers.js", temp);
Cu.import("resource://services-common/observers.js", temp);
temp.Observers.add("weave:notification:added", this.onNotificationAdded, this);
temp.Observers.add("weave:notification:removed", this.onNotificationRemoved, this);
@ -67,7 +67,7 @@
<destructor><![CDATA[
let temp = {};
Cu.import("resource://services-sync/ext/Observers.js", temp);
Cu.import("resource://services-common/observers.js", temp);
temp.Observers.remove("weave:notification:added", this.onNotificationAdded, this);
temp.Observers.remove("weave:notification:removed", this.onNotificationRemoved, this);
]]></destructor>

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,34 @@
# 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 \
tokenserverclient.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

View File

@ -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 <rnewman@mozilla.com>
*
* 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;

View File

@ -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) {

578
services/common/rest.js Normal file
View File

@ -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", "RESTResponse"];
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 || "<unknown>";
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
};

View File

@ -0,0 +1,7 @@
// 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");
pref("services.common.tokenserverclient.logger.level", "Info");

View File

@ -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.

View File

@ -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

View File

@ -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();

View File

@ -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);
}

View File

@ -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.");

View File

@ -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");

View File

@ -0,0 +1,19 @@
/* 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",
"tokenserverclient.js",
"utils.js",
];
function run_test() {
for each (let m in modules) {
let resource = "resource://services-common/" + m;
Components.utils.import(resource, {});
}
}

View File

@ -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) {

View File

@ -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();
});

View File

@ -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();
});

View File

@ -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);
});
});

View File

@ -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);
});
});

View File

@ -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");
}
}

View File

@ -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);
}

View File

@ -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();
});

View File

@ -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");

View File

@ -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);
}

View File

@ -0,0 +1,20 @@
[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_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]
[test_log4moz.js]
[test_observers.js]
[test_preferences.js]
[test_restrequest.js]
[test_tokenserverclient.js]

View File

@ -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 <app>/<app_version>. 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,
});
}
};

161
services/common/utils.js Normal file
View File

@ -0,0 +1,161 @@
/* 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://gre/modules/XPCOMUtils.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 = "<file:unknown>";
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;
},
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;
});

View File

@ -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
"

View File

@ -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/

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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() {

View File

@ -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");

View File

@ -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");
/**

View File

@ -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 = {

View File

@ -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");

View File

@ -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");

View File

@ -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");
/*

View File

@ -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 || "<unknown>";
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.
*/

View File

@ -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");

View File

@ -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");

View File

@ -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,17 @@ 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,
encodeUTF8: CommonUtils.encodeUTF8,
decodeUTF8: CommonUtils.decodeUTF8,
safeAtoB: CommonUtils.safeAtoB,
/**
* Wrap a function to catch all exceptions and log them
*
@ -269,62 +278,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 = "<file:unknown>";
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 +797,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 +870,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);
@ -990,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.
*/
@ -1108,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.

View File

@ -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();

View File

@ -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;
};
};

View File

@ -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);
}

View File

@ -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();

View File

@ -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;

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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) {

View File

@ -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");

View File

@ -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";

View File

@ -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");

View File

@ -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-";

View File

@ -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.");

View File

@ -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;

View File

@ -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;

View File

@ -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();
}

View File

@ -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",

View File

@ -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;

View File

@ -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");

View File

@ -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");

View File

@ -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();

View File

@ -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;

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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);

View File

@ -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");

View File

@ -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");

View File

@ -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.");

View File

@ -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");

View File

@ -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;

View File

@ -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");

View File

@ -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";

View File

@ -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");
}
}

View File

@ -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");

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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.
@ -12,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]
@ -27,23 +24,13 @@ 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)

View File

@ -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");

View File

@ -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 = {

View File

@ -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 = {

View File

@ -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");

View File

@ -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']

View File

@ -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,

View File

@ -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]