diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index c5f5c442721..265ea4b935a 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 63d3d7d1ef4..85911a600d7 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 7974a8541b2..abe6863294e 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index c5f5c442721..265ea4b935a 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml index 4df0da0532f..2da5f5250a8 100644 --- a/b2g/config/flame/sources.xml +++ b/b2g/config/flame/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 77f7e385879..ca159c4f52c 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -4,6 +4,6 @@ "remote": "", "branch": "" }, - "revision": "6f63b10d567e5a8fb51bbebe5e482294427cc05d", + "revision": "f705a3f96020c7d7aa5ec63bf3417db29e1ab2a2", "repo_path": "/integration/gaia-central" } diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index b6a8fa034f4..3c43b86d256 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index 8d18abbfec7..e9cc41bd743 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 7a1364f29c2..9d13b6e19d2 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index 3c29f6f3f14..05e7b9625d1 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/dom/apps/src/PermissionsTable.jsm b/dom/apps/src/PermissionsTable.jsm index 87d0656d23e..dfed59b8efd 100644 --- a/dom/apps/src/PermissionsTable.jsm +++ b/dom/apps/src/PermissionsTable.jsm @@ -367,6 +367,17 @@ this.PermissionsTable = { geolocation: { privileged: PROMPT_ACTION, certified: ALLOW_ACTION, access: ["read", "write", "create"] + }, + "firefox-accounts": { + app: DENY_ACTION, + privileged: DENY_ACTION, + certified: ALLOW_ACTION + }, + "moz-firefox-accounts": { + app: DENY_ACTION, + privileged: PROMPT_ACTION, + certified: ALLOW_ACTION, + substitute: ["firefox-accounts"] } }; diff --git a/dom/bluetooth/bluedroid/BluetoothSocket.cpp b/dom/bluetooth/bluedroid/BluetoothSocket.cpp index 0330fb27442..6b0e3d27359 100644 --- a/dom/bluetooth/bluedroid/BluetoothSocket.cpp +++ b/dom/bluetooth/bluedroid/BluetoothSocket.cpp @@ -145,8 +145,7 @@ public: MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(!mShuttingDownOnIOThread); - RemoveWatchers(READ_WATCHER | WRITE_WATCHER); - + Close(); // will also remove fd from I/O loop mShuttingDownOnIOThread = true; } diff --git a/dom/cellbroadcast/src/CellBroadcast.cpp b/dom/cellbroadcast/src/CellBroadcast.cpp index 849fbb07942..7df492eb5d0 100644 --- a/dom/cellbroadcast/src/CellBroadcast.cpp +++ b/dom/cellbroadcast/src/CellBroadcast.cpp @@ -37,6 +37,12 @@ public: MOZ_ASSERT(mCellBroadcast); mCellBroadcast = nullptr; } + +private: + ~Listener() + { + MOZ_ASSERT(!mCellBroadcast); + } }; NS_IMPL_ISUPPORTS(CellBroadcast::Listener, nsICellBroadcastListener) diff --git a/dom/icc/src/IccListener.h b/dom/icc/src/IccListener.h index e050393877e..c152678d3db 100644 --- a/dom/icc/src/IccListener.h +++ b/dom/icc/src/IccListener.h @@ -21,7 +21,6 @@ public: NS_DECL_NSIICCLISTENER IccListener(IccManager* aIccManager, uint32_t aClientId); - ~IccListener(); void Shutdown(); @@ -32,6 +31,9 @@ public: return mIcc; } +private: + ~IccListener(); + private: uint32_t mClientId; // We did not setup 'mIcc' and 'mIccManager' being a participant of cycle diff --git a/dom/icc/src/IccManager.h b/dom/icc/src/IccManager.h index c1a1f445169..8982cb9e057 100644 --- a/dom/icc/src/IccManager.h +++ b/dom/icc/src/IccManager.h @@ -26,7 +26,6 @@ public: NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IccManager, DOMEventTargetHelper) IccManager(nsPIDOMWindow* aWindow); - ~IccManager(); void Shutdown(); @@ -52,6 +51,9 @@ public: virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; +private: + ~IccManager(); + private: nsTArray> mIccListeners; }; diff --git a/dom/identity/DOMIdentity.jsm b/dom/identity/DOMIdentity.jsm index 2c411ce8ad2..e6c6e4a2b02 100644 --- a/dom/identity/DOMIdentity.jsm +++ b/dom/identity/DOMIdentity.jsm @@ -10,6 +10,7 @@ Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); const PREF_FXA_ENABLED = "identity.fxaccounts.enabled"; +const FXA_PERMISSION = "firefox-accounts"; // This is the parent process corresponding to nsDOMIdentity. this.EXPORTED_SYMBOLS = ["DOMIdentity"]; @@ -38,6 +39,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "ppmm", "@mozilla.org/parentprocessmessagemanager;1", "nsIMessageListenerManager"); +XPCOMUtils.defineLazyServiceGetter(this, "permissionManager", + "@mozilla.org/permissionmanager;1", + "nsIPermissionManager"); + function log(...aMessageArgs) { Logger.log.apply(Logger, ["DOMIdentity"].concat(aMessageArgs)); } @@ -98,7 +103,7 @@ IDPAuthenticationContext.prototype = { } }; -function RPWatchContext(aOptions, aTargetMM) { +function RPWatchContext(aOptions, aTargetMM, aPrincipal) { objectCopy(aOptions, this); // id and origin are required @@ -106,6 +111,8 @@ function RPWatchContext(aOptions, aTargetMM) { throw new Error("id and origin are required for RP watch context"); } + this.principal = aPrincipal; + // default for no loggedInUser is undefined, not null this.loggedInUser = aOptions.loggedInUser; @@ -182,8 +189,8 @@ this.DOMIdentity = { /* * Create a new RPWatchContext, and update the context maps. */ - newContext: function(message, targetMM) { - let context = new RPWatchContext(message, targetMM); + newContext: function(message, targetMM, principal) { + let context = new RPWatchContext(message, targetMM, principal); this._serviceContexts.set(message.id, context); this._mmContexts.set(targetMM, message.id); return context; @@ -234,6 +241,28 @@ this.DOMIdentity = { this._mmContexts.delete(targetMM); }, + hasPermission: function(aMessage) { + // We only check that the firefox accounts permission is present in the + // manifest. + if (aMessage.json && aMessage.json.wantIssuer == "firefox-accounts") { + if (!aMessage.principal) { + return false; + } + let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + let uri = Services.io.newURI(aMessage.principal.origin, null, null); + let principal = secMan.getAppCodebasePrincipal(uri, + aMessage.principal.appId, aMessage.principal.isInBrowserElement); + + let permission = + permissionManager.testPermissionFromPrincipal(principal, + FXA_PERMISSION); + return permission != Ci.nsIPermissionManager.UNKNOWN_ACTION && + permission != Ci.nsIPermissionManager.DENY_ACTION; + } + return true; + }, + // nsIMessageListener receiveMessage: function DOMIdentity_receiveMessage(aMessage) { let msg = aMessage.json; @@ -242,19 +271,23 @@ this.DOMIdentity = { // used to send replies back to the proper window. let targetMM = aMessage.target; + if (!this.hasPermission(aMessage)) { + throw new Error("PERMISSION_DENIED"); + } + switch (aMessage.name) { // RP case "Identity:RP:Watch": - this._watch(msg, targetMM); + this._watch(msg, targetMM, aMessage.principal); break; case "Identity:RP:Unwatch": this._unwatch(msg, targetMM); break; case "Identity:RP:Request": - this._request(msg, targetMM); + this._request(msg); break; case "Identity:RP:Logout": - this._logout(msg, targetMM); + this._logout(msg); break; // IDP case "Identity:IDP:BeginProvisioning": @@ -328,9 +361,9 @@ this.DOMIdentity = { ppmm = null; }, - _watch: function DOMIdentity__watch(message, targetMM) { - log("DOMIdentity__watch: " + message.id); - let context = this.newContext(message, targetMM); + _watch: function DOMIdentity__watch(message, targetMM, principal) { + log("DOMIdentity__watch: " + message.id + " - " + principal); + let context = this.newContext(message, targetMM, principal); this.getService(message).RP.watch(context); }, diff --git a/dom/identity/nsDOMIdentity.js b/dom/identity/nsDOMIdentity.js index 5e4597ccb9b..a5d7d946a3f 100644 --- a/dom/identity/nsDOMIdentity.js +++ b/dom/identity/nsDOMIdentity.js @@ -40,8 +40,6 @@ XPCOMUtils.defineLazyServiceGetter(this, "cpmm", const ERRORS = { - "ERROR_NOT_AUTHORIZED_FOR_FIREFOX_ACCOUNTS": - "Only privileged and certified apps may use Firefox Accounts", "ERROR_INVALID_ASSERTION_AUDIENCE": "Assertion audience may not differ from origin", "ERROR_REQUEST_WHILE_NOT_HANDLING_USER_INPUT": @@ -150,7 +148,12 @@ nsDOMIdentity.prototype = { // broken client to be able to call watch() any more. It's broken. return; } - this._identityInternal._mm.sendAsyncMessage("Identity:RP:Watch", message); + this._identityInternal._mm.sendAsyncMessage( + "Identity:RP:Watch", + message, + null, + this._window.document.nodePrincipal + ); }, request: function nsDOMIdentity_request(aOptions = {}) { @@ -221,7 +224,12 @@ nsDOMIdentity.prototype = { } this._rpCalls++; - this._identityInternal._mm.sendAsyncMessage("Identity:RP:Request", message); + this._identityInternal._mm.sendAsyncMessage( + "Identity:RP:Request", + message, + null, + this._window.document.nodePrincipal + ); }, logout: function nsDOMIdentity_logout() { @@ -241,7 +249,12 @@ nsDOMIdentity.prototype = { return; } - this._identityInternal._mm.sendAsyncMessage("Identity:RP:Logout", message); + this._identityInternal._mm.sendAsyncMessage( + "Identity:RP:Logout", + message, + null, + this._window.document.nodePrincipal + ); }, /* @@ -324,8 +337,12 @@ nsDOMIdentity.prototype = { } this._beginProvisioningCallback = aCallback; - this._identityInternal._mm.sendAsyncMessage("Identity:IDP:BeginProvisioning", - this.DOMIdentityMessage()); + this._identityInternal._mm.sendAsyncMessage( + "Identity:IDP:BeginProvisioning", + this.DOMIdentityMessage(), + null, + this._window.document.nodePrincipal + ); }, genKeyPair: function nsDOMIdentity_genKeyPair(aCallback) { @@ -341,8 +358,12 @@ nsDOMIdentity.prototype = { } this._genKeyPairCallback = aCallback; - this._identityInternal._mm.sendAsyncMessage("Identity:IDP:GenKeyPair", - this.DOMIdentityMessage()); + this._identityInternal._mm.sendAsyncMessage( + "Identity:IDP:GenKeyPair", + this.DOMIdentityMessage(), + null, + this._window.document.nodePrincipal + ); }, registerCertificate: function nsDOMIdentity_registerCertificate(aCertificate) { @@ -357,7 +378,12 @@ nsDOMIdentity.prototype = { let message = this.DOMIdentityMessage(); message.cert = aCertificate; - this._identityInternal._mm.sendAsyncMessage("Identity:IDP:RegisterCertificate", message); + this._identityInternal._mm.sendAsyncMessage( + "Identity:IDP:RegisterCertificate", + message, + null, + this._window.document.nodePrincipal + ); }, raiseProvisioningFailure: function nsDOMIdentity_raiseProvisioningFailure(aReason) { @@ -372,7 +398,12 @@ nsDOMIdentity.prototype = { let message = this.DOMIdentityMessage(); message.reason = aReason; - this._identityInternal._mm.sendAsyncMessage("Identity:IDP:ProvisioningFailure", message); + this._identityInternal._mm.sendAsyncMessage( + "Identity:IDP:ProvisioningFailure", + message, + null, + this._window.document.nodePrincipal + ); }, /** @@ -392,8 +423,12 @@ nsDOMIdentity.prototype = { } this._beginAuthenticationCallback = aCallback; - this._identityInternal._mm.sendAsyncMessage("Identity:IDP:BeginAuthentication", - this.DOMIdentityMessage()); + this._identityInternal._mm.sendAsyncMessage( + "Identity:IDP:BeginAuthentication", + this.DOMIdentityMessage(), + null, + this._window.document.nodePrincipal + ); }, completeAuthentication: function nsDOMIdentity_completeAuthentication() { @@ -405,8 +440,12 @@ nsDOMIdentity.prototype = { } this._authenticationEnded = true; - this._identityInternal._mm.sendAsyncMessage("Identity:IDP:CompleteAuthentication", - this.DOMIdentityMessage()); + this._identityInternal._mm.sendAsyncMessage( + "Identity:IDP:CompleteAuthentication", + this.DOMIdentityMessage(), + null, + this._window.document.nodePrincipal + ); }, raiseAuthenticationFailure: function nsDOMIdentity_raiseAuthenticationFailure(aReason) { @@ -419,7 +458,12 @@ nsDOMIdentity.prototype = { let message = this.DOMIdentityMessage(); message.reason = aReason; - this._identityInternal._mm.sendAsyncMessage("Identity:IDP:AuthenticationFailure", message); + this._identityInternal._mm.sendAsyncMessage( + "Identity:IDP:AuthenticationFailure", + message, + null, + this._window.document.nodePrincipal + ); }, // Private. @@ -510,7 +554,8 @@ nsDOMIdentity.prototype = { case "Identity:RP:Watch:OnCancel": // Do we have a watcher? if (!this._rpWatcher) { - this._log("WARNING: Received OnCancel message, but there is no RP watcher"); + this._log("WARNING: Received OnCancel message, but there is no RP " + + "watcher"); return; } @@ -520,7 +565,8 @@ nsDOMIdentity.prototype = { break; case "Identity:RP:Watch:OnError": if (!this._rpWatcher) { - this._log("WARNING: Received OnError message, but there is no RP watcher"); + this._log("WARNING: Received OnError message, but there is no RP " + + "watcher"); return; } @@ -593,7 +639,6 @@ nsDOMIdentity.prototype = { let message = { errors: [] }; - let principal = Ci.nsIPrincipal; objectCopy(aOptions, message); @@ -603,19 +648,6 @@ nsDOMIdentity.prototype = { // window origin message.origin = this._origin; - // On b2g, an app's status can be NOT_INSTALLED, INSTALLED, PRIVILEGED, or - // CERTIFIED. Compare the appStatus value to the constants enumerated in - // Ci.nsIPrincipal.APP_STATUS_*. - message.appStatus = this._appStatus; - - // Currently, we only permit certified and privileged apps to use - // Firefox Accounts. - if (aOptions.wantIssuer == "firefox-accounts" && - this._appStatus !== principal.APP_STATUS_PRIVILEGED && - this._appStatus !== principal.APP_STATUS_CERTIFIED) { - message.errors.push("ERROR_NOT_AUTHORIZED_FOR_FIREFOX_ACCOUNTS"); - } - // Normally the window origin will be the audience in assertions. On b2g, // certified apps have the power to override this and declare any audience // the want. Privileged apps can also declare a different audience, as @@ -628,7 +660,7 @@ nsDOMIdentity.prototype = { // and then post-message the results down to their app. let _audience = message.origin; if (message.audience && message.audience != message.origin) { - if (this._appStatus === principal.APP_STATUS_CERTIFIED) { + if (this._appStatus === Ci.nsIPrincipal.APP_STATUS_CERTIFIED) { _audience = message.audience; this._log("Certified app setting assertion audience: " + _audience); } else { @@ -648,7 +680,9 @@ nsDOMIdentity.prototype = { this._log("nsDOMIdentity uninit() " + this._id); this._identityInternal._mm.sendAsyncMessage( "Identity:RP:Unwatch", - { id: this._id } + { id: this._id }, + null, + this._window.document.nodePrincipal ); } diff --git a/dom/identity/tests/mochitest/file_declareAudience.html b/dom/identity/tests/mochitest/file_declareAudience.html index 3313ca3ca83..e8514409d5b 100644 --- a/dom/identity/tests/mochitest/file_declareAudience.html +++ b/dom/identity/tests/mochitest/file_declareAudience.html @@ -22,31 +22,49 @@ window.realParent.postMessage(JSON.stringify(message), "*"); } - function onready() { - navigator.mozId.request(); - } - - function onlogin(backedAssertion) { - postResults({success: true, backedAssertion: backedAssertion}); - } - - function onerror(error) { - postResults({success: false, error: error}); - } - onmessage = function(event) { - navigator.mozId.watch({ - wantIssuer: "firefox-accounts", - audience: event.data.audience, - onready: onready, - onlogin: onlogin, - onerror: onerror, + try { + navigator.mozId.watch({ + wantIssuer: "firefox-accounts", + audience: event.data.audience, + onready: function() { + try { + navigator.mozId.request(); + } catch(e) { + postResults({ + success: false, + error: e, + appIndex: event.data.appIndex + }); + } + }, + onlogin: function(backedAssertion) { + postResults({ + success: true, + backedAssertion: backedAssertion, + appIndex: event.data.appIndex + }); + }, + onerror: function(error) { + postResults({ + success: false, + error: error, + appIndex: event.data.appIndex + }); + }, - // onlogout will actually be called every time watch() is invoked, - // because fxa will find no signed-in user and so trigger logout. - // For this test, though, we don't care and just ignore logout. - onlogout: function () {}, - }); + // onlogout will actually be called every time watch() is invoked, + // because fxa will find no signed-in user and so trigger logout. + // For this test, though, we don't care and just ignore logout. + onlogout: function () {}, + }); + } catch (e) { + postResults({ + success: false, + error: e, + appIndex: event.data.appIndex + }); + } }; diff --git a/dom/identity/tests/mochitest/file_syntheticEvents.html b/dom/identity/tests/mochitest/file_syntheticEvents.html index 58cd005f5cc..74e105b7a70 100644 --- a/dom/identity/tests/mochitest/file_syntheticEvents.html +++ b/dom/identity/tests/mochitest/file_syntheticEvents.html @@ -23,24 +23,34 @@ window.realParent.postMessage(JSON.stringify(message), "*"); } - function onready() { - navigator.mozId.request(); - } - - function onlogin(backedAssertion) { - postResults({success: true, backedAssertion: backedAssertion}); - } - - function onerror(error) { - postResults({success: false, error: error}); - } - - onmessage = function(message) { + onmessage = function(event) { navigator.mozId.watch({ - wantIssuer: message.data.wantIssuer, - onready: onready, - onerror: onerror, - onlogin: onlogin, + wantIssuer: event.data.wantIssuer, + onready: function() { + try { + navigator.mozId.request(); + } catch(e) { + postResults({ + success: false, + error: e, + appIndex: event.data.appIndex + }); + } + }, + onlogin: function(backedAssertion) { + postResults({ + success: true, + backedAssertion: backedAssertion, + appIndex: event.data.appIndex + }); + }, + onerror: function(error) { + postResults({ + success: false, + error: error, + appIndex: event.data.appIndex + }); + }, onlogout: function() {}, }); }; diff --git a/dom/identity/tests/mochitest/test_declareAudience.html b/dom/identity/tests/mochitest/test_declareAudience.html index 4ef9daf5f4c..b80efda5b85 100644 --- a/dom/identity/tests/mochitest/test_declareAudience.html +++ b/dom/identity/tests/mochitest/test_declareAudience.html @@ -102,7 +102,7 @@ let apps = [ uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_declareAudience.html", expected: { success: false, - underprivileged: false, + underprivileged: true, }, }, { @@ -114,6 +114,7 @@ let apps = [ uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_declareAudience.html", expected: { success: true, + underprivileged: false, }, }, { @@ -129,9 +130,7 @@ let apps = [ }, ]; -let appIndex = 0; -let expectedErrors = 0; -let receivedErrors = []; +let eventsReceived = 0; let testRunner = runTest(); // Successful tests will send exactly one message. But for error tests, we may @@ -139,9 +138,24 @@ let testRunner = runTest(); // track of received errors; once they reach the expected count, we are done. function receiveMessage(event) { let result = JSON.parse(event.data); - let app = apps[appIndex]; + let app = apps[result.appIndex]; + if (app.received) { + return; + } + apps[result.appIndex].received = true; + let expected = app.expected; + let expectedErrors = 0; + let receivedErrors = []; + + if (expected.underprivileged) { + expectedErrors += 1; + } + if (expected.nopermission) { + expectedErrors += 1; + } + is(result.success, expected.success, "Assertion request succeeds"); @@ -150,51 +164,41 @@ function receiveMessage(event) { let components = extractAssertionComponents(result.backedAssertion); is(components.payload.aud, app.wantAudience || app.origin, "Got desired assertion audience"); - } else { receivedErrors.push(result.error); } - if (receivedErrors.length === expectedErrors) { - if (expected.underprivileged) { - ok(receivedErrors.indexOf("ERROR_NOT_AUTHORIZED_FOR_FIREFOX_ACCOUNTS") > -1, - "Expect a complaint that this app cannot use FxA."); - } - if (!expected.success) { - ok(receivedErrors.indexOf("ERROR_INVALID_ASSERTION_AUDIENCE") > -1, - "Expect an error getting an assertion"); - } + ok(receivedErrors.length === expectedErrors, + "Received errors should be equal to expected errors"); - appIndex += 1; - - if (appIndex === apps.length) { - window.removeEventListener("message", receiveMessage); - - FirefoxAccounts.fxAccountsManager = originalManager; - - SimpleTest.finish(); - return; - } - - testRunner.next(); + if (!expected.success && expected.underprivileged) { + ok(receivedErrors.indexOf("ERROR_INVALID_ASSERTION_AUDIENCE") > -1, + "Expect an error getting an assertion"); } + + eventsReceived += 1; + + if (eventsReceived === apps.length) { + window.removeEventListener("message", receiveMessage); + + FirefoxAccounts.fxAccountsManager = originalManager; + + SimpleTest.finish(); + + return; + } + + testRunner.next(); } window.addEventListener("message", receiveMessage, false, true); function runTest() { - for (let app of apps) { - dump("** Testing " + app.title + "\n"); - // Set up state for message handler - expectedErrors = 0; - receivedErrors = []; - if (!app.expected.success) { - expectedErrors += 1; - } - if (app.expected.underprivileged) { - expectedErrors += 1; - } + let index; + for (let i = 0; i < apps.length; i++) { + let app = apps[i]; + dump("\n\n** Testing " + app.title + "\n"); let iframe = document.createElement("iframe"); @@ -204,24 +208,35 @@ function runTest() { document.getElementById("content").appendChild(iframe); - iframe.addEventListener("load", function onLoad() { - iframe.removeEventListener("load", onLoad); + index = i; + (function(_index) { + iframe.addEventListener("load", function onLoad() { + iframe.removeEventListener("load", onLoad); - let principal = iframe.contentDocument.nodePrincipal; - is(principal.appStatus, app.appStatus, - "Iframe's document.nodePrincipal has expected appStatus"); + SpecialPowers.addPermission( + "firefox-accounts", + SpecialPowers.Ci.nsIPermissionManager.ALLOW_ACTION, + iframe.contentDocument + ); - // Because the