Bug 845546 - Port b2g identity implementation to desktop. r=benadida

This commit is contained in:
Jed Parsons 2013-03-27 11:19:09 -04:00
parent ecad0ec815
commit a81b79511e
21 changed files with 909 additions and 948 deletions

View File

@ -0,0 +1,149 @@
/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* 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 JS shim contains the callbacks to fire DOMRequest events for
// navigator.pay API within the payment processor's scope.
'use strict';
let { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
XPCOMUtils.defineLazyGetter(this, "logger", function() {
Cu.import('resource://gre/modules/identity/LogUtils.jsm');
return getLogger("Identity", "toolkit.identity.debug");
});
function IdentityShim() {
this.isLoaded = false;
}
IdentityShim.prototype = {
init: function IdentityShim_init() {
addMessageListener('identity-delegate-watch', this);
addMessageListener('identity-delegate-request', this);
addMessageListener('identity-delegate-logout', this);
sendAsyncMessage('identity-delegate-loaded');
logger.log('init(). sent identity-delegate-complete');
this.isLoaded = true;
},
uninit: function IdentityShim_uninit() {
if (this.isLoaded) {
removeMessageListener('identity-delegate-watch', this);
removeMessageListener('identity-delegate-request', this);
removeMessageListener('identity-delegate-logout', this);
sendAsyncMessage('identity-delegate-complete', null);
logger.log('uninit(). sent identity-delegate-complete');
this.isLoaded = false;
}
},
receiveMessage: function IdentityShim_receiveMessage(aMessage) {
switch (aMessage.name) {
case 'identity-delegate-watch':
this.watch(aMessage.json);
break;
case 'identity-delegate-request':
this.request(aMessage.json);
break;
case 'identity-delegate-logout':
this.logout(aMessage.json);
break;
default:
logger.error("received unexpected message:", aMessage.name);
break;
}
},
_identityDoMethod: function IdentityShim__identityDoMethod(message) {
sendAsyncMessage('identity-service-doMethod', message);
},
_close: function IdentityShim__close() {
this.uninit();
},
watch: function IdentityShim_watch(options) {
logger.log('doInternalWatch: isLoaded:', this.isLoaded, 'options:', options);
if (options) {
let BrowserID = content.wrappedJSObject.BrowserID;
let callback = function(aParams, aInternalParams) {
this._identityDoMethod(aParams);
if (aParams.method === 'ready') {
this._close();
}
}.bind(this);
BrowserID.internal.watch(
callback,
JSON.stringify(options),
function(...things) {
logger.log('internal watch returned:', things);
}
);
}
},
request: function IdentityShim_request(options) {
logger.log('doInternalRequest: isLoaded:', this.isLoaded, 'options:', options);
if (options) {
var stringifiedOptions = JSON.stringify(options);
let callback = function(assertion, internalParams) {
logger.log("received assertion:", assertion);
internalParams = internalParams || {};
if (assertion) {
logger.log("got assertion");
this._identityDoMethod({
method: 'login',
assertion: assertion,
_internal: options._internal,
_internalParams: internalParams});
}
this._close();
}.bind(this);
logger.log('call get() with origin', options.origin, ', callback', callback, ', and options', stringifiedOptions);
content.wrappedJSObject.BrowserID.internal.get(
options.origin,
callback,
stringifiedOptions
);
logger.log('called get()');
}
},
logout: function IdentityShim_logout(options) {
logger.log('doInternalLogout: isLoaded:', this.isLoaded, 'options:', options);
if (options) {
let BrowserID = content.wrappedJSObject.BrowserID;
let callback = function() {
this._identityDoMethod({method: 'logout', _internal: options._internal});
this._close();
}.bind(this);
BrowserID.internal.logout(options.origin, callback);
}
}
};
this.shim = null;
addEventListener('DOMContentLoaded', function(e) {
content.addEventListener('load', function(e) {
logger.log('content loaded');
this.shim = new IdentityShim();
this.shim.init();
});
});
content.addEventListener('beforeunload', function(e) {
if (this.shim) {
this.shim.uninit();
}
});

View File

@ -267,26 +267,6 @@ panel[noactions] > richlistbox > richlistitem[type~="action"] > .ac-url-box > .a
visibility: collapse; visibility: collapse;
} }
#urlbar[pageproxystate="invalid"] > #identity-box > #identity-icon-labels {
visibility: collapse;
}
#urlbar[pageproxystate="invalid"] > #identity-box {
pointer-events: none;
}
#identity-icon-labels {
max-width: 18em;
}
#identity-icon-country-label {
direction: ltr;
}
#identity-box.verifiedIdentity > #identity-icon-labels > #identity-icon-label {
-moz-margin-end: 0.25em !important;
}
#wrapper-search-container > #search-container > #searchbar > .searchbar-textbox > .autocomplete-textbox-container > .textbox-input-box > html|*.textbox-input { #wrapper-search-container > #search-container > #searchbar > .searchbar-textbox > .autocomplete-textbox-container > .textbox-input-box > html|*.textbox-input {
visibility: hidden; visibility: hidden;
} }
@ -358,16 +338,6 @@ window[chromehidden~="toolbar"] toolbar:not(.toolbar-primary):not(.chromeclass-m
} }
%endif %endif
/* Identity UI */
#identity-popup-content-box.unknownIdentity > #identity-popup-connectedToLabel ,
#identity-popup-content-box.unknownIdentity > #identity-popup-runByLabel ,
#identity-popup-content-box.unknownIdentity > #identity-popup-content-host ,
#identity-popup-content-box.unknownIdentity > #identity-popup-content-owner ,
#identity-popup-content-box.verifiedIdentity > #identity-popup-connectedToLabel2 ,
#identity-popup-content-box.verifiedDomain > #identity-popup-connectedToLabel2 {
display: none;
}
/* Full Screen UI */ /* Full Screen UI */
#fullscr-toggler { #fullscr-toggler {

View File

@ -1122,254 +1122,60 @@
</implementation> </implementation>
</binding> </binding>
<binding id="identity-request-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification"> <binding
<content align="start"> id="identity-request-notification"
extends="chrome://global/content/bindings/notification.xml#popup-notification">
<xul:image class="popup-notification-icon" <content>
xbl:inherits="popupid,src=icon"/> <panel id="persona-container" anonid="persona-container" flex="1" />
<xul:vbox flex="1">
<xul:vbox anonid="identity-deck">
<xul:vbox flex="1" pack="center"> <!-- 1: add an email -->
<html:input type="email" anonid="email" required="required" size="30"/>
<xul:description anonid="newidentitydesc"/>
<xul:spacer flex="1"/>
<xul:label class="text-link custom-link small-margin" anonid="chooseemail" hidden="true"/>
</xul:vbox>
<xul:vbox flex="1" hidden="true"> <!-- 2: choose an email -->
<xul:description anonid="chooseidentitydesc"/>
<xul:radiogroup anonid="identities">
</xul:radiogroup>
<xul:label class="text-link custom-link" anonid="newemail"/>
</xul:vbox>
</xul:vbox>
<xul:hbox class="popup-notification-button-container"
pack="end" align="center">
<xul:label anonid="tos" class="text-link" hidden="true"/>
<xul:label anonid="privacypolicy" class="text-link" hidden="true"/>
<xul:spacer flex="1"/>
<xul:image anonid="throbber" src="chrome://browser/skin/tabbrowser/loading.png"
style="visibility:hidden" width="16" height="16"/>
<xul:button anonid="button"
type="menu-button"
class="popup-notification-menubutton"
xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey">
<xul:menupopup anonid="menupopup"
xbl:inherits="oncommand=menucommand">
<children/>
<xul:menuitem class="menuitem-iconic popup-notification-closeitem"
label="&closeNotificationItem.label;"
xbl:inherits="oncommand=closeitemcommand"/>
</xul:menupopup>
</xul:button>
</xul:hbox>
</xul:vbox>
<xul:vbox pack="start">
<xul:toolbarbutton anonid="closebutton"
class="messageCloseButton popup-notification-closebutton tabbable"
xbl:inherits="oncommand=closebuttoncommand"
tooltiptext="&closeNotification.tooltip;"/>
</xul:vbox>
</content> </content>
<implementation>
<implementation implements="nsIObserver, nsIDOMEventListener">
<constructor><![CDATA[ <constructor><![CDATA[
// this.notification.options.identity is used to pass identity-specific info to the binding dump(" ** in identity xul constructor\n");
let origin = this.identity.origin this.complete = false;
// Populate text // Mark outgoing messages with the id of the caller
this.emailField.placeholder = gNavigatorBundle. this.messageSubject = Components.classes["@mozilla.org/supports-string;1"]
getString("identity.newIdentity.email.placeholder"); .createInstance(Components.interfaces.nsISupportsString);
this.newIdentityDesc.textContent = gNavigatorBundle.getFormattedString( this.messageSubject.data = this.notification.options.context.id;
"identity.newIdentity.description", [origin]);
this.chooseIdentityDesc.textContent = gNavigatorBundle.getFormattedString(
"identity.chooseIdentity.description", [origin]);
// Show optional terms of service and privacy policy links // adopt the iframe and display it in the panel
this._populateLink(this.identity.termsOfService, "tos", "identity.termsOfService"); // XXX this does not work in FF3.6 or older
this._populateLink(this.identity.privacyPolicy, "privacypolicy", "identity.privacyPolicy"); let personaIframe = this.notification.options.context.iframe;
let node = document.adoptNode(personaIframe);
let panel = document.getAnonymousElementByAttribute(this, "anonid", "persona-container");
panel.appendChild(node);
// Populate the list of identities to choose from. The origin is used to provide // The dimensions of the panel are modified dynamically by
// better suggestions. // SignInToWebsite.jsm.
let identities = this.SignInToWebsiteUX.getIdentitiesForSite(origin);
this._populateIdentityList(identities); dump(" ** Persona host iframe attached to xul panel\n");
if (typeof this.step == "undefined") { // Listen to messages from SignInToWebsite.jsm
// First opening of this notification Services.obs.addObserver(this, "identity-delegate-ui-close", false);
// Show the add email pane (0) if there are no existing identities otherwise show the list
this.step = "result" in identities && identities.result.length ? 1 : 0;
} else {
// Already opened so restore previous state
if (this.identity.typedEmail) {
this.emailField.value = this.identity.typedEmail;
}
if (this.identity.selected) {
// If the user already chose an identity then update the UI to reflect that
this.onIdentitySelected();
}
// Update the view for the step
this.step = this.step;
}
// Fire notification with the chosen identity when main button is clicked // message back to SignInToWebsite that we've started
this.button.addEventListener("command", this._onButtonCommand.bind(this), true); // and the flow with the given id can go ahead
// XXX we might not need this
// Do the same if enter is pressed in the email field Services.obs.notifyObservers(this.messageSubject, "identity-delegate-ui-open", null);
this.emailField.addEventListener("keypress", function emailFieldKeypress(aEvent) {
if (aEvent.keyCode != aEvent.DOM_VK_RETURN)
return;
this._onButtonCommand(aEvent);
}.bind(this));
this.addEmailLink.value = gNavigatorBundle.getString("identity.newIdentity.label");
this.addEmailLink.accessKey = gNavigatorBundle.getString("identity.newIdentity.accessKey");
this.addEmailLink.addEventListener("click", function addEmailClick(evt) {
this.step = 0;
}.bind(this));
this.chooseEmailLink.value = gNavigatorBundle.getString("identity.chooseIdentity.label");
this.chooseEmailLink.hidden = !("result" in identities && identities.result.length);
this.chooseEmailLink.addEventListener("click", function chooseEmailClick(evt) {
this.step = 1;
}.bind(this));
this.emailField.addEventListener("blur", function onEmailBlur() {
this.identity.typedEmail = this.emailField.value;
}.bind(this));
]]></constructor> ]]></constructor>
<field name="SignInToWebsiteUX" readonly="true"> <destructor><![CDATA[
let sitw = {}; if (!this.complete) {
Components.utils.import("resource:///modules/SignInToWebsite.jsm", sitw); Services.obs.notifyObservers(this.messageSubject, "identity-delegate-canceled", null);
sitw.SignInToWebsiteUX; }
</field> Services.obs.removeObserver(this, "identity-delegate-ui-close", false);
]]></destructor>
<field name="newIdentityDesc" readonly="true"> <method name="observe">
document.getAnonymousElementByAttribute(this, "anonid", "newidentitydesc"); <parameter name="aSubject"/>
</field> <parameter name="aTopic"/>
<parameter name="aData"/>
<field name="chooseIdentityDesc" readonly="true">
document.getAnonymousElementByAttribute(this, "anonid", "chooseidentitydesc");
</field>
<field name="identityList" readonly="true">
document.getAnonymousElementByAttribute(this, "anonid", "identities");
</field>
<field name="emailField" readonly="true">
document.getAnonymousElementByAttribute(this, "anonid", "email");
</field>
<field name="addEmailLink" readonly="true">
document.getAnonymousElementByAttribute(this, "anonid", "newemail");
</field>
<field name="chooseEmailLink" readonly="true">
document.getAnonymousElementByAttribute(this, "anonid", "chooseemail");
</field>
<field name="throbber" readonly="true">
document.getAnonymousElementByAttribute(this, "anonid", "throbber");
</field>
<field name="identity" readonly="true">
this.notification.options.identity;
</field>
<!-- persist the state on the identity object so we can re-create the
notification state upon re-opening -->
<property name="step">
<getter>
return this.identity.step;
</getter>
<setter><![CDATA[
let deck = document.getAnonymousElementByAttribute(this, "anonid", "identity-deck");
for (let i = 0; i < deck.children.length; i++) {
deck.children[i].hidden = (val != i);
}
this.identity.step = val;
switch (val) {
case 0:
this.emailField.focus();
break;
}]]>
</setter>
</property>
<method name="onIdentitySelected">
<body><![CDATA[ <body><![CDATA[
this.throbber.style.visibility = "visible"; // The only message we observe is identity-delegate-ui-close
this.button.disabled = true; this.complete = true;
this.emailField.value = this.identity.selected this.notification.remove();
this.emailField.disabled = true;
this.identityList.disabled = true;
]]></body>
</method>
<method name="_populateLink">
<parameter name="aURL"/>
<parameter name="aLinkId"/>
<parameter name="aStringId"/>
<body><![CDATA[
if (aURL) {
// Show optional link to aURL
let link = document.getAnonymousElementByAttribute(this, "anonid", aLinkId);
link.value = gNavigatorBundle.getString(aStringId);
link.href = aURL;
link.hidden = false;
}
]]></body>
</method>
<method name="_populateIdentityList">
<parameter name="aIdentities"/>
<body><![CDATA[
let foundLastUsed = false;
let lastUsed = this.identity.selected || aIdentities.lastUsed;
for (let id in aIdentities.result) {
let label = aIdentities.result[id];
let opt = this.identityList.appendItem(label);
if (label == lastUsed) {
this.identityList.selectedItem = opt;
foundLastUsed = true;
}
}
if (!foundLastUsed) {
this.identityList.selectedIndex = -1;
}
]]></body>
</method>
<method name="_onButtonCommand">
<parameter name="aEvent"/>
<body><![CDATA[
if (aEvent.target != aEvent.currentTarget)
return;
let chosenId;
switch (this.step) {
case 0:
aEvent.stopPropagation();
if (!this.emailField.validity.valid) {
this.emailField.focus();
return;
}
chosenId = this.emailField.value;
break;
case 1:
aEvent.stopPropagation();
let selectedItem = this.identityList.selectedItem
chosenId = selectedItem ? selectedItem.label : null;
if (!chosenId)
return;
break;
default:
throw new Error("Unknown case");
return;
}
// Actually select the identity
this.SignInToWebsiteUX.selectIdentity(this.identity.rpId, chosenId);
this.identity.selected = chosenId;
this.onIdentitySelected();
]]></body> ]]></body>
</method> </method>

