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;
}
#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 {
visibility: hidden;
}
@ -358,16 +338,6 @@ window[chromehidden~="toolbar"] toolbar:not(.toolbar-primary):not(.chromeclass-m
}
%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 */
#fullscr-toggler {

View File

@ -1122,254 +1122,60 @@
</implementation>
</binding>
<binding id="identity-request-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification">
<content align="start">
<binding
id="identity-request-notification"
extends="chrome://global/content/bindings/notification.xml#popup-notification">
<xul:image class="popup-notification-icon"
xbl:inherits="popupid,src=icon"/>
<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>
<panel id="persona-container" anonid="persona-container" flex="1" />
</content>
<implementation>
<implementation implements="nsIObserver, nsIDOMEventListener">
<constructor><![CDATA[
// this.notification.options.identity is used to pass identity-specific info to the binding
let origin = this.identity.origin
dump(" ** in identity xul constructor\n");
this.complete = false;
// Populate text
this.emailField.placeholder = gNavigatorBundle.
getString("identity.newIdentity.email.placeholder");
this.newIdentityDesc.textContent = gNavigatorBundle.getFormattedString(
"identity.newIdentity.description", [origin]);
this.chooseIdentityDesc.textContent = gNavigatorBundle.getFormattedString(
"identity.chooseIdentity.description", [origin]);
// Mark outgoing messages with the id of the caller
this.messageSubject = Components.classes["@mozilla.org/supports-string;1"]
.createInstance(Components.interfaces.nsISupportsString);
this.messageSubject.data = this.notification.options.context.id;
// Show optional terms of service and privacy policy links
this._populateLink(this.identity.termsOfService, "tos", "identity.termsOfService");
this._populateLink(this.identity.privacyPolicy, "privacypolicy", "identity.privacyPolicy");
// adopt the iframe and display it in the panel
// XXX this does not work in FF3.6 or older
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
// better suggestions.
let identities = this.SignInToWebsiteUX.getIdentitiesForSite(origin);
// The dimensions of the panel are modified dynamically by
// SignInToWebsite.jsm.
this._populateIdentityList(identities);
dump(" ** Persona host iframe attached to xul panel\n");
if (typeof this.step == "undefined") {
// First opening of this notification
// 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;
}
// Listen to messages from SignInToWebsite.jsm
Services.obs.addObserver(this, "identity-delegate-ui-close", false);
// Fire notification with the chosen identity when main button is clicked
this.button.addEventListener("command", this._onButtonCommand.bind(this), true);
// Do the same if enter is pressed in the email field
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));
// message back to SignInToWebsite that we've started
// and the flow with the given id can go ahead
// XXX we might not need this
Services.obs.notifyObservers(this.messageSubject, "identity-delegate-ui-open", null);
]]></constructor>
<field name="SignInToWebsiteUX" readonly="true">
let sitw = {};
Components.utils.import("resource:///modules/SignInToWebsite.jsm", sitw);
sitw.SignInToWebsiteUX;
</field>
<destructor><![CDATA[
if (!this.complete) {
Services.obs.notifyObservers(this.messageSubject, "identity-delegate-canceled", null);
}
Services.obs.removeObserver(this, "identity-delegate-ui-close", false);
]]></destructor>
<field name="newIdentityDesc" readonly="true">
document.getAnonymousElementByAttribute(this, "anonid", "newidentitydesc");
</field>
<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">
<method name="observe">
<parameter name="aSubject"/>
<parameter name="aTopic"/>
<parameter name="aData"/>
<body><![CDATA[
this.throbber.style.visibility = "visible";
this.button.disabled = true;
this.emailField.value = this.identity.selected
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();
// The only message we observe is identity-delegate-ui-close
this.complete = true;
this.notification.remove();
]]></body>
</method>

View File

