diff options
author | janekptacijarabaci <janekptacijarabaci@seznam.cz> | 2018-07-06 15:53:52 +0200 |
---|---|---|
committer | janekptacijarabaci <janekptacijarabaci@seznam.cz> | 2018-07-06 15:53:52 +0200 |
commit | 941e54654eabed0a3568f7fefe424a45aa02eddb (patch) | |
tree | 49aa02b174c428962d99142d8061267bfcd79e69 /services | |
parent | ad9ee72dcd7981bc47b3844a224d69fadfdfd8ef (diff) | |
parent | 0daa12376295d5d796256a116eb2a348a3a9273f (diff) | |
download | UXP-941e54654eabed0a3568f7fefe424a45aa02eddb.tar UXP-941e54654eabed0a3568f7fefe424a45aa02eddb.tar.gz UXP-941e54654eabed0a3568f7fefe424a45aa02eddb.tar.lz UXP-941e54654eabed0a3568f7fefe424a45aa02eddb.tar.xz UXP-941e54654eabed0a3568f7fefe424a45aa02eddb.zip |
Merge branch 'master' of https://github.com/MoonchildProductions/UXP into _testBranch_test_1
Diffstat (limited to 'services')
21 files changed, 211 insertions, 706 deletions
diff --git a/services/fxaccounts/FxAccountsManager.jsm b/services/fxaccounts/FxAccountsManager.jsm deleted file mode 100644 index 680310ff5..000000000 --- a/services/fxaccounts/FxAccountsManager.jsm +++ /dev/null @@ -1,654 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Temporary abstraction layer for common Fx Accounts operations. - * For now, we will be using this module only from B2G but in the end we might - * want this to be merged with FxAccounts.jsm and let other products also use - * it. - */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["FxAccountsManager"]; - -const { classes: Cc, interfaces: Ci, utils: Cu } = Components; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/FxAccounts.jsm"); -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/FxAccountsCommon.js"); - -XPCOMUtils.defineLazyServiceGetter(this, "permissionManager", - "@mozilla.org/permissionmanager;1", - "nsIPermissionManager"); - -this.FxAccountsManager = { - - init: function() { - Services.obs.addObserver(this, ONLOGOUT_NOTIFICATION, false); - Services.obs.addObserver(this, ON_FXA_UPDATE_NOTIFICATION, false); - }, - - observe: function(aSubject, aTopic, aData) { - // Both topics indicate our cache is invalid - this._activeSession = null; - - if (aData == ONVERIFIED_NOTIFICATION) { - log.debug("FxAccountsManager: cache cleared, broadcasting: " + aData); - Services.obs.notifyObservers(null, aData, null); - } - }, - - // We don't really need to save fxAccounts instance but this way we allow - // to mock FxAccounts from tests. - _fxAccounts: fxAccounts, - - // We keep the session details here so consumers don't need to deal with - // session tokens and are only required to handle the email. - _activeSession: null, - - // Are we refreshing our authentication? If so, allow attempts to sign in - // while we are already signed in. - _refreshing: false, - - // We only expose the email and the verified status so far. - get _user() { - if (!this._activeSession || !this._activeSession.email) { - return null; - } - - return { - email: this._activeSession.email, - verified: this._activeSession.verified, - profile: this._activeSession.profile, - } - }, - - _error: function(aError, aDetails) { - log.error(aError); - let reason = { - error: aError - }; - if (aDetails) { - reason.details = aDetails; - } - return Promise.reject(reason); - }, - - _getError: function(aServerResponse) { - if (!aServerResponse || !aServerResponse.error || !aServerResponse.error.errno) { - return; - } - let error = SERVER_ERRNO_TO_ERROR[aServerResponse.error.errno]; - return error; - }, - - _serverError: function(aServerResponse) { - let error = this._getError({ error: aServerResponse }); - return this._error(error ? error : ERROR_SERVER_ERROR, aServerResponse); - }, - - // As with _fxAccounts, we don't really need this method, but this way we - // allow tests to mock FxAccountsClient. By default, we want to return the - // client used by the fxAccounts object because deep down they should have - // access to the same hawk request object which will enable them to share - // local clock skeq data. - _getFxAccountsClient: function() { - return this._fxAccounts.getAccountsClient(); - }, - - _signInSignUp: function(aMethod, aEmail, aPassword, aFetchKeys) { - if (Services.io.offline) { - return this._error(ERROR_OFFLINE); - } - - if (!aEmail) { - return this._error(ERROR_INVALID_EMAIL); - } - - if (!aPassword) { - return this._error(ERROR_INVALID_PASSWORD); - } - - // Check that there is no signed in account first. - if ((!this._refreshing) && this._activeSession) { - return this._error(ERROR_ALREADY_SIGNED_IN_USER, { - user: this._user - }); - } - - let client = this._getFxAccountsClient(); - return this._fxAccounts.getSignedInUser().then( - user => { - if ((!this._refreshing) && user) { - return this._error(ERROR_ALREADY_SIGNED_IN_USER, { - user: this._user - }); - } - return client[aMethod](aEmail, aPassword, aFetchKeys); - } - ).then( - user => { - let error = this._getError(user); - if (!user || !user.uid || !user.sessionToken || error) { - return this._error(error ? error : ERROR_INTERNAL_INVALID_USER, { - user: user - }); - } - - // If the user object includes an email field, it may differ in - // capitalization from what we sent down. This is the server's - // canonical capitalization and should be used instead. - user.email = user.email || aEmail; - - // If we're using server-side sign to refreshAuthentication - // we don't need to update local state; also because of two - // interacting glitches we need to bypass an event emission. - // See https://bugzilla.mozilla.org/show_bug.cgi?id=1031580 - if (this._refreshing) { - return Promise.resolve({user: this._user}); - } - - return this._fxAccounts.setSignedInUser(user).then( - () => { - this._activeSession = user; - log.debug("User signed in: " + JSON.stringify(this._user) + - " - Account created " + (aMethod == "signUp")); - - // There is no way to obtain the key fetch token afterwards - // without login out the user and asking her to log in again. - // Also, key fetch tokens are designed to be short-lived, so - // we need to fetch kB as soon as we have the key fetch token. - if (aFetchKeys) { - this._fxAccounts.getKeys(); - } - - return this._fxAccounts.getSignedInUserProfile().catch(error => { - // Not fetching the profile is sad but the FxA logs will already - // have noise. - return null; - }); - } - ).then(profile => { - if (profile) { - this._activeSession.profile = profile; - } - - return Promise.resolve({ - accountCreated: aMethod === "signUp", - user: this._user - }); - }); - }, - reason => { return this._serverError(reason); } - ); - }, - - /** - * Determine whether the incoming error means that the current account - * has new server-side state via deletion or password change, and if so, - * spawn the appropriate UI (sign in or refresh); otherwise re-reject. - * - * As of May 2014, the only HTTP call triggered by this._getAssertion() - * is to /certificate/sign via: - * FxAccounts.getAssertion() - * FxAccountsInternal.getCertificateSigned() - * FxAccountsClient.signCertificate() - * See the latter method for possible (error code, errno) pairs. - */ - _handleGetAssertionError: function(reason, aAudience, aPrincipal) { - log.debug("FxAccountsManager._handleGetAssertionError()"); - let errno = (reason ? reason.errno : NaN) || NaN; - // If the previously valid email/password pair is no longer valid ... - if (errno == ERRNO_INVALID_AUTH_TOKEN) { - return this._fxAccounts.accountStatus().then( - (exists) => { - // ... if the email still maps to an account, the password - // must have changed, so ask the user to enter the new one ... - if (exists) { - return this.getAccount().then( - (user) => { - return this._refreshAuthentication(aAudience, user.email, - aPrincipal, - true /* logoutOnFailure */); - } - ); - } - // ... otherwise, the account was deleted, so ask for Sign In/Up - return this._localSignOut().then( - () => { - return this._uiRequest(UI_REQUEST_SIGN_IN_FLOW, aAudience, - aPrincipal); - }, - (reason) => { - // reject primary problem, not signout failure - log.error("Signing out in response to server error threw: " + - reason); - return this._error(reason); - } - ); - } - ); - } - return Promise.reject(reason.message ? { error: reason.message } : reason); - }, - - _getAssertion: function(aAudience, aPrincipal) { - return this._fxAccounts.getAssertion(aAudience).then( - (result) => { - if (aPrincipal) { - this._addPermission(aPrincipal); - } - return result; - }, - (reason) => { - return this._handleGetAssertionError(reason, aAudience, aPrincipal); - } - ); - }, - - /** - * "Refresh authentication" means: - * Interactively demonstrate knowledge of the FxA password - * for the currently logged-in account. - * There are two very different scenarios: - * 1) The password has changed on the server. Failure should log - * the current account OUT. - * 2) The person typing can't prove knowledge of the password used - * to log in. Failure should do nothing. - */ - _refreshAuthentication: function(aAudience, aEmail, aPrincipal, - logoutOnFailure=false) { - this._refreshing = true; - return this._uiRequest(UI_REQUEST_REFRESH_AUTH, - aAudience, aPrincipal, aEmail).then( - (assertion) => { - this._refreshing = false; - return assertion; - }, - (reason) => { - this._refreshing = false; - if (logoutOnFailure) { - return this._signOut().then( - () => { - return this._error(reason); - } - ); - } - return this._error(reason); - } - ); - }, - - _localSignOut: function() { - return this._fxAccounts.signOut(true); - }, - - _signOut: function() { - if (!this._activeSession) { - return Promise.resolve(); - } - - // We clear the local session cache as soon as we get the onlogout - // notification triggered within FxAccounts.signOut, so we save the - // session token value to be able to remove the remote server session - // in case that we have network connection. - let sessionToken = this._activeSession.sessionToken; - - return this._localSignOut().then( - () => { - // At this point the local session should already be removed. - - // The client can create new sessions up to the limit (100?). - // Orphaned tokens on the server will eventually be garbage collected. - if (Services.io.offline) { - return Promise.resolve(); - } - // Otherwise, we try to remove the remote session. - let client = this._getFxAccountsClient(); - return client.signOut(sessionToken).then( - result => { - let error = this._getError(result); - if (error) { - return this._error(error, result); - } - log.debug("Signed out"); - return Promise.resolve(); - }, - reason => { - return this._serverError(reason); - } - ); - } - ); - }, - - _uiRequest: function(aRequest, aAudience, aPrincipal, aParams) { - if (Services.io.offline) { - return this._error(ERROR_OFFLINE); - } - let ui = Cc["@mozilla.org/fxaccounts/fxaccounts-ui-glue;1"] - .createInstance(Ci.nsIFxAccountsUIGlue); - if (!ui[aRequest]) { - return this._error(ERROR_UI_REQUEST); - } - - if (!aParams || !Array.isArray(aParams)) { - aParams = [aParams]; - } - - return ui[aRequest].apply(this, aParams).then( - result => { - // Even if we get a successful result from the UI, the account will - // most likely be unverified, so we cannot get an assertion. - if (result && result.verified) { - return this._getAssertion(aAudience, aPrincipal); - } - - return this._error(ERROR_UNVERIFIED_ACCOUNT, { - user: result - }); - }, - error => { - return this._error(ERROR_UI_ERROR, error); - } - ); - }, - - _addPermission: function(aPrincipal) { - // This will fail from tests cause we are running them in the child - // process until we have chrome tests in b2g. Bug 797164. - try { - permissionManager.addFromPrincipal(aPrincipal, FXACCOUNTS_PERMISSION, - Ci.nsIPermissionManager.ALLOW_ACTION); - } catch (e) { - log.warn("Could not add permission " + e); - } - }, - - // -- API -- - - signIn: function(aEmail, aPassword, aFetchKeys) { - return this._signInSignUp("signIn", aEmail, aPassword, aFetchKeys); - }, - - signUp: function(aEmail, aPassword, aFetchKeys) { - return this._signInSignUp("signUp", aEmail, aPassword, aFetchKeys); - }, - - signOut: function() { - if (!this._activeSession) { - // If there is no cached active session, we try to get it from the - // account storage. - return this.getAccount().then( - result => { - if (!result) { - return Promise.resolve(); - } - return this._signOut(); - } - ); - } - return this._signOut(); - }, - - resendVerificationEmail: function() { - return this._fxAccounts.resendVerificationEmail().then( - (result) => { - return result; - }, - (error) => { - return this._error(ERROR_SERVER_ERROR, error); - } - ); - }, - - getAccount: function() { - // We check first if we have session details cached. - if (this._activeSession) { - // If our cache says that the account is not yet verified, - // we kick off verification before returning what we have. - if (!this._activeSession.verified) { - this.verificationStatus(this._activeSession); - } - log.debug("Account " + JSON.stringify(this._user)); - return Promise.resolve(this._user); - } - - // If no cached information, we try to get it from the persistent storage. - return this._fxAccounts.getSignedInUser().then( - user => { - if (!user || !user.email) { - log.debug("No signed in account"); - return Promise.resolve(null); - } - - this._activeSession = user; - // If we get a stored information of a not yet verified account, - // we kick off verification before returning what we have. - if (!user.verified) { - this.verificationStatus(user); - // Trying to get the profile for unverified users will fail, so we - // don't even try in that case. - log.debug("Account ", this._user); - return Promise.resolve(this._user); - } - - return this._fxAccounts.getSignedInUserProfile().then(profile => { - if (profile) { - this._activeSession.profile = profile; - } - log.debug("Account ", this._user); - return Promise.resolve(this._user); - }).catch(error => { - // FxAccounts logs already inform about the error. - log.debug("Account ", this._user); - return Promise.resolve(this._user); - }); - } - ); - }, - - queryAccount: function(aEmail) { - log.debug("queryAccount " + aEmail); - if (Services.io.offline) { - return this._error(ERROR_OFFLINE); - } - - let deferred = Promise.defer(); - - if (!aEmail) { - return this._error(ERROR_INVALID_EMAIL); - } - - let client = this._getFxAccountsClient(); - return client.accountExists(aEmail).then( - result => { - log.debug("Account " + result ? "" : "does not" + " exists"); - let error = this._getError(result); - if (error) { - return this._error(error, result); - } - - return Promise.resolve({ - registered: result - }); - }, - reason => { this._serverError(reason); } - ); - }, - - verificationStatus: function() { - log.debug("verificationStatus"); - if (!this._activeSession || !this._activeSession.sessionToken) { - this._error(ERROR_NO_TOKEN_SESSION); - } - - // There is no way to unverify an already verified account, so we just - // return the account details of a verified account - if (this._activeSession.verified) { - log.debug("Account already verified"); - return; - } - - if (Services.io.offline) { - log.warn("Offline; skipping verification."); - return; - } - - let client = this._getFxAccountsClient(); - client.recoveryEmailStatus(this._activeSession.sessionToken).then( - data => { - let error = this._getError(data); - if (error) { - this._error(error, data); - } - // If the verification status has changed, update state. - if (this._activeSession.verified != data.verified) { - this._activeSession.verified = data.verified; - this._fxAccounts.setSignedInUser(this._activeSession); - this._fxAccounts.getSignedInUserProfile().then(profile => { - if (profile) { - this._activeSession.profile = profile; - } - }).catch(error => { - // FxAccounts logs already inform about the error. - }); - } - log.debug(JSON.stringify(this._user)); - }, - reason => { this._serverError(reason); } - ); - }, - - /* - * Try to get an assertion for the given audience. Here we implement - * the heart of the response to navigator.mozId.request() on device. - * (We can also be called via the IAC API, but it's request() that - * makes this method complex.) The state machine looks like this, - * ignoring simple errors: - * If no one is signed in, and we aren't suppressing the UI: - * trigger the sign in flow. - * else if we were asked to refresh and the grace period is up: - * trigger the refresh flow. - * else: - * request user permission to share an assertion if we don't have it - * already and ask the core code for an assertion, which might itself - * trigger either the sign in or refresh flows (if our account - * changed on the server). - * - * aOptions can include: - * refreshAuthentication - (bool) Force re-auth. - * silent - (bool) Prevent any UI interaction. - * I.e., try to get an automatic assertion. - */ - getAssertion: function(aAudience, aPrincipal, aOptions) { - if (!aAudience) { - return this._error(ERROR_INVALID_AUDIENCE); - } - - let principal = aPrincipal; - log.debug("FxAccountsManager.getAssertion() aPrincipal: ", - principal.origin, principal.appId, - principal.isInIsolatedMozBrowserElement); - - return this.getAccount().then( - user => { - if (user) { - // Three have-user cases to consider. First: are we unverified? - if (!user.verified) { - return this._error(ERROR_UNVERIFIED_ACCOUNT, { - user: user - }); - } - // Second case: do we need to refresh? - if (aOptions && - (typeof(aOptions.refreshAuthentication) != "undefined")) { - let gracePeriod = aOptions.refreshAuthentication; - if (typeof(gracePeriod) !== "number" || isNaN(gracePeriod)) { - return this._error(ERROR_INVALID_REFRESH_AUTH_VALUE); - } - // Forcing refreshAuth to silent is a contradiction in terms, - // though it might succeed silently if we didn't reject here. - if (aOptions.silent) { - return this._error(ERROR_NO_SILENT_REFRESH_AUTH); - } - let secondsSinceAuth = (Date.now() / 1000) - - this._activeSession.authAt; - if (secondsSinceAuth > gracePeriod) { - return this._refreshAuthentication(aAudience, user.email, - principal, - false /* logoutOnFailure */); - } - } - // Third case: we are all set *locally*. Probably we just return - // the assertion, but the attempt might lead to the server saying - // we are deleted or have a new password, which will trigger a flow. - // Also we need to check if we have permission to get the assertion, - // otherwise we need to show the forceAuth UI to let the user know - // that the RP with no fxa permissions is trying to obtain an - // assertion. Once the user authenticates herself in the forceAuth UI - // the permission will be remembered by default. - let permission = permissionManager.testPermissionFromPrincipal( - principal, - FXACCOUNTS_PERMISSION - ); - if (permission == Ci.nsIPermissionManager.PROMPT_ACTION && - !this._refreshing) { - return this._refreshAuthentication(aAudience, user.email, - principal, - false /* logoutOnFailure */); - } else if (permission == Ci.nsIPermissionManager.DENY_ACTION && - !this._refreshing) { - return this._error(ERROR_PERMISSION_DENIED); - } else if (this._refreshing) { - // If we are blocked asking for a password we should not continue - // the getAssertion process. - return Promise.resolve(null); - } - return this._getAssertion(aAudience, principal); - } - log.debug("No signed in user"); - if (aOptions && aOptions.silent) { - return Promise.resolve(null); - } - return this._uiRequest(UI_REQUEST_SIGN_IN_FLOW, aAudience, principal); - } - ); - }, - - getKeys: function() { - let syncEnabled = false; - try { - syncEnabled = Services.prefs.getBoolPref("services.sync.enabled"); - } catch(e) { - dump("Sync is disabled, so you won't get the keys. " + e + "\n"); - } - - if (!syncEnabled) { - return Promise.reject(ERROR_SYNC_DISABLED); - } - - return this.getAccount().then( - user => { - if (!user) { - log.debug("No signed in user"); - return Promise.resolve(null); - } - - if (!user.verified) { - return this._error(ERROR_UNVERIFIED_ACCOUNT, { - user: user - }); - } - - return this._fxAccounts.getKeys(); - } - ); - } -}; - -FxAccountsManager.init(); diff --git a/services/fxaccounts/FxAccountsProfileClient.jsm b/services/fxaccounts/FxAccountsProfileClient.jsm index 37115a3fa..1e5edc634 100644 --- a/services/fxaccounts/FxAccountsProfileClient.jsm +++ b/services/fxaccounts/FxAccountsProfileClient.jsm @@ -89,7 +89,7 @@ this.FxAccountsProfileClient.prototype = { try { return (yield this._rawRequest(path, method, token)); } catch (ex) { - if (!ex instanceof FxAccountsProfileClientError || ex.code != 401) { + if (!(ex instanceof FxAccountsProfileClientError) || ex.code != 401) { throw ex; } // If this object was instantiated with a token then we don't refresh it. @@ -105,7 +105,7 @@ this.FxAccountsProfileClient.prototype = { try { return (yield this._rawRequest(path, method, token)); } catch (ex) { - if (!ex instanceof FxAccountsProfileClientError || ex.code != 401) { + if (!(ex instanceof FxAccountsProfileClientError) || ex.code != 401) { throw ex; } log.info("Retry fetching the profile still returned a 401 - revoking our token and failing"); diff --git a/services/fxaccounts/FxAccountsStorage.jsm b/services/fxaccounts/FxAccountsStorage.jsm index 021763b92..4362cdf5b 100644 --- a/services/fxaccounts/FxAccountsStorage.jsm +++ b/services/fxaccounts/FxAccountsStorage.jsm @@ -17,6 +17,8 @@ Cu.import("resource://gre/modules/FxAccountsCommon.js"); Cu.import("resource://gre/modules/osfile.jsm"); Cu.import("resource://services-common/utils.js"); +var haveLoginManager = true; + // A helper function so code can check what fields are able to be stored by // the storage manager without having a reference to a manager instance. function FxAccountsStorageManagerCanStoreField(fieldName) { @@ -403,7 +405,7 @@ this.FxAccountsStorageManager.prototype = { try { yield this.secureStorage.set(this.cachedPlain.uid, toWriteSecure); } catch (ex) { - if (!ex instanceof this.secureStorage.STORAGE_LOCKED) { + if (!(ex instanceof this.secureStorage.STORAGE_LOCKED)) { throw ex; } // This shouldn't be possible as once it is unlocked it can't be @@ -602,8 +604,3 @@ LoginManagerStorage.prototype = { }), } -// A global variable to indicate if the login manager is available - it doesn't -// exist on b2g. Defined here as the use of preprocessor directives skews line -// numbers in the runtime, meaning stack-traces etc end up off by a few lines. -// Doing it at the end of the file makes that less of a pita. -var haveLoginManager = !AppConstants.MOZ_B2G; diff --git a/services/fxaccounts/moz.build b/services/fxaccounts/moz.build index 30c8944c2..b1cd3b59c 100644 --- a/services/fxaccounts/moz.build +++ b/services/fxaccounts/moz.build @@ -30,6 +30,3 @@ EXTRA_JS_MODULES += [ 'FxAccountsWebChannel.jsm', ] -# For now, we will only be using the FxA manager in B2G. -if CONFIG['MOZ_B2G']: - EXTRA_JS_MODULES += ['FxAccountsManager.jsm'] diff --git a/services/fxaccounts/tests/xpcshell/test_oauth_grant_client.js b/services/fxaccounts/tests/xpcshell/test_oauth_grant_client.js index 244b79a5e..710a65ee5 100644 --- a/services/fxaccounts/tests/xpcshell/test_oauth_grant_client.js +++ b/services/fxaccounts/tests/xpcshell/test_oauth_grant_client.js @@ -143,7 +143,7 @@ add_test(function serverErrorResponse () { add_test(function networkErrorResponse () { let client = new FxAccountsOAuthGrantClient({ - serverURL: "http://", + serverURL: "http://domain.dummy", client_id: "abc123" }); Services.prefs.setBoolPref("identity.fxaccounts.skipDeviceRegistration", true); diff --git a/services/fxaccounts/tests/xpcshell/test_profile_client.js b/services/fxaccounts/tests/xpcshell/test_profile_client.js index 2243da3aa..20ff6efc6 100644 --- a/services/fxaccounts/tests/xpcshell/test_profile_client.js +++ b/services/fxaccounts/tests/xpcshell/test_profile_client.js @@ -268,7 +268,7 @@ add_test(function server401ResponsePersists () { add_test(function networkErrorResponse () { let client = new FxAccountsProfileClient({ - serverURL: "http://", + serverURL: "http://domain.dummy", fxa: mockFxa, }); client.fetchProfile() diff --git a/services/sync/SyncComponents.manifest b/services/sync/SyncComponents.manifest index 6493bb224..c58286277 100644 --- a/services/sync/SyncComponents.manifest +++ b/services/sync/SyncComponents.manifest @@ -3,7 +3,8 @@ # Thus we restrict it to these apps: # # b2g: {3c2e2abc-06d4-11e1-ac3b-374f68613e61} -# browser: {ec8030f7-c20a-464f-9b0e-13a3a9e97384} +# basilisk: {ec8030f7-c20a-464f-9b0e-13a3a9e97384} +# pale moon: {8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} # mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110} # mobile/xul: {a23983c0-fd0e-11dc-95ff-0800200c9a66} # suite (comm): {92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a} @@ -12,7 +13,7 @@ # Weave.js component {74b89fb0-f200-4ae8-a3ec-dd164117f6de} Weave.js contract @mozilla.org/weave/service;1 {74b89fb0-f200-4ae8-a3ec-dd164117f6de} -category app-startup WeaveService service,@mozilla.org/weave/service;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66} application={92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a} application={99bceaaa-e3c6-48c1-b981-ef9b46b67d60} application={d1bfe7d9-c01e-4237-998b-7b5f960a4314} +category app-startup WeaveService service,@mozilla.org/weave/service;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66} application={92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a} application={99bceaaa-e3c6-48c1-b981-ef9b46b67d60} application={d1bfe7d9-c01e-4237-998b-7b5f960a4314} component {d28f8a0b-95da-48f4-b712-caf37097be41} Weave.js contract @mozilla.org/network/protocol/about;1?what=sync-log {d28f8a0b-95da-48f4-b712-caf37097be41} diff --git a/services/sync/Weave.js b/services/sync/Weave.js index 4d79144e3..a414fa083 100644 --- a/services/sync/Weave.js +++ b/services/sync/Weave.js @@ -72,10 +72,12 @@ WeaveService.prototype = { Ci.nsISupportsWeakReference]), ensureLoaded: function () { +#ifndef MC_PALEMOON // If we are loaded and not using FxA, load the migration module. if (!this.fxAccountsEnabled) { Cu.import("resource://services-sync/FxaMigrator.jsm"); } +#endif Components.utils.import("resource://services-sync/main.js"); @@ -103,6 +105,9 @@ WeaveService.prototype = { * @return bool */ get fxAccountsEnabled() { +#ifdef MC_PALEMOON + return false; +#else try { // Old sync guarantees '@' will never appear in the username while FxA // uses the FxA email address - so '@' is the flag we use. @@ -111,6 +116,7 @@ WeaveService.prototype = { } catch (_) { return true; // No username == only allow FxA to be configured. } +#endif }, /** diff --git a/services/sync/locales/en-US/errors.properties b/services/sync/locales/en-US/errors.properties index 3dfa074bc..f67f5ea1c 100644 --- a/services/sync/locales/en-US/errors.properties +++ b/services/sync/locales/en-US/errors.properties @@ -12,7 +12,7 @@ error.login.reason.server = Server incorrectly configured error.sync.failed_partial = One or more data types could not be synced # LOCALIZATION NOTE (error.sync.reason.serverMaintenance): We removed the extraneous period from this string -error.sync.reason.serverMaintenance = Firefox Sync server maintenance is underway, syncing will resume automatically +error.sync.reason.serverMaintenance = Sync server maintenance is underway, syncing will resume automatically invalid-captcha = Incorrect words, try again weak-password = Use a stronger password diff --git a/services/sync/locales/en-US/sync.properties b/services/sync/locales/en-US/sync.properties index d00183118..a1a6f76b2 100644 --- a/services/sync/locales/en-US/sync.properties +++ b/services/sync/locales/en-US/sync.properties @@ -12,14 +12,33 @@ lastSync2.label = Last sync: %S # not configured. signInToSync.description = Sign In To Sync +error.login.title = Error While Signing In +error.login.description = Sync encountered an error while connecting: %1$S. Please try again. +error.login.prefs.label = Preferences… +error.login.prefs.accesskey = P +# should decide if we're going to show this +error.logout.title = Error While Signing Out +error.logout.description = Sync encountered an error while connecting. It's probably ok, and you don't have to do anything about it. error.sync.title = Error While Syncing error.sync.description = Sync encountered an error while syncing: %1$S. Sync will automatically retry this action. +error.sync.prolonged_failure = Sync has not been able to complete during the last %1$S days. Please check your network settings. +error.sync.serverStatusButton.label = Server Status +error.sync.serverStatusButton.accesskey = V +error.sync.needUpdate.description = You need to update Sync to continue syncing your data. +error.sync.needUpdate.label = Update Sync +error.sync.needUpdate.accesskey = U +error.sync.tryAgainButton.label = Sync Now +error.sync.tryAgainButton.accesskey = S +warning.sync.quota.label = Approaching Server Quota +warning.sync.quota.description = You are approaching the server quota. Please review which data to sync. +error.sync.viewQuotaButton.label = View Quota +error.sync.viewQuotaButton.accesskey = V warning.sync.eol.label = Service Shutting Down -# %1: the app name (Firefox) -warning.sync.eol.description = Your Firefox Sync service is shutting down soon. Upgrade %1$S to keep syncing. +# %1: the app name (Basilisk) +warning.sync.eol.description = Your Sync service is shutting down soon. Upgrade %1$S to keep syncing. error.sync.eol.label = Service Unavailable -# %1: the app name (Firefox) -error.sync.eol.description = Your Firefox Sync service is no longer available. You need to upgrade %1$S to keep syncing. +# %1: the app name (Basilisk) +error.sync.eol.description = Your Sync service is no longer available. You need to upgrade %1$S to keep syncing. sync.eol.learnMore.label = Learn more sync.eol.learnMore.accesskey = L diff --git a/services/sync/modules/main.js b/services/sync/modules/main.js index af3399e7a..e8e705e72 100644 --- a/services/sync/modules/main.js +++ b/services/sync/modules/main.js @@ -8,6 +8,7 @@ this.Weave = {}; Components.utils.import("resource://services-sync/constants.js", Weave); var lazies = { "jpakeclient.js": ["JPAKEClient", "SendCredentialsController"], + "notifications.js": ["Notifications", "Notification", "NotificationButton"], "service.js": ["Service"], "status.js": ["Status"], "util.js": ['Utils', 'Svc'] diff --git a/services/sync/modules/notifications.js b/services/sync/modules/notifications.js new file mode 100644 index 000000000..72187a4ce --- /dev/null +++ b/services/sync/modules/notifications.js @@ -0,0 +1,131 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +this.EXPORTED_SYMBOLS = ["Notifications", "Notification", "NotificationButton"]; + +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cr = Components.results; +var Cu = Components.utils; + +Cu.import("resource://services-common/observers.js"); +Cu.import("resource://gre/modules/Log.jsm"); +Cu.import("resource://services-sync/util.js"); + +this.Notifications = { + // Match the referenced values in toolkit/content/widgets/notification.xml. + get PRIORITY_INFO() { return 1; }, // PRIORITY_INFO_LOW + get PRIORITY_WARNING() { return 4; }, // PRIORITY_WARNING_LOW + get PRIORITY_ERROR() { return 7; }, // PRIORITY_CRITICAL_LOW + + // FIXME: instead of making this public, dress the Notifications object + // to behave like an iterator (using generators?) and have callers access + // this array through the Notifications object. + notifications: [], + + _observers: [], + + // XXX Should we have a helper method for adding a simple notification? + // I.e. something like |function notify(title, description, priority)|. + + add: function Notifications_add(notification) { + this.notifications.push(notification); + Observers.notify("weave:notification:added", notification, null); + }, + + remove: function Notifications_remove(notification) { + let index = this.notifications.indexOf(notification); + + if (index != -1) { + this.notifications.splice(index, 1); + Observers.notify("weave:notification:removed", notification, null); + } + }, + + /** + * Replace an existing notification. + */ + replace: function Notifications_replace(oldNotification, newNotification) { + let index = this.notifications.indexOf(oldNotification); + + if (index != -1) + this.notifications.splice(index, 1, newNotification); + else { + this.notifications.push(newNotification); + // XXX Should we throw because we didn't find the existing notification? + // XXX Should we notify observers about weave:notification:added? + } + + // XXX Should we notify observers about weave:notification:replaced? + }, + + /** + * Remove all notifications that match a title. If no title is provided, all + * notifications are removed. + * + * @param title [optional] + * Title of notifications to remove; falsy value means remove all + */ + removeAll: function Notifications_removeAll(title) { + this.notifications.filter(old => (old.title == title || !title)). + forEach(old => { this.remove(old); }, this); + }, + + // replaces all existing notifications with the same title as the new one + replaceTitle: function Notifications_replaceTitle(notification) { + this.removeAll(notification.title); + this.add(notification); + } +}; + + +/** + * A basic notification. Subclass this to create more complex notifications. + */ +this.Notification = +function Notification(title, description, iconURL, priority, buttons, link) { + this.title = title; + this.description = description; + + if (iconURL) + this.iconURL = iconURL; + + if (priority) + this.priority = priority; + + if (buttons) + this.buttons = buttons; + + if (link) + this.link = link; +} + +// We set each prototype property individually instead of redefining +// the entire prototype to avoid blowing away existing properties +// of the prototype like the the "constructor" property, which we use +// to bind notification objects to their XBL representations. +Notification.prototype.priority = Notifications.PRIORITY_INFO; +Notification.prototype.iconURL = null; +Notification.prototype.buttons = []; + +/** + * A button to display in a notification. + */ +this.NotificationButton = + function NotificationButton(label, accessKey, callback) { + function callbackWrapper() { + try { + callback.apply(this, arguments); + } catch (e) { + let logger = Log.repository.getLogger("Sync.Notifications"); + logger.error("An exception occurred: " + Utils.exceptionStr(e)); + logger.info(Utils.stackTrace(e)); + throw e; + } + } + + this.label = label; + this.accessKey = accessKey; + this.callback = callbackWrapper; +} diff --git a/services/sync/modules/record.js b/services/sync/modules/record.js index 02f7f281a..f7a69d9ef 100644 --- a/services/sync/modules/record.js +++ b/services/sync/modules/record.js @@ -43,7 +43,7 @@ WBORecord.prototype = { // Get thyself from your URI, then deserialize. // Set thine 'response' field. fetch: function fetch(resource) { - if (!resource instanceof Resource) { + if (!(resource instanceof Resource)) { throw new Error("First argument must be a Resource instance."); } @@ -56,7 +56,7 @@ WBORecord.prototype = { }, upload: function upload(resource) { - if (!resource instanceof Resource) { + if (!(resource instanceof Resource)) { throw new Error("First argument must be a Resource instance."); } diff --git a/services/sync/modules/service.js b/services/sync/modules/service.js index b0eb0f41d..f8c64b0fa 100644 --- a/services/sync/modules/service.js +++ b/services/sync/modules/service.js @@ -455,7 +455,7 @@ Sync11Service.prototype = { this.clientsEngine = new ClientEngine(this); for (let name of engines) { - if (!name in ENGINE_MODULES) { + if (!(name in ENGINE_MODULES)) { this._log.info("Do not know about engine: " + name); continue; } @@ -1067,31 +1067,34 @@ Sync11Service.prototype = { // Note: returns false if we failed for a reason other than the server not yet // supporting the api. _fetchServerConfiguration() { - // This is similar to _fetchInfo, but with different error handling. + if (Svc.Prefs.get("APILevel") >= 2) { + // This is similar to _fetchInfo, but with different error handling. + // Only supported by later sync implementations. - let infoURL = this.userBaseURL + "info/configuration"; - this._log.debug("Fetching server configuration", infoURL); - let configResponse; - try { - configResponse = this.resource(infoURL).get(); - } catch (ex) { - // This is probably a network or similar error. - this._log.warn("Failed to fetch info/configuration", ex); - this.errorHandler.checkServerError(ex); - return false; - } + let infoURL = this.userBaseURL + "info/configuration"; + this._log.debug("Fetching server configuration", infoURL); + let configResponse; + try { + configResponse = this.resource(infoURL).get(); + } catch (ex) { + // This is probably a network or similar error. + this._log.warn("Failed to fetch info/configuration", ex); + this.errorHandler.checkServerError(ex); + return false; + } - if (configResponse.status == 404) { - // This server doesn't support the URL yet - that's OK. - this._log.debug("info/configuration returned 404 - using default upload semantics"); - } else if (configResponse.status != 200) { - this._log.warn(`info/configuration returned ${configResponse.status} - using default configuration`); - this.errorHandler.checkServerError(configResponse); - return false; - } else { - this.serverConfiguration = configResponse.obj; + if (configResponse.status == 404) { + // This server doesn't support the URL yet - that's OK. + this._log.debug("info/configuration returned 404 - using default upload semantics"); + } else if (configResponse.status != 200) { + this._log.warn(`info/configuration returned ${configResponse.status} - using default configuration`); + this.errorHandler.checkServerError(configResponse); + return false; + } else { + this.serverConfiguration = configResponse.obj; + } + this._log.trace("info/configuration for this server", this.serverConfiguration); } - this._log.trace("info/configuration for this server", this.serverConfiguration); return true; }, diff --git a/services/sync/moz.build b/services/sync/moz.build index c4d3607b5..83c39274a 100644 --- a/services/sync/moz.build +++ b/services/sync/moz.build @@ -13,6 +13,9 @@ XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini'] EXTRA_COMPONENTS += [ 'SyncComponents.manifest', +] + +EXTRA_PP_COMPONENTS += [ 'Weave.js', ] @@ -28,11 +31,11 @@ EXTRA_JS_MODULES['services-sync'] += [ 'modules/jpakeclient.js', 'modules/keys.js', 'modules/main.js', + 'modules/notifications.js', 'modules/policies.js', 'modules/record.js', 'modules/resource.js', 'modules/rest.js', - 'modules/service.js', 'modules/status.js', 'modules/SyncedTabs.jsm', 'modules/telemetry.js', @@ -42,10 +45,11 @@ EXTRA_JS_MODULES['services-sync'] += [ EXTRA_PP_JS_MODULES['services-sync'] += [ 'modules/constants.js', + 'modules/service.js', ] # Definitions used by constants.js -DEFINES['weave_version'] = '1.54.0' +DEFINES['weave_version'] = '1.54.1' DEFINES['weave_id'] = '{340c2bbc-ce74-4362-90b5-7c26312808ef}' EXTRA_JS_MODULES['services-sync'].engines += [ diff --git a/services/sync/services-sync.js b/services/sync/services-sync.js index f4167c1ce..ebac4412b 100644 --- a/services/sync/services-sync.js +++ b/services/sync/services-sync.js @@ -12,6 +12,7 @@ pref("services.sync.syncKeyHelpURL", "https://services.mozilla.com/help/synckey" pref("services.sync.lastversion", "firstrun"); pref("services.sync.sendVersionInfo", true); +pref("services.sync.APILevel", 2); pref("services.sync.scheduler.eolInterval", 604800); // 1 week pref("services.sync.scheduler.idleInterval", 3600); // 1 hour diff --git a/services/sync/tests/unit/test_service_login.js b/services/sync/tests/unit/test_service_login.js index 42c163915..2ecb0a377 100644 --- a/services/sync/tests/unit/test_service_login.js +++ b/services/sync/tests/unit/test_service_login.js @@ -206,7 +206,7 @@ add_test(function test_login_on_sync() { _("Old passphrase function is " + oldGetter); Service.identity.__defineGetter__("syncKey", function() { - throw "User canceled Master Password entry"; + throw "User canceled master password entry"; }); let oldClearSyncTriggers = Service.scheduler.clearSyncTriggers; diff --git a/services/sync/tests/unit/test_syncscheduler.js b/services/sync/tests/unit/test_syncscheduler.js index b066eae82..730a3f996 100644 --- a/services/sync/tests/unit/test_syncscheduler.js +++ b/services/sync/tests/unit/test_syncscheduler.js @@ -535,7 +535,7 @@ add_task(function* test_autoconnect_mp_locked() { delete Service.identity.syncKey; Service.identity.__defineGetter__("syncKey", function() { _("Faking Master Password entry cancelation."); - throw "User canceled Master Password entry"; + throw "User canceled master password entry"; }); let deferred = Promise.defer(); diff --git a/services/sync/tests/unit/xpcshell.ini b/services/sync/tests/unit/xpcshell.ini index 5a90652b8..609003ce9 100644 --- a/services/sync/tests/unit/xpcshell.ini +++ b/services/sync/tests/unit/xpcshell.ini @@ -13,7 +13,6 @@ support-files = sync_ping_schema.json systemaddon-search.xml !/services/common/tests/unit/head_helpers.js - !/toolkit/mozapps/webextensions/test/xpcshell/head_addons.js !/toolkit/components/webextensions/test/xpcshell/head_sync.js # The manifest is roughly ordered from low-level to high-level. When making diff --git a/services/sync/tps/extensions/mozmill/resource/driver/controller.js b/services/sync/tps/extensions/mozmill/resource/driver/controller.js index a378ce51f..8d66a41ae 100644 --- a/services/sync/tps/extensions/mozmill/resource/driver/controller.js +++ b/services/sync/tps/extensions/mozmill/resource/driver/controller.js @@ -978,7 +978,7 @@ function browserAdditions (controller) { }, "Timeout", timeout, aInterval); } catch (ex) { - if (!ex instanceof errors.TimeoutError) { + if (!(ex instanceof errors.TimeoutError)) { throw ex; } timed_out = true; diff --git a/services/sync/tps/extensions/mozmill/resource/modules/assertions.js b/services/sync/tps/extensions/mozmill/resource/modules/assertions.js index c9991acf0..c76f95747 100644 --- a/services/sync/tps/extensions/mozmill/resource/modules/assertions.js +++ b/services/sync/tps/extensions/mozmill/resource/modules/assertions.js @@ -659,7 +659,7 @@ Expect.prototype.waitFor = function Expect_waitFor(aCallback, aMessage, aTimeout Assert.prototype.waitFor.apply(this, arguments); } catch (ex) { - if (!ex instanceof errors.AssertionError) { + if (!(ex instanceof errors.AssertionError)) { throw ex; } message = ex.message; |