View File

@ -60,6 +60,7 @@ browser.jar:
* content/browser/browser.css (content/browser.css) * content/browser/browser.css (content/browser.css)
* content/browser/browser.js (content/browser.js) * content/browser/browser.js (content/browser.js)
* content/browser/browser.xul (content/browser.xul) * content/browser/browser.xul (content/browser.xul)
content/browser/browser-identity.js (content/browser-identity.js)
* content/browser/browser-tabPreviews.xml (content/browser-tabPreviews.xml) * content/browser/browser-tabPreviews.xml (content/browser-tabPreviews.xml)
content/browser/content.js (content/content.js) content/browser/content.js (content/content.js)
content/browser/newtab/newTab.xul (content/newtab/newTab.xul) content/browser/newtab/newTab.xul (content/newtab/newTab.xul)

View File

@ -2,246 +2,444 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file, * 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/. */ * You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict"; 'use strict';
this.EXPORTED_SYMBOLS = ["SignInToWebsiteUX"]; this.EXPORTED_SYMBOLS = ['SignInToWebsiteUX'];
const Cc = Components.classes; const Cc = Components.classes;
const Ci = Components.interfaces; const Ci = Components.interfaces;
const Cu = Components.utils; const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm"); Cu.import('resource://gre/modules/Services.jsm');
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/identity/IdentityUtils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, "IdentityService", const kIdentityScreen = 'https://picl.personatest.org/sign_in#NATIVE';
"resource://gre/modules/identity/Identity.jsm"); const kIdentityFrame = 'https://picl.personatest.org/communication_iframe';
const kIdentityShim = 'chrome://browser/content/browser-identity.js';
XPCOMUtils.defineLazyModuleGetter(this, "Logger", const PANEL_MIN_HEIGHT = 440;
"resource://gre/modules/identity/LogUtils.jsm"); const PANEL_MIN_WIDTH = 300;
function log(...aMessageArgs) { XPCOMUtils.defineLazyModuleGetter(this, 'IdentityService',
Logger.log.apply(Logger, ["SignInToWebsiteUX"].concat(aMessageArgs)); 'resource://gre/modules/identity/MinimalIdentity.jsm');
XPCOMUtils.defineLazyGetter(this, "logger", function() {
Cu.import('resource://gre/modules/identity/LogUtils.jsm');
return getLogger("Identity", "toolkit.identity.debug");
});
/**
* Ripped off from the resize watcher in base/content/browser-social.js
*/
function sizePanelToContent(iframe) {
// FIXME: bug 764787: Maybe we can use nsIDOMWindowUtils.getRootBounds() here?
let doc = iframe.contentDocument;
if (!doc || !doc.body) {
return;
}
let body = doc.body;
// offsetHeight/Width don't include margins, so account for that.
let cs = doc.defaultView.getComputedStyle(body);
let computedHeight = parseInt(cs.marginTop) + body.offsetHeight + parseInt(cs.marginBottom);
let height = Math.max(computedHeight, PANEL_MIN_HEIGHT);
let computedWidth = parseInt(cs.marginLeft) + body.offsetWidth + parseInt(cs.marginRight);
let width = Math.max(computedWidth, PANEL_MIN_WIDTH);
// The panel can only resize vertically; otherwise, we would have to
// compensate for leftward or rightward shifts here
iframe.style.height = height + "px";
iframe.style.width = width + "px";
} }
function ResizeWatcher(iframe) {
this._mutationObserver = null;
this._iframe = iframe;
this.start();
}
ResizeWatcher.prototype = {
start: function ResizeWatcher_start() {
this.stop(); // just in case...
let doc = this._iframe.contentDocument;
this._mutationObserver = new this._iframe.contentWindow.MutationObserver(
function(mutations) {
sizePanelToContent(this._iframe);
}.bind(this));
// Observe anything that causes the size to change.
let config = {
attributes: true,
characterData: true,
childList: true,
subtree: true
};
this._mutationObserver.observe(doc, config);
// and since this may be setup after the load event has fired we do an
// initial resize now.
sizePanelToContent(this._iframe);
},
stop: function ResizeWatcher_stop() {
if (this._mutationObserver) {
try {
this._mutationObserver.disconnect();
} catch (ex) {
// may get "TypeError: can't access dead object" which seems strange,
// but doesn't seem to indicate a real problem, so ignore it...
}
this._mutationObserver = null;
}
}
};
/**
* Return the chrome window and <browser> for the given outer window ID.
*/
function getUIForWindowID(aWindowId) {
let someWindow = Services.wm.getMostRecentWindow('navigator:browser');
if (!someWindow) {
logger.error('SignInToWebsiteUX', 'no window');
return {};
}
let windowUtils = someWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let content = windowUtils.getOuterWindowWithId(aWindowId);
if (content) {
let browser = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell).chromeEventHandler;
//let browser = someWindow.gBrowser;
let chromeWin = browser.ownerDocument.defaultView;
return {
content: content,
browser: browser,
chromeWin: chromeWin
};
}
logger.error('SignInToWebsiteUX', 'no content');
return {};
}
function requestUI(aContext) {
logger.log('requestUI for windowId', aContext.id);
let UI = getUIForWindowID(aContext.id);
// message is not shown in the UI but is required
let mainAction = {
label: UI.chromeWin.gNavigatorBundle.getString('identity.next.label'),
accessKey: UI.chromeWin.gNavigatorBundle.getString('identity.next.accessKey'),
callback: function() {} // required
};
let secondaryActions = [];
let options = {
context: aContext
};
UI.chromeWin.PopupNotifications.show(UI.browser,
'identity-request', aContext.id,
'identity-notification-icon', mainAction,
[], options);
}
function HostFrame() {
this._iframe = null;
this._resizeWatcher = null;
}
HostFrame.prototype = {
/*
* getIframe - adds iframe to aOptions
*/
getIframe: function HostFrame_getIframe(aOptions, aCallback) {
if (this._gotIframe) {
logger.error("Can only get iframe once with HostFrame helper");
return;
}
this._createIframe(aOptions);
aCallback();
},
cleanUp: function HostFrame_cleanUp() {
if (this._resizeWatcher) {
this._resizeWatcher.stop();
}
},
/*
* create an iframe and insert it into aOptions. If showUI is
* true, attach the iframe to a xul panel in the popup notification.
* Otherwise attach to a hidden document.
*/
_createIframe: function HostFrame_createIframe(aOptions) {
let srcURI = aOptions.showUI ? kIdentityScreen : kIdentityFrame;
logger.log('showUI is', aOptions.showUI, 'so iframe src =', srcURI);
let hiddenDoc = Services.appShell.hiddenDOMWindow.document;
this._iframe = hiddenDoc.createElementNS('http://www.w3.org/1999/xhtml', 'iframe');
this._iframe.setAttribute('mozbrowser', true);
this._iframe.setAttribute('mozframetype', 'content');
this._iframe.setAttribute('type', 'content');
this._iframe.setAttribute('remote', true);
this._iframe.setAttribute('id', 'persona-host-frame');
this._iframe.setAttribute('src', srcURI);
// implement a dynamic resize watcher a la Social API
this._iframe.style.height = "440px";
this._iframe.style.width = "300px";
aOptions.iframe = this._iframe;
if (aOptions.showUI) {
// synchronous, so we can call _injectShim below with no race condition
requestUI(aOptions);
this._resizeWatcher = new ResizeWatcher(this._iframe);
} else {
hiddenDoc.documentElement.appendChild(this._iframe);
}
this._injectShim(this._iframe);
},
_injectShim: function HostFrame_injectShim(aIframe) {
let mm = aIframe.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
logger.log('loadFrameScript:', kIdentityShim);
mm.loadFrameScript(kIdentityShim, true);
}
};
function Pipe(aOptions, aController) {
this.options = aOptions;
this.controller = aController;
this.mm = null;
this._closed = false;
return this;
}
Pipe.prototype = {
observe: function pipe_observe(aSubject, aTopic, aData) {
logger.log('pipe observed', aTopic);
switch (aTopic) {
case 'identity-delegate-canceled':
this._close();
this.controller.serviceDoMethod({method: 'cancel'}, this.options.id);
break;
default:
logger.error('pipe observed unexpected topic: ' + aTopic);
break;
}
},
_close: function pipe__delegateClose() {
this._closed = true;
Services.obs.removeObserver(this, 'identity-delegate-canceled');
if (this.mm) {
this.mm.removeMessageListener('identity-service-doMethod', this._serviceDoMethod);
this.mm.removeMessageListener('identity-delegate-complete', this._delegateComplete);
this.mm.removeMessageListener('identity-delegate-loaded', this._delegateLoaded);
}
let subject = Cc['@mozilla.org/supports-string;1'].createInstance(Ci.nsISupportsString);
subject.data = this.options.id;
Services.obs.notifyObservers(subject, 'identity-delegate-ui-close', null);
if (typeof this.options.onComplete === 'function') {
this.options.onComplete();
}
},
_delegateLoaded: function pipe__delegateLoaded() {
this.mm.sendAsyncMessage(this.options.message, this.options.rpOptions);
//this.resizer = new DynamicResizeWatcher();
//this.resizer.start(
},
_delegateComplete: function pipe__delegateComplete() {
this._close();
},
_serviceDoMethod: function pipe__doMethod(aMethodOptions) {
let message = aMethodOptions.json;
if (typeof message === 'string') {
try {
message = JSON.parse(message);
} catch (err) {
logger.error('Bad json message: ' + message);
return;
}
}
this.controller.serviceDoMethod(message, this.options.id);
},
communicate: function pipe_communicate() {
if (this._closed) {
logger.error('Cannot communicate with persona frame; pipe already closed');
return;
}
Services.obs.addObserver(this, 'identity-delegate-canceled', false);
let frameLoader = this.options.iframe.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
if (frameLoader) {
this.mm = frameLoader.messageManager;
this.mm.addMessageListener('identity-service-doMethod', this._serviceDoMethod.bind(this));
this.mm.addMessageListener('identity-delegate-loaded', this._delegateLoaded.bind(this));
this.mm.addMessageListener('identity-delegate-complete', this._delegateComplete.bind(this));
} else {
logger.error('FrameLoader unavailable; Frame did not get attached properly?');
}
}
};
this.SignInToWebsiteUX = { this.SignInToWebsiteUX = {
init: function SignInToWebsiteUX_init() { init: function SignInToWebsiteUX_init() {
this.contexts = {};
/* Services.obs.addObserver(this, 'identity-controller-watch', false);
* bug 793906 - temporarily disabling desktop UI so we can Services.obs.addObserver(this, 'identity-controller-request', false);
* focus on b2g without worrying about desktop as well Services.obs.addObserver(this, 'identity-controller-logout', false);
* Services.obs.addObserver(this, 'identity-controller-canceled', false);
Services.obs.addObserver(this, "identity-request", false);
Services.obs.addObserver(this, "identity-auth", false);
Services.obs.addObserver(this, "identity-auth-complete", false);
Services.obs.addObserver(this, "identity-login-state-changed", false);
*/
}, },
uninit: function SignInToWebsiteUX_uninit() { uninit: function SignInToWebsiteUX_uninit() {
/* Services.obs.removeObserver(this, 'identity-controller-watch');
* As above: Services.obs.removeObserver(this, 'identity-controller-request');
* bug 793906 - temporarily disabling desktop UI so we can Services.obs.removeObserver(this, 'identity-controller-logout');
* focus on b2g without worrying about desktop as well Services.obs.removeObserver(this, 'identity-controller-canceled');
*
Services.obs.removeObserver(this, "identity-request");
Services.obs.removeObserver(this, "identity-auth");
Services.obs.removeObserver(this, "identity-auth-complete");
Services.obs.removeObserver(this, "identity-login-state-changed");
*/
}, },
observe: function SignInToWebsiteUX_observe(aSubject, aTopic, aData) { observe: function SignInToWebsiteUX_observe(aSubject, aTopic, aData) {
log("observe: received", aTopic, "with", aData, "for", aSubject); logger.log('controller observed:', aTopic);
let options = null; // XXX need to detect page unload of any of our flows
// XXX we get strings from xul, and objects from elsewhere
let rpOptions = {};
if (aSubject) { if (aSubject) {
options = aSubject.wrappedJSObject; if (aSubject.wrappedJSObject) {
rpOptions = aSubject.wrappedJSObject;
} else {
rpOptions = {id: aSubject.QueryInterface(Ci.nsISupportsString).data};
}
} }
switch(aTopic) { if (!rpOptions.id) {
case "identity-request": logger.error('Got a message with no RP id');
this.requestLogin(options); return;
}
let rpId = rpOptions.id;
let UI = getUIForWindowID(rpId);
let options = {
id: rpOptions.id,
rpOptions: rpOptions
};
switch (aTopic) {
case 'identity-controller-watch':
this.doWatch(options);
break; break;
case "identity-auth":
this._openAuthenticationUI(aData, options); case 'identity-controller-request':
this.doRequest(options);
break; break;
case "identity-auth-complete":
this._closeAuthenticationUI(aData); case 'identity-controller-logout':
this.doLogout(options);
break; break;
case "identity-login-state-changed":
let emailAddress = aData; default:
if (emailAddress) { logger.error('SignInToWebsiteUX', 'Unknown observer notification:', aTopic);
this._removeRequestUI(options); break;
this._showLoggedInUI(emailAddress, options); }
},
serviceDoMethod: function SignInToWebsiteUX_doMethod(aMessage, aId) {
logger.log('serviceDoMethod received:', aMessage);
switch (aMessage.method) {
case 'ready':
IdentityService.doReady(aId);
break;
case 'login':
if (aMessage._internalParams) {
IdentityService.doLogin(aId, aMessage.assertion, aMessage._internalParams);
} else { } else {
this._removeLoggedInUI(options); IdentityService.doLogin(aId, aMessage.assertion);
} }
break; break;
case 'logout':
IdentityService.doLogout(aId);
break;
case 'cancel':
IdentityService.doCancel(aId);
break;
default: default:
Logger.reportError("SignInToWebsiteUX", "Unknown observer notification:", aTopic); logger.error('Unknown identity method: ' + aMessage.method);
break; break;
} }
}, },
/** cleanUp: function SignInToWebsiteUX_cleanUp(aId) {
* The website is requesting login so the user must choose an identity to use. let context = this.contexts[aId];
*/ if (context) {
requestLogin: function SignInToWebsiteUX_requestLogin(aOptions) { if (context.hostFrame) {
let windowID = aOptions.rpId; context.hostFrame.cleanUp();
log("requestLogin", aOptions); }
let [chromeWin, browserEl] = this._getUIForWindowID(windowID); if (context.iframe && context.iframe.parentNode) {
logger.log("removing iframe from parent node and deleting it");
// message is not shown in the UI but is required context.iframe.parentNode.removeChild(context.iframe);
let message = aOptions.origin; delete context.iframe;
let mainAction = { }
label: chromeWin.gNavigatorBundle.getString("identity.next.label"), this.contexts[aId] = {};
accessKey: chromeWin.gNavigatorBundle.getString("identity.next.accessKey"), delete this.contexts[aId];
callback: function() {}, // required
};
let options = {
identity: {
origin: aOptions.origin,
},
};
let secondaryActions = [];
// add some extra properties to the notification to store some identity-related state
for (let opt in aOptions) {
options.identity[opt] = aOptions[opt];
} }
log("requestLogin: rpId: ", options.identity.rpId);
chromeWin.PopupNotifications.show(browserEl, "identity-request", message,
"identity-notification-icon", mainAction,
[], options);
}, },
/** delegate: function SignInToWebsiteUX_delegate(aOptions) {
* Get the list of possible identities to login to the given origin. let hostFrame = new HostFrame();
*/ hostFrame.getIframe(aOptions, function() {
getIdentitiesForSite: function SignInToWebsiteUX_getIdentitiesForSite(aOrigin) { // iframe has been added to aOptions
return IdentityService.RP.getIdentitiesForSite(aOrigin);
// callback for the pipe when flow is complete
aOptions.onComplete = function pipe_onComplete() {
this.cleanUp(aOptions.id);
}.bind(this);
// store context and communicate with pipe
this.contexts[aOptions.id] = aOptions;
this.contexts[aOptions.id].hostFrame = hostFrame;
let pipe = new Pipe(aOptions, this);
pipe.communicate();
}.bind(this));
}, },
/** doWatch: function SignInToWebsiteUX_doWatch(aOptions) {
* User chose a new or existing identity from the doorhanger after a request() call aOptions.message = 'identity-delegate-watch';
*/ aOptions.showUI = false;
selectIdentity: function SignInToWebsiteUX_selectIdentity(aRpId, aIdentity) { this.delegate(aOptions);
log("selectIdentity: rpId: ", aRpId, " identity: ", aIdentity);
IdentityService.selectIdentity(aRpId, aIdentity);
}, },
// Private doRequest: function SignInToWebsiteUX_doRequest(aOptions) {
aOptions.message = 'identity-delegate-request';
/** aOptions.showUI = true;
* Return the chrome window and <browser> for the given outer window ID. this.delegate(aOptions);
*/
_getUIForWindowID: function(aWindowID) {
let someWindow = Services.wm.getMostRecentWindow("navigator:browser");
if (!someWindow) {
Logger.reportError("SignInToWebsiteUX", "no window");
return [null, null];
}
let windowUtils = someWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let content = windowUtils.getOuterWindowWithId(aWindowID);
if (content) {
let browser = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell).chromeEventHandler;
let chromeWin = browser.ownerDocument.defaultView;
return [chromeWin, browser];
}
Logger.reportError("SignInToWebsiteUX", "no content");
return [null, null];
},
/**
* Open UI with a content frame displaying aAuthURI so that the user can authenticate with their
* IDP. Then tell Identity.jsm the identifier for the window so that it knows that the DOM API
* calls are for this authentication flow.
*/
_openAuthenticationUI: function _openAuthenticationUI(aAuthURI, aContext) {
// Open a tab/window with aAuthURI with an identifier (aID) attached so that the DOM APIs know this is an auth. window.
let chromeWin = Services.wm.getMostRecentWindow('navigator:browser');
let features = "chrome=false,width=640,height=480,centerscreen,location=yes,resizable=yes,scrollbars=yes,status=yes";
log("aAuthURI: ", aAuthURI);
let authWin = Services.ww.openWindow(chromeWin, "about:blank", "", features, null);
let windowID = authWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
log("authWin outer id: ", windowID);
let provId = aContext.provId;
// Tell the ID service about the id before loading the url
IdentityService.IDP.setAuthenticationFlow(windowID, provId);
authWin.location = aAuthURI;
},
_closeAuthenticationUI: function _closeAuthenticationUI(aAuthId) {
log("_closeAuthenticationUI:", aAuthId);
let [chromeWin, browserEl] = this._getUIForWindowID(aAuthId);
if (chromeWin)
chromeWin.close();
else
Logger.reportError("SignInToWebsite", "Could not close window with ID", aAuthId);
},
/**
* Show a doorhanger indicating the currently logged-in user.
*/
_showLoggedInUI: function _showLoggedInUI(aIdentity, aContext) {
let windowID = aContext.rpId;
log("_showLoggedInUI for ", windowID);
let [chromeWin, browserEl] = this._getUIForWindowID(windowID);
let message = chromeWin.gNavigatorBundle.getFormattedString("identity.loggedIn.description",
[aIdentity]);
let mainAction = {
label: chromeWin.gNavigatorBundle.getString("identity.loggedIn.signOut.label"),
accessKey: chromeWin.gNavigatorBundle.getString("identity.loggedIn.signOut.accessKey"),
callback: function() {
log("sign out callback fired");
IdentityService.RP.logout(windowID);
},
};
let secondaryActions = [];
let options = {
dismissed: true,
};
let loggedInNot = chromeWin.PopupNotifications.show(browserEl, "identity-logged-in", message,
"identity-notification-icon", mainAction,
secondaryActions, options);
loggedInNot.rpId = windowID;
},
/**
* Remove the doorhanger indicating the currently logged-in user.
*/
_removeLoggedInUI: function _removeLoggedInUI(aContext) {
let windowID = aContext.rpId;
log("_removeLoggedInUI for ", windowID);
if (!windowID)
throw "_removeLoggedInUI: Invalid RP ID";
let [chromeWin, browserEl] = this._getUIForWindowID(windowID);
let loggedInNot = chromeWin.PopupNotifications.getNotification("identity-logged-in", browserEl);
if (loggedInNot)
chromeWin.PopupNotifications.remove(loggedInNot);
},
/**
* Remove the doorhanger indicating the currently logged-in user.
*/
_removeRequestUI: function _removeRequestUI(aContext) {
let windowID = aContext.rpId;
log("_removeRequestUI for ", windowID);
let [chromeWin, browserEl] = this._getUIForWindowID(windowID);
let requestNot = chromeWin.PopupNotifications.getNotification("identity-request", browserEl);
if (requestNot)
chromeWin.PopupNotifications.remove(requestNot);
}, },
doLogout: function SignInToWebsiteUX_doLogout(aOptions) {
aOptions.message = 'identity-delegate-logout';
aOptions.showUI = false;
this.delegate(aOptions);
}
}; };

