new file mode 100644
--- /dev/null
+++ b/browser/base/content/browser-identity.js
@@ -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();
+ }
+});
+
+
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -262,36 +262,16 @@ panel[noactions] > richlistbox > richlis
#urlbar[pageproxystate="invalid"] > #urlbar-icons > .urlbar-icon:not(#go-button),
#urlbar[pageproxystate="valid"] > #urlbar-icons > #go-button,
#urlbar[pageproxystate="invalid"][focused="true"] > #urlbar-go-button ~ toolbarbutton,
#urlbar[pageproxystate="valid"] > #urlbar-go-button,
#urlbar:not([focused="true"]) > #urlbar-go-button {
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;
}
/* ::::: Unified Back-/Forward Button ::::: */
#back-button > .toolbarbutton-menu-dropmarker,
#forward-button > .toolbarbutton-menu-dropmarker {
display: none;
@@ -353,26 +333,16 @@ window[chromehidden~="toolbar"] toolbar:
overflow-y: visible !important;
}
#sync-notifications notification {
-moz-binding: url("chrome://browser/content/sync/notification.xml#notification");
}
%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 {
height: 1px;
background: black;
}
#full-screen-warning-container {
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -1117,264 +1117,70 @@
<method name="onDownloadEnded">
<body><![CDATA[
this.updateProgress();
]]></body>
</method>
</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"/>
+ <content>
+ <panel id="persona-container" anonid="persona-container" flex="1" />
+ </content>
- <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>
- <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;
+
+ // 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;
- // 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]);
+ // 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);
- // 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");
-
- // Populate the list of identities to choose from. The origin is used to provide
- // better suggestions.
- let identities = this.SignInToWebsiteUX.getIdentitiesForSite(origin);
-
- this._populateIdentityList(identities);
+ // The dimensions of the panel are modified dynamically by
+ // SignInToWebsite.jsm.
- 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;
- }
+ dump(" ** Persona host iframe attached to xul panel\n");
- // Fire notification with the chosen identity when main button is clicked
- this.button.addEventListener("command", this._onButtonCommand.bind(this), true);
+ // Listen to messages from SignInToWebsite.jsm
+ Services.obs.addObserver(this, "identity-delegate-ui-close", false);
- // 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>
-
- <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>
+ <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="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>
</implementation>
</binding>
<binding id="center-item">
<content align="center">
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -55,16 +55,17 @@ browser.jar:
content/browser/abouthealthreport/abouthealth.css (content/abouthealthreport/abouthealth.css)
#endif
content/browser/aboutRobots-icon.png (content/aboutRobots-icon.png)
content/browser/aboutRobots-widget-left.png (content/aboutRobots-widget-left.png)
content/browser/aboutSocialError.xhtml (content/aboutSocialError.xhtml)
* 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)
* content/browser/newtab/newTab.js (content/newtab/newTab.js)
content/browser/newtab/newTab.css (content/newtab/newTab.css)
content/browser/newtab/preload.xhtml (content/newtab/preload.xhtml)
* content/browser/pageinfo/pageInfo.xul (content/pageinfo/pageInfo.xul)
* content/browser/pageinfo/pageInfo.js (content/pageinfo/pageInfo.js)
--- a/browser/modules/SignInToWebsite.jsm
+++ b/browser/modules/SignInToWebsite.jsm
@@ -1,247 +1,445 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
+'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');
+
+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, "IdentityService",
- "resource://gre/modules/identity/Identity.jsm");
+const PANEL_MIN_HEIGHT = 440;
+const PANEL_MIN_WIDTH = 300;
+
+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");
+});
-XPCOMUtils.defineLazyModuleGetter(this, "Logger",
- "resource://gre/modules/identity/LogUtils.jsm");
+/**
+ * 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;
-function log(...aMessageArgs) {
- Logger.log.apply(Logger, ["SignInToWebsiteUX"].concat(aMessageArgs));
+ // 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";
}
-this.SignInToWebsiteUX = {
+function ResizeWatcher(iframe) {
+ this._mutationObserver = null;
+ this._iframe = iframe;
- init: function SignInToWebsiteUX_init() {
+ this.start();
+}
+
+ResizeWatcher.prototype = {
+ start: function ResizeWatcher_start() {
+ this.stop(); // just in case...
+ let doc = this._iframe.contentDocument;
- /*
- * 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._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);
},
- 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");
- */
+ 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();
},
- observe: function SignInToWebsiteUX_observe(aSubject, aTopic, aData) {
- log("observe: received", aTopic, "with", aData, "for", aSubject);
- let options = null;
- if (aSubject) {
- options = aSubject.wrappedJSObject;
+ cleanUp: function HostFrame_cleanUp() {
+ if (this._resizeWatcher) {
+ this._resizeWatcher.stop();
}
- switch(aTopic) {
- case "identity-request":
- this.requestLogin(options);
- break;
- case "identity-auth":
- this._openAuthenticationUI(aData, options);
+ },
+
+ /*
+ * 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;
- case "identity-auth-complete":
- this._closeAuthenticationUI(aData);
- break;
- case "identity-login-state-changed":
- let emailAddress = aData;
- if (emailAddress) {
- this._removeRequestUI(options);
- this._showLoggedInUI(emailAddress, options);
- } else {
- this._removeLoggedInUI(options);
- }
- break;
+
default:
- Logger.reportError("SignInToWebsiteUX", "Unknown observer notification:", aTopic);
+ logger.error('pipe observed unexpected topic: ' + aTopic);
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);
+ _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);
- // 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 = [];
+ if (typeof this.options.onComplete === 'function') {
+ this.options.onComplete();
+ }
+ },
- // add some extra properties to the notification to store some identity-related state
- for (let opt in aOptions) {
- options.identity[opt] = aOptions[opt];
- }
- log("requestLogin: rpId: ", options.identity.rpId);
+ _delegateLoaded: function pipe__delegateLoaded() {
+ this.mm.sendAsyncMessage(this.options.message, this.options.rpOptions);
+ //this.resizer = new DynamicResizeWatcher();
+ //this.resizer.start(
+ },
- chromeWin.PopupNotifications.show(browserEl, "identity-request", message,
- "identity-notification-icon", mainAction,
- [], options);
+ _delegateComplete: function pipe__delegateComplete() {
+ this._close();
},
- /**
- * Get the list of possible identities to login to the given origin.
- */
- getIdentitiesForSite: function SignInToWebsiteUX_getIdentitiesForSite(aOrigin) {
- return IdentityService.RP.getIdentitiesForSite(aOrigin);
- },
-
- /**
- * 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);
+ _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);
},
- // 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];
+ 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 windowUtils = someWindow.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindowUtils);
- let content = windowUtils.getOuterWindowWithId(aWindowID);
+ 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?');
+ }
+ }
+};
- 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");
+this.SignInToWebsiteUX = {
+ init: function SignInToWebsiteUX_init() {
+ 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);
+ },
- return [null, null];
+ uninit: function SignInToWebsiteUX_uninit() {
+ 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');
},
- /**
- * 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);
+ observe: function SignInToWebsiteUX_observe(aSubject, aTopic, aData) {
+ 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) {
+ if (aSubject.wrappedJSObject) {
+ rpOptions = aSubject.wrappedJSObject;
+ } else {
+ rpOptions = {id: aSubject.QueryInterface(Ci.nsISupportsString).data};
+ }
+ }
+ if (!rpOptions.id) {
+ logger.error('Got a message with no RP id');
+ return;
+ }
+
+ let rpId = rpOptions.id;
+ let UI = getUIForWindowID(rpId);
- let provId = aContext.provId;
- // Tell the ID service about the id before loading the url
- IdentityService.IDP.setAuthenticationFlow(windowID, provId);
+ let options = {
+ id: rpOptions.id,
+ rpOptions: rpOptions
+ };
- authWin.location = aAuthURI;
- },
+ switch (aTopic) {
+ case 'identity-controller-watch':
+ this.doWatch(options);
+ break;
- _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);
+ case 'identity-controller-request':
+ this.doRequest(options);
+ break;
+
+ case 'identity-controller-logout':
+ this.doLogout(options);
+ break;
+
+ default:
+ logger.error('SignInToWebsiteUX', 'Unknown observer notification:', aTopic);
+ break;
+ }
},
- /**
- * 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);
+ serviceDoMethod: function SignInToWebsiteUX_doMethod(aMessage, aId) {
+ logger.log('serviceDoMethod received:', aMessage);
+ switch (aMessage.method) {
+ case 'ready':
+ IdentityService.doReady(aId);
+ break;
- 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;
+ case 'login':
+ if (aMessage._internalParams) {
+ IdentityService.doLogin(aId, aMessage.assertion, aMessage._internalParams);
+ } else {
+ IdentityService.doLogin(aId, aMessage.assertion);
+ }
+ break;
+
+ case 'logout':
+ IdentityService.doLogout(aId);
+ break;
+
+ case 'cancel':
+ IdentityService.doCancel(aId);
+ break;
+
+ default:
+ logger.error('Unknown identity method: ' + aMessage.method);
+ break;
+ }
},
- /**
- * 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);
+ 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];
+ }
},
- /**
- * 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);
+ delegate: function SignInToWebsiteUX_delegate(aOptions) {
+ let hostFrame = new HostFrame();
+ hostFrame.getIframe(aOptions, function() {
+ // iframe has been added to aOptions
- let requestNot = chromeWin.PopupNotifications.getNotification("identity-request", browserEl);
- if (requestNot)
- chromeWin.PopupNotifications.remove(requestNot);
+ // callback for the pipe when flow is complete
+ aOptions.onComplete = function pipe_onComplete() {
+ this.cleanUp(aOptions.id);
+ }.bind(this);
+
+ // store context and communicate with pipe
+ this.contexts[aOptions.id] = aOptions;
+ this.contexts[aOptions.id].hostFrame = hostFrame;
+
+ let pipe = new Pipe(aOptions, this);
+ pipe.communicate();
+ }.bind(this));
},
+ doWatch: function SignInToWebsiteUX_doWatch(aOptions) {
+ aOptions.message = 'identity-delegate-watch';
+ aOptions.showUI = false;
+ this.delegate(aOptions);
+ },
+
+ 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);
+ }
};
--- a/dom/identity/DOMIdentity.jsm
+++ b/dom/identity/DOMIdentity.jsm
@@ -9,33 +9,26 @@ const {classes: Cc, interfaces: Ci, util
// This is the parent process corresponding to nsDOMIdentity.
this.EXPORTED_SYMBOLS = ["DOMIdentity"];
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",
-#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);
}
function IDPProvisioningContext(aID, aOrigin, aTargetMM) {
this._id = aID;
this._origin = aOrigin;
@@ -50,24 +43,24 @@ IDPProvisioningContext.prototype = {
let message = new IDDOMMessage({id: this.id});
message.identity = aID;
message.certDuration = aCertDuration;
this._mm.sendAsyncMessage("Identity:IDP:CallBeginProvisioningCallback",
message);
},
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);
}
};
function IDPAuthenticationContext(aID, aOrigin, aTargetMM) {
this._id = aID;
this._origin = aOrigin;
this._mm = aTargetMM;
}
@@ -79,61 +72,63 @@ IDPAuthenticationContext.prototype = {
doBeginAuthenticationCallback: function IDPAC_doBeginAuthCB(aIdentity) {
let message = new IDDOMMessage({id: this.id});
message.identity = aIdentity;
this._mm.sendAsyncMessage("Identity:IDP:CallBeginAuthenticationCallback",
message);
},
doError: function IDPAC_doError(msg) {
- log("Authentication ERROR: " + msg);
+ logger.warning(msg);
}
};
function RPWatchContext(aOptions, aTargetMM) {
objectCopy(aOptions, this);
// id and origin are required
if (! (this.id && this.origin)) {
- throw new Error("id and origin are required for RP watch context");
+ 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
this.loggedInUser = aOptions.loggedInUser;
// Maybe internal
this._internal = aOptions._internal;
this._mm = 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;
}
this._mm.sendAsyncMessage("Identity:RP:Watch:OnLogin", message);
},
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);
},
doError: function RPWatchContext_onerror(aMessage) {
log("doError: " + aMessage);
}
};
@@ -142,16 +137,18 @@ this.DOMIdentity = {
// nsIMessageListener
receiveMessage: function DOMIdentity_receiveMessage(aMessage) {
let msg = aMessage.json;
// Target is the frame message manager that called us and is
// used to send replies back to the proper window.
let targetMM = aMessage.target;
+ logger.log("received:", aMessage.name);
+
switch (aMessage.name) {
// RP
case "Identity:RP:Watch":
this._watch(msg, targetMM);
break;
case "Identity:RP:Unwatch":
this._unwatch(msg, targetMM);
break;
@@ -212,16 +209,17 @@ this.DOMIdentity = {
"Identity:RP:Unwatch",
"child-process-shutdown"],
// Private.
_init: function DOMIdentity__init() {
Services.ww.registerNotification(this);
Services.obs.addObserver(this, "xpcom-shutdown", false);
this._subscribeListeners();
+ logger.log("DOM identity service initialized");
},
_subscribeListeners: function DOMIdentity__subscribeListeners() {
if (!ppmm) return;
for (let message of this.messages) {
ppmm.addMessageListener(message, this);
}
},
@@ -229,26 +227,28 @@ this.DOMIdentity = {
_unsubscribeListeners: function DOMIdentity__unsubscribeListeners() {
for (let message of this.messages) {
ppmm.removeMessageListener(message, this);
}
ppmm = null;
},
_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);
IdentityService.RP.watch(context);
},
_unwatch: function DOMIdentity_unwatch(message, targetMM) {
IdentityService.RP.unwatch(message.id, targetMM);
--- a/dom/identity/nsDOMIdentity.js
+++ b/dom/identity/nsDOMIdentity.js
@@ -18,22 +18,28 @@ const PREF_SYNTHETIC_EVENTS_OK = "dom.id
const MAX_STRING_LENGTH = 2048;
// Maximum number of times navigator.id.request can be called for a document
const MAX_RP_CALLS = 100;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
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 = {
__exposedProps__: {
// Relying Party (RP)
watch: 'r',
request: 'r',
logout: 'r',
@@ -60,16 +66,17 @@ nsDOMIdentity.prototype = {
return true;
},
/**
* Relying Party (RP) APIs
*/
watch: function nsDOMIdentity_watch(aOptions) {
+ logger.log(aOptions);
if (this._rpWatcher) {
throw new Error("navigator.id.watch was already called");
}
if (!aOptions || typeof(aOptions) !== "object") {
throw new Error("options argument to watch is required");
}
@@ -84,56 +91,59 @@ nsDOMIdentity.prototype = {
// Optional callback "onready"
if (aOptions["onready"]
&& typeof(aOptions['onready']) !== "function") {
throw new Error("onready must be a function");
}
let message = this.DOMIdentityMessage(aOptions);
+ logger.log(message);
// loggedInUser vs loggedInEmail
// https://developer.mozilla.org/en-US/docs/DOM/navigator.id.watch
// This parameter, loggedInUser, was renamed from loggedInEmail in early
// September, 2012. Both names will continue to work for the time being,
// but code should be changed to use loggedInUser instead.
checkRenamed(aOptions, "loggedInEmail", "loggedInUser");
message["loggedInUser"] = aOptions["loggedInUser"];
+ logger.log(message);
let emailType = typeof(aOptions["loggedInUser"]);
if (aOptions["loggedInUser"] && aOptions["loggedInUser"] !== "undefined") {
if (emailType !== "string") {
throw new Error("loggedInUser must be a String or null");
}
// TODO: Bug 767610 - check email format.
// See nsHTMLInputElement::IsValidEmailAddress
if (aOptions["loggedInUser"].indexOf("@") == -1
|| aOptions["loggedInUser"].length > MAX_STRING_LENGTH) {
throw new Error("loggedInUser is not valid");
}
// 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);
// The only time we permit calling of request() outside of a user
// input handler is when we are handling the (deprecated) get() or
// 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;
}
// Has the caller called watch() before this?
if (!this._rpWatcher) {
throw new Error("navigator.id.request called before navigator.id.watch");
}
if (this._rpCalls > MAX_RP_CALLS) {
@@ -166,16 +176,17 @@ nsDOMIdentity.prototype = {
}
}
this._rpCalls++;
this._identityInternal._mm.sendAsyncMessage("Identity:RP:Request", message);
},
logout: function nsDOMIdentity_logout() {
+ logger.log("logout");
if (!this._rpWatcher) {
throw new Error("navigator.id.logout called before navigator.id.watch");
}
if (this._rpCalls > MAX_RP_CALLS) {
throw new Error("navigator.id.logout called too many times");
}
this._rpCalls++;
@@ -238,72 +249,72 @@ nsDOMIdentity.prototype = {
onlogout: function get_onlogout() {},
onready: function get_onready() {
self.request(opts);
}
});
},
getVerifiedEmail: function nsDOMIdentity_getVerifiedEmail(aCallback) {
- Cu.reportError("WARNING: getVerifiedEmail has been deprecated");
+ error("WARNING: getVerifiedEmail has been deprecated");
this.get(aCallback, {});
},
/**
* Identity Provider (IDP) Provisioning APIs
*/
beginProvisioning: function nsDOMIdentity_beginProvisioning(aCallback) {
- this._log("beginProvisioning");
+ logger.log("beginProvisioning");
if (this._beginProvisioningCallback) {
throw new Error("navigator.id.beginProvisioning already called.");
}
if (!aCallback || typeof(aCallback) !== "function") {
throw new Error("beginProvisioning callback is required.");
}
this._beginProvisioningCallback = aCallback;
this._identityInternal._mm.sendAsyncMessage("Identity:IDP:BeginProvisioning",
this.DOMIdentityMessage());
},
genKeyPair: function nsDOMIdentity_genKeyPair(aCallback) {
- this._log("genKeyPair");
+ logger.log("genKeyPair");
if (!this._beginProvisioningCallback) {
throw new Error("navigator.id.genKeyPair called outside of provisioning");
}
if (this._genKeyPairCallback) {
throw new Error("navigator.id.genKeyPair already called.");
}
if (!aCallback || typeof(aCallback) !== "function") {
throw new Error("genKeyPair callback is required.");
}
this._genKeyPairCallback = aCallback;
this._identityInternal._mm.sendAsyncMessage("Identity:IDP:GenKeyPair",
this.DOMIdentityMessage());
},
registerCertificate: function nsDOMIdentity_registerCertificate(aCertificate) {
- this._log("registerCertificate");
+ logger.log("registerCertificate");
if (!this._genKeyPairCallback) {
throw new Error("navigator.id.registerCertificate called outside of provisioning");
}
if (this._provisioningEnded) {
throw new Error("Provisioning already ended");
}
this._provisioningEnded = true;
let message = this.DOMIdentityMessage();
message.cert = aCertificate;
this._identityInternal._mm.sendAsyncMessage("Identity:IDP:RegisterCertificate", message);
},
raiseProvisioningFailure: function nsDOMIdentity_raiseProvisioningFailure(aReason) {
- this._log("raiseProvisioningFailure '" + aReason + "'");
+ logger.log("raiseProvisioningFailure '" + aReason + "'");
if (this._provisioningEnded) {
throw new Error("Provisioning already ended");
}
if (!aReason || typeof(aReason) != "string") {
throw new Error("raiseProvisioningFailure reason is required");
}
this._provisioningEnded = true;
@@ -312,17 +323,17 @@ nsDOMIdentity.prototype = {
this._identityInternal._mm.sendAsyncMessage("Identity:IDP:ProvisioningFailure", message);
},
/**
* Identity Provider (IDP) Authentication APIs
*/
beginAuthentication: function nsDOMIdentity_beginAuthentication(aCallback) {
- this._log("beginAuthentication");
+ logger.log("beginAuthentication");
if (this._beginAuthenticationCallback) {
throw new Error("navigator.id.beginAuthentication already called.");
}
if (typeof(aCallback) !== "function") {
throw new Error("beginAuthentication callback is required.");
}
if (!aCallback || typeof(aCallback) !== "function") {
throw new Error("beginAuthentication callback is required.");
@@ -400,54 +411,54 @@ nsDOMIdentity.prototype = {
return;
}
this._initializeState();
Services.obs.notifyObservers(null, "identity-DOM-state-reset", this._id);
break;
case "Identity:RP:Watch:OnLogin":
// Do we have a watcher?
if (!this._rpWatcher) {
- this._log("WARNING: Received OnLogin message, but there is no RP watcher");
+ logger.warning("Received OnLogin message, but there is no RP watcher");
return;
}
if (this._rpWatcher.onlogin) {
if (this._rpWatcher._internal) {
this._rpWatcher.onlogin(msg.assertion, msg._internalParams);
} else {
this._rpWatcher.onlogin(msg.assertion);
}
}
break;
case "Identity:RP:Watch:OnLogout":
// Do we have a watcher?
if (!this._rpWatcher) {
- this._log("WARNING: Received OnLogout message, but there is no RP watcher");
+ logger.warning("Received OnLogout message, but there is no RP watcher");
return;
}
if (this._rpWatcher.onlogout) {
this._rpWatcher.onlogout();
}
break;
case "Identity:RP:Watch:OnReady":
// Do we have a watcher?
if (!this._rpWatcher) {
- this._log("WARNING: Received OnReady message, but there is no RP watcher");
+ logger.warning("Received OnReady message, but there is no RP watcher");
return;
}
if (this._rpWatcher.onready) {
this._rpWatcher.onready();
}
break;
case "Identity:RP:Watch:OnCancel":
// Do we have a watcher?
if (!this._rpWatcher) {
- this._log("WARNING: Received OnCancel message, but there is no RP watcher");
+ logger.warning("Received OnCancel message, but there is no RP watcher");
return;
}
if (this._onCancelRequestCallback) {
this._onCancelRequestCallback();
}
break;
case "Identity:IDP:CallBeginProvisioningCallback":
@@ -457,20 +468,16 @@ nsDOMIdentity.prototype = {
this._callGenKeyPairCallback(msg);
break;
case "Identity:IDP:CallBeginAuthenticationCallback":
this._callBeginAuthenticationCallback(msg);
break;
}
},
- _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);
// bunch of stuff to create a proper object in window context
function genPropDesc(value) {
return {
enumerable: true, configurable: true, writable: true, value: value
@@ -520,17 +527,17 @@ nsDOMIdentity.prototype = {
// window origin
message.origin = this._origin;
return message;
},
uninit: function DOMIdentity_uninit() {
- this._log("nsDOMIdentity uninit()");
+ logger.log("unwatch", this._id);
this._identityInternal._mm.sendAsyncMessage(
"Identity:RP:Unwatch",
{ id: this._id }
);
}
};
@@ -593,17 +600,17 @@ nsDOMIdentityInternal.prototype = {
this._identity._init(aWindow);
let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
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;
// Setup listeners for messages from parent process.
this._messages = [
"Identity:ResetState",
"Identity:RP:Watch:OnLogin",
"Identity:RP:Watch:OnLogout",
@@ -618,24 +625,16 @@ nsDOMIdentityInternal.prototype = {
}, this);
// Setup observers so we can remove message listeners.
Services.obs.addObserver(this, "inner-window-destroyed", false);
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}"),
QueryInterface: XPCOMUtils.generateQI(
[Ci.nsIDOMGlobalPropertyInitializer, Ci.nsIMessageListener]
),
classInfo: XPCOMUtils.generateCI({
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -379,17 +379,17 @@ pref("toolkit.telemetry.server", "https:
pref("toolkit.telemetry.server_owner", "Mozilla");
// Information page about telemetry (temporary ; will be about:telemetry in the end)
pref("toolkit.telemetry.infoURL", "http://www.mozilla.com/legal/privacy/firefox.html#telemetry");
// Determines whether full SQL strings are returned when they might contain sensitive info
// i.e. dynamically constructed SQL strings or SQL executed by addons against addon DBs
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.
pref("devtools.errorconsole.deprecation_warnings", true);
// Disable remote debugging protocol logging
pref("devtools.debugger.log", false);
// Disable remote debugging connections
--- a/toolkit/identity/Identity.jsm
+++ b/toolkit/identity/Identity.jsm
@@ -10,31 +10,28 @@ this.EXPORTED_SYMBOLS = ["IdentityServic
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/IdentityStore.jsm");
Cu.import("resource://gre/modules/identity/RelyingParty.jsm");
Cu.import("resource://gre/modules/identity/IdentityProvider.jsm");
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);
Services.obs.addObserver(this, "identity-auth-complete", false);
this._store = IdentityStore;
this.RP = RelyingParty;
this.IDP = IdentityProvider;
@@ -48,17 +45,17 @@ IDService.prototype = {
case "quit-application-granted":
Services.obs.removeObserver(this, "quit-application-granted");
this.shutdown();
break;
case "identity-auth-complete":
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);
break;
}
},
reset: function reset() {
@@ -68,17 +65,17 @@ IDService.prototype = {
// called here, on RP, on IDP, and on the store. So you don't
// need to use this :)
this._store.reset();
this.RP.reset();
this.IDP.reset();
},
shutdown: function shutdown() {
- log("shutdown");
+ logger.log("shutdown");
Services.obs.removeObserver(this, "identity-auth-complete");
Services.obs.removeObserver(this, "quit-application-granted");
},
/**
* Parse an email into username and domain if it is valid, else return null
*/
parseEmail: function parseEmail(email) {
@@ -112,34 +109,34 @@ IDService.prototype = {
* @param aRPId
* (integer) the id of the doc object obtained in .watch() and
* passed to the UX component.
*
* @param aIdentity
* (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;
}
// It's possible that we are in the process of provisioning an
// identity.
let provId = rp.provId;
let rpLoginOptions = {
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.
let self = this;
this.RP._generateAssertion(rp.origin, aIdentity, function hadReadyAssertion(err, assertion) {
if (!err && assertion) {
self.RP._doLogin(rp, rpLoginOptions, assertion);
return;
@@ -170,17 +167,17 @@ IDService.prototype = {
// We are not authenticated. If we have already tried to
// authenticate and failed, then this is a "hard fail" and
// we give up. Otherwise we try to authenticate with the
// IdP.
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;
}
// Try to authenticate with the IdP. Note that we do
// not clean up the provision flow here. We will continue
// to use it.
self.IDP._doAuthentication(aProvId, idpParams);
return;
@@ -217,17 +214,17 @@ IDService.prototype = {
*/
_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);
+ logger.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
@@ -246,60 +243,60 @@ IDService.prototype = {
* 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);
+ 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"]
.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);
+ logger.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);
+ logger.log("_fetchWellKnownFile:", errStr);
return aCallback(errStr);
}
let callbackObj = {
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);
},
};
this.IdentityService = new IDService();
--- a/toolkit/identity/IdentityProvider.jsm
+++ b/toolkit/identity/IdentityProvider.jsm
@@ -8,33 +8,29 @@
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/Sandbox.jsm");
this.EXPORTED_SYMBOLS = ["IdentityProvider"];
const FALLBACK_PROVIDER = "browserid.org";
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,
"_store",
"resource://gre/modules/identity/IdentityStore.jsm",
"IdentityStore");
this.reset();
@@ -71,17 +67,17 @@ IdentityProviderService.prototype = {
getProvisionFlow: function getProvisionFlow(aProvId, aErrBack) {
let provFlow = this._provisionFlows[aProvId];
if (provFlow) {
return provFlow;
}
let err = "No provisioning flow found with id " + aProvId;
- log("ERROR:", err);
+ logger.warning("ERROR:", err);
if (typeof aErrBack === 'function') {
aErrBack(err);
}
},
shutdown: function RP_shutdown() {
this.reset();
@@ -117,25 +113,25 @@ IdentityProviderService.prototype = {
*
* @param aCallback
* (function) callback to invoke on completion
* with first-positional parameter the error.
*/
_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
// new provision flow.
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 {
this._createProvisioningSandbox(url, function createdSandbox(aSandbox) {
// create a provisioning flow, using the sandbox id, and
// stash callback associated with this provisioning workflow.
let provId = aSandbox.id;
@@ -144,17 +140,17 @@ IdentityProviderService.prototype = {
idpParams: aIDPParams,
securityLevel: this.securityLevel,
provisioningSandbox: aSandbox,
callback: function doCallback(aErr) {
aCallback(aErr, provId);
},
};
- 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));
}
},
// DOM Methods
/**
@@ -162,17 +158,17 @@ IdentityProviderService.prototype = {
*
* @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) {
- 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);
// keep the caller object around
provFlow.caller = aCaller;
let identity = provFlow.identity;
@@ -194,17 +190,17 @@ IdentityProviderService.prototype = {
* 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);
+ logger.warning("Provisioning failure", aReason);
// look up the provisioning caller and its callback
let provFlow = this.getProvisionFlow(aProvId);
// Sandbox is deleted in _cleanUpProvisionFlow in case we re-use it.
// This may be either a "soft" or "hard" fail. If it's a
// soft fail, we'll flow through setAuthenticationFlow, where
@@ -227,35 +223,35 @@ IdentityProviderService.prototype = {
* the beginProvisioning() call first.
*/
genKeyPair: function genKeyPair(aProvId) {
// Look up the provisioning caller and make sure it's valid.
let provFlow = this.getProvisionFlow(aProvId);
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;
}
provFlow.kp = kp;
// 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));
},
/**
* When navigator.id.registerCertificate is called from provisioning iframe
* sandbox.
*
@@ -266,28 +262,28 @@ IdentityProviderService.prototype = {
* (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) {
- 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;
}
// store the keypair and certificate just provided in IDStore.
this._store.addIdentity(provFlow.identity, provFlow.kp, aCert);
// Great success!
@@ -303,17 +299,17 @@ IdentityProviderService.prototype = {
* @param aProvId
* (int) the identifier of the provisioning flow which failed
*
* @param aCallback
* (function) to invoke upon completion, with
* 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.
// extract authentication URL from idpParams
let authPath = aIDPParams.idpParams.authentication;
let authURI = Services.io.newURI("https://" + aIDPParams.domain, null, null).resolve(authPath);
// beginAuthenticationFlow causes the "identity-auth" topic to be
@@ -336,49 +332,49 @@ IdentityProviderService.prototype = {
* 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) {
- 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
// the provisioning flow we just concluded. (see setAuthenticationFlow)
let authFlow = this._authenticationFlows[aCaller.id];
if (!authFlow) {
return aCaller.doError("beginAuthentication: no flow for caller id", aCaller.id);
}
authFlow.caller = aCaller;
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);
},
/**
* 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) {
- 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;
// delete caller
delete authFlow['caller'];
delete this._authenticationFlows[aAuthId];
@@ -394,92 +390,92 @@ IdentityProviderService.prototype = {
/**
* The auth frame has called navigator.id.cancelAuthentication
*
* @param aAuthId
* (int) the identifier of the authentication caller
*
*/
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;
// delete caller
delete authFlow['caller'];
delete this._authenticationFlows[aAuthId];
let provFlow = this.getProvisionFlow(provId);
provFlow.didAuthentication = true;
Services.obs.notifyObservers(null, "identity-auth-complete", aAuthId);
// invoke callback with ERROR.
let errStr = "Authentication canceled by IDP";
- log("ERROR: cancelAuthentication:", errStr);
+ logger.log("ERROR: cancelAuthentication:", errStr);
provFlow.callback(errStr);
},
/**
* Called by the UI to set the ID and caller for the authentication flow after it gets its ID
*/
setAuthenticationFlow: function(aAuthId, aProvId) {
// 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;
},
/**
* Load the provisioning URL in a hidden frame to start the provisioning
* process.
*/
_createProvisioningSandbox: function _createProvisioningSandbox(aURL, aCallback) {
- log("_createProvisioningSandbox:", aURL);
+ logger.log("_createProvisioningSandbox:", aURL);
if (!this._sandboxConfigured) {
// Configure message manager listening on the hidden window
Cu.import("resource://gre/modules/DOMIdentity.jsm");
DOMIdentity._configureMessages(Services.appShell.hiddenDOMWindow, true);
this._sandboxConfigured = true;
}
new Sandbox(aURL, aCallback);
},
/**
* 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);
},
/**
* Clean up a provision flow and the authentication flow and sandbox
* 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'];
}
// Clean up a related authentication flow, if there is one.
if (this._authenticationFlows[prov.authId]) {
delete this._authenticationFlows[prov.authId];
--- a/toolkit/identity/IdentityUtils.jsm
+++ b/toolkit/identity/IdentityUtils.jsm
@@ -18,40 +18,38 @@ this.EXPORTED_SYMBOLS = [
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
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';
}
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;
};
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);
}
if (checkDeprecated(aOptions, aOldName)) {
aOptions[aNewName] = aOptions[aOldName];
delete(aOptions[aOldName]);
}
};
@@ -59,17 +57,17 @@ this.checkRenamed = function checkRename
this.getRandomId = function getRandomId() {
return uuidgen.generateUUID().toString();
};
/*
* 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] !== '_') {
desc = Object.getOwnPropertyDescriptor(source, name);
Object.defineProperty(target, name, desc);
}
});
};
--- a/toolkit/identity/LogUtils.jsm
+++ b/toolkit/identity/LogUtils.jsm
@@ -1,103 +1,178 @@
/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
-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:
this.log("Logger observer", "Unknown topic:", aTopic);
break;
}
},
- _generateLogMessage: function _generateLogMessage(aPrefix, args) {
- // create a string representation of a list of arbitrary things
- let strings = [];
+ _generatePrefix: function _generatePrefix() {
+ let caller = Components.stack.caller.caller;
+ let parts = ['[' + this._identifier + ']'];
- // XXX bug 770418 - args look like flattened array, not list of strings
+ // 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);
+ }
+ }
- args.forEach(function(arg) {
- if (typeof arg === 'string') {
- strings.push(arg);
- } else if (typeof arg === 'undefined') {
- strings.push('undefined');
- } else if (arg === null) {
+ // 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 = [];
+ 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(' ');
},
/**
* log() - utility function to print a list of arbitrary things
*
* 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);
+ let output = this._generatePrefix() + this._generateLogMessage('info', argList);
+
+ // print to the shell console and the browser error console
dump(output + "\n");
-
- // Additionally, make the output visible in the Error Console
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];
+}
--- a/toolkit/identity/MinimalIdentity.jsm
+++ b/toolkit/identity/MinimalIdentity.jsm
@@ -14,34 +14,25 @@
*/
"use strict";
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 = {};
options.id = aRpCaller.id;
options.origin = aRpCaller.origin;
// loggedInUser can be undefined, null, or a string
@@ -59,64 +50,48 @@ function makeMessageObject(aRpCaller) {
options[option] = aRpCaller[option];
}
});
// check validity of message structure
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);
}
return options;
}
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;
this.IDP = this;
// keep track of flows
this._rpFlows = {};
this._authFlows = {};
this._provFlows = {};
}
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().
*
* @param aCaller
* (Object) an object that represents the caller document, and
* is expected to have properties:
* - id (unique, e.g. uuid)
* - loggedInUser (string or null)
@@ -125,51 +100,51 @@ IDService.prototype = {
* and a bunch of callbacks
* - doReady()
* - doLogin()
* - doLogout()
* - doError()
* - 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);
},
/*
* 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);
},
/**
* Initiate a login with user interaction as a result of a call to
* navigator.id.request().
*
* @param aRPId
* (integer) the id of the doc object obtained in .watch()
*
* @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.
// Pass the doc id to UX so it can pass it back to us later.
let options = makeMessageObject(rp);
objectCopy(aOptions, options);
Services.obs.notifyObservers({wrappedJSObject: options}, "identity-controller-request", null);
},
@@ -177,269 +152,74 @@ IDService.prototype = {
/**
* Invoked when a user wishes to logout of a site (for instance, when clicking
* on an in-content logout button).
*
* @param aRpCallerId
* (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);
},
/*
* once the UI-and-display-logic components have received
* notifications, they call back with direct invocation of the
* 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");
return;
}
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");
return;
}
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");
return;
}
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");
return;
}
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();
--- a/toolkit/identity/RelyingParty.jsm
+++ b/toolkit/identity/RelyingParty.jsm
@@ -8,32 +8,29 @@
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");
Cu.import("resource://gre/modules/identity/IdentityStore.jsm");
this.EXPORTED_SYMBOLS = ["RelyingParty"];
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
// IdentityProvider. The Identity module takes care of resetting
// state in the _store on shutdown.
this._store = IdentityStore;
this.reset();
@@ -82,17 +79,17 @@ IdentityRelyingParty.prototype = {
* - doCancel()
*
*/
watch: function watch(aRpCaller) {
this._rpFlows[aRpCaller.id] = aRpCaller;
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,
"email:", state.email);
// If the user is already logged in, then there are three cases
// to deal with:
//
@@ -134,45 +131,45 @@ IdentityRelyingParty.prototype = {
/**
* A utility for watch() to set state and notify the dom
* on login
*
* 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);
this._notifyLoginStateChanged(aRpCaller.id, aOptions.loggedInUser);
aRpCaller.doLogin(assertion);
aRpCaller.doReady();
}.bind(this);
if (aAssertion) {
loginWithAssertion(aAssertion);
} 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);
}
}.bind(this));
}
},
/**
* A utility for watch() to set state and notify the dom
* 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) || {};
state.isLoggedIn = false;
this._notifyLoginStateChanged(aRpCaller.id, null);
aRpCaller.doLogout();
aRpCaller.doReady();
@@ -186,17 +183,17 @@ IdentityRelyingParty.prototype = {
*
* @param aRpCallerId
* (integer) The id of the RP caller
*
* @param aIdentity
* (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},
"identity-login-state-changed",
aIdentity);
},
/**
@@ -205,17 +202,17 @@ IdentityRelyingParty.prototype = {
*
* @param aRPId
* (integer) the id of the doc object obtained in .watch()
*
* @param aOptions
* (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.
// Pass the doc id to UX so it can pass it back to us later.
let options = {rpId: aRPId, origin: rp.origin};
objectCopy(aOptions, options);
// Append URLs after resolving
@@ -233,33 +230,33 @@ IdentityRelyingParty.prototype = {
* Invoked when a user wishes to logout of a site (for instance, when clicking
* on an in-content logout button).
*
* @param aRpCallerId
* (integer) the id of the doc object obtained in .watch()
*
*/
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.
},
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;
},
/**
* Return the list of identities a user may want to use to login to aOrigin.
*/
getIdentitiesForSite: function getIdentitiesForSite(aOrigin) {
let rv = { result: [] };
@@ -288,33 +285,33 @@ IdentityRelyingParty.prototype = {
* issued. If this property is not set an exception
* will be thrown.
*
* Any properties not listed above will be ignored.
*/
_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";
}
// We might not have any identity info for this email
if (!this._store.fetchIdentity(email)) {
this._store.addIdentity(email, null, null);
}
let cert = this._store.fetchIdentity(email)['cert'];
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);
});
}
},
/**
* Generate an assertion, including provisioning via IdP if necessary,
* but no user interaction, so if provisioning fails, aCallback is invoked
@@ -326,45 +323,45 @@ IdentityRelyingParty.prototype = {
* @param aIdentity
* (string) the email we're logging in with
*
* @param aCallback
* (function) callback to invoke on completion
* 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;
}
let kp = id.keyPair;
if (!kp) {
let errStr = "Cannot generate an assertion without a keypair";
- log("ERROR: _generateAssertion:", errStr);
+ logger.log("ERROR: _generateAssertion:", errStr);
aCallback(errStr);
return;
}
jwcrypto.generateAssertion(id.cert, kp, aAudience, aCallback);
},
/**
* Clean up references to the provisioning flow for the specified RP.
*/
_cleanUpProvisionFlow: function RP_cleanUpProvisionFlow(aRPId, aProvId) {
let rp = this._rpFlows[aRPId];
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);
}
},
};
this.RelyingParty = new IdentityRelyingParty();
--- a/toolkit/identity/Sandbox.jsm
+++ b/toolkit/identity/Sandbox.jsm
@@ -8,19 +8,20 @@ this.EXPORTED_SYMBOLS = ["Sandbox"];
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
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
* callback provided to the constructor will be invoked when the sandbox is
* ready to be used. The callback will receive this object as its only argument.
*
* You must call free() when you are finished with the sandbox to explicitly
* free up all associated resources.
@@ -29,17 +30,17 @@ XPCOMUtils.defineLazyModuleGetter(this,
* (string) URL to load in the sandbox.
*
* @param aCallback
* (function) Callback to be invoked with a Sandbox, when ready.
*/
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);
};
this.Sandbox.prototype = {
/**
* Use the outer window ID as the identifier of the sandbox.
@@ -49,28 +50,28 @@ this.Sandbox.prototype = {
.getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
},
/**
* Reload the URL in the sandbox. This is useful to reuse a Sandbox (same
* 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));
},
/**
* 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;
this._url = null;
},
/**
* Creates an empty, hidden iframe and sets it to the _frame
@@ -110,17 +111,17 @@ this.Sandbox.prototype = {
// Set instance properties.
this._frame = frame;
this._container = doc.documentElement;
},
_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;
}
self._frame.removeEventListener(
"DOMWindowCreated", _makeSandboxContentLoaded, true
);
@@ -139,15 +140,10 @@ this.Sandbox.prototype = {
webNav.loadURI(
this._url,
Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE,
null, // referrer
null, // postData
null // headers
);
- },
-
- _log: function Sandbox__log(...aMessageArgs) {
- Logger.log.apply(Logger, ["sandbox"].concat(aMessageArgs));
- },
-
+ }
};
--- a/toolkit/identity/jwcrypto.jsm
+++ b/toolkit/identity/jwcrypto.jsm
@@ -9,33 +9,33 @@
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");
+
+XPCOMUtils.defineLazyGetter(this, "logger", function() {
+ Cu.import('resource://gre/modules/identity/LogUtils.jsm');
+ return getLogger("Identity test", "toolkit.identity.debug");
+});
XPCOMUtils.defineLazyServiceGetter(this,
"IdentityCryptoService",
"@mozilla.org/identity/crypto-service;1",
"nsIIdentityCryptoService");
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)) {
return aCallback("key generation failed");
}
var publicKey;
@@ -69,36 +69,36 @@ function generateKeyPair(aAlgorithmName,
return aCallback(null, keyWrapper);
});
}
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);
});
}
function jwcryptoClass()
{
}
jwcryptoClass.prototype = {
isCertValid: function(aCert, aCallback) {
// XXX check expiration, bug 769850
aCallback(true);
},
generateKeyPair: function(aAlgorithmName, aCallback) {
- log("generating");
+ logger.log("generating");
generateKeyPair(aAlgorithmName, aCallback);
},
generateAssertion: function(aCert, aKeyPair, aAudience, aCallback) {
// for now, we hack the algorithm name
// XXX bug 769851
var header = {"alg": "DS128"};
var headerBytes = IdentityCryptoService.base64UrlEncode(
@@ -108,17 +108,17 @@ jwcryptoClass.prototype = {
// expires in 2 minutes
// XXX clock skew needs exploration bug 769852
exp: Date.now() + (2 * 60 * 1000),
aud: aAudience
};
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);
var signedAssertion = headerBytes + "." + payloadBytes + "." + signature;
return aCallback(null, aCert + "~" + signedAssertion);
});
}
--- a/toolkit/identity/tests/unit/head_identity.js
+++ b/toolkit/identity/tests/unit/head_identity.js
@@ -20,20 +20,16 @@ XPCOMUtils.defineLazyModuleGetter(this,
XPCOMUtils.defineLazyModuleGetter(this, "IDService",
"resource://gre/modules/identity/Identity.jsm",
"IdentityService");
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",
"nsIUUIDGenerator");
const TEST_URL = "https://myfavoritebacon.com";
const TEST_URL2 = "https://myfavoritebaconinacan.com";
const TEST_USER = "user@mozilla.com";
@@ -70,20 +66,23 @@ let XULAppInfoFactory = {
};
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
registrar.registerFactory(Components.ID("{fbfae60b-64a4-44ef-a911-08ceb70b9f31}"),
"XULAppInfo", "@mozilla.org/xre/app-info;1",
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;
}
function partial(fn) {
let args = Array.prototype.slice.call(arguments, 1);
return function() {
--- a/toolkit/identity/tests/unit/test_crypto_service.js
+++ b/toolkit/identity/tests/unit/test_crypto_service.js
@@ -1,64 +1,68 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
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);
const ALG_DSA = "DS160";
const ALG_RSA = "RS256";
// When the output of an operation is a
function do_check_eq_or_slightly_less(x, y) {
do_check_true(x >= y - (3 * 8));
}
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);
do_check_eq_or_slightly_less(keyPair.hexDSAGenerator.length, 1024 / 8 * 2);
do_check_eq_or_slightly_less(keyPair.hexDSAPrime.length, 1024 / 8 * 2);
do_check_eq_or_slightly_less(keyPair.hexDSASubPrime.length, 160 / 8 * 2);
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
run_next_test();
});
});
}
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);
do_check_eq_or_slightly_less(keyPair.hexRSAPublicKeyModulus.length,
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();
});
});
}
add_test(test_dsa);
--- a/toolkit/identity/tests/unit/test_jwcrypto.js
+++ b/toolkit/identity/tests/unit/test_jwcrypto.js
@@ -1,22 +1,25 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict"
-Cu.import('resource://gre/modules/identity/LogUtils.jsm');
-
XPCOMUtils.defineLazyModuleGetter(this, "IDService",
"resource://gre/modules/identity/Identity.jsm",
"IdentityService");
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://";
function test_sanity() {
do_test_pending();
jwcrypto.generateKeyPair("DS160", function(err, kp) {
do_check_null(err);
@@ -42,29 +45,29 @@ function test_get_assertion() {
jwcrypto.generateKeyPair(
"DS160",
function(err, kp) {
jwcrypto.generateAssertion("fake-cert", kp, RP_ORIGIN, function(err, assertion) {
do_check_null(err);
// more checks on assertion
- log("assertion", assertion);
+ logger.log("assertion", assertion);
do_test_finished();
run_next_test();
});
});
}
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
do_check_neq(kpo.sign, null);
do_check_eq(typeof kpo.sign, "function");
do_check_neq(kpo.userID, null);
do_check_neq(kpo.url, null);
do_check_eq(kpo.url, INTERNAL_ORIGIN);
@@ -84,17 +87,17 @@ function test_rsa() {
jwcrypto.generateKeyPair("RS256", checkRSA);
}
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
do_check_neq(kpo.sign, null);
do_check_eq(typeof kpo.sign, "function");
do_check_neq(kpo.userID, null);
do_check_neq(kpo.url, null);
do_check_eq(kpo.url, INTERNAL_ORIGIN);
--- a/toolkit/identity/tests/unit/test_log_utils.js
+++ b/toolkit/identity/tests/unit/test_log_utils.js
@@ -1,74 +1,73 @@
"use strict";
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();
function Wrapper() {
this.init();
}
Wrapper.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
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();
}
},
init: function() {
Services.prefs.addObserver('toolkit.identity.debug', this, false);
}
};
var wrapper = new Wrapper();
Services.prefs.setBoolPref('toolkit.identity.debug', true);
}
// 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();
-}
\ No newline at end of file
+}
+
--- a/toolkit/identity/tests/unit/test_minimalidentity.js
+++ b/toolkit/identity/tests/unit/test_minimalidentity.js
@@ -1,20 +1,14 @@
"use strict";
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();
}
function test_mock_doc() {
do_test_pending();
let mockedDoc = mock_doc(null, TEST_URL, function(action, params) {