diff --git a/browserid/app.js b/browserid/app.js index f637f884a8bc9ecbde0e1a5e682b6618fb20a6da..6f661ee7c23063c93e6e44bfcfaaafda91e8e68f 100644 --- a/browserid/app.js +++ b/browserid/app.js @@ -80,11 +80,16 @@ function router(app) { metrics.userEntry(req); res.render('dialog.ejs', { title: 'A Better Way to Sign In', - layout: false, + layout: 'dialog_layout.ejs', + useJavascript: true, production: configuration.get('use_minified_resources') }); }); + app.get("/unsupported_dialog", function(req,res) { + res.render('unsupported_dialog.ejs', {layout: 'dialog_layout.ejs', useJavascript: false}); + }); + // simple redirects (internal for now) app.get('/register_iframe', internal_redirector('/dialog/register_iframe.html',true)); diff --git a/browserid/static/dialog/controllers/dialog_controller.js b/browserid/static/dialog/controllers/dialog_controller.js index 771c3177067bcf5432d58d20bb40fc4eba72a81a..49560e08de033003c50a668bb273d72ffd141d02 100644 --- a/browserid/static/dialog/controllers/dialog_controller.js +++ b/browserid/static/dialog/controllers/dialog_controller.js @@ -50,18 +50,17 @@ PageController.extend("Dialog", {}, { init: function(el) { var self=this; - //this.element.show(); // keep track of where we are and what we do on success and error self.onsuccess = null; self.onerror = null; setupChannel(self); self.stateMachine(); + }, getVerifiedEmail: function(origin_url, onsuccess, onerror) { var self=this; - self.onsuccess = onsuccess; self.onerror = onerror; @@ -71,8 +70,6 @@ } user.setOrigin(origin_url); - - // get the cleaned origin. $("#sitename").text(user.getHostname()); self.doCheckAuth(); @@ -88,7 +85,6 @@ var self=this, hub = OpenAjax.hub, el = this.element; - hub.subscribe("offline", function(msg, info) { self.doOffline(); @@ -119,7 +115,7 @@ }); hub.subscribe("assertion_generated", function(msg, info) { - if(info.assertion !== null) { + if (info.assertion !== null) { self.doAssertionGenerated(info.assertion); } else { @@ -158,12 +154,12 @@ }, doOffline: function() { - this.renderError(errors.offline); + this.renderError("wait.ejs", errors.offline); offline = true; }, doXHRError: function(info) { - if (!offline) this.renderError(errors.offline); + if (!offline) this.renderError("wait.ejs", errors.offline); }, doConfirmUser: function(email) { @@ -178,7 +174,7 @@ doCancel: function() { var self=this; - if(self.onsuccess) { + if (self.onsuccess) { self.onsuccess(null); } }, diff --git a/browserid/static/dialog/controllers/page_controller.js b/browserid/static/dialog/controllers/page_controller.js index ba80f9bbff4ee3569571b92843c422b76aa2c0e7..01ddbb26a0671476402fb333de998bdb32998db5 100644 --- a/browserid/static/dialog/controllers/page_controller.js +++ b/browserid/static/dialog/controllers/page_controller.js @@ -48,6 +48,8 @@ var me=this, bodyTemplate = options.bodyTemplate, bodyVars = options.bodyVars, + errorTemplate = options.errorTemplate, + errorVars = options.errorVars, waitTemplate = options.waitTemplate, waitVars = options.waitVars; @@ -60,6 +62,10 @@ me.renderWait(waitTemplate, waitVars); } + if(errorTemplate) { + me.renderError(errorTemplate, errorVars); + } + // XXX move all of these, bleck. $("form").bind("submit", me.onSubmit.bind(me)); $("#cancel").click(me.onCancel.bind(me)); @@ -99,10 +105,10 @@ $("#wait").stop().hide().fadeIn(ANIMATION_TIME); }, - renderError: function(error_vars) { - this.renderTemplates("#error", "wait.ejs", error_vars); - $("body").removeClass("waiting").removeClass("form").addClass("error").css('opacity', 1); - $("#error").stop().hide().fadeIn(ANIMATION_TIME); + renderError: function(body, body_vars) { + this.renderTemplates("#error", body, body_vars); + $("body").removeClass("waiting").removeClass("form").addClass("error"); + $("#error").stop().css('opacity', 1).hide().fadeIn(ANIMATION_TIME); }, onSubmit: function(event) { @@ -144,7 +150,7 @@ */ getErrorDialog: function(info) { var self=this; - return self.renderError.bind(self, info); + return self.renderError.bind(self, "wait.ejs", info); }, onCancel: function(event) { diff --git a/browserid/static/dialog/css/m.css b/browserid/static/dialog/css/m.css index 49f4a561375d7648adab479f5d104dab69b0c9bb..9ddb605ea69a40de8225cbc81420ceecf12ddcb4 100644 --- a/browserid/static/dialog/css/m.css +++ b/browserid/static/dialog/css/m.css @@ -111,6 +111,19 @@ height: 250px; } -} + #error .vertical { + width: auto; + } + #error .vertical > div { + display: block; + height: auto; + padding: 10px; + } + + #error #borderbox { + border-left: none; + padding: 0; + } + diff --git a/browserid/static/dialog/css/popup.css b/browserid/static/dialog/css/popup.css index b1b54243f2d023bc6e2a46f27d7a621ad3b1ba01..e5f4503f22515f34345a6993ba6c885a472fd836 100644 --- a/browserid/static/dialog/css/popup.css +++ b/browserid/static/dialog/css/popup.css @@ -118,16 +118,17 @@ section > .contents { #wait, #error { text-align: center; - background-image: url("/i/bg.png"); } #wait { z-index: 1; + background-image: url("/i/bg.png"); } #error { display: none; z-index: 2; + background-color: #fff; } #wait strong, #error strong { @@ -135,10 +136,40 @@ section > .contents { font-weight: bold; } -#error { - z-index: 2; + +#error .vertical { + width: 630px; + margin: 0 auto; + display: block; +} + + +#error .vertical > div { + display: table-cell; + vertical-align: middle; + padding: 0 10px; + height: 250px; +} + +#error #alternative a { + color: #549FDC; + text-decoration: underline; +} + +#error #borderbox { + border-left: 1px solid #777; + padding: 20px 0; } +#error #borderbox img { + border: none; +} + +#error #alternative .lighter { + color: #777; +} + + #formWrap { background-color: #fff; background-image: none; diff --git a/browserid/static/dialog/qunit.html b/browserid/static/dialog/qunit.html index 716ec5323d770965ab866d14c51cb36583920fce..7e8189f829a5ff161198b6c2aa4251a536757786 100644 --- a/browserid/static/dialog/qunit.html +++ b/browserid/static/dialog/qunit.html @@ -12,28 +12,31 @@ <div id="qunit-testrunner-toolbar"></div> <h2 id="qunit-userAgent"></h2> <div id="test-content"> - <div id="page_controller"> + </div> + <ol id="qunit-tests"></ol> + <div id="qunit-test-area"></div> - <div id="formWrap"> - <div class="contents"></div> - </div> + <h3>Content below here is test content that can be ignored</h3> - <div id="wait"> - <div class="contents"></div> - </div> + <div id="controller_head"> - <div id="error"> - <div class="contents"></div> - </div> + <div id="formWrap"> + <div class="contents"></div> + </div> + <div id="wait"> + <div class="contents"></div> </div> + + <div id="error"> + <div class="contents"></div> + </div> + <span id="email"></span> <span id="cannotconfirm" class="error">Cannot confirm</span> <span id="cannotcommunicate" class="error">Cannot communicate</span> <span id="siteinfo" class="error"><span class="website"></span></span> <span class=".hint">Hint</span> </div> - <ol id="qunit-tests"></ol> - <div id="qunit-test-area"></div> </body> </html> diff --git a/browserid/static/dialog/resources/browser-support.js b/browserid/static/dialog/resources/browser-support.js new file mode 100644 index 0000000000000000000000000000000000000000..6c2e34818d0e67ec6b20ed76ce0f03ce3404593a --- /dev/null +++ b/browserid/static/dialog/resources/browser-support.js @@ -0,0 +1,118 @@ +/*globals BrowserID: true */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla BrowserID. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +BrowserID.BrowserSupport = (function() { + var bid = BrowserID, + win = window, + nav = navigator, + reason; + + // For unit testing + function setTestEnv(newNav, newWindow) { + nav = newNav; + win = newWindow; + } + + function getInternetExplorerVersion() { + var rv = -1; // Return value assumes failure. + if (nav.appName == 'Microsoft Internet Explorer') { + var ua = nav.userAgent; + var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); + if (re.exec(ua) != null) + rv = parseFloat(RegExp.$1); + } + + return rv; + } + + function checkIE() { + var ieVersion = getInternetExplorerVersion(), + ieNosupport = ieVersion > -1 && ieVersion < 9; + + if(ieNosupport) { + return "IE_VERSION"; + } + } + + function explicitNosupport() { + return checkIE(); + } + + function checkLocalStorage() { + var localStorage = 'localStorage' in win && win['localStorage'] !== null; + if(!localStorage) { + return "LOCALSTORAGE"; + } + } + + function checkPostMessage() { + if(!win.postMessage) { + return "POSTMESSAGE"; + } + } + + function isSupported() { + reason = checkLocalStorage() || checkPostMessage() || explicitNosupport(); + + return !reason; + } + + function getNoSupportReason() { + return reason; + } + + return { + /** + * Set the test environment. + * @method setTestEnv + */ + setTestEnv: setTestEnv, + /** + * Check whether the current browser is supported + * @method isSupported + * @returns {boolean} + */ + isSupported: isSupported, + /** + * Called after isSupported, if isSupported returns false. Gets the reason + * why browser is not supported. + * @method getNoSupportReason + * @returns {string} + */ + getNoSupportReason: getNoSupportReason + }; + +}()); + diff --git a/browserid/static/dialog/test/qunit/controllers/page_controller_unit_test.js b/browserid/static/dialog/test/qunit/controllers/page_controller_unit_test.js index fca829d5c917981be1272ff7ee2148d1f089fa5c..e2df4dbb6a159fc4aa78c73b6a2e9c5842885996 100644 --- a/browserid/static/dialog/test/qunit/controllers/page_controller_unit_test.js +++ b/browserid/static/dialog/test/qunit/controllers/page_controller_unit_test.js @@ -41,16 +41,21 @@ steal.plugins("jquery").then("/dialog/controllers/page_controller", function() { bodyTemplate = "testBodyTemplate.ejs", waitTemplate = "wait.ejs"; + function reset() { + el = $("#controller_head"); + el.find("#formWrap .contents").html(""); + el.find("#wait .contents").html(""); + el.find("#error .contents").html(""); + } + module("PageController", { setup: function() { - el = $("#page_controller"); + reset(); }, teardown: function() { - el.find("#formWrap .contents").html(""); - el.find("#wait .contents").html(""); - el.find("#error .contents").html(""); controller.destroy(); + reset(); } }); @@ -76,10 +81,11 @@ steal.plugins("jquery").then("/dialog/controllers/page_controller", function() { var html = el.find("#formWrap .contents").html(); ok(html.length, "with template specified, form text is loaded"); +/* var input = el.find("input").eq(0); ok(input.is(":focus"), "make sure the first input is focused"); - +*/ html = el.find("#wait .contents").html(); equal(html, "", "with body template specified, wait text is not loaded"); }); @@ -100,6 +106,22 @@ steal.plugins("jquery").then("/dialog/controllers/page_controller", function() { ok(html.length, "with wait template specified, wait text is loaded"); }); + test("page controller with error template renders in #error .contents", function() { + controller = el.page({ + errorTemplate: waitTemplate, + errorVars: { + title: "Test title", + message: "Test message" + } + }).controller(); + + var html = el.find("#formWrap .contents").html(); + equal(html, "", "with error template specified, form is ignored"); + + html = el.find("#error .contents").html(); + ok(html.length, "with error template specified, error text is loaded"); + }); + test("renderError renders an error message", function() { controller = el.page({ waitTemplate: waitTemplate, @@ -109,7 +131,7 @@ steal.plugins("jquery").then("/dialog/controllers/page_controller", function() { } }).controller(); - controller.renderError({ + controller.renderError("wait.ejs", { title: "error title", message: "error message" }); diff --git a/browserid/static/dialog/test/qunit/include_unit_test.js b/browserid/static/dialog/test/qunit/include_unit_test.js new file mode 100644 index 0000000000000000000000000000000000000000..815ecf82138ce58855bffdae94d8639568269a97 --- /dev/null +++ b/browserid/static/dialog/test/qunit/include_unit_test.js @@ -0,0 +1,52 @@ +/*jshint browsers:true, forin: true, laxbreak: true */ +/*global steal: true, test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: true */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla BrowserID. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +steal.plugins("jquery", "funcunit/qunit").then("/include.js", function() { + "use strict"; + + module("include.js"); + + test("navigator.id is available", function() { + equal(typeof navigator.id, "object", "navigator.id namespace is available"); + }); + + test("navigator.id.getVerifiedEmail is available", function() { + equal(typeof navigator.id.getVerifiedEmail, "function", "navigator.id.getVerifiedEmail is available"); + }); + + +}); + diff --git a/browserid/static/dialog/test/qunit/qunit.js b/browserid/static/dialog/test/qunit/qunit.js index 1733ac5548a0e89a31aa0f86527d287d7fc4d2ca..450add4ea320c385bc56072036d2781843ffaccb 100644 --- a/browserid/static/dialog/test/qunit/qunit.js +++ b/browserid/static/dialog/test/qunit/qunit.js @@ -1,4 +1,5 @@ steal("/dialog/resources/browserid.js", + "/dialog/resources/browser-support.js", "/dialog/resources/storage.js", "/dialog/resources/tooltip.js", "/dialog/resources/validation.js", @@ -12,10 +13,15 @@ steal("/dialog/resources/browserid.js", "funcunit/qunit") .views('testBodyTemplate.ejs') .views('wait.ejs') + .then("/dialog/controllers/page_controller.js") .then("browserid_unit_test") + .then("include_unit_test") .then("pages/add_email_address_test") - .then("controllers/page_controller_unit_test") + .then("resources/browser-support_unit_test") .then("resources/validation_unit_test") .then("resources/storage_unit_test") .then("resources/network_unit_test") .then("resources/user_unit_test") + .then("controllers/page_controller_unit_test") + .then("controllers/page_controller_unit_test") + diff --git a/browserid/static/dialog/test/qunit/resources/browser-support_unit_test.js b/browserid/static/dialog/test/qunit/resources/browser-support_unit_test.js new file mode 100644 index 0000000000000000000000000000000000000000..a26857f01eef422ad03c0cc49fbb4d9257957cef --- /dev/null +++ b/browserid/static/dialog/test/qunit/resources/browser-support_unit_test.js @@ -0,0 +1,102 @@ +/*jshint browsers:true, forin: true, laxbreak: true */ +/*global steal: true, test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: true */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla BrowserID. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +steal.plugins("jquery", "funcunit/qunit").then(function() { + "use strict"; + + var bid = BrowserID, + support = bid.BrowserSupport, + stubWindow, + stubNavigator; + + module("browser-support", { + setup: function() { + // Hard coded goodness for testing purposes + stubNavigator = { + appName: "Netscape", + userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:7.0.1) Gecko/20100101 Firefox/7.0.1" + }; + + stubWindow = { + localStorage: {}, + postMessage: function() {} + }; + + support.setTestEnv(stubNavigator, stubWindow); + }, + + teardown: function() { + } + }); + + test("browser without localStorage", function() { + delete stubWindow.localStorage; + + equal(support.isSupported(), false, "window.localStorage is required"); + equal(support.getNoSupportReason(), "LOCALSTORAGE", "correct reason"); + }); + + + test("browser without postMessage", function() { + delete stubWindow.postMessage; + + equal(support.isSupported(), false, "window.postMessage is required"); + equal(support.getNoSupportReason(), "POSTMESSAGE", "correct reason"); + }); + + test("Fake being IE8 - unsupported intentionally", function() { + stubNavigator.appName = "Microsoft Internet Explorer"; + stubNavigator.userAgent = "MSIE 8.0"; + + equal(support.isSupported(), false, "IE8 is not supported"); + equal(support.getNoSupportReason(), "IE_VERSION", "correct reason"); + }); + + test("Fake being IE9 - supported", function() { + stubNavigator.appName = "Microsoft Internet Explorer"; + stubNavigator.userAgent = "MSIE 9.0"; + + equal(support.isSupported(), true, "IE9 is supported"); + equal(typeof support.getNoSupportReason(), "undefined", "no reason, we are all good"); + }); + + test("Firefox 7.01 with postMessage, localStorage", function() { + equal(support.isSupported(), true, "Firefox 7.01 is supported"); + equal(typeof support.getNoSupportReason(), "undefined", "no reason, we are all good"); + }); +}); + + diff --git a/browserid/static/i/firefox_logo.png b/browserid/static/i/firefox_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c55a338081cc1e0f1d2e78fc50226695c0b37488 Binary files /dev/null and b/browserid/static/i/firefox_logo.png differ diff --git a/browserid/static/include.js b/browserid/static/include.js index 9f16d5d47007e557a9c7987df3a98f23471be0fb..cebeafc44cb225688d5ed9b36a062a33f9492e5f 100644 --- a/browserid/static/include.js +++ b/browserid/static/include.js @@ -557,55 +557,88 @@ }; })(); - function getInternetExplorerVersion() { - var rv = -1; // Return value assumes failure. - if (navigator.appName == 'Microsoft Internet Explorer') { - var ua = navigator.userAgent; - var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); - if (re.exec(ua) != null) - rv = parseFloat(RegExp.$1); + var BrowserSupport = (function() { + var win = window, + nav = navigator, + reason; + + // For unit testing + function setTestEnv(newNav, newWindow) { + nav = newNav; + win = newWindow; } - return rv; - } - - function checkIE() { - var ieVersion = getInternetExplorerVersion(), - ieNosupport = ieVersion > -1 && ieVersion < 9, - message; + function getInternetExplorerVersion() { + var rv = -1; // Return value assumes failure. + if (nav.appName == 'Microsoft Internet Explorer') { + var ua = nav.userAgent; + var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); + if (re.exec(ua) != null) + rv = parseFloat(RegExp.$1); + } - if(ieNosupport) { - message = "Unfortunately, your version of Internet Explorer is not yet supported.\n" + - 'If you are using Internet Explorer 9, turn off "Compatibility View".'; + return rv; } - return message; - } + function checkIE() { + var ieVersion = getInternetExplorerVersion(), + ieNosupport = ieVersion > -1 && ieVersion < 9; - function explicitNosupport() { - var message = checkIE(); + if(ieNosupport) { + return "IE_VERSION"; + } + } - if (message) { - message += "\nWe are working hard to bring BrowserID support to your browser!"; - alert(message); + function explicitNosupport() { + return checkIE(); } - return message; - } + function checkLocalStorage() { + var localStorage = 'localStorage' in win && win['localStorage'] !== null; + if(!localStorage) { + return "LOCALSTORAGE"; + } + } - function checkRequirements() { - var localStorage = 'localStorage' in window && window['localStorage'] !== null; - var postMessage = !!window.postMessage; - var json = true; + function checkPostMessage() { + if(!win.postMessage) { + return "POSTMESSAGE"; + } + } - var explicitNo = explicitNosupport() + function isSupported() { + reason = checkLocalStorage() || checkPostMessage() || explicitNosupport(); - if(!explicitNo && !(localStorage && postMessage && json)) { - alert("Unfortunately, your browser does not meet the minimum HTML5 support required for BrowserID."); + return !reason; } - return localStorage && postMessage && json && !(explicitNo); - } + function getNoSupportReason() { + return reason; + } + + return { + /** + * Set the test environment. + * @method setTestEnv + */ + setTestEnv: setTestEnv, + /** + * Check whether the current browser is supported + * @method isSupported + * @returns {boolean} + */ + isSupported: isSupported, + /** + * Called after isSupported, if isSupported returns false. Gets the reason + * why browser is not supported. + * @method getNoSupportReason + * @returns {string} + */ + getNoSupportReason: getNoSupportReason + }; + + }()); + // this is for calls that are non-interactive function _open_hidden_iframe(doc) { @@ -636,16 +669,20 @@ return iframe; } - function _open_window() { + function _open_window(url) { + url = url || "about:blank"; // we open the window initially blank, and only after our relay frame has // been constructed do we update the location. This is done because we // must launch the window inside a click handler, but we should wait to // start loading it until our relay iframe is instantiated and ready. // see issue #287 & #286 - return window.open( - "about:blank", + var dialog = window.open( + url, "_mozid_signin", isFennec ? undefined : "menubar=0,location=0,resizable=0,scrollbars=0,status=0,dialog=1,width=700,height=375"); + + dialog.focus(); + return dialog; } function _attach_event(element, name, listener) { @@ -684,16 +721,17 @@ // keep track of these so that we can re-use/re-focus an already open window. navigator.id.getVerifiedEmail = function(callback) { - if(!checkRequirements()) { - return; - } - if (w) { // if there is already a window open, just focus the old window. w.focus(); return; } + if (!BrowserSupport.isSupported()) { + w = _open_window(ipServer + "/unsupported_dialog"); + return; + } + var frameid = _get_relayframe_id(); var iframe = _open_relayframe("browserid_relay_" + frameid); w = _open_window(); @@ -712,8 +750,7 @@ // has a problem re-attaching new iframes with the same name. Code inside // of frames with the same name sometimes does not get run. // See https://bugzilla.mozilla.org/show_bug.cgi?id=350023 - w.location = ipServer + "/sign_in#" + frameid; - w.focus(); + w = _open_window(ipServer + "/sign_in#" + frameid); } }); @@ -721,8 +758,10 @@ chan.destroy(); chan = null; - w.close(); - w = null; + if (w) { + w.close(); + w = null; + } iframe.parentNode.removeChild(iframe); iframe = null; diff --git a/browserid/tests/page-requests-test.js b/browserid/tests/page-requests-test.js new file mode 100755 index 0000000000000000000000000000000000000000..156a0611df558716b6c3d25edbc8e8fd13c3679d --- /dev/null +++ b/browserid/tests/page-requests-test.js @@ -0,0 +1,116 @@ +#!/usr/bin/env node + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla BrowserID. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +require('./lib/test_env.js'); + +const assert = require('assert'), +http = require('http'), +vows = require('vows'), +start_stop = require('./lib/start-stop.js'), +wsapi = require('./lib/wsapi.js'); + +var suite = vows.describe('page requests'); + +// start up a pristine server +start_stop.addStartupBatches(suite); + +// This set of tests check to make sure all of the expected pages are served +// up with the correct status codes. We use Lloyd's wsapi client as our REST +// interface. + + + +// Taken from the vows page. +function assertStatus(code) { + return function (res, err) { + assert.equal(res.code, code); + }; +} + +function respondsWith(status) { + var context = { + topic: function () { + // Get the current context's name, such as "POST /" + // and split it at the space. + var req = this.context.name.split(/ +/), // ["POST", "/"] + method = req[0].toLowerCase(), // "post" + path = req[1]; // "/" + + // Perform the contextual client request, + // with the above method and path. + wsapi[method](path).call(this); + } + }; + + // Create and assign the vow to the context. + // The description is generated from the expected status code + // and the status name, from node's http module. + context['should respond with a ' + status + ' ' + + http.STATUS_CODES[status]] = assertStatus(status); + + return context; +} + +suite.addBatch({ + 'GET /': respondsWith(200), + 'GET /signup': respondsWith(200), + 'GET /forgot': respondsWith(200), + 'GET /signin': respondsWith(200), + 'GET /about': respondsWith(200), + 'GET /tos': respondsWith(200), + 'GET /privacy': respondsWith(200), + 'GET /verify_email_address': respondsWith(200), + 'GET /add_email_address': respondsWith(200), + 'GET /pk': respondsWith(200), + 'GET /vepbundle': respondsWith(200), + 'GET /signin': respondsWith(200), + 'GET /unsupported_dialog': respondsWith(200), + 'GET /developers': respondsWith(200), + 'GET /manage': respondsWith(302), + 'GET /users': respondsWith(302), + 'GET /users/': respondsWith(302), + 'GET /primaries': respondsWith(302), + 'GET /primaries/': respondsWith(302), + 'GET /developers': respondsWith(302) +}); + +// shut the server down and cleanup +start_stop.addShutdownBatches(suite); + +// run or export the suite. +if (process.argv[1] === __filename) suite.run(); +else suite.export(module); diff --git a/browserid/views/dialog.ejs b/browserid/views/dialog.ejs index bf766dfdae7a176d976d3fca573e38a5638efab0..d2b634a26338b2e79f40b510c8351d1aba641e22 100644 --- a/browserid/views/dialog.ejs +++ b/browserid/views/dialog.ejs @@ -1,90 +1,35 @@ -<!doctype html> -<html> -<head> - <meta charset="utf-8"> - <meta name="viewport" content="initial-scale=1.0; maximum-scale=1.0; width=device-width;"> - <!--[if lt IE 9]> - <script type="text/javascript" src="/js/html5shim.js"></script> - <![endif]--> - <% if (production) { %> - <link href="/dialog/css/production.min.css" rel="stylesheet" type="text/css"> - <% } else { %> - <link href="/dialog/css/popup.css" rel="stylesheet" type="text/css"> - <link href="/dialog/css/m.css" rel="stylesheet" type="text/css"> - <% } %> - <link href="https://fonts.googleapis.com/css?family=Droid+Serif:400,400italic,700,700italic" rel="stylesheet" type="text/css"> - <title>Browser ID</title> -</head> - <body class="waiting"> - <div id="wrapper"> - <header id="header" class="cf"> - <ul> - <li><a class="home" target="_blank" href="/"></a></li> - </ul> - </header> - - <div id="content"> - <section id="formWrap"> - <form novalidate> - <div id="favicon"> - <div class="vertical"> - <strong id="sitename"></strong> - </div> - </div> - - <div id="signIn"> - <div class="arrow"></div> - <div class="table"> - <div class="vertical contents"> - </div> - </div> - </div> - </form> - </section> - - - <section id="wait"> - <div class="table"> - <div class="vertical contents"> - <h2>Communicating with server</h2> - <p>Just a moment while we talk with the server.</p> - </div> - </div> - </section> - - - <section id="error"> - <div class="table"> - <div class="vertical contents"> - </div> - </div> - </section> - </div> - - <footer> - <ul class="cf"> - <li>By <a href="http://mozillalabs.com">Mozilla Labs</a></li> - - <li>—</li> - <li><a href="#">Privacy</a></li> - <li><a href="#">TOS</a></li> - </ul> + <section id="formWrap"> + <form novalidate> + <div id="favicon"> + <div class="vertical"> + <strong id="sitename"></strong> + </div> + </div> - <div class="learn"> - BrowserID is the fast and secure way to sign in — <a target="_blank" href="/about">learn more</a> + <div id="signIn"> + <div class="arrow"></div> + <div class="table"> + <div class="vertical contents"> </div> + </div> + </div> + </form> + </section> + - <a class="help" href="https://support.mozilla.com/en-US/kb/what-browserid-and-how-does-it-work" target="_blank">need help?</a> - </footer> + <section id="wait"> + <div class="table"> + <div class="vertical contents"> + <h2>Communicating with server</h2> + <p>Just a moment while we talk with the server.</p> + </div> + </div> + </section> - </div> - <script type="text/html" id="templateTooltip"> - <div class="tooltip"> - {{ contents }} + <section id="error"> + <div class="table"> + <div class="vertical contents"> + </div> </div> - </script> - <script type="text/javascript" src="/vepbundle"></script> - <script type="text/javascript" src="steal/steal<%= production ? '.production' : '' %>.js?dialog"></script> - </body> -</html> + </section> diff --git a/browserid/views/dialog_layout.ejs b/browserid/views/dialog_layout.ejs new file mode 100644 index 0000000000000000000000000000000000000000..13dd4d98f92ddc60880e28d7105492ba153837ae --- /dev/null +++ b/browserid/views/dialog_layout.ejs @@ -0,0 +1,58 @@ +<!doctype html> +<html> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="initial-scale=1.0; maximum-scale=1.0; width=device-width;"> + <!--[if lt IE 9]> + <script type="text/javascript" src="/js/html5shim.js"></script> + <![endif]--> + <% if (production) { %> + <link href="/dialog/css/production.min.css" rel="stylesheet" type="text/css"> + <% } else { %> + <link href="/dialog/css/popup.css" rel="stylesheet" type="text/css"> + <link href="/dialog/css/m.css" rel="stylesheet" type="text/css"> + <% } %> + <link href="https://fonts.googleapis.com/css?family=Droid+Serif:400,400italic,700,700italic" rel="stylesheet" type="text/css"> + <title>Browser ID</title> +</head> + <body class="waiting"> + <div id="wrapper"> + <header id="header" class="cf"> + <ul> + <li><a class="home" target="_blank" href="/"></a></li> + </ul> + </header> + + <div id="content"> + <%- body %> + </div> + + <footer> + <ul class="cf"> + <li>By <a href="http://mozillalabs.com">Mozilla Labs</a></li> + + <li>—</li> + <li><a href="#">Privacy</a></li> + <li><a href="#">TOS</a></li> + </ul> + + <div class="learn"> + BrowserID is the fast and secure way to sign in — <a target="_blank" href="/about">learn more</a> + </div> + + <a class="help" href="https://support.mozilla.com/en-US/kb/what-browserid-and-how-does-it-work" target="_blank">need help?</a> + </footer> + + </div> + + <% if (useJavascript !== false) { %> + <script type="text/html" id="templateTooltip"> + <div class="tooltip"> + {{ contents }} + </div> + </script> + <script type="text/javascript" src="/vepbundle"></script> + <script type="text/javascript" src="steal/steal<%= production ? '.production' : '' %>.js?dialog"></script> + <% } %> + </body> +</html> diff --git a/browserid/views/unsupported_dialog.ejs b/browserid/views/unsupported_dialog.ejs new file mode 100644 index 0000000000000000000000000000000000000000..af410a76e1883f574edd61aba028f7e06ae7ae72 --- /dev/null +++ b/browserid/views/unsupported_dialog.ejs @@ -0,0 +1,28 @@ + <section id="error" style="display: block"> + <div class="table"> + <div class="vertical contents"> + <div id="reason"> + We're sorry, but currently your browser isn't supported. + </div> + + <div id="alternative"> + + <div id="borderbox"> + <a href="http://getfirefox.com" target="_blank"> + <img src="/i/firefox_logo.png" width="250" height="88" alt="Firefox logo" /> + </a> + + <p> + BrowserID requires <a href="http://getfirefox.com" target="_blank" title="Get Firefox">Firefox</a> + </p> + + <p class="lighter"> + or other <a href="" target="_blank">modern browser.</a> + </p> + </div> + + </div> + + </div> + </div> + </section>