View File

@ -14,23 +14,16 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/identity/IdentityUtils.jsm"); Cu.import("resource://gre/modules/identity/IdentityUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "IdentityService", XPCOMUtils.defineLazyModuleGetter(this, "IdentityService",
#ifdef MOZ_B2G_VERSION
"resource://gre/modules/identity/MinimalIdentity.jsm"); "resource://gre/modules/identity/MinimalIdentity.jsm");
#else
"resource://gre/modules/identity/Identity.jsm");
#endif
XPCOMUtils.defineLazyModuleGetter(this,
"Logger",
"resource://gre/modules/identity/LogUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "ppmm", XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
"@mozilla.org/parentprocessmessagemanager;1", "@mozilla.org/parentprocessmessagemanager;1",
"nsIMessageListenerManager"); "nsIMessageListenerManager");
function log(...aMessageArgs) { XPCOMUtils.defineLazyGetter(this, "logger", function() {
Logger.log.apply(Logger, ["DOMIdentity"].concat(aMessageArgs)); Cu.import('resource://gre/modules/identity/LogUtils.jsm');
} return getLogger("Identity", "toolkit.identity.debug");
});
function IDDOMMessage(aOptions) { function IDDOMMessage(aOptions) {
objectCopy(aOptions, this); objectCopy(aOptions, this);
@ -55,14 +48,14 @@ IDPProvisioningContext.prototype = {
}, },
doGenKeyPairCallback: function IDPPC_doGenKeyPairCallback(aPublicKey) { doGenKeyPairCallback: function IDPPC_doGenKeyPairCallback(aPublicKey) {
log("doGenKeyPairCallback"); logger.log("doGenKeyPairCallback");
let message = new IDDOMMessage({id: this.id}); let message = new IDDOMMessage({id: this.id});
message.publicKey = aPublicKey; message.publicKey = aPublicKey;
this._mm.sendAsyncMessage("Identity:IDP:CallGenKeyPairCallback", message); this._mm.sendAsyncMessage("Identity:IDP:CallGenKeyPairCallback", message);
}, },
doError: function(msg) { doError: function(msg) {
log("Provisioning ERROR: " + msg); logger.warning(msg);
} }
}; };
@ -84,7 +77,7 @@ IDPAuthenticationContext.prototype = {
}, },
doError: function IDPAC_doError(msg) { doError: function IDPAC_doError(msg) {
log("Authentication ERROR: " + msg); logger.warning(msg);
} }
}; };
@ -93,7 +86,9 @@ function RPWatchContext(aOptions, aTargetMM) {
// id and origin are required // id and origin are required
if (! (this.id && this.origin)) { if (! (this.id && this.origin)) {
throw new Error("id and origin are required for RP watch context"); let err = "id and origin are required for RP watch context";
logger.error(err);
throw new Error(err);
} }
// default for no loggedInUser is undefined, not null // default for no loggedInUser is undefined, not null
@ -107,7 +102,7 @@ function RPWatchContext(aOptions, aTargetMM) {
RPWatchContext.prototype = { RPWatchContext.prototype = {
doLogin: function RPWatchContext_onlogin(aAssertion, aMaybeInternalParams) { doLogin: function RPWatchContext_onlogin(aAssertion, aMaybeInternalParams) {
log("doLogin: " + this.id); logger.log("login id: " + this.id);
let message = new IDDOMMessage({id: this.id, assertion: aAssertion}); let message = new IDDOMMessage({id: this.id, assertion: aAssertion});
if (aMaybeInternalParams) { if (aMaybeInternalParams) {
message._internalParams = aMaybeInternalParams; message._internalParams = aMaybeInternalParams;
@ -116,19 +111,19 @@ RPWatchContext.prototype = {
}, },
doLogout: function RPWatchContext_onlogout() { doLogout: function RPWatchContext_onlogout() {
log("doLogout: " + this.id); logger.log("logout id: " + this.id);
let message = new IDDOMMessage({id: this.id}); let message = new IDDOMMessage({id: this.id});
this._mm.sendAsyncMessage("Identity:RP:Watch:OnLogout", message); this._mm.sendAsyncMessage("Identity:RP:Watch:OnLogout", message);
}, },
doReady: function RPWatchContext_onready() { doReady: function RPWatchContext_onready() {
log("doReady: " + this.id); logger.log("ready id: " + this.id);
let message = new IDDOMMessage({id: this.id}); let message = new IDDOMMessage({id: this.id});
this._mm.sendAsyncMessage("Identity:RP:Watch:OnReady", message); this._mm.sendAsyncMessage("Identity:RP:Watch:OnReady", message);
}, },
doCancel: function RPWatchContext_oncancel() { doCancel: function RPWatchContext_oncancel() {
log("doCancel: " + this.id); logger.log("cancel id: " + this.id);
let message = new IDDOMMessage({id: this.id}); let message = new IDDOMMessage({id: this.id});
this._mm.sendAsyncMessage("Identity:RP:Watch:OnCancel", message); this._mm.sendAsyncMessage("Identity:RP:Watch:OnCancel", message);
}, },
@ -147,6 +142,8 @@ this.DOMIdentity = {
// used to send replies back to the proper window. // used to send replies back to the proper window.
let targetMM = aMessage.target; let targetMM = aMessage.target;
logger.log("received:", aMessage.name);
switch (aMessage.name) { switch (aMessage.name) {
// RP // RP
case "Identity:RP:Watch": case "Identity:RP:Watch":
@ -217,6 +214,7 @@ this.DOMIdentity = {
Services.ww.registerNotification(this); Services.ww.registerNotification(this);
Services.obs.addObserver(this, "xpcom-shutdown", false); Services.obs.addObserver(this, "xpcom-shutdown", false);
this._subscribeListeners(); this._subscribeListeners();
logger.log("DOM identity service initialized");
}, },
_subscribeListeners: function DOMIdentity__subscribeListeners() { _subscribeListeners: function DOMIdentity__subscribeListeners() {
@ -234,16 +232,18 @@ this.DOMIdentity = {
}, },
_resetFrameState: function(aContext) { _resetFrameState: function(aContext) {
log("_resetFrameState: ", aContext.id); logger.log("_resetFrameState: ", aContext.id);
if (!aContext._mm) { if (!aContext._mm) {
throw new Error("ERROR: Trying to reset an invalid context"); let err = "Trying to reset an invalid context";
logger.error(err);
throw new Error(err);
} }
let message = new IDDOMMessage({id: aContext.id}); let message = new IDDOMMessage({id: aContext.id});
aContext._mm.sendAsyncMessage("Identity:ResetState", message); aContext._mm.sendAsyncMessage("Identity:ResetState", message);
}, },
_watch: function DOMIdentity__watch(message, targetMM) { _watch: function DOMIdentity__watch(message, targetMM) {
log("DOMIdentity__watch: " + message.id); logger.log("DOMIdentity__watch: " + message.id);
// Pass an object with the watch members to Identity.jsm so it can call the // Pass an object with the watch members to Identity.jsm so it can call the
// callbacks. // callbacks.
let context = new RPWatchContext(message, targetMM); let context = new RPWatchContext(message, targetMM);

View File

@ -23,12 +23,18 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/identity/IdentityUtils.jsm"); Cu.import("resource://gre/modules/identity/IdentityUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "logger", function() {
Cu.import('resource://gre/modules/identity/LogUtils.jsm');
return getLogger("Identity", "toolkit.identity.debug");
});
// This is the child process corresponding to nsIDOMIdentity // This is the child process corresponding to nsIDOMIdentity
XPCOMUtils.defineLazyServiceGetter(this, "cpmm", XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1", "@mozilla.org/childprocessmessagemanager;1",
"nsIMessageSender"); "nsIMessageSender");
function nsDOMIdentity(aIdentityInternal) { function nsDOMIdentity(aIdentityInternal) {
logger.log("nsDOMIdentity constructor");
this._identityInternal = aIdentityInternal; this._identityInternal = aIdentityInternal;
} }
nsDOMIdentity.prototype = { nsDOMIdentity.prototype = {
@ -65,6 +71,7 @@ nsDOMIdentity.prototype = {
*/ */
watch: function nsDOMIdentity_watch(aOptions) { watch: function nsDOMIdentity_watch(aOptions) {
logger.log(aOptions);
if (this._rpWatcher) { if (this._rpWatcher) {
throw new Error("navigator.id.watch was already called"); throw new Error("navigator.id.watch was already called");
} }
@ -89,6 +96,7 @@ nsDOMIdentity.prototype = {
} }
let message = this.DOMIdentityMessage(aOptions); let message = this.DOMIdentityMessage(aOptions);
logger.log(message);
// loggedInUser vs loggedInEmail // loggedInUser vs loggedInEmail
// https://developer.mozilla.org/en-US/docs/DOM/navigator.id.watch // https://developer.mozilla.org/en-US/docs/DOM/navigator.id.watch
@ -98,6 +106,7 @@ nsDOMIdentity.prototype = {
checkRenamed(aOptions, "loggedInEmail", "loggedInUser"); checkRenamed(aOptions, "loggedInEmail", "loggedInUser");
message["loggedInUser"] = aOptions["loggedInUser"]; message["loggedInUser"] = aOptions["loggedInUser"];
logger.log(message);
let emailType = typeof(aOptions["loggedInUser"]); let emailType = typeof(aOptions["loggedInUser"]);
if (aOptions["loggedInUser"] && aOptions["loggedInUser"] !== "undefined") { if (aOptions["loggedInUser"] && aOptions["loggedInUser"] !== "undefined") {
if (emailType !== "string") { if (emailType !== "string") {
@ -113,13 +122,14 @@ nsDOMIdentity.prototype = {
// Set loggedInUser in this block that "undefined" doesn't get through. // Set loggedInUser in this block that "undefined" doesn't get through.
message.loggedInUser = aOptions.loggedInUser; message.loggedInUser = aOptions.loggedInUser;
} }
this._log("loggedInUser: " + message.loggedInUser); logger.log("loggedInUser:", message.loggedInUser);
this._rpWatcher = aOptions; this._rpWatcher = aOptions;
this._identityInternal._mm.sendAsyncMessage("Identity:RP:Watch", message); this._identityInternal._mm.sendAsyncMessage("Identity:RP:Watch", message);
}, },
request: function nsDOMIdentity_request(aOptions) { request: function nsDOMIdentity_request(aOptions) {
logger.log(aOptions);
let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor) let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils); .getInterface(Ci.nsIDOMWindowUtils);
@ -128,7 +138,7 @@ nsDOMIdentity.prototype = {
// getVerifiedEmail() calls, which make use of an RP context // getVerifiedEmail() calls, which make use of an RP context
// marked as _internal. // marked as _internal.
if (this.nativeEventsRequired && !util.isHandlingUserInput && !aOptions._internal) { if (this.nativeEventsRequired && !util.isHandlingUserInput && !aOptions._internal) {
this._log("request: rejecting non-native event"); error("request: rejecting non-native event");
return; return;
} }
@ -171,6 +181,7 @@ nsDOMIdentity.prototype = {
}, },
logout: function nsDOMIdentity_logout() { logout: function nsDOMIdentity_logout() {
logger.log("logout");
if (!this._rpWatcher) { if (!this._rpWatcher) {
throw new Error("navigator.id.logout called before navigator.id.watch"); throw new Error("navigator.id.logout called before navigator.id.watch");
} }
@ -243,7 +254,7 @@ nsDOMIdentity.prototype = {
}, },
getVerifiedEmail: function nsDOMIdentity_getVerifiedEmail(aCallback) { getVerifiedEmail: function nsDOMIdentity_getVerifiedEmail(aCallback) {
Cu.reportError("WARNING: getVerifiedEmail has been deprecated"); error("WARNING: getVerifiedEmail has been deprecated");
this.get(aCallback, {}); this.get(aCallback, {});
}, },
@ -252,7 +263,7 @@ nsDOMIdentity.prototype = {
*/ */
beginProvisioning: function nsDOMIdentity_beginProvisioning(aCallback) { beginProvisioning: function nsDOMIdentity_beginProvisioning(aCallback) {
this._log("beginProvisioning"); logger.log("beginProvisioning");
if (this._beginProvisioningCallback) { if (this._beginProvisioningCallback) {
throw new Error("navigator.id.beginProvisioning already called."); throw new Error("navigator.id.beginProvisioning already called.");
} }
@ -266,7 +277,7 @@ nsDOMIdentity.prototype = {
}, },
genKeyPair: function nsDOMIdentity_genKeyPair(aCallback) { genKeyPair: function nsDOMIdentity_genKeyPair(aCallback) {
this._log("genKeyPair"); logger.log("genKeyPair");
if (!this._beginProvisioningCallback) { if (!this._beginProvisioningCallback) {
throw new Error("navigator.id.genKeyPair called outside of provisioning"); throw new Error("navigator.id.genKeyPair called outside of provisioning");
} }
@ -283,7 +294,7 @@ nsDOMIdentity.prototype = {
}, },
registerCertificate: function nsDOMIdentity_registerCertificate(aCertificate) { registerCertificate: function nsDOMIdentity_registerCertificate(aCertificate) {
this._log("registerCertificate"); logger.log("registerCertificate");
if (!this._genKeyPairCallback) { if (!this._genKeyPairCallback) {
throw new Error("navigator.id.registerCertificate called outside of provisioning"); throw new Error("navigator.id.registerCertificate called outside of provisioning");
} }
@ -298,7 +309,7 @@ nsDOMIdentity.prototype = {
}, },
raiseProvisioningFailure: function nsDOMIdentity_raiseProvisioningFailure(aReason) { raiseProvisioningFailure: function nsDOMIdentity_raiseProvisioningFailure(aReason) {
this._log("raiseProvisioningFailure '" + aReason + "'"); logger.log("raiseProvisioningFailure '" + aReason + "'");
if (this._provisioningEnded) { if (this._provisioningEnded) {
throw new Error("Provisioning already ended"); throw new Error("Provisioning already ended");
} }
@ -317,7 +328,7 @@ nsDOMIdentity.prototype = {
*/ */
beginAuthentication: function nsDOMIdentity_beginAuthentication(aCallback) { beginAuthentication: function nsDOMIdentity_beginAuthentication(aCallback) {
this._log("beginAuthentication"); logger.log("beginAuthentication");
if (this._beginAuthenticationCallback) { if (this._beginAuthenticationCallback) {
throw new Error("navigator.id.beginAuthentication already called."); throw new Error("navigator.id.beginAuthentication already called.");
} }
@ -405,7 +416,7 @@ nsDOMIdentity.prototype = {
case "Identity:RP:Watch:OnLogin": case "Identity:RP:Watch:OnLogin":
// Do we have a watcher? // Do we have a watcher?
if (!this._rpWatcher) { if (!this._rpWatcher) {
this._log("WARNING: Received OnLogin message, but there is no RP watcher"); logger.warning("Received OnLogin message, but there is no RP watcher");
return; return;
} }
@ -420,7 +431,7 @@ nsDOMIdentity.prototype = {
case "Identity:RP:Watch:OnLogout": case "Identity:RP:Watch:OnLogout":
// Do we have a watcher? // Do we have a watcher?
if (!this._rpWatcher) { if (!this._rpWatcher) {
this._log("WARNING: Received OnLogout message, but there is no RP watcher"); logger.warning("Received OnLogout message, but there is no RP watcher");
return; return;
} }
@ -431,7 +442,7 @@ nsDOMIdentity.prototype = {
case "Identity:RP:Watch:OnReady": case "Identity:RP:Watch:OnReady":
// Do we have a watcher? // Do we have a watcher?
if (!this._rpWatcher) { if (!this._rpWatcher) {
this._log("WARNING: Received OnReady message, but there is no RP watcher"); logger.warning("Received OnReady message, but there is no RP watcher");
return; return;
} }
@ -442,7 +453,7 @@ nsDOMIdentity.prototype = {
case "Identity:RP:Watch:OnCancel": case "Identity:RP:Watch:OnCancel":
// Do we have a watcher? // Do we have a watcher?
if (!this._rpWatcher) { if (!this._rpWatcher) {
this._log("WARNING: Received OnCancel message, but there is no RP watcher"); logger.warning("Received OnCancel message, but there is no RP watcher");
return; return;
} }
@ -462,10 +473,6 @@ nsDOMIdentity.prototype = {
} }
}, },
_log: function nsDOMIdentity__log(msg) {
this._identityInternal._log(msg);
},
_callGenKeyPairCallback: function nsDOMIdentity__callGenKeyPairCallback(message) { _callGenKeyPairCallback: function nsDOMIdentity__callGenKeyPairCallback(message) {
// create a pubkey object that works // create a pubkey object that works
let chrome_pubkey = JSON.parse(message.publicKey); let chrome_pubkey = JSON.parse(message.publicKey);
@ -525,7 +532,7 @@ nsDOMIdentity.prototype = {
}, },
uninit: function DOMIdentity_uninit() { uninit: function DOMIdentity_uninit() {
this._log("nsDOMIdentity uninit()"); logger.log("unwatch", this._id);
this._identityInternal._mm.sendAsyncMessage( this._identityInternal._mm.sendAsyncMessage(
"Identity:RP:Unwatch", "Identity:RP:Unwatch",
{ id: this._id } { id: this._id }
@ -598,7 +605,7 @@ nsDOMIdentityInternal.prototype = {
this._id = util.outerWindowID; this._id = util.outerWindowID;
this._innerWindowID = util.currentInnerWindowID; this._innerWindowID = util.currentInnerWindowID;
this._log("init was called from " + aWindow.document.location); logger.log("init was called from", aWindow.document.location);
this._mm = cpmm; this._mm = cpmm;
@ -623,14 +630,6 @@ nsDOMIdentityInternal.prototype = {
return this._identity; return this._identity;
}, },
// Private.
_log: function nsDOMIdentityInternal__log(msg) {
if (!this._debug) {
return;
}
dump("nsDOMIdentity (" + this._id + "): " + msg + "\n");
},
// Component setup. // Component setup.
classID: Components.ID("{210853d9-2c97-4669-9761-b1ab9cbf57ef}"), classID: Components.ID("{210853d9-2c97-4669-9761-b1ab9cbf57ef}"),

View File

@ -384,7 +384,7 @@ pref("toolkit.telemetry.infoURL", "http://www.mozilla.com/legal/privacy/firefox.
pref("toolkit.telemetry.debugSlowSql", false); pref("toolkit.telemetry.debugSlowSql", false);
// Identity module // Identity module
pref("toolkit.identity.enabled", false); pref("dom.identity.enabled", false);
pref("toolkit.identity.debug", false); pref("toolkit.identity.debug", false);
// Enable deprecation warnings. // Enable deprecation warnings.

View File

@ -15,7 +15,6 @@ const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/identity/LogUtils.jsm");
Cu.import("resource://gre/modules/identity/IdentityStore.jsm"); Cu.import("resource://gre/modules/identity/IdentityStore.jsm");
Cu.import("resource://gre/modules/identity/RelyingParty.jsm"); Cu.import("resource://gre/modules/identity/RelyingParty.jsm");
Cu.import("resource://gre/modules/identity/IdentityProvider.jsm"); Cu.import("resource://gre/modules/identity/IdentityProvider.jsm");
@ -24,12 +23,10 @@ XPCOMUtils.defineLazyModuleGetter(this,
"jwcrypto", "jwcrypto",
"resource://gre/modules/identity/jwcrypto.jsm"); "resource://gre/modules/identity/jwcrypto.jsm");
function log(...aMessageArgs) { XPCOMUtils.defineLazyGetter(this, "logger", function() {
Logger.log.apply(Logger, ["core"].concat(aMessageArgs)); Cu.import('resource://gre/modules/identity/LogUtils.jsm');
} return getLogger("Identity", "toolkit.identity.debug");
function reportError(...aMessageArgs) { });
Logger.reportError.apply(Logger, ["core"].concat(aMessageArgs));
}
function IDService() { function IDService() {
Services.obs.addObserver(this, "quit-application-granted", false); Services.obs.addObserver(this, "quit-application-granted", false);
@ -53,7 +50,7 @@ IDService.prototype = {
if (!aSubject || !aSubject.wrappedJSObject) if (!aSubject || !aSubject.wrappedJSObject)
break; break;
let subject = aSubject.wrappedJSObject; let subject = aSubject.wrappedJSObject;
log("Auth complete:", aSubject.wrappedJSObject); logger.log("Auth complete:", aSubject.wrappedJSObject);
// We have authenticated in order to provision an identity. // We have authenticated in order to provision an identity.
// So try again. // So try again.
this.selectIdentity(subject.rpId, subject.identity); this.selectIdentity(subject.rpId, subject.identity);
@ -73,7 +70,7 @@ IDService.prototype = {
}, },
shutdown: function shutdown() { shutdown: function shutdown() {
log("shutdown"); logger.log("shutdown");
Services.obs.removeObserver(this, "identity-auth-complete"); Services.obs.removeObserver(this, "identity-auth-complete");
Services.obs.removeObserver(this, "quit-application-granted"); Services.obs.removeObserver(this, "quit-application-granted");
}, },
@ -117,12 +114,12 @@ IDService.prototype = {
* (string) the email chosen for login * (string) the email chosen for login
*/ */
selectIdentity: function selectIdentity(aRPId, aIdentity) { selectIdentity: function selectIdentity(aRPId, aIdentity) {
log("selectIdentity: RP id:", aRPId, "identity:", aIdentity); logger.log("selectIdentity: RP id:", aRPId, "identity:", aIdentity);
// Get the RP that was stored when watch() was invoked. // Get the RP that was stored when watch() was invoked.
let rp = this.RP._rpFlows[aRPId]; let rp = this.RP._rpFlows[aRPId];
if (!rp) { if (!rp) {
reportError("selectIdentity", "Invalid RP id: ", aRPId); logger.warning("selectIdentity", "Invalid RP id: ", aRPId);
return; return;
} }
@ -134,7 +131,7 @@ IDService.prototype = {
loggedInUser: aIdentity, loggedInUser: aIdentity,
origin: rp.origin origin: rp.origin
}; };
log("selectIdentity: provId:", provId, "origin:", rp.origin); logger.log("selectIdentity: provId:", provId, "origin:", rp.origin);
// Once we have a cert, and once the user is authenticated with the // Once we have a cert, and once the user is authenticated with the
// IdP, we can generate an assertion and deliver it to the doc. // IdP, we can generate an assertion and deliver it to the doc.
@ -175,7 +172,7 @@ IDService.prototype = {
if (self.IDP._provisionFlows[aProvId].didAuthentication) { if (self.IDP._provisionFlows[aProvId].didAuthentication) {
self.IDP._cleanUpProvisionFlow(aProvId); self.IDP._cleanUpProvisionFlow(aProvId);
self.RP._cleanUpProvisionFlow(aRPId, aProvId); self.RP._cleanUpProvisionFlow(aRPId, aProvId);
log("ERROR: selectIdentity: authentication hard fail"); logger.error("ERROR: selectIdentity: authentication hard fail");
rp.doError("Authentication fail."); rp.doError("Authentication fail.");
return; return;
} }
@ -222,7 +219,7 @@ IDService.prototype = {
if (parsedEmail === null) { if (parsedEmail === null) {
return aCallback("Could not parse email: " + aIdentity); return aCallback("Could not parse email: " + aIdentity);
} }
log("_discoverIdentityProvider: identity:", aIdentity, "domain:", parsedEmail.domain); logger.log("_discoverIdentityProvider: identity:", aIdentity, "domain:", parsedEmail.domain);
this._fetchWellKnownFile(parsedEmail.domain, function fetchedWellKnown(err, idpParams) { this._fetchWellKnownFile(parsedEmail.domain, function fetchedWellKnown(err, idpParams) {
// idpParams includes the pk, authorization url, and // idpParams includes the pk, authorization url, and
@ -251,7 +248,7 @@ IDService.prototype = {
_fetchWellKnownFile: function _fetchWellKnownFile(aDomain, aCallback, aScheme='https') { _fetchWellKnownFile: function _fetchWellKnownFile(aDomain, aCallback, aScheme='https') {
// XXX bug 769854 make tests https and remove aScheme option // XXX bug 769854 make tests https and remove aScheme option
let url = aScheme + '://' + aDomain + "/.well-known/browserid"; let url = aScheme + '://' + aDomain + "/.well-known/browserid";
log("_fetchWellKnownFile:", url); logger.log("_fetchWellKnownFile:", url);
// this appears to be a more successful way to get at xmlhttprequest (which supposedly will close with a window // this appears to be a more successful way to get at xmlhttprequest (which supposedly will close with a window
let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
@ -264,7 +261,7 @@ IDService.prototype = {
req.mozBackgroundRequest = true; req.mozBackgroundRequest = true;
req.onload = function _fetchWellKnownFile_onload() { req.onload = function _fetchWellKnownFile_onload() {
if (req.status < 200 || req.status >= 400) { if (req.status < 200 || req.status >= 400) {
log("_fetchWellKnownFile", url, ": server returned status:", req.status); logger.log("_fetchWellKnownFile", url, ": server returned status:", req.status);
return aCallback("Error"); return aCallback("Error");
} }
try { try {
@ -275,7 +272,7 @@ IDService.prototype = {
idpParams.authentication && idpParams.authentication &&
idpParams['public-key'])) { idpParams['public-key'])) {
let errStr= "Invalid well-known file from: " + aDomain; let errStr= "Invalid well-known file from: " + aDomain;
log("_fetchWellKnownFile:", errStr); logger.log("_fetchWellKnownFile:", errStr);
return aCallback(errStr); return aCallback(errStr);
} }
@ -283,18 +280,18 @@ IDService.prototype = {
domain: aDomain, domain: aDomain,
idpParams: idpParams, idpParams: idpParams,
}; };
log("_fetchWellKnownFile result: ", callbackObj); logger.log("_fetchWellKnownFile result: ", callbackObj);
// Yay. Valid IdP configuration for the domain. // Yay. Valid IdP configuration for the domain.
return aCallback(null, callbackObj); return aCallback(null, callbackObj);
} catch (err) { } catch (err) {
reportError("_fetchWellKnownFile", "Bad configuration from", aDomain, err); logger.warning("_fetchWellKnownFile", "Bad configuration from", aDomain, err);
return aCallback(err.toString()); return aCallback(err.toString());
} }
}; };
req.onerror = function _fetchWellKnownFile_onerror() { req.onerror = function _fetchWellKnownFile_onerror() {
log("_fetchWellKnownFile", "ERROR:", req.status, req.statusText); logger.log("_fetchWellKnownFile", "ERROR:", req.status, req.statusText);
log("ERROR: _fetchWellKnownFile:", err); logger.error("ERROR: _fetchWellKnownFile:", err);
return aCallback("Error"); return aCallback("Error");
}; };
req.send(null); req.send(null);

View File

@ -13,7 +13,6 @@ const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/identity/LogUtils.jsm");
Cu.import("resource://gre/modules/identity/Sandbox.jsm"); Cu.import("resource://gre/modules/identity/Sandbox.jsm");
this.EXPORTED_SYMBOLS = ["IdentityProvider"]; this.EXPORTED_SYMBOLS = ["IdentityProvider"];
@ -23,13 +22,10 @@ XPCOMUtils.defineLazyModuleGetter(this,
"jwcrypto", "jwcrypto",
"resource://gre/modules/identity/jwcrypto.jsm"); "resource://gre/modules/identity/jwcrypto.jsm");
function log(...aMessageArgs) { XPCOMUtils.defineLazyGetter(this, "logger", function() {
Logger.log.apply(Logger, ["IDP"].concat(aMessageArgs)); Cu.import('resource://gre/modules/identity/LogUtils.jsm');
} return getLogger("Identity IDP", "toolkit.identity.debug");
function reportError(...aMessageArgs) { });
Logger.reportError.apply(Logger, ["IDP"].concat(aMessageArgs));
}
function IdentityProviderService() { function IdentityProviderService() {
XPCOMUtils.defineLazyModuleGetter(this, XPCOMUtils.defineLazyModuleGetter(this,
@ -76,7 +72,7 @@ IdentityProviderService.prototype = {
} }
let err = "No provisioning flow found with id " + aProvId; let err = "No provisioning flow found with id " + aProvId;
log("ERROR:", err); logger.warning("ERROR:", err);
if (typeof aErrBack === 'function') { if (typeof aErrBack === 'function') {
aErrBack(err); aErrBack(err);
} }
@ -122,7 +118,7 @@ IdentityProviderService.prototype = {
_provisionIdentity: function _provisionIdentity(aIdentity, aIDPParams, aProvId, aCallback) { _provisionIdentity: function _provisionIdentity(aIdentity, aIDPParams, aProvId, aCallback) {
let provPath = aIDPParams.idpParams.provisioning; let provPath = aIDPParams.idpParams.provisioning;
let url = Services.io.newURI("https://" + aIDPParams.domain, null, null).resolve(provPath); let url = Services.io.newURI("https://" + aIDPParams.domain, null, null).resolve(provPath);
log("_provisionIdentity: identity:", aIdentity, "url:", url); logger.log("_provisionIdentity: identity:", aIdentity, "url:", url);
// If aProvId is not null, then we already have a flow // If aProvId is not null, then we already have a flow
// with a sandbox. Otherwise, get a sandbox and create a // with a sandbox. Otherwise, get a sandbox and create a
@ -130,7 +126,7 @@ IdentityProviderService.prototype = {
if (aProvId) { if (aProvId) {
// Re-use an existing sandbox // Re-use an existing sandbox
log("_provisionIdentity: re-using sandbox in provisioning flow with id:", aProvId); logger.log("_provisionIdentity: re-using sandbox in provisioning flow with id:", aProvId);
this._provisionFlows[aProvId].provisioningSandbox.reload(); this._provisionFlows[aProvId].provisioningSandbox.reload();
} else { } else {
@ -149,7 +145,7 @@ IdentityProviderService.prototype = {
}, },
}; };
log("_provisionIdentity: Created sandbox and provisioning flow with id:", provId); logger.log("_provisionIdentity: Created sandbox and provisioning flow with id:", provId);
// XXX bug 769862 - provisioning flow should timeout after N seconds // XXX bug 769862 - provisioning flow should timeout after N seconds
}.bind(this)); }.bind(this));
@ -167,7 +163,7 @@ IdentityProviderService.prototype = {
* - doGenKeyPairCallback(pk) * - doGenKeyPairCallback(pk)
*/ */
beginProvisioning: function beginProvisioning(aCaller) { beginProvisioning: function beginProvisioning(aCaller) {
log("beginProvisioning:", aCaller.id); logger.log("beginProvisioning:", aCaller.id);
// Expect a flow for this caller already to be underway. // Expect a flow for this caller already to be underway.
let provFlow = this.getProvisionFlow(aCaller.id, aCaller.doError); let provFlow = this.getProvisionFlow(aCaller.id, aCaller.doError);
@ -199,7 +195,7 @@ IdentityProviderService.prototype = {
* @param aReason * @param aReason
*/ */
raiseProvisioningFailure: function raiseProvisioningFailure(aProvId, aReason) { raiseProvisioningFailure: function raiseProvisioningFailure(aProvId, aReason) {
reportError("Provisioning failure", aReason); logger.warning("Provisioning failure", aReason);
// look up the provisioning caller and its callback // look up the provisioning caller and its callback
let provFlow = this.getProvisionFlow(aProvId); let provFlow = this.getProvisionFlow(aProvId);
@ -232,16 +228,16 @@ IdentityProviderService.prototype = {
if (!provFlow.didBeginProvisioning) { if (!provFlow.didBeginProvisioning) {
let errStr = "ERROR: genKeyPair called before beginProvisioning"; let errStr = "ERROR: genKeyPair called before beginProvisioning";
log(errStr); logger.warning(errStr);
provFlow.callback(errStr); provFlow.callback(errStr);
return; return;
} }
// Ok generate a keypair // Ok generate a keypair
jwcrypto.generateKeyPair(jwcrypto.ALGORITHMS.DS160, function gkpCb(err, kp) { jwcrypto.generateKeyPair(jwcrypto.ALGORITHMS.DS160, function gkpCb(err, kp) {
log("in gkp callback"); logger.log("in gkp callback");
if (err) { if (err) {
log("ERROR: genKeyPair:", err); logger.error("ERROR: genKeyPair:", err);
provFlow.callback(err); provFlow.callback(err);
return; return;
} }
@ -250,7 +246,7 @@ IdentityProviderService.prototype = {
// Serialize the publicKey of the keypair and send it back to the // Serialize the publicKey of the keypair and send it back to the
// sandbox. // sandbox.
log("genKeyPair: generated keypair for provisioning flow with id:", aProvId); logger.log("genKeyPair: generated keypair for provisioning flow with id:", aProvId);
provFlow.caller.doGenKeyPairCallback(provFlow.kp.serializedPublicKey); provFlow.caller.doGenKeyPairCallback(provFlow.kp.serializedPublicKey);
}.bind(this)); }.bind(this));
}, },
@ -271,18 +267,18 @@ IdentityProviderService.prototype = {
* being provisioned, provided by the IdP. * being provisioned, provided by the IdP.
*/ */
registerCertificate: function registerCertificate(aProvId, aCert) { registerCertificate: function registerCertificate(aProvId, aCert) {
log("registerCertificate:", aProvId, aCert); logger.log("registerCertificate:", aProvId, aCert);
// look up provisioning caller, make sure it's valid. // look up provisioning caller, make sure it's valid.
let provFlow = this.getProvisionFlow(aProvId); let provFlow = this.getProvisionFlow(aProvId);
if (!provFlow.caller) { if (!provFlow.caller) {
reportError("registerCertificate", "No provision flow or caller"); logger.warning("registerCertificate", "No provision flow or caller");
return; return;
} }
if (!provFlow.kp) { if (!provFlow.kp) {
let errStr = "Cannot register a certificate without a keypair"; let errStr = "Cannot register a certificate without a keypair";
reportError("registerCertificate", errStr); logger.warning("registerCertificate", errStr);
provFlow.callback(errStr); provFlow.callback(errStr);
return; return;
} }
@ -308,7 +304,7 @@ IdentityProviderService.prototype = {
* first-positional-param error. * first-positional-param error.
*/ */
_doAuthentication: function _doAuthentication(aProvId, aIDPParams) { _doAuthentication: function _doAuthentication(aProvId, aIDPParams) {
log("_doAuthentication: provId:", aProvId, "idpParams:", aIDPParams); logger.log("_doAuthentication: provId:", aProvId, "idpParams:", aIDPParams);
// create an authentication caller and its identifier AuthId // create an authentication caller and its identifier AuthId
// stash aIdentity, idpparams, and callback in it. // stash aIdentity, idpparams, and callback in it.
@ -341,7 +337,7 @@ IdentityProviderService.prototype = {
* *
*/ */
beginAuthentication: function beginAuthentication(aCaller) { beginAuthentication: function beginAuthentication(aCaller) {
log("beginAuthentication: caller id:", aCaller.id); logger.log("beginAuthentication: caller id:", aCaller.id);
// Begin the authentication flow after having concluded a provisioning // Begin the authentication flow after having concluded a provisioning
// flow. The aCaller that the DOM gives us will have the same ID as // flow. The aCaller that the DOM gives us will have the same ID as
@ -356,7 +352,7 @@ IdentityProviderService.prototype = {
let identity = this._provisionFlows[authFlow.provId].identity; let identity = this._provisionFlows[authFlow.provId].identity;
// tell the UI to start the authentication process // tell the UI to start the authentication process
log("beginAuthentication: authFlow:", aCaller.id, "identity:", identity); logger.log("beginAuthentication: authFlow:", aCaller.id, "identity:", identity);
return authFlow.caller.doBeginAuthenticationCallback(identity); return authFlow.caller.doBeginAuthenticationCallback(identity);
}, },
@ -368,12 +364,12 @@ IdentityProviderService.prototype = {
* *
*/ */
completeAuthentication: function completeAuthentication(aAuthId) { completeAuthentication: function completeAuthentication(aAuthId) {
log("completeAuthentication:", aAuthId); logger.log("completeAuthentication:", aAuthId);
// look up the AuthId caller, and get its callback. // look up the AuthId caller, and get its callback.
let authFlow = this._authenticationFlows[aAuthId]; let authFlow = this._authenticationFlows[aAuthId];
if (!authFlow) { if (!authFlow) {
reportError("completeAuthentication", "No auth flow with id", aAuthId); logger.warning("completeAuthentication", "No auth flow with id", aAuthId);
return; return;
} }
let provId = authFlow.provId; let provId = authFlow.provId;
@ -399,12 +395,12 @@ IdentityProviderService.prototype = {
* *
*/ */
cancelAuthentication: function cancelAuthentication(aAuthId) { cancelAuthentication: function cancelAuthentication(aAuthId) {
log("cancelAuthentication:", aAuthId); logger.log("cancelAuthentication:", aAuthId);
// look up the AuthId caller, and get its callback. // look up the AuthId caller, and get its callback.
let authFlow = this._authenticationFlows[aAuthId]; let authFlow = this._authenticationFlows[aAuthId];
if (!authFlow) { if (!authFlow) {
reportError("cancelAuthentication", "No auth flow with id:", aAuthId); logger.warning("cancelAuthentication", "No auth flow with id:", aAuthId);
return; return;
} }
let provId = authFlow.provId; let provId = authFlow.provId;
@ -419,7 +415,7 @@ IdentityProviderService.prototype = {
// invoke callback with ERROR. // invoke callback with ERROR.
let errStr = "Authentication canceled by IDP"; let errStr = "Authentication canceled by IDP";
log("ERROR: cancelAuthentication:", errStr); logger.log("ERROR: cancelAuthentication:", errStr);
provFlow.callback(errStr); provFlow.callback(errStr);
}, },
@ -430,7 +426,7 @@ IdentityProviderService.prototype = {
// this is the transition point between the two flows, // this is the transition point between the two flows,
// provision and authenticate. We tell the auth flow which // provision and authenticate. We tell the auth flow which
// provisioning flow it is started from. // provisioning flow it is started from.
log("setAuthenticationFlow: authId:", aAuthId, "provId:", aProvId); logger.log("setAuthenticationFlow: authId:", aAuthId, "provId:", aProvId);
this._authenticationFlows[aAuthId] = { provId: aProvId }; this._authenticationFlows[aAuthId] = { provId: aProvId };
this._provisionFlows[aProvId].authId = aAuthId; this._provisionFlows[aProvId].authId = aAuthId;
}, },
@ -440,7 +436,7 @@ IdentityProviderService.prototype = {
* process. * process.
*/ */
_createProvisioningSandbox: function _createProvisioningSandbox(aURL, aCallback) { _createProvisioningSandbox: function _createProvisioningSandbox(aURL, aCallback) {
log("_createProvisioningSandbox:", aURL); logger.log("_createProvisioningSandbox:", aURL);
if (!this._sandboxConfigured) { if (!this._sandboxConfigured) {
// Configure message manager listening on the hidden window // Configure message manager listening on the hidden window
@ -456,7 +452,7 @@ IdentityProviderService.prototype = {
* Load the authentication UI to start the authentication process. * Load the authentication UI to start the authentication process.
*/ */
_beginAuthenticationFlow: function _beginAuthenticationFlow(aProvId, aURL) { _beginAuthenticationFlow: function _beginAuthenticationFlow(aProvId, aURL) {
log("_beginAuthenticationFlow:", aProvId, aURL); logger.log("_beginAuthenticationFlow:", aProvId, aURL);
let propBag = {provId: aProvId}; let propBag = {provId: aProvId};
Services.obs.notifyObservers({wrappedJSObject:propBag}, "identity-auth", aURL); Services.obs.notifyObservers({wrappedJSObject:propBag}, "identity-auth", aURL);
@ -467,14 +463,14 @@ IdentityProviderService.prototype = {
* that may be attached to it. * that may be attached to it.
*/ */
_cleanUpProvisionFlow: function _cleanUpProvisionFlow(aProvId) { _cleanUpProvisionFlow: function _cleanUpProvisionFlow(aProvId) {
log('_cleanUpProvisionFlow:', aProvId); logger.log('_cleanUpProvisionFlow:', aProvId);
let prov = this._provisionFlows[aProvId]; let prov = this._provisionFlows[aProvId];
// Clean up the sandbox, if there is one. // Clean up the sandbox, if there is one.
if (prov.provisioningSandbox) { if (prov.provisioningSandbox) {
let sandbox = this._provisionFlows[aProvId]['provisioningSandbox']; let sandbox = this._provisionFlows[aProvId]['provisioningSandbox'];
if (sandbox.free) { if (sandbox.free) {
log('_cleanUpProvisionFlow: freeing sandbox'); logger.log('_cleanUpProvisionFlow: freeing sandbox');
sandbox.free(); sandbox.free();
} }
delete this._provisionFlows[aProvId]['provisioningSandbox']; delete this._provisionFlows[aProvId]['provisioningSandbox'];

View File

@ -23,12 +23,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
"@mozilla.org/uuid-generator;1", "@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator"); "nsIUUIDGenerator");
XPCOMUtils.defineLazyModuleGetter(this, "Logger", XPCOMUtils.defineLazyGetter(this, "logger", function() {
"resource://gre/modules/identity/LogUtils.jsm"); Cu.import('resource://gre/modules/identity/LogUtils.jsm');
return getLogger("Identity", "toolkit.identity.debug");
function log(...aMessageArgs) { });
Logger.log.apply(Logger, ["Identity"].concat(aMessageArgs));
}
function defined(item) { function defined(item) {
return typeof item !== 'undefined'; return typeof item !== 'undefined';
@ -36,7 +34,7 @@ function defined(item) {
var checkDeprecated = this.checkDeprecated = function checkDeprecated(aOptions, aField) { var checkDeprecated = this.checkDeprecated = function checkDeprecated(aOptions, aField) {
if (defined(aOptions[aField])) { if (defined(aOptions[aField])) {
log("WARNING: field is deprecated:", aField); logger.log("WARNING: field is deprecated:", aField);
return true; return true;
} }
return false; return false;
@ -46,7 +44,7 @@ this.checkRenamed = function checkRenamed(aOptions, aOldName, aNewName) {
if (defined(aOptions[aOldName]) && if (defined(aOptions[aOldName]) &&
defined(aOptions[aNewName])) { defined(aOptions[aNewName])) {
let err = "You cannot provide both " + aOldName + " and " + aNewName; let err = "You cannot provide both " + aOldName + " and " + aNewName;
Logger.reportError(err); logger.error(err);
throw new Error(err); throw new Error(err);
} }
@ -64,7 +62,7 @@ this.getRandomId = function getRandomId() {
* copy source object into target, excluding private properties * copy source object into target, excluding private properties
* (those whose names begin with an underscore) * (those whose names begin with an underscore)
*/ */
this.objectCopy = function objectCopy(source, target){ this.objectCopy = function objectCopy(source, target) {
let desc; let desc;
Object.getOwnPropertyNames(source).forEach(function(name) { Object.getOwnPropertyNames(source).forEach(function(name) {
if (name[0] !== '_') { if (name[0] !== '_') {

View File

@ -6,34 +6,45 @@
"use strict"; "use strict";
this.EXPORTED_SYMBOLS = ["Logger"]; this.EXPORTED_SYMBOLS = ["Logger", "getLogger"];
const PREF_DEBUG = "toolkit.identity.debug";
const Cu = Components.utils; const Cu = Components.utils;
const Ci = Components.interfaces; const Ci = Components.interfaces;
const Cc = Components.classes;
const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
function IdentityLogger() { function Logger(aIdentifier, aEnablingPref) {
Services.prefs.addObserver(PREF_DEBUG, this, false); this._identifier = aIdentifier;
this._debug = Services.prefs.getBoolPref(PREF_DEBUG); this._enablingPref = aEnablingPref;
return this;
// Enabled by default if a pref for toggling the logger is not given
this._enabled = !this._enablingPref;
this.init();
} }
IdentityLogger.prototype = { Logger.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]), QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
init: function Logger_init() {
if (this._enablingPref) {
Services.prefs.addObserver(this._enablingPref, this, false);
this._enabled = Services.prefs.getBoolPref(this._enablingPref);
}
},
observe: function observe(aSubject, aTopic, aData) { observe: function observe(aSubject, aTopic, aData) {
switch(aTopic) { switch (aTopic) {
case "nsPref:changed": case "nsPref:changed":
this._debug = Services.prefs.getBoolPref(PREF_DEBUG); this._enabled = Services.prefs.getBoolPref(this._enablingPref);
dump("LogUtils " +
(this._enabled ? "enabled" : "disabled") +
" for " + this._identifier + "\n");
break; break;
case "quit-application-granted": case "quit-application-granted":
Services.prefs.removeObserver(PREF_DEBUG, this); Services.prefs.removeObserver(this._enablingPref, this);
break; break;
default: default:
@ -42,28 +53,64 @@ IdentityLogger.prototype = {
} }
}, },
_generateLogMessage: function _generateLogMessage(aPrefix, args) { _generatePrefix: function _generatePrefix() {
// create a string representation of a list of arbitrary things let caller = Components.stack.caller.caller;
let parts = ['[' + this._identifier + ']'];
// filename could be like path/to/foo.js or Scratchpad/1
if (caller.filename) {
let path = caller.filename.split('/');
if (path[path.length - 1].match(/\./)) {
parts.push(path[path.length - 1])
} else {
parts.push(caller.filename);
}
}
// Might not be called from a function; might be top-level
if (caller.name) {
parts.push(caller.name + '()');
}
parts.push('line ' + caller.lineNumber + ': ');
return parts.join(' ');
},
_generateLogMessage: function _generateLogMessage(severity, argList) {
let strings = []; let strings = [];
argList.forEach(function(arg) {
// XXX bug 770418 - args look like flattened array, not list of strings if (arg === null) {
args.forEach(function(arg) {
if (typeof arg === 'string') {
strings.push(arg);
} else if (typeof arg === 'undefined') {
strings.push('undefined');
} else if (arg === null) {
strings.push('null'); strings.push('null');
} else { } else {
try { switch (typeof arg) {
strings.push(JSON.stringify(arg, null, 2)); case 'string':
} catch(err) { strings.push(arg);
strings.push("<<something>>"); break;
case 'undefined':
strings.push('undefined');
break;
case 'function':
strings.push('<<function>>');
break;
case 'object':
try {
strings.push(JSON.stringify(arg, null, 2));
} catch (err) {
strings.push('<<object>>');
}
break;
default:
try {
strings.push(arg.toString());
} catch (err) {
strings.push('<<something>>');
}
break;
} }
} }
}); });
return 'Identity ' + aPrefix + ': ' + strings.join(' '); return strings.join(' ');
}, },
/** /**
@ -71,33 +118,61 @@ IdentityLogger.prototype = {
* *
* Enable with about:config pref toolkit.identity.debug * Enable with about:config pref toolkit.identity.debug
*/ */
log: function log(aPrefix, ...args) { log: function log(...argList) {
if (!this._debug) { if (!this._enabled) {
return; return;
} }
let output = this._generateLogMessage(aPrefix, args); let output = this._generatePrefix() + this._generateLogMessage('info', argList);
dump(output + "\n");
// Additionally, make the output visible in the Error Console // print to the shell console and the browser error console
dump(output + "\n");
Services.console.logStringMessage(output); Services.console.logStringMessage(output);
}, },
warning: function Logger_warning(...argList) {
if (!this._enabled) {
return;
}
let output = this._generatePrefix() + this._generateLogMessage('warning', argList);
},
/** /**
* reportError() - report an error through component utils as well as * error() - report an error through component utils as well as
* our log function * our log function
*/ */
reportError: function reportError(aPrefix, ...aArgs) { error: function Logger_error(...argList) {
let prefix = aPrefix + ' ERROR'; if (!this._enabled) {
return;
}
// Report the error in the browser // Report the error in the browser
let output = this._generateLogMessage(aPrefix, aArgs); let output = this._generatePrefix() + this._generateLogMessage('error', argList);
Cu.reportError(output); Cu.reportError(output);
// print to the console
dump("ERROR: " + output + "\n"); dump("ERROR: " + output + "\n");
dump(" traceback follows:\n");
for (let frame = Components.stack.caller; frame; frame = frame.caller) { for (let frame = Components.stack.caller; frame; frame = frame.caller) {
dump(frame + "\n"); dump(frame + "\n");
} }
} }
}; };
this.Logger = new IdentityLogger(); /**
* let logger = getLogger('my component', 'toolkit.foo.debug');
* logger.log("I would like", 42, "pies", {'and-some': 'object'});
*/
let _loggers = {};
this.getLogger = function(aIdentifier, aEnablingPref) {
let key = aIdentifier;
if (aEnablingPref) {
key = key + '-' + aEnablingPref;
}
if (!_loggers[key]) {
_loggers[key] = new Logger(aIdentifier, aEnablingPref);
}
return _loggers[key];
}

View File

@ -19,24 +19,15 @@ this.EXPORTED_SYMBOLS = ["IdentityService"];
const Cu = Components.utils; const Cu = Components.utils;
const Ci = Components.interfaces; const Ci = Components.interfaces;
const Cc = Components.classes;
const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/identity/LogUtils.jsm");
Cu.import("resource://gre/modules/identity/IdentityUtils.jsm"); Cu.import("resource://gre/modules/identity/IdentityUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, XPCOMUtils.defineLazyGetter(this, "logger", function() {
"jwcrypto", Cu.import('resource://gre/modules/identity/LogUtils.jsm');
"resource://gre/modules/identity/jwcrypto.jsm"); return getLogger("Identity", "toolkit.identity.debug");
});
function log(...aMessageArgs) {
Logger.log.apply(Logger, ["minimal core"].concat(aMessageArgs));
}
function reportError(...aMessageArgs) {
Logger.reportError.apply(Logger, ["core"].concat(aMessageArgs));
}
function makeMessageObject(aRpCaller) { function makeMessageObject(aRpCaller) {
let options = {}; let options = {};
@ -64,7 +55,7 @@ function makeMessageObject(aRpCaller) {
if ((typeof options.id === 'undefined') || if ((typeof options.id === 'undefined') ||
(typeof options.origin === 'undefined')) { (typeof options.origin === 'undefined')) {
let err = "id and origin required in relying-party message: " + JSON.stringify(options); let err = "id and origin required in relying-party message: " + JSON.stringify(options);
reportError(err); logger.error(err);
throw new Error(err); throw new Error(err);
} }
@ -73,7 +64,6 @@ function makeMessageObject(aRpCaller) {
function IDService() { function IDService() {
Services.obs.addObserver(this, "quit-application-granted", false); Services.obs.addObserver(this, "quit-application-granted", false);
// Services.obs.addObserver(this, "identity-auth-complete", false);
// simplify, it's one object // simplify, it's one object
this.RP = this; this.RP = this;
@ -88,29 +78,14 @@ function IDService() {
IDService.prototype = { IDService.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]), QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
observe: function observe(aSubject, aTopic, aData) { observe: function IDService_observe(aSubject, aTopic, aData) {
switch (aTopic) { switch (aTopic) {
case "quit-application-granted": case "quit-application-granted":
Services.obs.removeObserver(this, "quit-application-granted"); Services.obs.removeObserver(this, "quit-application-granted");
// Services.obs.removeObserver(this, "identity-auth-complete");
break; break;
} }
}, },
/**
* Parse an email into username and domain if it is valid, else return null
*/
parseEmail: function parseEmail(email) {
var match = email.match(/^([^@]+)@([^@^/]+.[a-z]+)$/);
if (match) {
return {
username: match[1],
domain: match[2]
};
}
return null;
},
/** /**
* Register a listener for a given windowID as a result of a call to * Register a listener for a given windowID as a result of a call to
* navigator.id.watch(). * navigator.id.watch().
@ -130,12 +105,12 @@ IDService.prototype = {
* - doCancel() * - doCancel()
* *
*/ */
watch: function watch(aRpCaller) { watch: function IDService_watch(aRpCaller) {
// store the caller structure and notify the UI observers // store the caller structure and notify the UI observers
this._rpFlows[aRpCaller.id] = aRpCaller; this._rpFlows[aRpCaller.id] = aRpCaller;
let options = makeMessageObject(aRpCaller); let options = makeMessageObject(aRpCaller);
log("sending identity-controller-watch:", options); logger.log("sending identity-controller-watch:", options);
Services.obs.notifyObservers({wrappedJSObject: options},"identity-controller-watch", null); Services.obs.notifyObservers({wrappedJSObject: options},"identity-controller-watch", null);
}, },
@ -143,14 +118,14 @@ IDService.prototype = {
* The RP has gone away; remove handles to the hidden iframe. * The RP has gone away; remove handles to the hidden iframe.
* It's probable that the frame will already have been cleaned up. * It's probable that the frame will already have been cleaned up.
*/ */
unwatch: function unwatch(aRpId, aTargetMM) { unwatch: function IDService_unwatch(aRpId, aTargetMM) {
let rp = this._rpFlows[aRpId]; let rp = this._rpFlows[aRpId];
let options = makeMessageObject({ let options = makeMessageObject({
id: aRpId, id: aRpId,
origin: rp.origin, origin: rp.origin,
messageManager: aTargetMM messageManager: aTargetMM
}); });
log("sending identity-controller-unwatch for id", options.id, options.origin); logger.log("sending identity-controller-unwatch for id", options.id, options.origin);
Services.obs.notifyObservers({wrappedJSObject: options}, "identity-controller-unwatch", null); Services.obs.notifyObservers({wrappedJSObject: options}, "identity-controller-unwatch", null);
}, },
@ -164,7 +139,7 @@ IDService.prototype = {
* @param aOptions * @param aOptions
* (Object) options including privacyPolicy, termsOfService * (Object) options including privacyPolicy, termsOfService
*/ */
request: function request(aRPId, aOptions) { request: function IDService_request(aRPId, aOptions) {
let rp = this._rpFlows[aRPId]; let rp = this._rpFlows[aRPId];
// Notify UX to display identity picker. // Notify UX to display identity picker.
@ -182,19 +157,19 @@ IDService.prototype = {
* (integer) the id of the doc object obtained in .watch() * (integer) the id of the doc object obtained in .watch()
* *
*/ */
logout: function logout(aRpCallerId) { logout: function IDService_logout(aRpCallerId) {
let rp = this._rpFlows[aRpCallerId]; let rp = this._rpFlows[aRpCallerId];
let options = makeMessageObject(rp); let options = makeMessageObject(rp);
Services.obs.notifyObservers({wrappedJSObject: options}, "identity-controller-logout", null); Services.obs.notifyObservers({wrappedJSObject: options}, "identity-controller-logout", null);
}, },
childProcessShutdown: function childProcessShutdown(messageManager) { childProcessShutdown: function IDService_childProcessShutdown(messageManager) {
let options = makeMessageObject({messageManager: messageManager, id: null, origin: null}); let options = makeMessageObject({messageManager: messageManager, id: null, origin: null});
Services.obs.notifyObservers({wrappedJSObject: options}, "identity-child-process-shutdown", null); Services.obs.notifyObservers({wrappedJSObject: options}, "identity-child-process-shutdown", null);
Object.keys(this._rpFlows).forEach(function(key) { Object.keys(this._rpFlows).forEach(function(key) {
if (this._rpFlows[key]._mm === messageManager) { if (this._rpFlows[key]._mm === messageManager) {
log("child process shutdown for rp", key, "- deleting flow"); logger.log("child process shutdown for rp", key, "- deleting flow");
delete this._rpFlows[key]; delete this._rpFlows[key];
} }
}, this); }, this);
@ -206,7 +181,7 @@ IDService.prototype = {
* following functions (doLogin, doLogout, or doReady) * following functions (doLogin, doLogout, or doReady)
*/ */
doLogin: function doLogin(aRpCallerId, aAssertion, aInternalParams) { doLogin: function IDService_doLogin(aRpCallerId, aAssertion, aInternalParams) {
let rp = this._rpFlows[aRpCallerId]; let rp = this._rpFlows[aRpCallerId];
if (!rp) { if (!rp) {
dump("WARNING: doLogin found no rp to go with callerId " + aRpCallerId + "\n"); dump("WARNING: doLogin found no rp to go with callerId " + aRpCallerId + "\n");
@ -216,7 +191,7 @@ IDService.prototype = {
rp.doLogin(aAssertion, aInternalParams); rp.doLogin(aAssertion, aInternalParams);
}, },
doLogout: function doLogout(aRpCallerId) { doLogout: function IDService_doLogout(aRpCallerId) {
let rp = this._rpFlows[aRpCallerId]; let rp = this._rpFlows[aRpCallerId];
if (!rp) { if (!rp) {
dump("WARNING: doLogout found no rp to go with callerId " + aRpCallerId + "\n"); dump("WARNING: doLogout found no rp to go with callerId " + aRpCallerId + "\n");
@ -226,7 +201,7 @@ IDService.prototype = {
rp.doLogout(); rp.doLogout();
}, },
doReady: function doReady(aRpCallerId) { doReady: function IDService_doReady(aRpCallerId) {
let rp = this._rpFlows[aRpCallerId]; let rp = this._rpFlows[aRpCallerId];
if (!rp) { if (!rp) {
dump("WARNING: doReady found no rp to go with callerId " + aRpCallerId + "\n"); dump("WARNING: doReady found no rp to go with callerId " + aRpCallerId + "\n");
@ -236,7 +211,7 @@ IDService.prototype = {
rp.doReady(); rp.doReady();
}, },
doCancel: function doCancel(aRpCallerId) { doCancel: function IDService_doCancel(aRpCallerId) {
let rp = this._rpFlows[aRpCallerId]; let rp = this._rpFlows[aRpCallerId];
if (!rp) { if (!rp) {
dump("WARNING: doCancel found no rp to go with callerId " + aRpCallerId + "\n"); dump("WARNING: doCancel found no rp to go with callerId " + aRpCallerId + "\n");
@ -244,202 +219,7 @@ IDService.prototype = {
} }
rp.doCancel(); rp.doCancel();
}, }
/*
* XXX Bug 804229: Implement Identity Provider Functions
*
* Stubs for Identity Provider functions follow
*/
/**
* the provisioning iframe sandbox has called navigator.id.beginProvisioning()
*
* @param aCaller
* (object) the iframe sandbox caller with all callbacks and
* other information. Callbacks include:
* - doBeginProvisioningCallback(id, duration_s)
* - doGenKeyPairCallback(pk)
*/
beginProvisioning: function beginProvisioning(aCaller) {
},
/**
* the provisioning iframe sandbox has called
* navigator.id.raiseProvisioningFailure()
*
* @param aProvId
* (int) the identifier of the provisioning flow tied to that sandbox
* @param aReason
*/
raiseProvisioningFailure: function raiseProvisioningFailure(aProvId, aReason) {
reportError("Provisioning failure", aReason);
},
/**
* When navigator.id.genKeyPair is called from provisioning iframe sandbox.
* Generates a keypair for the current user being provisioned.
*
* @param aProvId
* (int) the identifier of the provisioning caller tied to that sandbox
*
* It is an error to call genKeypair without receiving the callback for
* the beginProvisioning() call first.
*/
genKeyPair: function genKeyPair(aProvId) {
},
/**
* When navigator.id.registerCertificate is called from provisioning iframe
* sandbox.
*
* Sets the certificate for the user for which a certificate was requested
* via a preceding call to beginProvisioning (and genKeypair).
*
* @param aProvId
* (integer) the identifier of the provisioning caller tied to that
* sandbox
*
* @param aCert
* (String) A JWT representing the signed certificate for the user
* being provisioned, provided by the IdP.
*/
registerCertificate: function registerCertificate(aProvId, aCert) {
},
/**
* The authentication frame has called navigator.id.beginAuthentication
*
* IMPORTANT: the aCaller is *always* non-null, even if this is called from
* a regular content page. We have to make sure, on every DOM call, that
* aCaller is an expected authentication-flow identifier. If not, we throw
* an error or something.
*
* @param aCaller
* (object) the authentication caller
*
*/
beginAuthentication: function beginAuthentication(aCaller) {
},
/**
* The auth frame has called navigator.id.completeAuthentication
*
* @param aAuthId
* (int) the identifier of the authentication caller tied to that sandbox
*
*/
completeAuthentication: function completeAuthentication(aAuthId) {
},
/**
* The auth frame has called navigator.id.cancelAuthentication
*
* @param aAuthId
* (int) the identifier of the authentication caller
*
*/
cancelAuthentication: function cancelAuthentication(aAuthId) {
},
// methods for chrome and add-ons
/**
* Discover the IdP for an identity
*
* @param aIdentity
* (string) the email we're logging in with
*
* @param aCallback
* (function) callback to invoke on completion
* with first-positional parameter the error.
*/
_discoverIdentityProvider: function _discoverIdentityProvider(aIdentity, aCallback) {
// XXX bug 767610 - validate email address call
// When that is available, we can remove this custom parser
var parsedEmail = this.parseEmail(aIdentity);
if (parsedEmail === null) {
return aCallback("Could not parse email: " + aIdentity);
}
log("_discoverIdentityProvider: identity:", aIdentity, "domain:", parsedEmail.domain);
this._fetchWellKnownFile(parsedEmail.domain, function fetchedWellKnown(err, idpParams) {
// idpParams includes the pk, authorization url, and
// provisioning url.
// XXX bug 769861 follow any authority delegations
// if no well-known at any point in the delegation
// fall back to browserid.org as IdP
return aCallback(err, idpParams);
});
},
/**
* Fetch the well-known file from the domain.
*
* @param aDomain
*
* @param aScheme
* (string) (optional) Protocol to use. Default is https.
* This is necessary because we are unable to test
* https.
*
* @param aCallback
*
*/
_fetchWellKnownFile: function _fetchWellKnownFile(aDomain, aCallback, aScheme='https') {
// XXX bug 769854 make tests https and remove aScheme option
let url = aScheme + '://' + aDomain + "/.well-known/browserid";
log("_fetchWellKnownFile:", url);
// this appears to be a more successful way to get at xmlhttprequest (which supposedly will close with a window
let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Ci.nsIXMLHttpRequest);
// XXX bug 769865 gracefully handle being off-line
// XXX bug 769866 decide on how to handle redirects
req.open("GET", url, true);
req.responseType = "json";
req.mozBackgroundRequest = true;
req.onload = function _fetchWellKnownFile_onload() {
if (req.status < 200 || req.status >= 400) {
log("_fetchWellKnownFile", url, ": server returned status:", req.status);
return aCallback("Error");
}
try {
let idpParams = req.response;
// Verify that the IdP returned a valid configuration
if (! (idpParams.provisioning &&
idpParams.authentication &&
idpParams['public-key'])) {
let errStr= "Invalid well-known file from: " + aDomain;
log("_fetchWellKnownFile:", errStr);
return aCallback(errStr);
}
let callbackObj = {
domain: aDomain,
idpParams: idpParams,
};
log("_fetchWellKnownFile result: ", callbackObj);
// Yay. Valid IdP configuration for the domain.
return aCallback(null, callbackObj);
} catch (err) {
reportError("_fetchWellKnownFile", "Bad configuration from", aDomain, err);
return aCallback(err.toString());
}
};
req.onerror = function _fetchWellKnownFile_onerror() {
log("_fetchWellKnownFile", "ERROR:", req.status, req.statusText);
log("ERROR: _fetchWellKnownFile:", err);
return aCallback("Error");
};
req.send(null);
},
}; };
this.IdentityService = new IDService(); this.IdentityService = new IDService();

View File

@ -13,7 +13,6 @@ const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/identity/LogUtils.jsm");
Cu.import("resource://gre/modules/identity/IdentityUtils.jsm"); Cu.import("resource://gre/modules/identity/IdentityUtils.jsm");
Cu.import("resource://gre/modules/identity/IdentityStore.jsm"); Cu.import("resource://gre/modules/identity/IdentityStore.jsm");
@ -23,12 +22,10 @@ XPCOMUtils.defineLazyModuleGetter(this,
"jwcrypto", "jwcrypto",
"resource://gre/modules/identity/jwcrypto.jsm"); "resource://gre/modules/identity/jwcrypto.jsm");
function log(...aMessageArgs) { XPCOMUtils.defineLazyGetter(this, "logger", function() {
Logger.log.apply(Logger, ["RP"].concat(aMessageArgs)); Cu.import('resource://gre/modules/identity/LogUtils.jsm');
} return getLogger("Identity RP", "toolkit.identity.debug");
function reportError(...aMessageArgs) { });
Logger.reportError.apply(Logger, ["RP"].concat(aMessageArgs));
}
function IdentityRelyingParty() { function IdentityRelyingParty() {
// The store is a singleton shared among Identity, RelyingParty, and // The store is a singleton shared among Identity, RelyingParty, and
@ -87,7 +84,7 @@ IdentityRelyingParty.prototype = {
let origin = aRpCaller.origin; let origin = aRpCaller.origin;
let state = this._store.getLoginState(origin) || { isLoggedIn: false, email: null }; let state = this._store.getLoginState(origin) || { isLoggedIn: false, email: null };
log("watch: rpId:", aRpCaller.id, logger.log("watch: rpId:", aRpCaller.id,
"origin:", origin, "origin:", origin,
"loggedInUser:", aRpCaller.loggedInUser, "loggedInUser:", aRpCaller.loggedInUser,
"loggedIn:", state.isLoggedIn, "loggedIn:", state.isLoggedIn,
@ -139,7 +136,7 @@ IdentityRelyingParty.prototype = {
* Note that this calls _getAssertion * Note that this calls _getAssertion
*/ */
_doLogin: function _doLogin(aRpCaller, aOptions, aAssertion) { _doLogin: function _doLogin(aRpCaller, aOptions, aAssertion) {
log("_doLogin: rpId:", aRpCaller.id, "origin:", aOptions.origin); logger.log("_doLogin: rpId:", aRpCaller.id, "origin:", aOptions.origin);
let loginWithAssertion = function loginWithAssertion(assertion) { let loginWithAssertion = function loginWithAssertion(assertion) {
this._store.setLoginState(aOptions.origin, true, aOptions.loggedInUser); this._store.setLoginState(aOptions.origin, true, aOptions.loggedInUser);
@ -153,7 +150,7 @@ IdentityRelyingParty.prototype = {
} else { } else {
this._getAssertion(aOptions, function gotAssertion(err, assertion) { this._getAssertion(aOptions, function gotAssertion(err, assertion) {
if (err) { if (err) {
reportError("_doLogin:", "Failed to get assertion on login attempt:", err); logger.warning("_doLogin:", "Failed to get assertion on login attempt:", err);
this._doLogout(aRpCaller); this._doLogout(aRpCaller);
} else { } else {
loginWithAssertion(assertion); loginWithAssertion(assertion);
@ -167,7 +164,7 @@ IdentityRelyingParty.prototype = {
* on logout. * on logout.
*/ */
_doLogout: function _doLogout(aRpCaller, aOptions) { _doLogout: function _doLogout(aRpCaller, aOptions) {
log("_doLogout: rpId:", aRpCaller.id, "origin:", aOptions.origin); logger.log("_doLogout: rpId:", aRpCaller.id, "origin:", aOptions.origin);
let state = this._store.getLoginState(aOptions.origin) || {}; let state = this._store.getLoginState(aOptions.origin) || {};
@ -191,7 +188,7 @@ IdentityRelyingParty.prototype = {
* (string) The email of the user whose login state has changed * (string) The email of the user whose login state has changed
*/ */
_notifyLoginStateChanged: function _notifyLoginStateChanged(aRpCallerId, aIdentity) { _notifyLoginStateChanged: function _notifyLoginStateChanged(aRpCallerId, aIdentity) {
log("_notifyLoginStateChanged: rpId:", aRpCallerId, "identity:", aIdentity); logger.log("_notifyLoginStateChanged: rpId:", aRpCallerId, "identity:", aIdentity);
let options = {rpId: aRpCallerId}; let options = {rpId: aRpCallerId};
Services.obs.notifyObservers({wrappedJSObject: options}, Services.obs.notifyObservers({wrappedJSObject: options},
@ -210,7 +207,7 @@ IdentityRelyingParty.prototype = {
* (Object) options including privacyPolicy, termsOfService * (Object) options including privacyPolicy, termsOfService
*/ */
request: function request(aRPId, aOptions) { request: function request(aRPId, aOptions) {
log("request: rpId:", aRPId); logger.log("request: rpId:", aRPId);
let rp = this._rpFlows[aRPId]; let rp = this._rpFlows[aRPId];
// Notify UX to display identity picker. // Notify UX to display identity picker.
@ -238,14 +235,14 @@ IdentityRelyingParty.prototype = {
* *
*/ */
logout: function logout(aRpCallerId) { logout: function logout(aRpCallerId) {
log("logout: RP caller id:", aRpCallerId); logger.log("logout: RP caller id:", aRpCallerId);
let rp = this._rpFlows[aRpCallerId]; let rp = this._rpFlows[aRpCallerId];
if (rp && rp.origin) { if (rp && rp.origin) {
let origin = rp.origin; let origin = rp.origin;
log("logout: origin:", origin); logger.log("logout: origin:", origin);
this._doLogout(rp, {origin: origin}); this._doLogout(rp, {origin: origin});
} else { } else {
log("logout: no RP found with id:", aRpCallerId); logger.log("logout: no RP found with id:", aRpCallerId);
} }
// We don't delete this._rpFlows[aRpCallerId], because // We don't delete this._rpFlows[aRpCallerId], because
// the user might log back in again. // the user might log back in again.
@ -254,7 +251,7 @@ IdentityRelyingParty.prototype = {
getDefaultEmailForOrigin: function getDefaultEmailForOrigin(aOrigin) { getDefaultEmailForOrigin: function getDefaultEmailForOrigin(aOrigin) {
let identities = this.getIdentitiesForSite(aOrigin); let identities = this.getIdentitiesForSite(aOrigin);
let result = identities.lastUsed || null; let result = identities.lastUsed || null;
log("getDefaultEmailForOrigin:", aOrigin, "->", result); logger.log("getDefaultEmailForOrigin:", aOrigin, "->", result);
return result; return result;
}, },
@ -293,7 +290,7 @@ IdentityRelyingParty.prototype = {
_getAssertion: function _getAssertion(aOptions, aCallback) { _getAssertion: function _getAssertion(aOptions, aCallback) {
let audience = aOptions.origin; let audience = aOptions.origin;
let email = aOptions.loggedInUser || this.getDefaultEmailForOrigin(audience); let email = aOptions.loggedInUser || this.getDefaultEmailForOrigin(audience);
log("_getAssertion: audience:", audience, "email:", email); logger.log("_getAssertion: audience:", audience, "email:", email);
if (!audience) { if (!audience) {
throw "audience required for _getAssertion"; throw "audience required for _getAssertion";
} }
@ -307,9 +304,9 @@ IdentityRelyingParty.prototype = {
if (cert) { if (cert) {
this._generateAssertion(audience, email, function generatedAssertion(err, assertion) { this._generateAssertion(audience, email, function generatedAssertion(err, assertion) {
if (err) { if (err) {
log("ERROR: _getAssertion:", err); logger.warning("ERROR: _getAssertion:", err);
} }
log("_getAssertion: generated assertion:", assertion); logger.log("_getAssertion: generated assertion:", assertion);
return aCallback(err, assertion); return aCallback(err, assertion);
}); });
} }
@ -331,12 +328,12 @@ IdentityRelyingParty.prototype = {
* with first-positional parameter the error. * with first-positional parameter the error.
*/ */
_generateAssertion: function _generateAssertion(aAudience, aIdentity, aCallback) { _generateAssertion: function _generateAssertion(aAudience, aIdentity, aCallback) {
log("_generateAssertion: audience:", aAudience, "identity:", aIdentity); logger.log("_generateAssertion: audience:", aAudience, "identity:", aIdentity);
let id = this._store.fetchIdentity(aIdentity); let id = this._store.fetchIdentity(aIdentity);
if (! (id && id.cert)) { if (! (id && id.cert)) {
let errStr = "Cannot generate an assertion without a certificate"; let errStr = "Cannot generate an assertion without a certificate";
log("ERROR: _generateAssertion:", errStr); logger.log("ERROR: _generateAssertion:", errStr);
aCallback(errStr); aCallback(errStr);
return; return;
} }
@ -345,7 +342,7 @@ IdentityRelyingParty.prototype = {
if (!kp) { if (!kp) {
let errStr = "Cannot generate an assertion without a keypair"; let errStr = "Cannot generate an assertion without a keypair";
log("ERROR: _generateAssertion:", errStr); logger.log("ERROR: _generateAssertion:", errStr);
aCallback(errStr); aCallback(errStr);
return; return;
} }
@ -361,7 +358,7 @@ IdentityRelyingParty.prototype = {
if (rp) { if (rp) {
delete rp['provId']; delete rp['provId'];
} else { } else {
log("Error: Couldn't delete provision flow ", aProvId, " for RP ", aRPId); logger.log("Error: Couldn't delete provision flow ", aProvId, " for RP ", aRPId);
} }
}, },

View File

@ -13,9 +13,10 @@ const XHTML_NS = "http://www.w3.org/1999/xhtml";
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, XPCOMUtils.defineLazyGetter(this, "logger", function() {
"Logger", Cu.import('resource://gre/modules/identity/LogUtils.jsm');
"resource://gre/modules/identity/LogUtils.jsm"); return getLogger("Identity", "toolkit.identity.debug");
});
/** /**
* An object that represents a sandbox in an iframe loaded with aURL. The * An object that represents a sandbox in an iframe loaded with aURL. The
@ -34,7 +35,7 @@ XPCOMUtils.defineLazyModuleGetter(this,
this.Sandbox = function Sandbox(aURL, aCallback) { this.Sandbox = function Sandbox(aURL, aCallback) {
// Normalize the URL so the comparison in _makeSandboxContentLoaded works // Normalize the URL so the comparison in _makeSandboxContentLoaded works
this._url = Services.io.newURI(aURL, null, null).spec; this._url = Services.io.newURI(aURL, null, null).spec;
this._log("Creating sandbox for:", this._url); logger.log("Creating sandbox for:", this._url);
this._createFrame(); this._createFrame();
this._createSandbox(aCallback); this._createSandbox(aCallback);
}; };
@ -54,9 +55,9 @@ this.Sandbox.prototype = {
* id and URL). * id and URL).
*/ */
reload: function Sandbox_reload(aCallback) { reload: function Sandbox_reload(aCallback) {
this._log("reload:", this.id, ":", this._url); logger.log("reload:", this.id, ":", this._url);
this._createSandbox(function createdSandbox(aSandbox) { this._createSandbox(function createdSandbox(aSandbox) {
this._log("reloaded sandbox id:", aSandbox.id); logger.log("reloaded sandbox id:", aSandbox.id);
aCallback(aSandbox); aCallback(aSandbox);
}.bind(this)); }.bind(this));
}, },
@ -65,7 +66,7 @@ this.Sandbox.prototype = {
* Frees the sandbox and releases the iframe created to host it. * Frees the sandbox and releases the iframe created to host it.
*/ */
free: function Sandbox_free() { free: function Sandbox_free() {
this._log("free:", this.id); logger.log("free:", this.id);
this._container.removeChild(this._frame); this._container.removeChild(this._frame);
this._frame = null; this._frame = null;
this._container = null; this._container = null;
@ -115,7 +116,7 @@ this.Sandbox.prototype = {
_createSandbox: function Sandbox__createSandbox(aCallback) { _createSandbox: function Sandbox__createSandbox(aCallback) {
let self = this; let self = this;
function _makeSandboxContentLoaded(event) { function _makeSandboxContentLoaded(event) {
self._log("_makeSandboxContentLoaded:", self.id, logger.log("_makeSandboxContentLoaded:", self.id,
event.target.location.toString()); event.target.location.toString());
if (event.target != self._frame.contentDocument) { if (event.target != self._frame.contentDocument) {
return; return;
@ -144,10 +145,5 @@ this.Sandbox.prototype = {
null // headers null // headers
); );
}, }
_log: function Sandbox__log(...aMessageArgs) {
Logger.log.apply(Logger, ["sandbox"].concat(aMessageArgs));
},
}; };

View File

@ -14,7 +14,11 @@ const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/identity/LogUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "logger", function() {
Cu.import('resource://gre/modules/identity/LogUtils.jsm');
return getLogger("Identity test", "toolkit.identity.debug");
});
XPCOMUtils.defineLazyServiceGetter(this, XPCOMUtils.defineLazyServiceGetter(this,
"IdentityCryptoService", "IdentityCryptoService",
@ -25,12 +29,8 @@ this.EXPORTED_SYMBOLS = ["jwcrypto"];
const ALGORITHMS = { RS256: "RS256", DS160: "DS160" }; const ALGORITHMS = { RS256: "RS256", DS160: "DS160" };
function log(...aMessageArgs) {
Logger.log.apply(Logger, ["jwcrypto"].concat(aMessageArgs));
}
function generateKeyPair(aAlgorithmName, aCallback) { function generateKeyPair(aAlgorithmName, aCallback) {
log("Generate key pair; alg =", aAlgorithmName); logger.log("Generate key pair; alg =", aAlgorithmName);
IdentityCryptoService.generateKeyPair(aAlgorithmName, function(rv, aKeyPair) { IdentityCryptoService.generateKeyPair(aAlgorithmName, function(rv, aKeyPair) {
if (!Components.isSuccessCode(rv)) { if (!Components.isSuccessCode(rv)) {
@ -74,10 +74,10 @@ function generateKeyPair(aAlgorithmName, aCallback) {
function sign(aPayload, aKeypair, aCallback) { function sign(aPayload, aKeypair, aCallback) {
aKeypair._kp.sign(aPayload, function(rv, signature) { aKeypair._kp.sign(aPayload, function(rv, signature) {
if (!Components.isSuccessCode(rv)) { if (!Components.isSuccessCode(rv)) {
log("ERROR: signer.sign failed"); logger.warning("ERROR: signer.sign failed");
return aCallback("Sign failed"); return aCallback("Sign failed");
} }
log("signer.sign: success"); logger.log("signer.sign: success");
return aCallback(null, signature); return aCallback(null, signature);
}); });
} }
@ -93,7 +93,7 @@ jwcryptoClass.prototype = {
}, },
generateKeyPair: function(aAlgorithmName, aCallback) { generateKeyPair: function(aAlgorithmName, aCallback) {
log("generating"); logger.log("generating");
generateKeyPair(aAlgorithmName, aCallback); generateKeyPair(aAlgorithmName, aCallback);
}, },
@ -113,7 +113,7 @@ jwcryptoClass.prototype = {
var payloadBytes = IdentityCryptoService.base64UrlEncode( var payloadBytes = IdentityCryptoService.base64UrlEncode(
JSON.stringify(payload)); JSON.stringify(payload));
log("payload bytes", payload, payloadBytes); logger.log("payload bytes", payload, payloadBytes);
sign(headerBytes + "." + payloadBytes, aKeyPair, function(err, signature) { sign(headerBytes + "." + payloadBytes, aKeyPair, function(err, signature) {
if (err) if (err)
return aCallback(err); return aCallback(err);

View File

@ -25,10 +25,6 @@ XPCOMUtils.defineLazyModuleGetter(this,
"IdentityStore", "IdentityStore",
"resource://gre/modules/identity/IdentityStore.jsm"); "resource://gre/modules/identity/IdentityStore.jsm");
XPCOMUtils.defineLazyModuleGetter(this,
"Logger",
"resource://gre/modules/identity/LogUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, XPCOMUtils.defineLazyServiceGetter(this,
"uuidGenerator", "uuidGenerator",
"@mozilla.org/uuid-generator;1", "@mozilla.org/uuid-generator;1",
@ -75,10 +71,13 @@ registrar.registerFactory(Components.ID("{fbfae60b-64a4-44ef-a911-08ceb70b9f31}"
XULAppInfoFactory); XULAppInfoFactory);
// The following are utility functions for Identity testing // The following are utility functions for Identity testing
//
XPCOMUtils.defineLazyGetter(this, "logger", function() {
Cu.import('resource://gre/modules/identity/LogUtils.jsm');
return getLogger("Identity test", "toolkit.identity.debug");
});
function log(...aMessageArgs) { var log = logger.log;
Logger.log.apply(Logger, ["test"].concat(aMessageArgs));
}
function get_idstore() { function get_idstore() {
return IdentityStore; return IdentityStore;

View File

@ -5,7 +5,11 @@
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import('resource://gre/modules/identity/LogUtils.jsm');
XPCOMUtils.defineLazyGetter(this, "logger", function() {
Cu.import('resource://gre/modules/identity/LogUtils.jsm');
return getLogger("Identity test", "toolkit.identity.debug");
});
const idService = Cc["@mozilla.org/identity/crypto-service;1"] const idService = Cc["@mozilla.org/identity/crypto-service;1"]
.getService(Ci.nsIIdentityCryptoService); .getService(Ci.nsIIdentityCryptoService);
@ -20,7 +24,7 @@ function do_check_eq_or_slightly_less(x, y) {
function test_dsa() { function test_dsa() {
idService.generateKeyPair(ALG_DSA, function (rv, keyPair) { idService.generateKeyPair(ALG_DSA, function (rv, keyPair) {
log("DSA generateKeyPair finished ", rv); logger.log("DSA generateKeyPair finished ", rv);
do_check_true(Components.isSuccessCode(rv)); do_check_true(Components.isSuccessCode(rv));
do_check_eq(typeof keyPair.sign, "function"); do_check_eq(typeof keyPair.sign, "function");
do_check_eq(keyPair.keyType, ALG_DSA); do_check_eq(keyPair.keyType, ALG_DSA);
@ -30,9 +34,9 @@ function test_dsa() {
do_check_eq_or_slightly_less(keyPair.hexDSAPublicValue.length, 1024 / 8 * 2); do_check_eq_or_slightly_less(keyPair.hexDSAPublicValue.length, 1024 / 8 * 2);
// XXX: test that RSA parameters throw the correct error // XXX: test that RSA parameters throw the correct error
log("about to sign with DSA key"); logger.log("about to sign with DSA key");
keyPair.sign("foo", function (rv, signature) { keyPair.sign("foo", function (rv, signature) {
log("DSA sign finished ", rv, signature); logger.log("DSA sign finished ", rv, signature);
do_check_true(Components.isSuccessCode(rv)); do_check_true(Components.isSuccessCode(rv));
do_check_true(signature.length > 1); do_check_true(signature.length > 1);
// TODO: verify the signature with the public key // TODO: verify the signature with the public key
@ -43,7 +47,7 @@ function test_dsa() {
function test_rsa() { function test_rsa() {
idService.generateKeyPair(ALG_RSA, function (rv, keyPair) { idService.generateKeyPair(ALG_RSA, function (rv, keyPair) {
log("RSA generateKeyPair finished ", rv); logger.log("RSA generateKeyPair finished ", rv);
do_check_true(Components.isSuccessCode(rv)); do_check_true(Components.isSuccessCode(rv));
do_check_eq(typeof keyPair.sign, "function"); do_check_eq(typeof keyPair.sign, "function");
do_check_eq(keyPair.keyType, ALG_RSA); do_check_eq(keyPair.keyType, ALG_RSA);
@ -51,9 +55,9 @@ function test_rsa() {
2048 / 8); 2048 / 8);
do_check_true(keyPair.hexRSAPublicKeyExponent.length > 1); do_check_true(keyPair.hexRSAPublicKeyExponent.length > 1);
log("about to sign with RSA key"); logger.log("about to sign with RSA key");
keyPair.sign("foo", function (rv, signature) { keyPair.sign("foo", function (rv, signature) {
log("RSA sign finished ", rv, signature); logger.log("RSA sign finished ", rv, signature);
do_check_true(Components.isSuccessCode(rv)); do_check_true(Components.isSuccessCode(rv));
do_check_true(signature.length > 1); do_check_true(signature.length > 1);
run_next_test(); run_next_test();

View File

@ -3,8 +3,6 @@
"use strict" "use strict"
Cu.import('resource://gre/modules/identity/LogUtils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, "IDService", XPCOMUtils.defineLazyModuleGetter(this, "IDService",
"resource://gre/modules/identity/Identity.jsm", "resource://gre/modules/identity/Identity.jsm",
"IdentityService"); "IdentityService");
@ -12,6 +10,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "IDService",
XPCOMUtils.defineLazyModuleGetter(this, "jwcrypto", XPCOMUtils.defineLazyModuleGetter(this, "jwcrypto",
"resource://gre/modules/identity/jwcrypto.jsm"); "resource://gre/modules/identity/jwcrypto.jsm");
XPCOMUtils.defineLazyGetter(this, "logger", function() {
Cu.import('resource://gre/modules/identity/LogUtils.jsm');
return getLogger("Identity test", "toolkit.identity.debug");
});
const RP_ORIGIN = "http://123done.org"; const RP_ORIGIN = "http://123done.org";
const INTERNAL_ORIGIN = "browserid://"; const INTERNAL_ORIGIN = "browserid://";
@ -47,7 +50,7 @@ function test_get_assertion() {
do_check_null(err); do_check_null(err);
// more checks on assertion // more checks on assertion
log("assertion", assertion); logger.log("assertion", assertion);
do_test_finished(); do_test_finished();
run_next_test(); run_next_test();
@ -59,7 +62,7 @@ function test_rsa() {
do_test_pending(); do_test_pending();
function checkRSA(err, kpo) { function checkRSA(err, kpo) {
do_check_neq(kpo, undefined); do_check_neq(kpo, undefined);
log(kpo.serializedPublicKey); logger.log(kpo.serializedPublicKey);
let pk = JSON.parse(kpo.serializedPublicKey); let pk = JSON.parse(kpo.serializedPublicKey);
do_check_eq(pk.algorithm, "RS"); do_check_eq(pk.algorithm, "RS");
/* TODO /* TODO
@ -89,7 +92,7 @@ function test_dsa() {
do_test_pending(); do_test_pending();
function checkDSA(err, kpo) { function checkDSA(err, kpo) {
do_check_neq(kpo, undefined); do_check_neq(kpo, undefined);
log(kpo.serializedPublicKey); logger.log(kpo.serializedPublicKey);
let pk = JSON.parse(kpo.serializedPublicKey); let pk = JSON.parse(kpo.serializedPublicKey);
do_check_eq(pk.algorithm, "DS"); do_check_eq(pk.algorithm, "DS");
/* TODO /* TODO

View File

@ -3,7 +3,11 @@
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import('resource://gre/modules/Services.jsm'); Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/identity/LogUtils.jsm');
XPCOMUtils.defineLazyGetter(this, "logger", function() {
Cu.import('resource://gre/modules/identity/LogUtils.jsm');
return getLogger("Identity test", "toolkit.identity.debug");
});
function toggle_debug() { function toggle_debug() {
do_test_pending(); do_test_pending();
@ -17,7 +21,7 @@ function toggle_debug() {
observe: function observe(aSubject, aTopic, aData) { observe: function observe(aSubject, aTopic, aData) {
if (aTopic === "nsPref:changed") { if (aTopic === "nsPref:changed") {
// race condition? // race condition?
do_check_eq(Logger._debug, true); do_check_eq(logger._enabled, true);
do_test_finished(); do_test_finished();
run_next_test(); run_next_test();
} }
@ -34,41 +38,36 @@ function toggle_debug() {
// test that things don't break // test that things don't break
function logAlias(...args) {
Logger.log.apply(Logger, ["log alias"].concat(args));
}
function reportErrorAlias(...args) {
Logger.reportError.apply(Logger, ["report error alias"].concat(args));
}
function test_log() { function test_log() {
Logger.log("log test", "I like pie"); logger.log("log test", "I like pie");
do_test_finished(); do_test_finished();
run_next_test(); run_next_test();
} }
function test_reportError() { function test_warning() {
Logger.reportError("log test", "We are out of pies!!!"); logger.warning("similar log test", "We are still out of pies!!!");
do_test_finished(); do_test_finished();
run_next_test(); run_next_test();
} }
function test_wrappers() { function test_error() {
logAlias("I like potatoes"); logger.error("My head a splode");
do_test_finished(); do_test_finished();
reportErrorAlias("Too much red bull"); run_next_test();
} }
let TESTS = [ let TESTS = [
// XXX fix me // XXX fix me
// toggle_debug, // toggle_debug,
test_log, test_log,
test_reportError, test_warning,
test_wrappers test_error
]; ];
TESTS.forEach(add_test); TESTS.forEach(add_test);
function run_test() { function run_test() {
run_next_test(); run_next_test();
} }

View File

@ -4,12 +4,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "MinimalIDService",
"resource://gre/modules/identity/MinimalIdentity.jsm", "resource://gre/modules/identity/MinimalIdentity.jsm",
"IdentityService"); "IdentityService");
Cu.import("resource://gre/modules/identity/LogUtils.jsm");
function log(...aMessageArgs) {
Logger.log.apply(Logger, ["test_minimalidentity"].concat(aMessageArgs));
}
function test_overall() { function test_overall() {
do_check_neq(MinimalIDService, null); do_check_neq(MinimalIDService, null);
run_next_test(); run_next_test();