зеркало из https://github.com/mozilla/gecko-dev.git
Merge cedar into mozilla-central
This commit is contained in:
Коммит
5376138030
|
@ -43,13 +43,15 @@
|
|||
#include "nsCoreUtils.h"
|
||||
#include "nsDocAccessible.h"
|
||||
|
||||
#include "nsIFontMetrics.h"
|
||||
#include "nsIThebesFontMetrics.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsIRenderingContext.h"
|
||||
#include "nsIComponentManager.h"
|
||||
|
||||
#include "gfxFont.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsTextAccessibleWrap Accessible
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -253,48 +255,30 @@ STDMETHODIMP nsTextAccessibleWrap::get_fontFamily(
|
|||
__try {
|
||||
*aFontFamily = NULL;
|
||||
|
||||
nsIFrame *frame = GetFrame();
|
||||
nsCOMPtr<nsIPresShell> presShell = GetPresShell();
|
||||
if (!frame || !presShell || !presShell->GetPresContext()) {
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRenderingContext> rc = presShell->GetReferenceRenderingContext();
|
||||
if (!rc) {
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
const nsStyleFont *font = frame->GetStyleFont();
|
||||
|
||||
const nsStyleVisibility *visibility = frame->GetStyleVisibility();
|
||||
|
||||
if (NS_FAILED(rc->SetFont(font->mFont, visibility->mLanguage,
|
||||
presShell->GetPresContext()->GetUserFontSet()))) {
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDeviceContext> deviceContext;
|
||||
rc->GetDeviceContext(*getter_AddRefs(deviceContext));
|
||||
if (!deviceContext) {
|
||||
nsIFrame* frame = GetFrame();
|
||||
if (!frame) {
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFontMetrics> fm;
|
||||
rc->GetFontMetrics(*getter_AddRefs(fm));
|
||||
if (!fm) {
|
||||
return E_FAIL;
|
||||
}
|
||||
frame->PresContext()->DeviceContext()->
|
||||
GetMetricsFor(frame->GetStyleFont()->mFont,
|
||||
frame->GetStyleVisibility()->mLanguage,
|
||||
frame->PresContext()->GetUserFontSet(),
|
||||
*getter_AddRefs(fm));
|
||||
|
||||
nsAutoString fontFamily;
|
||||
deviceContext->FirstExistingFont(fm->Font(), fontFamily);
|
||||
if (fontFamily.IsEmpty())
|
||||
nsCOMPtr<nsIThebesFontMetrics> tfm = do_QueryInterface(fm);
|
||||
const nsString& name = tfm->GetThebesFontGroup()->GetFontAt(0)->GetName();
|
||||
|
||||
if (name.IsEmpty())
|
||||
return S_FALSE;
|
||||
|
||||
*aFontFamily = ::SysAllocStringLen(fontFamily.get(), fontFamily.Length());
|
||||
*aFontFamily = ::SysAllocStringLen(name.get(), name.Length());
|
||||
if (!*aFontFamily)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
} __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
|
||||
} __except(FilterA11yExceptions(::GetExceptionCode(),
|
||||
GetExceptionInformation())) { }
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
|
|
@ -163,6 +163,7 @@ CSPPolicyURIListener.prototype = {
|
|||
else {
|
||||
// problem fetching policy so fail closed
|
||||
this._csp.refinePolicy("allow 'none'", this._docURI, this._docRequest);
|
||||
this._csp.refinePolicy("default-src 'none'", this._docURI, this._docRequest);
|
||||
}
|
||||
// resume the parent document request
|
||||
this._docRequest.resume();
|
||||
|
@ -187,7 +188,7 @@ function CSPRep() {
|
|||
}
|
||||
|
||||
CSPRep.SRC_DIRECTIVES = {
|
||||
ALLOW: "allow",
|
||||
DEFAULT_SRC: "default-src",
|
||||
SCRIPT_SRC: "script-src",
|
||||
STYLE_SRC: "style-src",
|
||||
MEDIA_SRC: "media-src",
|
||||
|
@ -205,6 +206,7 @@ CSPRep.URI_DIRECTIVES = {
|
|||
};
|
||||
|
||||
CSPRep.OPTIONS_DIRECTIVE = "options";
|
||||
CSPRep.ALLOW_DIRECTIVE = "allow";
|
||||
|
||||
/**
|
||||
* Factory to create a new CSPRep, parsed from a string.
|
||||
|
@ -258,6 +260,17 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
|||
continue directive;
|
||||
}
|
||||
|
||||
// ALLOW DIRECTIVE //////////////////////////////////////////////////
|
||||
// parse "allow" as equivalent to "default-src", at least until the spec
|
||||
// stabilizes, at which time we can stop parsing "allow"
|
||||
if (dirname === CSPRep.ALLOW_DIRECTIVE) {
|
||||
var dv = CSPSourceList.fromString(dirvalue, self, true);
|
||||
if (dv) {
|
||||
aCSPR._directives[SD.DEFAULT_SRC] = dv;
|
||||
continue directive;
|
||||
}
|
||||
}
|
||||
|
||||
// SOURCE DIRECTIVES ////////////////////////////////////////////////
|
||||
for each(var sdi in SD) {
|
||||
if (dirname === sdi) {
|
||||
|
@ -338,13 +351,13 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
|||
// POLICY_URI can only be alone
|
||||
if (aCSPR._directives.length > 0 || dirs.length > 1) {
|
||||
CSPError("policy-uri directive can only appear alone");
|
||||
return CSPRep.fromString("allow 'none'");
|
||||
return CSPRep.fromString("default-src 'none'");
|
||||
}
|
||||
// if we were called without a reference to the parent document request
|
||||
// we won't be able to suspend it while we fetch the policy -> fail closed
|
||||
if (!docRequest || !csp) {
|
||||
CSPError("The policy-uri cannot be fetched without a parent request and a CSP.");
|
||||
return CSPRep.fromString("allow 'none'");
|
||||
return CSPRep.fromString("default-src 'none'");
|
||||
}
|
||||
|
||||
var uri = '';
|
||||
|
@ -352,22 +365,22 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
|||
uri = gIoService.newURI(dirvalue, null, selfUri);
|
||||
} catch(e) {
|
||||
CSPError("could not parse URI in policy URI: " + dirvalue);
|
||||
return CSPRep.fromString("allow 'none'");
|
||||
return CSPRep.fromString("default-src 'none'");
|
||||
}
|
||||
|
||||
// Verify that policy URI comes from the same origin
|
||||
if (selfUri) {
|
||||
if (selfUri.host !== uri.host){
|
||||
CSPError("can't fetch policy uri from non-matching hostname: " + uri.host);
|
||||
return CSPRep.fromString("allow 'none'");
|
||||
return CSPRep.fromString("default-src 'none'");
|
||||
}
|
||||
if (selfUri.port !== uri.port){
|
||||
CSPError("can't fetch policy uri from non-matching port: " + uri.port);
|
||||
return CSPRep.fromString("allow 'none'");
|
||||
return CSPRep.fromString("default-src 'none'");
|
||||
}
|
||||
if (selfUri.scheme !== uri.scheme){
|
||||
CSPError("can't fetch policy uri from non-matching scheme: " + uri.scheme);
|
||||
return CSPRep.fromString("allow 'none'");
|
||||
return CSPRep.fromString("default-src 'none'");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -384,12 +397,12 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
|||
// resume the document request and apply most restrictive policy
|
||||
docRequest.resume();
|
||||
CSPError("Error fetching policy-uri: " + e);
|
||||
return CSPRep.fromString("allow 'none'");
|
||||
return CSPRep.fromString("default-src 'none'");
|
||||
}
|
||||
|
||||
// return a fully-open policy to be intersected with the contents of the
|
||||
// policy-uri when it returns
|
||||
return CSPRep.fromString("allow *");
|
||||
return CSPRep.fromString("default-src *");
|
||||
}
|
||||
|
||||
// UNIDENTIFIED DIRECTIVE /////////////////////////////////////////////
|
||||
|
@ -397,11 +410,11 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
|||
|
||||
} // end directive: loop
|
||||
|
||||
// if makeExplicit fails for any reason, default to allow 'none'. This
|
||||
// includes the case where "allow" is not present.
|
||||
// if makeExplicit fails for any reason, default to default-src 'none'. This
|
||||
// includes the case where "default-src" is not present.
|
||||
if (aCSPR.makeExplicit())
|
||||
return aCSPR;
|
||||
return CSPRep.fromString("allow 'none'", self);
|
||||
return CSPRep.fromString("default-src 'none'", self);
|
||||
};
|
||||
|
||||
CSPRep.prototype = {
|
||||
|
@ -534,22 +547,22 @@ CSPRep.prototype = {
|
|||
makeExplicit:
|
||||
function cspsd_makeExplicit() {
|
||||
var SD = CSPRep.SRC_DIRECTIVES;
|
||||
var allowDir = this._directives[SD.ALLOW];
|
||||
if (!allowDir) {
|
||||
CSPWarning("'allow' directive required but not present. Reverting to \"allow 'none'\"");
|
||||
var defaultSrcDir = this._directives[SD.DEFAULT_SRC];
|
||||
if (!defaultSrcDir) {
|
||||
CSPWarning("'allow' or 'default-src' directive required but not present. Reverting to \"default-src 'none'\"");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var dir in SD) {
|
||||
var dirv = SD[dir];
|
||||
if (dirv === SD.ALLOW) continue;
|
||||
if (dirv === SD.DEFAULT_SRC) continue;
|
||||
if (!this._directives[dirv]) {
|
||||
// implicit directive, make explicit.
|
||||
// All but frame-ancestors directive inherit from 'allow' (bug 555068)
|
||||
if (dirv === SD.FRAME_ANCESTORS)
|
||||
this._directives[dirv] = CSPSourceList.fromString("*");
|
||||
else
|
||||
this._directives[dirv] = allowDir.clone();
|
||||
this._directives[dirv] = defaultSrcDir.clone();
|
||||
this._directives[dirv]._isImplicit = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ function ContentSecurityPolicy() {
|
|||
CSPdebug("CSP CREATED");
|
||||
this._isInitialized = false;
|
||||
this._reportOnlyMode = false;
|
||||
this._policy = CSPRep.fromString("allow *");
|
||||
this._policy = CSPRep.fromString("default-src *");
|
||||
|
||||
// default options "wide open" since this policy will be intersected soon
|
||||
this._policy._allowInlineScripts = true;
|
||||
|
@ -71,7 +71,7 @@ function ContentSecurityPolicy() {
|
|||
this._requestHeaders = [];
|
||||
this._request = "";
|
||||
this._docRequest = null;
|
||||
CSPdebug("CSP POLICY INITED TO 'allow *'");
|
||||
CSPdebug("CSP POLICY INITED TO 'default-src *'");
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -85,7 +85,7 @@ function ContentSecurityPolicy() {
|
|||
csp._MAPPINGS=[];
|
||||
|
||||
/* default, catch-all case */
|
||||
csp._MAPPINGS[cp.TYPE_OTHER] = cspr_sd.ALLOW;
|
||||
csp._MAPPINGS[cp.TYPE_OTHER] = cspr_sd.DEFAULT_SRC;
|
||||
|
||||
/* self */
|
||||
csp._MAPPINGS[cp.TYPE_DOCUMENT] = null;
|
||||
|
@ -106,9 +106,9 @@ function ContentSecurityPolicy() {
|
|||
|
||||
|
||||
/* These must go through the catch-all */
|
||||
csp._MAPPINGS[cp.TYPE_XBL] = cspr_sd.ALLOW;
|
||||
csp._MAPPINGS[cp.TYPE_PING] = cspr_sd.ALLOW;
|
||||
csp._MAPPINGS[cp.TYPE_DTD] = cspr_sd.ALLOW;
|
||||
csp._MAPPINGS[cp.TYPE_XBL] = cspr_sd.DEFAULT_SRC;
|
||||
csp._MAPPINGS[cp.TYPE_PING] = cspr_sd.DEFAULT_SRC;
|
||||
csp._MAPPINGS[cp.TYPE_DTD] = cspr_sd.DEFAULT_SRC;
|
||||
}
|
||||
|
||||
ContentSecurityPolicy.prototype = {
|
||||
|
@ -386,7 +386,7 @@ ContentSecurityPolicy.prototype = {
|
|||
// report the frame-ancestor violation
|
||||
let directive = this._policy._directives[cspContext];
|
||||
let violatedPolicy = (directive._isImplicit
|
||||
? 'allow' : 'frame-ancestors ')
|
||||
? 'default-src' : 'frame-ancestors ')
|
||||
+ directive.toString();
|
||||
|
||||
this._asyncReportViolation(ancestors[i], violatedPolicy);
|
||||
|
@ -441,7 +441,7 @@ ContentSecurityPolicy.prototype = {
|
|||
try {
|
||||
let directive = this._policy._directives[cspContext];
|
||||
let violatedPolicy = (directive._isImplicit
|
||||
? 'allow' : cspContext)
|
||||
? 'default-src' : cspContext)
|
||||
+ ' ' + directive.toString();
|
||||
this._asyncReportViolation(aContentLocation, violatedPolicy);
|
||||
} catch(e) {
|
||||
|
|
|
@ -1 +1 @@
|
|||
X-Content-Security-Policy: allow 'self'
|
||||
X-Content-Security-Policy: default-src 'self'
|
||||
|
|
|
@ -91,7 +91,7 @@ window.checkResults = function(reportObj) {
|
|||
"http://example.org/tests/content/base/test/file_CSP.sjs?testid=img_bad&type=img/png",
|
||||
"Incorrect blocked uri");
|
||||
// correct violated-directive
|
||||
is(cspReport["violated-directive"], "allow http://mochi.test:8888",
|
||||
is(cspReport["violated-directive"], "default-src http://mochi.test:8888",
|
||||
"Incorrect violated directive");
|
||||
// not practical to test request-headers as header names and values will
|
||||
// change with the trunk
|
||||
|
|
|
@ -73,11 +73,11 @@ function do_check_has_key(foo, key, stack) {
|
|||
stack = Components.stack.caller;
|
||||
|
||||
var keys = [];
|
||||
for(let k in keys) { keys.push(k); }
|
||||
for (let k in foo) { keys.push(k); }
|
||||
var text = key + " in [" + keys.join(",") + "]";
|
||||
|
||||
for(var x in foo) {
|
||||
if(x == key) {
|
||||
for (var x in foo) {
|
||||
if (x == key) {
|
||||
//succeed
|
||||
++_passedChecks;
|
||||
dump("TEST-PASS | " + stack.filename + " | [" + stack.name + " : " +
|
||||
|
@ -359,17 +359,17 @@ test(
|
|||
|
||||
var cspr;
|
||||
var cspr_allowval;
|
||||
var SD = CSPRep.SRC_DIRECTIVES;
|
||||
|
||||
// check default policy "allow *"
|
||||
cspr = CSPRep.fromString("allow *", "http://self.com:80");
|
||||
//"ALLOW directive is missing when specified in fromString"
|
||||
do_check_has_key(cspr._directives, CSPRep.SRC_DIRECTIVES.ALLOW);
|
||||
// "DEFAULT_SRC directive is missing when specified in fromString"
|
||||
do_check_has_key(cspr._directives, SD.DEFAULT_SRC);
|
||||
|
||||
// ... and check that the other directives were auto-filled with the
|
||||
// ALLOW one.
|
||||
var SD = CSPRep.SRC_DIRECTIVES;
|
||||
cspr_allowval = cspr._directives[SD.ALLOW];
|
||||
for(var d in CSPRep.SRC_DIRECTIVES) {
|
||||
// DEFAULT_SRC one.
|
||||
cspr_allowval = cspr._directives[SD.DEFAULT_SRC];
|
||||
for(var d in SD) {
|
||||
//"Missing key " + d
|
||||
do_check_has_key(cspr._directives, SD[d]);
|
||||
//"Implicit directive " + d + " has non-allow value."
|
||||
|
@ -378,6 +378,37 @@ test(
|
|||
});
|
||||
|
||||
|
||||
test(
|
||||
function test_CSPRep_defaultSrc() {
|
||||
var cspr, cspr_default_val, cspr_allow;
|
||||
var SD = CSPRep.SRC_DIRECTIVES;
|
||||
|
||||
// apply policy of "default-src *" (e.g. "allow *")
|
||||
cspr = CSPRep.fromString("default-src *", "http://self.com:80");
|
||||
// "DEFAULT_SRC directive is missing when specified in fromString"
|
||||
do_check_has_key(cspr._directives, SD.DEFAULT_SRC);
|
||||
|
||||
// check that the other directives were auto-filled with the
|
||||
// DEFAULT_SRC one.
|
||||
cspr_default_val = cspr._directives[SD.DEFAULT_SRC];
|
||||
for (var d in SD) {
|
||||
do_check_has_key(cspr._directives, SD[d]);
|
||||
// "Implicit directive " + d + " has non-default-src value."
|
||||
do_check_eq(cspr._directives[SD[d]].toString(), cspr_default_val.toString());
|
||||
}
|
||||
|
||||
// check that |allow *| and |default-src *| are parsed equivalently and
|
||||
// result in the same set of explicit policy directives
|
||||
cspr = CSPRep.fromString("default-src *", "http://self.com:80");
|
||||
cspr_allow = CSPRep.fromString("allow *", "http://self.com:80");
|
||||
|
||||
for (var d in SD) {
|
||||
do_check_equivalent(cspr._directives[SD[d]],
|
||||
cspr_allow._directives[SD[d]]);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
test(
|
||||
function test_CSPRep_fromString_oneDir() {
|
||||
|
||||
|
|
|
@ -1055,6 +1055,36 @@ HttpChannelChild::SetupFallbackChannel(const char *aFallbackKey)
|
|||
DROP_DEAD();
|
||||
}
|
||||
|
||||
// The next four _should_ be implemented, but we need to figure out how
|
||||
// to transfer the data from the chrome process first.
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelChild::GetRemoteAddress(nsACString & _result)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelChild::GetRemotePort(PRInt32 * _result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(_result);
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelChild::GetLocalAddress(nsACString & _result)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelChild::GetLocalPort(PRInt32 * _result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(_result);
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// HttpChannelChild::nsICacheInfoChannel
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
@ -109,6 +109,10 @@ public:
|
|||
PRBool aMerge);
|
||||
// nsIHttpChannelInternal
|
||||
NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey);
|
||||
NS_IMETHOD GetLocalAddress(nsACString& addr);
|
||||
NS_IMETHOD GetLocalPort(PRInt32* port);
|
||||
NS_IMETHOD GetRemoteAddress(nsACString& addr);
|
||||
NS_IMETHOD GetRemotePort(PRInt32* port);
|
||||
// nsISupportsPriority
|
||||
NS_IMETHOD SetPriority(PRInt32 value);
|
||||
// nsIResumableChannel
|
||||
|
|
|
@ -45,6 +45,7 @@ class nsAHttpSegmentReader;
|
|||
class nsAHttpSegmentWriter;
|
||||
class nsIInterfaceRequestor;
|
||||
class nsIEventTarget;
|
||||
class nsITransport;
|
||||
class nsHttpRequestHead;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
@ -62,13 +63,14 @@ public:
|
|||
// called by the connection when it takes ownership of the transaction.
|
||||
virtual void SetConnection(nsAHttpConnection *) = 0;
|
||||
|
||||
// called by the connection to get security callbacks to set on the
|
||||
// called by the connection to get security callbacks to set on the
|
||||
// socket transport.
|
||||
virtual void GetSecurityCallbacks(nsIInterfaceRequestor **,
|
||||
nsIEventTarget **) = 0;
|
||||
|
||||
// called to report socket status (see nsITransportEventSink)
|
||||
virtual void OnTransportStatus(nsresult status, PRUint64 progress) = 0;
|
||||
virtual void OnTransportStatus(nsITransport* transport,
|
||||
nsresult status, PRUint64 progress) = 0;
|
||||
|
||||
// called to check the transaction status.
|
||||
virtual PRBool IsDone() = 0;
|
||||
|
@ -99,7 +101,8 @@ public:
|
|||
void SetConnection(nsAHttpConnection *); \
|
||||
void GetSecurityCallbacks(nsIInterfaceRequestor **, \
|
||||
nsIEventTarget **); \
|
||||
void OnTransportStatus(nsresult status, PRUint64 progress); \
|
||||
void OnTransportStatus(nsITransport* transport, \
|
||||
nsresult status, PRUint64 progress); \
|
||||
PRBool IsDone(); \
|
||||
nsresult Status(); \
|
||||
PRUint32 Available(); \
|
||||
|
|
|
@ -134,13 +134,16 @@ nsHttpChannel::nsHttpChannel()
|
|||
, mRequestTimeInitialized(PR_FALSE)
|
||||
{
|
||||
LOG(("Creating nsHttpChannel [this=%p]\n", this));
|
||||
// Subfields of unions cannot be targeted in an initializer list
|
||||
mSelfAddr.raw.family = PR_AF_UNSPEC;
|
||||
mPeerAddr.raw.family = PR_AF_UNSPEC;
|
||||
}
|
||||
|
||||
nsHttpChannel::~nsHttpChannel()
|
||||
{
|
||||
LOG(("Destroying nsHttpChannel [this=%p]\n", this));
|
||||
|
||||
if (mAuthProvider)
|
||||
if (mAuthProvider)
|
||||
mAuthProvider->Disconnect(NS_ERROR_ABORT);
|
||||
}
|
||||
|
||||
|
@ -150,7 +153,7 @@ nsHttpChannel::Init(nsIURI *uri,
|
|||
nsProxyInfo *proxyInfo)
|
||||
{
|
||||
nsresult rv = HttpBaseChannel::Init(uri, caps, proxyInfo);
|
||||
if (NS_FAILED(rv))
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
LOG(("nsHttpChannel::Init [this=%p]\n", this));
|
||||
|
@ -158,7 +161,7 @@ nsHttpChannel::Init(nsIURI *uri,
|
|||
mAuthProvider =
|
||||
do_CreateInstance("@mozilla.org/network/http-channel-auth-provider;1",
|
||||
&rv);
|
||||
if (NS_FAILED(rv))
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
rv = mAuthProvider->Init(this);
|
||||
|
||||
|
@ -3711,6 +3714,66 @@ nsHttpChannel::SetupFallbackChannel(const char *aFallbackKey)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHttpChannel::GetRemoteAddress(nsACString & _result)
|
||||
{
|
||||
if (mPeerAddr.raw.family == PR_AF_UNSPEC)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
_result.SetCapacity(64);
|
||||
PR_NetAddrToString(&mPeerAddr, _result.BeginWriting(), 64);
|
||||
_result.SetLength(strlen(_result.BeginReading()));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHttpChannel::GetRemotePort(PRInt32 * _result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(_result);
|
||||
|
||||
if (mPeerAddr.raw.family == PR_AF_INET) {
|
||||
*_result = (PRInt32)PR_ntohs(mPeerAddr.inet.port);
|
||||
}
|
||||
else if (mPeerAddr.raw.family == PR_AF_INET6) {
|
||||
*_result = (PRInt32)PR_ntohs(mPeerAddr.ipv6.port);
|
||||
}
|
||||
else
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHttpChannel::GetLocalAddress(nsACString & _result)
|
||||
{
|
||||
if (mSelfAddr.raw.family == PR_AF_UNSPEC)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
_result.SetCapacity(64);
|
||||
PR_NetAddrToString(&mSelfAddr, _result.BeginWriting(), 64);
|
||||
_result.SetLength(strlen(_result.BeginReading()));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHttpChannel::GetLocalPort(PRInt32 * _result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(_result);
|
||||
|
||||
if (mSelfAddr.raw.family == PR_AF_INET) {
|
||||
*_result = (PRInt32)PR_ntohs(mSelfAddr.inet.port);
|
||||
}
|
||||
else if (mSelfAddr.raw.family == PR_AF_INET6) {
|
||||
*_result = (PRInt32)PR_ntohs(mSelfAddr.ipv6.port);
|
||||
}
|
||||
else
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpChannel::nsISupportsPriority
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -4153,6 +4216,16 @@ nsHttpChannel::OnTransportStatus(nsITransport *trans, nsresult status,
|
|||
if (!mProgressSink)
|
||||
GetCallback(mProgressSink);
|
||||
|
||||
if (status == nsISocketTransport::STATUS_CONNECTED_TO ||
|
||||
status == nsISocketTransport::STATUS_WAITING_FOR) {
|
||||
nsCOMPtr<nsISocketTransport> socketTransport =
|
||||
do_QueryInterface(trans);
|
||||
if (socketTransport) {
|
||||
socketTransport->GetSelfAddr(&mSelfAddr);
|
||||
socketTransport->GetPeerAddr(&mPeerAddr);
|
||||
}
|
||||
}
|
||||
|
||||
// block socket status event after Cancel or OnStopRequest has been called.
|
||||
if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending && !(mLoadFlags & LOAD_BACKGROUND)) {
|
||||
LOG(("sending status notification [this=%p status=%x progress=%llu/%llu]\n",
|
||||
|
|
|
@ -137,6 +137,10 @@ public:
|
|||
NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext);
|
||||
// nsIHttpChannelInternal
|
||||
NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey);
|
||||
NS_IMETHOD GetLocalAddress(nsACString& addr);
|
||||
NS_IMETHOD GetLocalPort(PRInt32* port);
|
||||
NS_IMETHOD GetRemoteAddress(nsACString& addr);
|
||||
NS_IMETHOD GetRemotePort(PRInt32* port);
|
||||
// nsISupportsPriority
|
||||
NS_IMETHOD SetPriority(PRInt32 value);
|
||||
// nsIResumableChannel
|
||||
|
@ -347,6 +351,9 @@ private:
|
|||
// the cache entry's expiration time. Otherwise, it is not(see bug 567360).
|
||||
PRUint32 mRequestTimeInitialized : 1;
|
||||
|
||||
PRNetAddr mSelfAddr;
|
||||
PRNetAddr mPeerAddr;
|
||||
|
||||
nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack;
|
||||
|
||||
nsCOMPtr<nsICryptoHash> mHasher;
|
||||
|
|
|
@ -615,7 +615,8 @@ nsHttpConnection::OnSocketWritable()
|
|||
// here to reflect the fact that we are waiting. this message will be
|
||||
// trumped (overwritten) if the server responds quickly.
|
||||
//
|
||||
mTransaction->OnTransportStatus(nsISocketTransport::STATUS_WAITING_FOR,
|
||||
mTransaction->OnTransportStatus(mSocketTransport,
|
||||
nsISocketTransport::STATUS_WAITING_FOR,
|
||||
LL_ZERO);
|
||||
|
||||
rv = mSocketIn->AsyncWait(this, 0, 0, nsnull); // start reading
|
||||
|
@ -811,7 +812,7 @@ nsHttpConnection::OnTransportStatus(nsITransport *trans,
|
|||
PRUint64 progressMax)
|
||||
{
|
||||
if (mTransaction)
|
||||
mTransaction->OnTransportStatus(status, progress);
|
||||
mTransaction->OnTransportStatus(trans, status, progress);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -1491,7 +1491,7 @@ nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans,
|
|||
PRUint64 progressMax)
|
||||
{
|
||||
if (mTransaction)
|
||||
mTransaction->OnTransportStatus(status, progress);
|
||||
mTransaction->OnTransportStatus(trans, status, progress);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -367,7 +367,8 @@ nsHttpPipeline::GetSecurityCallbacks(nsIInterfaceRequestor **result,
|
|||
}
|
||||
|
||||
void
|
||||
nsHttpPipeline::OnTransportStatus(nsresult status, PRUint64 progress)
|
||||
nsHttpPipeline::OnTransportStatus(nsITransport* transport,
|
||||
nsresult status, PRUint64 progress)
|
||||
{
|
||||
LOG(("nsHttpPipeline::OnStatus [this=%x status=%x progress=%llu]\n",
|
||||
this, status, progress));
|
||||
|
@ -377,10 +378,10 @@ nsHttpPipeline::OnTransportStatus(nsresult status, PRUint64 progress)
|
|||
nsAHttpTransaction *trans;
|
||||
switch (status) {
|
||||
case NS_NET_STATUS_RECEIVING_FROM:
|
||||
// forward this only to the transaction currently recieving data
|
||||
// forward this only to the transaction currently recieving data
|
||||
trans = Response(0);
|
||||
if (trans)
|
||||
trans->OnTransportStatus(status, progress);
|
||||
trans->OnTransportStatus(transport, status, progress);
|
||||
break;
|
||||
default:
|
||||
// forward other notifications to all transactions
|
||||
|
@ -388,7 +389,7 @@ nsHttpPipeline::OnTransportStatus(nsresult status, PRUint64 progress)
|
|||
for (i=0; i<count; ++i) {
|
||||
trans = Request(i);
|
||||
if (trans)
|
||||
trans->OnTransportStatus(status, progress);
|
||||
trans->OnTransportStatus(transport, status, progress);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -351,14 +351,15 @@ nsHttpTransaction::GetSecurityCallbacks(nsIInterfaceRequestor **cb,
|
|||
}
|
||||
|
||||
void
|
||||
nsHttpTransaction::OnTransportStatus(nsresult status, PRUint64 progress)
|
||||
nsHttpTransaction::OnTransportStatus(nsITransport* transport,
|
||||
nsresult status, PRUint64 progress)
|
||||
{
|
||||
LOG(("nsHttpTransaction::OnSocketStatus [this=%x status=%x progress=%llu]\n",
|
||||
this, status, progress));
|
||||
|
||||
if (!mTransportSink)
|
||||
return;
|
||||
|
||||
|
||||
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
|
||||
// Need to do this before the STATUS_RECEIVING_FROM check below, to make
|
||||
|
@ -410,7 +411,7 @@ nsHttpTransaction::OnTransportStatus(nsresult status, PRUint64 progress)
|
|||
progressMax = 0;
|
||||
}
|
||||
|
||||
mTransportSink->OnTransportStatus(nsnull, status, progress, progressMax);
|
||||
mTransportSink->OnTransportStatus(transport, status, progress, progressMax);
|
||||
}
|
||||
|
||||
PRBool
|
||||
|
|
|
@ -46,12 +46,12 @@ class nsCString;
|
|||
interface nsIURI;
|
||||
interface nsIProxyInfo;
|
||||
|
||||
/**
|
||||
* Dumping ground for http. This interface will never be frozen. If you are
|
||||
* using any feature exposed by this interface, be aware that this interface
|
||||
/**
|
||||
* Dumping ground for http. This interface will never be frozen. If you are
|
||||
* using any feature exposed by this interface, be aware that this interface
|
||||
* will change and you will be broken. You have been warned.
|
||||
*/
|
||||
[scriptable, uuid(44e35ead-6656-4f9e-b51d-ebaf1bee6e2e)]
|
||||
[scriptable, uuid(12eb906a-71fe-4b79-b33a-6fe9ab57ea38)]
|
||||
interface nsIHttpChannelInternal : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -91,17 +91,58 @@ interface nsIHttpChannelInternal : nsISupports
|
|||
attribute boolean forceAllowThirdPartyCookie;
|
||||
|
||||
/**
|
||||
* Returns true iff the channel has been canceled.
|
||||
* True iff the channel has been canceled.
|
||||
*/
|
||||
readonly attribute boolean canceled;
|
||||
|
||||
/**
|
||||
* Lets externalhandler tell the channel it is open on behalf of a download
|
||||
* External handlers may set this to true to notify the channel
|
||||
* that it is open on behalf of a download.
|
||||
*/
|
||||
attribute boolean channelIsForDownload;
|
||||
|
||||
/**
|
||||
* Transfer chain of redirected cache-keys
|
||||
* The local IP address to which this channel is bound, in the
|
||||
* format produced by PR_NetAddrToString. May be IPv4 or IPv6.
|
||||
* Note: in the presence of NAT, this may not be the same as the
|
||||
* address that the remote host thinks it's talking to.
|
||||
*
|
||||
* May throw NS_ERROR_NOT_AVAILABLE if accessed when the channel's
|
||||
* endpoints are not yet determined, or in any case when
|
||||
* nsIHttpActivityObserver.isActive is false. See bugs 534698 and 526207.
|
||||
*/
|
||||
readonly attribute AUTF8String localAddress;
|
||||
|
||||
/**
|
||||
* The local port number to which this channel is bound.
|
||||
*
|
||||
* May throw NS_ERROR_NOT_AVAILABLE if accessed when the channel's
|
||||
* endpoints are not yet determined, or in any case when
|
||||
* nsIHttpActivityObserver.isActive is false. See bugs 534698 and 526207.
|
||||
*/
|
||||
readonly attribute PRInt32 localPort;
|
||||
|
||||
/**
|
||||
* The IP address of the remote host that this channel is
|
||||
* connected to, in the format produced by PR_NetAddrToString.
|
||||
*
|
||||
* May throw NS_ERROR_NOT_AVAILABLE if accessed when the channel's
|
||||
* endpoints are not yet determined, or in any case when
|
||||
* nsIHttpActivityObserver.isActive is false. See bugs 534698 and 526207.
|
||||
*/
|
||||
readonly attribute AUTF8String remoteAddress;
|
||||
|
||||
/**
|
||||
* The remote port number that this channel is connected to.
|
||||
*
|
||||
* May throw NS_ERROR_NOT_AVAILABLE if accessed when the channel's
|
||||
* endpoints are not yet determined, or in any case when
|
||||
* nsIHttpActivityObserver.isActive is false. See bugs 534698 and 526207.
|
||||
*/
|
||||
readonly attribute PRInt32 remotePort;
|
||||
|
||||
/**
|
||||
* Transfer chain of redirected cache-keys.
|
||||
*/
|
||||
[noscript] void setCacheKeysRedirectChain(in StringArray cacheKeys);
|
||||
};
|
||||
|
|
|
@ -20,6 +20,13 @@ TracingListener.prototype = {
|
|||
|
||||
gotOnStartRequest = true;
|
||||
|
||||
request.QueryInterface(Components.interfaces.nsIHttpChannelInternal);
|
||||
do_check_eq(request.localAddress, "127.0.0.1");
|
||||
do_check_eq(request.localPort > 0, true);
|
||||
do_check_neq(request.localPort, 4444);
|
||||
do_check_eq(request.remoteAddress, "127.0.0.1");
|
||||
do_check_eq(request.remotePort, 4444);
|
||||
|
||||
// Make sure listener can't be replaced after OnStartRequest was called.
|
||||
request.QueryInterface(Components.interfaces.nsITraceableChannel);
|
||||
try {
|
||||
|
|
|
@ -50,9 +50,16 @@ registrar.registerFactory(Components.ID("{fbfae60b-64a4-44ef-a911-08ceb70b9f31}"
|
|||
|
||||
}
|
||||
|
||||
// Provide resource://services-crypto if it isn't already available
|
||||
let weaveService = Cc["@mozilla.org/weave/service;1"].getService();
|
||||
weaveService.wrappedJSObject.addResourceAlias();
|
||||
// Register resource alias. Normally done in SyncComponents.manifest.
|
||||
function addResourceAlias() {
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
const resProt = Services.io.getProtocolHandler("resource")
|
||||
.QueryInterface(Ci.nsIResProtocolHandler);
|
||||
let uri = Services.io.newURI("resource:///modules/services-crypto/",
|
||||
null, null);
|
||||
resProt.setSubstitution("services-crypto", uri);
|
||||
}
|
||||
addResourceAlias();
|
||||
|
||||
/**
|
||||
* Print some debug message to the console. All arguments will be printed,
|
||||
|
|
|
@ -6,3 +6,6 @@ component {d28f8a0b-95da-48f4-b712-caf37097be41} Weave.js
|
|||
contract @mozilla.org/network/protocol/about;1?what=sync-log {d28f8a0b-95da-48f4-b712-caf37097be41}
|
||||
component {a08ee179-df50-48e0-9c87-79e4dd5caeb1} Weave.js
|
||||
contract @mozilla.org/network/protocol/about;1?what=sync-log.1 {a08ee179-df50-48e0-9c87-79e4dd5caeb1}
|
||||
# Register resource aliases
|
||||
resource services-sync resource:///modules/services-sync/
|
||||
resource services-crypto resource:///modules/services-crypto/
|
||||
|
|
|
@ -58,8 +58,6 @@ WeaveService.prototype = {
|
|||
break;
|
||||
|
||||
case "final-ui-startup":
|
||||
this.addResourceAlias();
|
||||
|
||||
// Force Weave service to load if it hasn't triggered from overlays
|
||||
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this.timer.initWithCallback({
|
||||
|
@ -71,25 +69,6 @@ WeaveService.prototype = {
|
|||
}, 10000, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
addResourceAlias: function() {
|
||||
let ioService = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService);
|
||||
let resProt = ioService.getProtocolHandler("resource")
|
||||
.QueryInterface(Ci.nsIResProtocolHandler);
|
||||
|
||||
// Only create alias if resource://services-sync doesn't already exist.
|
||||
if (!resProt.hasSubstitution("services-sync")) {
|
||||
let uri = ioService.newURI("resource:///modules/services-sync/",
|
||||
null, null);
|
||||
resProt.setSubstitution("services-sync", uri);
|
||||
}
|
||||
if (!resProt.hasSubstitution("services-crypto")) {
|
||||
let uri = ioService.newURI("resource:///modules/services-crypto/",
|
||||
null, null);
|
||||
resProt.setSubstitution("services-crypto", uri);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
error.login.reason.network = Failed to connect to the server
|
||||
error.login.reason.synckey = Wrong Sync Key
|
||||
# error.login.reason.password is deprecated.
|
||||
error.login.reason.password = Incorrect username or password
|
||||
error.login.reason.account = Incorrect account name or password
|
||||
error.login.reason.no_password= No saved password to use
|
||||
error.login.reason.no_synckey = No saved Sync Key to use
|
||||
|
|
|
@ -43,6 +43,11 @@ WEAVE_CHANNEL: "@weave_channel@",
|
|||
WEAVE_VERSION: "@weave_version@",
|
||||
WEAVE_ID: "@weave_id@",
|
||||
|
||||
// Sync Server API version that the client supports.
|
||||
SYNC_API_VERSION: "1.1",
|
||||
USER_API_VERSION: "1.0",
|
||||
MISC_API_VERSION: "1.0",
|
||||
|
||||
// Version of the data format this client supports. The data format describes
|
||||
// how records are packaged; this is separate from the Server API version and
|
||||
// the per-engine cleartext formats.
|
||||
|
@ -88,10 +93,16 @@ HMAC_EVENT_INTERVAL: 600000,
|
|||
// How long to wait between sync attempts if the Master Password is locked.
|
||||
MASTER_PASSWORD_LOCKED_RETRY_INTERVAL: 15 * 60 * 1000, // 15 minutes
|
||||
|
||||
// 50 is hardcoded here because of URL length restrictions.
|
||||
// (GUIDs can be up to 64 chars long)
|
||||
// Separate from the ID fetch batch size to allow tuning for mobile.
|
||||
MOBILE_BATCH_SIZE: 50,
|
||||
|
||||
// 50 is hardcoded here because of URL length restrictions.
|
||||
// (GUIDs can be up to 64 chars long.)
|
||||
// Individual engines can set different values for their limit if their
|
||||
// identifiers are shorter.
|
||||
DEFAULT_GUID_FETCH_BATCH_SIZE: 50,
|
||||
DEFAULT_MOBILE_GUID_FETCH_BATCH_SIZE: 50,
|
||||
|
||||
// Default batch size for applying incoming records.
|
||||
DEFAULT_STORE_BATCH_SIZE: 1,
|
||||
HISTORY_STORE_BATCH_SIZE: 50, // same as MOBILE_BATCH_SIZE
|
||||
|
|
|
@ -416,14 +416,33 @@ function SyncEngine(name) {
|
|||
Engine.call(this, name || "SyncEngine");
|
||||
this.loadToFetch();
|
||||
}
|
||||
|
||||
// Enumeration to define approaches to handling bad records.
|
||||
// Attached to the constructor to allow use as a kind of static enumeration.
|
||||
SyncEngine.kRecoveryStrategy = {
|
||||
ignore: "ignore",
|
||||
retry: "retry",
|
||||
error: "error"
|
||||
};
|
||||
|
||||
SyncEngine.prototype = {
|
||||
__proto__: Engine.prototype,
|
||||
_recordObj: CryptoWrapper,
|
||||
version: 1,
|
||||
|
||||
// How many records to pull in a single sync. This is primarily to avoid very
|
||||
// long first syncs against profiles with many history records.
|
||||
downloadLimit: null,
|
||||
|
||||
// How many records to pull at one time when specifying IDs. This is to avoid
|
||||
// URI length limitations.
|
||||
guidFetchBatchSize: DEFAULT_GUID_FETCH_BATCH_SIZE,
|
||||
mobileGUIDFetchBatchSize: DEFAULT_MOBILE_GUID_FETCH_BATCH_SIZE,
|
||||
|
||||
// How many records to process in a single batch.
|
||||
applyIncomingBatchSize: DEFAULT_STORE_BATCH_SIZE,
|
||||
|
||||
get storageURL() Svc.Prefs.get("clusterURL") + Svc.Prefs.get("storageAPI") +
|
||||
get storageURL() Svc.Prefs.get("clusterURL") + SYNC_API_VERSION +
|
||||
"/" + ID.get("WeaveID").username + "/storage/",
|
||||
|
||||
get engineURL() this.storageURL + this.name,
|
||||
|
@ -589,7 +608,9 @@ SyncEngine.prototype = {
|
|||
// Figure out how many total items to fetch this sync; do less on mobile.
|
||||
let batchSize = Infinity;
|
||||
let newitems = new Collection(this.engineURL, this._recordObj);
|
||||
if (Svc.Prefs.get("client.type") == "mobile") {
|
||||
let isMobile = (Svc.Prefs.get("client.type") == "mobile");
|
||||
|
||||
if (isMobile) {
|
||||
batchSize = MOBILE_BATCH_SIZE;
|
||||
}
|
||||
newitems.newer = this.lastSync;
|
||||
|
@ -640,14 +661,37 @@ SyncEngine.prototype = {
|
|||
try {
|
||||
try {
|
||||
item.decrypt();
|
||||
} catch (ex if (Utils.isHMACMismatch(ex) &&
|
||||
self.handleHMACMismatch(item))) {
|
||||
// Let's try handling it.
|
||||
// If the callback returns true, try decrypting again, because
|
||||
// we've got new keys.
|
||||
self._log.info("Trying decrypt again...");
|
||||
item.decrypt();
|
||||
}
|
||||
} catch (ex if Utils.isHMACMismatch(ex)) {
|
||||
let strategy = self.handleHMACMismatch(item, true);
|
||||
if (strategy == SyncEngine.kRecoveryStrategy.retry) {
|
||||
// You only get one retry.
|
||||
try {
|
||||
// Try decrypting again, typically because we've got new keys.
|
||||
self._log.info("Trying decrypt again...");
|
||||
item.decrypt();
|
||||
strategy = null;
|
||||
} catch (ex if Utils.isHMACMismatch(ex)) {
|
||||
strategy = self.handleHMACMismatch(item, false);
|
||||
}
|
||||
}
|
||||
|
||||
switch (strategy) {
|
||||
case null:
|
||||
// Retry succeeded! No further handling.
|
||||
break;
|
||||
case SyncEngine.kRecoveryStrategy.retry:
|
||||
self._log.debug("Ignoring second retry suggestion.");
|
||||
// Fall through to error case.
|
||||
case SyncEngine.kRecoveryStrategy.error:
|
||||
self._log.warn("Error decrypting record: " + Utils.exceptionStr(ex));
|
||||
failed.push(item.id);
|
||||
return;
|
||||
case SyncEngine.kRecoveryStrategy.ignore:
|
||||
self._log.debug("Ignoring record " + item.id +
|
||||
" with bad HMAC: already handled.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (ex) {
|
||||
self._log.warn("Error decrypting record: " + Utils.exceptionStr(ex));
|
||||
failed.push(item.id);
|
||||
|
@ -718,7 +762,12 @@ SyncEngine.prototype = {
|
|||
this.lastSync = this.lastModified;
|
||||
}
|
||||
|
||||
// Mobile: process any backlog of GUIDs
|
||||
// Process any backlog of GUIDs.
|
||||
// At this point we impose an upper limit on the number of items to fetch
|
||||
// in a single request, even for desktop, to avoid hitting URI limits.
|
||||
batchSize = isMobile ? this.mobileGUIDFetchBatchSize :
|
||||
this.guidFetchBatchSize;
|
||||
|
||||
while (fetchBatch.length) {
|
||||
// Reuse the original query, but get rid of the restricting params
|
||||
// and batch remaining records.
|
||||
|
@ -1007,8 +1056,32 @@ SyncEngine.prototype = {
|
|||
new Resource(this.engineURL).delete();
|
||||
this._resetClient();
|
||||
},
|
||||
|
||||
handleHMACMismatch: function handleHMACMismatch(item) {
|
||||
return Weave.Service.handleHMACEvent();
|
||||
|
||||
removeClientData: function removeClientData() {
|
||||
// Implement this method in engines that store client specific data
|
||||
// on the server.
|
||||
},
|
||||
|
||||
/*
|
||||
* Decide on (and partially effect) an error-handling strategy.
|
||||
*
|
||||
* Asks the Service to respond to an HMAC error, which might result in keys
|
||||
* being downloaded. That call returns true if an action which might allow a
|
||||
* retry to occur.
|
||||
*
|
||||
* If `mayRetry` is truthy, and the Service suggests a retry,
|
||||
* handleHMACMismatch returns kRecoveryStrategy.retry. Otherwise, it returns
|
||||
* kRecoveryStrategy.error.
|
||||
*
|
||||
* Subclasses of SyncEngine can override this method to allow for different
|
||||
* behavior -- e.g., to delete and ignore erroneous entries.
|
||||
*
|
||||
* All return values will be part of the kRecoveryStrategy enumeration.
|
||||
*/
|
||||
handleHMACMismatch: function handleHMACMismatch(item, mayRetry) {
|
||||
// By default we either try again, or bail out noisily.
|
||||
return (Weave.Service.handleHMACEvent() && mayRetry) ?
|
||||
SyncEngine.kRecoveryStrategy.retry :
|
||||
SyncEngine.kRecoveryStrategy.error;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -58,20 +58,14 @@ const MOBILEROOT_ANNO = "mobile/bookmarksRoot";
|
|||
const MOBILE_ANNO = "MobileBookmarks";
|
||||
const EXCLUDEBACKUP_ANNO = "places/excludeFromBackup";
|
||||
const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
|
||||
const GUID_ANNO = "sync/guid";
|
||||
const PARENT_ANNO = "sync/parent";
|
||||
const ANNOS_TO_TRACK = [DESCRIPTION_ANNO, SIDEBAR_ANNO, STATICTITLE_ANNO,
|
||||
FEEDURI_ANNO, SITEURI_ANNO, GENERATORURI_ANNO];
|
||||
FEEDURI_ANNO, SITEURI_ANNO, GENERATORURI_ANNO];
|
||||
|
||||
const SERVICE_NOT_SUPPORTED = "Service not supported on this platform";
|
||||
const FOLDER_SORTINDEX = 1000000;
|
||||
|
||||
try {
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
}
|
||||
catch(ex) {
|
||||
Cu.import("resource://gre/modules/utils.js");
|
||||
}
|
||||
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");
|
||||
|
@ -993,31 +987,12 @@ BookmarksStore.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
__childGUIDsStm: null,
|
||||
get _childGUIDsStm() {
|
||||
if (this.__childGUIDsStm) {
|
||||
return this.__childGUIDsStm;
|
||||
}
|
||||
|
||||
let stmt;
|
||||
if (this._haveGUIDColumn) {
|
||||
stmt = this._getStmt(
|
||||
"SELECT id AS item_id, guid " +
|
||||
"FROM moz_bookmarks " +
|
||||
"WHERE parent = :parent " +
|
||||
"ORDER BY position");
|
||||
} else {
|
||||
stmt = this._getStmt(
|
||||
"SELECT b.id AS item_id, " +
|
||||
"(SELECT id FROM moz_anno_attributes WHERE name = '" + GUID_ANNO + "') AS name_id," +
|
||||
"a.content AS guid " +
|
||||
"FROM moz_bookmarks b " +
|
||||
"LEFT JOIN moz_items_annos a ON a.item_id = b.id " +
|
||||
"AND a.anno_attribute_id = name_id " +
|
||||
"WHERE b.parent = :parent " +
|
||||
"ORDER BY b.position");
|
||||
}
|
||||
return this.__childGUIDsStm = stmt;
|
||||
return this._getStmt(
|
||||
"SELECT id AS item_id, guid " +
|
||||
"FROM moz_bookmarks " +
|
||||
"WHERE parent = :parent " +
|
||||
"ORDER BY position");
|
||||
},
|
||||
_childGUIDsCols: ["item_id", "guid"],
|
||||
|
||||
|
@ -1145,24 +1120,8 @@ BookmarksStore.prototype = {
|
|||
return this._stmts[query];
|
||||
|
||||
this._log.trace("Creating SQL statement: " + query);
|
||||
return this._stmts[query] = Utils.createStatement(this._hsvc.DBConnection,
|
||||
query);
|
||||
},
|
||||
|
||||
__haveGUIDColumn: null,
|
||||
get _haveGUIDColumn() {
|
||||
if (this.__haveGUIDColumn !== null) {
|
||||
return this.__haveGUIDColumn;
|
||||
}
|
||||
let stmt;
|
||||
try {
|
||||
stmt = this._hsvc.DBConnection.createStatement(
|
||||
"SELECT guid FROM moz_places");
|
||||
stmt.finalize();
|
||||
return this.__haveGUIDColumn = true;
|
||||
} catch(ex) {
|
||||
return this.__haveGUIDColumn = false;
|
||||
}
|
||||
return this._stmts[query] = this._hsvc.DBConnection
|
||||
.createAsyncStatement(query);
|
||||
},
|
||||
|
||||
get _frecencyStm() {
|
||||
|
@ -1174,54 +1133,11 @@ BookmarksStore.prototype = {
|
|||
},
|
||||
_frecencyCols: ["frecency"],
|
||||
|
||||
get _addGUIDAnnotationNameStm() {
|
||||
let stmt = this._getStmt(
|
||||
"INSERT OR IGNORE INTO moz_anno_attributes (name) VALUES (:anno_name)");
|
||||
stmt.params.anno_name = GUID_ANNO;
|
||||
return stmt;
|
||||
},
|
||||
|
||||
get _checkGUIDItemAnnotationStm() {
|
||||
// Gecko <2.0 only
|
||||
let stmt = this._getStmt(
|
||||
"SELECT b.id AS item_id, " +
|
||||
"(SELECT id FROM moz_anno_attributes WHERE name = :anno_name) AS name_id, " +
|
||||
"a.id AS anno_id, a.dateAdded AS anno_date " +
|
||||
"FROM moz_bookmarks b " +
|
||||
"LEFT JOIN moz_items_annos a ON a.item_id = b.id " +
|
||||
"AND a.anno_attribute_id = name_id " +
|
||||
"WHERE b.id = :item_id");
|
||||
stmt.params.anno_name = GUID_ANNO;
|
||||
return stmt;
|
||||
},
|
||||
_checkGUIDItemAnnotationCols: ["item_id", "name_id", "anno_id", "anno_date"],
|
||||
|
||||
get _addItemAnnotationStm() {
|
||||
return this._getStmt(
|
||||
"INSERT OR REPLACE INTO moz_items_annos " +
|
||||
"(id, item_id, anno_attribute_id, mime_type, content, flags, " +
|
||||
"expiration, type, dateAdded, lastModified) " +
|
||||
"VALUES (:id, :item_id, :name_id, :mime_type, :content, :flags, " +
|
||||
":expiration, :type, :date_added, :last_modified)");
|
||||
},
|
||||
|
||||
__setGUIDStm: null,
|
||||
get _setGUIDStm() {
|
||||
if (this.__setGUIDStm !== null) {
|
||||
return this.__setGUIDStm;
|
||||
}
|
||||
|
||||
// Obtains a statement to set the guid iff the guid column exists.
|
||||
let stmt;
|
||||
if (this._haveGUIDColumn) {
|
||||
stmt = this._getStmt(
|
||||
"UPDATE moz_bookmarks " +
|
||||
"SET guid = :guid " +
|
||||
"WHERE id = :item_id");
|
||||
} else {
|
||||
stmt = false;
|
||||
}
|
||||
return this.__setGUIDStm = stmt;
|
||||
return this._getStmt(
|
||||
"UPDATE moz_bookmarks " +
|
||||
"SET guid = :guid " +
|
||||
"WHERE id = :item_id");
|
||||
},
|
||||
|
||||
// Some helper functions to handle GUIDs
|
||||
|
@ -1229,73 +1145,18 @@ BookmarksStore.prototype = {
|
|||
if (!guid)
|
||||
guid = Utils.makeGUID();
|
||||
|
||||
// If we can, set the GUID on moz_bookmarks and do not do any other work.
|
||||
let (stmt = this._setGUIDStm) {
|
||||
if (stmt) {
|
||||
stmt.params.guid = guid;
|
||||
stmt.params.item_id = id;
|
||||
Utils.queryAsync(stmt);
|
||||
return guid;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure annotation name exists
|
||||
Utils.queryAsync(this._addGUIDAnnotationNameStm);
|
||||
|
||||
let stmt = this._checkGUIDItemAnnotationStm;
|
||||
let stmt = this._setGUIDStm;
|
||||
stmt.params.guid = guid;
|
||||
stmt.params.item_id = id;
|
||||
let result = Utils.queryAsync(stmt, this._checkGUIDItemAnnotationCols)[0];
|
||||
if (!result) {
|
||||
this._log.warn("Couldn't annotate bookmark id " + id);
|
||||
return guid;
|
||||
}
|
||||
|
||||
stmt = this._addItemAnnotationStm;
|
||||
if (result.anno_id) {
|
||||
stmt.params.id = result.anno_id;
|
||||
stmt.params.date_added = result.anno_date;
|
||||
} else {
|
||||
stmt.params.id = null;
|
||||
stmt.params.date_added = Date.now() * 1000;
|
||||
}
|
||||
stmt.params.item_id = result.item_id;
|
||||
stmt.params.name_id = result.name_id;
|
||||
stmt.params.content = guid;
|
||||
stmt.params.flags = 0;
|
||||
stmt.params.expiration = Ci.nsIAnnotationService.EXPIRE_NEVER;
|
||||
stmt.params.type = Ci.nsIAnnotationService.TYPE_STRING;
|
||||
stmt.params.last_modified = Date.now() * 1000;
|
||||
Utils.queryAsync(stmt);
|
||||
|
||||
return guid;
|
||||
},
|
||||
|
||||
__guidForIdStm: null,
|
||||
get _guidForIdStm() {
|
||||
if (this.__guidForIdStm) {
|
||||
return this.__guidForIdStm;
|
||||
}
|
||||
|
||||
// Try to first read from moz_bookmarks. Creating the statement will
|
||||
// fail, however, if the guid column does not exist. We fallback to just
|
||||
// reading the annotation table in this case.
|
||||
let stmt;
|
||||
if (this._haveGUIDColumn) {
|
||||
stmt = this._getStmt(
|
||||
"SELECT guid " +
|
||||
"FROM moz_bookmarks " +
|
||||
"WHERE id = :item_id");
|
||||
} else {
|
||||
stmt = this._getStmt(
|
||||
"SELECT a.content AS guid " +
|
||||
"FROM moz_items_annos a " +
|
||||
"JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id " +
|
||||
"JOIN moz_bookmarks b ON b.id = a.item_id " +
|
||||
"WHERE n.name = '" + GUID_ANNO + "' " +
|
||||
"AND b.id = :item_id");
|
||||
}
|
||||
|
||||
return this.__guidForIdStm = stmt;
|
||||
return this._getStmt(
|
||||
"SELECT guid " +
|
||||
"FROM moz_bookmarks " +
|
||||
"WHERE id = :item_id");
|
||||
},
|
||||
_guidForIdCols: ["guid"],
|
||||
|
||||
|
@ -1316,37 +1177,11 @@ BookmarksStore.prototype = {
|
|||
return this._setGUID(id);
|
||||
},
|
||||
|
||||
__idForGUIDStm: null,
|
||||
get _idForGUIDStm() {
|
||||
if (this.__idForGUIDStm) {
|
||||
return this.__idForGUIDStm;
|
||||
}
|
||||
|
||||
|
||||
// Try to first read from moz_bookmarks. Creating the statement will
|
||||
// fail, however, if the guid column does not exist. We fallback to just
|
||||
// reading the annotation table in this case.
|
||||
let stmt;
|
||||
if (this._haveGUIDColumn) {
|
||||
stmt = this._getStmt(
|
||||
"SELECT id AS item_id " +
|
||||
"FROM moz_bookmarks " +
|
||||
"WHERE guid = :guid");
|
||||
} else {
|
||||
// Order results by lastModified so we can preserve the ID of the oldest bookmark.
|
||||
// Copying a record preserves its dateAdded, and only modifying the
|
||||
// bookmark alters its lastModified, so we also order by its item_id --
|
||||
// lowest wins ties. Of course, Places can still screw us by reassigning IDs...
|
||||
stmt = this._getStmt(
|
||||
"SELECT a.item_id AS item_id " +
|
||||
"FROM moz_items_annos a " +
|
||||
"JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id " +
|
||||
"WHERE n.name = '" + GUID_ANNO + "' " +
|
||||
"AND a.content = :guid " +
|
||||
"ORDER BY a.lastModified, a.item_id");
|
||||
}
|
||||
|
||||
return this.__idForGUIDStm = stmt;
|
||||
return this._getStmt(
|
||||
"SELECT id AS item_id " +
|
||||
"FROM moz_bookmarks " +
|
||||
"WHERE guid = :guid");
|
||||
},
|
||||
_idForGUIDCols: ["item_id"],
|
||||
|
||||
|
@ -1370,22 +1205,6 @@ BookmarksStore.prototype = {
|
|||
if (!result)
|
||||
return -1;
|
||||
|
||||
if (!this._haveGUIDColumn) {
|
||||
try {
|
||||
// Assign new GUIDs to any that came later.
|
||||
for (let i = 1; i < results.length; ++i) {
|
||||
let surplus = results[i];
|
||||
this._log.debug("Assigning new GUID to copied row " + surplus.item_id);
|
||||
this._setGUID(surplus.item_id);
|
||||
}
|
||||
} catch (ex) {
|
||||
// Just skip it and carry on. This shouldn't happen, but if it does we
|
||||
// don't want to fail hard.
|
||||
this._log.debug("Got exception assigning new GUIDs: " +
|
||||
Utils.exceptionStr(ex));
|
||||
}
|
||||
}
|
||||
|
||||
return result.item_id;
|
||||
},
|
||||
|
||||
|
@ -1589,34 +1408,44 @@ BookmarksTracker.prototype = {
|
|||
* Folder of the item being changed
|
||||
*/
|
||||
_ignore: function BMT__ignore(itemId, folder) {
|
||||
// Ignore unconditionally if the engine tells us to
|
||||
// Ignore unconditionally if the engine tells us to.
|
||||
if (this.ignoreAll)
|
||||
return true;
|
||||
|
||||
// Ensure that the mobile bookmarks query is correct in the UI
|
||||
this._ensureMobileQuery();
|
||||
// Get the folder id if we weren't given one.
|
||||
if (folder == null) {
|
||||
try {
|
||||
folder = this._bms.getFolderIdForItem(itemId);
|
||||
} catch (ex) {
|
||||
this._log.debug("getFolderIdForItem(" + itemId +
|
||||
") threw; calling _ensureMobileQuery.");
|
||||
// I'm guessing that gFIFI can throw, and perhaps that's why
|
||||
// _ensureMobileQuery is here at all. Try not to call it.
|
||||
this._ensureMobileQuery();
|
||||
folder = this._bms.getFolderIdForItem(itemId);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure to remove items that have the exclude annotation
|
||||
// Ignore livemark children.
|
||||
if (this._ls.isLivemark(folder))
|
||||
return true;
|
||||
|
||||
// Ignore changes to tags (folders under the tags folder).
|
||||
let tags = kSpecialIds.tags;
|
||||
if (folder == tags)
|
||||
return true;
|
||||
|
||||
// Ignore tag items (the actual instance of a tag for a bookmark).
|
||||
if (this._bms.getFolderIdForItem(folder) == tags)
|
||||
return true;
|
||||
|
||||
// Make sure to remove items that have the exclude annotation.
|
||||
if (Svc.Annos.itemHasAnnotation(itemId, EXCLUDEBACKUP_ANNO)) {
|
||||
this.removeChangedID(this._GUIDForId(itemId));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the folder id if we weren't given one
|
||||
if (folder == null)
|
||||
folder = this._bms.getFolderIdForItem(itemId);
|
||||
|
||||
let tags = kSpecialIds.tags;
|
||||
// Ignore changes to tags (folders under the tags folder)
|
||||
if (folder == tags)
|
||||
return true;
|
||||
|
||||
// Ignore tag items (the actual instance of a tag for a bookmark)
|
||||
if (this._bms.getFolderIdForItem(folder) == tags)
|
||||
return true;
|
||||
|
||||
// Ignore livemark children
|
||||
return this._ls.isLivemark(folder);
|
||||
return false;
|
||||
},
|
||||
|
||||
onItemAdded: function BMT_onEndUpdateBatch(itemId, folder, index) {
|
||||
|
@ -1655,7 +1484,7 @@ BookmarksTracker.prototype = {
|
|||
let queryURI = Utils.makeURI("place:folder=" + kSpecialIds.mobile);
|
||||
let title = Str.sync.get("mobile.label");
|
||||
|
||||
// Don't add OR do remove the mobile bookmarks if there's nothing
|
||||
// Don't add OR remove the mobile bookmarks if there's nothing.
|
||||
if (Svc.Bookmark.getIdForItemAt(kSpecialIds.mobile, 0) == -1) {
|
||||
if (mobile.length != 0)
|
||||
Svc.Bookmark.removeItem(mobile[0]);
|
||||
|
@ -1675,28 +1504,26 @@ BookmarksTracker.prototype = {
|
|||
this.ignoreAll = false;
|
||||
},
|
||||
|
||||
// This method is oddly structured, but the idea is to return as quickly as
|
||||
// possible -- this handler gets called *every time* a bookmark changes, for
|
||||
// *each change*. That's particularly bad when a bunch of livemarks are
|
||||
// updated.
|
||||
onItemChanged: function BMT_onItemChanged(itemId, property, isAnno, value) {
|
||||
if (this._ignore(itemId))
|
||||
// Quicker checks first.
|
||||
if (this.ignoreAll)
|
||||
return;
|
||||
|
||||
// Allocate a new GUID if necessary.
|
||||
// We only want to do it if there's a dupe, so use idForGUID to achieve that.
|
||||
if (isAnno && (property == GUID_ANNO)) {
|
||||
this._log.trace("onItemChanged for " + GUID_ANNO +
|
||||
": probably needs a new one.");
|
||||
this._idForGUID(this._GUIDForId(itemId));
|
||||
this._addId(itemId);
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore annotations except for the ones that we sync
|
||||
if (isAnno && ANNOS_TO_TRACK.indexOf(property) == -1)
|
||||
if (isAnno && (ANNOS_TO_TRACK.indexOf(property) == -1))
|
||||
// Ignore annotations except for the ones that we sync.
|
||||
return;
|
||||
|
||||
// Ignore favicon changes to avoid unnecessary churn
|
||||
// Ignore favicon changes to avoid unnecessary churn.
|
||||
if (property == "favicon")
|
||||
return;
|
||||
|
||||
if (this._ignore(itemId))
|
||||
return;
|
||||
|
||||
this._log.trace("onItemChanged: " + itemId +
|
||||
(", " + property + (isAnno? " (anno)" : "")) +
|
||||
(value ? (" = \"" + value + "\"") : ""));
|
||||
|
|
|
@ -45,6 +45,7 @@ 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");
|
||||
|
||||
const CLIENTS_TTL = 1814400; // 21 days
|
||||
|
@ -193,19 +194,26 @@ ClientEngine.prototype = {
|
|||
SyncEngine.prototype._resetClient.call(this);
|
||||
this._store.wipe();
|
||||
},
|
||||
|
||||
|
||||
removeClientData: function removeClientData() {
|
||||
let res = new Resource(this.engineURL + "/" + this.localID);
|
||||
res.delete();
|
||||
},
|
||||
|
||||
// Override the default behavior to delete bad records from the server.
|
||||
handleHMACMismatch: function handleHMACMismatch(item) {
|
||||
handleHMACMismatch: function handleHMACMismatch(item, mayRetry) {
|
||||
this._log.debug("Handling HMAC mismatch for " + item.id);
|
||||
if (SyncEngine.prototype.handleHMACMismatch.call(this, item))
|
||||
return true;
|
||||
|
||||
let base = SyncEngine.prototype.handleHMACMismatch.call(this, item, mayRetry);
|
||||
if (base != SyncEngine.kRecoveryStrategy.error)
|
||||
return base;
|
||||
|
||||
// It's a bad client record. Save it to be deleted at the end of the sync.
|
||||
this._log.debug("Bad client record detected. Scheduling for deletion.");
|
||||
this._deleteId(item.id);
|
||||
|
||||
// Don't try again.
|
||||
return false;
|
||||
// Neither try again nor error; we're going to delete it.
|
||||
return SyncEngine.kRecoveryStrategy.ignore;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ let FormWrapper = {
|
|||
|
||||
getAllEntries: function getAllEntries() {
|
||||
// Sort by (lastUsed - minLast) / (maxLast - minLast) * timesUsed / maxTimes
|
||||
let query = this.createStatement(
|
||||
let query = Svc.Form.DBConnection.createAsyncStatement(
|
||||
"SELECT fieldname name, value FROM moz_formhistory " +
|
||||
"ORDER BY 1.0 * (lastUsed - (SELECT lastUsed FROM moz_formhistory ORDER BY lastUsed ASC LIMIT 1)) / " +
|
||||
"((SELECT lastUsed FROM moz_formhistory ORDER BY lastUsed DESC LIMIT 1) - (SELECT lastUsed FROM moz_formhistory ORDER BY lastUsed ASC LIMIT 1)) * " +
|
||||
|
@ -76,7 +76,7 @@ let FormWrapper = {
|
|||
},
|
||||
|
||||
getEntry: function getEntry(guid) {
|
||||
let query = this.createStatement(
|
||||
let query = Svc.Form.DBConnection.createAsyncStatement(
|
||||
"SELECT fieldname name, value FROM moz_formhistory WHERE guid = :guid");
|
||||
query.params.guid = guid;
|
||||
return Utils.queryAsync(query, ["name", "value"])[0];
|
||||
|
@ -84,7 +84,7 @@ let FormWrapper = {
|
|||
|
||||
getGUID: function getGUID(name, value) {
|
||||
// Query for the provided entry
|
||||
let getQuery = this.createStatement(
|
||||
let getQuery = Svc.Form.DBConnection.createAsyncStatement(
|
||||
"SELECT guid FROM moz_formhistory " +
|
||||
"WHERE fieldname = :name AND value = :value");
|
||||
getQuery.params.name = name;
|
||||
|
@ -106,7 +106,7 @@ let FormWrapper = {
|
|||
return item.guid;
|
||||
|
||||
// We need to create a guid for this entry
|
||||
let setQuery = this.createStatement(
|
||||
let setQuery = Svc.Form.DBConnection.createAsyncStatement(
|
||||
"UPDATE moz_formhistory SET guid = :guid " +
|
||||
"WHERE fieldname = :name AND value = :value");
|
||||
let guid = Utils.makeGUID();
|
||||
|
@ -119,37 +119,20 @@ let FormWrapper = {
|
|||
},
|
||||
|
||||
hasGUID: function hasGUID(guid) {
|
||||
let query = this.createStatement(
|
||||
let query = Svc.Form.DBConnection.createAsyncStatement(
|
||||
"SELECT guid FROM moz_formhistory WHERE guid = :guid LIMIT 1");
|
||||
query.params.guid = guid;
|
||||
return Utils.queryAsync(query, ["guid"]).length == 1;
|
||||
},
|
||||
|
||||
replaceGUID: function replaceGUID(oldGUID, newGUID) {
|
||||
let query = this.createStatement(
|
||||
let query = Svc.Form.DBConnection.createAsyncStatement(
|
||||
"UPDATE moz_formhistory SET guid = :newGUID WHERE guid = :oldGUID");
|
||||
query.params.oldGUID = oldGUID;
|
||||
query.params.newGUID = newGUID;
|
||||
Utils.queryAsync(query);
|
||||
},
|
||||
|
||||
createStatement: function createStatement(query) {
|
||||
try {
|
||||
// Just return the statement right away if it's okay
|
||||
return Utils.createStatement(Svc.Form.DBConnection, query);
|
||||
}
|
||||
catch(ex) {
|
||||
// Assume guid column must not exist yet, so add it with an index
|
||||
Svc.Form.DBConnection.executeSimpleSQL(
|
||||
"ALTER TABLE moz_formhistory ADD COLUMN guid TEXT");
|
||||
Svc.Form.DBConnection.executeSimpleSQL(
|
||||
"CREATE INDEX IF NOT EXISTS moz_formhistory_guid_index " +
|
||||
"ON moz_formhistory (guid)");
|
||||
|
||||
// Try creating the query now that the column exists
|
||||
return Utils.createStatement(Svc.Form.DBConnection, query);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function FormEngine() {
|
||||
|
@ -231,7 +214,7 @@ FormStore.prototype = {
|
|||
},
|
||||
|
||||
update: function FormStore_update(record) {
|
||||
this._log.warn("Ignoring form record update request!");
|
||||
this._log.trace("Ignoring form record update request!");
|
||||
},
|
||||
|
||||
wipe: function FormStore_wipe() {
|
||||
|
@ -283,7 +266,6 @@ FormTracker.prototype = {
|
|||
this._enabled = false;
|
||||
}
|
||||
break;
|
||||
// Firefox 4.0
|
||||
case "satchel-storage-changed":
|
||||
if (data == "addEntry" || data == "before-removeEntry") {
|
||||
subject = subject.QueryInterface(Ci.nsIArray);
|
||||
|
@ -294,32 +276,9 @@ FormTracker.prototype = {
|
|||
this.trackEntry(name, value);
|
||||
}
|
||||
break;
|
||||
// Firefox 3.5/3.6
|
||||
case "form-notifier":
|
||||
this.onFormNotifier(data);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
// Firefox 3.5/3.6
|
||||
onFormNotifier: function onFormNotifier(data) {
|
||||
let name, value;
|
||||
|
||||
// Figure out if it's a function that we care about tracking
|
||||
let formCall = JSON.parse(data);
|
||||
let func = formCall.func;
|
||||
if ((func == "addEntry" && formCall.type == "after") ||
|
||||
(func == "removeEntry" && formCall.type == "before"))
|
||||
[name, value] = formCall.args;
|
||||
|
||||
// Skip if there's nothing of interest
|
||||
if (name == null || value == null)
|
||||
return;
|
||||
|
||||
this._log.trace("Logging form action: " + [func, name, value]);
|
||||
this.trackEntry(name, value);
|
||||
},
|
||||
|
||||
notify: function FormTracker_notify(formElement, aWindow, actionURI) {
|
||||
if (this.ignoreAll)
|
||||
return;
|
||||
|
|
|
@ -44,7 +44,6 @@ const Ci = Components.interfaces;
|
|||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
const GUID_ANNO = "sync/guid";
|
||||
const HISTORY_TTL = 5184000; // 60 days
|
||||
const TOPIC_UPDATEPLACES_COMPLETE = "places-updatePlaces-complete";
|
||||
|
||||
|
@ -78,9 +77,6 @@ HistoryEngine.prototype = {
|
|||
downloadLimit: MAX_HISTORY_DOWNLOAD,
|
||||
applyIncomingBatchSize: HISTORY_STORE_BATCH_SIZE,
|
||||
|
||||
// For Gecko <2.0
|
||||
_sync: Utils.batchSync("History", SyncEngine),
|
||||
|
||||
_findDupe: function _findDupe(item) {
|
||||
return this._store.GUIDForUri(item.histUri);
|
||||
}
|
||||
|
@ -113,110 +109,28 @@ HistoryStore.prototype = {
|
|||
|
||||
__asyncHistory: null,
|
||||
get _asyncHistory() {
|
||||
if (!this.__asyncHistory && "mozIAsyncHistory" in Components.interfaces) {
|
||||
if (!this.__asyncHistory) {
|
||||
this.__asyncHistory = Cc["@mozilla.org/browser/history;1"]
|
||||
.getService(Ci.mozIAsyncHistory);
|
||||
}
|
||||
return this.__asyncHistory;
|
||||
},
|
||||
|
||||
get _db() {
|
||||
return this._hsvc.DBConnection;
|
||||
},
|
||||
|
||||
_stmts: {},
|
||||
_getStmt: function(query) {
|
||||
if (query in this._stmts)
|
||||
return this._stmts[query];
|
||||
|
||||
this._log.trace("Creating SQL statement: " + query);
|
||||
return this._stmts[query] = Utils.createStatement(this._db, query);
|
||||
return this._stmts[query] = this._hsvc.DBConnection
|
||||
.createAsyncStatement(query);
|
||||
},
|
||||
|
||||
get _haveTempTablesStm() {
|
||||
return this._getStmt(
|
||||
"SELECT name FROM sqlite_temp_master " +
|
||||
"WHERE name IN ('moz_places_temp', 'moz_historyvisits_temp')");
|
||||
},
|
||||
_haveTempTablesCols: ["name"],
|
||||
|
||||
__haveTempTables: null,
|
||||
get _haveTempTables() {
|
||||
if (this.__haveTempTables === null) {
|
||||
this.__haveTempTables = !!Utils.queryAsync(
|
||||
this._haveTempTablesStm, this._haveTempTablesCols).length;
|
||||
}
|
||||
return this.__haveTempTables;
|
||||
},
|
||||
|
||||
__haveGUIDColumn: null,
|
||||
get _haveGUIDColumn() {
|
||||
if (this.__haveGUIDColumn !== null) {
|
||||
return this.__haveGUIDColumn;
|
||||
}
|
||||
let stmt;
|
||||
try {
|
||||
stmt = this._db.createStatement("SELECT guid FROM moz_places");
|
||||
stmt.finalize();
|
||||
return this.__haveGUIDColumn = true;
|
||||
} catch(ex) {
|
||||
return this.__haveGUIDColumn = false;
|
||||
}
|
||||
},
|
||||
|
||||
get _addGUIDAnnotationNameStm() {
|
||||
// Gecko <2.0 only
|
||||
let stmt = this._getStmt(
|
||||
"INSERT OR IGNORE INTO moz_anno_attributes (name) VALUES (:anno_name)");
|
||||
stmt.params.anno_name = GUID_ANNO;
|
||||
return stmt;
|
||||
},
|
||||
|
||||
get _checkGUIDPageAnnotationStm() {
|
||||
// Gecko <2.0 only
|
||||
let stmt = this._getStmt(
|
||||
"SELECT h.id AS place_id, " +
|
||||
"(SELECT id FROM moz_anno_attributes WHERE name = :anno_name) AS name_id, " +
|
||||
"a.id AS anno_id, a.dateAdded AS anno_date " +
|
||||
"FROM (SELECT id FROM moz_places_temp WHERE url = :page_url " +
|
||||
"UNION " +
|
||||
"SELECT id FROM moz_places WHERE url = :page_url) AS h " +
|
||||
"LEFT JOIN moz_annos a ON a.place_id = h.id " +
|
||||
"AND a.anno_attribute_id = name_id");
|
||||
stmt.params.anno_name = GUID_ANNO;
|
||||
return stmt;
|
||||
},
|
||||
_checkGUIDPageAnnotationCols: ["place_id", "name_id", "anno_id",
|
||||
"anno_date"],
|
||||
|
||||
get _addPageAnnotationStm() {
|
||||
// Gecko <2.0 only
|
||||
return this._getStmt(
|
||||
"INSERT OR REPLACE INTO moz_annos " +
|
||||
"(id, place_id, anno_attribute_id, mime_type, content, flags, " +
|
||||
"expiration, type, dateAdded, lastModified) " +
|
||||
"VALUES (:id, :place_id, :name_id, :mime_type, :content, :flags, " +
|
||||
":expiration, :type, :date_added, :last_modified)");
|
||||
},
|
||||
|
||||
__setGUIDStm: null,
|
||||
get _setGUIDStm() {
|
||||
if (this.__setGUIDStm !== null) {
|
||||
return this.__setGUIDStm;
|
||||
}
|
||||
|
||||
// Obtains a statement to set the guid iff the guid column exists.
|
||||
let stmt;
|
||||
if (this._haveGUIDColumn) {
|
||||
stmt = this._getStmt(
|
||||
"UPDATE moz_places " +
|
||||
"SET guid = :guid " +
|
||||
"WHERE url = :page_url");
|
||||
} else {
|
||||
stmt = false;
|
||||
}
|
||||
|
||||
return this.__setGUIDStm = stmt;
|
||||
return this._getStmt(
|
||||
"UPDATE moz_places " +
|
||||
"SET guid = :guid " +
|
||||
"WHERE url = :page_url");
|
||||
},
|
||||
|
||||
// Some helper functions to handle GUIDs
|
||||
|
@ -226,77 +140,18 @@ HistoryStore.prototype = {
|
|||
if (!guid)
|
||||
guid = Utils.makeGUID();
|
||||
|
||||
// If we can, set the GUID on moz_places and do not do any other work.
|
||||
let (stmt = this._setGUIDStm) {
|
||||
if (stmt) {
|
||||
stmt.params.guid = guid;
|
||||
stmt.params.page_url = uri;
|
||||
Utils.queryAsync(stmt);
|
||||
return guid;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure annotation name exists
|
||||
Utils.queryAsync(this._addGUIDAnnotationNameStm);
|
||||
|
||||
let stmt = this._checkGUIDPageAnnotationStm;
|
||||
let stmt = this._setGUIDStm;
|
||||
stmt.params.guid = guid;
|
||||
stmt.params.page_url = uri;
|
||||
let result = Utils.queryAsync(stmt, this._checkGUIDPageAnnotationCols)[0];
|
||||
if (!result) {
|
||||
let log = Log4Moz.repository.getLogger("Engine.History");
|
||||
log.warn("Couldn't annotate URI " + uri);
|
||||
return guid;
|
||||
}
|
||||
|
||||
stmt = this._addPageAnnotationStm;
|
||||
if (result.anno_id) {
|
||||
stmt.params.id = result.anno_id;
|
||||
stmt.params.date_added = result.anno_date;
|
||||
} else {
|
||||
stmt.params.id = null;
|
||||
stmt.params.date_added = Date.now() * 1000;
|
||||
}
|
||||
stmt.params.place_id = result.place_id;
|
||||
stmt.params.name_id = result.name_id;
|
||||
stmt.params.content = guid;
|
||||
stmt.params.flags = 0;
|
||||
stmt.params.expiration = Ci.nsIAnnotationService.EXPIRE_WITH_HISTORY;
|
||||
stmt.params.type = Ci.nsIAnnotationService.TYPE_STRING;
|
||||
stmt.params.last_modified = Date.now() * 1000;
|
||||
Utils.queryAsync(stmt);
|
||||
|
||||
return guid;
|
||||
},
|
||||
|
||||
__guidStm: null,
|
||||
get _guidStm() {
|
||||
if (this.__guidStm) {
|
||||
return this.__guidStm;
|
||||
}
|
||||
|
||||
// Try to first read from moz_places. Creating the statement will throw
|
||||
// if the column doesn't exist, though so fallback to just reading from
|
||||
// the annotation table.
|
||||
let stmt;
|
||||
if (this._haveGUIDColumn) {
|
||||
stmt = this._getStmt(
|
||||
"SELECT guid " +
|
||||
"FROM moz_places " +
|
||||
"WHERE url = :page_url");
|
||||
} else {
|
||||
stmt = this._getStmt(
|
||||
"SELECT a.content AS guid " +
|
||||
"FROM moz_annos a " +
|
||||
"JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id " +
|
||||
"JOIN ( " +
|
||||
"SELECT id FROM moz_places_temp WHERE url = :page_url " +
|
||||
"UNION " +
|
||||
"SELECT id FROM moz_places WHERE url = :page_url " +
|
||||
") AS h ON h.id = a.place_id " +
|
||||
"WHERE n.name = '" + GUID_ANNO + "'");
|
||||
}
|
||||
|
||||
return this.__guidStmt = stmt;
|
||||
return this._getStmt(
|
||||
"SELECT guid " +
|
||||
"FROM moz_places " +
|
||||
"WHERE url = :page_url");
|
||||
},
|
||||
_guidCols: ["guid"],
|
||||
|
||||
|
@ -315,21 +170,6 @@ HistoryStore.prototype = {
|
|||
},
|
||||
|
||||
get _visitStm() {
|
||||
// Gecko <2.0
|
||||
if (this._haveTempTables) {
|
||||
let where =
|
||||
"WHERE place_id = IFNULL( " +
|
||||
"(SELECT id FROM moz_places_temp WHERE url = :url), " +
|
||||
"(SELECT id FROM moz_places WHERE url = :url) " +
|
||||
") ";
|
||||
return this._getStmt(
|
||||
"SELECT visit_type type, visit_date date " +
|
||||
"FROM moz_historyvisits_temp " + where + "UNION " +
|
||||
"SELECT visit_type type, visit_date date " +
|
||||
"FROM moz_historyvisits " + where +
|
||||
"ORDER BY date DESC LIMIT 10 ");
|
||||
}
|
||||
// Gecko 2.0
|
||||
return this._getStmt(
|
||||
"SELECT visit_type type, visit_date date " +
|
||||
"FROM moz_historyvisits " +
|
||||
|
@ -338,53 +178,15 @@ HistoryStore.prototype = {
|
|||
},
|
||||
_visitCols: ["date", "type"],
|
||||
|
||||
__urlStmt: null,
|
||||
get _urlStm() {
|
||||
if (this.__urlStmt) {
|
||||
return this.__urlStmt;
|
||||
}
|
||||
|
||||
// Try to first read from moz_places. Creating the statement will throw
|
||||
// if the column doesn't exist, though so fallback to just reading from
|
||||
// the annotation table.
|
||||
let stmt;
|
||||
if (this._haveGUIDColumn) {
|
||||
stmt = this._getStmt(
|
||||
"SELECT url, title, frecency " +
|
||||
"FROM moz_places " +
|
||||
"WHERE guid = :guid");
|
||||
} else {
|
||||
let where =
|
||||
"WHERE id = (" +
|
||||
"SELECT place_id " +
|
||||
"FROM moz_annos " +
|
||||
"WHERE content = :guid AND anno_attribute_id = (" +
|
||||
"SELECT id " +
|
||||
"FROM moz_anno_attributes " +
|
||||
"WHERE name = '" + GUID_ANNO + "')) ";
|
||||
stmt = this._getStmt(
|
||||
"SELECT url, title, frecency FROM moz_places_temp " + where +
|
||||
"UNION ALL " +
|
||||
"SELECT url, title, frecency FROM moz_places " + where + "LIMIT 1");
|
||||
}
|
||||
|
||||
return this.__urlStmt = stmt;
|
||||
return this._getStmt(
|
||||
"SELECT url, title, frecency " +
|
||||
"FROM moz_places " +
|
||||
"WHERE guid = :guid");
|
||||
},
|
||||
_urlCols: ["url", "title", "frecency"],
|
||||
|
||||
get _allUrlStm() {
|
||||
// Gecko <2.0
|
||||
if (this._haveTempTables)
|
||||
return this._getStmt(
|
||||
"SELECT url, frecency FROM moz_places_temp " +
|
||||
"WHERE last_visit_date > :cutoff_date " +
|
||||
"UNION " +
|
||||
"SELECT url, frecency FROM moz_places " +
|
||||
"WHERE last_visit_date > :cutoff_date " +
|
||||
"ORDER BY 2 DESC " +
|
||||
"LIMIT :max_results");
|
||||
|
||||
// Gecko 2.0
|
||||
return this._getStmt(
|
||||
"SELECT url " +
|
||||
"FROM moz_places " +
|
||||
|
@ -425,12 +227,6 @@ HistoryStore.prototype = {
|
|||
},
|
||||
|
||||
applyIncomingBatch: function applyIncomingBatch(records) {
|
||||
// Gecko <2.0
|
||||
if (!this._asyncHistory) {
|
||||
return Store.prototype.applyIncomingBatch.call(this, records);
|
||||
}
|
||||
|
||||
// Gecko 2.0
|
||||
let failed = [];
|
||||
|
||||
// Convert incoming records to mozIPlaceInfo objects. Some records can be
|
||||
|
@ -529,9 +325,8 @@ HistoryStore.prototype = {
|
|||
+ visit.date);
|
||||
throw "Visit has no date!";
|
||||
}
|
||||
// TRANSITION_FRAMED_LINK = TRANSITION_DOWNLOAD + 1 is new in Gecko 2.0
|
||||
if (!visit.type || !(visit.type >= Svc.History.TRANSITION_LINK &&
|
||||
visit.type <= Svc.History.TRANSITION_DOWNLOAD + 1)) {
|
||||
visit.type <= Svc.History.TRANSITION_FRAMED_LINK)) {
|
||||
this._log.warn("Encountered record with invalid visit type: "
|
||||
+ visit.type);
|
||||
throw "Invalid visit type!";
|
||||
|
@ -563,12 +358,6 @@ HistoryStore.prototype = {
|
|||
return true;
|
||||
},
|
||||
|
||||
create: function HistStore_create(record) {
|
||||
// Add the url and set the GUID
|
||||
this.update(record);
|
||||
this.setGUID(record.histUri, record.id);
|
||||
},
|
||||
|
||||
remove: function HistStore_remove(record) {
|
||||
let page = this._findURLByGUID(record.id);
|
||||
if (page == null) {
|
||||
|
@ -581,29 +370,6 @@ HistoryStore.prototype = {
|
|||
this._log.trace("Removed page: " + [record.id, page.url, page.title]);
|
||||
},
|
||||
|
||||
update: function HistStore_update(record) {
|
||||
this._log.trace(" -> processing history entry: " + record.histUri);
|
||||
|
||||
if (!this._recordToPlaceInfo(record)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for each (let {visitDate, transitionType} in record.visits) {
|
||||
Svc.History.addVisit(record.uri, visitDate, null, transitionType,
|
||||
transitionType == 5 || transitionType == 6, 0);
|
||||
}
|
||||
|
||||
if (record.title) {
|
||||
try {
|
||||
this._hsvc.setPageTitle(record.uri, record.title);
|
||||
} catch (ex if ex.result == Cr.NS_ERROR_NOT_AVAILABLE) {
|
||||
// There's no entry for the given URI, either because it's a
|
||||
// URI that Places ignores (e.g. javascript:) or there were no
|
||||
// visits. We can just ignore those cases.
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
itemExists: function HistStore_itemExists(id) {
|
||||
if (this._findURLByGUID(id))
|
||||
return true;
|
||||
|
|
|
@ -114,13 +114,8 @@ function PasswordStore(name) {
|
|||
"@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo, "init");
|
||||
|
||||
Utils.lazy2(this, "DBConnection", function() {
|
||||
try {
|
||||
return Svc.Login.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.mozIStorageConnection);
|
||||
} catch (ex if (ex.result == Cr.NS_ERROR_NO_INTERFACE)) {
|
||||
// Gecko <2.0 *sadface*
|
||||
return null;
|
||||
}
|
||||
return Svc.Login.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.mozIStorageConnection);
|
||||
});
|
||||
}
|
||||
PasswordStore.prototype = {
|
||||
|
|
|
@ -47,6 +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/ext/Preferences.js");
|
||||
Cu.import("resource://gre/modules/LightweightThemeManager.jsm");
|
||||
|
||||
const PREFS_GUID = Utils.encodeBase64url(Svc.AppInfo.ID);
|
||||
|
||||
|
@ -138,20 +139,9 @@ PrefStore.prototype = {
|
|||
},
|
||||
|
||||
_setAllPrefs: function PrefStore__setAllPrefs(values) {
|
||||
// cache
|
||||
let ltmExists = true;
|
||||
let ltm = {};
|
||||
let enabledBefore = false;
|
||||
let enabledPref = "lightweightThemes.isThemeSelected";
|
||||
let prevTheme = "";
|
||||
try {
|
||||
Cu.import("resource://gre/modules/LightweightThemeManager.jsm", ltm);
|
||||
ltm = ltm.LightweightThemeManager;
|
||||
enabledBefore = this._prefs.get(enabledPref, false);
|
||||
prevTheme = ltm.currentTheme;
|
||||
} catch(ex) {
|
||||
ltmExists = false;
|
||||
} // LightweightThemeManager only exists in Firefox 3.6+
|
||||
let enabledBefore = this._prefs.get(enabledPref, false);
|
||||
let prevTheme = LightweightThemeManager.currentTheme;
|
||||
|
||||
for (let [pref, value] in Iterator(values)) {
|
||||
if (!this._isSynced(pref))
|
||||
|
@ -171,14 +161,12 @@ PrefStore.prototype = {
|
|||
}
|
||||
|
||||
// Notify the lightweight theme manager of all the new values
|
||||
if (ltmExists) {
|
||||
let enabledNow = this._prefs.get(enabledPref, false);
|
||||
if (enabledBefore && !enabledNow)
|
||||
ltm.currentTheme = null;
|
||||
else if (enabledNow && ltm.usedThemes[0] != prevTheme) {
|
||||
ltm.currentTheme = null;
|
||||
ltm.currentTheme = ltm.usedThemes[0];
|
||||
}
|
||||
let enabledNow = this._prefs.get(enabledPref, false);
|
||||
if (enabledBefore && !enabledNow) {
|
||||
LightweightThemeManager.currentTheme = null;
|
||||
} else if (enabledNow && LightweightThemeManager.usedThemes[0] != prevTheme) {
|
||||
LightweightThemeManager.currentTheme = null;
|
||||
LightweightThemeManager.currentTheme = ltm.usedThemes[0];
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
Cu.import("resource://services-sync/engines.js");
|
||||
Cu.import("resource://services-sync/engines/clients.js");
|
||||
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/ext/Preferences.js");
|
||||
|
||||
|
@ -106,6 +107,10 @@ TabEngine.prototype = {
|
|||
this._tracker.modified = true;
|
||||
},
|
||||
|
||||
removeClientData: function removeClientData() {
|
||||
new Resource(this.engineURL + "/" + Clients.localID).delete();
|
||||
},
|
||||
|
||||
/* The intent is not to show tabs in the menu if they're already
|
||||
* open locally. There are a couple ways to interpret this: for
|
||||
* instance, we could do it by removing a tab from the list when
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
* Dan Mills <thunder@mozilla.com>
|
||||
* Anant Narayanan <anant@kix.in>
|
||||
* Philipp von Weitershausen <philipp@weitershausen.de>
|
||||
* 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
|
||||
|
@ -70,7 +71,7 @@ function BrokenBasicAuthenticator(identity) {
|
|||
}
|
||||
BrokenBasicAuthenticator.prototype = {
|
||||
onRequest: function BasicAuth_onRequest(headers) {
|
||||
headers['Authorization'] = 'Basic ' +
|
||||
headers['authorization'] = 'Basic ' +
|
||||
btoa(this._id.username + ':' + this._id.password);
|
||||
return headers;
|
||||
}
|
||||
|
@ -81,7 +82,7 @@ function BasicAuthenticator(identity) {
|
|||
}
|
||||
BasicAuthenticator.prototype = {
|
||||
onRequest: function onRequest(headers) {
|
||||
headers['Authorization'] = 'Basic ' +
|
||||
headers['authorization'] = 'Basic ' +
|
||||
btoa(this._id.username + ':' + this._id.passwordUTF8);
|
||||
return headers;
|
||||
}
|
||||
|
@ -127,7 +128,7 @@ AuthMgr.prototype = {
|
|||
* function callback(error, result) {...}
|
||||
*
|
||||
* 'error' will be null on successful requests. Likewise, result will not be
|
||||
* passes (=undefined) when an error occurs. Note that this is independent of
|
||||
* passed (=undefined) when an error occurs. Note that this is independent of
|
||||
* the status of the HTTP response.
|
||||
*/
|
||||
function AsyncResource(uri) {
|
||||
|
@ -141,7 +142,21 @@ function AsyncResource(uri) {
|
|||
AsyncResource.prototype = {
|
||||
_logName: "Net.Resource",
|
||||
|
||||
// Wait 5 minutes before killing a request
|
||||
// The string to use as the base User-Agent in Sync requests.
|
||||
// These strings will look something like
|
||||
//
|
||||
// Firefox/4.0 FxSync/1.8.0.20100101.mobile
|
||||
//
|
||||
// or
|
||||
//
|
||||
// Firefox Aurora/5.0a1 FxSync/1.9.0.20110409.desktop
|
||||
//
|
||||
_userAgent:
|
||||
Svc.AppInfo.name + "/" + Svc.AppInfo.version + // Product.
|
||||
" FxSync/" + WEAVE_VERSION + "." + // Sync.
|
||||
Svc.AppInfo.appBuildID + ".", // Build.
|
||||
|
||||
// Wait 5 minutes before killing a request.
|
||||
ABORT_TIMEOUT: 300000,
|
||||
|
||||
// ** {{{ Resource.authenticator }}} **
|
||||
|
@ -171,12 +186,8 @@ AsyncResource.prototype = {
|
|||
set headers(value) {
|
||||
this._headers = value;
|
||||
},
|
||||
setHeader: function Res_setHeader() {
|
||||
if (arguments.length % 2)
|
||||
throw "setHeader only accepts arguments in multiples of 2";
|
||||
for (let i = 0; i < arguments.length; i += 2) {
|
||||
this._headers[arguments[i].toLowerCase()] = arguments[i + 1];
|
||||
}
|
||||
setHeader: function Res_setHeader(header, value) {
|
||||
this._headers[header.toLowerCase()] = value;
|
||||
},
|
||||
|
||||
// ** {{{ Resource.uri }}} **
|
||||
|
@ -227,10 +238,16 @@ AsyncResource.prototype = {
|
|||
// Setup a callback to handle bad HTTPS certificates.
|
||||
channel.notificationCallbacks = new BadCertListener();
|
||||
|
||||
// Avoid calling the authorizer more than once
|
||||
// Compose a UA string fragment from the various available identifiers.
|
||||
if (Svc.Prefs.get("sendVersionInfo", true)) {
|
||||
let ua = this._userAgent + Svc.Prefs.get("client.type", "desktop");
|
||||
channel.setRequestHeader("user-agent", ua, false);
|
||||
}
|
||||
|
||||
// Avoid calling the authorizer more than once.
|
||||
let headers = this.headers;
|
||||
for (let key in headers) {
|
||||
if (key == 'Authorization')
|
||||
if (key == 'authorization')
|
||||
this._log.trace("HTTP Header " + key + ": ***** (suppressed)");
|
||||
else
|
||||
this._log.trace("HTTP Header " + key + ": " + headers[key]);
|
||||
|
@ -242,14 +259,14 @@ AsyncResource.prototype = {
|
|||
_onProgress: function Res__onProgress(channel) {},
|
||||
|
||||
_doRequest: function _doRequest(action, data, callback) {
|
||||
this._log.trace("In _doRequest.");
|
||||
this._callback = callback;
|
||||
let channel = this._channel = this._createRequest();
|
||||
|
||||
if ("undefined" != typeof(data))
|
||||
this._data = data;
|
||||
|
||||
// PUT and POST are trreated differently because
|
||||
// they have payload data.
|
||||
// PUT and POST are treated differently because they have payload data.
|
||||
if ("PUT" == action || "POST" == action) {
|
||||
// Convert non-string bodies into JSON
|
||||
if (this._data.constructor.toString() != String)
|
||||
|
@ -278,6 +295,8 @@ AsyncResource.prototype = {
|
|||
},
|
||||
|
||||
_onComplete: function _onComplete(error, data) {
|
||||
this._log.trace("In _onComplete. Error is " + error + ".");
|
||||
|
||||
if (error) {
|
||||
this._callback(error);
|
||||
return;
|
||||
|
@ -287,29 +306,54 @@ AsyncResource.prototype = {
|
|||
let channel = this._channel;
|
||||
let action = channel.requestMethod;
|
||||
|
||||
// Set some default values in-case there's no response header
|
||||
let headers = {};
|
||||
this._log.trace("Channel: " + channel);
|
||||
this._log.trace("Action: " + action);
|
||||
|
||||
// Process status and success first. This way a problem with headers
|
||||
// doesn't fail to include accurate status information.
|
||||
let status = 0;
|
||||
let success = false;
|
||||
|
||||
try {
|
||||
// Read out the response headers if available
|
||||
status = channel.responseStatus;
|
||||
success = channel.requestSucceeded; // HTTP status.
|
||||
|
||||
this._log.trace("Status: " + status);
|
||||
this._log.trace("Success: " + success);
|
||||
|
||||
// Log the status of the request.
|
||||
let mesg = [action, success ? "success" : "fail", status,
|
||||
channel.URI.spec].join(" ");
|
||||
this._log.debug("mesg: " + mesg);
|
||||
|
||||
if (mesg.length > 200)
|
||||
mesg = mesg.substr(0, 200) + "…";
|
||||
this._log.debug(mesg);
|
||||
|
||||
// Additionally give the full response body when Trace logging.
|
||||
if (this._log.level <= Log4Moz.Level.Trace)
|
||||
this._log.trace(action + " body: " + data);
|
||||
|
||||
} catch(ex) {
|
||||
// Got a response, but an exception occurred during processing.
|
||||
// This shouldn't occur.
|
||||
this._log.warn("Caught unexpected exception " + Utils.exceptionStr(ex) +
|
||||
" in _onComplete.");
|
||||
this._log.debug(Utils.stackTrace(ex));
|
||||
}
|
||||
|
||||
// Process headers. They can be empty, or the call can otherwise fail, so
|
||||
// put this in its own try block.
|
||||
let headers = {};
|
||||
try {
|
||||
this._log.trace("Processing response headers.");
|
||||
|
||||
// Read out the response headers if available.
|
||||
channel.visitResponseHeaders({
|
||||
visitHeader: function visitHeader(header, value) {
|
||||
headers[header.toLowerCase()] = value;
|
||||
}
|
||||
});
|
||||
status = channel.responseStatus;
|
||||
success = channel.requestSucceeded;
|
||||
|
||||
// Log the status of the request
|
||||
let mesg = [action, success ? "success" : "fail", status,
|
||||
channel.URI.spec].join(" ");
|
||||
if (mesg.length > 200)
|
||||
mesg = mesg.substr(0, 200) + "…";
|
||||
this._log.debug(mesg);
|
||||
// Additionally give the full response body when Trace logging
|
||||
if (this._log.level <= Log4Moz.Level.Trace)
|
||||
this._log.trace(action + " body: " + data);
|
||||
|
||||
// This is a server-side safety valve to allow slowing down
|
||||
// clients without hurting performance.
|
||||
|
@ -320,23 +364,26 @@ AsyncResource.prototype = {
|
|||
if (success && headers["x-weave-quota-remaining"])
|
||||
Observers.notify("weave:service:quota:remaining",
|
||||
parseInt(headers["x-weave-quota-remaining"], 10));
|
||||
}
|
||||
// Got a response but no header; must be cached (use default values)
|
||||
catch(ex) {
|
||||
this._log.debug(action + " cached: " + status);
|
||||
} catch (ex) {
|
||||
this._log.debug("Caught exception " + Utils.exceptionStr(ex) +
|
||||
" visiting headers in _onComplete.");
|
||||
this._log.debug(Utils.stackTrace(ex));
|
||||
}
|
||||
|
||||
let ret = new String(data);
|
||||
ret.headers = headers;
|
||||
ret.status = status;
|
||||
let ret = new String(data);
|
||||
ret.status = status;
|
||||
ret.success = success;
|
||||
ret.headers = headers;
|
||||
|
||||
// Make a lazy getter to convert the json response into an object
|
||||
// Make a lazy getter to convert the json response into an object.
|
||||
// Note that this can cause a parse error to be thrown far away from the
|
||||
// actual fetch, so be warned!
|
||||
Utils.lazy2(ret, "obj", function() JSON.parse(ret));
|
||||
|
||||
// Notify if we get a 401 to maybe try again with a new uri
|
||||
// Notify if we get a 401 to maybe try again with a new URI.
|
||||
// TODO: more retry logic.
|
||||
if (status == 401) {
|
||||
// Create an object to allow observers to decide if we should try again
|
||||
// Create an object to allow observers to decide if we should try again.
|
||||
let subject = {
|
||||
newUri: "",
|
||||
resource: this,
|
||||
|
@ -344,7 +391,7 @@ AsyncResource.prototype = {
|
|||
}
|
||||
Observers.notify("weave:resource:status:401", subject);
|
||||
|
||||
// Do the same type of request but with the new uri
|
||||
// Do the same type of request but with the new URI.
|
||||
if (subject.newUri != "") {
|
||||
this.uri = subject.newUri;
|
||||
this._doRequest(action, this._data, this._callback);
|
||||
|
@ -481,36 +528,46 @@ function ChannelListener(onComplete, onProgress, logger, timeout) {
|
|||
ChannelListener.prototype = {
|
||||
|
||||
onStartRequest: function Channel_onStartRequest(channel) {
|
||||
this._log.trace("onStartRequest called for channel " + channel + ".");
|
||||
channel.QueryInterface(Ci.nsIHttpChannel);
|
||||
|
||||
// Save the latest server timestamp when possible
|
||||
// Save the latest server timestamp when possible.
|
||||
try {
|
||||
Resource.serverTime = channel.getResponseHeader("X-Weave-Timestamp") - 0;
|
||||
}
|
||||
catch(ex) {}
|
||||
|
||||
this._log.trace(channel.requestMethod + " " + channel.URI.spec);
|
||||
this._log.trace("onStartRequest: " + channel.requestMethod + " " +
|
||||
channel.URI.spec);
|
||||
this._data = '';
|
||||
this.delayAbort();
|
||||
},
|
||||
|
||||
onStopRequest: function Channel_onStopRequest(channel, context, status) {
|
||||
// Clear the abort timer now that the channel is done
|
||||
// Clear the abort timer now that the channel is done.
|
||||
this.abortTimer.clear();
|
||||
|
||||
let success = Components.isSuccessCode(status);
|
||||
this._log.trace("Channel for " + channel.requestMethod + " " +
|
||||
channel.URI.spec + ": isSuccessCode(" + status + ")? " +
|
||||
success);
|
||||
|
||||
if (this._data == '')
|
||||
this._data = null;
|
||||
|
||||
// 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 (!Components.isSuccessCode(status)) {
|
||||
if (!success) {
|
||||
let message = Components.Exception("", status).name;
|
||||
let error = Components.Exception(message, status);
|
||||
this._onComplete(error);
|
||||
return;
|
||||
}
|
||||
|
||||
this._log.trace("Channel: flags = " + channel.loadFlags +
|
||||
", URI = " + channel.URI.spec +
|
||||
", HTTP success? " + channel.requestSucceeded);
|
||||
this._onComplete(null, this._data);
|
||||
},
|
||||
|
||||
|
|
|
@ -154,7 +154,7 @@ WeaveSvc.prototype = {
|
|||
let misc = Svc.Prefs.get("miscURL");
|
||||
if (misc.indexOf(":") == -1)
|
||||
misc = this.serverURL + misc;
|
||||
return misc + "1.0/";
|
||||
return misc + MISC_API_VERSION + "/";
|
||||
},
|
||||
|
||||
get userAPI() {
|
||||
|
@ -162,7 +162,7 @@ WeaveSvc.prototype = {
|
|||
let user = Svc.Prefs.get("userURL");
|
||||
if (user.indexOf(":") == -1)
|
||||
user = this.serverURL + user;
|
||||
return user + "1.0/";
|
||||
return user + USER_API_VERSION + "/";
|
||||
},
|
||||
|
||||
get pwResetURL() {
|
||||
|
@ -237,7 +237,7 @@ WeaveSvc.prototype = {
|
|||
if (this.clusterURL == "" || this.username == "")
|
||||
return;
|
||||
|
||||
let storageAPI = this.clusterURL + Svc.Prefs.get("storageAPI") + "/";
|
||||
let storageAPI = this.clusterURL + SYNC_API_VERSION + "/";
|
||||
this.userBaseURL = storageAPI + this.username + "/";
|
||||
this._log.debug("Caching URLs under storage user base: " + this.userBaseURL);
|
||||
|
||||
|
@ -1014,6 +1014,16 @@ WeaveSvc.prototype = {
|
|||
}))(),
|
||||
|
||||
startOver: function() {
|
||||
// Clear client-specific data from the server, including disabled engines.
|
||||
for each (let engine in [Clients].concat(Engines.getAll())) {
|
||||
try {
|
||||
engine.removeClientData();
|
||||
} catch(ex) {
|
||||
this._log.warn("Deleting client data for " + engine.name + " failed:"
|
||||
+ Utils.exceptionStr(ex));
|
||||
}
|
||||
}
|
||||
|
||||
// Set a username error so the status message shows "set up..."
|
||||
Status.login = LOGIN_FAILED_NO_USERNAME;
|
||||
this.logout();
|
||||
|
@ -1184,12 +1194,6 @@ WeaveSvc.prototype = {
|
|||
|
||||
checkAccount: function checkAccount(account) {
|
||||
let username = this._usernameFromAccount(account);
|
||||
return this.checkUsername(username);
|
||||
},
|
||||
|
||||
// Backwards compat with the Firefox UI. Fold into checkAccount() once
|
||||
// bug 595066 has landed.
|
||||
checkUsername: function checkUsername(username) {
|
||||
let url = this.userAPI + username;
|
||||
let res = new Resource(url);
|
||||
res.authenticator = new NoOpAuthenticator();
|
||||
|
@ -1211,18 +1215,9 @@ WeaveSvc.prototype = {
|
|||
return this._errorStr(data);
|
||||
},
|
||||
|
||||
createAccount: function createAccount() {
|
||||
// Backwards compat with the Firefox UI. Change to signature to
|
||||
// (email, password, captchaChallenge, captchaResponse) once
|
||||
// bug 595066 has landed.
|
||||
let username, email, password, captchaChallenge, captchaResponse;
|
||||
if (arguments.length == 4) {
|
||||
[email, password, captchaChallenge, captchaResponse] = arguments;
|
||||
username = this._usernameFromAccount(email);
|
||||
} else {
|
||||
[username, password, email, captchaChallenge, captchaResponse] = arguments;
|
||||
}
|
||||
|
||||
createAccount: function createAccount(email, password,
|
||||
captchaChallenge, captchaResponse) {
|
||||
let username = this._usernameFromAccount(email);
|
||||
let payload = JSON.stringify({
|
||||
"password": Utils.encodeUTF8(password),
|
||||
"email": email,
|
||||
|
|
|
@ -47,15 +47,7 @@ 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");
|
||||
|
||||
let NetUtil;
|
||||
try {
|
||||
let ns = {};
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm", ns);
|
||||
NetUtil = ns.NetUtil;
|
||||
} catch (ex) {
|
||||
// Firefox 3.5 :(
|
||||
}
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
// Constants for makeSyncCallback, waitForSyncCallback
|
||||
const CB_READY = {};
|
||||
|
@ -220,15 +212,6 @@ let Utils = {
|
|||
}
|
||||
},
|
||||
|
||||
createStatement: function createStatement(db, query) {
|
||||
// Gecko 2.0
|
||||
if (db.createAsyncStatement)
|
||||
return db.createAsyncStatement(query);
|
||||
|
||||
// Gecko <2.0
|
||||
return db.createStatement(query);
|
||||
},
|
||||
|
||||
// Prototype for mozIStorageCallback, used in queryAsync below.
|
||||
// This allows us to define the handle* functions just once rather
|
||||
// than on every queryAsync invocation.
|
||||
|
@ -1069,21 +1052,6 @@ let Utils = {
|
|||
return;
|
||||
}
|
||||
|
||||
// Gecko < 2.0
|
||||
if (!NetUtil || !NetUtil.newChannel) {
|
||||
let json;
|
||||
try {
|
||||
let [is] = Utils.open(file, "<");
|
||||
json = JSON.parse(Utils.readStream(is));
|
||||
is.close();
|
||||
} catch (ex) {
|
||||
if (that._log)
|
||||
that._log.debug("Failed to load json: " + Utils.exceptionStr(ex));
|
||||
}
|
||||
callback.call(that, json);
|
||||
return;
|
||||
}
|
||||
|
||||
let channel = NetUtil.newChannel(file);
|
||||
channel.contentType = "application/json";
|
||||
|
||||
|
@ -1127,21 +1095,10 @@ let Utils = {
|
|||
let json = typeof obj == "function" ? obj.call(that) : obj;
|
||||
let out = JSON.stringify(json);
|
||||
|
||||
// Firefox 3.5
|
||||
if (!NetUtil) {
|
||||
let [fos] = Utils.open(file, ">");
|
||||
fos.writeString(out);
|
||||
fos.close();
|
||||
if (typeof callback == "function") {
|
||||
callback.call(that);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let fos = Cc["@mozilla.org/network/safe-file-output-stream;1"]
|
||||
.createInstance(Ci.nsIFileOutputStream);
|
||||
fos.init(file, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, PERMS_FILE,
|
||||
fos.DEFER_OPEN || 0);
|
||||
fos.DEFER_OPEN);
|
||||
let is = this._utf8Converter.convertToInputStream(out);
|
||||
NetUtil.asyncCopy(is, fos, function (result) {
|
||||
if (typeof callback == "function") {
|
||||
|
@ -1190,59 +1147,6 @@ let Utils = {
|
|||
return thisObj[name] = timer;
|
||||
},
|
||||
|
||||
// Gecko <2.0
|
||||
open: function open(pathOrFile, mode, perms) {
|
||||
let stream, file;
|
||||
|
||||
if (pathOrFile instanceof Ci.nsIFile) {
|
||||
file = pathOrFile;
|
||||
} else {
|
||||
file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
|
||||
dump("PATH IS" + pathOrFile + "\n");
|
||||
file.initWithPath(pathOrFile);
|
||||
}
|
||||
|
||||
if (!perms)
|
||||
perms = PERMS_FILE;
|
||||
|
||||
switch(mode) {
|
||||
case "<": {
|
||||
if (!file.exists())
|
||||
throw "Cannot open file for reading, file does not exist";
|
||||
let fis = Cc["@mozilla.org/network/file-input-stream;1"].
|
||||
createInstance(Ci.nsIFileInputStream);
|
||||
fis.init(file, MODE_RDONLY, perms, 0);
|
||||
stream = Cc["@mozilla.org/intl/converter-input-stream;1"].
|
||||
createInstance(Ci.nsIConverterInputStream);
|
||||
stream.init(fis, "UTF-8", 4096,
|
||||
Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
|
||||
} break;
|
||||
|
||||
case ">": {
|
||||
let fos = Cc["@mozilla.org/network/file-output-stream;1"].
|
||||
createInstance(Ci.nsIFileOutputStream);
|
||||
fos.init(file, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, perms, 0);
|
||||
stream = Cc["@mozilla.org/intl/converter-output-stream;1"]
|
||||
.createInstance(Ci.nsIConverterOutputStream);
|
||||
stream.init(fos, "UTF-8", 4096, 0x0000);
|
||||
} break;
|
||||
|
||||
case ">>": {
|
||||
let fos = Cc["@mozilla.org/network/file-output-stream;1"].
|
||||
createInstance(Ci.nsIFileOutputStream);
|
||||
fos.init(file, MODE_WRONLY | MODE_CREATE | MODE_APPEND, perms, 0);
|
||||
stream = Cc["@mozilla.org/intl/converter-output-stream;1"]
|
||||
.createInstance(Ci.nsIConverterOutputStream);
|
||||
stream.init(fos, "UTF-8", 4096, 0x0000);
|
||||
} break;
|
||||
|
||||
default:
|
||||
throw "Illegal mode to open(): " + mode;
|
||||
}
|
||||
|
||||
return [stream, file];
|
||||
},
|
||||
|
||||
getIcon: function(iconUri, defaultIcon) {
|
||||
try {
|
||||
let iconURI = Utils.makeURI(iconUri);
|
||||
|
@ -1263,16 +1167,6 @@ let Utils = {
|
|||
return Str.errors.get("error.reason.unknown");
|
||||
},
|
||||
|
||||
// Gecko <2.0
|
||||
// assumes an nsIConverterInputStream
|
||||
readStream: function Weave_readStream(is) {
|
||||
let ret = "", str = {};
|
||||
while (is.readString(4096, str) != 0) {
|
||||
ret += str.value;
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
|
||||
encodeUTF8: function(str) {
|
||||
try {
|
||||
str = this._utf8Converter.ConvertFromUnicode(str);
|
||||
|
@ -1672,15 +1566,9 @@ this.__defineGetter__("_sessionCID", function() {
|
|||
|
||||
Svc.__defineGetter__("Crypto", function() {
|
||||
let cryptoSvc;
|
||||
try {
|
||||
let ns = {};
|
||||
Cu.import("resource://services-crypto/WeaveCrypto.js", ns);
|
||||
cryptoSvc = new ns.WeaveCrypto();
|
||||
} catch (ex) {
|
||||
// Fallback to binary WeaveCrypto
|
||||
cryptoSvc = Cc["@labs.mozilla.com/Weave/Crypto;1"].
|
||||
getService(Ci.IWeaveCrypto);
|
||||
}
|
||||
let ns = {};
|
||||
Cu.import("resource://services-crypto/WeaveCrypto.js", ns);
|
||||
cryptoSvc = new ns.WeaveCrypto();
|
||||
delete Svc.Crypto;
|
||||
return Svc.Crypto = cryptoSvc;
|
||||
});
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
pref("services.sync.serverURL", "https://auth.services.mozilla.com/");
|
||||
pref("services.sync.storageAPI", "1.0");
|
||||
pref("services.sync.userURL", "user/");
|
||||
pref("services.sync.miscURL", "misc/");
|
||||
pref("services.sync.termsURL", "https://services.mozilla.com/tos/");
|
||||
|
@ -9,6 +8,7 @@ pref("services.sync.syncKeyHelpURL", "https://services.mozilla.com/help/synckey"
|
|||
|
||||
pref("services.sync.lastversion", "firstrun");
|
||||
pref("services.sync.autoconnect", true);
|
||||
pref("services.sync.sendVersionInfo", true);
|
||||
|
||||
pref("services.sync.engine.bookmarks", true);
|
||||
pref("services.sync.engine.history", true);
|
||||
|
|
|
@ -66,9 +66,18 @@ registrar.registerFactory(Components.ID("{fbfae60b-64a4-44ef-a911-08ceb70b9f31}"
|
|||
|
||||
}
|
||||
|
||||
// Provide resource://services-sync if it isn't already available
|
||||
let weaveService = Cc["@mozilla.org/weave/service;1"].getService();
|
||||
weaveService.wrappedJSObject.addResourceAlias();
|
||||
// Register resource aliases. Normally done in SyncComponents.manifest.
|
||||
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);
|
||||
}
|
||||
addResourceAlias();
|
||||
|
||||
|
||||
// Some tests hang on OSX debug builds. See bug 604565.
|
||||
|
|
|
@ -267,7 +267,7 @@ ServerCollection.prototype = {
|
|||
* Test setup helpers.
|
||||
*/
|
||||
function sync_httpd_setup(handlers) {
|
||||
handlers["/1.0/foo/storage/meta/global"]
|
||||
handlers["/1.1/foo/storage/meta/global"]
|
||||
= (new ServerWBO('global', {})).handler();
|
||||
return httpd_setup(handlers);
|
||||
}
|
||||
|
|
|
@ -5,12 +5,7 @@ Cu.import("resource://services-sync/log4moz.js");
|
|||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
try {
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
}
|
||||
catch(ex) {
|
||||
Cu.import("resource://gre/modules/utils.js");
|
||||
}
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
|
||||
Engines.register(BookmarksEngine);
|
||||
|
||||
|
@ -71,8 +66,8 @@ function test_processIncoming_error_orderChildren() {
|
|||
{engines: {bookmarks: {version: engine.version,
|
||||
syncID: engine.syncID}}});
|
||||
let server = httpd_setup({
|
||||
"/1.0/foo/storage/meta/global": global.handler(),
|
||||
"/1.0/foo/storage/bookmarks": collection.handler()
|
||||
"/1.1/foo/storage/meta/global": global.handler(),
|
||||
"/1.1/foo/storage/bookmarks": collection.handler()
|
||||
});
|
||||
|
||||
try {
|
||||
|
@ -153,8 +148,8 @@ function test_restorePromptsReupload() {
|
|||
{engines: {bookmarks: {version: engine.version,
|
||||
syncID: engine.syncID}}});
|
||||
let server = httpd_setup({
|
||||
"/1.0/foo/storage/meta/global": global.handler(),
|
||||
"/1.0/foo/storage/bookmarks": collection.handler()
|
||||
"/1.1/foo/storage/meta/global": global.handler(),
|
||||
"/1.1/foo/storage/bookmarks": collection.handler()
|
||||
});
|
||||
|
||||
Svc.Obs.notify("weave:engine:start-tracking"); // We skip usual startup...
|
||||
|
@ -328,8 +323,8 @@ function test_mismatched_types() {
|
|||
syncID: engine.syncID}}});
|
||||
_("GUID: " + store.GUIDForId(6, true));
|
||||
let server = httpd_setup({
|
||||
"/1.0/foo/storage/meta/global": global.handler(),
|
||||
"/1.0/foo/storage/bookmarks": collection.handler()
|
||||
"/1.1/foo/storage/meta/global": global.handler(),
|
||||
"/1.1/foo/storage/bookmarks": collection.handler()
|
||||
});
|
||||
|
||||
try {
|
||||
|
|
|
@ -5,12 +5,7 @@ Cu.import("resource://services-sync/engines/bookmarks.js");
|
|||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
try {
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
}
|
||||
catch(ex) {
|
||||
Cu.import("resource://gre/modules/utils.js");
|
||||
}
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
|
||||
const DESCRIPTION_ANNO = "bookmarkProperties/description";
|
||||
|
||||
|
|
|
@ -5,12 +5,7 @@ Cu.import("resource://services-sync/log4moz.js");
|
|||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
try {
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
}
|
||||
catch(ex) {
|
||||
Cu.import("resource://gre/modules/utils.js");
|
||||
}
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
|
||||
const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
|
||||
var IOService = Cc["@mozilla.org/network/io-service;1"]
|
||||
|
@ -115,8 +110,8 @@ function test_annotation_uploaded() {
|
|||
{engines: {bookmarks: {version: engine.version,
|
||||
syncID: engine.syncID}}});
|
||||
let server = httpd_setup({
|
||||
"/1.0/foo/storage/meta/global": global.handler(),
|
||||
"/1.0/foo/storage/bookmarks": collection.handler()
|
||||
"/1.1/foo/storage/meta/global": global.handler(),
|
||||
"/1.1/foo/storage/bookmarks": collection.handler()
|
||||
});
|
||||
|
||||
try {
|
||||
|
@ -204,8 +199,8 @@ function test_smart_bookmarks_duped() {
|
|||
{engines: {bookmarks: {version: engine.version,
|
||||
syncID: engine.syncID}}});
|
||||
let server = httpd_setup({
|
||||
"/1.0/foo/storage/meta/global": global.handler(),
|
||||
"/1.0/foo/storage/bookmarks": collection.handler()
|
||||
"/1.1/foo/storage/meta/global": global.handler(),
|
||||
"/1.1/foo/storage/bookmarks": collection.handler()
|
||||
});
|
||||
|
||||
try {
|
||||
|
|
|
@ -352,64 +352,6 @@ function test_reparentOrphans() {
|
|||
}
|
||||
}
|
||||
|
||||
// Copying a bookmark in the UI also copies its annotations, including the GUID
|
||||
// annotation in 3.x.
|
||||
function test_copying_avoid_duplicate_guids() {
|
||||
if (store._haveGUIDColumn) {
|
||||
_("No GUID annotation handling functionality; returning without testing.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
_("Ensure the record isn't present yet.");
|
||||
let ids = Svc.Bookmark.getBookmarkIdsForURI(fxuri, {});
|
||||
do_check_eq(ids.length, 0);
|
||||
|
||||
_("Let's create a new record.");
|
||||
let fxrecord = new Bookmark("bookmarks", "get-firefox1");
|
||||
fxrecord.bmkUri = fxuri.spec;
|
||||
fxrecord.description = "Firefox is awesome.";
|
||||
fxrecord.title = "Get Firefox!";
|
||||
fxrecord.tags = ["firefox", "awesome", "browser"];
|
||||
fxrecord.keyword = "awesome";
|
||||
fxrecord.loadInSidebar = false;
|
||||
fxrecord.parentName = "Bookmarks Toolbar";
|
||||
fxrecord.parentid = "toolbar";
|
||||
store.applyIncoming(fxrecord);
|
||||
|
||||
_("Verify it has been created correctly.");
|
||||
let id = store.idForGUID(fxrecord.id);
|
||||
do_check_eq(store.GUIDForId(id), fxrecord.id);
|
||||
do_check_true(Svc.Bookmark.getBookmarkURI(id).equals(fxuri));
|
||||
do_check_eq(Svc.Annos.getItemAnnotation(id, "bookmarkProperties/description"),
|
||||
fxrecord.description);
|
||||
|
||||
_("Copy the record as happens in the UI: with the same GUID.");
|
||||
let parentID = Svc.Bookmark.getFolderIdForItem(id);
|
||||
let copy = Svc.Bookmark.insertBookmark(parentID, fxuri, -1, fxrecord.title);
|
||||
Svc.Bookmark.setItemLastModified(copy, Svc.Bookmark.getItemLastModified(id));
|
||||
Svc.Bookmark.setItemDateAdded(copy, Svc.Bookmark.getItemDateAdded(id));
|
||||
store._setGUID(copy, fxrecord.id);
|
||||
|
||||
do_check_eq(store.GUIDForId(copy), store.GUIDForId(id));
|
||||
|
||||
_("Calling idForGUID fixes things.");
|
||||
_("GUID before: " + store.GUIDForId(copy));
|
||||
do_check_eq(store.idForGUID(fxrecord.id), id); // Oldest wins.
|
||||
_("GUID now: " + store.GUIDForId(copy));
|
||||
do_check_neq(store.GUIDForId(copy), store.GUIDForId(id));
|
||||
|
||||
_("Verify that the anno itself has changed.");
|
||||
do_check_neq(Svc.Annos.getItemAnnotation(copy, "sync/guid"), fxrecord.id);
|
||||
do_check_eq(Svc.Annos.getItemAnnotation(copy, "sync/guid"),
|
||||
store.GUIDForId(copy));
|
||||
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
store.wipe();
|
||||
}
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
initTestLogging('Trace');
|
||||
test_bookmark_create();
|
||||
|
@ -422,5 +364,4 @@ function run_test() {
|
|||
test_move_order();
|
||||
test_orphan();
|
||||
test_reparentOrphans();
|
||||
test_copying_avoid_duplicate_guids();
|
||||
}
|
||||
|
|
|
@ -1,15 +1,7 @@
|
|||
Cu.import("resource://services-sync/engines/bookmarks.js");
|
||||
Cu.import("resource://services-sync/engines.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
try {
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
}
|
||||
catch(ex) {
|
||||
Cu.import("resource://gre/modules/utils.js");
|
||||
}
|
||||
|
||||
// Grab a backstage pass so we can fetch the anno constant. Evil but useful.
|
||||
let bsp = Cu.import("resource://services-sync/engines/bookmarks.js");
|
||||
const SYNC_GUID_ANNO = bsp.GUID_ANNO; // Not the same as Places' GUID_ANNO!
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
|
||||
Engines.register(BookmarksEngine);
|
||||
let engine = Engines.get("bookmarks");
|
||||
|
@ -248,55 +240,33 @@ function test_tracking() {
|
|||
}
|
||||
}
|
||||
|
||||
function test_guid_stripping() {
|
||||
// Gecko <2.0
|
||||
if (store._haveGUIDColumn) {
|
||||
_("We have a GUID column; not testing anno GUID fixing.");
|
||||
return;
|
||||
}
|
||||
function test_onItemChanged() {
|
||||
// Anno that's in ANNOS_TO_TRACK.
|
||||
const GENERATOR_ANNO = "microsummary/generatorURI";
|
||||
|
||||
_("Verify we've got an empty tracker to work with.");
|
||||
let tracker = engine._tracker;
|
||||
let folder = Svc.Bookmark.createFolder(Svc.Bookmark.bookmarksMenuFolder,
|
||||
"Test Folder",
|
||||
Svc.Bookmark.DEFAULT_INDEX);
|
||||
function createBmk() {
|
||||
return Svc.Bookmark.insertBookmark(folder,
|
||||
Utils.makeURI("http://getfirefox.com"),
|
||||
Svc.Bookmark.DEFAULT_INDEX,
|
||||
"Get Firefox!");
|
||||
}
|
||||
|
||||
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
|
||||
|
||||
try {
|
||||
_("Directly testing GUID stripping.");
|
||||
Svc.Obs.notify("weave:engine:stop-tracking");
|
||||
let folder = Svc.Bookmark.createFolder(Svc.Bookmark.bookmarksMenuFolder,
|
||||
"Parent",
|
||||
Svc.Bookmark.DEFAULT_INDEX);
|
||||
_("Track changes to annos.");
|
||||
let b = Svc.Bookmark.insertBookmark(folder,
|
||||
Utils.makeURI("http://getfirefox.com"),
|
||||
Svc.Bookmark.DEFAULT_INDEX,
|
||||
"Get Firefox!");
|
||||
let bGUID = engine._store.GUIDForId(b);
|
||||
_("New item is " + b);
|
||||
_("GUID: " + bGUID);
|
||||
|
||||
Svc.Obs.notify("weave:engine:start-tracking");
|
||||
tracker.ignoreAll = false;
|
||||
let suspect = createBmk();
|
||||
let victim = createBmk();
|
||||
Svc.Annos.setItemAnnotation(b, GENERATOR_ANNO, "http://foo.bar/", 0,
|
||||
Svc.Annos.EXPIRE_NEVER);
|
||||
do_check_true(tracker.changedIDs[bGUID] > 0);
|
||||
|
||||
_("Suspect: " + suspect + ", victim: " + victim);
|
||||
|
||||
let suspectGUID = store.GUIDForId(suspect);
|
||||
let victimGUID = store.GUIDForId(victim);
|
||||
_("Set the GUID on one entry to be the same as another.");
|
||||
do_check_neq(suspectGUID, victimGUID);
|
||||
Svc.Annos.setItemAnnotation(suspect, SYNC_GUID_ANNO, store.GUIDForId(victim),
|
||||
0, Svc.Annos.EXPIRE_NEVER);
|
||||
|
||||
_("Tracker changed it to something else.");
|
||||
let newGUID = store.GUIDForId(suspect);
|
||||
do_check_neq(newGUID, victimGUID);
|
||||
do_check_neq(newGUID, suspectGUID);
|
||||
|
||||
_("Victim GUID remains unchanged.");
|
||||
do_check_eq(victimGUID, store.GUIDForId(victim));
|
||||
|
||||
_("New GUID is in the tracker.");
|
||||
_(JSON.stringify(tracker.changedIDs));
|
||||
do_check_neq(tracker.changedIDs[newGUID], null);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
store.wipe();
|
||||
|
@ -312,8 +282,8 @@ function run_test() {
|
|||
Log4Moz.repository.getLogger("Store.Bookmarks").level = Log4Moz.Level.Trace;
|
||||
Log4Moz.repository.getLogger("Tracker.Bookmarks").level = Log4Moz.Level.Trace;
|
||||
|
||||
test_onItemChanged();
|
||||
test_copying_places();
|
||||
test_tracking();
|
||||
test_guid_stripping();
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ Cu.import("resource://services-sync/constants.js");
|
|||
Cu.import("resource://services-sync/record.js");
|
||||
Cu.import("resource://services-sync/identity.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://services-sync/engines.js");
|
||||
Cu.import("resource://services-sync/engines/clients.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
|
||||
|
@ -33,10 +34,10 @@ function test_bad_hmac() {
|
|||
}
|
||||
|
||||
let handlers = {
|
||||
"/1.0/foo/info/collections": collectionsHelper.handler,
|
||||
"/1.0/foo/storage/meta/global": upd("meta", global.handler()),
|
||||
"/1.0/foo/storage/crypto/keys": upd("crypto", keysWBO.handler()),
|
||||
"/1.0/foo/storage/clients": trackDeletedHandler("crypto", clientsColl.handler())
|
||||
"/1.1/foo/info/collections": collectionsHelper.handler,
|
||||
"/1.1/foo/storage/meta/global": upd("meta", global.handler()),
|
||||
"/1.1/foo/storage/crypto/keys": upd("crypto", keysWBO.handler()),
|
||||
"/1.1/foo/storage/clients": trackDeletedHandler("crypto", clientsColl.handler())
|
||||
};
|
||||
|
||||
let server = httpd_setup(handlers);
|
||||
|
@ -76,6 +77,61 @@ function test_bad_hmac() {
|
|||
do_check_eq(1, clientsColl.count());
|
||||
_("Records now: " + clientsColl.get({}));
|
||||
|
||||
_("Now change our keys but don't upload them. " +
|
||||
"That means we get an HMAC error but redownload keys.");
|
||||
Service.lastHMACEvent = 0;
|
||||
Clients.localID = Utils.makeGUID();
|
||||
Clients.resetClient();
|
||||
CollectionKeys.generateNewKeys();
|
||||
deleted = false;
|
||||
do_check_eq(1, clientsColl.count());
|
||||
Clients.sync();
|
||||
|
||||
_("Old record was not deleted, new one uploaded.");
|
||||
do_check_false(deleted);
|
||||
do_check_eq(2, clientsColl.count());
|
||||
_("Records now: " + clientsColl.get({}));
|
||||
|
||||
_("Now try the scenario where our keys are wrong *and* there's a bad record.");
|
||||
// Clean up and start fresh.
|
||||
clientsColl.wbos = {};
|
||||
Service.lastHMACEvent = 0;
|
||||
Clients.localID = Utils.makeGUID();
|
||||
Clients.resetClient();
|
||||
deleted = false;
|
||||
do_check_eq(0, clientsColl.count());
|
||||
|
||||
// Create and upload keys.
|
||||
CollectionKeys.generateNewKeys();
|
||||
serverKeys = CollectionKeys.asWBO("crypto", "keys");
|
||||
serverKeys.encrypt(Weave.Service.syncKeyBundle);
|
||||
do_check_true(serverKeys.upload(Weave.Service.cryptoKeysURL).success);
|
||||
|
||||
// Sync once to upload a record.
|
||||
Clients.sync();
|
||||
do_check_eq(1, clientsColl.count());
|
||||
|
||||
// Generate and upload new keys, so the old client record is wrong.
|
||||
CollectionKeys.generateNewKeys();
|
||||
serverKeys = CollectionKeys.asWBO("crypto", "keys");
|
||||
serverKeys.encrypt(Weave.Service.syncKeyBundle);
|
||||
do_check_true(serverKeys.upload(Weave.Service.cryptoKeysURL).success);
|
||||
|
||||
// Create a new client record and new keys. Now our keys are wrong, as well
|
||||
// as the object on the server. We'll download the new keys and also delete
|
||||
// the bad client record.
|
||||
Clients.localID = Utils.makeGUID();
|
||||
Clients.resetClient();
|
||||
CollectionKeys.generateNewKeys();
|
||||
let oldKey = CollectionKeys.keyForCollection();
|
||||
|
||||
do_check_false(deleted);
|
||||
Clients.sync();
|
||||
do_check_true(deleted);
|
||||
do_check_eq(1, clientsColl.count());
|
||||
let newKey = CollectionKeys.keyForCollection();
|
||||
do_check_false(oldKey.equals(newKey));
|
||||
|
||||
} finally {
|
||||
server.stop(do_test_finished);
|
||||
Svc.Prefs.resetBranch("");
|
||||
|
@ -101,7 +157,6 @@ function test_sync() {
|
|||
_("Ensure that Clients engine uploads a new client record once a week.");
|
||||
Svc.Prefs.set("clusterURL", "http://localhost:8080/");
|
||||
Svc.Prefs.set("username", "foo");
|
||||
new SyncTestingInfrastructure();
|
||||
|
||||
CollectionKeys.generateNewKeys();
|
||||
|
||||
|
@ -111,9 +166,12 @@ function test_sync() {
|
|||
let coll = new ServerCollection();
|
||||
let clientwbo = coll.wbos[Clients.localID] = new ServerWBO(Clients.localID);
|
||||
let server = httpd_setup({
|
||||
"/1.0/foo/storage/meta/global": global.handler(),
|
||||
"/1.0/foo/storage/clients": coll.handler()
|
||||
"/1.1/foo/storage/meta/global": global.handler(),
|
||||
"/1.1/foo/storage/clients": coll.handler()
|
||||
});
|
||||
server.registerPathHandler(
|
||||
"/1.1/foo/storage/clients/" + Clients.localID, clientwbo.handler());
|
||||
|
||||
do_test_pending();
|
||||
|
||||
try {
|
||||
|
@ -133,10 +191,13 @@ function test_sync() {
|
|||
do_check_true(!!clientwbo.payload);
|
||||
do_check_true(Clients.lastRecordUpload > lastweek);
|
||||
|
||||
_("Remove client record.");
|
||||
Clients.removeClientData();
|
||||
do_check_eq(clientwbo.payload, undefined);
|
||||
|
||||
_("Time travel one day back, no record uploaded.");
|
||||
Clients.lastRecordUpload -= LESS_THAN_CLIENTS_TTL_REFRESH;
|
||||
let yesterday = Clients.lastRecordUpload;
|
||||
clientwbo.payload = undefined;
|
||||
Clients.sync();
|
||||
do_check_eq(clientwbo.payload, undefined);
|
||||
do_check_eq(Clients.lastRecordUpload, yesterday);
|
||||
|
@ -152,7 +213,7 @@ function test_sync() {
|
|||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
Log4Moz.repository.getLogger("Engine.Clients").level = Log4Moz.Level.Trace;
|
||||
test_bad_hmac(); // Needs to run first: doesn't use fake service!
|
||||
test_bad_hmac();
|
||||
test_properties();
|
||||
test_sync();
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ function run_test() {
|
|||
_("Set up test fixtures.");
|
||||
ID.set('WeaveID', new Identity('Some Identity', 'foo'));
|
||||
Svc.Prefs.set("clusterURL", "http://fakebase/");
|
||||
let baseUri = "http://fakebase/1.0/foo/storage/";
|
||||
let baseUri = "http://fakebase/1.1/foo/storage/";
|
||||
let pubUri = baseUri + "keys/pubkey";
|
||||
let privUri = baseUri + "keys/privkey";
|
||||
|
||||
|
|
|
@ -36,22 +36,22 @@ function test_locally_changed_keys() {
|
|||
do_test_pending();
|
||||
let server = httpd_setup({
|
||||
// Special.
|
||||
"/1.0/johndoe/storage/meta/global": upd("meta", meta_global.handler()),
|
||||
"/1.0/johndoe/info/collections": collectionsHelper.handler,
|
||||
"/1.0/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()),
|
||||
"/1.1/johndoe/storage/meta/global": upd("meta", meta_global.handler()),
|
||||
"/1.1/johndoe/info/collections": collectionsHelper.handler,
|
||||
"/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()),
|
||||
|
||||
// Track modified times.
|
||||
"/1.0/johndoe/storage/clients": upd("clients", clients.handler()),
|
||||
"/1.0/johndoe/storage/clients/foobar": upd("clients", new ServerWBO("clients").handler()),
|
||||
"/1.0/johndoe/storage/tabs": upd("tabs", new ServerCollection().handler()),
|
||||
"/1.1/johndoe/storage/clients": upd("clients", clients.handler()),
|
||||
"/1.1/johndoe/storage/clients/foobar": upd("clients", new ServerWBO("clients").handler()),
|
||||
"/1.1/johndoe/storage/tabs": upd("tabs", new ServerCollection().handler()),
|
||||
|
||||
// Just so we don't get 404s in the logs.
|
||||
"/1.0/johndoe/storage/bookmarks": new ServerCollection().handler(),
|
||||
"/1.0/johndoe/storage/forms": new ServerCollection().handler(),
|
||||
"/1.0/johndoe/storage/passwords": new ServerCollection().handler(),
|
||||
"/1.0/johndoe/storage/prefs": new ServerCollection().handler(),
|
||||
"/1.1/johndoe/storage/bookmarks": new ServerCollection().handler(),
|
||||
"/1.1/johndoe/storage/forms": new ServerCollection().handler(),
|
||||
"/1.1/johndoe/storage/passwords": new ServerCollection().handler(),
|
||||
"/1.1/johndoe/storage/prefs": new ServerCollection().handler(),
|
||||
|
||||
"/1.0/johndoe/storage/history": upd("history", history.handler()),
|
||||
"/1.1/johndoe/storage/history": upd("history", history.handler()),
|
||||
});
|
||||
|
||||
try {
|
||||
|
@ -144,7 +144,7 @@ function test_locally_changed_keys() {
|
|||
wbo.modified = modified;
|
||||
history.wbos[id] = wbo;
|
||||
server.registerPathHandler(
|
||||
"/1.0/johndoe/storage/history/record-no--" + i,
|
||||
"/1.1/johndoe/storage/history/record-no--" + i,
|
||||
upd("history", wbo.handler()));
|
||||
}
|
||||
|
||||
|
@ -204,7 +204,7 @@ function test_locally_changed_keys() {
|
|||
wbo.modified = modified;
|
||||
history.wbos[id] = wbo;
|
||||
server.registerPathHandler(
|
||||
"/1.0/johndoe/storage/history/record-no--" + i,
|
||||
"/1.1/johndoe/storage/history/record-no--" + i,
|
||||
upd("history", wbo.handler()));
|
||||
}
|
||||
collections.history = Date.now()/1000;
|
||||
|
|
|
@ -51,7 +51,7 @@ function test_processIncoming_mobile_history_batched() {
|
|||
}
|
||||
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/foo/storage/history": collection.handler()
|
||||
"/1.1/foo/storage/history": collection.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
|
|
@ -160,11 +160,10 @@ function run_test() {
|
|||
}, function (next) {
|
||||
|
||||
_("Make sure we handle invalid URLs in places databases gracefully.");
|
||||
let table = store._haveTempTables ? "moz_places_temp" : "moz_places";
|
||||
let query = "INSERT INTO " + table + " "
|
||||
let query = "INSERT INTO moz_places "
|
||||
+ "(url, title, rev_host, visit_count, last_visit_date) "
|
||||
+ "VALUES ('invalid-uri', 'Invalid URI', '.', 1, " + TIMESTAMP3 + ")";
|
||||
let stmt = Utils.createStatement(Svc.History.DBConnection, query);
|
||||
let stmt = Svc.History.DBConnection.createAsyncStatement(query);
|
||||
let result = Utils.queryAsync(stmt);
|
||||
do_check_eq([id for (id in store.getAllIDs())].length, 4);
|
||||
|
||||
|
|
|
@ -95,8 +95,7 @@ function test_history_guids() {
|
|||
dump("tbguid: " + tbguid + "\n");
|
||||
|
||||
_("History: Verify GUIDs are added to the guid column.");
|
||||
let stmt = Utils.createStatement(
|
||||
Svc.History.DBConnection,
|
||||
let stmt = Svc.History.DBConnection.createAsyncStatement(
|
||||
"SELECT id FROM moz_places WHERE guid = :guid");
|
||||
|
||||
stmt.params.guid = fxguid;
|
||||
|
@ -108,8 +107,7 @@ function test_history_guids() {
|
|||
do_check_eq(result.length, 1);
|
||||
|
||||
_("History: Verify GUIDs weren't added to annotations.");
|
||||
stmt = Utils.createStatement(
|
||||
Svc.History.DBConnection,
|
||||
stmt = Svc.History.DBConnection.createAsyncStatement(
|
||||
"SELECT a.content AS guid FROM moz_annos a WHERE guid = :guid");
|
||||
|
||||
stmt.params.guid = fxguid;
|
||||
|
@ -136,8 +134,7 @@ function test_bookmark_guids() {
|
|||
let tbguid = store.GUIDForId(tbid);
|
||||
|
||||
_("Bookmarks: Verify GUIDs are added to the guid column.");
|
||||
let stmt = Utils.createStatement(
|
||||
Svc.History.DBConnection,
|
||||
let stmt = Svc.History.DBConnection.createAsyncStatement(
|
||||
"SELECT id FROM moz_bookmarks WHERE guid = :guid");
|
||||
|
||||
stmt.params.guid = fxguid;
|
||||
|
@ -151,8 +148,7 @@ function test_bookmark_guids() {
|
|||
do_check_eq(result[0].id, tbid);
|
||||
|
||||
_("Bookmarks: Verify GUIDs weren't added to annotations.");
|
||||
stmt = Utils.createStatement(
|
||||
Svc.History.DBConnection,
|
||||
stmt = Svc.History.DBConnection.createAsyncStatement(
|
||||
"SELECT a.content AS guid FROM moz_items_annos a WHERE guid = :guid");
|
||||
|
||||
stmt.params.guid = fxguid;
|
||||
|
|
|
@ -221,6 +221,23 @@ function run_test() {
|
|||
let did401 = false;
|
||||
Observers.add("weave:resource:status:401", function() did401 = true);
|
||||
|
||||
_("Test that the BasicAuthenticator doesn't screw up header case.");
|
||||
let res1 = new Resource("http://localhost:8080/foo");
|
||||
res1.setHeader("Authorization", "Basic foobar");
|
||||
res1.authenticator = new NoOpAuthenticator();
|
||||
do_check_eq(res1._headers["authorization"], "Basic foobar");
|
||||
do_check_eq(res1.headers["authorization"], "Basic foobar");
|
||||
let id = new Identity("secret", "guest", "guest");
|
||||
res1.authenticator = new BasicAuthenticator(id);
|
||||
|
||||
// In other words... it correctly overwrites our downcased version
|
||||
// when accessed through .headers.
|
||||
do_check_eq(res1._headers["authorization"], "Basic foobar");
|
||||
do_check_eq(res1.headers["authorization"], "Basic Z3Vlc3Q6Z3Vlc3Q=");
|
||||
do_check_eq(res1._headers["authorization"], "Basic Z3Vlc3Q6Z3Vlc3Q=");
|
||||
do_check_true(!res1._headers["Authorization"]);
|
||||
do_check_true(!res1.headers["Authorization"]);
|
||||
|
||||
_("GET a password protected resource (test that it'll fail w/o pass, no throw)");
|
||||
let res2 = new Resource("http://localhost:8080/protected");
|
||||
content = res2.get();
|
||||
|
@ -346,8 +363,8 @@ function run_test() {
|
|||
do_check_eq(content, JSON.stringify({"x-what-is-weave": "awesome"}));
|
||||
|
||||
_("setHeader(): setting multiple headers, overwriting existing header");
|
||||
res9.setHeader('X-WHAT-is-Weave', 'more awesomer',
|
||||
'X-Another-Header', 'hello world');
|
||||
res9.setHeader('X-WHAT-is-Weave', 'more awesomer');
|
||||
res9.setHeader('X-Another-Header', 'hello world');
|
||||
do_check_eq(res9.headers['x-what-is-weave'], 'more awesomer');
|
||||
do_check_eq(res9.headers['x-another-header'], 'hello world');
|
||||
content = res9.get();
|
||||
|
|
|
@ -206,6 +206,26 @@ function run_test() {
|
|||
next();
|
||||
}));
|
||||
|
||||
}, function (next) {
|
||||
|
||||
_("Test that the BasicAuthenticator doesn't screw up header case.");
|
||||
let res1 = new AsyncResource("http://localhost:8080/foo");
|
||||
res1.setHeader("Authorization", "Basic foobar");
|
||||
res1.authenticator = new NoOpAuthenticator();
|
||||
do_check_eq(res1._headers["authorization"], "Basic foobar");
|
||||
do_check_eq(res1.headers["authorization"], "Basic foobar");
|
||||
let id = new Identity("secret", "guest", "guest");
|
||||
res1.authenticator = new BasicAuthenticator(id);
|
||||
|
||||
// In other words... it correctly overwrites our downcased version
|
||||
// when accessed through .headers.
|
||||
do_check_eq(res1._headers["authorization"], "Basic foobar");
|
||||
do_check_eq(res1.headers["authorization"], "Basic Z3Vlc3Q6Z3Vlc3Q=");
|
||||
do_check_eq(res1._headers["authorization"], "Basic Z3Vlc3Q6Z3Vlc3Q=");
|
||||
do_check_true(!res1._headers["Authorization"]);
|
||||
do_check_true(!res1.headers["Authorization"]);
|
||||
next();
|
||||
|
||||
}, function (next) {
|
||||
|
||||
_("GET a password protected resource (test that it'll fail w/o pass, no throw)");
|
||||
|
@ -458,8 +478,8 @@ function run_test() {
|
|||
|
||||
_("setHeader(): setting multiple headers, overwriting existing header");
|
||||
do_test_pending();
|
||||
res_headers.setHeader('X-WHAT-is-Weave', 'more awesomer',
|
||||
'X-Another-Header', 'hello world');
|
||||
res_headers.setHeader('X-WHAT-is-Weave', 'more awesomer');
|
||||
res_headers.setHeader('X-Another-Header', 'hello world');
|
||||
do_check_eq(res_headers.headers['x-what-is-weave'], 'more awesomer');
|
||||
do_check_eq(res_headers.headers['x-another-header'], 'hello world');
|
||||
res_headers.get(ensureThrows(function (error, content) {
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
Cu.import("resource://services-sync/constants.js");
|
||||
Cu.import("resource://services-sync/resource.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
|
||||
function test_resource_user_agent() {
|
||||
let meta_global = new ServerWBO('global');
|
||||
|
||||
// Tracking info/collections.
|
||||
let collectionsHelper = track_collections_helper();
|
||||
let collections = collectionsHelper.collections;
|
||||
|
||||
let ua;
|
||||
function uaHandler(f) {
|
||||
return function(request, response) {
|
||||
ua = request.getHeader("User-Agent");
|
||||
return f(request, response);
|
||||
};
|
||||
}
|
||||
|
||||
do_test_pending();
|
||||
let server = httpd_setup({
|
||||
"/1.1/johndoe/info/collections": uaHandler(collectionsHelper.handler),
|
||||
"/1.1/johndoe/storage/meta/global": uaHandler(meta_global.handler()),
|
||||
});
|
||||
|
||||
Weave.Service.serverURL = "http://localhost:8080/";
|
||||
Weave.Service.clusterURL = "http://localhost:8080/";
|
||||
Weave.Service.username = "johndoe";
|
||||
Weave.Service.password = "ilovejane";
|
||||
|
||||
let expectedUA = Svc.AppInfo.name + "/" + Svc.AppInfo.version +
|
||||
" FxSync/" + WEAVE_VERSION + "." + Svc.AppInfo.appBuildID;
|
||||
|
||||
function test_fetchInfo(next) {
|
||||
_("Testing _fetchInfo.");
|
||||
Weave.Service._fetchInfo();
|
||||
_("User-Agent: " + ua);
|
||||
do_check_eq(ua, expectedUA + ".desktop");
|
||||
ua = "";
|
||||
next();
|
||||
}
|
||||
|
||||
function test_desktop_post(next) {
|
||||
_("Testing direct Resource POST.");
|
||||
let r = new AsyncResource("http://localhost:8080/1.1/johndoe/storage/meta/global");
|
||||
r.post("foo=bar", function (error, content) {
|
||||
_("User-Agent: " + ua);
|
||||
do_check_eq(ua, expectedUA + ".desktop");
|
||||
ua = "";
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
function test_desktop_get(next) {
|
||||
_("Testing async.");
|
||||
Svc.Prefs.set("client.type", "desktop");
|
||||
let r = new AsyncResource("http://localhost:8080/1.1/johndoe/storage/meta/global");
|
||||
r.get(function(error, content) {
|
||||
_("User-Agent: " + ua);
|
||||
do_check_eq(ua, expectedUA + ".desktop");
|
||||
ua = "";
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
function test_mobile_get(next) {
|
||||
_("Testing mobile.");
|
||||
Svc.Prefs.set("client.type", "mobile");
|
||||
let r = new AsyncResource("http://localhost:8080/1.1/johndoe/storage/meta/global");
|
||||
r.get(function (error, content) {
|
||||
_("User-Agent: " + ua);
|
||||
do_check_eq(ua, expectedUA + ".mobile");
|
||||
ua = "";
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
Utils.asyncChain(
|
||||
test_fetchInfo,
|
||||
test_desktop_post,
|
||||
test_desktop_get,
|
||||
test_mobile_get,
|
||||
function (next) {
|
||||
server.stop(next);
|
||||
},
|
||||
do_test_finished)();
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
test_resource_user_agent();
|
||||
}
|
|
@ -81,13 +81,13 @@ function test_urls() {
|
|||
Service.clusterURL = "http://weave.cluster/";
|
||||
do_check_eq(Svc.Prefs.get("clusterURL"), "http://weave.cluster/");
|
||||
|
||||
do_check_eq(Service.userBaseURL, "http://weave.cluster/1.0/johndoe/");
|
||||
do_check_eq(Service.userBaseURL, "http://weave.cluster/1.1/johndoe/");
|
||||
do_check_eq(Service.infoURL,
|
||||
"http://weave.cluster/1.0/johndoe/info/collections");
|
||||
"http://weave.cluster/1.1/johndoe/info/collections");
|
||||
do_check_eq(Service.storageURL,
|
||||
"http://weave.cluster/1.0/johndoe/storage/");
|
||||
"http://weave.cluster/1.1/johndoe/storage/");
|
||||
do_check_eq(Service.metaURL,
|
||||
"http://weave.cluster/1.0/johndoe/storage/meta/global");
|
||||
"http://weave.cluster/1.1/johndoe/storage/meta/global");
|
||||
|
||||
_("The 'miscURL' and 'userURL' attributes can be relative to 'serverURL' or absolute.");
|
||||
Svc.Prefs.set("miscURL", "relative/misc/");
|
||||
|
|
|
@ -15,7 +15,7 @@ function run_test() {
|
|||
Service.serverURL = "http://localhost:8080/";
|
||||
|
||||
_("A 404 will be recorded as 'generic-server-error'");
|
||||
do_check_eq(Service.checkUsername("jimdoe"), "generic-server-error");
|
||||
do_check_eq(Service.checkAccount("jimdoe"), "generic-server-error");
|
||||
|
||||
_("Account that's available.");
|
||||
do_check_eq(Service.checkAccount("john@doe.com"), "available");
|
||||
|
@ -23,13 +23,11 @@ function run_test() {
|
|||
_("Account that's not available.");
|
||||
do_check_eq(Service.checkAccount("jane@doe.com"), "notAvailable");
|
||||
|
||||
// Backwards compat with the Firefox UI. Remove once bug 595066 has landed.
|
||||
_("Username fallback: Account that's not available.");
|
||||
do_check_eq(Service.checkAccount("johndoe"), "notAvailable");
|
||||
|
||||
_("Account that's not available.");
|
||||
do_check_eq(Service.checkUsername("johndoe"), "notAvailable");
|
||||
|
||||
_("Account that's available.");
|
||||
do_check_eq(Service.checkUsername("janedoe"), "available");
|
||||
_("Username fallback: Account that's available.");
|
||||
do_check_eq(Service.checkAccount("janedoe"), "available");
|
||||
|
||||
} finally {
|
||||
Svc.Prefs.resetBranch("");
|
||||
|
|
|
@ -23,10 +23,7 @@ function run_test() {
|
|||
// jane@doe.com
|
||||
"/user/1.0/vuuf3eqgloxpxmzph27f5a6ve7gzlrms": send(400, "Bad Request", "2"),
|
||||
// jim@doe.com
|
||||
"/user/1.0/vz6fhecgw5t3sgx3a4cektoiokyczkqd": send(500, "Server Error", "Server Error"),
|
||||
"/user/1.0/johndoe": send(200, "OK", "0"),
|
||||
"/user/1.0/janedoe": send(400, "Bad Request", "2"),
|
||||
"/user/1.0/jimdoe": send(500, "Server Error", "Server Error")
|
||||
"/user/1.0/vz6fhecgw5t3sgx3a4cektoiokyczkqd": send(500, "Server Error", "Server Error")
|
||||
});
|
||||
try {
|
||||
Service.serverURL = "http://localhost:8080/";
|
||||
|
@ -65,24 +62,6 @@ function run_test() {
|
|||
"challenge", "response");
|
||||
do_check_eq(secretHeader, "my-server-secret");
|
||||
|
||||
|
||||
// Backwards compat with the Firefox UI. Remove once bug 595066 has landed.
|
||||
|
||||
_("Create an old-style account.");
|
||||
res = Service.createAccount("johndoe", "mysecretpw", "john@doe.com",
|
||||
"challenge", "response");
|
||||
do_check_eq(res, null);
|
||||
|
||||
_("Invalid captcha or other user-friendly error.");
|
||||
res = Service.createAccount("janedoe", "anothersecretpw", "jane@doe.com",
|
||||
"challenge", "response");
|
||||
do_check_eq(res, "invalid-captcha");
|
||||
|
||||
_("Generic server error.");
|
||||
res = Service.createAccount("jimdoe", "preciousss", "jim@doe.com",
|
||||
"challenge", "response");
|
||||
do_check_eq(res, "generic-server-error");
|
||||
|
||||
} finally {
|
||||
Svc.Prefs.resetBranch("");
|
||||
server.stop(do_test_finished);
|
||||
|
|
|
@ -24,20 +24,20 @@ function v4_upgrade(next) {
|
|||
let keysWBO = new ServerWBO("keys");
|
||||
let server = httpd_setup({
|
||||
// Special.
|
||||
"/1.0/johndoe/info/collections": collectionsHelper.handler,
|
||||
"/1.0/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()),
|
||||
"/1.0/johndoe/storage/meta/global": upd("meta", meta_global.handler()),
|
||||
"/1.1/johndoe/info/collections": collectionsHelper.handler,
|
||||
"/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()),
|
||||
"/1.1/johndoe/storage/meta/global": upd("meta", meta_global.handler()),
|
||||
|
||||
// Track modified times.
|
||||
"/1.0/johndoe/storage/clients": upd("clients", clients.handler()),
|
||||
"/1.0/johndoe/storage/tabs": upd("tabs", new ServerCollection().handler()),
|
||||
"/1.1/johndoe/storage/clients": upd("clients", clients.handler()),
|
||||
"/1.1/johndoe/storage/tabs": upd("tabs", new ServerCollection().handler()),
|
||||
|
||||
// Just so we don't get 404s in the logs.
|
||||
"/1.0/johndoe/storage/bookmarks": new ServerCollection().handler(),
|
||||
"/1.0/johndoe/storage/forms": new ServerCollection().handler(),
|
||||
"/1.0/johndoe/storage/history": new ServerCollection().handler(),
|
||||
"/1.0/johndoe/storage/passwords": new ServerCollection().handler(),
|
||||
"/1.0/johndoe/storage/prefs": new ServerCollection().handler()
|
||||
"/1.1/johndoe/storage/bookmarks": new ServerCollection().handler(),
|
||||
"/1.1/johndoe/storage/forms": new ServerCollection().handler(),
|
||||
"/1.1/johndoe/storage/history": new ServerCollection().handler(),
|
||||
"/1.1/johndoe/storage/passwords": new ServerCollection().handler(),
|
||||
"/1.1/johndoe/storage/prefs": new ServerCollection().handler()
|
||||
});
|
||||
|
||||
try {
|
||||
|
@ -203,14 +203,14 @@ function v5_upgrade(next) {
|
|||
|
||||
let server = httpd_setup({
|
||||
// Special.
|
||||
"/1.0/johndoe/storage/meta/global": upd("meta", meta_global.handler()),
|
||||
"/1.0/johndoe/info/collections": collectionsHelper.handler,
|
||||
"/1.0/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()),
|
||||
"/1.0/johndoe/storage/crypto/bulk": upd("crypto", bulkWBO.handler()),
|
||||
"/1.1/johndoe/storage/meta/global": upd("meta", meta_global.handler()),
|
||||
"/1.1/johndoe/info/collections": collectionsHelper.handler,
|
||||
"/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()),
|
||||
"/1.1/johndoe/storage/crypto/bulk": upd("crypto", bulkWBO.handler()),
|
||||
|
||||
// Track modified times.
|
||||
"/1.0/johndoe/storage/clients": upd("clients", clients.handler()),
|
||||
"/1.0/johndoe/storage/tabs": upd("tabs", new ServerCollection().handler()),
|
||||
"/1.1/johndoe/storage/clients": upd("clients", clients.handler()),
|
||||
"/1.1/johndoe/storage/tabs": upd("tabs", new ServerCollection().handler()),
|
||||
});
|
||||
|
||||
try {
|
||||
|
|
|
@ -38,16 +38,16 @@ function run_test() {
|
|||
|
||||
do_test_pending();
|
||||
let server = httpd_setup({
|
||||
"/1.0/johndoe/info/collections": login_handler,
|
||||
"/1.0/janedoe/info/collections": login_handler,
|
||||
"/1.1/johndoe/info/collections": login_handler,
|
||||
"/1.1/janedoe/info/collections": login_handler,
|
||||
|
||||
// We need these handlers because we test login, and login
|
||||
// is where keys are generated or fetched.
|
||||
// TODO: have Jane fetch her keys, not generate them...
|
||||
"/1.0/johndoe/storage/crypto/keys": new ServerWBO().handler(),
|
||||
"/1.0/johndoe/storage/meta/global": new ServerWBO().handler(),
|
||||
"/1.0/janedoe/storage/crypto/keys": new ServerWBO().handler(),
|
||||
"/1.0/janedoe/storage/meta/global": new ServerWBO().handler()
|
||||
"/1.1/johndoe/storage/crypto/keys": new ServerWBO().handler(),
|
||||
"/1.1/johndoe/storage/meta/global": new ServerWBO().handler(),
|
||||
"/1.1/janedoe/storage/crypto/keys": new ServerWBO().handler(),
|
||||
"/1.1/janedoe/storage/meta/global": new ServerWBO().handler()
|
||||
});
|
||||
|
||||
try {
|
||||
|
|
|
@ -55,9 +55,9 @@ function run_test() {
|
|||
|
||||
do_test_pending();
|
||||
let server = httpd_setup({
|
||||
"/1.0/johndoe/info/collections": info_collections,
|
||||
"/1.0/johndoe/storage/meta/global": new ServerWBO().handler(),
|
||||
"/1.0/johndoe/storage/crypto/keys": new ServerWBO().handler(),
|
||||
"/1.1/johndoe/info/collections": info_collections,
|
||||
"/1.1/johndoe/storage/meta/global": new ServerWBO().handler(),
|
||||
"/1.1/johndoe/storage/crypto/keys": new ServerWBO().handler(),
|
||||
"/user/1.0/johndoe/password": change_password
|
||||
});
|
||||
|
||||
|
|
|
@ -9,10 +9,10 @@ function run_test() {
|
|||
|
||||
do_test_pending();
|
||||
let server = httpd_setup({
|
||||
"/1.0/johndoe/info/collection_usage": httpd_handler(200, "OK", JSON.stringify(collection_usage)),
|
||||
"/1.0/johndoe/info/quota": httpd_handler(200, "OK", JSON.stringify(quota)),
|
||||
"/1.0/janedoe/info/collection_usage": httpd_handler(200, "OK", "gargabe"),
|
||||
"/1.0/janedoe/info/quota": httpd_handler(200, "OK", "more garbage")
|
||||
"/1.1/johndoe/info/collection_usage": httpd_handler(200, "OK", JSON.stringify(collection_usage)),
|
||||
"/1.1/johndoe/info/quota": httpd_handler(200, "OK", JSON.stringify(quota)),
|
||||
"/1.1/janedoe/info/collection_usage": httpd_handler(200, "OK", "gargabe"),
|
||||
"/1.1/janedoe/info/quota": httpd_handler(200, "OK", "more garbage")
|
||||
});
|
||||
|
||||
try {
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
Cu.import("resource://services-sync/engines.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
function BlaEngine() {
|
||||
SyncEngine.call(this, "Bla");
|
||||
}
|
||||
BlaEngine.prototype = {
|
||||
__proto__: SyncEngine.prototype,
|
||||
|
||||
removed: false,
|
||||
removeClientData: function() {
|
||||
this.removed = true;
|
||||
}
|
||||
|
||||
};
|
||||
Engines.register(BlaEngine);
|
||||
|
||||
|
||||
function test_removeClientData() {
|
||||
let engine = Engines.get("bla");
|
||||
do_check_false(engine.removed);
|
||||
Service.startOver();
|
||||
do_check_true(engine.removed);
|
||||
}
|
||||
|
||||
|
||||
function run_test() {
|
||||
test_removeClientData();
|
||||
}
|
|
@ -20,9 +20,9 @@ function run_test() {
|
|||
|
||||
do_test_pending();
|
||||
let server = httpd_setup({
|
||||
"/1.0/johndoe/storage/crypto/keys": new ServerWBO().handler(),
|
||||
"/1.0/johndoe/storage/meta/global": new ServerWBO().handler(),
|
||||
"/1.0/johndoe/info/collections": login_handler
|
||||
"/1.1/johndoe/storage/crypto/keys": new ServerWBO().handler(),
|
||||
"/1.1/johndoe/storage/meta/global": new ServerWBO().handler(),
|
||||
"/1.1/johndoe/info/collections": login_handler
|
||||
});
|
||||
|
||||
const GLOBAL_SCORE = 42;
|
||||
|
|
|
@ -38,10 +38,10 @@ function sync_httpd_setup() {
|
|||
engines: engines});
|
||||
|
||||
let handlers = {
|
||||
"/1.0/johndoe/info/collections": collectionsHelper.handler,
|
||||
"/1.0/johndoe/storage/meta/global": upd("meta", globalWBO.handler()),
|
||||
"/1.0/johndoe/storage/clients": upd("clients", clientsColl.handler()),
|
||||
"/1.0/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler())
|
||||
"/1.1/johndoe/info/collections": collectionsHelper.handler,
|
||||
"/1.1/johndoe/storage/meta/global": upd("meta", globalWBO.handler()),
|
||||
"/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler()),
|
||||
"/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler())
|
||||
}
|
||||
return httpd_setup(handlers);
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ function generateAndUploadKeys() {
|
|||
CollectionKeys.generateNewKeys();
|
||||
let serverKeys = CollectionKeys.asWBO("crypto", "keys");
|
||||
serverKeys.encrypt(Weave.Service.syncKeyBundle);
|
||||
return serverKeys.upload("http://localhost:8080/1.0/johndoe/storage/crypto/keys").success;
|
||||
return serverKeys.upload("http://localhost:8080/1.1/johndoe/storage/crypto/keys").success;
|
||||
}
|
||||
|
||||
function test_backoff500(next) {
|
||||
|
|
|
@ -33,12 +33,12 @@ function run_test() {
|
|||
let metaColl = new ServerCollection({global: meta_global});
|
||||
do_test_pending();
|
||||
let server = httpd_setup({
|
||||
"/1.0/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()),
|
||||
"/1.0/johndoe/storage/crypto": upd("crypto", cryptoColl.handler()),
|
||||
"/1.0/johndoe/storage/clients": upd("clients", clients.handler()),
|
||||
"/1.0/johndoe/storage/meta/global": upd("meta", wasCalledHandler(meta_global)),
|
||||
"/1.0/johndoe/storage/meta": upd("meta", wasCalledHandler(metaColl)),
|
||||
"/1.0/johndoe/info/collections": collectionsHelper.handler
|
||||
"/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()),
|
||||
"/1.1/johndoe/storage/crypto": upd("crypto", cryptoColl.handler()),
|
||||
"/1.1/johndoe/storage/clients": upd("clients", clients.handler()),
|
||||
"/1.1/johndoe/storage/meta/global": upd("meta", wasCalledHandler(meta_global)),
|
||||
"/1.1/johndoe/storage/meta": upd("meta", wasCalledHandler(metaColl)),
|
||||
"/1.1/johndoe/info/collections": collectionsHelper.handler
|
||||
});
|
||||
|
||||
try {
|
||||
|
|
|
@ -48,14 +48,14 @@ let upd = collectionsHelper.with_updated_collection;
|
|||
|
||||
function sync_httpd_setup(handlers) {
|
||||
|
||||
handlers["/1.0/johndoe/info/collections"] = collectionsHelper.handler;
|
||||
handlers["/1.1/johndoe/info/collections"] = collectionsHelper.handler;
|
||||
|
||||
let cr = new ServerWBO("keys");
|
||||
handlers["/1.0/johndoe/storage/crypto/keys"] =
|
||||
handlers["/1.1/johndoe/storage/crypto/keys"] =
|
||||
upd("crypto", cr.handler());
|
||||
|
||||
let cl = new ServerCollection();
|
||||
handlers["/1.0/johndoe/storage/clients"] =
|
||||
handlers["/1.1/johndoe/storage/clients"] =
|
||||
upd("clients", cl.handler());
|
||||
|
||||
return httpd_setup(handlers);
|
||||
|
@ -75,8 +75,8 @@ function test_newAccount() {
|
|||
_("Test: New account does not disable locally enabled engines.");
|
||||
let engine = Engines.get("steam");
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/johndoe/storage/meta/global": new ServerWBO("global", {}).handler(),
|
||||
"/1.0/johndoe/storage/steam": new ServerWBO("steam", {}).handler()
|
||||
"/1.1/johndoe/storage/meta/global": new ServerWBO("global", {}).handler(),
|
||||
"/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler()
|
||||
});
|
||||
do_test_pending();
|
||||
setUp();
|
||||
|
@ -107,8 +107,8 @@ function test_enabledLocally() {
|
|||
storageVersion: STORAGE_VERSION,
|
||||
engines: {}});
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/johndoe/storage/meta/global": metaWBO.handler(),
|
||||
"/1.0/johndoe/storage/steam": new ServerWBO("steam", {}).handler()
|
||||
"/1.1/johndoe/storage/meta/global": metaWBO.handler(),
|
||||
"/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler()
|
||||
});
|
||||
do_test_pending();
|
||||
setUp();
|
||||
|
@ -144,8 +144,8 @@ function test_disabledLocally() {
|
|||
});
|
||||
let steamCollection = new ServerWBO("steam", PAYLOAD);
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/johndoe/storage/meta/global": metaWBO.handler(),
|
||||
"/1.0/johndoe/storage/steam": steamCollection.handler()
|
||||
"/1.1/johndoe/storage/meta/global": metaWBO.handler(),
|
||||
"/1.1/johndoe/storage/steam": steamCollection.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
setUp();
|
||||
|
@ -186,8 +186,8 @@ function test_enabledRemotely() {
|
|||
version: engine.version}}
|
||||
});
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/johndoe/storage/meta/global": metaWBO.handler(),
|
||||
"/1.0/johndoe/storage/steam": new ServerWBO("steam", {}).handler()
|
||||
"/1.1/johndoe/storage/meta/global": metaWBO.handler(),
|
||||
"/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler()
|
||||
});
|
||||
do_test_pending();
|
||||
setUp();
|
||||
|
@ -219,10 +219,10 @@ function test_disabledRemotelyTwoClients() {
|
|||
storageVersion: STORAGE_VERSION,
|
||||
engines: {}});
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/johndoe/storage/meta/global":
|
||||
"/1.1/johndoe/storage/meta/global":
|
||||
upd("meta", metaWBO.handler()),
|
||||
|
||||
"/1.0/johndoe/storage/steam":
|
||||
"/1.1/johndoe/storage/steam":
|
||||
upd("steam", new ServerWBO("steam", {}).handler())
|
||||
});
|
||||
do_test_pending();
|
||||
|
@ -265,8 +265,8 @@ function test_disabledRemotely() {
|
|||
storageVersion: STORAGE_VERSION,
|
||||
engines: {}});
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/johndoe/storage/meta/global": metaWBO.handler(),
|
||||
"/1.0/johndoe/storage/steam": new ServerWBO("steam", {}).handler()
|
||||
"/1.1/johndoe/storage/meta/global": metaWBO.handler(),
|
||||
"/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler()
|
||||
});
|
||||
do_test_pending();
|
||||
setUp();
|
||||
|
@ -299,9 +299,9 @@ function test_dependentEnginesEnabledLocally() {
|
|||
storageVersion: STORAGE_VERSION,
|
||||
engines: {}});
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/johndoe/storage/meta/global": metaWBO.handler(),
|
||||
"/1.0/johndoe/storage/steam": new ServerWBO("steam", {}).handler(),
|
||||
"/1.0/johndoe/storage/stirling": new ServerWBO("stirling", {}).handler()
|
||||
"/1.1/johndoe/storage/meta/global": metaWBO.handler(),
|
||||
"/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler(),
|
||||
"/1.1/johndoe/storage/stirling": new ServerWBO("stirling", {}).handler()
|
||||
});
|
||||
do_test_pending();
|
||||
setUp();
|
||||
|
@ -344,9 +344,9 @@ function test_dependentEnginesDisabledLocally() {
|
|||
let steamCollection = new ServerWBO("steam", PAYLOAD);
|
||||
let stirlingCollection = new ServerWBO("stirling", PAYLOAD);
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/johndoe/storage/meta/global": metaWBO.handler(),
|
||||
"/1.0/johndoe/storage/steam": steamCollection.handler(),
|
||||
"/1.0/johndoe/storage/stirling": stirlingCollection.handler()
|
||||
"/1.1/johndoe/storage/meta/global": metaWBO.handler(),
|
||||
"/1.1/johndoe/storage/steam": steamCollection.handler(),
|
||||
"/1.1/johndoe/storage/stirling": stirlingCollection.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
setUp();
|
||||
|
|
|
@ -34,10 +34,10 @@ function run_test() {
|
|||
|
||||
do_test_pending();
|
||||
let server = httpd_setup({
|
||||
"/api/1.0/johndoe/info/collections": login_handler,
|
||||
"/api/1.0/janedoe/info/collections": service_unavailable,
|
||||
"/api/1.0/johndoe/storage/meta/global": new ServerWBO().handler(),
|
||||
"/api/1.0/johndoe/storage/crypto/keys": new ServerWBO().handler(),
|
||||
"/api/1.1/johndoe/info/collections": login_handler,
|
||||
"/api/1.1/janedoe/info/collections": service_unavailable,
|
||||
"/api/1.1/johndoe/storage/meta/global": new ServerWBO().handler(),
|
||||
"/api/1.1/johndoe/storage/crypto/keys": new ServerWBO().handler(),
|
||||
"/user/1.0/johndoe/node/weave": httpd_handler(200, "OK", "http://localhost:8080/api/")
|
||||
});
|
||||
|
||||
|
|
|
@ -44,9 +44,9 @@ function test_withCollectionList_fail() {
|
|||
let diesel_coll = new FakeCollection();
|
||||
|
||||
let server = httpd_setup({
|
||||
"/1.0/johndoe/storage/steam": steam_coll.handler(),
|
||||
"/1.0/johndoe/storage/petrol": serviceUnavailable,
|
||||
"/1.0/johndoe/storage/diesel": diesel_coll.handler()
|
||||
"/1.1/johndoe/storage/steam": steam_coll.handler(),
|
||||
"/1.1/johndoe/storage/petrol": serviceUnavailable,
|
||||
"/1.1/johndoe/storage/diesel": diesel_coll.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
@ -99,10 +99,10 @@ function test_wipeServer_leaves_collections() {
|
|||
}
|
||||
|
||||
let server = httpd_setup({
|
||||
"/1.0/johndoe/storage/steam": steam_coll.handler(),
|
||||
"/1.0/johndoe/storage/diesel": diesel_coll.handler(),
|
||||
"/1.0/johndoe/storage/keys": keys_coll.handler(),
|
||||
"/1.0/johndoe/info/collections": info_collections
|
||||
"/1.1/johndoe/storage/steam": steam_coll.handler(),
|
||||
"/1.1/johndoe/storage/diesel": diesel_coll.handler(),
|
||||
"/1.1/johndoe/storage/keys": keys_coll.handler(),
|
||||
"/1.1/johndoe/info/collections": info_collections
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
|
|
@ -12,9 +12,9 @@ function test_url_attributes() {
|
|||
Svc.Prefs.set("clusterURL", "https://cluster/");
|
||||
let engine = makeSteamEngine();
|
||||
try {
|
||||
do_check_eq(engine.storageURL, "https://cluster/1.0/foo/storage/");
|
||||
do_check_eq(engine.engineURL, "https://cluster/1.0/foo/storage/steam");
|
||||
do_check_eq(engine.metaURL, "https://cluster/1.0/foo/storage/meta/global");
|
||||
do_check_eq(engine.storageURL, "https://cluster/1.1/foo/storage/");
|
||||
do_check_eq(engine.engineURL, "https://cluster/1.1/foo/storage/steam");
|
||||
do_check_eq(engine.metaURL, "https://cluster/1.1/foo/storage/meta/global");
|
||||
} finally {
|
||||
Svc.Prefs.resetBranch("");
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ function test_wipeServer() {
|
|||
const PAYLOAD = 42;
|
||||
let steamCollection = new ServerWBO("steam", PAYLOAD);
|
||||
let server = httpd_setup({
|
||||
"/1.0/foo/storage/steam": steamCollection.handler()
|
||||
"/1.1/foo/storage/steam": steamCollection.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ function test_syncStartup_emptyOrOutdatedGlobalsResetsSync() {
|
|||
denomination: "Flying Scotsman"}));
|
||||
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/foo/storage/steam": collection.handler()
|
||||
"/1.1/foo/storage/steam": collection.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
@ -176,7 +176,7 @@ function test_syncStartup_serverHasNewerVersion() {
|
|||
Svc.Prefs.set("username", "foo");
|
||||
let global = new ServerWBO('global', {engines: {steam: {version: 23456}}});
|
||||
let server = httpd_setup({
|
||||
"/1.0/foo/storage/meta/global": global.handler()
|
||||
"/1.1/foo/storage/meta/global": global.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
@ -215,7 +215,7 @@ function test_syncStartup_syncIDMismatchResetsClient() {
|
|||
let global = new ServerWBO('global',
|
||||
{engines: {steam: {version: engine.version,
|
||||
syncID: 'foobar'}}});
|
||||
server.registerPathHandler("/1.0/foo/storage/meta/global", global.handler());
|
||||
server.registerPathHandler("/1.1/foo/storage/meta/global", global.handler());
|
||||
|
||||
try {
|
||||
|
||||
|
@ -250,7 +250,7 @@ function test_processIncoming_emptyServer() {
|
|||
let collection = new ServerCollection();
|
||||
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/foo/storage/steam": collection.handler()
|
||||
"/1.1/foo/storage/steam": collection.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
@ -293,9 +293,9 @@ function test_processIncoming_createFromServer() {
|
|||
denomination: "Pathological Case"}));
|
||||
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/foo/storage/steam": collection.handler(),
|
||||
"/1.0/foo/storage/steam/flying": collection.wbos.flying.handler(),
|
||||
"/1.0/foo/storage/steam/scotsman": collection.wbos.scotsman.handler()
|
||||
"/1.1/foo/storage/steam": collection.handler(),
|
||||
"/1.1/foo/storage/steam/flying": collection.wbos.flying.handler(),
|
||||
"/1.1/foo/storage/steam/scotsman": collection.wbos.scotsman.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
@ -387,7 +387,7 @@ function test_processIncoming_reconcile() {
|
|||
deleted: true}));
|
||||
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/foo/storage/steam": collection.handler()
|
||||
"/1.1/foo/storage/steam": collection.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
@ -483,7 +483,7 @@ function test_processIncoming_mobile_batchSize() {
|
|||
}
|
||||
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/foo/storage/steam": collection.handler()
|
||||
"/1.1/foo/storage/steam": collection.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
@ -563,7 +563,7 @@ function test_processIncoming_store_toFetch() {
|
|||
meta_global.payload.engines = {steam: {version: engine.version,
|
||||
syncID: engine.syncID}};
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/foo/storage/steam": collection.handler()
|
||||
"/1.1/foo/storage/steam": collection.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
@ -631,7 +631,7 @@ function test_processIncoming_resume_toFetch() {
|
|||
meta_global.payload.engines = {steam: {version: engine.version,
|
||||
syncID: engine.syncID}};
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/foo/storage/steam": collection.handler()
|
||||
"/1.1/foo/storage/steam": collection.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
@ -688,7 +688,7 @@ function test_processIncoming_applyIncomingBatchSize_smaller() {
|
|||
meta_global.payload.engines = {steam: {version: engine.version,
|
||||
syncID: engine.syncID}};
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/foo/storage/steam": collection.handler()
|
||||
"/1.1/foo/storage/steam": collection.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
@ -746,7 +746,7 @@ function test_processIncoming_applyIncomingBatchSize_multiple() {
|
|||
meta_global.payload.engines = {steam: {version: engine.version,
|
||||
syncID: engine.syncID}};
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/foo/storage/steam": collection.handler()
|
||||
"/1.1/foo/storage/steam": collection.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
@ -777,10 +777,6 @@ function test_processIncoming_failed_records() {
|
|||
Svc.Prefs.set("clusterURL", "http://localhost:8080/");
|
||||
Svc.Prefs.set("username", "foo");
|
||||
|
||||
// Pretend to be a mobile client so we can test failed record handling
|
||||
// while batching GETs.
|
||||
Svc.Prefs.set("client.type", "mobile");
|
||||
|
||||
// Let's create three and a bit batches worth of server side records.
|
||||
let collection = new ServerCollection();
|
||||
const NUMBER_OF_RECORDS = MOBILE_BATCH_SIZE * 3 + 5;
|
||||
|
@ -824,8 +820,20 @@ function test_processIncoming_failed_records() {
|
|||
let meta_global = Records.set(engine.metaURL, new WBORecord(engine.metaURL));
|
||||
meta_global.payload.engines = {steam: {version: engine.version,
|
||||
syncID: engine.syncID}};
|
||||
|
||||
// Keep track of requests made of a collection.
|
||||
let count = 0;
|
||||
let uris = [];
|
||||
function recording_handler(collection) {
|
||||
let h = collection.handler();
|
||||
return function(req, res) {
|
||||
++count;
|
||||
uris.push(req.path + "?" + req.queryString);
|
||||
return h(req, res);
|
||||
};
|
||||
}
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/foo/storage/steam": collection.handler()
|
||||
"/1.1/foo/storage/steam": recording_handler(collection)
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
@ -863,6 +871,33 @@ function test_processIncoming_failed_records() {
|
|||
// Ensure the observer was notified
|
||||
do_check_eq(observerData, engine.name);
|
||||
do_check_eq(observerSubject.failed, BOGUS_RECORDS.length);
|
||||
|
||||
// Testing batching of failed item fetches.
|
||||
// Try to sync again. Ensure that we split the request into chunks to avoid
|
||||
// URI length limitations.
|
||||
function batchDownload(batchSize) {
|
||||
count = 0;
|
||||
uris = [];
|
||||
engine.guidFetchBatchSize = batchSize;
|
||||
engine._processIncoming();
|
||||
_("Tried again. Requests: " + count + "; URIs: " + JSON.stringify(uris));
|
||||
return count;
|
||||
}
|
||||
|
||||
// There are 8 bad records, so this needs 3 fetches.
|
||||
_("Test batching with ID batch size 3, normal mobile batch size.");
|
||||
do_check_eq(batchDownload(3), 3);
|
||||
|
||||
// Now see with a more realistic limit.
|
||||
_("Test batching with sufficient ID batch size.");
|
||||
do_check_eq(batchDownload(BOGUS_RECORDS.length), 1);
|
||||
|
||||
// If we're on mobile, that limit is used by default.
|
||||
_("Test batching with tiny mobile batch size.");
|
||||
Svc.Prefs.set("client.type", "mobile");
|
||||
engine.mobileGUIDFetchBatchSize = 2;
|
||||
do_check_eq(batchDownload(BOGUS_RECORDS.length), 4);
|
||||
|
||||
} finally {
|
||||
server.stop(do_test_finished);
|
||||
Svc.Prefs.resetBranch("");
|
||||
|
@ -910,7 +945,7 @@ function test_processIncoming_decrypt_failed() {
|
|||
meta_global.payload.engines = {steam: {version: engine.version,
|
||||
syncID: engine.syncID}};
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/foo/storage/steam": collection.handler()
|
||||
"/1.1/foo/storage/steam": collection.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
@ -961,9 +996,9 @@ function test_uploadOutgoing_toEmptyServer() {
|
|||
collection.wbos.scotsman = new ServerWBO('scotsman');
|
||||
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/foo/storage/steam": collection.handler(),
|
||||
"/1.0/foo/storage/steam/flying": collection.wbos.flying.handler(),
|
||||
"/1.0/foo/storage/steam/scotsman": collection.wbos.scotsman.handler()
|
||||
"/1.1/foo/storage/steam": collection.handler(),
|
||||
"/1.1/foo/storage/steam/flying": collection.wbos.flying.handler(),
|
||||
"/1.1/foo/storage/steam/scotsman": collection.wbos.scotsman.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
CollectionKeys.generateNewKeys();
|
||||
|
@ -1023,7 +1058,7 @@ function test_uploadOutgoing_failed() {
|
|||
collection.wbos.flying = new ServerWBO('flying');
|
||||
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/foo/storage/steam": collection.handler()
|
||||
"/1.1/foo/storage/steam": collection.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
@ -1107,7 +1142,7 @@ function test_uploadOutgoing_MAX_UPLOAD_RECORDS() {
|
|||
syncID: engine.syncID}};
|
||||
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/foo/storage/steam": collection.handler()
|
||||
"/1.1/foo/storage/steam": collection.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
@ -1165,7 +1200,7 @@ function test_syncFinish_deleteByIds() {
|
|||
denomination: "Rekonstruktionslokomotive"}));
|
||||
|
||||
let server = httpd_setup({
|
||||
"/1.0/foo/storage/steam": collection.handler()
|
||||
"/1.1/foo/storage/steam": collection.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
@ -1219,7 +1254,7 @@ function test_syncFinish_deleteLotsInBatches() {
|
|||
}
|
||||
|
||||
let server = httpd_setup({
|
||||
"/1.0/foo/storage/steam": collection.handler()
|
||||
"/1.1/foo/storage/steam": collection.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
@ -1274,7 +1309,7 @@ function test_sync_partialUpload() {
|
|||
|
||||
let collection = new ServerCollection();
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/foo/storage/steam": collection.handler()
|
||||
"/1.1/foo/storage/steam": collection.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
CollectionKeys.generateNewKeys();
|
||||
|
@ -1356,7 +1391,7 @@ function test_canDecrypt_noCryptoKeys() {
|
|||
denomination: "LNER Class A3 4472"}));
|
||||
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/foo/storage/steam": collection.handler()
|
||||
"/1.1/foo/storage/steam": collection.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
@ -1387,7 +1422,7 @@ function test_canDecrypt_true() {
|
|||
denomination: "LNER Class A3 4472"}));
|
||||
|
||||
let server = sync_httpd_setup({
|
||||
"/1.0/foo/storage/steam": collection.handler()
|
||||
"/1.1/foo/storage/steam": collection.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
_("Test file-related utility functions");
|
||||
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://services-sync/constants.js");
|
||||
|
||||
function run_test() {
|
||||
// Disabled due to Windows failures (bug 599193)
|
||||
//_test_getTmp();
|
||||
//_test_open();
|
||||
}
|
||||
|
||||
function _test_getTmp() {
|
||||
// as the setup phase remove the tmp directory from the
|
||||
// filesystem
|
||||
let svc = Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Ci.nsIProperties);
|
||||
let tmp = svc.get("ProfD", Ci.nsIFile);
|
||||
tmp.QueryInterface(Ci.nsILocalFile);
|
||||
tmp.append("weave");
|
||||
tmp.append("tmp");
|
||||
if (tmp.exists())
|
||||
tmp.remove(true);
|
||||
|
||||
// call getTmp with no argument. A ref to the tmp
|
||||
// dir is returned
|
||||
_("getTmp with no argument");
|
||||
let tmpDir = Utils.getTmp();
|
||||
do_check_true(tmpDir instanceof Ci.nsILocalFile);
|
||||
do_check_true(tmpDir.isDirectory());
|
||||
do_check_true(tmpDir.exists());
|
||||
do_check_eq(tmpDir.leafName, "tmp");
|
||||
|
||||
// call getTmp with a string. A ref to the file
|
||||
// named with this string and included in the
|
||||
// tmp dir is returned
|
||||
_("getTmp with a string");
|
||||
let tmpFile = Utils.getTmp("name");
|
||||
do_check_true(tmpFile instanceof Ci.nsILocalFile);
|
||||
do_check_true(!tmpFile.exists()); // getTmp doesn't create the file!
|
||||
do_check_eq(tmpFile.leafName, "name");
|
||||
do_check_true(tmpDir.contains(tmpFile, false));
|
||||
}
|
||||
|
||||
function _test_open() {
|
||||
|
||||
// we rely on Utils.getTmp to get a temporary file and
|
||||
// test Utils.open on that file, that's ok Util.getTmp
|
||||
// is also tested (test_getTmp)
|
||||
function createFile() {
|
||||
let f = Utils.getTmp("_test_");
|
||||
f.create(f.NORMAL_FILE_TYPE, PERMS_FILE);
|
||||
return f;
|
||||
}
|
||||
|
||||
// we should probably test more things here, for example
|
||||
// we should test that we cannot write to a stream that
|
||||
// is created as a result of opening a file for reading
|
||||
|
||||
let s, f;
|
||||
|
||||
_("Open for reading, providing a file");
|
||||
let f1 = createFile();
|
||||
[s, f] = Utils.open(f1, "<");
|
||||
do_check_eq(f.path, f1.path);
|
||||
do_check_true(s instanceof Ci.nsIConverterInputStream);
|
||||
f1.remove(false);
|
||||
|
||||
_("Open for reading, providing a file name");
|
||||
let f2 = createFile();
|
||||
let path2 = f2.path;
|
||||
[s, f] = Utils.open(path2, "<");
|
||||
do_check_eq(f.path, path2);
|
||||
do_check_true(s instanceof Ci.nsIConverterInputStream);
|
||||
f2.remove(false);
|
||||
|
||||
_("Open for writing with truncate mode, providing a file");
|
||||
let f3 = createFile();
|
||||
[s, f] = Utils.open(f3, ">");
|
||||
do_check_eq(f.path, f3.path);
|
||||
do_check_true(s instanceof Ci.nsIConverterOutputStream);
|
||||
f3.remove(false);
|
||||
|
||||
_("Open for writing with truncate mode, providing a file name");
|
||||
let f4 = createFile();
|
||||
let path4 = f4.path;
|
||||
[s, f] = Utils.open(path4, ">");
|
||||
do_check_eq(f.path, path4);
|
||||
do_check_true(s instanceof Ci.nsIConverterOutputStream);
|
||||
f4.remove(false);
|
||||
|
||||
_("Open for writing with append mode, providing a file");
|
||||
let f5 = createFile();
|
||||
[s, f] = Utils.open(f5, ">>");
|
||||
do_check_eq(f.path, f5.path);
|
||||
do_check_true(s instanceof Ci.nsIConverterOutputStream);
|
||||
f5.remove(false);
|
||||
|
||||
_("Open for writing with append mode, providing a file name");
|
||||
let f6 = createFile();
|
||||
let path6 = f6.path;
|
||||
[s, f] = Utils.open(path6, ">>");
|
||||
do_check_eq(f.path, path6);
|
||||
do_check_true(s instanceof Ci.nsIConverterOutputStream);
|
||||
f6.remove(false);
|
||||
|
||||
_("Open with illegal mode");
|
||||
let f7 = createFile();
|
||||
let except7;
|
||||
try {
|
||||
Utils.open(f7, "?!");
|
||||
} catch(e) {
|
||||
except7 = e;
|
||||
}
|
||||
do_check_true(!!except7);
|
||||
f7.remove(false);
|
||||
|
||||
_("Open non-existing file for reading");
|
||||
let f8 = createFile();
|
||||
let path8 = f8.path;
|
||||
f8.remove(false);
|
||||
let except8;
|
||||
try {
|
||||
Utils.open(path8, "<");
|
||||
} catch(e) {
|
||||
except8 = e;
|
||||
}
|
||||
do_check_true(!!except8);
|
||||
|
||||
_("Open for reading, provide permissions");
|
||||
let f9 = createFile();
|
||||
[s, f] = Utils.open(f9, "<", 0644);
|
||||
do_check_eq(f.path, f9.path);
|
||||
do_check_true(s instanceof Ci.nsIConverterInputStream);
|
||||
f9.remove(false);
|
||||
}
|
|
@ -6,7 +6,7 @@ function run_test() {
|
|||
// we just test whether the returned string includes the
|
||||
// string "unknown", should be good enough
|
||||
|
||||
str = Utils.getErrorString("error.login.reason.password");
|
||||
str = Utils.getErrorString("error.login.reason.account");
|
||||
do_check_true(str.match(/unknown/i) == null);
|
||||
|
||||
str = Utils.getErrorString("foobar");
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
_("Make sure json saves and loads from disk");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://services-sync/constants.js");
|
||||
|
||||
function run_test() {
|
||||
do_test_pending();
|
||||
|
@ -68,9 +69,15 @@ function run_test() {
|
|||
// Write a file with some invalid JSON
|
||||
let file = Utils.getProfileFile({ autoCreate: true,
|
||||
path: "weave/log.json" });
|
||||
let [fos] = Utils.open(file, ">");
|
||||
fos.writeString("invalid json!");
|
||||
fos.close();
|
||||
let fos = Cc["@mozilla.org/network/file-output-stream;1"]
|
||||
.createInstance(Ci.nsIFileOutputStream);
|
||||
fos.init(file, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, PERMS_FILE,
|
||||
fos.DEFER_OPEN);
|
||||
let stream = Cc["@mozilla.org/intl/converter-output-stream;1"]
|
||||
.createInstance(Ci.nsIConverterOutputStream);
|
||||
stream.init(fos, "UTF-8", 4096, 0x0000);
|
||||
stream.writeString("invalid json!");
|
||||
stream.close();
|
||||
|
||||
let trace, debug;
|
||||
Utils.jsonLoad("log",
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.7
|
||||
1.8.0
|
||||
|
|
Загрузка…
Ссылка в новой задаче