├── addons ├── sessions │ ├── test │ │ ├── test-window_manager.js │ │ ├── test-faker.js │ │ ├── test-main.js │ │ ├── test-login_chrome.js │ │ ├── test-cookie_monster.js │ │ └── test-helpers.js │ ├── data │ │ ├── key.png │ │ ├── sessions.js │ │ ├── page_interaction.js │ │ ├── styles │ │ │ └── identity-session.css │ │ ├── faker_twitter.js │ │ ├── faker_google.js │ │ ├── faker_wikipedia.js │ │ └── sessions.html │ ├── identity_sessions.xpi │ ├── doc │ │ └── main.md │ ├── package.json │ ├── lib │ │ ├── faker.js │ │ ├── fakers.js │ │ ├── cookie_monster.js │ │ ├── window_manager.js │ │ ├── login_chrome.js │ │ ├── helpers.js │ │ └── main.js │ └── README.md └── browserid │ ├── doc │ └── main.md │ ├── test │ └── test-main.js │ ├── package.json │ ├── data │ ├── injector.js │ └── channel.js │ ├── README.md │ └── lib │ ├── window_listener.js │ └── main.js ├── ORGANIZATION.md ├── LICENSE.md └── README.md /addons/sessions/test/test-window_manager.js: -------------------------------------------------------------------------------- 1 | const {WindowManager} = require("window_manager"); 2 | 3 | -------------------------------------------------------------------------------- /addons/sessions/data/key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shane-tomlinson/browserid_addon/master/addons/sessions/data/key.png -------------------------------------------------------------------------------- /addons/sessions/identity_sessions.xpi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shane-tomlinson/browserid_addon/master/addons/sessions/identity_sessions.xpi -------------------------------------------------------------------------------- /addons/sessions/doc/main.md: -------------------------------------------------------------------------------- 1 | The main module is a program that creates a widget. When a user clicks on 2 | the widget, the program loads the mozilla.org website in a new tab. 3 | -------------------------------------------------------------------------------- /addons/browserid/doc/main.md: -------------------------------------------------------------------------------- 1 | The main module is a program that creates a widget. When a user clicks on 2 | the widget, the program loads the mozilla.org website in a new tab. 3 | -------------------------------------------------------------------------------- /addons/browserid/test/test-main.js: -------------------------------------------------------------------------------- 1 | const main = require("main"); 2 | 3 | 4 | exports.test_id = function(test) { 5 | test.assert(require("self").id.length > 0); 6 | }; 7 | 8 | -------------------------------------------------------------------------------- /ORGANIZATION.md: -------------------------------------------------------------------------------- 1 | There are currently two addons in this repository, both are found in addons. 2 | 3 | addons/ 4 | browserid/ - BrowserID addon - bring BrowserID login into the browser. 5 | sessions/ - Implement the experimental Sessions API. 6 | 7 | -------------------------------------------------------------------------------- /addons/sessions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sessions", 3 | "license": "MPL 1.1/GPL 2.0/LGPL 2.1", 4 | "author": "Mozilla Labs", 5 | "version": "0.1", 6 | "fullName": "Sessions API", 7 | "id": "jid1-FSOkwUIAZAuMDA", 8 | "description": "User session and status in the browser" 9 | } 10 | -------------------------------------------------------------------------------- /addons/browserid/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "browserid_addon", 3 | "license": "MPL 1.1/GPL 2.0/LGPL 2.1", 4 | "author": "Mozilla Labs", 5 | "version": "0.1", 6 | "fullName": "browserid_addon", 7 | "id": "browserid_addon@mozillalabs.com", 8 | "description": "Log in to browserid.org directly from Firefox!" 9 | } 10 | -------------------------------------------------------------------------------- /addons/sessions/lib/faker.js: -------------------------------------------------------------------------------- 1 | const {PageMod} = require("page-mod"); 2 | const {data} = require("self"); 3 | 4 | exports.Faker = function(domain, script) { 5 | var domains = domain.indexOf ? domain : [domain]; 6 | 7 | return PageMod({ 8 | include: domains, 9 | contentScriptWhen: "end", 10 | contentScriptFile: data.url(script) 11 | }); 12 | }; 13 | -------------------------------------------------------------------------------- /addons/sessions/lib/fakers.js: -------------------------------------------------------------------------------- 1 | const {Faker} = require("./faker"); 2 | 3 | exports.Fakers = function() { 4 | let fakerGoogle = Faker(["*.google.com", "http://google.com/*", "https://google.com/*"], "faker_google.js"); 5 | let fakerWikipedia = Faker(["*.wikipedia.org", "http://wikipedia.org/*", "https://wikipedia.org/*"], "faker_wikipedia.js"); 6 | let fakerWikipedia = Faker(["*.twitter.com", "http://twitter.com/*", "https://twitter.com/*"], "faker_twitter.js"); 7 | }; 8 | 9 | 10 | -------------------------------------------------------------------------------- /addons/browserid/data/injector.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | let assertionCallback; 3 | 4 | self.port.on("assertionReady", function(payload) { 5 | if(assertionCallback) { 6 | console.log("The assertion is finally ready! " + payload.assertion); 7 | assertionCallback(payload.assertion); 8 | } 9 | }); 10 | 11 | var nav = unsafeWindow.navigator; 12 | nav.id = nav.id || {}; 13 | nav.id.getVerifiedEmail = function(callback) { 14 | assertionCallback = callback; 15 | self.port.emit("getAssertion", { 16 | location: unsafeWindow.location.toString() 17 | }); 18 | }; 19 | 20 | }()); 21 | -------------------------------------------------------------------------------- /addons/sessions/test/test-faker.js: -------------------------------------------------------------------------------- 1 | const {Faker} = require("faker"); 2 | const {Helpers} = require("helpers"); 3 | 4 | var faker; 5 | 6 | exports['can create with single page'] = function(test) { 7 | faker = Faker("*.google.com", "faker_google.js"); 8 | 9 | test.assertNotUndefined(faker, 'a faker has been created'); 10 | 11 | var include = faker.include; 12 | test.assertEqual(1, include.length, 'includes has 1 item'); 13 | }; 14 | 15 | exports['can create with multiple matches'] = function(test) { 16 | faker = Faker(["*.google.com", "http://google.com/*"], "faker_google.js"); 17 | 18 | var include = faker.include; 19 | test.assertEqual(2, include.length, 'includes has 2 item'); 20 | }; 21 | 22 | -------------------------------------------------------------------------------- /addons/sessions/data/sessions.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | self.port.on("sessions.clear", onSessionsClear); 4 | self.port.on("sessions.set", onSessionsSet); 5 | 6 | var username = document.getElementById("username"); 7 | var body = document.querySelector("body"); 8 | function onSessionsClear() { 9 | username.innerHTML = ''; 10 | }; 11 | 12 | function onSessionsSet(sessions) { 13 | username.innerHTML = sessions[0].email; 14 | self.port.emit("sessions.setheight", body.clientHeight); 15 | } 16 | 17 | var logout = document.getElementById("logout"); 18 | logout.addEventListener("click", function() { 19 | self.port.emit("sessions.logout"); 20 | }, false); 21 | }()); 22 | -------------------------------------------------------------------------------- /addons/browserid/README.md: -------------------------------------------------------------------------------- 1 | #Sessions API Firefox addon 2 | 3 | ##To run: 4 | 1. Get the addon-sdk at https://github.com/mozilla/addon-sdk/ 5 | 2. Start up the addon-sdk environment by cd into the addon-sdk, then 'source 6 | bin/activate'. 7 | 3. from the addon directory, type cfx run. 8 | 4. Go to myfavoritebeer.org or any other site that supports BrowserID and log in! 9 | 10 | ##Running Tests: 11 | Basic tests are found in the test directory. These can be run using 'cfx test'. 12 | 13 | ##Reporting bugs: 14 | 15 | Bug reports can be filed using the "Issues" in GitHub under the browserid_addon repo. 16 | 17 | ##Author: 18 | * Shane Tomlinson @ Mozilla Labs. 19 | * stomlinson@mozilla.com 20 | * @shane_tomlinson 21 | 22 | ##Licencing: 23 | Tri licensed under any of the MPL, GPL, or LGPL. 24 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ***** BEGIN LICENSE BLOCK ***** 2 | Version: MPL 1.1/LGPL 2.1/GPL 2.0 3 | 4 | The contents of this file are subject to the Mozilla Public License Version 5 | 1.1 (the "License"); you may not use this file except in compliance with 6 | ... 7 | for the specific language governing rights and limitations under the 8 | License. 9 | 10 | The Original Code is BrowserID-addon. 11 | 12 | The Initial Developer of the Original Code is 13 | Shane Tomlinson 14 | 15 | Portions created by the Initial Developer are Copyright (C) 2011 16 | the Initial Developer. All Rights Reserved. 17 | 18 | Contributor(s): 19 | 20 | Alternatively, the contents of this file may be used under the terms of 21 | either of the GNU General Public License Version 2 or later (the "GPL"), 22 | ... 23 | the terms of any one of the MPL, the GPL or the LGPL. 24 | 25 | ***** END LICENSE BLOCK ***** -------------------------------------------------------------------------------- /addons/sessions/test/test-main.js: -------------------------------------------------------------------------------- 1 | const main = require("main"); 2 | 3 | exports.test_test_run = function(test) { 4 | test.pass("Unit test running!"); 5 | }; 6 | 7 | exports.test_id = function(test) { 8 | test.assert(require("self").id.length > 0); 9 | }; 10 | 11 | exports.test_url = function(test) { 12 | require("request").Request({ 13 | url: "http://www.mozilla.org/", 14 | onComplete: function(response) { 15 | test.assertEqual(response.statusText, "OK"); 16 | test.done(); 17 | } 18 | }).get(); 19 | test.waitUntilDone(20000); 20 | }; 21 | 22 | exports.test_open_tab = function(test) { 23 | const tabs = require("tabs"); 24 | tabs.open({ 25 | url: "http://www.mozilla.org/", 26 | onReady: function(tab) { 27 | test.assertEqual(tab.url, "http://www.mozilla.org/"); 28 | test.done(); 29 | } 30 | }); 31 | test.waitUntilDone(20000); 32 | }; 33 | -------------------------------------------------------------------------------- /addons/browserid/data/channel.js: -------------------------------------------------------------------------------- 1 | 2 | let nav = unsafeWindow.navigator; 3 | 4 | nav.id = nav.id || {}; 5 | nav.id.channel = nav.id.channel || {}; 6 | 7 | let browseridController; 8 | 9 | nav.id.channel.registerController = function(controller) { 10 | browseridController = controller; 11 | self.port.emit("controllerReady"); 12 | }; 13 | 14 | self.port.on("getVerifiedEmail", function(payload) { 15 | browseridController.getVerifiedEmail.call( browseridController, 16 | payload.host, 17 | unsafeWindow.onGetAssertionSuccess, 18 | unsafeWindow.onGetAssertionFailure 19 | ); 20 | }); 21 | 22 | unsafeWindow.onGetAssertionSuccess = function(assertion) { 23 | console.log("getVerifiedEmail assertion: " + assertion); 24 | self.port.emit("assertionReady", { 25 | assertion: assertion 26 | }); 27 | } 28 | 29 | unsafeWindow.onGetAssertionFailure = function() { 30 | console.log("failure getting verified email"); 31 | } 32 | 33 | -------------------------------------------------------------------------------- /addons/sessions/README.md: -------------------------------------------------------------------------------- 1 | #sessions Firefox addon 2 | 3 | This is the sessions addon. The purpose of this addon is to allow 4 | sites that support the sessions protocol to display the status of their session 5 | in the browser's URL bar. This includes logging in, checking current status, 6 | changing accounts, and logging out. 7 | 8 | ##To run: 9 | 1. Get the addon-sdk at https://github.com/mozilla/addon-sdk/ 10 | 2. Start up the addon-sdk environment by cd into the addon-sdk, then 'source 11 | bin/activate'. 12 | 3. from the sessions directory, type cfx run. 13 | 4. Go to google.com, wikipedia.org, twitter.com or myfavoritebeer.com to see 14 | the sessions in action. 15 | 16 | 17 | ##Running Tests: 18 | Basic tests are found in the test directory. These can be run using 'cfx test'. 19 | 20 | ##Reporting bugs: 21 | 22 | Bug reports can be filed using the "Issues" in GitHub under the browserid_addon repo. 23 | 24 | ##Author: 25 | * Shane Tomlinson @ Mozilla Labs. 26 | * stomlinson@mozilla.com 27 | * @shane_tomlinson 28 | 29 | ##Licencing: 30 | Tri licensed under any of the MPL, GPL, or LGPL. 31 | -------------------------------------------------------------------------------- /addons/sessions/data/page_interaction.js: -------------------------------------------------------------------------------- 1 | 2 | (function() { 3 | let id = unsafeWindow.navigator.id = unsafeWindow.navigator.id || {}; 4 | let sessions; 5 | 6 | Object.defineProperty(id, "sessions", { 7 | enumerable: true, 8 | get: function() { return sessions; }, 9 | set: function(newSessions) { 10 | sessions = newSessions; 11 | self.port.emit("sessions.set", { 12 | sessions: sessions, 13 | host: unsafeWindow.document.location.host 14 | }); 15 | return true; 16 | } 17 | }); 18 | 19 | self.port.on("emitevent.login", emitEvent.bind(undefined, "login")); 20 | self.port.on("emitevent.logout", emitEvent.bind(undefined, "logout")); 21 | 22 | function emitEvent(name) { 23 | console.log('emitting event: ' + name); 24 | var doc = unsafeWindow.document; 25 | var evt = doc.createEvent("UIEvents"); 26 | evt.initUIEvent(name, false, false, unsafeWindow, 1); 27 | doc.dispatchEvent(evt); 28 | } 29 | 30 | if(unsafeWindow.top === unsafeWindow.self) { 31 | self.port.emit("sessions.opentab"); 32 | } 33 | }()); 34 | 35 | -------------------------------------------------------------------------------- /addons/sessions/data/styles/identity-session.css: -------------------------------------------------------------------------------- 1 | @namespace xul url(http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul); 2 | 3 | #identity-sessions-box { 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | #identity-session-signin, #identity-session-userinfo { 9 | margin: 1px 0 1px 1px; 10 | padding: 0 5px; 11 | border: 1px solid #555; 12 | border-radius: 2px 0 0 2px; 13 | border-color: #C2C2C2 #809ec7 #8b8b8b #979797; 14 | background: -moz-linear-gradient(center top, #ffffff 0%, #cccccc 100%); 15 | color: #555; 16 | font-size: .9em; 17 | } 18 | 19 | #identity-session-signin:hover, #identity-session-userinfo:hover { 20 | background: -moz-linear-gradient(center top, #ffffff 40%, #cccccc 100%); 21 | } 22 | 23 | #identity-session-userinfo { 24 | } 25 | 26 | #identity-session-icon { 27 | display: none; 28 | } 29 | 30 | #identity-session-site { 31 | margin: 1px 1px 1px 0; 32 | padding: 0 5px; 33 | background: -moz-linear-gradient(center top, #9ABEEA 0%, #4f80bb 100%); 34 | border: 1px solid #3f4ed9; 35 | border-color: #5d85b8 #5d85b8 #5d85b8 #cccccc; 36 | border-radius: 0 2px 2px 0; 37 | font-size: .9em; 38 | color: #fff; 39 | } 40 | -------------------------------------------------------------------------------- /addons/sessions/data/faker_twitter.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | 4 | let doc = unsafeWindow.document; 5 | 6 | if(unsafeWindow.top !== unsafeWindow.self) { 7 | // IFRAMEs will not have the same content, so we get out of here. 8 | return; 9 | } 10 | let loginEls = doc.querySelectorAll('#screen-name span'); 11 | 12 | let sessions = []; 13 | 14 | if(loginEls && loginEls.length) { 15 | let count = loginEls.length; 16 | 17 | for(let index = 0, loginEl; index < count; ++index) { 18 | loginEl = loginEls[index]; 19 | let email = loginEl.innerHTML; 20 | if(email) { 21 | sessions.push({ 22 | email: email, 23 | bound_to: { 24 | type: "cookie", 25 | name: "SID" 26 | } 27 | }); 28 | } 29 | } 30 | } 31 | 32 | unsafeWindow.navigator.id.sessions = sessions; 33 | 34 | doc.addEventListener("login", function(event) { 35 | doc.location.href = "https://www.twitter.com/login"; 36 | },false); 37 | 38 | doc.addEventListener("logout", function(event) { 39 | unsafeWindow.navigator.id.sessions = []; 40 | doc.location.href = "http://www.twitter.com/logout"; 41 | },false); 42 | }()); 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BrowserID and Sessions API Firefox Addons 2 | 3 | Contained in this project are two addons, one to implement BrowserID as a 4 | Firefox addon, the second implementing the still experimental Sessions API. 5 | 6 | ## BrowserID Addon 7 | This is an addon that implements BrowserID as part of the browser chrome 8 | instead of opening a popup window to log in. 9 | 10 | Code for this is found in addons/browserid 11 | 12 | ## Sessions API Addon 13 | The Sessions API is an experimental API designed to bring session information 14 | into the browser chrome. The goal is to train the user to relate their 15 | login information with a particular site as well as to look to their URL 16 | bar for all of their session related needs. When visiting a supporting site, 17 | a small box to the left of the URL bar will allow a user to login, logout, and 18 | view their session information. 19 | 20 | Code for this is found in addons/sessions 21 | 22 | ## Developer Info: 23 | 24 | * Mozilla Labs 25 | * @mozlabs 26 | * https://mozillalabs.com 27 | * http://identity.mozilla.com 28 | * #identity channel in irc.mozilla.org 29 | 30 | ## Background Info 31 | * https://wiki.mozilla.org/Identity 32 | * https://wiki.mozilla.org/Identity/Verified_Email_Protocol 33 | 34 | Primary Developer: 35 | Shane Tomlinson - stomlinson@mozilla.com, @shane_tomlinson 36 | 37 | 38 | ## License Info: 39 | MPL 1.1, GPL 2.0, LGPL 2.1 40 | -------------------------------------------------------------------------------- /addons/sessions/test/test-login_chrome.js: -------------------------------------------------------------------------------- 1 | const {Cc, Ci} = require("chrome"); 2 | const {LoginManager} = require("login_chrome"); 3 | const {Helpers} = require("helpers"); 4 | 5 | let widget; 6 | exports.setup = function() { 7 | if(!widget) { 8 | let doc = Helpers.chrome.getDocument(); 9 | widget = LoginManager({ 10 | document: doc 11 | }); 12 | } 13 | }; 14 | 15 | exports['test creatable'] = function(test) { 16 | test.assertNotUndefined(widget, 'widget has been created'); 17 | }; 18 | 19 | exports['adds elements'] = function(test) { 20 | let box = Helpers.chrome.getElementById('identity-session-box'); 21 | test.assertEqual(true, !!box, 'box has been added'); 22 | }; 23 | 24 | exports['login emitted when signIn button pressed'] = function(test) { 25 | let success = false; 26 | widget.on("login", function() { 27 | success = true; 28 | }); 29 | 30 | let signIn = Helpers.chrome.getElementById('identity-session-signin'); 31 | let evt = Helpers.chrome.simulateDOMEvent(signIn, "MouseEvents", "click"); 32 | 33 | test.assertEqual(success, true, "login event fired on click"); 34 | }; 35 | 36 | exports['userinfo emitted when userinfo button pressed'] = function(test) { 37 | let success = false; 38 | widget.on("userinfo", function() { 39 | success = true; 40 | }); 41 | 42 | let signIn = Helpers.chrome.getElementById('identity-session-userinfo'); 43 | let evt = Helpers.chrome.simulateDOMEvent(signIn, "MouseEvents", "click"); 44 | 45 | test.assertEqual(success, true, "userinfo event fired on click"); 46 | }; 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /addons/browserid/lib/window_listener.js: -------------------------------------------------------------------------------- 1 | const {Cc, Ci, Cm, Cu, Cr, components} = require("chrome"); 2 | 3 | var tmp = {}; 4 | Cu.import("resource://gre/modules/Services.jsm", tmp); 5 | var {Services} = tmp; 6 | 7 | const {EventEmitter} = require("events"); 8 | 9 | const WindowListener = EventEmitter.compose({ 10 | constructor: function WindowListener() { 11 | // not doing a whole lot 12 | }, 13 | init: function() { 14 | createAuthForOpenWindows.call(this); 15 | listenForNewWindows.call(this); 16 | } 17 | }); 18 | 19 | exports.WindowListener = WindowListener; 20 | 21 | function createAuthForOpenWindows() { 22 | let iter = Cc["@mozilla.org/appshell/window-mediator;1"] 23 | .getService(Ci.nsIWindowMediator) 24 | .getEnumerator("navigator:browser"); 25 | 26 | while(iter.hasMoreElements()) { 27 | let window = iter.getNext().QueryInterface(Ci.nsIDOMWindow); 28 | this._emit("windowopen", window); 29 | } 30 | } 31 | 32 | function listenForNewWindows() { 33 | Services.ww.registerNotification(winWatcher.bind(this)); 34 | 35 | function winWatcher(subject, topic) { 36 | if(topic === "domwindowopened") { 37 | subject.addEventListener("load", onLoad.bind(this, subject), false); 38 | } 39 | } 40 | 41 | function onLoad(subject) { 42 | subject.removeEventListener("load", arguments.callee, false); 43 | 44 | let doc = subject.document.documentElement; 45 | if(doc.getAttribute("windowtype") === "navigator:browser") { 46 | this._emit("windowopen", subject); 47 | } 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /addons/sessions/data/faker_google.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | 4 | let doc = unsafeWindow.document; 5 | 6 | if(unsafeWindow.top !== unsafeWindow.self) { 7 | // console.log('this is an iframe, get outta here'); 8 | // IFRAMEs will not have the same content, so we get out of here. 9 | return; 10 | } 11 | let loginEls = doc.querySelectorAll('.gbps2'); 12 | 13 | //console.log('length: ' + loginEls.length); 14 | let sessions = []; 15 | 16 | if(loginEls && loginEls.length) { 17 | let count = loginEls.length; 18 | 19 | for(let index = 0, loginEl; index < count; ++index) { 20 | loginEl = loginEls[index]; 21 | let email = loginEl.innerHTML; 22 | if(email) { 23 | sessions.push({ 24 | email: email, 25 | bound_to: { 26 | type: "cookie", 27 | name: "SID" 28 | } 29 | }); 30 | } 31 | } 32 | } 33 | 34 | //console.log("sessions: " + sessions); 35 | unsafeWindow.navigator.id.sessions = sessions; 36 | 37 | doc.addEventListener("login", function(event) { 38 | // console.log("going to login page"); 39 | doc.location.href = "https://www.google.com/accounts/ServiceLogin?hl=en&continue=" + doc.location.href; 40 | },false); 41 | 42 | doc.addEventListener("logout", function(event) { 43 | //console.log("clearing sessions, logout"); 44 | unsafeWindow.navigator.id.sessions = []; 45 | doc.location.href = "http://www.google.com/accounts/Logout?continue=" + doc.location.href; 46 | },false); 47 | }()); 48 | -------------------------------------------------------------------------------- /addons/sessions/data/faker_wikipedia.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | 4 | let doc = unsafeWindow.document; 5 | 6 | if(unsafeWindow.top !== unsafeWindow.self) { 7 | // console.log('this is an iframe, get outta here'); 8 | // IFRAMEs will not have the same content, so we get out of here. 9 | return; 10 | } 11 | let loginEls = doc.querySelectorAll('#pt-userpage a'); 12 | 13 | // set this undefined initially to fake that we have no JS enabled 14 | // which could happen if we are viewing an image without the rest of 15 | // the page content. 16 | let sessions = []; 17 | 18 | if(loginEls && loginEls.length) { 19 | let count = loginEls.length; 20 | 21 | for(let index = 0, loginEl; index < count; ++index) { 22 | loginEl = loginEls[index]; 23 | let email = loginEl.innerHTML; 24 | if(email) { 25 | sessions.push({ 26 | email: email, 27 | bound_to: { 28 | type: "cookie", 29 | name: "centralauth_User" 30 | } 31 | }); 32 | } 33 | } 34 | } 35 | 36 | unsafeWindow.navigator.id.sessions = sessions; 37 | 38 | doc.addEventListener("login", function(event) { 39 | doc.location.href = "http://en.wikipedia.org/w/index.php?title=Special:UserLogin&returnto=Main_Page&campaign=ACP1"; 40 | },false); 41 | 42 | doc.addEventListener("logout", function(event) { 43 | unsafeWindow.navigator.id.sessions = []; 44 | doc.location.href = "http://en.wikipedia.org/w/index.php?title=Special:UserLogout&returnto=Main_Page"; 45 | },false); 46 | }()); 47 | -------------------------------------------------------------------------------- /addons/sessions/lib/cookie_monster.js: -------------------------------------------------------------------------------- 1 | const {Cc, Ci, components} = require("chrome"); 2 | const obSvc = require("observer-service"); 3 | 4 | const CookieMonster = function() { 5 | this.handlers = {}; 6 | obSvc.add("cookie-changed", onCookieChange, this); 7 | }; 8 | 9 | CookieMonster.prototype = { 10 | watch: function(host, name, callback) { 11 | var handlers = this.getHandlers(host, name); 12 | handlers.push(callback); 13 | }, 14 | 15 | unregisterListener: function(host, name, callback) { 16 | var handlers = this.getHandlers(host, name); 17 | if(handlers) { 18 | handlers.forEach(function(handler, index) { 19 | if(handler === callback) { 20 | handlers.splice(index, 1); 21 | } 22 | }); 23 | } 24 | }, 25 | 26 | simulate: function(host, name, value) { 27 | callHandlers.call(this, host, name, value); 28 | }, 29 | 30 | getHandlers: function(host, name) { 31 | var handlers = this.handlers[name + host] = this.handlers[name + host] || []; 32 | return handlers; 33 | } 34 | 35 | }; 36 | 37 | 38 | function callHandlers(host, name, value) { 39 | var handlers = this.getHandlers(host, name); 40 | if(handlers) { 41 | handlers.forEach(function(handler) { 42 | handler(value); 43 | }); 44 | } 45 | } 46 | 47 | function onCookieChange(subject, data) { 48 | var cookieInfo = subject.QueryInterface(Ci.nsICookie2); 49 | var name = cookieInfo.name; 50 | var host = cookieInfo.host; 51 | 52 | //console.log("cookie change: " + host + ':' + name + " action: " + data); 53 | callHandlers.call(this, host, name, cookieInfo.value); 54 | } 55 | 56 | exports.CookieMonster = CookieMonster; 57 | 58 | -------------------------------------------------------------------------------- /addons/sessions/data/sessions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sessions Panel 5 | 52 | 53 | 54 |
55 | 56 |
57 |

