Merge cedar into mozilla-central

This commit is contained in:
Ehsan Akhgari 2011-04-10 15:11:22 -04:00
Родитель d69ef514f0 c6e163d025
Коммит 5376138030
70 изменённых файлов: 1110 добавлений и 1352 удалений

Просмотреть файл

@ -43,13 +43,15 @@
#include "nsCoreUtils.h" #include "nsCoreUtils.h"
#include "nsDocAccessible.h" #include "nsDocAccessible.h"
#include "nsIFontMetrics.h" #include "nsIThebesFontMetrics.h"
#include "nsIFrame.h" #include "nsIFrame.h"
#include "nsPresContext.h" #include "nsPresContext.h"
#include "nsIPresShell.h" #include "nsIPresShell.h"
#include "nsIRenderingContext.h" #include "nsIRenderingContext.h"
#include "nsIComponentManager.h" #include "nsIComponentManager.h"
#include "gfxFont.h"
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// nsTextAccessibleWrap Accessible // nsTextAccessibleWrap Accessible
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -253,48 +255,30 @@ STDMETHODIMP nsTextAccessibleWrap::get_fontFamily(
__try { __try {
*aFontFamily = NULL; *aFontFamily = NULL;
nsIFrame *frame = GetFrame(); nsIFrame* frame = GetFrame();
nsCOMPtr<nsIPresShell> presShell = GetPresShell(); if (!frame) {
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) {
return E_FAIL; return E_FAIL;
} }
nsCOMPtr<nsIFontMetrics> fm; nsCOMPtr<nsIFontMetrics> fm;
rc->GetFontMetrics(*getter_AddRefs(fm)); frame->PresContext()->DeviceContext()->
if (!fm) { GetMetricsFor(frame->GetStyleFont()->mFont,
return E_FAIL; frame->GetStyleVisibility()->mLanguage,
} frame->PresContext()->GetUserFontSet(),
*getter_AddRefs(fm));
nsAutoString fontFamily; nsCOMPtr<nsIThebesFontMetrics> tfm = do_QueryInterface(fm);
deviceContext->FirstExistingFont(fm->Font(), fontFamily); const nsString& name = tfm->GetThebesFontGroup()->GetFontAt(0)->GetName();
if (fontFamily.IsEmpty())
if (name.IsEmpty())
return S_FALSE; return S_FALSE;
*aFontFamily = ::SysAllocStringLen(fontFamily.get(), fontFamily.Length()); *aFontFamily = ::SysAllocStringLen(name.get(), name.Length());
if (!*aFontFamily) if (!*aFontFamily)
return E_OUTOFMEMORY; return E_OUTOFMEMORY;
} __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { } } __except(FilterA11yExceptions(::GetExceptionCode(),
GetExceptionInformation())) { }
return S_OK; return S_OK;
} }

Просмотреть файл

@ -163,6 +163,7 @@ CSPPolicyURIListener.prototype = {
else { else {
// problem fetching policy so fail closed // problem fetching policy so fail closed
this._csp.refinePolicy("allow 'none'", this._docURI, this._docRequest); this._csp.refinePolicy("allow 'none'", this._docURI, this._docRequest);
this._csp.refinePolicy("default-src 'none'", this._docURI, this._docRequest);
} }
// resume the parent document request // resume the parent document request
this._docRequest.resume(); this._docRequest.resume();
@ -187,7 +188,7 @@ function CSPRep() {
} }
CSPRep.SRC_DIRECTIVES = { CSPRep.SRC_DIRECTIVES = {
ALLOW: "allow", DEFAULT_SRC: "default-src",
SCRIPT_SRC: "script-src", SCRIPT_SRC: "script-src",
STYLE_SRC: "style-src", STYLE_SRC: "style-src",
MEDIA_SRC: "media-src", MEDIA_SRC: "media-src",
@ -205,6 +206,7 @@ CSPRep.URI_DIRECTIVES = {
}; };
CSPRep.OPTIONS_DIRECTIVE = "options"; CSPRep.OPTIONS_DIRECTIVE = "options";
CSPRep.ALLOW_DIRECTIVE = "allow";
/** /**
* Factory to create a new CSPRep, parsed from a string. * Factory to create a new CSPRep, parsed from a string.
@ -258,6 +260,17 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
continue directive; 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 //////////////////////////////////////////////// // SOURCE DIRECTIVES ////////////////////////////////////////////////
for each(var sdi in SD) { for each(var sdi in SD) {
if (dirname === sdi) { if (dirname === sdi) {
@ -338,13 +351,13 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
// POLICY_URI can only be alone // POLICY_URI can only be alone
if (aCSPR._directives.length > 0 || dirs.length > 1) { if (aCSPR._directives.length > 0 || dirs.length > 1) {
CSPError("policy-uri directive can only appear alone"); 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 // 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 // we won't be able to suspend it while we fetch the policy -> fail closed
if (!docRequest || !csp) { if (!docRequest || !csp) {
CSPError("The policy-uri cannot be fetched without a parent request and a 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 = ''; var uri = '';
@ -352,22 +365,22 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
uri = gIoService.newURI(dirvalue, null, selfUri); uri = gIoService.newURI(dirvalue, null, selfUri);
} catch(e) { } catch(e) {
CSPError("could not parse URI in policy URI: " + dirvalue); 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 // Verify that policy URI comes from the same origin
if (selfUri) { if (selfUri) {
if (selfUri.host !== uri.host){ if (selfUri.host !== uri.host){
CSPError("can't fetch policy uri from non-matching hostname: " + 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){ if (selfUri.port !== uri.port){
CSPError("can't fetch policy uri from non-matching 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){ if (selfUri.scheme !== uri.scheme){
CSPError("can't fetch policy uri from non-matching 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 // resume the document request and apply most restrictive policy
docRequest.resume(); docRequest.resume();
CSPError("Error fetching policy-uri: " + e); 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 // return a fully-open policy to be intersected with the contents of the
// policy-uri when it returns // policy-uri when it returns
return CSPRep.fromString("allow *"); return CSPRep.fromString("default-src *");
} }
// UNIDENTIFIED DIRECTIVE ///////////////////////////////////////////// // UNIDENTIFIED DIRECTIVE /////////////////////////////////////////////
@ -397,11 +410,11 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
} // end directive: loop } // end directive: loop
// if makeExplicit fails for any reason, default to allow 'none'. This // if makeExplicit fails for any reason, default to default-src 'none'. This
// includes the case where "allow" is not present. // includes the case where "default-src" is not present.
if (aCSPR.makeExplicit()) if (aCSPR.makeExplicit())
return aCSPR; return aCSPR;
return CSPRep.fromString("allow 'none'", self); return CSPRep.fromString("default-src 'none'", self);
}; };
CSPRep.prototype = { CSPRep.prototype = {
@ -534,22 +547,22 @@ CSPRep.prototype = {
makeExplicit: makeExplicit:
function cspsd_makeExplicit() { function cspsd_makeExplicit() {
var SD = CSPRep.SRC_DIRECTIVES; var SD = CSPRep.SRC_DIRECTIVES;
var allowDir = this._directives[SD.ALLOW]; var defaultSrcDir = this._directives[SD.DEFAULT_SRC];
if (!allowDir) { if (!defaultSrcDir) {
CSPWarning("'allow' directive required but not present. Reverting to \"allow 'none'\""); CSPWarning("'allow' or 'default-src' directive required but not present. Reverting to \"default-src 'none'\"");
return false; return false;
} }
for (var dir in SD) { for (var dir in SD) {
var dirv = SD[dir]; var dirv = SD[dir];
if (dirv === SD.ALLOW) continue; if (dirv === SD.DEFAULT_SRC) continue;
if (!this._directives[dirv]) { if (!this._directives[dirv]) {
// implicit directive, make explicit. // implicit directive, make explicit.
// All but frame-ancestors directive inherit from 'allow' (bug 555068) // All but frame-ancestors directive inherit from 'allow' (bug 555068)
if (dirv === SD.FRAME_ANCESTORS) if (dirv === SD.FRAME_ANCESTORS)
this._directives[dirv] = CSPSourceList.fromString("*"); this._directives[dirv] = CSPSourceList.fromString("*");
else else
this._directives[dirv] = allowDir.clone(); this._directives[dirv] = defaultSrcDir.clone();
this._directives[dirv]._isImplicit = true; this._directives[dirv]._isImplicit = true;
} }
} }

Просмотреть файл

@ -62,7 +62,7 @@ function ContentSecurityPolicy() {
CSPdebug("CSP CREATED"); CSPdebug("CSP CREATED");
this._isInitialized = false; this._isInitialized = false;
this._reportOnlyMode = 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 // default options "wide open" since this policy will be intersected soon
this._policy._allowInlineScripts = true; this._policy._allowInlineScripts = true;
@ -71,7 +71,7 @@ function ContentSecurityPolicy() {
this._requestHeaders = []; this._requestHeaders = [];
this._request = ""; this._request = "";
this._docRequest = null; this._docRequest = null;
CSPdebug("CSP POLICY INITED TO 'allow *'"); CSPdebug("CSP POLICY INITED TO 'default-src *'");
} }
/* /*
@ -85,7 +85,7 @@ function ContentSecurityPolicy() {
csp._MAPPINGS=[]; csp._MAPPINGS=[];
/* default, catch-all case */ /* default, catch-all case */
csp._MAPPINGS[cp.TYPE_OTHER] = cspr_sd.ALLOW; csp._MAPPINGS[cp.TYPE_OTHER] = cspr_sd.DEFAULT_SRC;
/* self */ /* self */
csp._MAPPINGS[cp.TYPE_DOCUMENT] = null; csp._MAPPINGS[cp.TYPE_DOCUMENT] = null;
@ -106,9 +106,9 @@ function ContentSecurityPolicy() {
/* These must go through the catch-all */ /* These must go through the catch-all */
csp._MAPPINGS[cp.TYPE_XBL] = cspr_sd.ALLOW; csp._MAPPINGS[cp.TYPE_XBL] = cspr_sd.DEFAULT_SRC;
csp._MAPPINGS[cp.TYPE_PING] = cspr_sd.ALLOW; csp._MAPPINGS[cp.TYPE_PING] = cspr_sd.DEFAULT_SRC;
csp._MAPPINGS[cp.TYPE_DTD] = cspr_sd.ALLOW; csp._MAPPINGS[cp.TYPE_DTD] = cspr_sd.DEFAULT_SRC;
} }
ContentSecurityPolicy.prototype = { ContentSecurityPolicy.prototype = {
@ -386,7 +386,7 @@ ContentSecurityPolicy.prototype = {
// report the frame-ancestor violation // report the frame-ancestor violation
let directive = this._policy._directives[cspContext]; let directive = this._policy._directives[cspContext];
let violatedPolicy = (directive._isImplicit let violatedPolicy = (directive._isImplicit
? 'allow' : 'frame-ancestors ') ? 'default-src' : 'frame-ancestors ')
+ directive.toString(); + directive.toString();
this._asyncReportViolation(ancestors[i], violatedPolicy); this._asyncReportViolation(ancestors[i], violatedPolicy);
@ -441,7 +441,7 @@ ContentSecurityPolicy.prototype = {
try { try {
let directive = this._policy._directives[cspContext]; let directive = this._policy._directives[cspContext];
let violatedPolicy = (directive._isImplicit let violatedPolicy = (directive._isImplicit
? 'allow' : cspContext) ? 'default-src' : cspContext)
+ ' ' + directive.toString(); + ' ' + directive.toString();
this._asyncReportViolation(aContentLocation, violatedPolicy); this._asyncReportViolation(aContentLocation, violatedPolicy);
} catch(e) { } 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", "http://example.org/tests/content/base/test/file_CSP.sjs?testid=img_bad&type=img/png",
"Incorrect blocked uri"); "Incorrect blocked uri");
// correct violated-directive // 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"); "Incorrect violated directive");
// not practical to test request-headers as header names and values will // not practical to test request-headers as header names and values will
// change with the trunk // change with the trunk

Просмотреть файл

@ -73,11 +73,11 @@ function do_check_has_key(foo, key, stack) {
stack = Components.stack.caller; stack = Components.stack.caller;
var keys = []; var keys = [];
for(let k in keys) { keys.push(k); } for (let k in foo) { keys.push(k); }
var text = key + " in [" + keys.join(",") + "]"; var text = key + " in [" + keys.join(",") + "]";
for(var x in foo) { for (var x in foo) {
if(x == key) { if (x == key) {
//succeed //succeed
++_passedChecks; ++_passedChecks;
dump("TEST-PASS | " + stack.filename + " | [" + stack.name + " : " + dump("TEST-PASS | " + stack.filename + " | [" + stack.name + " : " +
@ -359,17 +359,17 @@ test(
var cspr; var cspr;
var cspr_allowval; var cspr_allowval;
var SD = CSPRep.SRC_DIRECTIVES;
// check default policy "allow *" // check default policy "allow *"
cspr = CSPRep.fromString("allow *", "http://self.com:80"); cspr = CSPRep.fromString("allow *", "http://self.com:80");
//"ALLOW directive is missing when specified in fromString" // "DEFAULT_SRC directive is missing when specified in fromString"
do_check_has_key(cspr._directives, CSPRep.SRC_DIRECTIVES.ALLOW); do_check_has_key(cspr._directives, SD.DEFAULT_SRC);
// ... and check that the other directives were auto-filled with the // ... and check that the other directives were auto-filled with the
// ALLOW one. // DEFAULT_SRC one.
var SD = CSPRep.SRC_DIRECTIVES; cspr_allowval = cspr._directives[SD.DEFAULT_SRC];
cspr_allowval = cspr._directives[SD.ALLOW]; for(var d in SD) {
for(var d in CSPRep.SRC_DIRECTIVES) {
//"Missing key " + d //"Missing key " + d
do_check_has_key(cspr._directives, SD[d]); do_check_has_key(cspr._directives, SD[d]);
//"Implicit directive " + d + " has non-allow value." //"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( test(
function test_CSPRep_fromString_oneDir() { function test_CSPRep_fromString_oneDir() {

Просмотреть файл

@ -1055,6 +1055,36 @@ HttpChannelChild::SetupFallbackChannel(const char *aFallbackKey)
DROP_DEAD(); 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 // HttpChannelChild::nsICacheInfoChannel
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

Просмотреть файл

@ -109,6 +109,10 @@ public:
PRBool aMerge); PRBool aMerge);
// nsIHttpChannelInternal // nsIHttpChannelInternal
NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey); 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 // nsISupportsPriority
NS_IMETHOD SetPriority(PRInt32 value); NS_IMETHOD SetPriority(PRInt32 value);
// nsIResumableChannel // nsIResumableChannel

Просмотреть файл

@ -45,6 +45,7 @@ class nsAHttpSegmentReader;
class nsAHttpSegmentWriter; class nsAHttpSegmentWriter;
class nsIInterfaceRequestor; class nsIInterfaceRequestor;
class nsIEventTarget; class nsIEventTarget;
class nsITransport;
class nsHttpRequestHead; class nsHttpRequestHead;
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -62,13 +63,14 @@ public:
// called by the connection when it takes ownership of the transaction. // called by the connection when it takes ownership of the transaction.
virtual void SetConnection(nsAHttpConnection *) = 0; 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. // socket transport.
virtual void GetSecurityCallbacks(nsIInterfaceRequestor **, virtual void GetSecurityCallbacks(nsIInterfaceRequestor **,
nsIEventTarget **) = 0; nsIEventTarget **) = 0;
// called to report socket status (see nsITransportEventSink) // 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. // called to check the transaction status.
virtual PRBool IsDone() = 0; virtual PRBool IsDone() = 0;
@ -99,7 +101,8 @@ public:
void SetConnection(nsAHttpConnection *); \ void SetConnection(nsAHttpConnection *); \
void GetSecurityCallbacks(nsIInterfaceRequestor **, \ void GetSecurityCallbacks(nsIInterfaceRequestor **, \
nsIEventTarget **); \ nsIEventTarget **); \
void OnTransportStatus(nsresult status, PRUint64 progress); \ void OnTransportStatus(nsITransport* transport, \
nsresult status, PRUint64 progress); \
PRBool IsDone(); \ PRBool IsDone(); \
nsresult Status(); \ nsresult Status(); \
PRUint32 Available(); \ PRUint32 Available(); \

Просмотреть файл

@ -134,13 +134,16 @@ nsHttpChannel::nsHttpChannel()
, mRequestTimeInitialized(PR_FALSE) , mRequestTimeInitialized(PR_FALSE)
{ {
LOG(("Creating nsHttpChannel [this=%p]\n", this)); 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() nsHttpChannel::~nsHttpChannel()
{ {
LOG(("Destroying nsHttpChannel [this=%p]\n", this)); LOG(("Destroying nsHttpChannel [this=%p]\n", this));
if (mAuthProvider) if (mAuthProvider)
mAuthProvider->Disconnect(NS_ERROR_ABORT); mAuthProvider->Disconnect(NS_ERROR_ABORT);
} }
@ -150,7 +153,7 @@ nsHttpChannel::Init(nsIURI *uri,
nsProxyInfo *proxyInfo) nsProxyInfo *proxyInfo)
{ {
nsresult rv = HttpBaseChannel::Init(uri, caps, proxyInfo); nsresult rv = HttpBaseChannel::Init(uri, caps, proxyInfo);
if (NS_FAILED(rv)) if (NS_FAILED(rv))
return rv; return rv;
LOG(("nsHttpChannel::Init [this=%p]\n", this)); LOG(("nsHttpChannel::Init [this=%p]\n", this));
@ -158,7 +161,7 @@ nsHttpChannel::Init(nsIURI *uri,
mAuthProvider = mAuthProvider =
do_CreateInstance("@mozilla.org/network/http-channel-auth-provider;1", do_CreateInstance("@mozilla.org/network/http-channel-auth-provider;1",
&rv); &rv);
if (NS_FAILED(rv)) if (NS_FAILED(rv))
return rv; return rv;
rv = mAuthProvider->Init(this); rv = mAuthProvider->Init(this);
@ -3711,6 +3714,66 @@ nsHttpChannel::SetupFallbackChannel(const char *aFallbackKey)
return NS_OK; 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 // nsHttpChannel::nsISupportsPriority
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -4153,6 +4216,16 @@ nsHttpChannel::OnTransportStatus(nsITransport *trans, nsresult status,
if (!mProgressSink) if (!mProgressSink)
GetCallback(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. // block socket status event after Cancel or OnStopRequest has been called.
if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending && !(mLoadFlags & LOAD_BACKGROUND)) { if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending && !(mLoadFlags & LOAD_BACKGROUND)) {
LOG(("sending status notification [this=%p status=%x progress=%llu/%llu]\n", LOG(("sending status notification [this=%p status=%x progress=%llu/%llu]\n",

Просмотреть файл

@ -137,6 +137,10 @@ public:
NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext); NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext);
// nsIHttpChannelInternal // nsIHttpChannelInternal
NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey); 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 // nsISupportsPriority
NS_IMETHOD SetPriority(PRInt32 value); NS_IMETHOD SetPriority(PRInt32 value);
// nsIResumableChannel // nsIResumableChannel
@ -347,6 +351,9 @@ private:
// the cache entry's expiration time. Otherwise, it is not(see bug 567360). // the cache entry's expiration time. Otherwise, it is not(see bug 567360).
PRUint32 mRequestTimeInitialized : 1; PRUint32 mRequestTimeInitialized : 1;
PRNetAddr mSelfAddr;
PRNetAddr mPeerAddr;
nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack; nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack;
nsCOMPtr<nsICryptoHash> mHasher; nsCOMPtr<nsICryptoHash> mHasher;

Просмотреть файл

@ -615,7 +615,8 @@ nsHttpConnection::OnSocketWritable()
// here to reflect the fact that we are waiting. this message will be // here to reflect the fact that we are waiting. this message will be
// trumped (overwritten) if the server responds quickly. // trumped (overwritten) if the server responds quickly.
// //
mTransaction->OnTransportStatus(nsISocketTransport::STATUS_WAITING_FOR, mTransaction->OnTransportStatus(mSocketTransport,
nsISocketTransport::STATUS_WAITING_FOR,
LL_ZERO); LL_ZERO);
rv = mSocketIn->AsyncWait(this, 0, 0, nsnull); // start reading rv = mSocketIn->AsyncWait(this, 0, 0, nsnull); // start reading
@ -811,7 +812,7 @@ nsHttpConnection::OnTransportStatus(nsITransport *trans,
PRUint64 progressMax) PRUint64 progressMax)
{ {
if (mTransaction) if (mTransaction)
mTransaction->OnTransportStatus(status, progress); mTransaction->OnTransportStatus(trans, status, progress);
return NS_OK; return NS_OK;
} }

Просмотреть файл

@ -1491,7 +1491,7 @@ nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans,
PRUint64 progressMax) PRUint64 progressMax)
{ {
if (mTransaction) if (mTransaction)
mTransaction->OnTransportStatus(status, progress); mTransaction->OnTransportStatus(trans, status, progress);
return NS_OK; return NS_OK;
} }

Просмотреть файл

@ -367,7 +367,8 @@ nsHttpPipeline::GetSecurityCallbacks(nsIInterfaceRequestor **result,
} }
void 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", LOG(("nsHttpPipeline::OnStatus [this=%x status=%x progress=%llu]\n",
this, status, progress)); this, status, progress));
@ -377,10 +378,10 @@ nsHttpPipeline::OnTransportStatus(nsresult status, PRUint64 progress)
nsAHttpTransaction *trans; nsAHttpTransaction *trans;
switch (status) { switch (status) {
case NS_NET_STATUS_RECEIVING_FROM: 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); trans = Response(0);
if (trans) if (trans)
trans->OnTransportStatus(status, progress); trans->OnTransportStatus(transport, status, progress);
break; break;
default: default:
// forward other notifications to all transactions // forward other notifications to all transactions
@ -388,7 +389,7 @@ nsHttpPipeline::OnTransportStatus(nsresult status, PRUint64 progress)
for (i=0; i<count; ++i) { for (i=0; i<count; ++i) {
trans = Request(i); trans = Request(i);
if (trans) if (trans)
trans->OnTransportStatus(status, progress); trans->OnTransportStatus(transport, status, progress);
} }
break; break;
} }

Просмотреть файл

@ -351,14 +351,15 @@ nsHttpTransaction::GetSecurityCallbacks(nsIInterfaceRequestor **cb,
} }
void 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", LOG(("nsHttpTransaction::OnSocketStatus [this=%x status=%x progress=%llu]\n",
this, status, progress)); this, status, progress));
if (!mTransportSink) if (!mTransportSink)
return; return;
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread"); NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
// Need to do this before the STATUS_RECEIVING_FROM check below, to make // 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; progressMax = 0;
} }
mTransportSink->OnTransportStatus(nsnull, status, progress, progressMax); mTransportSink->OnTransportStatus(transport, status, progress, progressMax);
} }
PRBool PRBool

Просмотреть файл

@ -46,12 +46,12 @@ class nsCString;
interface nsIURI; interface nsIURI;
interface nsIProxyInfo; interface nsIProxyInfo;
/** /**
* Dumping ground for http. This interface will never be frozen. If you are * 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 * using any feature exposed by this interface, be aware that this interface
* will change and you will be broken. You have been warned. * 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 interface nsIHttpChannelInternal : nsISupports
{ {
/** /**
@ -91,17 +91,58 @@ interface nsIHttpChannelInternal : nsISupports
attribute boolean forceAllowThirdPartyCookie; attribute boolean forceAllowThirdPartyCookie;
/** /**
* Returns true iff the channel has been canceled. * True iff the channel has been canceled.
*/ */
readonly attribute boolean 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; 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); [noscript] void setCacheKeysRedirectChain(in StringArray cacheKeys);
}; };

