Bug 1309030 - Remove DOM/identity and related code. r=Ehsan

MozReview-Commit-ID: 2v2ZCSoX4Mz

--HG--
extra : rebase_source : 72cbccd1f59758e290294bfcca56523372978120
This commit is contained in:
Michelangelo De Simone 2016-10-10 13:55:16 -07:00
parent 5fcdb45cbd
commit 6b31140392
22 changed files with 1 additions and 2434 deletions

View File

@ -791,9 +791,6 @@ pref("memory.dump_reports_on_oom", false);
pref("layout.framevisibility.numscrollportwidths", 1);
pref("layout.framevisibility.numscrollportheights", 1);
// Enable native identity (persona/browserid)
pref("dom.identity.enabled", true);
// Wait up to this much milliseconds when orientation changed
pref("layers.orientation.sync.timeout", 1000);
@ -962,7 +959,7 @@ pref("layout.accessiblecaret.timeout_ms", 3000);
// Hide carets and text selection dialog during scrolling.
pref("layout.accessiblecaret.always_show_when_scrolling", false);
// Enable sync and mozId with Firefox Accounts.
// Enable sync with Firefox Accounts.
pref("services.sync.fxaccounts.enabled", true);
pref("identity.fxaccounts.enabled", true);

View File

@ -1,478 +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/. */
"use strict";
const {classes: Cc,
interfaces: Ci,
utils: Cu,
results: Cr} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const PREF_FXA_ENABLED = "identity.fxaccounts.enabled";
const FXA_PERMISSION = "firefox-accounts";
// This is the parent process corresponding to nsDOMIdentity.
this.EXPORTED_SYMBOLS = ["DOMIdentity"];
XPCOMUtils.defineLazyModuleGetter(this, "objectCopy",
"resource://gre/modules/identity/IdentityUtils.jsm");
/* jshint ignore:start */
XPCOMUtils.defineLazyModuleGetter(this, "IdentityService",
#ifdef MOZ_B2G_VERSION
"resource://gre/modules/identity/MinimalIdentity.jsm");
#else
"resource://gre/modules/identity/Identity.jsm");
#endif
/* jshint ignore:end */
XPCOMUtils.defineLazyModuleGetter(this, "FirefoxAccounts",
"resource://gre/modules/identity/FirefoxAccounts.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "makeMessageObject",
"resource://gre/modules/identity/IdentityUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this,
"Logger",
"resource://gre/modules/identity/LogUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
"@mozilla.org/parentprocessmessagemanager;1",
"nsIMessageListenerManager");
XPCOMUtils.defineLazyServiceGetter(this, "permissionManager",
"@mozilla.org/permissionmanager;1",
"nsIPermissionManager");
function log(...aMessageArgs) {
Logger.log.apply(Logger, ["DOMIdentity"].concat(aMessageArgs));
}
function IDDOMMessage(aOptions) {
objectCopy(aOptions, this);
}
function _sendAsyncMessage(identifier, message) {
if (this._mm) {
try {
this._mm.sendAsyncMessage(identifier, message);
} catch(err) {
// We may receive a NS_ERROR_NOT_INITIALIZED if the target window has
// been closed. This can legitimately happen if an app has been killed
// while we are in the midst of a sign-in flow.
if (err.result == Cr.NS_ERROR_NOT_INITIALIZED) {
log("Cannot sendAsyncMessage because the recipient frame has closed");
return;
}
log("ERROR: sendAsyncMessage: " + err);
}
}
};
function IDPProvisioningContext(aID, aOrigin, aTargetMM) {
this._id = aID;
this._origin = aOrigin;
this._mm = aTargetMM;
}
IDPProvisioningContext.prototype = {
get id() { return this._id; },
get origin() { return this._origin; },
sendAsyncMessage: _sendAsyncMessage,
doBeginProvisioningCallback: function IDPPC_doBeginProvCB(aID, aCertDuration) {
let message = new IDDOMMessage({id: this.id});
message.identity = aID;
message.certDuration = aCertDuration;
this.sendAsyncMessage("Identity:IDP:CallBeginProvisioningCallback",
message);
},
doGenKeyPairCallback: function IDPPC_doGenKeyPairCallback(aPublicKey) {
log("doGenKeyPairCallback");
let message = new IDDOMMessage({id: this.id});
message.publicKey = aPublicKey;
this.sendAsyncMessage("Identity:IDP:CallGenKeyPairCallback", message);
},
doError: function(msg) {
log("Provisioning ERROR: " + msg);
}
};
function IDPAuthenticationContext(aID, aOrigin, aTargetMM) {
this._id = aID;
this._origin = aOrigin;
this._mm = aTargetMM;
}
IDPAuthenticationContext.prototype = {
get id() { return this._id; },
get origin() { return this._origin; },
sendAsyncMessage: _sendAsyncMessage,
doBeginAuthenticationCallback: function IDPAC_doBeginAuthCB(aIdentity) {
let message = new IDDOMMessage({id: this.id});
message.identity = aIdentity;
this.sendAsyncMessage("Identity:IDP:CallBeginAuthenticationCallback",
message);
},
doError: function IDPAC_doError(msg) {
log("Authentication ERROR: " + msg);
}
};
function RPWatchContext(aOptions, aTargetMM, aPrincipal) {
objectCopy(aOptions, this);
// id and origin are required
if (! (this.id && this.origin)) {
throw new Error("id and origin are required for RP watch context");
}
this.principal = aPrincipal;
// default for no loggedInUser is undefined, not null
this.loggedInUser = aOptions.loggedInUser;
// Maybe internal. For hosted b2g identity shim.
this._internal = aOptions._internal;
this._mm = aTargetMM;
}
RPWatchContext.prototype = {
sendAsyncMessage: _sendAsyncMessage,
doLogin: function RPWatchContext_onlogin(aAssertion, aMaybeInternalParams) {
log("doLogin: " + this.id);
let message = new IDDOMMessage({id: this.id, assertion: aAssertion});
if (aMaybeInternalParams) {
message._internalParams = aMaybeInternalParams;
}
this.sendAsyncMessage("Identity:RP:Watch:OnLogin", message);
},
doLogout: function RPWatchContext_onlogout() {
log("doLogout: " + this.id);
let message = new IDDOMMessage({id: this.id});
this.sendAsyncMessage("Identity:RP:Watch:OnLogout", message);
},
doReady: function RPWatchContext_onready() {
log("doReady: " + this.id);
let message = new IDDOMMessage({id: this.id});
this.sendAsyncMessage("Identity:RP:Watch:OnReady", message);
},
doCancel: function RPWatchContext_oncancel() {
log("doCancel: " + this.id);
let message = new IDDOMMessage({id: this.id});
this.sendAsyncMessage("Identity:RP:Watch:OnCancel", message);
},
doError: function RPWatchContext_onerror(aMessage) {
log("doError: " + this.id + ": " + JSON.stringify(aMessage));
let message = new IDDOMMessage({id: this.id, message: aMessage});
this.sendAsyncMessage("Identity:RP:Watch:OnError", message);
}
};
this.DOMIdentity = {
/*
* When relying parties (RPs) invoke the watch() method, they can request
* to use Firefox Accounts as their auth service or BrowserID (the default).
* For each RP, we create an RPWatchContext to store the parameters given to
* watch(), and to provide hooks to invoke the onlogin(), onlogout(), etc.
* callbacks held in the nsDOMIdentity state.
*
* The serviceContexts map associates the window ID of the RP with the
* context object. The mmContexts map associates a message manager with a
* window ID. We use the mmContexts map when child-process-shutdown is
* observed, and all we have is a message manager to identify the window in
* question.
*/
_serviceContexts: new Map(),
_mmContexts: new Map(),
/*
* Mockable, for testing
*/
_mockIdentityService: null,
get IdentityService() {
if (this._mockIdentityService) {
log("Using a mocked identity service");
return this._mockIdentityService;
}
return IdentityService;
},
/*
* Create a new RPWatchContext, and update the context maps.
*/
newContext: function(message, targetMM, principal) {
let context = new RPWatchContext(message, targetMM, principal);
this._serviceContexts.set(message.id, context);
this._mmContexts.set(targetMM, message.id);
return context;
},
/*
* Get the identity service used for an RP.
*
* @object message
* A message received from an RP. Will include the id of the window
* whence the message originated.
*
* Returns FirefoxAccounts or IdentityService
*/
getService: function(message) {
if (!this._serviceContexts.has(message.id)) {
log("ERROR: getService called before newContext for " + message.id);
return null;
}
let context = this._serviceContexts.get(message.id);
if (context.wantIssuer == "firefox-accounts") {
if (Services.prefs.getPrefType(PREF_FXA_ENABLED) === Ci.nsIPrefBranch.PREF_BOOL
&& Services.prefs.getBoolPref(PREF_FXA_ENABLED)) {
return FirefoxAccounts;
}
log("WARNING: Firefox Accounts is not enabled; Defaulting to BrowserID");
}
return this.IdentityService;
},
/*
* Get the RPWatchContext object for a given message manager.
*/
getContextForMM: function(targetMM) {
return this._serviceContexts.get(this._mmContexts.get(targetMM));
},
hasContextForMM: function(targetMM) {
return this._mmContexts.has(targetMM);
},
/*
* Delete the RPWatchContext object for a given message manager. Removes the
* mapping both from _serviceContexts and _mmContexts.
*/
deleteContextForMM: function(targetMM) {
this._serviceContexts.delete(this._mmContexts.get(targetMM));
this._mmContexts.delete(targetMM);
},
hasPermission: function(aMessage) {
// We only check that the firefox accounts permission is present in the
// manifest.
if (aMessage.json && aMessage.json.wantIssuer == "firefox-accounts") {
if (!aMessage.principal) {
return false;
}
let permission =
permissionManager.testPermissionFromPrincipal(aMessage.principal,
FXA_PERMISSION);
return permission != Ci.nsIPermissionManager.UNKNOWN_ACTION &&
permission != Ci.nsIPermissionManager.DENY_ACTION;
}
return true;
},
// nsIMessageListener
receiveMessage: function DOMIdentity_receiveMessage(aMessage) {
let msg = aMessage.json;
// Target is the frame message manager that called us and is
// used to send replies back to the proper window.
let targetMM = aMessage.target;
if (!this.hasPermission(aMessage)) {
throw new Error("PERMISSION_DENIED");
}
switch (aMessage.name) {
// RP
case "Identity:RP:Watch":
this._watch(msg, targetMM, aMessage.principal);
break;
case "Identity:RP:Unwatch":
this._unwatch(msg, targetMM);
break;
case "Identity:RP:Request":
this._request(msg);
break;
case "Identity:RP:Logout":
this._logout(msg);
break;
// IDP
case "Identity:IDP:BeginProvisioning":
this._beginProvisioning(msg, targetMM);
break;
case "Identity:IDP:GenKeyPair":
this._genKeyPair(msg);
break;
case "Identity:IDP:RegisterCertificate":
this._registerCertificate(msg);
break;
case "Identity:IDP:ProvisioningFailure":
this._provisioningFailure(msg);
break;
case "Identity:IDP:BeginAuthentication":
this._beginAuthentication(msg, targetMM);
break;
case "Identity:IDP:CompleteAuthentication":
this._completeAuthentication(msg);
break;
case "Identity:IDP:AuthenticationFailure":
this._authenticationFailure(msg);
break;
case "child-process-shutdown":
// we receive child-process-shutdown if the appliction crashes,
// including if it is crashed by the OS (killed for out-of-memory,
// for example)
this._childProcessShutdown(targetMM);
break;
}
},
// nsIObserver
observe: function DOMIdentity_observe(aSubject, aTopic, aData) {
switch (aTopic) {
case "xpcom-shutdown":
this._unsubscribeListeners();
Services.obs.removeObserver(this, "xpcom-shutdown");
Services.ww.unregisterNotification(this);
break;
}
},
messages: ["Identity:RP:Watch", "Identity:RP:Request", "Identity:RP:Logout",
"Identity:IDP:BeginProvisioning", "Identity:IDP:ProvisioningFailure",
"Identity:IDP:RegisterCertificate", "Identity:IDP:GenKeyPair",
"Identity:IDP:BeginAuthentication",
"Identity:IDP:CompleteAuthentication",
"Identity:IDP:AuthenticationFailure",
"Identity:RP:Unwatch",
"child-process-shutdown"],
// Private.
_init: function DOMIdentity__init() {
Services.ww.registerNotification(this);
Services.obs.addObserver(this, "xpcom-shutdown", false);
this._subscribeListeners();
},
_subscribeListeners: function DOMIdentity__subscribeListeners() {
if (!ppmm) {
return;
}
for (let message of this.messages) {
ppmm.addMessageListener(message, this);
}
},
_unsubscribeListeners: function DOMIdentity__unsubscribeListeners() {
for (let message of this.messages) {
ppmm.removeMessageListener(message, this);
}
ppmm = null;
},
_watch: function DOMIdentity__watch(message, targetMM, principal) {
log("DOMIdentity__watch: " + message.id + " - " + principal);
let context = this.newContext(message, targetMM, principal);
this.getService(message).RP.watch(context);
},
_unwatch: function DOMIdentity_unwatch(message, targetMM) {
log("DOMIDentity__unwatch: " + message.id);
// If watch failed for some reason (e.g., exception thrown because RP did
// not have the right callbacks, we don't want unwatch to throw, because it
// will break the process of releasing the page's resources and leak
// memory.
let service = this.getService(message);
if (service && service.RP) {
service.RP.unwatch(message.id, targetMM);
this.deleteContextForMM(targetMM);
return;
}
log("Can't find a service to unwatch() for " + message.id);
},
_request: function DOMIdentity__request(message) {
let service = this.getService(message);
if (service && service.RP) {
service.RP.request(message.id, message);
return;
}
log("No context in which to call request(); Did you call watch() first?");
},
_logout: function DOMIdentity__logout(message) {
let service = this.getService(message);
if (service && service.RP) {
service.RP.logout(message.id, message.origin, message);
return;
}
log("No context in which to call logout(); Did you call watch() first?");
},
_childProcessShutdown: function DOMIdentity__childProcessShutdown(targetMM) {
if (!this.hasContextForMM(targetMM)) {
return;
}
let service = this.getContextForMM(targetMM);
if (service && service.RP) {
service.RP.childProcessShutdown(targetMM);
}
this.deleteContextForMM(targetMM);
let options = makeMessageObject({messageManager: targetMM, id: null, origin: null});
Services.obs.notifyObservers({wrappedJSObject: options}, "identity-child-process-shutdown", null);
},
_beginProvisioning: function DOMIdentity__beginProvisioning(message, targetMM) {
let context = new IDPProvisioningContext(message.id, message.origin,
targetMM);
this.getService(message).IDP.beginProvisioning(context);
},
_genKeyPair: function DOMIdentity__genKeyPair(message) {
this.getService(message).IDP.genKeyPair(message.id);
},
_registerCertificate: function DOMIdentity__registerCertificate(message) {
this.getService(message).IDP.registerCertificate(message.id, message.cert);
},
_provisioningFailure: function DOMIdentity__provisioningFailure(message) {
this.getService(message).IDP.raiseProvisioningFailure(message.id, message.reason);
},
_beginAuthentication: function DOMIdentity__beginAuthentication(message, targetMM) {
let context = new IDPAuthenticationContext(message.id, message.origin,
targetMM);
this.getService(message).IDP.beginAuthentication(context);
},
_completeAuthentication: function DOMIdentity__completeAuthentication(message) {
this.getService(message).IDP.completeAuthentication(message.id);
},
_authenticationFailure: function DOMIdentity__authenticationFailure(message) {
this.getService(message).IDP.cancelAuthentication(message.id);
}
};
// Object is initialized by nsIDService.js