You are logged in as:

58 |
59 | 60 |
61 |
62 | 63 | 64 | -------------------------------------------------------------------------------- /addons/sessions/lib/window_manager.js: -------------------------------------------------------------------------------- 1 | const {WindowTracker} = require("window-utils"); 2 | const {LoginManager} = require("./login_chrome"); 3 | const {Helpers} = require("./helpers"); 4 | const self = require("self"); 5 | 6 | const logins = []; 7 | logins.login = function(host) { 8 | this.forEach(function(login) { 9 | login.login(host); 10 | }); 11 | }; 12 | 13 | logins.loggedIn = function(username, host) { 14 | this.forEach(function(login) { 15 | login.loggedIn(username, host); 16 | }); 17 | }; 18 | 19 | logins.none = function() { 20 | this.forEach(function(login) { 21 | login.none(); 22 | }); 23 | }; 24 | 25 | let login, logout, sessions; 26 | var delegate = { 27 | onTrack: function(window) { 28 | console.log("window track"); 29 | let loginManager = LoginManager({ 30 | window: window, 31 | document: window.document 32 | }); 33 | loginManager.none(); 34 | loginManager.on("login", function() { 35 | login(); 36 | }); 37 | loginManager.on("userinfo", function() { 38 | sessions(); 39 | }); 40 | logins.push(loginManager); 41 | 42 | let uri = self.data.url("styles/identity-session.css"); 43 | Helpers.chrome.loadStylesheet(uri, window.document); 44 | }, 45 | 46 | onUntrack: function(window) { 47 | console.log("Untracking a window: " + window.location); 48 | } 49 | 50 | }; 51 | 52 | var tracker = new WindowTracker(delegate); 53 | 54 | exports.WindowManager = function(loginCallback, logoutCallback, sessionCallback) { 55 | login = loginCallback; 56 | logout = logoutCallback; 57 | sessions = sessionCallback; 58 | 59 | return { 60 | none: logins.none.bind(logins), 61 | login: logins.login.bind(logins), 62 | loggedIn: logins.loggedIn.bind(logins) 63 | }; 64 | }; 65 | 66 | 67 | -------------------------------------------------------------------------------- /addons/sessions/test/test-cookie_monster.js: -------------------------------------------------------------------------------- 1 | const {CookieMonster} = require("cookie_monster"); 2 | const obSvc = require("observer-service"); 3 | const {Cc, Ci} = require("chrome"); 4 | 5 | var monster; 6 | 7 | exports.setup = function() { 8 | monster = new CookieMonster(); 9 | }; 10 | 11 | exports["can create"] = function(test) { 12 | test.assertEqual(true, monster instanceof CookieMonster, "we have a monster on our hands"); 13 | }; 14 | 15 | exports["can watch"] = function(test) { 16 | monster.watch("mozilla", "test-cookie", function(value) { 17 | test.assertEqual("test-value", value, "we got the right value"); 18 | test.done(); 19 | }); 20 | 21 | monster.simulate("mozilla", "test-cookie", "test-value"); 22 | test.waitUntilDone(); 23 | }; 24 | 25 | exports["can unregister"] = function(test) { 26 | var success = true; 27 | var callback = function() { 28 | success = false; 29 | }; 30 | 31 | monster.watch("mozilla", "test-cookie", callback); 32 | monster.unregisterListener("mozilla", "test-cookie", callback); 33 | monster.simulate("mozilla", "test-cookie", "test-value"); 34 | 35 | test.assertEqual(true, success, "unregister failure, callback should not have been called"); 36 | }; 37 | 38 | 39 | exports["changes with cookie"] = function(test) { 40 | var success=false; 41 | 42 | monster.watch("Mozilla", "monster", function(value) { 43 | success = true; 44 | }); 45 | 46 | 47 | obSvc.notify("cookie-changed", { 48 | QueryInterface:function() { 49 | return { 50 | name: 'monster', 51 | host: 'Mozilla' 52 | }; 53 | } 54 | }, "added"); 55 | 56 | test.assertEqual(success, true, "our cookie stuff works!"); 57 | }; 58 | 59 | function createCookie(name,value,days) { 60 | if (days) { 61 | var date = new Date(); 62 | date.setTime(date.getTime()+(days*24*60*60*1000)); 63 | var expires = "; expires="+date.toGMTString(); 64 | } 65 | else var expires = ""; 66 | var cookie = name+"="+value+expires+"; path=/"; 67 | return cookie; 68 | } 69 | -------------------------------------------------------------------------------- /addons/browserid/lib/main.js: -------------------------------------------------------------------------------- 1 | const {data} = require("self"); 2 | const {WindowListener} = require("./window_listener"); 3 | const {Panel} = require("panel"); 4 | const {PageMod} = require("page-mod"); 5 | const {Cc, Ci} = require("chrome"); 6 | 7 | exports.main = AuthLoader; 8 | 9 | 10 | function AuthLoader() { 11 | createWindowListener.call(this); 12 | } 13 | 14 | function showPanel(host, worker) { 15 | var panel = createShowPanel(); 16 | 17 | panel.port.on("controllerReady", function() { 18 | panel.port.emit("getVerifiedEmail", { 19 | host: host 20 | }); 21 | }); 22 | 23 | panel.port.on("assertionReady", function(payload) { 24 | worker.port.emit("assertionReady", { 25 | assertion: payload.assertion 26 | }); 27 | panel.hide(); 28 | }); 29 | 30 | 31 | 32 | return panel; 33 | } 34 | 35 | function createWindowListener() { 36 | var windowListener = new WindowListener(); 37 | windowListener.on("windowopen", onNewWindow); 38 | windowListener.init(); 39 | return windowListener; 40 | } 41 | 42 | function onNewWindow(window) { 43 | var pageMod = PageMod({ 44 | include: "*", 45 | contentScriptWhen: "start", 46 | contentScriptFile: data.url("injector.js"), 47 | onAttach: function(worker) { 48 | worker.port.on("getAssertion", function(payload) { 49 | showPanel(payload.location, worker); 50 | }); 51 | 52 | } 53 | }); 54 | } 55 | 56 | function createShowPanel() { 57 | var panel = Panel({ 58 | contentURL: "https://browserid.org/dialog/dialog.html", 59 | contentScriptFile: [ 60 | data.url("channel.js") 61 | ], 62 | contentScriptWhen: "start", 63 | allow: { script: true }, 64 | width: 540, 65 | height: 370 66 | }); 67 | 68 | let WM = Cc["@mozilla.org/appshell/window-mediator;1"] 69 | .getService(Ci.nsIWindowMediator); 70 | let doc = WM.getMostRecentWindow("navigator:browser").document; 71 | let el = doc.getElementById("identity-box-inner"); 72 | panel.show(el); 73 | 74 | return panel; 75 | } 76 | 77 | -------------------------------------------------------------------------------- /addons/sessions/lib/login_chrome.js: -------------------------------------------------------------------------------- 1 | const {Cc, Ci, Cs, Cr} = require("chrome"); 2 | const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 3 | const {EventEmitter} = require("events"); 4 | 5 | const LoginManager = EventEmitter.compose({ 6 | constructor: function(options) { 7 | let {document} = options; 8 | 9 | 10 | this.box = createNode(document, "box", { 11 | id: "identity-session-box" 12 | }); 13 | 14 | this.signIn = createNode(document, "label", { 15 | id: "identity-session-signin", 16 | value: "Sign in", 17 | parentNode: this.box 18 | }); 19 | this.signIn.addEventListener("click", this._emit.bind(this, "login"), false); 20 | 21 | this.userInfo = createNode(document, "label", { 22 | id: "identity-session-userinfo", 23 | value: "", 24 | parentNode: this.box 25 | }); 26 | this.userInfo.addEventListener("click", this._emit.bind(this, "userinfo"), false); 27 | 28 | this.image = createNode(document, "image", { 29 | id: "identity-session-icon", 30 | parentNode: this.box 31 | }); 32 | this.image.hide(); 33 | 34 | this.siteName = createNode(document, "label", { 35 | id: "identity-session-site", 36 | value: "site name", 37 | parentNode: this.box 38 | }); 39 | this.siteName.hide(); 40 | 41 | let insertBefore = document.getElementById("notification-popup-box"); 42 | if(insertBefore) { 43 | insertBefore.parentNode.insertBefore(this.box, insertBefore); 44 | } 45 | 46 | }, 47 | 48 | show: function(host) { 49 | this.box.show(); 50 | this.siteName.setAttribute("value", host); 51 | }, 52 | 53 | login: function(host) { 54 | this.show(host); 55 | this.signIn.show(); 56 | this.userInfo.hide(); 57 | }, 58 | 59 | loggedIn: function(username, host) { 60 | this.show(host); 61 | this.signIn.hide(); 62 | this.userInfo.setAttribute("value", username); 63 | this.userInfo.show(); 64 | }, 65 | 66 | none: function() { 67 | this.box.hide(); 68 | }, 69 | 70 | setURL: function(url) { 71 | this.siteName.setAttribute("value", url); 72 | } 73 | 74 | }); 75 | 76 | exports.LoginManager = LoginManager; 77 | 78 | const NODE_SPECIAL = ['parentNode']; 79 | function createNode(document, nodeName, attribs) { 80 | let node = document.createElementNS(XUL_NS, nodeName); 81 | 82 | node.show = function() { 83 | node.collapsed = node.hidden = false; 84 | }; 85 | 86 | node.hide = function() { 87 | node.collapsed = node.hidden = true; 88 | }; 89 | 90 | for(let attrib in attribs) { 91 | let val = attribs[attrib]; 92 | if(NODE_SPECIAL.indexOf(attrib) === -1) { 93 | node.setAttribute(attrib, val); 94 | } 95 | else if(attrib === 'parentNode') { 96 | val.insertBefore(node, null); 97 | } 98 | } 99 | 100 | return node; 101 | } 102 | 103 | -------------------------------------------------------------------------------- /addons/sessions/lib/helpers.js: -------------------------------------------------------------------------------- 1 | const {Cc, Ci} = require("chrome"); 2 | 3 | const Helpers = { 4 | toConsole: function(obj, printer) { 5 | let printer = printer || console; 6 | printer.log("printing object"); 7 | for(var key in obj) { 8 | printer.log(key + ': ' + obj[key]); 9 | } 10 | }, 11 | 12 | chrome: { 13 | getMostRecentWindow: function() { 14 | let WM = Cc["@mozilla.org/appshell/window-mediator;1"] 15 | .getService(Ci.nsIWindowMediator); 16 | let win = WM.getMostRecentWindow("navigator:browser"); 17 | return win; 18 | }, 19 | 20 | getDocument: function(window) { 21 | window = window || Helpers.chrome.getMostRecentWindow(); 22 | return window.document; 23 | }, 24 | 25 | getElementById: function(id, document) { 26 | document = document || Helpers.chrome.getDocument(); 27 | let el = document.getElementById(id); 28 | return el; 29 | }, 30 | 31 | 32 | simulateDOMEvent: function(el, evtClass, type, document) { 33 | document = document || Helpers.chrome.getDocument(); 34 | let evt = document.createEvent(evtClass); 35 | 36 | if(evtClass === "MouseEvents") { 37 | evt.initMouseEvent(type, true, true, document.defaultView, 38 | 0, 0, 0, 0, 0, false, false, false, false, 0, null); 39 | } 40 | 41 | el.dispatchEvent(evt); 42 | 43 | return evt; 44 | }, 45 | 46 | loadStylesheet: function(uri, document) { 47 | document = document || Helpers.chrome.getDocument(); 48 | let pi = document.createProcessingInstruction( 49 | "xml-stylesheet", "href=\"" + uri + "\" type=\"text/css\""); 50 | document.insertBefore(pi, document.firstChild); 51 | return pi; 52 | } 53 | }, 54 | 55 | workers: { 56 | 57 | // Overrides worker.port.on so that listeners are automatically 58 | // detached when the worker is removed. 59 | // Not sure if this is strictly needed. 60 | prepare: function(worker) { 61 | worker.portListeners = {}; 62 | 63 | let origOn = worker.port.on; 64 | worker.port.on = function(message, listener) { 65 | worker.portListeners[message] = worker.portListeners[message] || []; 66 | worker.portListeners[message].push(listener); 67 | origOn.call(worker, message, listener); 68 | }; 69 | 70 | worker.once("detach", function() { 71 | for(var key in worker.portListeners) { 72 | var listeners = worker.portListeners[key]; 73 | listeners.forEach(function(listener) { 74 | worker.port.removeListener(key, listener); 75 | }); 76 | worker.portListeners[key] = null; 77 | delete worker.portListeners[key]; 78 | } 79 | }); 80 | } 81 | 82 | 83 | } 84 | }; 85 | 86 | exports.Helpers = Helpers; 87 | -------------------------------------------------------------------------------- /addons/sessions/lib/main.js: -------------------------------------------------------------------------------- 1 | const {data} = require("self"); 2 | const {Panel} = require("panel"); 3 | const {Widget} = require("widget"); 4 | const {PageMod} = require("page-mod"); 5 | const {Fakers} = require("./fakers"); 6 | const LoginWidget = require("./window_manager").WindowManager; 7 | const tabs = require("tabs"); 8 | const {Helpers} = require("./helpers"); 9 | const timers = require("timers"); 10 | 11 | const {CookieMonster} = require("./cookie_monster"); 12 | 13 | var cookieMonster = new CookieMonster(); 14 | 15 | var panel = Panel({ 16 | contentURL: data.url("sessions.html"), 17 | contentScriptFile: data.url("sessions.js") 18 | }); 19 | 20 | panel.port.on("sessions.setheight", function(height) { 21 | panel.height = height + 20; 22 | }); 23 | panel.port.on("sessions.selectsession", onSessionSelection); 24 | panel.port.on("sessions.logout", function() { 25 | panel.hide(); 26 | logoutCallback(); 27 | }); 28 | 29 | Fakers(); 30 | 31 | var loginWidget = LoginWidget(loginCallback, logoutCallback, sessionCallback); 32 | 33 | var currWorker; 34 | 35 | var pageMod = PageMod({ 36 | include: ["*"], 37 | contentScriptWhen: "start", 38 | contentScriptFile: data.url("page_interaction.js"), 39 | onAttach: function(worker) { 40 | Helpers.workers.prepare(worker); 41 | 42 | console.log("we have a worker"); 43 | 44 | worker.port.on("sessions.set", onSessionSet.bind(worker)); 45 | worker.port.on("sessions.opentab", onSessionTabOpen.bind(worker, worker.tab)); 46 | } 47 | }); 48 | 49 | 50 | 51 | function onSessionSet(data) { 52 | console.log("session set"); 53 | currWorker = this; 54 | var tab = this.tab; 55 | var sessions = data.sessions; 56 | var host = data.host; 57 | 58 | if("undefined" === typeof sessions) { 59 | loginWidget.none(); 60 | console.log("clearing sessions"); 61 | if(this.loggedin) { 62 | this.loggedin = false; 63 | this.port.emit("emitevent.logout"); 64 | } 65 | } 66 | else if(sessions.length) { 67 | // We support cookie login and have sessions. Keep track of 68 | // these cookies, we may have to logout. 69 | this.loggedin = true; 70 | panel.port.emit("sessions.set", sessions); 71 | var username = ''; 72 | sessions.forEach(function(session) { 73 | var boundTo = session.bound_to; 74 | if(boundTo && boundTo.type === "cookie") { 75 | console.log("bound to a cookie: " + boundTo.name); 76 | tab.cookie = boundTo.name; 77 | cookieMonster.watch(host, boundTo.name, function() { 78 | //console.log("cookie changed, we should do something"); 79 | }); 80 | } 81 | username = session.email; 82 | }); 83 | console.log("setting loggedIn"); 84 | tab.loginShown = true; 85 | loginWidget.loggedIn(username, host); 86 | } 87 | else if(this.loggedin) { 88 | // in this case, sessions is set to [], which means the site 89 | // supports our login scheme. We were logged in, so we have 90 | // to logout. 91 | this.loggedin = false; 92 | this.port.emit("emitevent.logout"); 93 | console.log("setting login"); 94 | loginWidget.login(host); 95 | tab.loginShown = true; 96 | } 97 | else { 98 | // in this case, we have a sessions, but we were not previously 99 | // logged in, show the login button only. 100 | console.log("setting login"); 101 | loginWidget.login(host); 102 | tab.loginShown = true; 103 | } 104 | } 105 | 106 | 107 | function onSessionTabOpen(tab) { 108 | console.log("session tab open"); 109 | 110 | console.log("typeof tab.window: " + typeof tab.window); 111 | tab.loginShown = false; 112 | }; 113 | 114 | tabs.on("ready", onSessionTabReady); 115 | tabs.on("activate", function() { 116 | console.log("tab activated"); 117 | }); 118 | 119 | function onSessionTabReady(tab) { 120 | if(tab.cookie) { 121 | console.log("some cookies watched for this tab"); 122 | } 123 | 124 | timers.setTimeout(function() { 125 | if(!tab.loginShown) { 126 | console.log("hiding login"); 127 | loginWidget.none(); 128 | } 129 | }, 1000); 130 | } 131 | 132 | function onSessionSelection(payload) { 133 | var session = payload.session; 134 | } 135 | 136 | function loginCallback() { 137 | if(currWorker) { 138 | currWorker.port.emit("emitevent.login"); 139 | } 140 | }; 141 | 142 | function logoutCallback() { 143 | if(currWorker) { 144 | currWorker.port.emit("emitevent.logout"); 145 | } 146 | }; 147 | 148 | function sessionCallback() { 149 | var el = Helpers.chrome.getElementById('identity-session-box'); 150 | panel.show(el); 151 | }; 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /addons/sessions/test/test-helpers.js: -------------------------------------------------------------------------------- 1 | const self = require("self"); 2 | const {Helpers} = require("helpers"); 3 | 4 | let getMostRecentWindowCalled, 5 | getMostRecentWindow = Helpers.chrome.getMostRecentWindow, 6 | getDocumentCalled, 7 | getDocument = Helpers.chrome.getDocument; 8 | 9 | exports.setup = function() { 10 | getMostRecentWindowCalled = getDocumentCalled = false; 11 | 12 | Helpers.chrome.getMostRecentWindow = function() { 13 | getMostRecentWindowCalled = true; 14 | return getMostRecentWindow.apply(null, arguments); 15 | }; 16 | 17 | Helpers.chrome.getDocument = function() { 18 | getDocumentCalled = true; 19 | return getDocument.apply(null, arguments); 20 | }; 21 | }; 22 | 23 | exports.teardown = function() { 24 | Helpers.chrome.getMostRecentWindow = getMostRecentWindow; 25 | Helpers.chrome.getDocument = getDocument; 26 | }; 27 | 28 | 29 | 30 | exports['toConsole'] = function(test) { 31 | let logged = []; 32 | Helpers.toConsole({ 33 | a: true, 34 | b: 'wee' 35 | }, { 36 | log: function(val) { 37 | logged.push(val); 38 | } 39 | }); 40 | 41 | test.assertEqual(logged.length, 3, 'three items logged - header + 2 items'); 42 | }; 43 | 44 | exports['getMostRecentWindow'] = function(test) { 45 | let win = Helpers.chrome.getMostRecentWindow(); 46 | test.assertEqual( !!win, true, 'we have a window!'); 47 | }; 48 | 49 | exports['getDocument no window'] = function(test) { 50 | let doc = Helpers.chrome.getDocument(); 51 | test.assertNotUndefined(doc, 'we have a doc!'); 52 | }; 53 | 54 | exports['getDocument with window'] = function(test) { 55 | let win = Helpers.chrome.getMostRecentWindow(); 56 | getMostRecentWindowCalled = false; 57 | 58 | let doc = Helpers.chrome.getDocument(win); 59 | test.assertNotUndefined(doc, 'we have a doc!'); 60 | test.assertEqual(false, getMostRecentWindowCalled, 'getMostRecentWindow was not called when passed a window'); 61 | }; 62 | 63 | exports['getElementById'] = function(test) { 64 | let el = Helpers.chrome.getElementById('urlbar-container'); 65 | 66 | test.assertNotUndefined(el, 'we have an element!'); 67 | test.assertEqual(typeof el.parentNode, "object", 'has a parent node, can assume it\'s an element'); 68 | }; 69 | 70 | exports['getElementById with document'] = function(test) { 71 | let doc = Helpers.chrome.getDocument(); 72 | getDocumentCalled = false; 73 | let el = Helpers.chrome.getElementById('urlbar-container', doc); 74 | 75 | test.assertNotUndefined(el, 'we have an element!'); 76 | test.assertEqual(typeof el.parentNode, "object", 'has a parent node, can assume it\'s an element'); 77 | test.assertEqual(false, getDocumentCalled, 'getDocument was not called when passed a document'); 78 | }; 79 | 80 | exports['simulateDOMEvent with click'] = function(test) { 81 | 82 | let success = false; 83 | let el = Helpers.chrome.getElementById('urlbar-container'); 84 | 85 | el.addEventListener("click", function() { 86 | success = true; 87 | }, false); 88 | Helpers.chrome.simulateDOMEvent(el, "MouseEvents", "click"); 89 | 90 | test.assertEqual(success, true, "click event happened"); 91 | }; 92 | 93 | exports['simulateDOMEvent with click and document'] = function(test) { 94 | let success = false; 95 | let el = Helpers.chrome.getElementById('urlbar-container'); 96 | 97 | let doc = Helpers.chrome.getDocument(); 98 | getDocumentCalled = false; 99 | el.addEventListener("click", function() { 100 | success = true; 101 | }, false); 102 | Helpers.chrome.simulateDOMEvent(el, "MouseEvents", "click", doc); 103 | 104 | test.assertEqual(success, true, "click event happened"); 105 | test.assertEqual(false, getDocumentCalled, 'getDocument was not called when passed a document'); 106 | }; 107 | 108 | exports['loadStylesheet'] = function(test) { 109 | let pi = Helpers.chrome.loadStylesheet(self.data.url('styles/identity-sessions.css')); 110 | 111 | test.assertEqual(!!pi, true, 'we have some pi without failure'); 112 | } 113 | 114 | exports['loadStylesheet with document'] = function(test) { 115 | let doc = Helpers.chrome.getDocument(); 116 | getDocumentCalled = false; 117 | let pi = Helpers.chrome.loadStylesheet(self.data.url('styles/identity-sessions.css'), doc); 118 | 119 | test.assertEqual(!!pi, true, 'we have some pi without failure'); 120 | test.assertEqual(false, getDocumentCalled, 'getDocument was not called when passed a document'); 121 | } 122 | 123 | exports['workers.prepare makes it so listeners are automatically removed'] = function(test) { 124 | var detach, listener, remove; 125 | var worker = { 126 | once: function(message, func) {detach = func}, 127 | port: { 128 | on: function(message, func) { 129 | listener = func; 130 | }, 131 | removeListener: function(message, func) { 132 | remove = func; 133 | } 134 | } 135 | }; 136 | 137 | Helpers.workers.prepare(worker); 138 | 139 | let callback = function(){}; 140 | worker.port.on("message", callback); 141 | 142 | test.assertEqual(listener, callback, 'our callback has been registered'); 143 | 144 | detach(); 145 | test.assertEqual(remove, callback, 'we removed our registered function'); 146 | }; 147 | 148 | --------------------------------------------------------------------------------