Просмотреть файл

@ -20,6 +20,13 @@ TracingListener.prototype = {
gotOnStartRequest = true; 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. // Make sure listener can't be replaced after OnStartRequest was called.
request.QueryInterface(Components.interfaces.nsITraceableChannel); request.QueryInterface(Components.interfaces.nsITraceableChannel);
try { try {

Просмотреть файл

@ -50,9 +50,16 @@ registrar.registerFactory(Components.ID("{fbfae60b-64a4-44ef-a911-08ceb70b9f31}"
} }
// Provide resource://services-crypto if it isn't already available // Register resource alias. Normally done in SyncComponents.manifest.
let weaveService = Cc["@mozilla.org/weave/service;1"].getService(); function addResourceAlias() {
weaveService.wrappedJSObject.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, * 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} contract @mozilla.org/network/protocol/about;1?what=sync-log {d28f8a0b-95da-48f4-b712-caf37097be41}
component {a08ee179-df50-48e0-9c87-79e4dd5caeb1} Weave.js component {a08ee179-df50-48e0-9c87-79e4dd5caeb1} Weave.js
contract @mozilla.org/network/protocol/about;1?what=sync-log.1 {a08ee179-df50-48e0-9c87-79e4dd5caeb1} 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; break;
case "final-ui-startup": case "final-ui-startup":
this.addResourceAlias();
// Force Weave service to load if it hasn't triggered from overlays // Force Weave service to load if it hasn't triggered from overlays
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this.timer.initWithCallback({ this.timer.initWithCallback({
@ -71,25 +69,6 @@ WeaveService.prototype = {
}, 10000, Ci.nsITimer.TYPE_ONE_SHOT); }, 10000, Ci.nsITimer.TYPE_ONE_SHOT);
break; 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.network = Failed to connect to the server
error.login.reason.synckey = Wrong Sync Key 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.account = Incorrect account name or password
error.login.reason.no_password= No saved password to use error.login.reason.no_password= No saved password to use
error.login.reason.no_synckey = No saved Sync Key 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_VERSION: "@weave_version@",
WEAVE_ID: "@weave_id@", 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 // 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 // how records are packaged; this is separate from the Server API version and
// the per-engine cleartext formats. // 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. // How long to wait between sync attempts if the Master Password is locked.
MASTER_PASSWORD_LOCKED_RETRY_INTERVAL: 15 * 60 * 1000, // 15 minutes MASTER_PASSWORD_LOCKED_RETRY_INTERVAL: 15 * 60 * 1000, // 15 minutes
// 50 is hardcoded here because of URL length restrictions. // Separate from the ID fetch batch size to allow tuning for mobile.
// (GUIDs can be up to 64 chars long)
MOBILE_BATCH_SIZE: 50, 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 batch size for applying incoming records.
DEFAULT_STORE_BATCH_SIZE: 1, DEFAULT_STORE_BATCH_SIZE: 1,
HISTORY_STORE_BATCH_SIZE: 50, // same as MOBILE_BATCH_SIZE HISTORY_STORE_BATCH_SIZE: 50, // same as MOBILE_BATCH_SIZE

Просмотреть файл

@ -416,14 +416,33 @@ function SyncEngine(name) {
Engine.call(this, name || "SyncEngine"); Engine.call(this, name || "SyncEngine");
this.loadToFetch(); 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 = { SyncEngine.prototype = {
__proto__: Engine.prototype, __proto__: Engine.prototype,
_recordObj: CryptoWrapper, _recordObj: CryptoWrapper,
version: 1, 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, 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, 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/", "/" + ID.get("WeaveID").username + "/storage/",
get engineURL() this.storageURL + this.name, 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. // Figure out how many total items to fetch this sync; do less on mobile.
let batchSize = Infinity; let batchSize = Infinity;
let newitems = new Collection(this.engineURL, this._recordObj); 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; batchSize = MOBILE_BATCH_SIZE;
} }
newitems.newer = this.lastSync; newitems.newer = this.lastSync;
@ -640,14 +661,37 @@ SyncEngine.prototype = {
try { try {
try { try {
item.decrypt(); item.decrypt();
} catch (ex if (Utils.isHMACMismatch(ex) && } catch (ex if Utils.isHMACMismatch(ex)) {
self.handleHMACMismatch(item))) { let strategy = self.handleHMACMismatch(item, true);
// Let's try handling it. if (strategy == SyncEngine.kRecoveryStrategy.retry) {
// If the callback returns true, try decrypting again, because // You only get one retry.
// we've got new keys. try {
self._log.info("Trying decrypt again..."); // Try decrypting again, typically because we've got new keys.
item.decrypt(); 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) { } catch (ex) {
self._log.warn("Error decrypting record: " + Utils.exceptionStr(ex)); self._log.warn("Error decrypting record: " + Utils.exceptionStr(ex));
failed.push(item.id); failed.push(item.id);
@ -718,7 +762,12 @@ SyncEngine.prototype = {
this.lastSync = this.lastModified; 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) { while (fetchBatch.length) {
// Reuse the original query, but get rid of the restricting params // Reuse the original query, but get rid of the restricting params
// and batch remaining records. // and batch remaining records.
@ -1007,8 +1056,32 @@ SyncEngine.prototype = {
new Resource(this.engineURL).delete(); new Resource(this.engineURL).delete();
this._resetClient(); this._resetClient();
}, },
handleHMACMismatch: function handleHMACMismatch(item) { removeClientData: function removeClientData() {
return Weave.Service.handleHMACEvent(); // 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 MOBILE_ANNO = "MobileBookmarks";
const EXCLUDEBACKUP_ANNO = "places/excludeFromBackup"; const EXCLUDEBACKUP_ANNO = "places/excludeFromBackup";
const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark"; const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
const GUID_ANNO = "sync/guid";
const PARENT_ANNO = "sync/parent"; const PARENT_ANNO = "sync/parent";
const ANNOS_TO_TRACK = [DESCRIPTION_ANNO, SIDEBAR_ANNO, STATICTITLE_ANNO, 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 SERVICE_NOT_SUPPORTED = "Service not supported on this platform";
const FOLDER_SORTINDEX = 1000000; const FOLDER_SORTINDEX = 1000000;
try { Cu.import("resource://gre/modules/PlacesUtils.jsm");
Cu.import("resource://gre/modules/PlacesUtils.jsm");
}
catch(ex) {
Cu.import("resource://gre/modules/utils.js");
}
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/engines.js");
Cu.import("resource://services-sync/record.js"); Cu.import("resource://services-sync/record.js");
@ -993,31 +987,12 @@ BookmarksStore.prototype = {
} }
}, },
__childGUIDsStm: null,
get _childGUIDsStm() { get _childGUIDsStm() {
if (this.__childGUIDsStm) { return this._getStmt(
return this.__childGUIDsStm; "SELECT id AS item_id, guid " +
} "FROM moz_bookmarks " +
"WHERE parent = :parent " +
let stmt; "ORDER BY position");
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;
}, },
_childGUIDsCols: ["item_id", "guid"], _childGUIDsCols: ["item_id", "guid"],
@ -1145,24 +1120,8 @@ BookmarksStore.prototype = {
return this._stmts[query]; return this._stmts[query];
this._log.trace("Creating SQL statement: " + query); this._log.trace("Creating SQL statement: " + query);
return this._stmts[query] = Utils.createStatement(this._hsvc.DBConnection, return this._stmts[query] = this._hsvc.DBConnection
query); .createAsyncStatement(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;
}
}, },
get _frecencyStm() { get _frecencyStm() {
@ -1174,54 +1133,11 @@ BookmarksStore.prototype = {
}, },
_frecencyCols: ["frecency"], _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() { get _setGUIDStm() {
if (this.__setGUIDStm !== null) { return this._getStmt(
return this.__setGUIDStm; "UPDATE moz_bookmarks " +
} "SET guid = :guid " +
"WHERE id = :item_id");
// 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;
}, },
// Some helper functions to handle GUIDs // Some helper functions to handle GUIDs
@ -1229,73 +1145,18 @@ BookmarksStore.prototype = {
if (!guid) if (!guid)
guid = Utils.makeGUID(); guid = Utils.makeGUID();
// If we can, set the GUID on moz_bookmarks and do not do any other work. let stmt = this._setGUIDStm;
let (stmt = this._setGUIDStm) { stmt.params.guid = guid;
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;
stmt.params.item_id = id; 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); Utils.queryAsync(stmt);
return guid; return guid;
}, },
__guidForIdStm: null,
get _guidForIdStm() { get _guidForIdStm() {
if (this.__guidForIdStm) { return this._getStmt(
return this.__guidForIdStm; "SELECT guid " +
} "FROM moz_bookmarks " +
"WHERE id = :item_id");
// 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;
}, },
_guidForIdCols: ["guid"], _guidForIdCols: ["guid"],
@ -1316,37 +1177,11 @@ BookmarksStore.prototype = {
return this._setGUID(id); return this._setGUID(id);
}, },
__idForGUIDStm: null,
get _idForGUIDStm() { get _idForGUIDStm() {
if (this.__idForGUIDStm) { return this._getStmt(
return this.__idForGUIDStm; "SELECT id AS item_id " +
} "FROM moz_bookmarks " +
"WHERE guid = :guid");
// 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;
}, },
_idForGUIDCols: ["item_id"], _idForGUIDCols: ["item_id"],
@ -1370,22 +1205,6 @@ BookmarksStore.prototype = {
if (!result) if (!result)
return -1; 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; return result.item_id;
}, },
@ -1589,34 +1408,44 @@ BookmarksTracker.prototype = {
* Folder of the item being changed * Folder of the item being changed
*/ */
_ignore: function BMT__ignore(itemId, folder) { _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) if (this.ignoreAll)
return true; return true;
// Ensure that the mobile bookmarks query is correct in the UI // Get the folder id if we weren't given one.
this._ensureMobileQuery(); 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)) { if (Svc.Annos.itemHasAnnotation(itemId, EXCLUDEBACKUP_ANNO)) {
this.removeChangedID(this._GUIDForId(itemId)); this.removeChangedID(this._GUIDForId(itemId));
return true; return true;
} }
// Get the folder id if we weren't given one return false;
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);
}, },
onItemAdded: function BMT_onEndUpdateBatch(itemId, folder, index) { onItemAdded: function BMT_onEndUpdateBatch(itemId, folder, index) {
@ -1655,7 +1484,7 @@ BookmarksTracker.prototype = {
let queryURI = Utils.makeURI("place:folder=" + kSpecialIds.mobile); let queryURI = Utils.makeURI("place:folder=" + kSpecialIds.mobile);
let title = Str.sync.get("mobile.label"); 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 (Svc.Bookmark.getIdForItemAt(kSpecialIds.mobile, 0) == -1) {
if (mobile.length != 0) if (mobile.length != 0)
Svc.Bookmark.removeItem(mobile[0]); Svc.Bookmark.removeItem(mobile[0]);
@ -1675,28 +1504,26 @@ BookmarksTracker.prototype = {
this.ignoreAll = false; 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) { onItemChanged: function BMT_onItemChanged(itemId, property, isAnno, value) {
if (this._ignore(itemId)) // Quicker checks first.
if (this.ignoreAll)
return; return;
// Allocate a new GUID if necessary. if (isAnno && (ANNOS_TO_TRACK.indexOf(property) == -1))
// We only want to do it if there's a dupe, so use idForGUID to achieve that. // Ignore annotations except for the ones that we sync.
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)
return; return;
// Ignore favicon changes to avoid unnecessary churn // Ignore favicon changes to avoid unnecessary churn.
if (property == "favicon") if (property == "favicon")
return; return;
if (this._ignore(itemId))
return;
this._log.trace("onItemChanged: " + itemId + this._log.trace("onItemChanged: " + itemId +
(", " + property + (isAnno? " (anno)" : "")) + (", " + property + (isAnno? " (anno)" : "")) +
(value ? (" = \"" + value + "\"") : "")); (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/engines.js");
Cu.import("resource://services-sync/ext/StringBundle.js"); Cu.import("resource://services-sync/ext/StringBundle.js");
Cu.import("resource://services-sync/record.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/util.js");
const CLIENTS_TTL = 1814400; // 21 days const CLIENTS_TTL = 1814400; // 21 days
@ -193,19 +194,26 @@ ClientEngine.prototype = {
SyncEngine.prototype._resetClient.call(this); SyncEngine.prototype._resetClient.call(this);
this._store.wipe(); 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. // 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); 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. // 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._log.debug("Bad client record detected. Scheduling for deletion.");
this._deleteId(item.id); this._deleteId(item.id);
// Don't try again. // Neither try again nor error; we're going to delete it.
return false; return SyncEngine.kRecoveryStrategy.ignore;
} }
}; };

Просмотреть файл

@ -66,7 +66,7 @@ let FormWrapper = {
getAllEntries: function getAllEntries() { getAllEntries: function getAllEntries() {
// Sort by (lastUsed - minLast) / (maxLast - minLast) * timesUsed / maxTimes // 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 " + "SELECT fieldname name, value FROM moz_formhistory " +
"ORDER BY 1.0 * (lastUsed - (SELECT lastUsed FROM moz_formhistory ORDER BY lastUsed ASC LIMIT 1)) / " + "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)) * " + "((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) { getEntry: function getEntry(guid) {
let query = this.createStatement( let query = Svc.Form.DBConnection.createAsyncStatement(
"SELECT fieldname name, value FROM moz_formhistory WHERE guid = :guid"); "SELECT fieldname name, value FROM moz_formhistory WHERE guid = :guid");
query.params.guid = guid; query.params.guid = guid;
return Utils.queryAsync(query, ["name", "value"])[0]; return Utils.queryAsync(query, ["name", "value"])[0];
@ -84,7 +84,7 @@ let FormWrapper = {
getGUID: function getGUID(name, value) { getGUID: function getGUID(name, value) {
// Query for the provided entry // Query for the provided entry
let getQuery = this.createStatement( let getQuery = Svc.Form.DBConnection.createAsyncStatement(
"SELECT guid FROM moz_formhistory " + "SELECT guid FROM moz_formhistory " +
"WHERE fieldname = :name AND value = :value"); "WHERE fieldname = :name AND value = :value");
getQuery.params.name = name; getQuery.params.name = name;
@ -106,7 +106,7 @@ let FormWrapper = {
return item.guid; return item.guid;
// We need to create a guid for this entry // 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 " + "UPDATE moz_formhistory SET guid = :guid " +
"WHERE fieldname = :name AND value = :value"); "WHERE fieldname = :name AND value = :value");
let guid = Utils.makeGUID(); let guid = Utils.makeGUID();
@ -119,37 +119,20 @@ let FormWrapper = {
}, },
hasGUID: function hasGUID(guid) { hasGUID: function hasGUID(guid) {
let query = this.createStatement( let query = Svc.Form.DBConnection.createAsyncStatement(
"SELECT guid FROM moz_formhistory WHERE guid = :guid LIMIT 1"); "SELECT guid FROM moz_formhistory WHERE guid = :guid LIMIT 1");
query.params.guid = guid; query.params.guid = guid;
return Utils.queryAsync(query, ["guid"]).length == 1; return Utils.queryAsync(query, ["guid"]).length == 1;
}, },
replaceGUID: function replaceGUID(oldGUID, newGUID) { replaceGUID: function replaceGUID(oldGUID, newGUID) {
let query = this.createStatement( let query = Svc.Form.DBConnection.createAsyncStatement(
"UPDATE moz_formhistory SET guid = :newGUID WHERE guid = :oldGUID"); "UPDATE moz_formhistory SET guid = :newGUID WHERE guid = :oldGUID");
query.params.oldGUID = oldGUID; query.params.oldGUID = oldGUID;
query.params.newGUID = newGUID; query.params.newGUID = newGUID;
Utils.queryAsync(query); 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() { function FormEngine() {
@ -231,7 +214,7 @@ FormStore.prototype = {
}, },
update: function FormStore_update(record) { 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() { wipe: function FormStore_wipe() {
@ -283,7 +266,6 @@ FormTracker.prototype = {
this._enabled = false; this._enabled = false;
} }
break; break;
// Firefox 4.0
case "satchel-storage-changed": case "satchel-storage-changed":
if (data == "addEntry" || data == "before-removeEntry") { if (data == "addEntry" || data == "before-removeEntry") {
subject = subject.QueryInterface(Ci.nsIArray); subject = subject.QueryInterface(Ci.nsIArray);
@ -294,32 +276,9 @@ FormTracker.prototype = {
this.trackEntry(name, value); this.trackEntry(name, value);
} }
break; 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) { notify: function FormTracker_notify(formElement, aWindow, actionURI) {
if (this.ignoreAll) if (this.ignoreAll)
return; return;

Просмотреть файл

@ -44,7 +44,6 @@ const Ci = Components.interfaces;
const Cu = Components.utils; const Cu = Components.utils;
const Cr = Components.results; const Cr = Components.results;
const GUID_ANNO = "sync/guid";
const HISTORY_TTL = 5184000; // 60 days const HISTORY_TTL = 5184000; // 60 days
const TOPIC_UPDATEPLACES_COMPLETE = "places-updatePlaces-complete"; const TOPIC_UPDATEPLACES_COMPLETE = "places-updatePlaces-complete";
@ -78,9 +77,6 @@ HistoryEngine.prototype = {
downloadLimit: MAX_HISTORY_DOWNLOAD, downloadLimit: MAX_HISTORY_DOWNLOAD,
applyIncomingBatchSize: HISTORY_STORE_BATCH_SIZE, applyIncomingBatchSize: HISTORY_STORE_BATCH_SIZE,
// For Gecko <2.0
_sync: Utils.batchSync("History", SyncEngine),
_findDupe: function _findDupe(item) { _findDupe: function _findDupe(item) {
return this._store.GUIDForUri(item.histUri); return this._store.GUIDForUri(item.histUri);
} }
@ -113,110 +109,28 @@ HistoryStore.prototype = {
__asyncHistory: null, __asyncHistory: null,
get _asyncHistory() { get _asyncHistory() {
if (!this.__asyncHistory && "mozIAsyncHistory" in Components.interfaces) { if (!this.__asyncHistory) {
this.__asyncHistory = Cc["@mozilla.org/browser/history;1"] this.__asyncHistory = Cc["@mozilla.org/browser/history;1"]
.getService(Ci.mozIAsyncHistory); .getService(Ci.mozIAsyncHistory);
} }
return this.__asyncHistory; return this.__asyncHistory;
}, },
get _db() {
return this._hsvc.DBConnection;
},
_stmts: {}, _stmts: {},
_getStmt: function(query) { _getStmt: function(query) {
if (query in this._stmts) if (query in this._stmts)
return this._stmts[query]; return this._stmts[query];
this._log.trace("Creating SQL statement: " + 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() { get _setGUIDStm() {
if (this.__setGUIDStm !== null) { return this._getStmt(
return this.__setGUIDStm; "UPDATE moz_places " +
} "SET guid = :guid " +
"WHERE url = :page_url");
// 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;
}, },
// Some helper functions to handle GUIDs // Some helper functions to handle GUIDs
@ -226,77 +140,18 @@ HistoryStore.prototype = {
if (!guid) if (!guid)
guid = Utils.makeGUID(); guid = Utils.makeGUID();
// If we can, set the GUID on moz_places and do not do any other work. let stmt = this._setGUIDStm;
let (stmt = this._setGUIDStm) { stmt.params.guid = guid;
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;
stmt.params.page_url = uri; 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); Utils.queryAsync(stmt);
return guid; return guid;
}, },
__guidStm: null,
get _guidStm() { get _guidStm() {
if (this.__guidStm) { return this._getStmt(
return this.__guidStm; "SELECT guid " +
} "FROM moz_places " +
"WHERE url = :page_url");
// 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;
}, },
_guidCols: ["guid"], _guidCols: ["guid"],
@ -315,21 +170,6 @@ HistoryStore.prototype = {
}, },
get _visitStm() { 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( return this._getStmt(
"SELECT visit_type type, visit_date date " + "SELECT visit_type type, visit_date date " +
"FROM moz_historyvisits " + "FROM moz_historyvisits " +
@ -338,53 +178,15 @@ HistoryStore.prototype = {
}, },
_visitCols: ["date", "type"], _visitCols: ["date", "type"],
__urlStmt: null,
get _urlStm() { get _urlStm() {
if (this.__urlStmt) { return this._getStmt(
return this.__urlStmt; "SELECT url, title, frecency " +
} "FROM moz_places " +
"WHERE guid = :guid");
// 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;
}, },
_urlCols: ["url", "title", "frecency"], _urlCols: ["url", "title", "frecency"],
get _allUrlStm() { 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( return this._getStmt(
"SELECT url " + "SELECT url " +
"FROM moz_places " + "FROM moz_places " +
@ -425,12 +227,6 @@ HistoryStore.prototype = {
}, },
applyIncomingBatch: function applyIncomingBatch(records) { applyIncomingBatch: function applyIncomingBatch(records) {
// Gecko <2.0
if (!this._asyncHistory) {
return Store.prototype.applyIncomingBatch.call(this, records);
}
// Gecko 2.0
let failed = []; let failed = [];
// Convert incoming records to mozIPlaceInfo objects. Some records can be // Convert incoming records to mozIPlaceInfo objects. Some records can be
@ -529,9 +325,8 @@ HistoryStore.prototype = {
+ visit.date); + visit.date);
throw "Visit has no 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 && 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: " this._log.warn("Encountered record with invalid visit type: "
+ visit.type); + visit.type);
throw "Invalid visit type!"; throw "Invalid visit type!";
@ -563,12 +358,6 @@ HistoryStore.prototype = {
return true; 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) { remove: function HistStore_remove(record) {
let page = this._findURLByGUID(record.id); let page = this._findURLByGUID(record.id);
if (page == null) { if (page == null) {
@ -581,29 +370,6 @@ HistoryStore.prototype = {
this._log.trace("Removed page: " + [record.id, page.url, page.title]); 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) { itemExists: function HistStore_itemExists(id) {
if (this._findURLByGUID(id)) if (this._findURLByGUID(id))
return true; return true;

Просмотреть файл

@ -114,13 +114,8 @@ function PasswordStore(name) {
"@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo, "init"); "@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo, "init");
Utils.lazy2(this, "DBConnection", function() { Utils.lazy2(this, "DBConnection", function() {
try { return Svc.Login.QueryInterface(Ci.nsIInterfaceRequestor)
return Svc.Login.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.mozIStorageConnection);
.getInterface(Ci.mozIStorageConnection);
} catch (ex if (ex.result == Cr.NS_ERROR_NO_INTERFACE)) {
// Gecko <2.0 *sadface*
return null;
}
}); });
} }
PasswordStore.prototype = { 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/record.js");
Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-sync/ext/Preferences.js"); Cu.import("resource://services-sync/ext/Preferences.js");
Cu.import("resource://gre/modules/LightweightThemeManager.jsm");
const PREFS_GUID = Utils.encodeBase64url(Svc.AppInfo.ID); const PREFS_GUID = Utils.encodeBase64url(Svc.AppInfo.ID);
@ -138,20 +139,9 @@ PrefStore.prototype = {
}, },
_setAllPrefs: function PrefStore__setAllPrefs(values) { _setAllPrefs: function PrefStore__setAllPrefs(values) {
// cache
let ltmExists = true;
let ltm = {};
let enabledBefore = false;
let enabledPref = "lightweightThemes.isThemeSelected"; let enabledPref = "lightweightThemes.isThemeSelected";
let prevTheme = ""; let enabledBefore = this._prefs.get(enabledPref, false);
try { let prevTheme = LightweightThemeManager.currentTheme;
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+
for (let [pref, value] in Iterator(values)) { for (let [pref, value] in Iterator(values)) {
if (!this._isSynced(pref)) if (!this._isSynced(pref))
@ -171,14 +161,12 @@ PrefStore.prototype = {
} }
// Notify the lightweight theme manager of all the new values // Notify the lightweight theme manager of all the new values
if (ltmExists) { let enabledNow = this._prefs.get(enabledPref, false);
let enabledNow = this._prefs.get(enabledPref, false); if (enabledBefore && !enabledNow) {
if (enabledBefore && !enabledNow) LightweightThemeManager.currentTheme = null;
ltm.currentTheme = null; } else if (enabledNow && LightweightThemeManager.usedThemes[0] != prevTheme) {
else if (enabledNow && ltm.usedThemes[0] != prevTheme) { LightweightThemeManager.currentTheme = null;
ltm.currentTheme = null; LightweightThemeManager.currentTheme = ltm.usedThemes[0];
ltm.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.js");
Cu.import("resource://services-sync/engines/clients.js"); Cu.import("resource://services-sync/engines/clients.js");
Cu.import("resource://services-sync/record.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/util.js");
Cu.import("resource://services-sync/ext/Preferences.js"); Cu.import("resource://services-sync/ext/Preferences.js");
@ -106,6 +107,10 @@ TabEngine.prototype = {
this._tracker.modified = true; 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 /* 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 * open locally. There are a couple ways to interpret this: for
* instance, we could do it by removing a tab from the list when * instance, we could do it by removing a tab from the list when

Просмотреть файл

@ -21,6 +21,7 @@
* Dan Mills <thunder@mozilla.com> * Dan Mills <thunder@mozilla.com>
* Anant Narayanan <anant@kix.in> * Anant Narayanan <anant@kix.in>
* Philipp von Weitershausen <philipp@weitershausen.de> * Philipp von Weitershausen <philipp@weitershausen.de>
* Richard Newman <rnewman@mozilla.com>
* *
* Alternatively, the contents of this file may be used under the terms of * 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 * either the GNU General Public License Version 2 or later (the "GPL"), or
@ -70,7 +71,7 @@ function BrokenBasicAuthenticator(identity) {
} }
BrokenBasicAuthenticator.prototype = { BrokenBasicAuthenticator.prototype = {
onRequest: function BasicAuth_onRequest(headers) { onRequest: function BasicAuth_onRequest(headers) {
headers['Authorization'] = 'Basic ' + headers['authorization'] = 'Basic ' +
btoa(this._id.username + ':' + this._id.password); btoa(this._id.username + ':' + this._id.password);
return headers; return headers;
} }
@ -81,7 +82,7 @@ function BasicAuthenticator(identity) {
} }
BasicAuthenticator.prototype = { BasicAuthenticator.prototype = {
onRequest: function onRequest(headers) { onRequest: function onRequest(headers) {
headers['Authorization'] = 'Basic ' + headers['authorization'] = 'Basic ' +
btoa(this._id.username + ':' + this._id.passwordUTF8); btoa(this._id.username + ':' + this._id.passwordUTF8);
return headers; return headers;
} }
@ -127,7 +128,7 @@ AuthMgr.prototype = {
* function callback(error, result) {...} * function callback(error, result) {...}
* *
* 'error' will be null on successful requests. Likewise, result will not be * '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. * the status of the HTTP response.
*/ */
function AsyncResource(uri) { function AsyncResource(uri) {
@ -141,7 +142,21 @@ function AsyncResource(uri) {
AsyncResource.prototype = { AsyncResource.prototype = {
_logName: "Net.Resource", _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, ABORT_TIMEOUT: 300000,
// ** {{{ Resource.authenticator }}} ** // ** {{{ Resource.authenticator }}} **
@ -171,12 +186,8 @@ AsyncResource.prototype = {
set headers(value) { set headers(value) {
this._headers = value; this._headers = value;
}, },
setHeader: function Res_setHeader() { setHeader: function Res_setHeader(header, value) {
if (arguments.length % 2) this._headers[header.toLowerCase()] = value;
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];
}
}, },
// ** {{{ Resource.uri }}} ** // ** {{{ Resource.uri }}} **
@ -227,10 +238,16 @@ AsyncResource.prototype = {
// Setup a callback to handle bad HTTPS certificates. // Setup a callback to handle bad HTTPS certificates.
channel.notificationCallbacks = new BadCertListener(); 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; let headers = this.headers;
for (let key in headers) { for (let key in headers) {
if (key == 'Authorization') if (key == 'authorization')
this._log.trace("HTTP Header " + key + ": ***** (suppressed)"); this._log.trace("HTTP Header " + key + ": ***** (suppressed)");
else else
this._log.trace("HTTP Header " + key + ": " + headers[key]); this._log.trace("HTTP Header " + key + ": " + headers[key]);
@ -242,14 +259,14 @@ AsyncResource.prototype = {
_onProgress: function Res__onProgress(channel) {}, _onProgress: function Res__onProgress(channel) {},
_doRequest: function _doRequest(action, data, callback) { _doRequest: function _doRequest(action, data, callback) {
this._log.trace("In _doRequest.");
this._callback = callback; this._callback = callback;
let channel = this._channel = this._createRequest(); let channel = this._channel = this._createRequest();
if ("undefined" != typeof(data)) if ("undefined" != typeof(data))
this._data = data; this._data = data;
// PUT and POST are trreated differently because // PUT and POST are treated differently because they have payload data.
// they have payload data.
if ("PUT" == action || "POST" == action) { if ("PUT" == action || "POST" == action) {
// Convert non-string bodies into JSON // Convert non-string bodies into JSON
if (this._data.constructor.toString() != String) if (this._data.constructor.toString() != String)
@ -278,6 +295,8 @@ AsyncResource.prototype = {
}, },
_onComplete: function _onComplete(error, data) { _onComplete: function _onComplete(error, data) {
this._log.trace("In _onComplete. Error is " + error + ".");
if (error) { if (error) {
this._callback(error); this._callback(error);
return; return;
@ -287,29 +306,54 @@ AsyncResource.prototype = {
let channel = this._channel; let channel = this._channel;
let action = channel.requestMethod; let action = channel.requestMethod;
// Set some default values in-case there's no response header this._log.trace("Channel: " + channel);
let headers = {}; 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 status = 0;
let success = false; let success = false;
try { 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({ channel.visitResponseHeaders({
visitHeader: function visitHeader(header, value) { visitHeader: function visitHeader(header, value) {
headers[header.toLowerCase()] = 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 // This is a server-side safety valve to allow slowing down
// clients without hurting performance. // clients without hurting performance.
@ -320,23 +364,26 @@ AsyncResource.prototype = {
if (success && headers["x-weave-quota-remaining"]) if (success && headers["x-weave-quota-remaining"])
Observers.notify("weave:service:quota:remaining", Observers.notify("weave:service:quota:remaining",
parseInt(headers["x-weave-quota-remaining"], 10)); parseInt(headers["x-weave-quota-remaining"], 10));
} } catch (ex) {
// Got a response but no header; must be cached (use default values) this._log.debug("Caught exception " + Utils.exceptionStr(ex) +
catch(ex) { " visiting headers in _onComplete.");
this._log.debug(action + " cached: " + status); this._log.debug(Utils.stackTrace(ex));
} }
let ret = new String(data); let ret = new String(data);
ret.headers = headers; ret.status = status;
ret.status = status;
ret.success = success; 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)); 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) { 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 = { let subject = {
newUri: "", newUri: "",
resource: this, resource: this,
@ -344,7 +391,7 @@ AsyncResource.prototype = {
} }
Observers.notify("weave:resource:status:401", subject); 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 != "") { if (subject.newUri != "") {
this.uri = subject.newUri; this.uri = subject.newUri;
this._doRequest(action, this._data, this._callback); this._doRequest(action, this._data, this._callback);
@ -481,36 +528,46 @@ function ChannelListener(onComplete, onProgress, logger, timeout) {
ChannelListener.prototype = { ChannelListener.prototype = {
onStartRequest: function Channel_onStartRequest(channel) { onStartRequest: function Channel_onStartRequest(channel) {
this._log.trace("onStartRequest called for channel " + channel + ".");
channel.QueryInterface(Ci.nsIHttpChannel); channel.QueryInterface(Ci.nsIHttpChannel);
// Save the latest server timestamp when possible // Save the latest server timestamp when possible.
try { try {
Resource.serverTime = channel.getResponseHeader("X-Weave-Timestamp") - 0; Resource.serverTime = channel.getResponseHeader("X-Weave-Timestamp") - 0;
} }
catch(ex) {} catch(ex) {}
this._log.trace(channel.requestMethod + " " + channel.URI.spec); this._log.trace("onStartRequest: " + channel.requestMethod + " " +
channel.URI.spec);
this._data = ''; this._data = '';
this.delayAbort(); this.delayAbort();
}, },
onStopRequest: function Channel_onStopRequest(channel, context, status) { 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(); this.abortTimer.clear();
let success = Components.isSuccessCode(status);
this._log.trace("Channel for " + channel.requestMethod + " " +
channel.URI.spec + ": isSuccessCode(" + status + ")? " +
success);
if (this._data == '') if (this._data == '')
this._data = null; this._data = null;
// Throw the failure code and stop execution. Use Components.Exception() // Throw the failure code and stop execution. Use Components.Exception()
// instead of Error() so the exception is QI-able and can be passed across // instead of Error() so the exception is QI-able and can be passed across
// XPCOM borders while preserving the status code. // XPCOM borders while preserving the status code.
if (!Components.isSuccessCode(status)) { if (!success) {
let message = Components.Exception("", status).name; let message = Components.Exception("", status).name;
let error = Components.Exception(message, status); let error = Components.Exception(message, status);
this._onComplete(error); this._onComplete(error);
return; return;
} }
this._log.trace("Channel: flags = " + channel.loadFlags +
", URI = " + channel.URI.spec +
", HTTP success? " + channel.requestSucceeded);
this._onComplete(null, this._data); this._onComplete(null, this._data);
}, },

Просмотреть файл

@ -154,7 +154,7 @@ WeaveSvc.prototype = {
let misc = Svc.Prefs.get("miscURL"); let misc = Svc.Prefs.get("miscURL");
if (misc.indexOf(":") == -1) if (misc.indexOf(":") == -1)
misc = this.serverURL + misc; misc = this.serverURL + misc;
return misc + "1.0/"; return misc + MISC_API_VERSION + "/";
}, },
get userAPI() { get userAPI() {
@ -162,7 +162,7 @@ WeaveSvc.prototype = {
let user = Svc.Prefs.get("userURL"); let user = Svc.Prefs.get("userURL");
if (user.indexOf(":") == -1) if (user.indexOf(":") == -1)
user = this.serverURL + user; user = this.serverURL + user;
return user + "1.0/"; return user + USER_API_VERSION + "/";
}, },
get pwResetURL() { get pwResetURL() {
@ -237,7 +237,7 @@ WeaveSvc.prototype = {
if (this.clusterURL == "" || this.username == "") if (this.clusterURL == "" || this.username == "")
return; return;
let storageAPI = this.clusterURL + Svc.Prefs.get("storageAPI") + "/"; let storageAPI = this.clusterURL + SYNC_API_VERSION + "/";
this.userBaseURL = storageAPI + this.username + "/"; this.userBaseURL = storageAPI + this.username + "/";
this._log.debug("Caching URLs under storage user base: " + this.userBaseURL); this._log.debug("Caching URLs under storage user base: " + this.userBaseURL);
@ -1014,6 +1014,16 @@ WeaveSvc.prototype = {
}))(), }))(),
startOver: function() { 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..." // Set a username error so the status message shows "set up..."
Status.login = LOGIN_FAILED_NO_USERNAME; Status.login = LOGIN_FAILED_NO_USERNAME;
this.logout(); this.logout();
@ -1184,12 +1194,6 @@ WeaveSvc.prototype = {
checkAccount: function checkAccount(account) { checkAccount: function checkAccount(account) {
let username = this._usernameFromAccount(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 url = this.userAPI + username;
let res = new Resource(url); let res = new Resource(url);
res.authenticator = new NoOpAuthenticator(); res.authenticator = new NoOpAuthenticator();
@ -1211,18 +1215,9 @@ WeaveSvc.prototype = {
return this._errorStr(data); return this._errorStr(data);
}, },
createAccount: function createAccount() { createAccount: function createAccount(email, password,
// Backwards compat with the Firefox UI. Change to signature to captchaChallenge, captchaResponse) {
// (email, password, captchaChallenge, captchaResponse) once let username = this._usernameFromAccount(email);
// 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;
}
let payload = JSON.stringify({ let payload = JSON.stringify({
"password": Utils.encodeUTF8(password), "password": Utils.encodeUTF8(password),
"email": email, "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/Preferences.js");
Cu.import("resource://services-sync/ext/StringBundle.js"); Cu.import("resource://services-sync/ext/StringBundle.js");
Cu.import("resource://services-sync/log4moz.js"); Cu.import("resource://services-sync/log4moz.js");
Cu.import("resource://gre/modules/NetUtil.jsm");
let NetUtil;
try {
let ns = {};
Cu.import("resource://gre/modules/NetUtil.jsm", ns);
NetUtil = ns.NetUtil;
} catch (ex) {
// Firefox 3.5 :(
}
// Constants for makeSyncCallback, waitForSyncCallback // Constants for makeSyncCallback, waitForSyncCallback
const CB_READY = {}; 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. // Prototype for mozIStorageCallback, used in queryAsync below.
// This allows us to define the handle* functions just once rather // This allows us to define the handle* functions just once rather
// than on every queryAsync invocation. // than on every queryAsync invocation.
@ -1069,21 +1052,6 @@ let Utils = {
return; 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); let channel = NetUtil.newChannel(file);
channel.contentType = "application/json"; channel.contentType = "application/json";
@ -1127,21 +1095,10 @@ let Utils = {
let json = typeof obj == "function" ? obj.call(that) : obj; let json = typeof obj == "function" ? obj.call(that) : obj;
let out = JSON.stringify(json); 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"] let fos = Cc["@mozilla.org/network/safe-file-output-stream;1"]
.createInstance(Ci.nsIFileOutputStream); .createInstance(Ci.nsIFileOutputStream);
fos.init(file, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, PERMS_FILE, fos.init(file, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, PERMS_FILE,
fos.DEFER_OPEN || 0); fos.DEFER_OPEN);
let is = this._utf8Converter.convertToInputStream(out); let is = this._utf8Converter.convertToInputStream(out);
NetUtil.asyncCopy(is, fos, function (result) { NetUtil.asyncCopy(is, fos, function (result) {
if (typeof callback == "function") { if (typeof callback == "function") {
@ -1190,59 +1147,6 @@ let Utils = {
return thisObj[name] = timer; 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) { getIcon: function(iconUri, defaultIcon) {
try { try {
let iconURI = Utils.makeURI(iconUri); let iconURI = Utils.makeURI(iconUri);
@ -1263,16 +1167,6 @@ let Utils = {
return Str.errors.get("error.reason.unknown"); 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) { encodeUTF8: function(str) {
try { try {
str = this._utf8Converter.ConvertFromUnicode(str); str = this._utf8Converter.ConvertFromUnicode(str);
@ -1672,15 +1566,9 @@ this.__defineGetter__("_sessionCID", function() {
Svc.__defineGetter__("Crypto", function() { Svc.__defineGetter__("Crypto", function() {
let cryptoSvc; let cryptoSvc;
try { let ns = {};
let ns = {}; Cu.import("resource://services-crypto/WeaveCrypto.js", ns);
Cu.import("resource://services-crypto/WeaveCrypto.js", ns); cryptoSvc = new ns.WeaveCrypto();
cryptoSvc = new ns.WeaveCrypto();
} catch (ex) {
// Fallback to binary WeaveCrypto
cryptoSvc = Cc["@labs.mozilla.com/Weave/Crypto;1"].
getService(Ci.IWeaveCrypto);
}
delete Svc.Crypto; delete Svc.Crypto;
return Svc.Crypto = cryptoSvc; return Svc.Crypto = cryptoSvc;
}); });

Просмотреть файл

@ -1,5 +1,4 @@
pref("services.sync.serverURL", "https://auth.services.mozilla.com/"); pref("services.sync.serverURL", "https://auth.services.mozilla.com/");
pref("services.sync.storageAPI", "1.0");
pref("services.sync.userURL", "user/"); pref("services.sync.userURL", "user/");
pref("services.sync.miscURL", "misc/"); pref("services.sync.miscURL", "misc/");
pref("services.sync.termsURL", "https://services.mozilla.com/tos/"); 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.lastversion", "firstrun");
pref("services.sync.autoconnect", true); pref("services.sync.autoconnect", true);
pref("services.sync.sendVersionInfo", true);
pref("services.sync.engine.bookmarks", true); pref("services.sync.engine.bookmarks", true);
pref("services.sync.engine.history", 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 // Register resource aliases. Normally done in SyncComponents.manifest.
let weaveService = Cc["@mozilla.org/weave/service;1"].getService(); function addResourceAlias() {
weaveService.wrappedJSObject.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. // Some tests hang on OSX debug builds. See bug 604565.

Просмотреть файл

@ -267,7 +267,7 @@ ServerCollection.prototype = {
* Test setup helpers. * Test setup helpers.
*/ */
function sync_httpd_setup(handlers) { function sync_httpd_setup(handlers) {
handlers["/1.0/foo/storage/meta/global"] handlers["/1.1/foo/storage/meta/global"]
= (new ServerWBO('global', {})).handler(); = (new ServerWBO('global', {})).handler();
return httpd_setup(handlers); 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/util.js");
Cu.import("resource://services-sync/service.js"); Cu.import("resource://services-sync/service.js");
try { Cu.import("resource://gre/modules/PlacesUtils.jsm");
Cu.import("resource://gre/modules/PlacesUtils.jsm");
}
catch(ex) {
Cu.import("resource://gre/modules/utils.js");
}
Engines.register(BookmarksEngine); Engines.register(BookmarksEngine);
@ -71,8 +66,8 @@ function test_processIncoming_error_orderChildren() {
{engines: {bookmarks: {version: engine.version, {engines: {bookmarks: {version: engine.version,
syncID: engine.syncID}}}); syncID: engine.syncID}}});
let server = httpd_setup({ let server = httpd_setup({
"/1.0/foo/storage/meta/global": global.handler(), "/1.1/foo/storage/meta/global": global.handler(),
"/1.0/foo/storage/bookmarks": collection.handler() "/1.1/foo/storage/bookmarks": collection.handler()
}); });
try { try {
@ -153,8 +148,8 @@ function test_restorePromptsReupload() {
{engines: {bookmarks: {version: engine.version, {engines: {bookmarks: {version: engine.version,
syncID: engine.syncID}}}); syncID: engine.syncID}}});
let server = httpd_setup({ let server = httpd_setup({
"/1.0/foo/storage/meta/global": global.handler(), "/1.1/foo/storage/meta/global": global.handler(),
"/1.0/foo/storage/bookmarks": collection.handler() "/1.1/foo/storage/bookmarks": collection.handler()
}); });
Svc.Obs.notify("weave:engine:start-tracking"); // We skip usual startup... Svc.Obs.notify("weave:engine:start-tracking"); // We skip usual startup...
@ -328,8 +323,8 @@ function test_mismatched_types() {
syncID: engine.syncID}}}); syncID: engine.syncID}}});
_("GUID: " + store.GUIDForId(6, true)); _("GUID: " + store.GUIDForId(6, true));
let server = httpd_setup({ let server = httpd_setup({
"/1.0/foo/storage/meta/global": global.handler(), "/1.1/foo/storage/meta/global": global.handler(),
"/1.0/foo/storage/bookmarks": collection.handler() "/1.1/foo/storage/bookmarks": collection.handler()
}); });
try { 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/util.js");
Cu.import("resource://services-sync/service.js"); Cu.import("resource://services-sync/service.js");
try { Cu.import("resource://gre/modules/PlacesUtils.jsm");
Cu.import("resource://gre/modules/PlacesUtils.jsm");
}
catch(ex) {
Cu.import("resource://gre/modules/utils.js");
}
const DESCRIPTION_ANNO = "bookmarkProperties/description"; 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/util.js");
Cu.import("resource://services-sync/service.js"); Cu.import("resource://services-sync/service.js");
try { Cu.import("resource://gre/modules/PlacesUtils.jsm");
Cu.import("resource://gre/modules/PlacesUtils.jsm");
}
catch(ex) {
Cu.import("resource://gre/modules/utils.js");
}
const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark"; const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
var IOService = Cc["@mozilla.org/network/io-service;1"] var IOService = Cc["@mozilla.org/network/io-service;1"]
@ -115,8 +110,8 @@ function test_annotation_uploaded() {
{engines: {bookmarks: {version: engine.version, {engines: {bookmarks: {version: engine.version,
syncID: engine.syncID}}}); syncID: engine.syncID}}});
let server = httpd_setup({ let server = httpd_setup({
"/1.0/foo/storage/meta/global": global.handler(), "/1.1/foo/storage/meta/global": global.handler(),
"/1.0/foo/storage/bookmarks": collection.handler() "/1.1/foo/storage/bookmarks": collection.handler()
}); });
try { try {
@ -204,8 +199,8 @@ function test_smart_bookmarks_duped() {
{engines: {bookmarks: {version: engine.version, {engines: {bookmarks: {version: engine.version,
syncID: engine.syncID}}}); syncID: engine.syncID}}});
let server = httpd_setup({ let server = httpd_setup({
"/1.0/foo/storage/meta/global": global.handler(), "/1.1/foo/storage/meta/global": global.handler(),
"/1.0/foo/storage/bookmarks": collection.handler() "/1.1/foo/storage/bookmarks": collection.handler()
}); });
try { 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() { function run_test() {
initTestLogging('Trace'); initTestLogging('Trace');
test_bookmark_create(); test_bookmark_create();
@ -422,5 +364,4 @@ function run_test() {
test_move_order(); test_move_order();
test_orphan(); test_orphan();
test_reparentOrphans(); 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/engines.js");
Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-sync/util.js");
try { Cu.import("resource://gre/modules/PlacesUtils.jsm");
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!
Engines.register(BookmarksEngine); Engines.register(BookmarksEngine);
let engine = Engines.get("bookmarks"); let engine = Engines.get("bookmarks");
@ -248,55 +240,33 @@ function test_tracking() {
} }
} }
function test_guid_stripping() { function test_onItemChanged() {
// Gecko <2.0 // Anno that's in ANNOS_TO_TRACK.
if (store._haveGUIDColumn) { const GENERATOR_ANNO = "microsummary/generatorURI";
_("We have a GUID column; not testing anno GUID fixing.");
return;
}
_("Verify we've got an empty tracker to work with."); _("Verify we've got an empty tracker to work with.");
let tracker = engine._tracker; 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); do_check_eq([id for (id in tracker.changedIDs)].length, 0);
try { 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"); Svc.Obs.notify("weave:engine:start-tracking");
tracker.ignoreAll = false; Svc.Annos.setItemAnnotation(b, GENERATOR_ANNO, "http://foo.bar/", 0,
let suspect = createBmk(); Svc.Annos.EXPIRE_NEVER);
let victim = createBmk(); 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 { } finally {
_("Clean up."); _("Clean up.");
store.wipe(); store.wipe();
@ -312,8 +282,8 @@ function run_test() {
Log4Moz.repository.getLogger("Store.Bookmarks").level = Log4Moz.Level.Trace; Log4Moz.repository.getLogger("Store.Bookmarks").level = Log4Moz.Level.Trace;
Log4Moz.repository.getLogger("Tracker.Bookmarks").level = Log4Moz.Level.Trace; Log4Moz.repository.getLogger("Tracker.Bookmarks").level = Log4Moz.Level.Trace;
test_onItemChanged();
test_copying_places(); test_copying_places();
test_tracking(); 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/record.js");
Cu.import("resource://services-sync/identity.js"); Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/util.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/engines/clients.js");
Cu.import("resource://services-sync/service.js"); Cu.import("resource://services-sync/service.js");
@ -33,10 +34,10 @@ function test_bad_hmac() {
} }
let handlers = { let handlers = {
"/1.0/foo/info/collections": collectionsHelper.handler, "/1.1/foo/info/collections": collectionsHelper.handler,
"/1.0/foo/storage/meta/global": upd("meta", global.handler()), "/1.1/foo/storage/meta/global": upd("meta", global.handler()),
"/1.0/foo/storage/crypto/keys": upd("crypto", keysWBO.handler()), "/1.1/foo/storage/crypto/keys": upd("crypto", keysWBO.handler()),
"/1.0/foo/storage/clients": trackDeletedHandler("crypto", clientsColl.handler()) "/1.1/foo/storage/clients": trackDeletedHandler("crypto", clientsColl.handler())
}; };
let server = httpd_setup(handlers); let server = httpd_setup(handlers);
@ -76,6 +77,61 @@ function test_bad_hmac() {
do_check_eq(1, clientsColl.count()); do_check_eq(1, clientsColl.count());
_("Records now: " + clientsColl.get({})); _("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 { } finally {
server.stop(do_test_finished); server.stop(do_test_finished);
Svc.Prefs.resetBranch(""); Svc.Prefs.resetBranch("");
@ -101,7 +157,6 @@ function test_sync() {
_("Ensure that Clients engine uploads a new client record once a week."); _("Ensure that Clients engine uploads a new client record once a week.");
Svc.Prefs.set("clusterURL", "http://localhost:8080/"); Svc.Prefs.set("clusterURL", "http://localhost:8080/");
Svc.Prefs.set("username", "foo"); Svc.Prefs.set("username", "foo");
new SyncTestingInfrastructure();
CollectionKeys.generateNewKeys(); CollectionKeys.generateNewKeys();
@ -111,9 +166,12 @@ function test_sync() {
let coll = new ServerCollection(); let coll = new ServerCollection();
let clientwbo = coll.wbos[Clients.localID] = new ServerWBO(Clients.localID); let clientwbo = coll.wbos[Clients.localID] = new ServerWBO(Clients.localID);
let server = httpd_setup({ let server = httpd_setup({
"/1.0/foo/storage/meta/global": global.handler(), "/1.1/foo/storage/meta/global": global.handler(),
"/1.0/foo/storage/clients": coll.handler() "/1.1/foo/storage/clients": coll.handler()
}); });
server.registerPathHandler(
"/1.1/foo/storage/clients/" + Clients.localID, clientwbo.handler());
do_test_pending(); do_test_pending();
try { try {
@ -133,10 +191,13 @@ function test_sync() {
do_check_true(!!clientwbo.payload); do_check_true(!!clientwbo.payload);
do_check_true(Clients.lastRecordUpload > lastweek); 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."); _("Time travel one day back, no record uploaded.");
Clients.lastRecordUpload -= LESS_THAN_CLIENTS_TTL_REFRESH; Clients.lastRecordUpload -= LESS_THAN_CLIENTS_TTL_REFRESH;
let yesterday = Clients.lastRecordUpload; let yesterday = Clients.lastRecordUpload;
clientwbo.payload = undefined;
Clients.sync(); Clients.sync();
do_check_eq(clientwbo.payload, undefined); do_check_eq(clientwbo.payload, undefined);
do_check_eq(Clients.lastRecordUpload, yesterday); do_check_eq(Clients.lastRecordUpload, yesterday);
@ -152,7 +213,7 @@ function test_sync() {
function run_test() { function run_test() {
initTestLogging("Trace"); initTestLogging("Trace");
Log4Moz.repository.getLogger("Engine.Clients").level = Log4Moz.Level.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_properties();
test_sync(); test_sync();
} }

Просмотреть файл

@ -8,7 +8,7 @@ function run_test() {
_("Set up test fixtures."); _("Set up test fixtures.");
ID.set('WeaveID', new Identity('Some Identity', 'foo')); ID.set('WeaveID', new Identity('Some Identity', 'foo'));
Svc.Prefs.set("clusterURL", "http://fakebase/"); 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 pubUri = baseUri + "keys/pubkey";
let privUri = baseUri + "keys/privkey"; let privUri = baseUri + "keys/privkey";

Просмотреть файл

@ -36,22 +36,22 @@ function test_locally_changed_keys() {
do_test_pending(); do_test_pending();
let server = httpd_setup({ let server = httpd_setup({
// Special. // Special.
"/1.0/johndoe/storage/meta/global": upd("meta", meta_global.handler()), "/1.1/johndoe/storage/meta/global": upd("meta", meta_global.handler()),
"/1.0/johndoe/info/collections": collectionsHelper.handler, "/1.1/johndoe/info/collections": collectionsHelper.handler,
"/1.0/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()), "/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()),
// Track modified times. // Track modified times.
"/1.0/johndoe/storage/clients": upd("clients", clients.handler()), "/1.1/johndoe/storage/clients": upd("clients", clients.handler()),
"/1.0/johndoe/storage/clients/foobar": upd("clients", new ServerWBO("clients").handler()), "/1.1/johndoe/storage/clients/foobar": upd("clients", new ServerWBO("clients").handler()),
"/1.0/johndoe/storage/tabs": upd("tabs", new ServerCollection().handler()), "/1.1/johndoe/storage/tabs": upd("tabs", new ServerCollection().handler()),
// Just so we don't get 404s in the logs. // Just so we don't get 404s in the logs.
"/1.0/johndoe/storage/bookmarks": new ServerCollection().handler(), "/1.1/johndoe/storage/bookmarks": new ServerCollection().handler(),
"/1.0/johndoe/storage/forms": new ServerCollection().handler(), "/1.1/johndoe/storage/forms": new ServerCollection().handler(),
"/1.0/johndoe/storage/passwords": new ServerCollection().handler(), "/1.1/johndoe/storage/passwords": new ServerCollection().handler(),
"/1.0/johndoe/storage/prefs": 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 { try {
@ -144,7 +144,7 @@ function test_locally_changed_keys() {
wbo.modified = modified; wbo.modified = modified;
history.wbos[id] = wbo; history.wbos[id] = wbo;
server.registerPathHandler( server.registerPathHandler(
"/1.0/johndoe/storage/history/record-no--" + i, "/1.1/johndoe/storage/history/record-no--" + i,
upd("history", wbo.handler())); upd("history", wbo.handler()));
} }
@ -204,7 +204,7 @@ function test_locally_changed_keys() {
wbo.modified = modified; wbo.modified = modified;
history.wbos[id] = wbo; history.wbos[id] = wbo;
server.registerPathHandler( server.registerPathHandler(
"/1.0/johndoe/storage/history/record-no--" + i, "/1.1/johndoe/storage/history/record-no--" + i,
upd("history", wbo.handler())); upd("history", wbo.handler()));
} }
collections.history = Date.now()/1000; collections.history = Date.now()/1000;

Просмотреть файл

@ -51,7 +51,7 @@ function test_processIncoming_mobile_history_batched() {
} }
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/foo/storage/history": collection.handler() "/1.1/foo/storage/history": collection.handler()
}); });
do_test_pending(); do_test_pending();

Просмотреть файл

@ -160,11 +160,10 @@ function run_test() {
}, function (next) { }, function (next) {
_("Make sure we handle invalid URLs in places databases gracefully."); _("Make sure we handle invalid URLs in places databases gracefully.");
let table = store._haveTempTables ? "moz_places_temp" : "moz_places"; let query = "INSERT INTO moz_places "
let query = "INSERT INTO " + table + " "
+ "(url, title, rev_host, visit_count, last_visit_date) " + "(url, title, rev_host, visit_count, last_visit_date) "
+ "VALUES ('invalid-uri', 'Invalid URI', '.', 1, " + TIMESTAMP3 + ")"; + "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); let result = Utils.queryAsync(stmt);
do_check_eq([id for (id in store.getAllIDs())].length, 4); do_check_eq([id for (id in store.getAllIDs())].length, 4);

Просмотреть файл

@ -95,8 +95,7 @@ function test_history_guids() {
dump("tbguid: " + tbguid + "\n"); dump("tbguid: " + tbguid + "\n");
_("History: Verify GUIDs are added to the guid column."); _("History: Verify GUIDs are added to the guid column.");
let stmt = Utils.createStatement( let stmt = Svc.History.DBConnection.createAsyncStatement(
Svc.History.DBConnection,
"SELECT id FROM moz_places WHERE guid = :guid"); "SELECT id FROM moz_places WHERE guid = :guid");
stmt.params.guid = fxguid; stmt.params.guid = fxguid;
@ -108,8 +107,7 @@ function test_history_guids() {
do_check_eq(result.length, 1); do_check_eq(result.length, 1);
_("History: Verify GUIDs weren't added to annotations."); _("History: Verify GUIDs weren't added to annotations.");
stmt = Utils.createStatement( stmt = Svc.History.DBConnection.createAsyncStatement(
Svc.History.DBConnection,
"SELECT a.content AS guid FROM moz_annos a WHERE guid = :guid"); "SELECT a.content AS guid FROM moz_annos a WHERE guid = :guid");
stmt.params.guid = fxguid; stmt.params.guid = fxguid;
@ -136,8 +134,7 @@ function test_bookmark_guids() {
let tbguid = store.GUIDForId(tbid); let tbguid = store.GUIDForId(tbid);
_("Bookmarks: Verify GUIDs are added to the guid column."); _("Bookmarks: Verify GUIDs are added to the guid column.");
let stmt = Utils.createStatement( let stmt = Svc.History.DBConnection.createAsyncStatement(
Svc.History.DBConnection,
"SELECT id FROM moz_bookmarks WHERE guid = :guid"); "SELECT id FROM moz_bookmarks WHERE guid = :guid");
stmt.params.guid = fxguid; stmt.params.guid = fxguid;
@ -151,8 +148,7 @@ function test_bookmark_guids() {
do_check_eq(result[0].id, tbid); do_check_eq(result[0].id, tbid);
_("Bookmarks: Verify GUIDs weren't added to annotations."); _("Bookmarks: Verify GUIDs weren't added to annotations.");
stmt = Utils.createStatement( stmt = Svc.History.DBConnection.createAsyncStatement(
Svc.History.DBConnection,
"SELECT a.content AS guid FROM moz_items_annos a WHERE guid = :guid"); "SELECT a.content AS guid FROM moz_items_annos a WHERE guid = :guid");
stmt.params.guid = fxguid; stmt.params.guid = fxguid;

Просмотреть файл

@ -221,6 +221,23 @@ function run_test() {
let did401 = false; let did401 = false;
Observers.add("weave:resource:status:401", function() did401 = true); 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)"); _("GET a password protected resource (test that it'll fail w/o pass, no throw)");
let res2 = new Resource("http://localhost:8080/protected"); let res2 = new Resource("http://localhost:8080/protected");
content = res2.get(); content = res2.get();
@ -346,8 +363,8 @@ function run_test() {
do_check_eq(content, JSON.stringify({"x-what-is-weave": "awesome"})); do_check_eq(content, JSON.stringify({"x-what-is-weave": "awesome"}));
_("setHeader(): setting multiple headers, overwriting existing header"); _("setHeader(): setting multiple headers, overwriting existing header");
res9.setHeader('X-WHAT-is-Weave', 'more awesomer', res9.setHeader('X-WHAT-is-Weave', 'more awesomer');
'X-Another-Header', 'hello world'); res9.setHeader('X-Another-Header', 'hello world');
do_check_eq(res9.headers['x-what-is-weave'], 'more awesomer'); do_check_eq(res9.headers['x-what-is-weave'], 'more awesomer');
do_check_eq(res9.headers['x-another-header'], 'hello world'); do_check_eq(res9.headers['x-another-header'], 'hello world');
content = res9.get(); content = res9.get();

Просмотреть файл

@ -206,6 +206,26 @@ function run_test() {
next(); 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) { }, function (next) {
_("GET a password protected resource (test that it'll fail w/o pass, no throw)"); _("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"); _("setHeader(): setting multiple headers, overwriting existing header");
do_test_pending(); do_test_pending();
res_headers.setHeader('X-WHAT-is-Weave', 'more awesomer', res_headers.setHeader('X-WHAT-is-Weave', 'more awesomer');
'X-Another-Header', 'hello world'); 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-what-is-weave'], 'more awesomer');
do_check_eq(res_headers.headers['x-another-header'], 'hello world'); do_check_eq(res_headers.headers['x-another-header'], 'hello world');
res_headers.get(ensureThrows(function (error, content) { 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/"; Service.clusterURL = "http://weave.cluster/";
do_check_eq(Svc.Prefs.get("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, 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, do_check_eq(Service.storageURL,
"http://weave.cluster/1.0/johndoe/storage/"); "http://weave.cluster/1.1/johndoe/storage/");
do_check_eq(Service.metaURL, 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."); _("The 'miscURL' and 'userURL' attributes can be relative to 'serverURL' or absolute.");
Svc.Prefs.set("miscURL", "relative/misc/"); Svc.Prefs.set("miscURL", "relative/misc/");

Просмотреть файл

@ -15,7 +15,7 @@ function run_test() {
Service.serverURL = "http://localhost:8080/"; Service.serverURL = "http://localhost:8080/";
_("A 404 will be recorded as 'generic-server-error'"); _("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."); _("Account that's available.");
do_check_eq(Service.checkAccount("john@doe.com"), "available"); do_check_eq(Service.checkAccount("john@doe.com"), "available");
@ -23,13 +23,11 @@ function run_test() {
_("Account that's not available."); _("Account that's not available.");
do_check_eq(Service.checkAccount("jane@doe.com"), "notAvailable"); 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."); _("Username fallback: Account that's available.");
do_check_eq(Service.checkUsername("johndoe"), "notAvailable"); do_check_eq(Service.checkAccount("janedoe"), "available");
_("Account that's available.");
do_check_eq(Service.checkUsername("janedoe"), "available");
} finally { } finally {
Svc.Prefs.resetBranch(""); Svc.Prefs.resetBranch("");

Просмотреть файл

@ -23,10 +23,7 @@ function run_test() {
// jane@doe.com // jane@doe.com
"/user/1.0/vuuf3eqgloxpxmzph27f5a6ve7gzlrms": send(400, "Bad Request", "2"), "/user/1.0/vuuf3eqgloxpxmzph27f5a6ve7gzlrms": send(400, "Bad Request", "2"),
// jim@doe.com // jim@doe.com
"/user/1.0/vz6fhecgw5t3sgx3a4cektoiokyczkqd": send(500, "Server Error", "Server Error"), "/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")
}); });
try { try {
Service.serverURL = "http://localhost:8080/"; Service.serverURL = "http://localhost:8080/";
@ -65,24 +62,6 @@ function run_test() {
"challenge", "response"); "challenge", "response");
do_check_eq(secretHeader, "my-server-secret"); 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 { } finally {
Svc.Prefs.resetBranch(""); Svc.Prefs.resetBranch("");
server.stop(do_test_finished); server.stop(do_test_finished);

Просмотреть файл

@ -24,20 +24,20 @@ function v4_upgrade(next) {
let keysWBO = new ServerWBO("keys"); let keysWBO = new ServerWBO("keys");
let server = httpd_setup({ let server = httpd_setup({
// Special. // Special.
"/1.0/johndoe/info/collections": collectionsHelper.handler, "/1.1/johndoe/info/collections": collectionsHelper.handler,
"/1.0/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()), "/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()),
"/1.0/johndoe/storage/meta/global": upd("meta", meta_global.handler()), "/1.1/johndoe/storage/meta/global": upd("meta", meta_global.handler()),
// Track modified times. // Track modified times.
"/1.0/johndoe/storage/clients": upd("clients", clients.handler()), "/1.1/johndoe/storage/clients": upd("clients", clients.handler()),
"/1.0/johndoe/storage/tabs": upd("tabs", new ServerCollection().handler()), "/1.1/johndoe/storage/tabs": upd("tabs", new ServerCollection().handler()),
// Just so we don't get 404s in the logs. // Just so we don't get 404s in the logs.
"/1.0/johndoe/storage/bookmarks": new ServerCollection().handler(), "/1.1/johndoe/storage/bookmarks": new ServerCollection().handler(),
"/1.0/johndoe/storage/forms": new ServerCollection().handler(), "/1.1/johndoe/storage/forms": new ServerCollection().handler(),
"/1.0/johndoe/storage/history": new ServerCollection().handler(), "/1.1/johndoe/storage/history": new ServerCollection().handler(),
"/1.0/johndoe/storage/passwords": new ServerCollection().handler(), "/1.1/johndoe/storage/passwords": new ServerCollection().handler(),
"/1.0/johndoe/storage/prefs": new ServerCollection().handler() "/1.1/johndoe/storage/prefs": new ServerCollection().handler()
}); });
try { try {
@ -203,14 +203,14 @@ function v5_upgrade(next) {
let server = httpd_setup({ let server = httpd_setup({
// Special. // Special.
"/1.0/johndoe/storage/meta/global": upd("meta", meta_global.handler()), "/1.1/johndoe/storage/meta/global": upd("meta", meta_global.handler()),
"/1.0/johndoe/info/collections": collectionsHelper.handler, "/1.1/johndoe/info/collections": collectionsHelper.handler,
"/1.0/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()), "/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()),
"/1.0/johndoe/storage/crypto/bulk": upd("crypto", bulkWBO.handler()), "/1.1/johndoe/storage/crypto/bulk": upd("crypto", bulkWBO.handler()),
// Track modified times. // Track modified times.
"/1.0/johndoe/storage/clients": upd("clients", clients.handler()), "/1.1/johndoe/storage/clients": upd("clients", clients.handler()),
"/1.0/johndoe/storage/tabs": upd("tabs", new ServerCollection().handler()), "/1.1/johndoe/storage/tabs": upd("tabs", new ServerCollection().handler()),
}); });
try { try {

Просмотреть файл

@ -38,16 +38,16 @@ function run_test() {
do_test_pending(); do_test_pending();
let server = httpd_setup({ let server = httpd_setup({
"/1.0/johndoe/info/collections": login_handler, "/1.1/johndoe/info/collections": login_handler,
"/1.0/janedoe/info/collections": login_handler, "/1.1/janedoe/info/collections": login_handler,
// We need these handlers because we test login, and login // We need these handlers because we test login, and login
// is where keys are generated or fetched. // is where keys are generated or fetched.
// TODO: have Jane fetch her keys, not generate them... // TODO: have Jane fetch her keys, not generate them...
"/1.0/johndoe/storage/crypto/keys": new ServerWBO().handler(), "/1.1/johndoe/storage/crypto/keys": new ServerWBO().handler(),
"/1.0/johndoe/storage/meta/global": new ServerWBO().handler(), "/1.1/johndoe/storage/meta/global": new ServerWBO().handler(),
"/1.0/janedoe/storage/crypto/keys": new ServerWBO().handler(), "/1.1/janedoe/storage/crypto/keys": new ServerWBO().handler(),
"/1.0/janedoe/storage/meta/global": new ServerWBO().handler() "/1.1/janedoe/storage/meta/global": new ServerWBO().handler()
}); });
try { try {

Просмотреть файл

@ -55,9 +55,9 @@ function run_test() {
do_test_pending(); do_test_pending();
let server = httpd_setup({ let server = httpd_setup({
"/1.0/johndoe/info/collections": info_collections, "/1.1/johndoe/info/collections": info_collections,
"/1.0/johndoe/storage/meta/global": new ServerWBO().handler(), "/1.1/johndoe/storage/meta/global": new ServerWBO().handler(),
"/1.0/johndoe/storage/crypto/keys": new ServerWBO().handler(), "/1.1/johndoe/storage/crypto/keys": new ServerWBO().handler(),
"/user/1.0/johndoe/password": change_password "/user/1.0/johndoe/password": change_password
}); });

Просмотреть файл

@ -9,10 +9,10 @@ function run_test() {
do_test_pending(); do_test_pending();
let server = httpd_setup({ let server = httpd_setup({
"/1.0/johndoe/info/collection_usage": httpd_handler(200, "OK", JSON.stringify(collection_usage)), "/1.1/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.1/johndoe/info/quota": httpd_handler(200, "OK", JSON.stringify(quota)),
"/1.0/janedoe/info/collection_usage": httpd_handler(200, "OK", "gargabe"), "/1.1/janedoe/info/collection_usage": httpd_handler(200, "OK", "gargabe"),
"/1.0/janedoe/info/quota": httpd_handler(200, "OK", "more garbage") "/1.1/janedoe/info/quota": httpd_handler(200, "OK", "more garbage")
}); });
try { 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(); do_test_pending();
let server = httpd_setup({ let server = httpd_setup({
"/1.0/johndoe/storage/crypto/keys": new ServerWBO().handler(), "/1.1/johndoe/storage/crypto/keys": new ServerWBO().handler(),
"/1.0/johndoe/storage/meta/global": new ServerWBO().handler(), "/1.1/johndoe/storage/meta/global": new ServerWBO().handler(),
"/1.0/johndoe/info/collections": login_handler "/1.1/johndoe/info/collections": login_handler
}); });
const GLOBAL_SCORE = 42; const GLOBAL_SCORE = 42;

Просмотреть файл

@ -38,10 +38,10 @@ function sync_httpd_setup() {
engines: engines}); engines: engines});
let handlers = { let handlers = {
"/1.0/johndoe/info/collections": collectionsHelper.handler, "/1.1/johndoe/info/collections": collectionsHelper.handler,
"/1.0/johndoe/storage/meta/global": upd("meta", globalWBO.handler()), "/1.1/johndoe/storage/meta/global": upd("meta", globalWBO.handler()),
"/1.0/johndoe/storage/clients": upd("clients", clientsColl.handler()), "/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler()),
"/1.0/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()) "/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler())
} }
return httpd_setup(handlers); return httpd_setup(handlers);
} }
@ -58,7 +58,7 @@ function generateAndUploadKeys() {
CollectionKeys.generateNewKeys(); CollectionKeys.generateNewKeys();
let serverKeys = CollectionKeys.asWBO("crypto", "keys"); let serverKeys = CollectionKeys.asWBO("crypto", "keys");
serverKeys.encrypt(Weave.Service.syncKeyBundle); 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) { function test_backoff500(next) {

Просмотреть файл

@ -33,12 +33,12 @@ function run_test() {
let metaColl = new ServerCollection({global: meta_global}); let metaColl = new ServerCollection({global: meta_global});
do_test_pending(); do_test_pending();
let server = httpd_setup({ let server = httpd_setup({
"/1.0/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()), "/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()),
"/1.0/johndoe/storage/crypto": upd("crypto", cryptoColl.handler()), "/1.1/johndoe/storage/crypto": upd("crypto", cryptoColl.handler()),
"/1.0/johndoe/storage/clients": upd("clients", clients.handler()), "/1.1/johndoe/storage/clients": upd("clients", clients.handler()),
"/1.0/johndoe/storage/meta/global": upd("meta", wasCalledHandler(meta_global)), "/1.1/johndoe/storage/meta/global": upd("meta", wasCalledHandler(meta_global)),
"/1.0/johndoe/storage/meta": upd("meta", wasCalledHandler(metaColl)), "/1.1/johndoe/storage/meta": upd("meta", wasCalledHandler(metaColl)),
"/1.0/johndoe/info/collections": collectionsHelper.handler "/1.1/johndoe/info/collections": collectionsHelper.handler
}); });
try { try {

Просмотреть файл

@ -48,14 +48,14 @@ let upd = collectionsHelper.with_updated_collection;
function sync_httpd_setup(handlers) { 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"); let cr = new ServerWBO("keys");
handlers["/1.0/johndoe/storage/crypto/keys"] = handlers["/1.1/johndoe/storage/crypto/keys"] =
upd("crypto", cr.handler()); upd("crypto", cr.handler());
let cl = new ServerCollection(); let cl = new ServerCollection();
handlers["/1.0/johndoe/storage/clients"] = handlers["/1.1/johndoe/storage/clients"] =
upd("clients", cl.handler()); upd("clients", cl.handler());
return httpd_setup(handlers); return httpd_setup(handlers);
@ -75,8 +75,8 @@ function test_newAccount() {
_("Test: New account does not disable locally enabled engines."); _("Test: New account does not disable locally enabled engines.");
let engine = Engines.get("steam"); let engine = Engines.get("steam");
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/johndoe/storage/meta/global": new ServerWBO("global", {}).handler(), "/1.1/johndoe/storage/meta/global": new ServerWBO("global", {}).handler(),
"/1.0/johndoe/storage/steam": new ServerWBO("steam", {}).handler() "/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler()
}); });
do_test_pending(); do_test_pending();
setUp(); setUp();
@ -107,8 +107,8 @@ function test_enabledLocally() {
storageVersion: STORAGE_VERSION, storageVersion: STORAGE_VERSION,
engines: {}}); engines: {}});
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/johndoe/storage/meta/global": metaWBO.handler(), "/1.1/johndoe/storage/meta/global": metaWBO.handler(),
"/1.0/johndoe/storage/steam": new ServerWBO("steam", {}).handler() "/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler()
}); });
do_test_pending(); do_test_pending();
setUp(); setUp();
@ -144,8 +144,8 @@ function test_disabledLocally() {
}); });
let steamCollection = new ServerWBO("steam", PAYLOAD); let steamCollection = new ServerWBO("steam", PAYLOAD);
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/johndoe/storage/meta/global": metaWBO.handler(), "/1.1/johndoe/storage/meta/global": metaWBO.handler(),
"/1.0/johndoe/storage/steam": steamCollection.handler() "/1.1/johndoe/storage/steam": steamCollection.handler()
}); });
do_test_pending(); do_test_pending();
setUp(); setUp();
@ -186,8 +186,8 @@ function test_enabledRemotely() {
version: engine.version}} version: engine.version}}
}); });
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/johndoe/storage/meta/global": metaWBO.handler(), "/1.1/johndoe/storage/meta/global": metaWBO.handler(),
"/1.0/johndoe/storage/steam": new ServerWBO("steam", {}).handler() "/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler()
}); });
do_test_pending(); do_test_pending();
setUp(); setUp();
@ -219,10 +219,10 @@ function test_disabledRemotelyTwoClients() {
storageVersion: STORAGE_VERSION, storageVersion: STORAGE_VERSION,
engines: {}}); engines: {}});
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/johndoe/storage/meta/global": "/1.1/johndoe/storage/meta/global":
upd("meta", metaWBO.handler()), upd("meta", metaWBO.handler()),
"/1.0/johndoe/storage/steam": "/1.1/johndoe/storage/steam":
upd("steam", new ServerWBO("steam", {}).handler()) upd("steam", new ServerWBO("steam", {}).handler())
}); });
do_test_pending(); do_test_pending();
@ -265,8 +265,8 @@ function test_disabledRemotely() {
storageVersion: STORAGE_VERSION, storageVersion: STORAGE_VERSION,
engines: {}}); engines: {}});
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/johndoe/storage/meta/global": metaWBO.handler(), "/1.1/johndoe/storage/meta/global": metaWBO.handler(),
"/1.0/johndoe/storage/steam": new ServerWBO("steam", {}).handler() "/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler()
}); });
do_test_pending(); do_test_pending();
setUp(); setUp();
@ -299,9 +299,9 @@ function test_dependentEnginesEnabledLocally() {
storageVersion: STORAGE_VERSION, storageVersion: STORAGE_VERSION,
engines: {}}); engines: {}});
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/johndoe/storage/meta/global": metaWBO.handler(), "/1.1/johndoe/storage/meta/global": metaWBO.handler(),
"/1.0/johndoe/storage/steam": new ServerWBO("steam", {}).handler(), "/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler(),
"/1.0/johndoe/storage/stirling": new ServerWBO("stirling", {}).handler() "/1.1/johndoe/storage/stirling": new ServerWBO("stirling", {}).handler()
}); });
do_test_pending(); do_test_pending();
setUp(); setUp();
@ -344,9 +344,9 @@ function test_dependentEnginesDisabledLocally() {
let steamCollection = new ServerWBO("steam", PAYLOAD); let steamCollection = new ServerWBO("steam", PAYLOAD);
let stirlingCollection = new ServerWBO("stirling", PAYLOAD); let stirlingCollection = new ServerWBO("stirling", PAYLOAD);
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/johndoe/storage/meta/global": metaWBO.handler(), "/1.1/johndoe/storage/meta/global": metaWBO.handler(),
"/1.0/johndoe/storage/steam": steamCollection.handler(), "/1.1/johndoe/storage/steam": steamCollection.handler(),
"/1.0/johndoe/storage/stirling": stirlingCollection.handler() "/1.1/johndoe/storage/stirling": stirlingCollection.handler()
}); });
do_test_pending(); do_test_pending();
setUp(); setUp();

Просмотреть файл

@ -34,10 +34,10 @@ function run_test() {
do_test_pending(); do_test_pending();
let server = httpd_setup({ let server = httpd_setup({
"/api/1.0/johndoe/info/collections": login_handler, "/api/1.1/johndoe/info/collections": login_handler,
"/api/1.0/janedoe/info/collections": service_unavailable, "/api/1.1/janedoe/info/collections": service_unavailable,
"/api/1.0/johndoe/storage/meta/global": new ServerWBO().handler(), "/api/1.1/johndoe/storage/meta/global": new ServerWBO().handler(),
"/api/1.0/johndoe/storage/crypto/keys": 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/") "/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 diesel_coll = new FakeCollection();
let server = httpd_setup({ let server = httpd_setup({
"/1.0/johndoe/storage/steam": steam_coll.handler(), "/1.1/johndoe/storage/steam": steam_coll.handler(),
"/1.0/johndoe/storage/petrol": serviceUnavailable, "/1.1/johndoe/storage/petrol": serviceUnavailable,
"/1.0/johndoe/storage/diesel": diesel_coll.handler() "/1.1/johndoe/storage/diesel": diesel_coll.handler()
}); });
do_test_pending(); do_test_pending();
@ -99,10 +99,10 @@ function test_wipeServer_leaves_collections() {
} }
let server = httpd_setup({ let server = httpd_setup({
"/1.0/johndoe/storage/steam": steam_coll.handler(), "/1.1/johndoe/storage/steam": steam_coll.handler(),
"/1.0/johndoe/storage/diesel": diesel_coll.handler(), "/1.1/johndoe/storage/diesel": diesel_coll.handler(),
"/1.0/johndoe/storage/keys": keys_coll.handler(), "/1.1/johndoe/storage/keys": keys_coll.handler(),
"/1.0/johndoe/info/collections": info_collections "/1.1/johndoe/info/collections": info_collections
}); });
do_test_pending(); do_test_pending();

Просмотреть файл

@ -12,9 +12,9 @@ function test_url_attributes() {
Svc.Prefs.set("clusterURL", "https://cluster/"); Svc.Prefs.set("clusterURL", "https://cluster/");
let engine = makeSteamEngine(); let engine = makeSteamEngine();
try { try {
do_check_eq(engine.storageURL, "https://cluster/1.0/foo/storage/"); do_check_eq(engine.storageURL, "https://cluster/1.1/foo/storage/");
do_check_eq(engine.engineURL, "https://cluster/1.0/foo/storage/steam"); do_check_eq(engine.engineURL, "https://cluster/1.1/foo/storage/steam");
do_check_eq(engine.metaURL, "https://cluster/1.0/foo/storage/meta/global"); do_check_eq(engine.metaURL, "https://cluster/1.1/foo/storage/meta/global");
} finally { } finally {
Svc.Prefs.resetBranch(""); Svc.Prefs.resetBranch("");
} }
@ -129,7 +129,7 @@ function test_wipeServer() {
const PAYLOAD = 42; const PAYLOAD = 42;
let steamCollection = new ServerWBO("steam", PAYLOAD); let steamCollection = new ServerWBO("steam", PAYLOAD);
let server = httpd_setup({ let server = httpd_setup({
"/1.0/foo/storage/steam": steamCollection.handler() "/1.1/foo/storage/steam": steamCollection.handler()
}); });
do_test_pending(); do_test_pending();

Просмотреть файл

@ -129,7 +129,7 @@ function test_syncStartup_emptyOrOutdatedGlobalsResetsSync() {
denomination: "Flying Scotsman"})); denomination: "Flying Scotsman"}));
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/foo/storage/steam": collection.handler() "/1.1/foo/storage/steam": collection.handler()
}); });
do_test_pending(); do_test_pending();
@ -176,7 +176,7 @@ function test_syncStartup_serverHasNewerVersion() {
Svc.Prefs.set("username", "foo"); Svc.Prefs.set("username", "foo");
let global = new ServerWBO('global', {engines: {steam: {version: 23456}}}); let global = new ServerWBO('global', {engines: {steam: {version: 23456}}});
let server = httpd_setup({ let server = httpd_setup({
"/1.0/foo/storage/meta/global": global.handler() "/1.1/foo/storage/meta/global": global.handler()
}); });
do_test_pending(); do_test_pending();
@ -215,7 +215,7 @@ function test_syncStartup_syncIDMismatchResetsClient() {
let global = new ServerWBO('global', let global = new ServerWBO('global',
{engines: {steam: {version: engine.version, {engines: {steam: {version: engine.version,
syncID: 'foobar'}}}); syncID: 'foobar'}}});
server.registerPathHandler("/1.0/foo/storage/meta/global", global.handler()); server.registerPathHandler("/1.1/foo/storage/meta/global", global.handler());
try { try {
@ -250,7 +250,7 @@ function test_processIncoming_emptyServer() {
let collection = new ServerCollection(); let collection = new ServerCollection();
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/foo/storage/steam": collection.handler() "/1.1/foo/storage/steam": collection.handler()
}); });
do_test_pending(); do_test_pending();
@ -293,9 +293,9 @@ function test_processIncoming_createFromServer() {
denomination: "Pathological Case"})); denomination: "Pathological Case"}));
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/foo/storage/steam": collection.handler(), "/1.1/foo/storage/steam": collection.handler(),
"/1.0/foo/storage/steam/flying": collection.wbos.flying.handler(), "/1.1/foo/storage/steam/flying": collection.wbos.flying.handler(),
"/1.0/foo/storage/steam/scotsman": collection.wbos.scotsman.handler() "/1.1/foo/storage/steam/scotsman": collection.wbos.scotsman.handler()
}); });
do_test_pending(); do_test_pending();
@ -387,7 +387,7 @@ function test_processIncoming_reconcile() {
deleted: true})); deleted: true}));
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/foo/storage/steam": collection.handler() "/1.1/foo/storage/steam": collection.handler()
}); });
do_test_pending(); do_test_pending();
@ -483,7 +483,7 @@ function test_processIncoming_mobile_batchSize() {
} }
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/foo/storage/steam": collection.handler() "/1.1/foo/storage/steam": collection.handler()
}); });
do_test_pending(); do_test_pending();
@ -563,7 +563,7 @@ function test_processIncoming_store_toFetch() {
meta_global.payload.engines = {steam: {version: engine.version, meta_global.payload.engines = {steam: {version: engine.version,
syncID: engine.syncID}}; syncID: engine.syncID}};
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/foo/storage/steam": collection.handler() "/1.1/foo/storage/steam": collection.handler()
}); });
do_test_pending(); do_test_pending();
@ -631,7 +631,7 @@ function test_processIncoming_resume_toFetch() {
meta_global.payload.engines = {steam: {version: engine.version, meta_global.payload.engines = {steam: {version: engine.version,
syncID: engine.syncID}}; syncID: engine.syncID}};
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/foo/storage/steam": collection.handler() "/1.1/foo/storage/steam": collection.handler()
}); });
do_test_pending(); do_test_pending();
@ -688,7 +688,7 @@ function test_processIncoming_applyIncomingBatchSize_smaller() {
meta_global.payload.engines = {steam: {version: engine.version, meta_global.payload.engines = {steam: {version: engine.version,
syncID: engine.syncID}}; syncID: engine.syncID}};
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/foo/storage/steam": collection.handler() "/1.1/foo/storage/steam": collection.handler()
}); });
do_test_pending(); do_test_pending();
@ -746,7 +746,7 @@ function test_processIncoming_applyIncomingBatchSize_multiple() {
meta_global.payload.engines = {steam: {version: engine.version, meta_global.payload.engines = {steam: {version: engine.version,
syncID: engine.syncID}}; syncID: engine.syncID}};
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/foo/storage/steam": collection.handler() "/1.1/foo/storage/steam": collection.handler()
}); });
do_test_pending(); do_test_pending();
@ -777,10 +777,6 @@ function test_processIncoming_failed_records() {
Svc.Prefs.set("clusterURL", "http://localhost:8080/"); Svc.Prefs.set("clusterURL", "http://localhost:8080/");
Svc.Prefs.set("username", "foo"); 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's create three and a bit batches worth of server side records.
let collection = new ServerCollection(); let collection = new ServerCollection();
const NUMBER_OF_RECORDS = MOBILE_BATCH_SIZE * 3 + 5; 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)); let meta_global = Records.set(engine.metaURL, new WBORecord(engine.metaURL));
meta_global.payload.engines = {steam: {version: engine.version, meta_global.payload.engines = {steam: {version: engine.version,
syncID: engine.syncID}}; 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({ let server = sync_httpd_setup({
"/1.0/foo/storage/steam": collection.handler() "/1.1/foo/storage/steam": recording_handler(collection)
}); });
do_test_pending(); do_test_pending();
@ -863,6 +871,33 @@ function test_processIncoming_failed_records() {
// Ensure the observer was notified // Ensure the observer was notified
do_check_eq(observerData, engine.name); do_check_eq(observerData, engine.name);
do_check_eq(observerSubject.failed, BOGUS_RECORDS.length); 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 { } finally {
server.stop(do_test_finished); server.stop(do_test_finished);
Svc.Prefs.resetBranch(""); Svc.Prefs.resetBranch("");
@ -910,7 +945,7 @@ function test_processIncoming_decrypt_failed() {
meta_global.payload.engines = {steam: {version: engine.version, meta_global.payload.engines = {steam: {version: engine.version,
syncID: engine.syncID}}; syncID: engine.syncID}};
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/foo/storage/steam": collection.handler() "/1.1/foo/storage/steam": collection.handler()
}); });
do_test_pending(); do_test_pending();
@ -961,9 +996,9 @@ function test_uploadOutgoing_toEmptyServer() {
collection.wbos.scotsman = new ServerWBO('scotsman'); collection.wbos.scotsman = new ServerWBO('scotsman');
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/foo/storage/steam": collection.handler(), "/1.1/foo/storage/steam": collection.handler(),
"/1.0/foo/storage/steam/flying": collection.wbos.flying.handler(), "/1.1/foo/storage/steam/flying": collection.wbos.flying.handler(),
"/1.0/foo/storage/steam/scotsman": collection.wbos.scotsman.handler() "/1.1/foo/storage/steam/scotsman": collection.wbos.scotsman.handler()
}); });
do_test_pending(); do_test_pending();
CollectionKeys.generateNewKeys(); CollectionKeys.generateNewKeys();
@ -1023,7 +1058,7 @@ function test_uploadOutgoing_failed() {
collection.wbos.flying = new ServerWBO('flying'); collection.wbos.flying = new ServerWBO('flying');
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/foo/storage/steam": collection.handler() "/1.1/foo/storage/steam": collection.handler()
}); });
do_test_pending(); do_test_pending();
@ -1107,7 +1142,7 @@ function test_uploadOutgoing_MAX_UPLOAD_RECORDS() {
syncID: engine.syncID}}; syncID: engine.syncID}};
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/foo/storage/steam": collection.handler() "/1.1/foo/storage/steam": collection.handler()
}); });
do_test_pending(); do_test_pending();
@ -1165,7 +1200,7 @@ function test_syncFinish_deleteByIds() {
denomination: "Rekonstruktionslokomotive"})); denomination: "Rekonstruktionslokomotive"}));
let server = httpd_setup({ let server = httpd_setup({
"/1.0/foo/storage/steam": collection.handler() "/1.1/foo/storage/steam": collection.handler()
}); });
do_test_pending(); do_test_pending();
@ -1219,7 +1254,7 @@ function test_syncFinish_deleteLotsInBatches() {
} }
let server = httpd_setup({ let server = httpd_setup({
"/1.0/foo/storage/steam": collection.handler() "/1.1/foo/storage/steam": collection.handler()
}); });
do_test_pending(); do_test_pending();
@ -1274,7 +1309,7 @@ function test_sync_partialUpload() {
let collection = new ServerCollection(); let collection = new ServerCollection();
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/foo/storage/steam": collection.handler() "/1.1/foo/storage/steam": collection.handler()
}); });
do_test_pending(); do_test_pending();
CollectionKeys.generateNewKeys(); CollectionKeys.generateNewKeys();
@ -1356,7 +1391,7 @@ function test_canDecrypt_noCryptoKeys() {
denomination: "LNER Class A3 4472"})); denomination: "LNER Class A3 4472"}));
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/foo/storage/steam": collection.handler() "/1.1/foo/storage/steam": collection.handler()
}); });
do_test_pending(); do_test_pending();
@ -1387,7 +1422,7 @@ function test_canDecrypt_true() {
denomination: "LNER Class A3 4472"})); denomination: "LNER Class A3 4472"}));
let server = sync_httpd_setup({ let server = sync_httpd_setup({
"/1.0/foo/storage/steam": collection.handler() "/1.1/foo/storage/steam": collection.handler()
}); });
do_test_pending(); 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 // we just test whether the returned string includes the
// string "unknown", should be good enough // 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); do_check_true(str.match(/unknown/i) == null);
str = Utils.getErrorString("foobar"); str = Utils.getErrorString("foobar");

Просмотреть файл

@ -1,5 +1,6 @@
_("Make sure json saves and loads from disk"); _("Make sure json saves and loads from disk");
Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-sync/constants.js");
function run_test() { function run_test() {
do_test_pending(); do_test_pending();
@ -68,9 +69,15 @@ function run_test() {
// Write a file with some invalid JSON // Write a file with some invalid JSON
let file = Utils.getProfileFile({ autoCreate: true, let file = Utils.getProfileFile({ autoCreate: true,
path: "weave/log.json" }); path: "weave/log.json" });
let [fos] = Utils.open(file, ">"); let fos = Cc["@mozilla.org/network/file-output-stream;1"]
fos.writeString("invalid json!"); .createInstance(Ci.nsIFileOutputStream);
fos.close(); 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; let trace, debug;
Utils.jsonLoad("log", Utils.jsonLoad("log",

Просмотреть файл

@ -1 +1 @@
1.7 1.8.0