View File

@ -1,8 +0,0 @@
# nsDOMIdentity.js
component {210853d9-2c97-4669-9761-b1ab9cbf57ef} nsDOMIdentity.js
contract @mozilla.org/identity/manager;1 {210853d9-2c97-4669-9761-b1ab9cbf57ef}
# nsIDService.js (initialization on startup)
component {4e0a0e98-b1d3-4745-a1eb-f815199dd06b} nsIDService.js
contract @mozilla.org/dom/identity/service;1 {4e0a0e98-b1d3-4745-a1eb-f815199dd06b}
category app-startup IDService @mozilla.org/dom/identity/service;1

View File

@ -1,17 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
EXTRA_COMPONENTS += [
'Identity.manifest',
'nsDOMIdentity.js',
'nsIDService.js',
]
EXTRA_PP_JS_MODULES += [
'DOMIdentity.jsm',
]
MOCHITEST_CHROME_MANIFESTS += ['tests/mochitest/chrome.ini']

View File

@ -1,800 +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/. */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
const PREF_DEBUG = "toolkit.identity.debug";
const PREF_ENABLED = "dom.identity.enabled";
// Bug 822450: Workaround for Bug 821740. When testing with marionette,
// relax navigator.id.request's requirement that it be handling native
// events. Synthetic marionette events are ok.
const PREF_SYNTHETIC_EVENTS_OK = "dom.identity.syntheticEventsOk";
// Maximum length of a string that will go through IPC
const MAX_STRING_LENGTH = 2048;
// Maximum number of times navigator.id.request can be called for a document
const MAX_RP_CALLS = 100;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "checkDeprecated",
"resource://gre/modules/identity/IdentityUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "checkRenamed",
"resource://gre/modules/identity/IdentityUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "objectCopy",
"resource://gre/modules/identity/IdentityUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "makeMessageObject",
"resource://gre/modules/identity/IdentityUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
// This is the child process corresponding to nsIDOMIdentity
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1",
"nsIMessageSender");
const ERRORS = {
"ERROR_INVALID_ASSERTION_AUDIENCE":
"Assertion audience may not differ from origin",
"ERROR_REQUEST_WHILE_NOT_HANDLING_USER_INPUT":
"The request() method may only be invoked when handling user input",
};
function nsDOMIdentity() {
}
nsDOMIdentity.prototype = {
// require native events unless syntheticEventsOk is set
get nativeEventsRequired() {
if (Services.prefs.prefHasUserValue(PREF_SYNTHETIC_EVENTS_OK) &&
(Services.prefs.getPrefType(PREF_SYNTHETIC_EVENTS_OK) ===
Ci.nsIPrefBranch.PREF_BOOL)) {
return !Services.prefs.getBoolPref(PREF_SYNTHETIC_EVENTS_OK);
}
return true;
},
reportErrors: function(message) {
let onerror = function() {};
if (this._rpWatcher && this._rpWatcher.onerror) {
onerror = this._rpWatcher.onerror;
}
message.errors.forEach((error) => {
// Report an error string to content
Cu.reportError(ERRORS[error]);
// Report error code to RP callback, if available
onerror(error);
});
},
/**
* Relying Party (RP) APIs
*/
watch: function nsDOMIdentity_watch(aOptions = {}) {
aOptions = Cu.waiveXrays(aOptions);
if (this._rpWatcher) {
// For the initial release of Firefox Accounts, we support callers who
// invoke watch() either for Firefox Accounts, or Persona, but not both.
// In the future, we may wish to support the dual invocation (say, for
// packaged apps so they can sign users in who reject the app's request
// to sign in with their Firefox Accounts identity).
throw new Error("navigator.id.watch was already called");
}
assertCorrectCallbacks(aOptions);
let message = this.DOMIdentityMessage(aOptions);
// loggedInUser vs loggedInEmail
// https://developer.mozilla.org/en-US/docs/DOM/navigator.id.watch
// This parameter, loggedInUser, was renamed from loggedInEmail in early
// September, 2012. Both names will continue to work for the time being,
// but code should be changed to use loggedInUser instead.
checkRenamed(aOptions, "loggedInEmail", "loggedInUser");
// Bad IPC or IDL converts null and undefined to "null" and "undefined".
// We can't assign to aOptions, which complicates the workaround.
message["loggedInUser"] = aOptions["loggedInUser"];
if (message.loggedInUser == "null" || message.loggedInUser == "undefined") {
message.loggedInUser = null;
}
if (message.loggedInUser) {
if (typeof(message.loggedInUser) !== "string") {
throw new Error("loggedInUser must be a String or null");
}
// TODO: Bug 767610 - check email format.
// See HTMLInputElement::IsValidEmailAddress
if (aOptions["loggedInUser"].indexOf("@") == -1
|| aOptions["loggedInUser"].length > MAX_STRING_LENGTH) {
throw new Error("loggedInUser is not valid");
}
}
this._log("loggedInUser: " + message.loggedInUser);
this._rpWatcher = aOptions;
this._rpWatcher.audience = message.audience;
if (message.errors.length) {
this.reportErrors(message);
// We don't delete the rpWatcher object, because we don't want the
// broken client to be able to call watch() any more. It's broken.
return;
}
this._mm.sendAsyncMessage(
"Identity:RP:Watch",
message,
null,
this._window.document.nodePrincipal
);
},
request: function nsDOMIdentity_request(aOptions = {}) {
aOptions = Cu.waiveXrays(aOptions);
this._log("request: " + JSON.stringify(aOptions));
// Has the caller called watch() before this?
if (!this._rpWatcher) {
throw new Error("navigator.id.request called before navigator.id.watch");
}
if (this._rpCalls > MAX_RP_CALLS) {
throw new Error("navigator.id.request called too many times");
}
let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let message = this.DOMIdentityMessage(aOptions);
// We permit calling of request() outside of a user input handler only when
// a certified or privileged app is calling, or when we are handling the
// (deprecated) get() or getVerifiedEmail() calls, which make use of an RP
// context marked as _internal.
if (!aOptions._internal &&
this._appStatus !== Ci.nsIPrincipal.APP_STATUS_CERTIFIED &&
this._appStatus !== Ci.nsIPrincipal.APP_STATUS_PRIVILEGED) {
// If the caller is not special in one of those ways, see if the user has
// preffed on 'syntheticEventsOk' (useful for testing); otherwise, if
// this is a non-native event, reject it.
let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
if (!util.isHandlingUserInput && this.nativeEventsRequired) {
message.errors.push("ERROR_REQUEST_WHILE_NOT_HANDLING_USER_INPUT");
}
}
// Report and fail hard on any errors.
if (message.errors.length) {
this.reportErrors(message);
return;
}
if (aOptions) {
// Optional string properties
let optionalStringProps = ["privacyPolicy", "termsOfService"];
for (let propName of optionalStringProps) {
if (!aOptions[propName] || aOptions[propName] === "undefined")
continue;
if (typeof(aOptions[propName]) !== "string") {
throw new Error(propName + " must be a string representing a URL.");
}
if (aOptions[propName].length > MAX_STRING_LENGTH) {
throw new Error(propName + " is invalid.");
}
message[propName] = aOptions[propName];
}
if (aOptions["oncancel"]
&& typeof(aOptions["oncancel"]) !== "function") {
throw new Error("oncancel is not a function");
} else {
// Store optional cancel callback for later.
this._onCancelRequestCallback = aOptions.oncancel;
}
}
this._rpCalls++;
this._mm.sendAsyncMessage(
"Identity:RP:Request",
message,
null,
this._window.document.nodePrincipal
);
},
logout: function nsDOMIdentity_logout() {
if (!this._rpWatcher) {
throw new Error("navigator.id.logout called before navigator.id.watch");
}
if (this._rpCalls > MAX_RP_CALLS) {
throw new Error("navigator.id.logout called too many times");
}
this._rpCalls++;
let message = this.DOMIdentityMessage();
// Report and fail hard on any errors.
if (message.errors.length) {
this.reportErrors(message);
return;
}
this._mm.sendAsyncMessage(
"Identity:RP:Logout",
message,
null,
this._window.document.nodePrincipal
);
},
/*
* Get an assertion. This function is deprecated. RPs are
* encouraged to use the observer API instead (watch + request).
*/
get: function nsDOMIdentity_get(aCallback, aOptions) {
var opts = {};
aOptions = aOptions || {};
// We use the observer API (watch + request) to implement get().
// Because the caller can call get() and getVerifiedEmail() as
// many times as they want, we lift the restriction that watch() can
// only be called once.
this._rpWatcher = null;
// This flag tells internal_api.js (in the shim) to record in the
// login parameters whether the assertion was acquired silently or
// with user interaction.
opts._internal = true;
opts.privacyPolicy = aOptions.privacyPolicy || undefined;
opts.termsOfService = aOptions.termsOfService || undefined;
opts.privacyURL = aOptions.privacyURL || undefined;
opts.tosURL = aOptions.tosURL || undefined;
opts.siteName = aOptions.siteName || undefined;
opts.siteLogo = aOptions.siteLogo || undefined;
opts.oncancel = function get_oncancel() {
if (aCallback) {
aCallback(null);
aCallback = null;
}
};
if (checkDeprecated(aOptions, "silent")) {
// Silent has been deprecated, do nothing. Placing the check here
// prevents the callback from being called twice, once with null and
// once after internalWatch has been called. See issue #1532:
// https://github.com/mozilla/browserid/issues/1532
if (aCallback) {
setTimeout(function() { aCallback(null); }, 0);
}
return;
}
// Get an assertion by using our observer api: watch + request.
var self = this;
this.watch({
_internal: true,
onlogin: function get_onlogin(assertion, internalParams) {
if (assertion && aCallback && internalParams && !internalParams.silent) {
aCallback(assertion);
aCallback = null;
}
},
onlogout: function get_onlogout() {},
onready: function get_onready() {
self.request(opts);
}
});
},
getVerifiedEmail: function nsDOMIdentity_getVerifiedEmail(aCallback) {
Cu.reportError("WARNING: getVerifiedEmail has been deprecated");
this.get(aCallback, {});
},
/**
* Identity Provider (IDP) Provisioning APIs
*/
beginProvisioning: function nsDOMIdentity_beginProvisioning(aCallback) {
this._log("beginProvisioning");
if (this._beginProvisioningCallback) {
throw new Error("navigator.id.beginProvisioning already called.");
}
if (!aCallback || typeof(aCallback) !== "function") {
throw new Error("beginProvisioning callback is required.");
}
this._beginProvisioningCallback = aCallback;
this._mm.sendAsyncMessage(
"Identity:IDP:BeginProvisioning",
this.DOMIdentityMessage(),
null,
this._window.document.nodePrincipal
);
},
genKeyPair: function nsDOMIdentity_genKeyPair(aCallback) {
this._log("genKeyPair");
if (!this._beginProvisioningCallback) {
throw new Error("navigator.id.genKeyPair called outside of provisioning");
}
if (this._genKeyPairCallback) {
throw new Error("navigator.id.genKeyPair already called.");
}
if (!aCallback || typeof(aCallback) !== "function") {
throw new Error("genKeyPair callback is required.");
}
this._genKeyPairCallback = aCallback;
this._mm.sendAsyncMessage(
"Identity:IDP:GenKeyPair",
this.DOMIdentityMessage(),
null,
this._window.document.nodePrincipal
);
},
registerCertificate: function nsDOMIdentity_registerCertificate(aCertificate) {
this._log("registerCertificate");
if (!this._genKeyPairCallback) {
throw new Error("navigator.id.registerCertificate called outside of provisioning");
}
if (this._provisioningEnded) {
throw new Error("Provisioning already ended");
}
this._provisioningEnded = true;
let message = this.DOMIdentityMessage();
message.cert = aCertificate;
this._mm.sendAsyncMessage(
"Identity:IDP:RegisterCertificate",
message,
null,
this._window.document.nodePrincipal
);
},
raiseProvisioningFailure: function nsDOMIdentity_raiseProvisioningFailure(aReason) {
this._log("raiseProvisioningFailure '" + aReason + "'");
if (this._provisioningEnded) {
throw new Error("Provisioning already ended");
}
if (!aReason || typeof(aReason) != "string") {
throw new Error("raiseProvisioningFailure reason is required");
}
this._provisioningEnded = true;
let message = this.DOMIdentityMessage();
message.reason = aReason;
this._mm.sendAsyncMessage(
"Identity:IDP:ProvisioningFailure",
message,
null,
this._window.document.nodePrincipal
);
},
/**
* Identity Provider (IDP) Authentication APIs
*/
beginAuthentication: function nsDOMIdentity_beginAuthentication(aCallback) {
this._log("beginAuthentication");
if (this._beginAuthenticationCallback) {
throw new Error("navigator.id.beginAuthentication already called.");
}
if (typeof(aCallback) !== "function") {
throw new Error("beginAuthentication callback is required.");
}
if (!aCallback || typeof(aCallback) !== "function") {
throw new Error("beginAuthentication callback is required.");
}
this._beginAuthenticationCallback = aCallback;
this._mm.sendAsyncMessage(
"Identity:IDP:BeginAuthentication",
this.DOMIdentityMessage(),
null,
this._window.document.nodePrincipal
);
},
completeAuthentication: function nsDOMIdentity_completeAuthentication() {
if (this._authenticationEnded) {
throw new Error("Authentication already ended");
}
if (!this._beginAuthenticationCallback) {
throw new Error("navigator.id.completeAuthentication called outside of authentication");
}
this._authenticationEnded = true;
this._mm.sendAsyncMessage(
"Identity:IDP:CompleteAuthentication",
this.DOMIdentityMessage(),
null,
this._window.document.nodePrincipal
);
},
raiseAuthenticationFailure: function nsDOMIdentity_raiseAuthenticationFailure(aReason) {
if (this._authenticationEnded) {
throw new Error("Authentication already ended");
}
if (!aReason || typeof(aReason) != "string") {
throw new Error("raiseProvisioningFailure reason is required");
}
let message = this.DOMIdentityMessage();
message.reason = aReason;
this._mm.sendAsyncMessage(
"Identity:IDP:AuthenticationFailure",
message,
null,
this._window.document.nodePrincipal
);
},
/**
* Called during init and shutdown.
*/
_initializeState: function nsDOMIdentity__initializeState() {
// Some state to prevent abuse
// Limit the number of calls to .request
this._rpCalls = 0;
this._provisioningEnded = false;
this._authenticationEnded = false;
this._rpWatcher = null;
this._onCancelRequestCallback = null;
this._beginProvisioningCallback = null;
this._genKeyPairCallback = null;
this._beginAuthenticationCallback = null;
},
// nsIMessageListener
receiveMessage: function nsDOMIdentity_receiveMessage(aMessage) {
let msg = aMessage.json;
// Is this message intended for this window?
if (msg.id != this._id) {
return;
}
switch (aMessage.name) {
case "Identity:ResetState":
if (!this._debug) {
return;
}
this._initializeState();
Services.obs.notifyObservers(null, "identity-DOM-state-reset", this._id);
break;
case "Identity:RP:Watch:OnLogin":
// Do we have a watcher?
if (!this._rpWatcher) {
this._log("WARNING: Received OnLogin message, but there is no RP watcher");
return;
}
if (this._rpWatcher.onlogin) {
if (this._rpWatcher._internal) {
this._rpWatcher.onlogin(msg.assertion, msg._internalParams);
} else {
this._rpWatcher.onlogin(msg.assertion);
}
}
break;
case "Identity:RP:Watch:OnLogout":
// Do we have a watcher?
if (!this._rpWatcher) {
this._log("WARNING: Received OnLogout message, but there is no RP watcher");
return;
}
if (this._rpWatcher.onlogout) {
this._rpWatcher.onlogout();
}
break;
case "Identity:RP:Watch:OnReady":
// Do we have a watcher?
if (!this._rpWatcher) {
this._log("WARNING: Received OnReady message, but there is no RP watcher");
return;
}
if (this._rpWatcher.onready) {
this._rpWatcher.onready();
}
break;
case "Identity:RP:Watch:OnCancel":
// Do we have a watcher?
if (!this._rpWatcher) {
this._log("WARNING: Received OnCancel message, but there is no RP " +
"watcher");
return;
}
if (this._onCancelRequestCallback) {
this._onCancelRequestCallback();
}
break;
case "Identity:RP:Watch:OnError":
if (!this._rpWatcher) {
this._log("WARNING: Received OnError message, but there is no RP " +
"watcher");
return;
}
if (this._rpWatcher.onerror) {
this._rpWatcher.onerror(JSON.stringify({name: msg.message.error}));
}
break;
case "Identity:IDP:CallBeginProvisioningCallback":
this._callBeginProvisioningCallback(msg);
break;
case "Identity:IDP:CallGenKeyPairCallback":
this._callGenKeyPairCallback(msg);
break;
case "Identity:IDP:CallBeginAuthenticationCallback":
this._callBeginAuthenticationCallback(msg);
break;
}
},
_callGenKeyPairCallback: function nsDOMIdentity__callGenKeyPairCallback(message) {
// create a pubkey object that works
let chrome_pubkey = JSON.parse(message.publicKey);
// bunch of stuff to create a proper object in window context
function genPropDesc(value) {
return {
enumerable: true, configurable: true, writable: true, value: value
};
}
let propList = {};
for (let k in chrome_pubkey) {
propList[k] = genPropDesc(chrome_pubkey[k]);
}
let pubkey = Cu.createObjectIn(this._window);
Object.defineProperties(pubkey, propList);
Cu.makeObjectPropsNormal(pubkey);
// do the callback
this._genKeyPairCallback(pubkey);
},
_callBeginProvisioningCallback:
function nsDOMIdentity__callBeginProvisioningCallback(message) {
let identity = message.identity;
let certValidityDuration = message.certDuration;
this._beginProvisioningCallback(identity,
certValidityDuration);
},
_callBeginAuthenticationCallback:
function nsDOMIdentity__callBeginAuthenticationCallback(message) {
let identity = message.identity;
this._beginAuthenticationCallback(identity);
},
/**
* Helper to create messages to send using a message manager.
* Pass through user options if they are not functions. Always
* overwrite id, origin, audience, and appStatus. The caller
* does not get to set those.
*/
DOMIdentityMessage: function DOMIdentityMessage(aOptions) {
aOptions = aOptions || {};
let message = {
errors: []
};
objectCopy(aOptions, message);
// outer window id
message.id = this._id;
// window origin
message.origin = this._origin;
// Normally the window origin will be the audience in assertions. On b2g,
// certified apps have the power to override this and declare any audience
// the want. Privileged apps can also declare a different audience, as
// long as it is the same as the origin specified in their manifest files.
// All other apps are stuck with b2g origins of the form app://{guid}.
// Since such an origin is meaningless for the purposes of verification,
// they will have to jump through some hoops to sign in: Specifically, they
// will have to host their sign-in flows and DOM API requests in an iframe,
// have the iframe xhr post assertions up to their server for verification,
// and then post-message the results down to their app.
let _audience = message.origin;
if (message.audience && message.audience != message.origin) {
if (this._appStatus === Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
_audience = message.audience;
this._log("Certified app setting assertion audience: " + _audience);
} else {
message.errors.push("ERROR_INVALID_ASSERTION_AUDIENCE");
}
}
// Replace any audience supplied by the RP with one that has been sanitised
message.audience = _audience;
this._log("DOMIdentityMessage: " + JSON.stringify(message));
return makeMessageObject(message);
},
/**
* Internal methods that are not exposed to content.
* See dom/webidl/Identity.webidl for the public interface.
*/
// nsIObserver
observe: function nsDOMIdentityInternal_observe(aSubject, aTopic, aData) {
let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
if (wId != this._innerWindowID) {
return;
}
this.uninit();
Services.obs.removeObserver(this, "inner-window-destroyed");
this._initializeState();
// TODO: Also send message to DOMIdentity notifiying window is no longer valid
// ie. in the case that the user closes the auth. window and we need to know.
try {
for (let msgName of this._messages) {
this._mm.removeMessageListener(msgName, this);
}
} catch (ex) {
// Avoid errors when removing more than once.
}
this._mm = null;
},
// Because we implement nsIDOMGlobalPropertyInitializer, our init() method
// is invoked with content window as its single argument.
init: function nsDOMIdentityInternal_init(aWindow) {
if (Services.prefs.getPrefType(PREF_ENABLED) != Ci.nsIPrefBranch.PREF_BOOL
|| !Services.prefs.getBoolPref(PREF_ENABLED)) {
return null;
}
this._debug =
Services.prefs.getPrefType(PREF_DEBUG) == Ci.nsIPrefBranch.PREF_BOOL
&& Services.prefs.getBoolPref(PREF_DEBUG);
// Setup identifiers for current window.
let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
// To avoid cross-process windowId collisions, use a uuid as an
// almost certainly unique identifier.
//
// XXX Bug 869182 - use a combination of child process id and
// innerwindow id to construct the unique id.
this._id = uuidgen.generateUUID().toString();
this._window = aWindow;
// nsDOMIdentity needs to know our _id, so this goes after
// its creation.
this._initializeState();
// Store window and origin URI.
this._window = aWindow;
this._origin = aWindow.document.nodePrincipal.origin;
this._appStatus = aWindow.document.nodePrincipal.appStatus;
this._appId = aWindow.document.nodePrincipal.appId;
this._log("init was called from " + aWindow.document.location);
this._mm = cpmm;
// Setup listeners for messages from parent process.
this._messages = [
"Identity:ResetState",
"Identity:RP:Watch:OnLogin",
"Identity:RP:Watch:OnLogout",
"Identity:RP:Watch:OnReady",
"Identity:RP:Watch:OnCancel",
"Identity:RP:Watch:OnError",
"Identity:IDP:CallBeginProvisioningCallback",
"Identity:IDP:CallGenKeyPairCallback",
"Identity:IDP:CallBeginAuthenticationCallback"
];
this._messages.forEach(function(msgName) {
this._mm.addMessageListener(msgName, this);
}, this);
// Setup observers so we can remove message listeners.
Services.obs.addObserver(this, "inner-window-destroyed", false);
},
uninit: function DOMIdentity_uninit() {
this._log("nsDOMIdentity uninit() " + this._id);
this._mm.sendAsyncMessage(
"Identity:RP:Unwatch",
{ id: this._id }
);
},
// Private.
_log: function nsDOMIdentityInternal__log(msg) {
if (!this._debug) {
return;
}
dump("nsDOMIdentity (" + this._id + "): " + msg + "\n");
},
// Component setup.
classID: Components.ID("{210853d9-2c97-4669-9761-b1ab9cbf57ef}"),
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIMessageListener,
Ci.nsIObserver,
Ci.nsIDOMGlobalPropertyInitializer
]),
classInfo: XPCOMUtils.generateCI({
classID: Components.ID("{210853d9-2c97-4669-9761-b1ab9cbf57ef}"),
contractID: "@mozilla.org/dom/identity;1",
interfaces: [],
classDescription: "Identity DOM Implementation"
})
};
function assertCorrectCallbacks(aOptions) {
// The relying party (RP) provides callbacks on watch().
//
// In the future, BrowserID will probably only require an onlogin()
// callback, lifting the requirement that BrowserID handle logged-in
// state management for RPs. See
// https://github.com/mozilla/id-specs/blob/greenfield/browserid/api-rp.md
//
// However, Firefox Accounts requires callers to provide onlogout(),
// onready(), and also supports an onerror() callback.
let requiredCallbacks = ["onlogin"];
let optionalCallbacks = ["onlogout", "onready", "onerror"];
if (aOptions.wantIssuer == "firefox-accounts") {
requiredCallbacks = ["onlogin", "onlogout", "onready"];
optionalCallbacks = ["onerror"];
}
for (let cbName of requiredCallbacks) {
if (typeof(aOptions[cbName]) != "function") {
throw new Error(cbName + " callback is required.");
}
}
for (let cbName of optionalCallbacks) {
if (aOptions[cbName] && typeof(aOptions[cbName]) != "function") {
throw new Error(cbName + " must be a function");
}
}
}
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsDOMIdentity]);