@ -60,6 +60,7 @@ browser.jar:
* content/browser/browser.css (content/browser.css)
* content/browser/browser.js (content/browser.js)
* 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/content.js (content/content.js)
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,
* 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 Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/identity/IdentityUtils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, "IdentityService",
"resource://gre/modules/identity/Identity.jsm");
const kIdentityScreen = 'https://picl.personatest.org/sign_in#NATIVE';
const kIdentityFrame = 'https://picl.personatest.org/communication_iframe';
const kIdentityShim = 'chrome://browser/content/browser-identity.js';
XPCOMUtils.defineLazyModuleGetter(this, "Logger",
"resource://gre/modules/identity/LogUtils.jsm");
const PANEL_MIN_HEIGHT = 440;
const PANEL_MIN_WIDTH = 300;
function log(...aMessageArgs) {
Logger.log.apply(Logger, ["SignInToWebsiteUX"].concat(aMessageArgs));
XPCOMUtils.defineLazyModuleGetter(this, 'IdentityService',
'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 = {
init: function SignInToWebsiteUX_init() {
/*
* bug 793906 - temporarily disabling desktop UI so we can
* focus on b2g without worrying about desktop as well
*
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);
*/
this.contexts = {};
Services.obs.addObserver(this, 'identity-controller-watch', false);
Services.obs.addObserver(this, 'identity-controller-request', false);
Services.obs.addObserver(this, 'identity-controller-logout', false);
Services.obs.addObserver(this, 'identity-controller-canceled', false);
},
uninit: function SignInToWebsiteUX_uninit() {
/*
* As above:
* bug 793906 - temporarily disabling desktop UI so we can
* focus on b2g without worrying about desktop as well
*
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");
*/
Services.obs.removeObserver(this, 'identity-controller-watch');
Services.obs.removeObserver(this, 'identity-controller-request');
Services.obs.removeObserver(this, 'identity-controller-logout');
Services.obs.removeObserver(this, 'identity-controller-canceled');
},
observe: function SignInToWebsiteUX_observe(aSubject, aTopic, aData) {
log("observe: received", aTopic, "with", aData, "for", aSubject);
let options = null;
logger.log('controller observed:', aTopic);
// 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) {
options = aSubject.wrappedJSObject;
if (aSubject.wrappedJSObject) {
rpOptions = aSubject.wrappedJSObject;
} else {
rpOptions = {id: aSubject.QueryInterface(Ci.nsISupportsString).data};
}
}
switch(aTopic) {
case "identity-request":
this.requestLogin(options);
if (!rpOptions.id) {
logger.error('Got a message with no RP id');
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;
case "identity-auth":
this._openAuthenticationUI(aData, options);
case 'identity-controller-request':
this.doRequest(options);
break;
case "identity-auth-complete":
this._closeAuthenticationUI(aData);
case 'identity-controller-logout':
this.doLogout(options);
break;
case "identity-login-state-changed":
let emailAddress = aData;
if (emailAddress) {
this._removeRequestUI(options);
this._showLoggedInUI(emailAddress, options);
default:
logger.error('SignInToWebsiteUX', 'Unknown observer notification:', aTopic);
break;
}
},
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 {
this._removeLoggedInUI(options);
IdentityService.doLogin(aId, aMessage.assertion);
}
break;
case 'logout':
IdentityService.doLogout(aId);
break;
case 'cancel':
IdentityService.doCancel(aId);
break;
default:
Logger.reportError("SignInToWebsiteUX", "Unknown observer notification:", aTopic);
logger.error('Unknown identity method: ' + aMessage.method);
break;
}
},
/**
* The website is requesting login so the user must choose an identity to use.
*/
requestLogin: function SignInToWebsiteUX_requestLogin(aOptions) {
let windowID = aOptions.rpId;
log("requestLogin", aOptions);
let [chromeWin, browserEl] = this._getUIForWindowID(windowID);
// message is not shown in the UI but is required
let message = aOptions.origin;
let mainAction = {
label: chromeWin.gNavigatorBundle.getString("identity.next.label"),
accessKey: chromeWin.gNavigatorBundle.getString("identity.next.accessKey"),
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];
cleanUp: function SignInToWebsiteUX_cleanUp(aId) {
let context = this.contexts[aId];
if (context) {
if (context.hostFrame) {
context.hostFrame.cleanUp();
}
if (context.iframe && context.iframe.parentNode) {
logger.log("removing iframe from parent node and deleting it");
context.iframe.parentNode.removeChild(context.iframe);
delete context.iframe;
}
this.contexts[aId] = {};
delete this.contexts[aId];
}
log("requestLogin: rpId: ", options.identity.rpId);
chromeWin.PopupNotifications.show(browserEl, "identity-request", message,
"identity-notification-icon", mainAction,
[], options);
},
/**
* Get the list of possible identities to login to the given origin.
*/
getIdentitiesForSite: function SignInToWebsiteUX_getIdentitiesForSite(aOrigin) {
return IdentityService.RP.getIdentitiesForSite(aOrigin);
delegate: function SignInToWebsiteUX_delegate(aOptions) {
let hostFrame = new HostFrame();
hostFrame.getIframe(aOptions, function() {
// iframe has been added to aOptions
// 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));
},
/**
* User chose a new or existing identity from the doorhanger after a request() call
*/
selectIdentity: function SignInToWebsiteUX_selectIdentity(aRpId, aIdentity) {
log("selectIdentity: rpId: ", aRpId, " identity: ", aIdentity);
IdentityService.selectIdentity(aRpId, aIdentity);
doWatch: function SignInToWebsiteUX_doWatch(aOptions) {
aOptions.message = 'identity-delegate-watch';
aOptions.showUI = false;
this.delegate(aOptions);
},
// Private
/**
* Return the chrome window and <browser> for the given outer window ID.
*/
_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);
doRequest: function SignInToWebsiteUX_doRequest(aOptions) {
aOptions.message = 'identity-delegate-request';
aOptions.showUI = true;
this.delegate(aOptions);
},
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");
XPCOMUtils.defineLazyModuleGetter(this, "IdentityService",
#ifdef MOZ_B2G_VERSION
"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",
"@mozilla.org/parentprocessmessagemanager;1",
"nsIMessageListenerManager");
function log(...aMessageArgs) {
Logger.log.apply(Logger, ["DOMIdentity"].concat(aMessageArgs));
}
XPCOMUtils.defineLazyGetter(this, "logger", function() {
Cu.import('resource://gre/modules/identity/LogUtils.jsm');
return getLogger("Identity", "toolkit.identity.debug");
});
function IDDOMMessage(aOptions) {
objectCopy(aOptions, this);
@ -55,14 +48,14 @@ IDPProvisioningContext.prototype = {
},
doGenKeyPairCallback: function IDPPC_doGenKeyPairCallback(aPublicKey) {
log("doGenKeyPairCallback");
logger.log("doGenKeyPairCallback");
let message = new IDDOMMessage({id: this.id});
message.publicKey = aPublicKey;
this._mm.sendAsyncMessage("Identity:IDP:CallGenKeyPairCallback", message);
},
doError: function(msg) {
log("Provisioning ERROR: " + msg);
logger.warning(msg);
}
};
@ -84,7 +77,7 @@ IDPAuthenticationContext.prototype = {
},
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
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
@ -107,7 +102,7 @@ function RPWatchContext(aOptions, aTargetMM) {
RPWatchContext.prototype = {
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});
if (aMaybeInternalParams) {
message._internalParams = aMaybeInternalParams;
@ -116,19 +111,19 @@ RPWatchContext.prototype = {
},
doLogout: function RPWatchContext_onlogout() {
log("doLogout: " + this.id);
logger.log("logout id: " + this.id);
let message = new IDDOMMessage({id: this.id});
this._mm.sendAsyncMessage("Identity:RP:Watch:OnLogout", message);
},
doReady: function RPWatchContext_onready() {
log("doReady: " + this.id);
logger.log("ready id: " + this.id);
let message = new IDDOMMessage({id: this.id});
this._mm.sendAsyncMessage("Identity:RP:Watch:OnReady", message);
},
doCancel: function RPWatchContext_oncancel() {
log("doCancel: " + this.id);
logger.log("cancel id: " + this.id);
let message = new IDDOMMessage({id: this.id});
this._mm.sendAsyncMessage("Identity:RP:Watch:OnCancel", message);
},
@ -147,6 +142,8 @@ this.DOMIdentity = {
// used to send replies back to the proper window.
let targetMM = aMessage.target;
logger.log("received:", aMessage.name);
switch (aMessage.name) {
// RP
case "Identity:RP:Watch":
@ -217,6 +214,7 @@ this.DOMIdentity = {
Services.ww.registerNotification(this);
Services.obs.addObserver(this, "xpcom-shutdown", false);
this._subscribeListeners();
logger.log("DOM identity service initialized");
},
_subscribeListeners: function DOMIdentity__subscribeListeners() {
@ -234,16 +232,18 @@ this.DOMIdentity = {
},
_resetFrameState: function(aContext) {
log("_resetFrameState: ", aContext.id);
logger.log("_resetFrameState: ", aContext.id);
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});
aContext._mm.sendAsyncMessage("Identity:ResetState", message);
},
_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
// callbacks.
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/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
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1",
"nsIMessageSender");
function nsDOMIdentity(aIdentityInternal) {
logger.log("nsDOMIdentity constructor");
this._identityInternal = aIdentityInternal;
}
nsDOMIdentity.prototype = {
@ -65,6 +71,7 @@ nsDOMIdentity.prototype = {
*/
watch: function nsDOMIdentity_watch(aOptions) {
logger.log(aOptions);
if (this._rpWatcher) {
throw new Error("navigator.id.watch was already called");
}
@ -89,6 +96,7 @@ nsDOMIdentity.prototype = {
}
let message = this.DOMIdentityMessage(aOptions);
logger.log(message);
// loggedInUser vs loggedInEmail
// https://developer.mozilla.org/en-US/docs/DOM/navigator.id.watch
@ -98,6 +106,7 @@ nsDOMIdentity.prototype = {
checkRenamed(aOptions, "loggedInEmail", "loggedInUser");
message["loggedInUser"] = aOptions["loggedInUser"];
logger.log(message);
let emailType = typeof(aOptions["loggedInUser"]);
if (aOptions["loggedInUser"] && aOptions["loggedInUser"] !== "undefined") {
if (emailType !== "string") {
@ -113,13 +122,14 @@ nsDOMIdentity.prototype = {
// Set loggedInUser in this block that "undefined" doesn't get through.
message.loggedInUser = aOptions.loggedInUser;
}
this._log("loggedInUser: " + message.loggedInUser);
logger.log("loggedInUser:", message.loggedInUser);
this._rpWatcher = aOptions;
this._identityInternal._mm.sendAsyncMessage("Identity:RP:Watch", message);
},
request: function nsDOMIdentity_request(aOptions) {
logger.log(aOptions);
let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
@ -128,7 +138,7 @@ nsDOMIdentity.prototype = {
// getVerifiedEmail() calls, which make use of an RP context
// marked as _internal.
if (this.nativeEventsRequired && !util.isHandlingUserInput && !aOptions._internal) {
this._log("request: rejecting non-native event");
error("request: rejecting non-native event");
return;
}
@ -171,6 +181,7 @@ nsDOMIdentity.prototype = {
},
logout: function nsDOMIdentity_logout() {
logger.log("logout");
if (!this._rpWatcher) {
throw new Error("navigator.id.logout called before navigator.id.watch");
}
@ -243,7 +254,7 @@ nsDOMIdentity.prototype = {
},
getVerifiedEmail: function nsDOMIdentity_getVerifiedEmail(aCallback) {
Cu.reportError("WARNING: getVerifiedEmail has been deprecated");
error("WARNING: getVerifiedEmail has been deprecated");
this.get(aCallback, {});
},
@ -252,7 +263,7 @@ nsDOMIdentity.prototype = {
*/
beginProvisioning: function nsDOMIdentity_beginProvisioning(aCallback) {
this._log("beginProvisioning");
logger.log("beginProvisioning");
if (this._beginProvisioningCallback) {
throw new Error("navigator.id.beginProvisioning already called.");
}
@ -266,7 +277,7 @@ nsDOMIdentity.prototype = {
},
genKeyPair: function nsDOMIdentity_genKeyPair(aCallback) {
this._log("genKeyPair");
logger.log("genKeyPair");
if (!this._beginProvisioningCallback) {
throw new Error("navigator.id.genKeyPair called outside of provisioning");
}
@ -283,7 +294,7 @@ nsDOMIdentity.prototype = {
},
registerCertificate: function nsDOMIdentity_registerCertificate(aCertificate) {
this._log("registerCertificate");
logger.log("registerCertificate");
if (!this._genKeyPairCallback) {
throw new Error("navigator.id.registerCertificate called outside of provisioning");
}
@ -298,7 +309,7 @@ nsDOMIdentity.prototype = {
},
raiseProvisioningFailure: function nsDOMIdentity_raiseProvisioningFailure(aReason) {
this._log("raiseProvisioningFailure '" + aReason + "'");
logger.log("raiseProvisioningFailure '" + aReason + "'");
if (this._provisioningEnded) {
throw new Error("Provisioning already ended");
}
@ -317,7 +328,7 @@ nsDOMIdentity.prototype = {
*/
beginAuthentication: function nsDOMIdentity_beginAuthentication(aCallback) {
this._log("beginAuthentication");
logger.log("beginAuthentication");
if (this._beginAuthenticationCallback) {
throw new Error("navigator.id.beginAuthentication already called.");
}
@ -405,7 +416,7 @@ nsDOMIdentity.prototype = {
case "Identity:RP:Watch:OnLogin":
// Do we have a watcher?
if (!this._rpWatcher) {
this._log("WARNING: Received OnLogin message, but there is no RP watcher");
logger.warning("Received OnLogin message, but there is no RP watcher");
return;
}
@ -420,7 +431,7 @@ nsDOMIdentity.prototype = {
case "Identity:RP:Watch:OnLogout":
// Do we have a watcher?
if (!this._rpWatcher) {
this._log("WARNING: Received OnLogout message, but there is no RP watcher");
logger.warning("Received OnLogout message, but there is no RP watcher");
return;
}
@ -431,7 +442,7 @@ nsDOMIdentity.prototype = {
case "Identity:RP:Watch:OnReady":
// Do we have a watcher?
if (!this._rpWatcher) {
this._log("WARNING: Received OnReady message, but there is no RP watcher");
logger.warning("Received OnReady message, but there is no RP watcher");
return;
}
@ -442,7 +453,7 @@ nsDOMIdentity.prototype = {
case "Identity:RP:Watch:OnCancel":
// Do we have a watcher?
if (!this._rpWatcher) {
this._log("WARNING: Received OnCancel message, but there is no RP watcher");
logger.warning("Received OnCancel message, but there is no RP watcher");
return;
}
@ -462,10 +473,6 @@ nsDOMIdentity.prototype = {
}
},
_log: function nsDOMIdentity__log(msg) {
this._identityInternal._log(msg);
},
_callGenKeyPairCallback: function nsDOMIdentity__callGenKeyPairCallback(message) {
// create a pubkey object that works
let chrome_pubkey = JSON.parse(message.publicKey);
@ -525,7 +532,7 @@ nsDOMIdentity.prototype = {
},
uninit: function DOMIdentity_uninit() {
this._log("nsDOMIdentity uninit()");
logger.log("unwatch", this._id);
this._identityInternal._mm.sendAsyncMessage(
"Identity:RP:Unwatch",
{ id: this._id }
@ -598,7 +605,7 @@ nsDOMIdentityInternal.prototype = {
this._id = util.outerWindowID;
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;
@ -623,14 +630,6 @@ nsDOMIdentityInternal.prototype = {
return this._identity;
},
// Private.
_log: function nsDOMIdentityInternal__log(msg) {
if (!this._debug) {
return;
}
dump("nsDOMIdentity (" + this._id + "): " + msg + "\n");
},
// Component setup.
classID: Components.ID("{210853d9-2c97-4669-9761-b1ab9cbf57ef}"),

View File

@ -384,7 +384,7 @@ pref("toolkit.telemetry.infoURL", "http://www.mozilla.com/legal/privacy/firefox.
pref("toolkit.telemetry.debugSlowSql", false);
// Identity module
pref("toolkit.identity.enabled", false);
pref("dom.identity.enabled", false);
pref("toolkit.identity.debug", false);
// 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/Services.jsm");
Cu.import("resource://gre/modules/identity/LogUtils.jsm");
Cu.import("resource://gre/modules/identity/IdentityStore.jsm");
Cu.import("resource://gre/modules/identity/RelyingParty.jsm");
Cu.import("resource://gre/modules/identity/IdentityProvider.jsm");
@ -24,12 +23,10 @@ XPCOMUtils.defineLazyModuleGetter(this,
"jwcrypto",
"resource://gre/modules/identity/jwcrypto.jsm");
function log(...aMessageArgs) {
Logger.log.apply(Logger, ["core"].concat(aMessageArgs));
}
function reportError(...aMessageArgs) {
Logger.reportError.apply(Logger, ["core"].concat(aMessageArgs));
}
XPCOMUtils.defineLazyGetter(this, "logger", function() {
Cu.import('resource://gre/modules/identity/LogUtils.jsm');
return getLogger("Identity", "toolkit.identity.debug");
});
function IDService() {
Services.obs.addObserver(this, "quit-application-granted", false);
@ -53,7 +50,7 @@ IDService.prototype = {
if (!aSubject || !aSubject.wrappedJSObject)
break;
let subject = aSubject.wrappedJSObject;
log("Auth complete:", aSubject.wrappedJSObject);
logger.log("Auth complete:", aSubject.wrappedJSObject);
// We have authenticated in order to provision an identity.
// So try again.
this.selectIdentity(subject.rpId, subject.identity);
@ -73,7 +70,7 @@ IDService.prototype = {
},
shutdown: function shutdown() {
log("shutdown");
logger.log("shutdown");
Services.obs.removeObserver(this, "identity-auth-complete");
Services.obs.removeObserver(this, "quit-application-granted");
},
@ -117,12 +114,12 @@ IDService.prototype = {
* (string) the email chosen for login
*/
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.
let rp = this.RP._rpFlows[aRPId];
if (!rp) {
reportError("selectIdentity", "Invalid RP id: ", aRPId);
logger.warning("selectIdentity", "Invalid RP id: ", aRPId);
return;
}
@ -134,7 +131,7 @@ IDService.prototype = {
loggedInUser: aIdentity,
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
// IdP, we can generate an assertion and deliver it to the doc.
@ -175,7 +172,7 @@ IDService.prototype = {
if (self.IDP._provisionFlows[aProvId].didAuthentication) {
self.IDP._cleanUpProvisionFlow(aProvId);
self.RP._cleanUpProvisionFlow(aRPId, aProvId);
log("ERROR: selectIdentity: authentication hard fail");
logger.error("ERROR: selectIdentity: authentication hard fail");
rp.doError("Authentication fail.");
return;
}
@ -222,7 +219,7 @@ IDService.prototype = {
if (parsedEmail === null) {
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) {
// idpParams includes the pk, authorization url, and
@ -251,7 +248,7 @@ IDService.prototype = {
_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);
logger.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"]
@ -264,7 +261,7 @@ IDService.prototype = {
req.mozBackgroundRequest = true;
req.onload = function _fetchWellKnownFile_onload() {
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");
}
try {
@ -275,7 +272,7 @@ IDService.prototype = {
idpParams.authentication &&
idpParams['public-key'])) {
let errStr= "Invalid well-known file from: " + aDomain;
log("_fetchWellKnownFile:", errStr);
logger.log("_fetchWellKnownFile:", errStr);
return aCallback(errStr);
}
@ -283,18 +280,18 @@ IDService.prototype = {
domain: aDomain,
idpParams: idpParams,
};
log("_fetchWellKnownFile result: ", callbackObj);
logger.log("_fetchWellKnownFile result: ", callbackObj);
// Yay. Valid IdP configuration for the domain.
return aCallback(null, callbackObj);
} catch (err) {
reportError("_fetchWellKnownFile", "Bad configuration from", aDomain, err);
logger.warning("_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);
logger.log("_fetchWellKnownFile", "ERROR:", req.status, req.statusText);
logger.error("ERROR: _fetchWellKnownFile:", err);
return aCallback("Error");
};
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/Services.jsm");
Cu.import("resource://gre/modules/identity/LogUtils.jsm");
Cu.import("resource://gre/modules/identity/Sandbox.jsm");
this.EXPORTED_SYMBOLS = ["IdentityProvider"];
@ -23,13 +22,10 @@ XPCOMUtils.defineLazyModuleGetter(this,
"jwcrypto",
"resource://gre/modules/identity/jwcrypto.jsm");
function log(...aMessageArgs) {
Logger.log.apply(Logger, ["IDP"].concat(aMessageArgs));
}
function reportError(...aMessageArgs) {
Logger.reportError.apply(Logger, ["IDP"].concat(aMessageArgs));
}
XPCOMUtils.defineLazyGetter(this, "logger", function() {
Cu.import('resource://gre/modules/identity/LogUtils.jsm');
return getLogger("Identity IDP", "toolkit.identity.debug");
});
function IdentityProviderService() {
XPCOMUtils.defineLazyModuleGetter(this,
@ -76,7 +72,7 @@ IdentityProviderService.prototype = {
}
let err = "No provisioning flow found with id " + aProvId;
log("ERROR:", err);
logger.warning("ERROR:", err);
if (typeof aErrBack === 'function') {
aErrBack(err);
}
@ -122,7 +118,7 @@ IdentityProviderService.prototype = {
_provisionIdentity: function _provisionIdentity(aIdentity, aIDPParams, aProvId, aCallback) {
let provPath = aIDPParams.idpParams.provisioning;
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
// with a sandbox. Otherwise, get a sandbox and create a
@ -130,7 +126,7 @@ IdentityProviderService.prototype = {
if (aProvId) {
// 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();
} 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
}.bind(this));
@ -167,7 +163,7 @@ IdentityProviderService.prototype = {
* - doGenKeyPairCallback(pk)
*/
beginProvisioning: function beginProvisioning(aCaller) {
log("beginProvisioning:", aCaller.id);
logger.log("beginProvisioning:", aCaller.id);
// Expect a flow for this caller already to be underway.
let provFlow = this.getProvisionFlow(aCaller.id, aCaller.doError);
@ -199,7 +195,7 @@ IdentityProviderService.prototype = {
* @param aReason
*/
raiseProvisioningFailure: function raiseProvisioningFailure(aProvId, aReason) {
reportError("Provisioning failure", aReason);
logger.warning("Provisioning failure", aReason);
// look up the provisioning caller and its callback
let provFlow = this.getProvisionFlow(aProvId);
@ -232,16 +228,16 @@ IdentityProviderService.prototype = {
if (!provFlow.didBeginProvisioning) {
let errStr = "ERROR: genKeyPair called before beginProvisioning";
log(errStr);
logger.warning(errStr);
provFlow.callback(errStr);
return;
}
// Ok generate a keypair
jwcrypto.generateKeyPair(jwcrypto.ALGORITHMS.DS160, function gkpCb(err, kp) {
log("in gkp callback");
logger.log("in gkp callback");
if (err) {
log("ERROR: genKeyPair:", err);
logger.error("ERROR: genKeyPair:", err);
provFlow.callback(err);
return;
}
@ -250,7 +246,7 @@ IdentityProviderService.prototype = {
// Serialize the publicKey of the keypair and send it back to the
// 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);
}.bind(this));
},
@ -271,18 +267,18 @@ IdentityProviderService.prototype = {
* being provisioned, provided by the IdP.
*/
registerCertificate: function registerCertificate(aProvId, aCert) {
log("registerCertificate:", aProvId, aCert);
logger.log("registerCertificate:", aProvId, aCert);
// look up provisioning caller, make sure it's valid.
let provFlow = this.getProvisionFlow(aProvId);
if (!provFlow.caller) {
reportError("registerCertificate", "No provision flow or caller");
logger.warning("registerCertificate", "No provision flow or caller");
return;
}
if (!provFlow.kp) {
let errStr = "Cannot register a certificate without a keypair";
reportError("registerCertificate", errStr);
logger.warning("registerCertificate", errStr);
provFlow.callback(errStr);
return;
}
@ -308,7 +304,7 @@ IdentityProviderService.prototype = {
* first-positional-param error.
*/
_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
// stash aIdentity, idpparams, and callback in it.
@ -341,7 +337,7 @@ IdentityProviderService.prototype = {
*
*/
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
// 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;
// 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);
},
@ -368,12 +364,12 @@ IdentityProviderService.prototype = {
*
*/
completeAuthentication: function completeAuthentication(aAuthId) {
log("completeAuthentication:", aAuthId);
logger.log("completeAuthentication:", aAuthId);
// look up the AuthId caller, and get its callback.
let authFlow = this._authenticationFlows[aAuthId];
if (!authFlow) {
reportError("completeAuthentication", "No auth flow with id", aAuthId);
logger.warning("completeAuthentication", "No auth flow with id", aAuthId);
return;
}
let provId = authFlow.provId;
@ -399,12 +395,12 @@ IdentityProviderService.prototype = {
*
*/
cancelAuthentication: function cancelAuthentication(aAuthId) {
log("cancelAuthentication:", aAuthId);
logger.log("cancelAuthentication:", aAuthId);
// look up the AuthId caller, and get its callback.
let authFlow = this._authenticationFlows[aAuthId];
if (!authFlow) {
reportError("cancelAuthentication", "No auth flow with id:", aAuthId);
logger.warning("cancelAuthentication", "No auth flow with id:", aAuthId);
return;
}
let provId = authFlow.provId;
@ -419,7 +415,7 @@ IdentityProviderService.prototype = {
// invoke callback with ERROR.
let errStr = "Authentication canceled by IDP";
log("ERROR: cancelAuthentication:", errStr);
logger.log("ERROR: cancelAuthentication:", errStr);
provFlow.callback(errStr);
},
@ -430,7 +426,7 @@ IdentityProviderService.prototype = {
// this is the transition point between the two flows,
// provision and authenticate. We tell the auth flow which
// 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._provisionFlows[aProvId].authId = aAuthId;
},
@ -440,7 +436,7 @@ IdentityProviderService.prototype = {
* process.
*/
_createProvisioningSandbox: function _createProvisioningSandbox(aURL, aCallback) {
log("_createProvisioningSandbox:", aURL);
logger.log("_createProvisioningSandbox:", aURL);
if (!this._sandboxConfigured) {
// Configure message manager listening on the hidden window
@ -456,7 +452,7 @@ IdentityProviderService.prototype = {
* Load the authentication UI to start the authentication process.
*/
_beginAuthenticationFlow: function _beginAuthenticationFlow(aProvId, aURL) {
log("_beginAuthenticationFlow:", aProvId, aURL);
logger.log("_beginAuthenticationFlow:", aProvId, aURL);
let propBag = {provId: aProvId};
Services.obs.notifyObservers({wrappedJSObject:propBag}, "identity-auth", aURL);
@ -467,14 +463,14 @@ IdentityProviderService.prototype = {
* that may be attached to it.
*/
_cleanUpProvisionFlow: function _cleanUpProvisionFlow(aProvId) {
log('_cleanUpProvisionFlow:', aProvId);
logger.log('_cleanUpProvisionFlow:', aProvId);
let prov = this._provisionFlows[aProvId];
// Clean up the sandbox, if there is one.
if (prov.provisioningSandbox) {
let sandbox = this._provisionFlows[aProvId]['provisioningSandbox'];
if (sandbox.free) {
log('_cleanUpProvisionFlow: freeing sandbox');
logger.log('_cleanUpProvisionFlow: freeing sandbox');
sandbox.free();
}
delete this._provisionFlows[aProvId]['provisioningSandbox'];

View File

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

View File

@ -6,34 +6,45 @@
"use strict";
this.EXPORTED_SYMBOLS = ["Logger"];
const PREF_DEBUG = "toolkit.identity.debug";
this.EXPORTED_SYMBOLS = ["Logger", "getLogger"];
const Cu = Components.utils;
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
function IdentityLogger() {
Services.prefs.addObserver(PREF_DEBUG, this, false);
this._debug = Services.prefs.getBoolPref(PREF_DEBUG);
return this;
function Logger(aIdentifier, aEnablingPref) {
this._identifier = aIdentifier;
this._enablingPref = aEnablingPref;
// 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]),
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) {
switch(aTopic) {
switch (aTopic) {
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;
case "quit-application-granted":
Services.prefs.removeObserver(PREF_DEBUG, this);
Services.prefs.removeObserver(this._enablingPref, this);
break;
default:
@ -42,28 +53,64 @@ IdentityLogger.prototype = {
}
},
_generateLogMessage: function _generateLogMessage(aPrefix, args) {
// create a string representation of a list of arbitrary things
_generatePrefix: function _generatePrefix() {
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 = [];
// XXX bug 770418 - args look like flattened array, not list of strings
args.forEach(function(arg) {
if (typeof arg === 'string') {
strings.push(arg);
} else if (typeof arg === 'undefined') {
strings.push('undefined');
} else if (arg === null) {
argList.forEach(function(arg) {
if (arg === null) {
strings.push('null');
} else {
try {
strings.push(JSON.stringify(arg, null, 2));
} catch(err) {
strings.push("<<something>>");
switch (typeof arg) {
case 'string':
strings.push(arg);
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
*/
log: function log(aPrefix, ...args) {
if (!this._debug) {
log: function log(...argList) {
if (!this._enabled) {
return;
}
let output = this._generateLogMessage(aPrefix, args);
dump(output + "\n");
let output = this._generatePrefix() + this._generateLogMessage('info', argList);
// 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);
},
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
*/
reportError: function reportError(aPrefix, ...aArgs) {
let prefix = aPrefix + ' ERROR';
error: function Logger_error(...argList) {
if (!this._enabled) {
return;
}
// Report the error in the browser
let output = this._generateLogMessage(aPrefix, aArgs);
let output = this._generatePrefix() + this._generateLogMessage('error', argList);
Cu.reportError(output);
// print to the console
dump("ERROR: " + output + "\n");
dump(" traceback follows:\n");
for (let frame = Components.stack.caller; frame; frame = frame.caller) {
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 Ci = Components.interfaces;
const Cc = Components.classes;
const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/identity/LogUtils.jsm");
Cu.import("resource://gre/modules/identity/IdentityUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this,
"jwcrypto",
"resource://gre/modules/identity/jwcrypto.jsm");
function log(...aMessageArgs) {
Logger.log.apply(Logger, ["minimal core"].concat(aMessageArgs));
}
function reportError(...aMessageArgs) {
Logger.reportError.apply(Logger, ["core"].concat(aMessageArgs));
}
XPCOMUtils.defineLazyGetter(this, "logger", function() {
Cu.import('resource://gre/modules/identity/LogUtils.jsm');
return getLogger("Identity", "toolkit.identity.debug");
});
function makeMessageObject(aRpCaller) {
let options = {};
@ -64,7 +55,7 @@ function makeMessageObject(aRpCaller) {
if ((typeof options.id === 'undefined') ||
(typeof options.origin === 'undefined')) {
let err = "id and origin required in relying-party message: " + JSON.stringify(options);
reportError(err);
logger.error(err);
throw new Error(err);
}
@ -73,7 +64,6 @@ function makeMessageObject(aRpCaller) {
function IDService() {
Services.obs.addObserver(this, "quit-application-granted", false);
// Services.obs.addObserver(this, "identity-auth-complete", false);
// simplify, it's one object
this.RP = this;
@ -88,29 +78,14 @@ function IDService() {
IDService.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
observe: function observe(aSubject, aTopic, aData) {
observe: function IDService_observe(aSubject, aTopic, aData) {
switch (aTopic) {
case "quit-application-granted":
Services.obs.removeObserver(this, "quit-application-granted");
// Services.obs.removeObserver(this, "identity-auth-complete");
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
* navigator.id.watch().
@ -130,12 +105,12 @@ IDService.prototype = {
* - doCancel()
*
*/
watch: function watch(aRpCaller) {
watch: function IDService_watch(aRpCaller) {
// store the caller structure and notify the UI observers
this._rpFlows[aRpCaller.id] = 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);
},
@ -143,14 +118,14 @@ IDService.prototype = {
* The RP has gone away; remove handles to the hidden iframe.
* 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 options = makeMessageObject({
id: aRpId,
origin: rp.origin,
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);
},
@ -164,7 +139,7 @@ IDService.prototype = {
* @param aOptions
* (Object) options including privacyPolicy, termsOfService
*/
request: function request(aRPId, aOptions) {
request: function IDService_request(aRPId, aOptions) {
let rp = this._rpFlows[aRPId];
// Notify UX to display identity picker.
@ -182,19 +157,19 @@ IDService.prototype = {
* (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 options = makeMessageObject(rp);
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});
Services.obs.notifyObservers({wrappedJSObject: options}, "identity-child-process-shutdown", null);
Object.keys(this._rpFlows).forEach(function(key) {
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];
}
}, this);
@ -206,7 +181,7 @@ IDService.prototype = {
* following functions (doLogin, doLogout, or doReady)
*/
doLogin: function doLogin(aRpCallerId, aAssertion, aInternalParams) {
doLogin: function IDService_doLogin(aRpCallerId, aAssertion, aInternalParams) {
let rp = this._rpFlows[aRpCallerId];
if (!rp) {
dump("WARNING: doLogin found no rp to go with callerId " + aRpCallerId + "\n");
@ -216,7 +191,7 @@ IDService.prototype = {
rp.doLogin(aAssertion, aInternalParams);
},
doLogout: function doLogout(aRpCallerId) {
doLogout: function IDService_doLogout(aRpCallerId) {
let rp = this._rpFlows[aRpCallerId];
if (!rp) {
dump("WARNING: doLogout found no rp to go with callerId " + aRpCallerId + "\n");
@ -226,7 +201,7 @@ IDService.prototype = {
rp.doLogout();
},
doReady: function doReady(aRpCallerId) {
doReady: function IDService_doReady(aRpCallerId) {
let rp = this._rpFlows[aRpCallerId];
if (!rp) {
dump("WARNING: doReady found no rp to go with callerId " + aRpCallerId + "\n");
@ -236,7 +211,7 @@ IDService.prototype = {
rp.doReady();
},
doCancel: function doCancel(aRpCallerId) {
doCancel: function IDService_doCancel(aRpCallerId) {
let rp = this._rpFlows[aRpCallerId];
if (!rp) {
dump("WARNING: doCancel found no rp to go with callerId " + aRpCallerId + "\n");
@ -244,202 +219,7 @@ IDService.prototype = {
}
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();

View File

@ -13,7 +13,6 @@ const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.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/IdentityStore.jsm");
@ -23,12 +22,10 @@ XPCOMUtils.defineLazyModuleGetter(this,
"jwcrypto",
"resource://gre/modules/identity/jwcrypto.jsm");
function log(...aMessageArgs) {
Logger.log.apply(Logger, ["RP"].concat(aMessageArgs));
}
function reportError(...aMessageArgs) {
Logger.reportError.apply(Logger, ["RP"].concat(aMessageArgs));
}
XPCOMUtils.defineLazyGetter(this, "logger", function() {
Cu.import('resource://gre/modules/identity/LogUtils.jsm');
return getLogger("Identity RP", "toolkit.identity.debug");
});
function IdentityRelyingParty() {
// The store is a singleton shared among Identity, RelyingParty, and
@ -87,7 +84,7 @@ IdentityRelyingParty.prototype = {
let origin = aRpCaller.origin;
let state = this._store.getLoginState(origin) || { isLoggedIn: false, email: null };
log("watch: rpId:", aRpCaller.id,
logger.log("watch: rpId:", aRpCaller.id,
"origin:", origin,
"loggedInUser:", aRpCaller.loggedInUser,
"loggedIn:", state.isLoggedIn,
@ -139,7 +136,7 @@ IdentityRelyingParty.prototype = {
* Note that this calls _getAssertion
*/
_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) {
this._store.setLoginState(aOptions.origin, true, aOptions.loggedInUser);
@ -153,7 +150,7 @@ IdentityRelyingParty.prototype = {
} else {
this._getAssertion(aOptions, function gotAssertion(err, assertion) {
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);
} else {
loginWithAssertion(assertion);
@ -167,7 +164,7 @@ IdentityRelyingParty.prototype = {
* on logout.
*/
_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) || {};
@ -191,7 +188,7 @@ IdentityRelyingParty.prototype = {
* (string) The email of the user whose login state has changed
*/
_notifyLoginStateChanged: function _notifyLoginStateChanged(aRpCallerId, aIdentity) {
log("_notifyLoginStateChanged: rpId:", aRpCallerId, "identity:", aIdentity);
logger.log("_notifyLoginStateChanged: rpId:", aRpCallerId, "identity:", aIdentity);
let options = {rpId: aRpCallerId};
Services.obs.notifyObservers({wrappedJSObject: options},
@ -210,7 +207,7 @@ IdentityRelyingParty.prototype = {
* (Object) options including privacyPolicy, termsOfService
*/
request: function request(aRPId, aOptions) {
log("request: rpId:", aRPId);
logger.log("request: rpId:", aRPId);
let rp = this._rpFlows[aRPId];
// Notify UX to display identity picker.
@ -238,14 +235,14 @@ IdentityRelyingParty.prototype = {
*
*/
logout: function logout(aRpCallerId) {
log("logout: RP caller id:", aRpCallerId);
logger.log("logout: RP caller id:", aRpCallerId);
let rp = this._rpFlows[aRpCallerId];
if (rp && rp.origin) {
let origin = rp.origin;
log("logout: origin:", origin);
logger.log("logout: origin:", origin);
this._doLogout(rp, {origin: origin});
} 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
// the user might log back in again.
@ -254,7 +251,7 @@ IdentityRelyingParty.prototype = {
getDefaultEmailForOrigin: function getDefaultEmailForOrigin(aOrigin) {
let identities = this.getIdentitiesForSite(aOrigin);
let result = identities.lastUsed || null;
log("getDefaultEmailForOrigin:", aOrigin, "->", result);
logger.log("getDefaultEmailForOrigin:", aOrigin, "->", result);
return result;
},
@ -293,7 +290,7 @@ IdentityRelyingParty.prototype = {
_getAssertion: function _getAssertion(aOptions, aCallback) {
let audience = aOptions.origin;
let email = aOptions.loggedInUser || this.getDefaultEmailForOrigin(audience);
log("_getAssertion: audience:", audience, "email:", email);
logger.log("_getAssertion: audience:", audience, "email:", email);
if (!audience) {
throw "audience required for _getAssertion";
}
@ -307,9 +304,9 @@ IdentityRelyingParty.prototype = {
if (cert) {
this._generateAssertion(audience, email, function generatedAssertion(err, assertion) {
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);
});
}
@ -331,12 +328,12 @@ IdentityRelyingParty.prototype = {
* with first-positional parameter the error.
*/
_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);
if (! (id && id.cert)) {
let errStr = "Cannot generate an assertion without a certificate";
log("ERROR: _generateAssertion:", errStr);
logger.log("ERROR: _generateAssertion:", errStr);
aCallback(errStr);
return;
}
@ -345,7 +342,7 @@ IdentityRelyingParty.prototype = {
if (!kp) {
let errStr = "Cannot generate an assertion without a keypair";
log("ERROR: _generateAssertion:", errStr);
logger.log("ERROR: _generateAssertion:", errStr);
aCallback(errStr);
return;
}
@ -361,7 +358,7 @@ IdentityRelyingParty.prototype = {
if (rp) {
delete rp['provId'];
} 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/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this,
"Logger",
"resource://gre/modules/identity/LogUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "logger", function() {
Cu.import('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
@ -34,7 +35,7 @@ XPCOMUtils.defineLazyModuleGetter(this,
this.Sandbox = function Sandbox(aURL, aCallback) {
// Normalize the URL so the comparison in _makeSandboxContentLoaded works
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._createSandbox(aCallback);
};
@ -54,9 +55,9 @@ this.Sandbox.prototype = {
* id and URL).
*/
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._log("reloaded sandbox id:", aSandbox.id);
logger.log("reloaded sandbox id:", aSandbox.id);
aCallback(aSandbox);
}.bind(this));
},
@ -65,7 +66,7 @@ this.Sandbox.prototype = {
* Frees the sandbox and releases the iframe created to host it.
*/
free: function Sandbox_free() {
this._log("free:", this.id);
logger.log("free:", this.id);
this._container.removeChild(this._frame);
this._frame = null;
this._container = null;
@ -115,7 +116,7 @@ this.Sandbox.prototype = {
_createSandbox: function Sandbox__createSandbox(aCallback) {
let self = this;
function _makeSandboxContentLoaded(event) {
self._log("_makeSandboxContentLoaded:", self.id,
logger.log("_makeSandboxContentLoaded:", self.id,
event.target.location.toString());
if (event.target != self._frame.contentDocument) {
return;
@ -144,10 +145,5 @@ this.Sandbox.prototype = {
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/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,
"IdentityCryptoService",
@ -25,12 +29,8 @@ this.EXPORTED_SYMBOLS = ["jwcrypto"];
const ALGORITHMS = { RS256: "RS256", DS160: "DS160" };
function log(...aMessageArgs) {
Logger.log.apply(Logger, ["jwcrypto"].concat(aMessageArgs));
}
function generateKeyPair(aAlgorithmName, aCallback) {
log("Generate key pair; alg =", aAlgorithmName);
logger.log("Generate key pair; alg =", aAlgorithmName);
IdentityCryptoService.generateKeyPair(aAlgorithmName, function(rv, aKeyPair) {
if (!Components.isSuccessCode(rv)) {
@ -74,10 +74,10 @@ function generateKeyPair(aAlgorithmName, aCallback) {
function sign(aPayload, aKeypair, aCallback) {
aKeypair._kp.sign(aPayload, function(rv, signature) {
if (!Components.isSuccessCode(rv)) {
log("ERROR: signer.sign failed");
logger.warning("ERROR: signer.sign failed");
return aCallback("Sign failed");
}
log("signer.sign: success");
logger.log("signer.sign: success");
return aCallback(null, signature);
});
}
@ -93,7 +93,7 @@ jwcryptoClass.prototype = {
},
generateKeyPair: function(aAlgorithmName, aCallback) {
log("generating");
logger.log("generating");
generateKeyPair(aAlgorithmName, aCallback);
},
@ -113,7 +113,7 @@ jwcryptoClass.prototype = {
var payloadBytes = IdentityCryptoService.base64UrlEncode(
JSON.stringify(payload));
log("payload bytes", payload, payloadBytes);
logger.log("payload bytes", payload, payloadBytes);
sign(headerBytes + "." + payloadBytes, aKeyPair, function(err, signature) {
if (err)
return aCallback(err);

View File

@ -25,10 +25,6 @@ XPCOMUtils.defineLazyModuleGetter(this,
"IdentityStore",
"resource://gre/modules/identity/IdentityStore.jsm");
XPCOMUtils.defineLazyModuleGetter(this,
"Logger",
"resource://gre/modules/identity/LogUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this,
"uuidGenerator",
"@mozilla.org/uuid-generator;1",
@ -75,10 +71,13 @@ registrar.registerFactory(Components.ID("{fbfae60b-64a4-44ef-a911-08ceb70b9f31}"
XULAppInfoFactory);
// 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) {
Logger.log.apply(Logger, ["test"].concat(aMessageArgs));
}
var log = logger.log;
function get_idstore() {
return IdentityStore;

View File

@ -5,7 +5,11 @@
Cu.import("resource://gre/modules/Services.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"]
.getService(Ci.nsIIdentityCryptoService);
@ -20,7 +24,7 @@ function do_check_eq_or_slightly_less(x, y) {
function test_dsa() {
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_eq(typeof keyPair.sign, "function");
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);
// 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) {
log("DSA sign finished ", rv, signature);
logger.log("DSA sign finished ", rv, signature);
do_check_true(Components.isSuccessCode(rv));
do_check_true(signature.length > 1);
// TODO: verify the signature with the public key
@ -43,7 +47,7 @@ function test_dsa() {
function test_rsa() {
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_eq(typeof keyPair.sign, "function");
do_check_eq(keyPair.keyType, ALG_RSA);
@ -51,9 +55,9 @@ function test_rsa() {
2048 / 8);
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) {
log("RSA sign finished ", rv, signature);
logger.log("RSA sign finished ", rv, signature);
do_check_true(Components.isSuccessCode(rv));
do_check_true(signature.length > 1);
run_next_test();

View File

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

View File

@ -3,7 +3,11 @@
Cu.import("resource://gre/modules/XPCOMUtils.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() {
do_test_pending();
@ -17,7 +21,7 @@ function toggle_debug() {
observe: function observe(aSubject, aTopic, aData) {
if (aTopic === "nsPref:changed") {
// race condition?
do_check_eq(Logger._debug, true);
do_check_eq(logger._enabled, true);
do_test_finished();
run_next_test();
}
@ -34,41 +38,36 @@ function toggle_debug() {
// 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() {
Logger.log("log test", "I like pie");
logger.log("log test", "I like pie");
do_test_finished();
run_next_test();
}
function test_reportError() {
Logger.reportError("log test", "We are out of pies!!!");
function test_warning() {
logger.warning("similar log test", "We are still out of pies!!!");
do_test_finished();
run_next_test();
}
function test_wrappers() {
logAlias("I like potatoes");
function test_error() {
logger.error("My head a splode");
do_test_finished();
reportErrorAlias("Too much red bull");
run_next_test();
}
let TESTS = [
// XXX fix me
// toggle_debug,
test_log,
test_reportError,
test_wrappers
test_warning,
test_error
];
TESTS.forEach(add_test);
function run_test() {
run_next_test();
}
}

View File

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