View File

@ -1,35 +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/. */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
this.IDService = function IDService() {
this.wrappedJSObject = this;
};
this.IDService.prototype = {
classID: Components.ID("{4e0a0e98-b1d3-4745-a1eb-f815199dd06b}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsISupportsWeakReference]),
observe: function observe(subject, topic, data) {
switch (topic) {
case "app-startup":
Services.obs.addObserver(this, "final-ui-startup", true);
break;
case "final-ui-startup":
// Startup DOMIdentity.jsm
Cu.import("resource://gre/modules/DOMIdentity.jsm");
DOMIdentity._init();
break;
}
}
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([IDService]);

View File

@ -1,18 +0,0 @@
[DEFAULT]
skip-if = buildapp == 'b2g'
support-files=
file_browserid_rp_ok.html
file_browserid_rp_noOnlogin.html
file_declareAudience.html
file_fxa_rp_ok.html
file_fxa_rp_noOnready.html
file_fxa_rp_noOnlogin.html
file_fxa_rp_noOnlogout.html
file_syntheticEvents.html
[test_declareAudience.html]
[test_rpHasValidCallbacks.html]
skip-if = buildapp == 'mulet'
[test_syntheticEvents.html]

View File

@ -1,44 +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/. */
-->
<!DOCTYPE html>
<html>
<!--
RPs have the correct callbacks for BrowserID or Firefox Accounts
https://bugzilla.mozilla.org/show_bug.cgi?id=945363
-->
<head>
<meta charset="utf-8">
<title>Test app for bug 945363</title>
</head>
<body>
<script type="application/javascript;version=1.7">
// This is a bad RP. It does not provide an onlogin callback.
// nsDOMIdentity will throw an exception when we call watch().
function postMessage(message) {
SpecialPowers.wrap(window).parent
.postMessage(JSON.stringify(message), "*");
}
let error = false;
window.addEventListener('load', function onLoad(event) {
window.removeEventListener('load', onLoad);
try {
navigator.mozId.watch({ });
} catch(ex) {
error = true;
}
postMessage({error: error});
}, false);
</script>
</body>
</html>

View File

@ -1,43 +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/. */
-->
<!DOCTYPE html>
<html>
<!--
RPs have the correct callbacks for BrowserID or Firefox Accounts
https://bugzilla.mozilla.org/show_bug.cgi?id=945363
-->
<head>
<meta charset="utf-8">
<title>Test app for bug 945363</title>
</head>
<body>
<script type="application/javascript;version=1.7">
// This is a happy RP. It has the expected callbacks for BrowserID.
function postMessage(message) {
SpecialPowers.wrap(window).parent
.postMessage(JSON.stringify(message), "*");
}
let error = false;
window.addEventListener('load', function onLoad(event) {
window.removeEventListener('load', onLoad);
try {
navigator.mozId.watch({
onlogin: function() {},
});
} catch(ex) {
error = true;
}
postMessage({error: error});
}, false);
</script>
</body>
</html>

View File

@ -1,73 +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/. */
-->
<!DOCTYPE html>
<html>
<!--
Certified and packaged apps should be able to declare assertion audience.
https://bugzilla.mozilla.org/show_bug.cgi?id=947374
-->
<head>
<meta charset="utf-8">
<title>Test app for bug 947374</title>
</head>
<body>
<div id='test'>
<script type="application/javascript;version=1.8">
function postResults(message) {
doPostMessage(JSON.stringify(message), "*");
}
onmessage = function(event) {
try {
navigator.mozId.watch({
wantIssuer: "firefox-accounts",
audience: event.data.audience,
onready: function() {
try {
navigator.mozId.request();
} catch(e) {
postResults({
success: false,
error: e,
appIndex: event.data.appIndex
});
}
},
onlogin: function(backedAssertion) {
postResults({
success: true,
backedAssertion: backedAssertion,
appIndex: event.data.appIndex
});
},
onerror: function(error) {
postResults({
success: false,
error: error,
appIndex: event.data.appIndex
});
},
// onlogout will actually be called every time watch() is invoked,
// because fxa will find no signed-in user and so trigger logout.
// For this test, though, we don't care and just ignore logout.
onlogout: function () {},
});
} catch (e) {
postResults({
success: false,
error: e,
appIndex: event.data.appIndex
});
}
};
</script>
</div>
</body>
</html>

View File

@ -1,46 +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/. */
-->
<!DOCTYPE html>
<html>
<!--
RPs have the correct callbacks for BrowserID or Firefox Accounts
https://bugzilla.mozilla.org/show_bug.cgi?id=945363
-->
<head>
<meta charset="utf-8">
<title>Test app for bug 945363</title>
</head>
<body>
<script type="application/javascript;version=1.7">
// This is a bad Firefox Accounts RP. It has no onlogin callback.
// nsDOMIdentity will throw an exception.
function postMessage(message) {
SpecialPowers.wrap(window).parent
.postMessage(JSON.stringify(message), "*");
}
let error = false;
window.addEventListener('load', function onLoad(event) {
window.removeEventListener('load', onLoad);
try {
navigator.mozId.watch({
wantIssuer: "firefox-accounts",
onready: function() {},
onlogout: function() {},
});
} catch(ex) {
error = true;
}
postMessage({error: error});
}, false);
</script>
</body>
</html>

View File

@ -1,46 +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/. */
-->
<!DOCTYPE html>
<html>
<!--
RPs have the correct callbacks for BrowserID or Firefox Accounts
https://bugzilla.mozilla.org/show_bug.cgi?id=945363
-->
<head>
<meta charset="utf-8">
<title>Test app for bug 945363</title>
</head>
<body>
<script type="application/javascript;version=1.7">
// This is a bad Firefox Accounts RP. It has no onlogout callback.
// nsDOMIdentity will throw an exception.
function postMessage(message) {
SpecialPowers.wrap(window).parent
.postMessage(JSON.stringify(message), "*");
}
let error = false;
window.addEventListener('load', function onLoad(event) {
window.removeEventListener('load', onLoad);
try {
navigator.mozId.watch({
wantIssuer: "firefox-accounts",
onlogin: function() {},
onready: function() {},
});
} catch(ex) {
error = true;
}
postMessage({error: error});
}, false);
</script>
</body>
</html>

View File

@ -1,46 +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/. */
-->
<!DOCTYPE html>
<html>
<!--
RPs have the correct callbacks for BrowserID or Firefox Accounts
https://bugzilla.mozilla.org/show_bug.cgi?id=945363
-->
<head>
<meta charset="utf-8">
<title>Test app for bug 945363</title>
</head>
<body>
<script type="application/javascript;version=1.7">
// This is a bad Firefox Accounts RP. It has no onready callback.
// nsDOMIdentity will throw an exception.
function postMessage(message) {
SpecialPowers.wrap(window).parent
.postMessage(JSON.stringify(message), "*");
}
let error = false;
window.addEventListener('load', function onLoad(event) {
window.removeEventListener('load', onLoad);
try {
navigator.mozId.watch({
wantIssuer: "firefox-accounts",
onlogin: function() {},
onlogout: function() {},
});
} catch(ex) {
error = true;
}
postMessage({error: error});
}, false);
</script>
</body>
</html>

View File

@ -1,46 +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/. */
-->
<!DOCTYPE html>
<html>
<!--
RPs have the correct callbacks for BrowserID or Firefox Accounts
https://bugzilla.mozilla.org/show_bug.cgi?id=945363
-->
<head>
<meta charset="utf-8">
<title>Test app for bug 945363</title>
</head>
<body>
<script type="application/javascript;version=1.7">
// This is a happy RP. It has the expected callbacks for Firefox Accounts.
function postMessage(message) {
SpecialPowers.wrap(window).parent
.postMessage(JSON.stringify(message), "*");
}
let error = false;
window.addEventListener('load', function onLoad(event) {
window.removeEventListener('load', onLoad);
try {
navigator.mozId.watch({
wantIssuer: "firefox-accounts",
onready: function() {},
onlogin: function() {},
onlogout: function() {},
});
} catch(ex) {
error = true;
}
postMessage({error: error});
}, false);
</script>
</body>
</html>

View File

@ -1,61 +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/. */
-->
<!DOCTYPE html>
<html>
<!--
Certified and privileged apps can call mozId outside an event handler
Certified apps: https://bugzilla.mozilla.org/show_bug.cgi?id=971379
Privileged apps: https://bugzilla.mozilla.org/show_bug.cgi?id=982460
-->
<head>
<meta charset="utf-8">
<title>Test app for bug 971379</title>
</head>
<body>
<div id='test'>
<script type="application/javascript;version=1.8">
function postResults(message) {
doPostMessage(JSON.stringify(message), "*");
}
onmessage = function(event) {
navigator.mozId.watch({
wantIssuer: event.data.wantIssuer,
onready: function() {
try {
navigator.mozId.request();
} catch(e) {
postResults({
success: false,
error: e,
appIndex: event.data.appIndex
});
}
},
onlogin: function(backedAssertion) {
postResults({
success: true,
backedAssertion: backedAssertion,
appIndex: event.data.appIndex
});
},
onerror: function(error) {
postResults({
success: false,
error: error,
appIndex: event.data.appIndex
});
},
onlogout: function() {},
});
};
</script>
</div>
</body>
</html>

View File

@ -1,289 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=947374
-->
<head>
<meta charset="utf-8">
<title>Certified apps can changed the default audience of an assertion -- Bug 947374</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=947374">Mozilla Bug 947374</a>
<p id="display"></p>
<div id="content">
</div>
<pre id="test">
<script type="application/javascript;version=1.8">
SimpleTest.waitForExplicitFinish();
Components.utils.import("resource://gre/modules/Promise.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/identity/jwcrypto.jsm");
Components.utils.import("resource://gre/modules/identity/FirefoxAccounts.jsm");
// quick check to make sure we can test apps:
is("appStatus" in document.nodePrincipal, true,
"appStatus should be present in nsIPrincipal, if not the rest of this test will fail");
// Mock the Firefox Accounts manager to generate a keypair and provide a fake
// cert for the caller on each getAssertion request.
function MockFXAManager() {}
MockFXAManager.prototype = {
getAssertion: function(audience, options) {
// Always reject a request for a silent assertion, simulating the
// scenario in which there is no signed-in user to begin with.
if (options.silent) {
return Promise.resolve(null);
}
let deferred = Promise.defer();
jwcrypto.generateKeyPair("DS160", (err, kp) => {
if (err) {
return deferred.reject(err);
}
jwcrypto.generateAssertion("fake-cert", kp, audience, (err, assertion) => {
if (err) {
return deferred.reject(err);
}
return deferred.resolve(assertion);
});
});
return deferred.promise;
}
};
let originalManager = FirefoxAccounts.fxAccountsManager;
FirefoxAccounts.fxAccountsManager = new MockFXAManager();
// The manifests for these apps are all declared in
// /testing/profiles/webapps_mochitest.json. They are injected into the profile
// by /testing/mochitest/runtests.py with the appropriate appStatus. So we don't
// have to manually install any apps.
//
// For each app, we will use the file_declareAudience.html content to populate an
// iframe. The iframe will request() a firefox accounts assertion. It will then
// postMessage the results of this experiment back down to us, and we will
// compare it with the expected results.
let apps = [
{
title: "an installed app, which should neither be able to use FxA, nor change audience",
manifest: "https://example.com/manifest.webapp",
appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_INSTALLED,
origin: "https://example.com",
wantAudience: "https://i-cant-have-this.com",
uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_declareAudience.html",
expected: {
success: false,
underprivileged: true,
},
},
{
title: "an app's assertion audience should be its origin by default",
manifest: "https://example.com/manifest_priv.webapp",
appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_PRIVILEGED,
origin: "https://example.com",
uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_declareAudience.html",
expected: {
success: true,
underprivileged: false,
},
},
{
title: "a privileged app, which may not have an audience other than its origin",
manifest: "https://example.com/manifest_priv.webapp",
appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_PRIVILEGED,
origin: "https://example.com",
wantAudience: "https://i-like-pie.com",
uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_declareAudience.html",
expected: {
success: false,
underprivileged: true,
},
},
{
title: "a privileged app, which may declare an audience the same as its origin",
manifest: "https://example.com/manifest_priv.webapp",
appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_PRIVILEGED,
origin: "https://example.com",
wantAudience: "https://example.com",
uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_declareAudience.html",
expected: {
success: true,
underprivileged: false,
},
},
{
title: "a certified app, which may do whatever it damn well pleases",
manifest: "https://example.com/manifest_cert.webapp",
appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_CERTIFIED,
origin: "https://example.com",
wantAudience: "https://whatever-i-want.com",
uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_declareAudience.html",
expected: {
success: true,
},
},
];
let eventsReceived = 0;
let testRunner = runTest();
// Successful tests will send exactly one message. But for error tests, we may
// have more than one message from the onerror handler in the client. So we keep
// track of received errors; once they reach the expected count, we are done.
function receiveMessage(event) {
let result = JSON.parse(event.data);
let app = apps[result.appIndex];
if (app.received) {
return;
}
apps[result.appIndex].received = true;
let expected = app.expected;
let expectedErrors = 0;
let receivedErrors = [];
if (expected.underprivileged) {
expectedErrors += 1;
}
if (expected.nopermission) {
expectedErrors += 1;
}
is(result.success, expected.success,
"Assertion request succeeds");
if (expected.success) {
// Confirm that the assertion audience and origin are as expected
let components = extractAssertionComponents(result.backedAssertion);
is(components.payload.aud, app.wantAudience || app.origin,
"Got desired assertion audience");
} else {
receivedErrors.push(result.error);
}
ok(receivedErrors.length === expectedErrors,
"Received errors should be equal to expected errors");
if (!expected.success && expected.underprivileged) {
ok(receivedErrors.indexOf("ERROR_INVALID_ASSERTION_AUDIENCE") > -1,
"Expect an error getting an assertion");
}
eventsReceived += 1;
if (eventsReceived === apps.length) {
window.removeEventListener("message", receiveMessage);
FirefoxAccounts.fxAccountsManager = originalManager;
SimpleTest.finish();
return;
}
testRunner.next();
}
window.addEventListener("message", receiveMessage, false, true);
function runTest() {
let index;
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
dump("\n\n** Testing " + app.title + "\n");
let iframe = document.createElement("iframe");
iframe.setAttribute("mozapp", app.manifest);
iframe.setAttribute("mozbrowser", "true");
iframe.src = app.uri;
document.getElementById("content").appendChild(iframe);
index = i;
(function(_index) {
iframe.addEventListener("load", function onLoad() {
iframe.removeEventListener("load", onLoad);
SpecialPowers.addPermission(
"firefox-accounts",
SpecialPowers.Ci.nsIPermissionManager.ALLOW_ACTION,
iframe.contentDocument
);
let principal = iframe.contentDocument.nodePrincipal;
is(principal.appStatus, app.appStatus,
"Iframe's document.nodePrincipal has expected appStatus");
// Because the <iframe mozapp> can't parent its way back to us, we
// provide this handle it can use to postMessage to us.
Components.utils.exportFunction(window.postMessage.bind(window), iframe.contentWindow, {defineAs: 'doPostMessage'});
// Test what we want to test, viz. whether or not the app can request
// an assertion with an audience the same as or different from its
// origin. The client will post back its success or failure in procuring
// an identity assertion from Firefox Accounts.
iframe.contentWindow.postMessage({
audience: app.wantAudience,
appIndex: _index
}, "*");
}, false);
})(index);
yield undefined;
}
}
function extractAssertionComponents(backedAssertion) {
let [_, signedObject] = backedAssertion.split("~");
let parts = signedObject.split(".");
let headerSegment = parts[0];
let payloadSegment = parts[1];
let cryptoSegment = parts[2];
let header = JSON.parse(base64UrlDecode(headerSegment));
let payload = JSON.parse(base64UrlDecode(payloadSegment));
return {header: header,
payload: payload,
headerSegment: headerSegment,
payloadSegment: payloadSegment,
cryptoSegment: cryptoSegment};
};
function base64UrlDecode(s) {
s = s.replace(/-/g, "+");
s = s.replace(/_/g, "/");
// Don't need to worry about reintroducing padding ('=='), since
// jwcrypto provides that.
return atob(s);
}
SpecialPowers.pushPrefEnv({"set":
[
["dom.mozBrowserFramesEnabled", true],
["dom.identity.enabled", true],
["identity.fxaccounts.enabled", true],
["toolkit.identity.debug", true],
["dom.identity.syntheticEventsOk", true],
["security.apps.privileged.CSP.default", "'inline-script';"],
["security.apps.certified.CSP.default", "'inline-script';"],
]},
function() {
testRunner.next();
}
);
</script>
</pre>
</body>
</html>

View File

@ -1,97 +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/. */
-->
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=945363
-->
<head>
<meta charset="utf-8">
<title>BrowserID and Firefox Accounts RPs provide requried callbacks - Bug 945363</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=945363">Mozilla Bug 945363</a>
<p id="display"></p>
<div id="content">
</div>
<pre id="test">
<script type="application/javascript;version=1.7">
/** Test for Bug 945363 **/
const BASE_URL = "http://mochi.test:8888/chrome/dom/identity/tests/mochitest/";
SimpleTest.waitForExplicitFinish();
// Candidate RPs, and whether we expect them to experience an error.
// Will will load each of these in turn into an iframe. The candiates
// will invoke navigator.mozId.watch(). If they do not provide the
// correct arguments to watch(), nsDOMIdentity will throw an exception.
let candidates = [
[BASE_URL + "file_browserid_rp_ok.html", false],
[BASE_URL + "file_browserid_rp_noOnlogin.html", true ],
[BASE_URL + "file_fxa_rp_ok.html", false],
[BASE_URL + "file_fxa_rp_noOnlogin.html", true ],
[BASE_URL + "file_fxa_rp_noOnlogout.html", true ],
[BASE_URL + "file_fxa_rp_noOnready.html", true ],
];
let checkedCount = 0;
let checksTodo = candidates.length;
// Each iframe will postMessage to us, telling us whether they caught
// an exception or not when calling watch().
window.addEventListener('message', function onMessage(event) {
let message = JSON.parse(event.data);
let [uri, expectedError] = candidates[checkedCount];
is(message.error, expectedError, "Unexpected error result from " + uri);
if (++checkedCount < checksTodo) {
testRunner.next();
} else {
window.removeEventListener('message', onMessage);
SimpleTest.finish();
}
}, false);
let content = document.getElementById('content');
function runTest() {
for (let [uri, _] of candidates) {
let iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', 'true');
iframe.src = uri;
content.appendChild(iframe);
yield undefined;
}
}
let testRunner = runTest();
// Enable the identity systems and use verbose logging
SpecialPowers.pushPrefEnv({'set': [
['dom.identity.enabled', true], // navigator.mozId
['identity.fxaccounts.enabled', true], // fx accounts
['dom.identity.syntheticEventsOk', true], // so we can call request()
['toolkit.identity.debug', true], // verbose identity logging
['browser.dom.window.dump.enabled', true],
["security.apps.privileged.CSP.default", "'inline-script';"],
["security.apps.certified.CSP.default", "'inline-script';"],
]},
function () { testRunner.next(); }
);
</script>
</pre>
</body>
</html>

View File

@ -1,210 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=971379
-->
<head>
<meta charset="utf-8">
<title>Certified/packaged apps may use synthetic events with FXA -- Bug 971379</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=971379">Mozilla Bug 971379</a>
<p id="display"></p>
<div id="content">
</div>
<pre id="test">
<script type="application/javascript;version=1.8">
SimpleTest.waitForExplicitFinish();
Components.utils.import("resource://gre/modules/Promise.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/DOMIdentity.jsm");
Components.utils.import("resource://gre/modules/identity/jwcrypto.jsm");
Components.utils.import("resource://gre/modules/identity/FirefoxAccounts.jsm");
// Mock the Firefox Accounts manager to give a dummy assertion, just to confirm
// that we're making the trip through the dom/identity and toolkit/identity
// plumbing.
function MockFXAManager() {}
MockFXAManager.prototype = {
getAssertion: function(audience, options) {
if (options.silent) {
return Promise.resolve(null);
}
return Promise.resolve("here~you.go.dude");
}
};
let originalManager = FirefoxAccounts.fxAccountsManager;
FirefoxAccounts.fxAccountsManager = new MockFXAManager();
// Mock IdentityService (Persona) so we can test request() while not handling
// user input on an installed app. Since in this test suite, we have only this
// one test for Persona, we additionally cause request() to throw if invoked, as
// added security that nsDOMIdentity did not emit a request message.
let MockIdentityService = function() {
this.RP = this;
this.contexts = {};
}
MockIdentityService.prototype = {
watch: function(context) {
this.contexts[context.id] = context;
context.doReady();
},
request: function(message) {
ok(false, "nsDOMIdentity should block Persona request() in this test suite");
},
};
DOMIdentity._mockIdentityService = new MockIdentityService();
// The manifests for these apps are all declared in
// /testing/profiles/webapps_mochitest.json. They are injected into the profile
// by /testing/mochitest/runtests.py with the appropriate appStatus. So we don't
// have to manually install any apps.
let apps = [
{
title: "an installed app, which must request() in a native event",
manifest: "https://example.com/manifest.webapp",
origin: "https://example.com",
uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_syntheticEvents.html",
wantIssuer: "", // default to persona
expected: {
success: false,
errors: [
"ERROR_REQUEST_WHILE_NOT_HANDLING_USER_INPUT",
],
},
},
{
title: "a privileged app, which may use synthetic events",
manifest: "https://example.com/manifest_priv.webapp",
origin: "https://example.com",
uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_syntheticEvents.html",
wantIssuer: "firefox-accounts",
expected: {
success: true,
},
},
{
title: "a certified app, which may use synthetic events",
manifest: "https://example.com/manifest_cert.webapp",
origin: "https://example.com",
uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_syntheticEvents.html",
wantIssuer: "firefox-accounts",
expected: {
success: true,
},
},
];
let eventsReceived = 0;
let testRunner = runTest();
function receiveMessage(event) {
dump("** Received response: " + event.data + "\n");
let result = JSON.parse(event.data);
let app = apps[result.appIndex];
if (app.received) {
return;
}
apps[result.appIndex].received = true;
let expected = app.expected;
let receivedErrors = [];
is(result.success, expected.success,
"Assertion request " + (expected.success ? "succeeds" : "fails"));
if (result.error) {
receivedErrors.push(result.error);
}
ok(receivedErrors.length === (expected.errors || []).length,
"Received errors should be equal to expected errors");
receivedErrors.forEach((error) => {
ok(expected.errors.indexOf(error) > -1,
"Received " + error + ". " +
"Expected errors are: " + JSON.stringify(expected.errors));
});
eventsReceived += 1;
if (eventsReceived === apps.length) {
window.removeEventListener("message", receiveMessage);
FirefoxAccounts.fxAccountsManager = originalManager;
SimpleTest.finish();
return;
}
testRunner.next();
}
window.addEventListener("message", receiveMessage, false, true);
function runTest() {
let index;
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
dump("** Testing " + app.title + "\n");
let iframe = document.createElement("iframe");
iframe.setAttribute("mozapp", app.manifest);
iframe.setAttribute("mozbrowser", "true");
iframe.src = app.uri;
document.getElementById("content").appendChild(iframe);
index = i;
(function(_index) {
iframe.addEventListener("load", function onLoad() {
iframe.removeEventListener("load", onLoad);
SpecialPowers.addPermission(
"firefox-accounts",
SpecialPowers.Ci.nsIPermissionManager.ALLOW_ACTION,
iframe.contentDocument
);
// Because the <iframe mozapp> can't parent its way back to us, we
// provide this handle it can use to postMessage to us.
Components.utils.exportFunction(window.postMessage.bind(window), iframe.contentWindow, {defineAs: 'doPostMessage'});
iframe.contentWindow.postMessage({
wantIssuer: app.wantIssuer,
appIndex: _index
}, "*");
}, false);
})(index);
yield undefined;
}
}
SpecialPowers.pushPrefEnv({"set":
[
["dom.mozBrowserFramesEnabled", true],
["dom.identity.enabled", true],
["identity.fxaccounts.enabled", true],
["toolkit.identity.debug", true],
["security.apps.privileged.CSP.default", "'inline-script';"],
["security.apps.certified.CSP.default", "'inline-script';"],
]},
function() {
testRunner.next();
}
);
</script>
</pre>
</body>
</html>

View File

@ -139,7 +139,6 @@ if CONFIG['MOZ_SECUREELEMENT']:
if CONFIG['MOZ_B2G']:
DIRS += [
'downloads',
'identity'
]
DIRS += ['presentation']

View File

@ -1,70 +0,0 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*/
callback IdentityOnReadyCallback = void();
callback IdentityOnLoginCallback = void(DOMString identityAssertion);
callback IdentityOnLogoutCallback = void();
callback IdentityOnCancelCallback = void(DOMString? error);
callback IdentityOnErrorCallback = void(DOMString error);
dictionary IdentityWatchOptions {
// Required callback
IdentityOnLoginCallback onlogin;
// Optional parameters
DOMString wantIssuer;
DOMString loggedInUser;
// Optional callbacks
IdentityOnReadyCallback onready;
IdentityOnLogoutCallback onlogout;
IdentityOnErrorCallback onerror;
// Certified apps can specify this
DOMString audience;
};
dictionary IdentityRequestOptions {
// Optional parameters
long refreshAuthentication;
DOMString termsOfService;
DOMString privacyPolicy;
DOMString backgroundColor;
DOMString siteLogo;
DOMString siteName;
DOMString returnTo;
IdentityOnCancelCallback oncancel;
// Certified apps can specify this
DOMString origin;
};
dictionary IdentityGetOptions {
DOMString privacyPolicy;
DOMString termsOfService;
DOMString privacyURL;
DOMString tosURL;
DOMString siteName;
DOMString siteLogo;
};
[JSImplementation="@mozilla.org/identity/manager;1",
NoInterfaceObject,
NavigatorProperty="mozId",
Pref="dom.identity.enabled"]
interface IdentityManager {
void watch(optional IdentityWatchOptions options);
void request(optional IdentityRequestOptions options);
void logout();
[Pref="dom.identity.exposeLegacyGetAPI"]
void get(IdentityOnLoginCallback callback, optional IdentityGetOptions options);
[Pref="dom.identity.exposeLegacyGetVerifiedEmailAPI"]
void getVerifiedEmail(IdentityOnLoginCallback callback);
};

View File

@ -905,7 +905,6 @@ if CONFIG['MOZ_BUILD_APP'] in ['browser', 'mobile/android', 'xulrunner']:
if CONFIG['MOZ_B2G']:
WEBIDL_FILES += [
'Apps.webidl',
'Identity.webidl',
'MozApplicationEvent.webidl'
]
GENERATED_EVENTS_WEBIDL_FILES += [

View File

@ -116,7 +116,6 @@ function runTest() {
};
SpecialPowers.pushPrefEnv({"set": [
["dom.identity.enabled", true], // navigator.mozId
["identity.fxaccounts.enabled", true], // fx accounts
["identity.fxaccounts.auth.uri", TEST_SERVER], // our sjs server
["toolkit.identity.debug", true], // verbose identity logging