diff --git a/b2g/chrome/content/settings.js b/b2g/chrome/content/settings.js index 98847326d3ef..d0a17ebad37a 100644 --- a/b2g/chrome/content/settings.js +++ b/b2g/chrome/content/settings.js @@ -671,6 +671,44 @@ SettingsListener.observe("accessibility.screenreader", false, function(value) { }); })(); +// =================== Low-precision buffer ====================== +(function setupLowPrecisionSettings() { + // The gaia setting layers.low-precision maps to two gecko prefs + SettingsListener.observe('layers.low-precision', null, function(value) { + if (value !== null) { + // Update gecko from the new Gaia setting + Services.prefs.setBoolPref('layers.low-precision-buffer', value); + Services.prefs.setBoolPref('layers.progressive-paint', value); + } else { + // Update gaia setting from gecko value + try { + let prefValue = Services.prefs.getBoolPref('layers.low-precision-buffer'); + let setting = { 'layers.low-precision': prefValue }; + window.navigator.mozSettings.createLock().set(setting); + } catch (e) { + console.log('Unable to read pref layers.low-precision-buffer: ' + e); + } + } + }); + + // The gaia setting layers.low-opacity maps to a string gecko pref (0.5/1.0) + SettingsListener.observe('layers.low-opacity', null, function(value) { + if (value !== null) { + // Update gecko from the new Gaia setting + Services.prefs.setCharPref('layers.low-precision-opacity', value ? '0.5' : '1.0'); + } else { + // Update gaia setting from gecko value + try { + let prefValue = Services.prefs.getCharPref('layers.low-precision-opacity'); + let setting = { 'layers.low-opacity': (prefValue == '0.5') }; + window.navigator.mozSettings.createLock().set(setting); + } catch (e) { + console.log('Unable to read pref layers.low-precision-opacity: ' + e); + } + } + }); +})(); + // =================== Various simple mapping ====================== let settingsToObserve = { 'app.update.channel': { diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index ce91dfef4d96..9b36032b8935 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,8 +19,8 @@ - - + + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 7a4a1c2af74e..ca93d0cddc8a 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,8 +17,8 @@ - - + + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 8bcacc4de2fd..07d59541378c 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,9 +15,9 @@ - + - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index ce91dfef4d96..9b36032b8935 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,8 +19,8 @@ - - + + diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml index deb1fd98067d..750025d1f064 100644 --- a/b2g/config/flame/sources.xml +++ b/b2g/config/flame/sources.xml @@ -17,8 +17,8 @@ - - + + diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index c06a6e0e68fd..6ccc08273288 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -4,6 +4,6 @@ "remote": "", "branch": "" }, - "revision": "d7d92199ea7e8f850ca0c6f0514bf596f178a16f", + "revision": "4024e28dbc44b11d3297378484c2474dcee425fa", "repo_path": "/integration/gaia-central" } diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index 417594453899..d36dd8c7adae 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -17,8 +17,8 @@ - - + + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index 57d096e1a735..21f84dc9baed 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -15,8 +15,8 @@ - - + + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 1225f594375d..b62ca9587d38 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,8 +17,8 @@ - - + + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index ef0ea35a5d71..7076bfd938d2 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -17,8 +17,8 @@ - - + + diff --git a/dom/apps/src/AppsServiceChild.jsm b/dom/apps/src/AppsServiceChild.jsm index c7cb9890f768..58bb4fee21ae 100644 --- a/dom/apps/src/AppsServiceChild.jsm +++ b/dom/apps/src/AppsServiceChild.jsm @@ -8,10 +8,10 @@ const Cu = Components.utils; const Cc = Components.classes; const Ci = Components.interfaces; -// This module exposes a subset of the functionnalities of the parent DOM -// Registry to content processes, to be be used from the AppsService component. +// This module exposes a subset of the functionalities of the parent DOM +// Registry to content processes, to be used from the AppsService component. -this.EXPORTED_SYMBOLS = ["DOMApplicationRegistry"]; +this.EXPORTED_SYMBOLS = ["DOMApplicationRegistry", "WrappedManifestCache"]; Cu.import("resource://gre/modules/AppsUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); @@ -20,54 +20,324 @@ function debug(s) { //dump("-*- AppsServiceChild.jsm: " + s + "\n"); } +const APPS_IPC_MSG_NAMES = [ + "Webapps:AddApp", + "Webapps:RemoveApp", + "Webapps:UpdateApp", + "Webapps:CheckForUpdate:Return:KO", + "Webapps:FireEvent", + "Webapps:UpdateState" +]; + +// A simple cache for the wrapped manifests. +this.WrappedManifestCache = { + _cache: { }, + + // Gets an entry from the cache, and populates the cache if needed. + get: function mcache_get(aManifestURL, aManifest, aWindow, aInnerWindowID) { + if (!aManifest) { + return; + } + + if (!(aManifestURL in this._cache)) { + this._cache[aManifestURL] = { }; + } + + let winObjs = this._cache[aManifestURL]; + if (!(aInnerWindowID in winObjs)) { + winObjs[aInnerWindowID] = Cu.cloneInto(aManifest, aWindow); + } + + return winObjs[aInnerWindowID]; + }, + + // Invalidates an entry in the cache. + evict: function mcache_evict(aManifestURL, aInnerWindowID) { + debug("Evicting manifest " + aManifestURL + " window ID " + + aInnerWindowID); + if (aManifestURL in this._cache) { + let winObjs = this._cache[aManifestURL]; + if (aInnerWindowID in winObjs) { + delete winObjs[aInnerWindowID]; + } + + if (Object.keys(winObjs).length == 0) { + delete this._cache[aManifestURL]; + } + } + }, + + observe: function(aSubject, aTopic, aData) { + // Clear the cache on memory pressure. + this._cache = { }; + Cu.forceGC(); + }, + + init: function() { + Services.obs.addObserver(this, "memory-pressure", false); + } +}; + +this.WrappedManifestCache.init(); + + +// DOMApplicationRegistry keeps a cache containing a list of apps in the device. +// This information is updated with the data received from the main process and +// it is queried by the DOM objects to set their state. +// This module handle all the messages broadcasted from the parent process, +// including DOM events, which are dispatched to the corresponding DOM objects. + this.DOMApplicationRegistry = { + // DOMApps will hold a list of arrays of weak references to + // mozIDOMApplication objects indexed by manifest URL. + DOMApps: {}, + + ready: false, + webapps: null, + init: function init() { - debug("init"); this.cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"] .getService(Ci.nsISyncMessageSender); - ["Webapps:AddApp", "Webapps:RemoveApp"].forEach((function(aMsgName) { + APPS_IPC_MSG_NAMES.forEach((function(aMsgName) { this.cpmm.addMessageListener(aMsgName, this); }).bind(this)); - // We need to prime the cache with the list of apps. - // XXX shoud we do this async and block callers if it's not yet there? - this.webapps = this.cpmm.sendSyncMessage("Webapps:GetList", { })[0]; + this.cpmm.sendAsyncMessage("Webapps:RegisterForMessages", { + messages: APPS_IPC_MSG_NAMES + }); + // We need to prime the cache with the list of apps. + let list = this.cpmm.sendSyncMessage("Webapps:GetList", { })[0]; + this.webapps = list.webapps; // We need a fast mapping from localId -> app, so we add an index. + // We also add the manifest to the app object. this.localIdIndex = { }; for (let id in this.webapps) { let app = this.webapps[id]; this.localIdIndex[app.localId] = app; + app.manifest = list.manifests[id]; } Services.obs.addObserver(this, "xpcom-shutdown", false); }, observe: function(aSubject, aTopic, aData) { - // cpmm.addMessageListener causes the DOMApplicationRegistry object to live - // forever if we don't clean up properly. + // cpmm.addMessageListener causes the DOMApplicationRegistry object to + // live forever if we don't clean up properly. this.webapps = null; - ["Webapps:AddApp", "Webapps:RemoveApp"].forEach((function(aMsgName) { + this.DOMApps = null; + + APPS_IPC_MSG_NAMES.forEach((aMsgName) => { this.cpmm.removeMessageListener(aMsgName, this); - }).bind(this)); + }); + + this.cpmm.sendAsyncMessage("Webapps:UnregisterForMessages", + APPS_IPC_MSG_NAMES) }, receiveMessage: function receiveMessage(aMessage) { debug("Received " + aMessage.name + " message."); - let msg = aMessage.json; + let msg = aMessage.data; switch (aMessage.name) { case "Webapps:AddApp": this.webapps[msg.id] = msg.app; this.localIdIndex[msg.app.localId] = msg.app; + if (msg.manifest) { + this.webapps[msg.id].manifest = msg.manifest; + } break; case "Webapps:RemoveApp": + delete this.DOMApps[this.webapps[msg.id].manifestURL]; delete this.localIdIndex[this.webapps[msg.id].localId]; delete this.webapps[msg.id]; break; + case "Webapps:UpdateApp": + let app = this.webapps[msg.oldId]; + if (!app) { + return; + } + + if (msg.app) { + for (let prop in msg.app) { + app[prop] = msg.app[prop]; + } + } + + this.webapps[msg.newId] = app; + this.localIdIndex[app.localId] = app; + delete this.webapps[msg.oldId]; + + let apps = this.DOMApps[msg.app.manifestURL]; + if (!apps) { + return; + } + for (let i = 0; i < apps.length; i++) { + let domApp = apps[i].get(); + if (!domApp || domApp._window === null) { + apps.splice(i, 1); + continue; + } + domApp._proxy = new Proxy(domApp, { + get: function(target, prop) { + if (!DOMApplicationRegistry.webapps[msg.newId]) { + return; + } + return DOMApplicationRegistry.webapps[msg.newId][prop]; + }, + set: function(target, prop, val) { + if (!DOMApplicationRegistry.webapps[msg.newId]) { + return; + } + DOMApplicationRegistry.webapps[msg.newId][prop] = val; + return; + }, + }); + } + break; + case "Webapps:FireEvent": + this._fireEvent(aMessage); + break; + case "Webapps:UpdateState": + this._updateState(msg); + break; + case "Webapps:CheckForUpdate:Return:KO": + let DOMApps = this.DOMApps[msg.manifestURL]; + if (!DOMApps || !msg.requestID) { + return; + } + DOMApps.forEach((DOMApp) => { + let domApp = DOMApp.get(); + if (domApp && msg.requestID) { + domApp._fireRequestResult(aMessage, true /* aIsError */); + } + }); + break; } }, + /** + * mozIDOMApplication management + */ + + // Every time a DOM app is created, we save a weak reference to it that will + // be used to dispatch events and fire request results. + addDOMApp: function(aApp, aManifestURL, aId) { + let weakRef = Cu.getWeakReference(aApp); + + if (!this.DOMApps[aManifestURL]) { + this.DOMApps[aManifestURL] = []; + } + + let apps = this.DOMApps[aManifestURL]; + + // Get rid of dead weak references. + for (let i = 0; i < apps.length; i++) { + let app = apps[i].get(); + if (!app || app._window === null) { + apps.splice(i, 1); + } + } + + apps.push(weakRef); + + // Each DOM app contains a proxy object used to build their state. We + // return the handler for this proxy object with traps to get and set + // app properties kept in the DOMApplicationRegistry app cache. + return { + get: function(target, prop) { + if (!DOMApplicationRegistry.webapps[aId]) { + return; + } + return DOMApplicationRegistry.webapps[aId][prop]; + }, + set: function(target, prop, val) { + if (!DOMApplicationRegistry.webapps[aId]) { + return; + } + DOMApplicationRegistry.webapps[aId][prop] = val; + return; + }, + }; + }, + + _fireEvent: function(aMessage) { + let msg = aMessage.data; + debug("_fireEvent " + JSON.stringify(msg)); + if (!this.DOMApps || !msg.manifestURL || !msg.eventType) { + return; + } + + let DOMApps = this.DOMApps[msg.manifestURL]; + if (!DOMApps) { + return; + } + + // The parent might ask childs to trigger more than one event in one + // shot, so in order to avoid needless IPC we allow an array for the + // 'eventType' IPC message field. + if (!Array.isArray(msg.eventType)) { + msg.eventType = [msg.eventType]; + } + + DOMApps.forEach((DOMApp) => { + let domApp = DOMApp.get(); + if (!domApp) { + return; + } + msg.eventType.forEach((aEventType) => { + if ('on' + aEventType in domApp) { + domApp._fireEvent(aEventType); + } + }); + + if (msg.requestID) { + aMessage.data.result = msg.manifestURL; + domApp._fireRequestResult(aMessage); + } + }); + }, + + _updateState: function(aMessage) { + if (!this.DOMApps || !aMessage.id) { + return; + } + + let app = this.webapps[aMessage.id]; + if (!app) { + return; + } + + if (aMessage.app) { + for (let prop in aMessage.app) { + app[prop] = aMessage.app[prop]; + } + } + + if ("error" in aMessage) { + app.downloadError = aMessage.error; + } + + if (aMessage.manifest) { + app.manifest = aMessage.manifest; + // Evict the wrapped manifest cache for all the affected DOM objects. + let DOMApps = this.DOMApps[app.manifestURL]; + if (!DOMApps) { + return; + } + DOMApps.forEach((DOMApp) => { + let domApp = DOMApp.get(); + if (!domApp) { + return; + } + WrappedManifestCache.evict(app.manifestURL, domApp.innerWindowID); + }); + } + }, + + /** + * nsIAppsService API + */ getAppByManifestURL: function getAppByManifestURL(aManifestURL) { debug("getAppByManifestURL " + aManifestURL); return AppsUtils.getAppByManifestURL(this.webapps, aManifestURL); @@ -89,7 +359,7 @@ this.DOMApplicationRegistry = { }, getAppByLocalId: function getAppByLocalId(aLocalId) { - debug("getAppByLocalId " + aLocalId); + debug("getAppByLocalId " + aLocalId + " - ready: " + this.ready); let app = this.localIdIndex[aLocalId]; if (!app) { debug("Ouch, No app!"); diff --git a/dom/apps/src/Webapps.js b/dom/apps/src/Webapps.js index 47a3156159f0..ed2c3226ebac 100644 --- a/dom/apps/src/Webapps.js +++ b/dom/apps/src/Webapps.js @@ -12,6 +12,7 @@ Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); Cu.import("resource://gre/modules/AppsUtils.jsm"); Cu.import("resource://gre/modules/BrowserElementPromptService.jsm"); +Cu.import("resource://gre/modules/AppsServiceChild.jsm"); XPCOMUtils.defineLazyServiceGetter(this, "cpmm", "@mozilla.org/childprocessmessagemanager;1", @@ -278,50 +279,9 @@ WebappsRegistry.prototype = { * mozIDOMApplication object */ -// A simple cache for the wrapped manifests. -let manifestCache = { - _cache: { }, - - // Gets an entry from the cache, and populates the cache if needed. - get: function mcache_get(aManifestURL, aManifest, aWindow, aInnerWindowID) { - if (!(aManifestURL in this._cache)) { - this._cache[aManifestURL] = { }; - } - - let winObjs = this._cache[aManifestURL]; - if (!(aInnerWindowID in winObjs)) { - winObjs[aInnerWindowID] = Cu.cloneInto(aManifest, aWindow); - } - - return winObjs[aInnerWindowID]; - }, - - // Invalidates an entry in the cache. - evict: function mcache_evict(aManifestURL, aInnerWindowID) { - if (aManifestURL in this._cache) { - let winObjs = this._cache[aManifestURL]; - if (aInnerWindowID in winObjs) { - delete winObjs[aInnerWindowID]; - } - - if (Object.keys(winObjs).length == 0) { - delete this._cache[aManifestURL]; - } - } - }, - - observe: function(aSubject, aTopic, aData) { - // Clear the cache on memory pressure. - this._cache = { }; - }, - - init: function() { - Services.obs.addObserver(this, "memory-pressure", false); - } -}; - function createApplicationObject(aWindow, aApp) { - let app = Cc["@mozilla.org/webapps/application;1"].createInstance(Ci.mozIDOMApplication); + let app = Cc["@mozilla.org/webapps/application;1"] + .createInstance(Ci.mozIDOMApplication); app.wrappedJSObject.init(aWindow, aApp); return app; } @@ -334,27 +294,12 @@ WebappsApplication.prototype = { __proto__: DOMRequestIpcHelper.prototype, init: function(aWindow, aApp) { + let proxyHandler = DOMApplicationRegistry.addDOMApp(this, + aApp.manifestURL, + aApp.id); + this._proxy = new Proxy(this, proxyHandler); + this._window = aWindow; - let principal = this._window.document.nodePrincipal; - this._appStatus = principal.appStatus; - this.origin = aApp.origin; - this._manifest = aApp.manifest; - this._updateManifest = aApp.updateManifest; - this.manifestURL = aApp.manifestURL; - this.receipts = aApp.receipts; - this.installOrigin = aApp.installOrigin; - this.installTime = aApp.installTime; - this.installState = aApp.installState || "installed"; - this.removable = aApp.removable; - this.lastUpdateCheck = aApp.lastUpdateCheck ? aApp.lastUpdateCheck - : Date.now(); - this.updateTime = aApp.updateTime ? aApp.updateTime - : aApp.installTime; - this.progress = NaN; - this.downloadAvailable = aApp.downloadAvailable; - this.downloading = aApp.downloading; - this.readyToApplyDownload = aApp.readyToApplyDownload; - this.downloadSize = aApp.downloadSize || 0; this._onprogress = null; this._ondownloadsuccess = null; @@ -362,40 +307,83 @@ WebappsApplication.prototype = { this._ondownloadavailable = null; this._ondownloadapplied = null; - this._downloadError = null; + this.initDOMRequestHelper(aWindow); + }, - this.initDOMRequestHelper(aWindow, [ - { name: "Webapps:CheckForUpdate:Return:KO", weakRef: true }, - { name: "Webapps:Connect:Return:OK", weakRef: true }, - { name: "Webapps:Connect:Return:KO", weakRef: true }, - { name: "Webapps:FireEvent", weakRef: true }, - { name: "Webapps:GetConnections:Return:OK", weakRef: true }, - { name: "Webapps:UpdateState", weakRef: true } - ]); + get _appStatus() { + return this._proxy.appStatus; + }, - cpmm.sendAsyncMessage("Webapps:RegisterForMessages", { - messages: ["Webapps:FireEvent", - "Webapps:UpdateState"], - app: { - id: this.id, - manifestURL: this.manifestURL, - installState: this.installState, - downloading: this.downloading - } - }); + get downloadAvailable() { + return this._proxy.downloadAvailable; + }, + + get downloading() { + return this._proxy.downloading; + }, + + get downloadSize() { + return this._proxy.downloadSize; + }, + + get installOrigin() { + return this._proxy.installOrigin; + }, + + get installState() { + return this._proxy.installState; + }, + + get installTime() { + return this._proxy.installTime; + }, + + get lastUpdateCheck() { + return this._proxy.lastUpdateCheck; + }, + + get manifestURL() { + return this._proxy.manifestURL; + }, + + get origin() { + return this._proxy.origin; + }, + + get progress() { + return this._proxy.progress; + }, + + get readyToApplyDownload() { + return this._proxy.readyToApplyDownload; + }, + + get receipts() { + return this._proxy.receipts; + }, + + set receipts(aReceipts) { + this._proxy.receipts = aReceipts; + }, + + get removable() { + return this._proxy.removable; + }, + + get updateTime() { + return this._proxy.updateTime; }, get manifest() { - return manifestCache.get(this.manifestURL, - this._manifest, - this._window, - this.innerWindowID); + return WrappedManifestCache.get(this.manifestURL, + this._proxy.manifest, + this._window, + this.innerWindowID); }, get updateManifest() { - return this.updateManifest = - this._updateManifest ? Cu.cloneInto(this._updateManifest, this._window) - : null; + return this._proxy.updateManifest ? + Cu.cloneInto(this._proxy.updateManifest, this._window) : null; }, set onprogress(aCallback) { @@ -440,10 +428,10 @@ WebappsApplication.prototype = { get downloadError() { // Only return DOMError when we have an error. - if (!this._downloadError) { + if (!this._proxy.downloadError) { return null; } - return new this._window.DOMError(this._downloadError); + return new this._window.DOMError(this._proxy.downloadError); }, download: function() { @@ -485,12 +473,11 @@ WebappsApplication.prototype = { BrowserElementPromptService.getBrowserElementChildForWindow(this._window); if (browserChild) { this.addMessageListeners("Webapps:ClearBrowserData:Return"); - browserChild.messageManager.sendAsyncMessage( - "Webapps:ClearBrowserData", - { manifestURL: this.manifestURL, - oid: this._id, - requestID: this.getRequestId(request) } - ); + browserChild.messageManager.sendAsyncMessage("Webapps:ClearBrowserData", { + manifestURL: this.manifestURL, + oid: this._id, + requestID: this.getRequestId(request) + }); } else { Services.DOMRequest.fireErrorAsync(request, "NO_CLEARABLE_BROWSER"); } @@ -498,28 +485,33 @@ WebappsApplication.prototype = { }, connect: function(aKeyword, aRules) { + this.addMessageListeners(["Webapps:Connect:Return:OK", + "Webapps:Connect:Return:KO"]); return this.createPromise(function (aResolve, aReject) { - cpmm.sendAsyncMessage("Webapps:Connect", - { keyword: aKeyword, - rules: aRules, - manifestURL: this.manifestURL, - outerWindowID: this._id, - requestID: this.getPromiseResolverId({ - resolve: aResolve, - reject: aReject - })}); + cpmm.sendAsyncMessage("Webapps:Connect", { + keyword: aKeyword, + rules: aRules, + manifestURL: this.manifestURL, + outerWindowID: this._id, + requestID: this.getPromiseResolverId({ + resolve: aResolve, + reject: aReject + }) + }); }.bind(this)); }, getConnections: function() { + this.addMessageListeners("Webapps:GetConnections:Return:OK"); return this.createPromise(function (aResolve, aReject) { - cpmm.sendAsyncMessage("Webapps:GetConnections", - { manifestURL: this.manifestURL, - outerWindowID: this._id, - requestID: this.getPromiseResolverId({ - resolve: aResolve, - reject: aReject - })}); + cpmm.sendAsyncMessage("Webapps:GetConnections", { + manifestURL: this.manifestURL, + outerWindowID: this._id, + requestID: this.getPromiseResolverId({ + resolve: aResolve, + reject: aReject + }) + }); }.bind(this)); }, @@ -568,12 +560,7 @@ WebappsApplication.prototype = { uninit: function() { this._onprogress = null; - cpmm.sendAsyncMessage("Webapps:UnregisterForMessages", [ - "Webapps:FireEvent", - "Webapps:UpdateState" - ]); - - manifestCache.evict(this.manifestURL, this.innerWindowID); + WrappedManifestCache.evict(this.manifestURL, this.innerWindowID); }, _fireEvent: function(aName) { @@ -590,22 +577,16 @@ WebappsApplication.prototype = { } }, - _updateState: function(aMsg) { - if (aMsg.app) { - for (let prop in aMsg.app) { - this[prop] = aMsg.app[prop]; - } + _fireRequestResult: function(aMessage, aIsError) { + let req; + let msg = aMessage.data; + req = this.takeRequest(msg.requestID); + if (!req) { + return; } - // Intentional use of 'in' so we unset the error if this is explicitly null. - if ('error' in aMsg) { - this._downloadError = aMsg.error; - } - - if (aMsg.manifest) { - this._manifest = aMsg.manifest; - manifestCache.evict(this.manifestURL, this.innerWindowID); - } + aIsError ? Services.DOMRequest.fireError(req, msg.error) + : Services.DOMRequest.fireSuccess(req, msg.result); }, receiveMessage: function(aMessage) { @@ -619,10 +600,7 @@ WebappsApplication.prototype = { req = this.takeRequest(msg.requestID); } - // ondownload* callbacks should be triggered on all app instances - if ((msg.oid != this._id || !req) && - aMessage.name !== "Webapps:FireEvent" && - aMessage.name !== "Webapps:UpdateState") { + if (msg.oid !== this._id || !req) { return; } @@ -637,51 +615,13 @@ WebappsApplication.prototype = { "Webapps:Launch:Return:KO"]); Services.DOMRequest.fireSuccess(req, null); break; - case "Webapps:CheckForUpdate:Return:KO": - Services.DOMRequest.fireError(req, msg.error); - break; - case "Webapps:FireEvent": - if (msg.manifestURL != this.manifestURL) { - return; - } - - // The parent might ask childs to trigger more than one event in one - // shot, so in order to avoid needless IPC we allow an array for the - // 'eventType' IPC message field. - if (!Array.isArray(msg.eventType)) { - msg.eventType = [msg.eventType]; - } - - msg.eventType.forEach((aEventType) => { - // If we are in a successful state clear any past errors. - if (aEventType === 'downloadapplied' || - aEventType === 'downloadsuccess') { - this._downloadError = null; - } - - if ("_on" + aEventType in this) { - this._fireEvent(aEventType); - } else { - dump("Unsupported event type " + aEventType + "\n"); - } - }); - - if (req) { - Services.DOMRequest.fireSuccess(req, this.manifestURL); - } - break; - case "Webapps:UpdateState": - if (msg.manifestURL != this.manifestURL) { - return; - } - - this._updateState(msg); - break; case "Webapps:ClearBrowserData:Return": this.removeMessageListeners(aMessage.name); Services.DOMRequest.fireSuccess(req, null); break; case "Webapps:Connect:Return:OK": + this.removeMessageListeners(["Webapps:Connect:Return:OK", + "Webapps:Connect:Return:KO"]); let messagePorts = []; msg.messagePortIDs.forEach((aPortID) => { let port = new this._window.MozInterAppMessagePort(aPortID); @@ -690,9 +630,12 @@ WebappsApplication.prototype = { req.resolve(messagePorts); break; case "Webapps:Connect:Return:KO": + this.removeMessageListeners(["Webapps:Connect:Return:OK", + "Webapps:Connect:Return:KO"]); req.reject("No connections registered"); break; case "Webapps:GetConnections:Return:OK": + this.removeMessageListeners(aMessage.name); let connections = []; msg.connections.forEach((aConnection) => { let connection = @@ -805,7 +748,6 @@ WebappsApplicationMgmt.prototype = { }, uninstall: function(aApp) { - dump("-- webapps.js uninstall " + aApp.manifestURL + "\n"); let request = this.createRequest(); cpmm.sendAsyncMessage("Webapps:Uninstall", { origin: aApp.origin, manifestURL: aApp.manifestURL, @@ -874,12 +816,8 @@ WebappsApplicationMgmt.prototype = { break; case "Webapps:Uninstall:Broadcast:Return:OK": if (this._onuninstall) { - let detail = { - manifestURL: msg.manifestURL, - origin: msg.origin - }; let event = new this._window.MozApplicationEvent("applicationuninstall", - { application : createApplicationObject(this._window, detail) }); + { application : createApplicationObject(this._window, msg) }); this._onuninstall.handleEvent(event); } break; @@ -908,7 +846,5 @@ WebappsApplicationMgmt.prototype = { classDescription: "Webapps Application Mgmt"}) } -manifestCache.init(); - this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry, WebappsApplication]); diff --git a/dom/apps/src/Webapps.jsm b/dom/apps/src/Webapps.jsm index fe57310d272a..2fba41293edd 100755 --- a/dom/apps/src/Webapps.jsm +++ b/dom/apps/src/Webapps.jsm @@ -179,6 +179,7 @@ this.DOMApplicationRegistry = { }).bind(this)); cpmm.addMessageListener("Activities:Register:OK", this); + cpmm.addMessageListener("Activities:Register:KO", this); Services.obs.addObserver(this, "xpcom-shutdown", false); Services.obs.addObserver(this, "memory-pressure", false); @@ -275,9 +276,15 @@ this.DOMApplicationRegistry = { return this._registryStarted.promise; }, + // The registry will be safe to clone when this promise is resolved. + _safeToClone: Promise.defer(), + // Notify we are done with registering apps and save a copy of the registry. _registryReady: Promise.defer(), notifyAppsRegistryReady: function notifyAppsRegistryReady() { + // Usually this promise will be resolved earlier, but just in case, + // resolve it here also. + this._safeToClone.resolve(); this._registryReady.resolve(); Services.obs.notifyObservers(this, "webapps-registry-ready", null); this._saveApps(); @@ -287,6 +294,10 @@ this.DOMApplicationRegistry = { return this._registryReady.promise; }, + get safeToClone() { + return this._safeToClone.promise; + }, + // Ensure that the .to property in redirects is a relative URL. sanitizeRedirects: function sanitizeRedirects(aSource) { if (!aSource) { @@ -962,6 +973,7 @@ this.DOMApplicationRegistry = { this._registerInterAppConnections(manifest, app); appsToRegister.push({ manifest: manifest, app: app }); }); + this._safeToClone.resolve(); this._registerActivitiesForApps(appsToRegister, aRunUpdate); }); }, @@ -1089,88 +1101,114 @@ this.DOMApplicationRegistry = { let mm = aMessage.target; msg.mm = mm; + let processedImmediately = true; + + // There are two kind of messages: the messages that only make sense once the + // registry is ready, and those that can (or have to) be treated as soon as + // they're received. switch (aMessage.name) { - case "Webapps:Install": { -#ifdef MOZ_WIDGET_ANDROID - Services.obs.notifyObservers(mm, "webapps-runtime-install", JSON.stringify(msg)); -#else - this.doInstall(msg, mm); -#endif + case "Activities:Register:KO": + dump("Activities didn't register correctly!"); + case "Activities:Register:OK": + // Activities:Register:OK is special because it's one way the registryReady + // promise can be resolved. + // XXX: What to do when the activities registration failed? At this point + // just act as if nothing happened. + this.notifyAppsRegistryReady(); break; - } - case "Webapps:GetSelf": - this.getSelf(msg, mm); + case "Webapps:GetList": + // GetList is special because it's synchronous. So far so well, it's the + // only synchronous message, if we get more at some point they should get + // this treatment also. + return this.doGetList(); + case "child-process-shutdown": + this.removeMessageListener(["Webapps:Internal:AllMessages"], mm); break; - case "Webapps:Uninstall": -#ifdef MOZ_WIDGET_ANDROID - Services.obs.notifyObservers(mm, "webapps-runtime-uninstall", JSON.stringify(msg)); -#else - this.doUninstall(msg, mm); -#endif - break; - case "Webapps:Launch": - this.doLaunch(msg, mm); - break; - case "Webapps:CheckInstalled": - this.checkInstalled(msg, mm); - break; - case "Webapps:GetInstalled": - this.getInstalled(msg, mm); - break; - case "Webapps:GetNotInstalled": - this.getNotInstalled(msg, mm); - break; - case "Webapps:GetAll": - this.doGetAll(msg, mm); - break; - case "Webapps:InstallPackage": { -#ifdef MOZ_WIDGET_ANDROID - Services.obs.notifyObservers(mm, "webapps-runtime-install-package", JSON.stringify(msg)); -#else - this.doInstallPackage(msg, mm); -#endif - break; - } case "Webapps:RegisterForMessages": this.addMessageListener(msg.messages, msg.app, mm); break; case "Webapps:UnregisterForMessages": this.removeMessageListener(msg, mm); break; - case "child-process-shutdown": - this.removeMessageListener(["Webapps:Internal:AllMessages"], mm); - break; - case "Webapps:GetList": - this.addMessageListener(["Webapps:AddApp", "Webapps:RemoveApp"], null, mm); - return this.webapps; - case "Webapps:Download": - this.startDownload(msg.manifestURL); - break; - case "Webapps:CancelDownload": - this.cancelDownload(msg.manifestURL); - break; - case "Webapps:CheckForUpdate": - this.checkForUpdate(msg, mm); - break; - case "Webapps:ApplyDownload": - this.applyDownload(msg.manifestURL); - break; - case "Activities:Register:OK": - this.notifyAppsRegistryReady(); - break; - case "Webapps:Install:Return:Ack": - this.onInstallSuccessAck(msg.manifestURL); - break; - case "Webapps:AddReceipt": - this.addReceipt(msg, mm); - break; - case "Webapps:RemoveReceipt": - this.removeReceipt(msg, mm); - break; - case "Webapps:ReplaceReceipt": - this.replaceReceipt(msg, mm); - break; + default: + processedImmediately = false; } + + if (processedImmediately) { + return; + } + + // For all the rest (asynchronous), we wait till the registry is ready + // before processing the message. + this.registryReady.then( () => { + switch (aMessage.name) { + case "Webapps:Install": { +#ifdef MOZ_WIDGET_ANDROID + Services.obs.notifyObservers(mm, "webapps-runtime-install", JSON.stringify(msg)); +#else + this.doInstall(msg, mm); +#endif + break; + } + case "Webapps:GetSelf": + this.getSelf(msg, mm); + break; + case "Webapps:Uninstall": +#ifdef MOZ_WIDGET_ANDROID + Services.obs.notifyObservers(mm, "webapps-runtime-uninstall", JSON.stringify(msg)); +#else + this.doUninstall(msg, mm); +#endif + break; + case "Webapps:Launch": + this.doLaunch(msg, mm); + break; + case "Webapps:CheckInstalled": + this.checkInstalled(msg, mm); + break; + case "Webapps:GetInstalled": + this.getInstalled(msg, mm); + break; + case "Webapps:GetNotInstalled": + this.getNotInstalled(msg, mm); + break; + case "Webapps:GetAll": + this.doGetAll(msg, mm); + break; + case "Webapps:InstallPackage": { +#ifdef MOZ_WIDGET_ANDROID + Services.obs.notifyObservers(mm, "webapps-runtime-install-package", JSON.stringify(msg)); +#else + this.doInstallPackage(msg, mm); +#endif + break; + } + case "Webapps:Download": + this.startDownload(msg.manifestURL); + break; + case "Webapps:CancelDownload": + this.cancelDownload(msg.manifestURL); + break; + case "Webapps:CheckForUpdate": + this.checkForUpdate(msg, mm); + break; + case "Webapps:ApplyDownload": + this.applyDownload(msg.manifestURL); + break; + case "Webapps:Install:Return:Ack": + this.onInstallSuccessAck(msg.manifestURL); + break; + case "Webapps:AddReceipt": + this.addReceipt(msg, mm); + break; + case "Webapps:RemoveReceipt": + this.removeReceipt(msg, mm); + break; + case "Webapps:ReplaceReceipt": + this.replaceReceipt(msg, mm); + break; + } + }); }, getAppInfo: function getAppInfo(aAppId) { @@ -1245,6 +1283,38 @@ this.DOMApplicationRegistry = { return deferred.promise; }, + /** + * Returns the full list of apps and manifests. + */ + doGetList: function() { + let tmp = []; + + let res = {}; + let done = false; + + // We allow cloning the registry when the local processing has been done. + this.safeToClone.then( () => { + for (let id in this.webapps) { + tmp.push({ id: id }); + } + this._readManifests(tmp).then( + function(manifests) { + manifests.forEach((item) => { + res[item.id] = item.manifest; + }); + done = true; + } + ); + }); + + let thread = Services.tm.currentThread; + while (!done) { + thread.processNextEvent(/* mayWait */ true); + } + return { webapps: this.webapps, manifests: res }; + }, + + doLaunch: function (aData, aMm) { this.launch( aData.manifestURL, @@ -1330,7 +1400,7 @@ this.DOMApplicationRegistry = { downloading: false }, error: error, - manifestURL: app.manifestURL, + id: app.id }) this.broadcastMessage("Webapps:FireEvent", { eventType: "downloaderror", @@ -1361,7 +1431,7 @@ this.DOMApplicationRegistry = { if (!app.downloadAvailable) { this.broadcastMessage("Webapps:UpdateState", { error: "NO_DOWNLOAD_AVAILABLE", - manifestURL: app.manifestURL + id: app.id }); this.broadcastMessage("Webapps:FireEvent", { eventType: "downloaderror", @@ -1409,7 +1479,7 @@ this.DOMApplicationRegistry = { this.broadcastMessage("Webapps:UpdateState", { app: app, manifest: jsonManifest, - manifestURL: aManifestURL + id: app.id }); this.broadcastMessage("Webapps:FireEvent", { eventType: "downloadsuccess", @@ -1463,7 +1533,7 @@ this.DOMApplicationRegistry = { this.broadcastMessage("Webapps:UpdateState", { app: app, - manifestURL: aManifestURL + id: app.id }); this.broadcastMessage("Webapps:FireEvent", { eventType: "downloadsuccess", @@ -1565,7 +1635,7 @@ this.DOMApplicationRegistry = { this.broadcastMessage("Webapps:UpdateState", { app: app, manifest: newManifest, - manifestURL: app.manifestURL + id: app.id }); this.broadcastMessage("Webapps:FireEvent", { eventType: "downloadapplied", @@ -1604,7 +1674,7 @@ this.DOMApplicationRegistry = { installState: aApp.installState, progress: 0 }, - manifestURL: aApp.manifestURL + id: aApp.id }); let cacheUpdate = updateSvc.scheduleAppUpdate( appcacheURI, docURI, aApp.localId, false, aProfileDir); @@ -1654,6 +1724,7 @@ this.DOMApplicationRegistry = { debug("checkForUpdate for " + aData.manifestURL); function sendError(aError) { + debug("checkForUpdate error " + aError); aData.error = aError; aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData); } @@ -1683,8 +1754,7 @@ this.DOMApplicationRegistry = { // then we can't have an update. if (app.origin.startsWith("app://") && app.manifestURL.startsWith("app://")) { - aData.error = "NOT_UPDATABLE"; - aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData); + sendError("NOT_UPDATABLE"); return; } @@ -1701,8 +1771,7 @@ this.DOMApplicationRegistry = { if (onlyCheckAppCache) { // Bail out for packaged apps. if (app.origin.startsWith("app://")) { - aData.error = "NOT_UPDATABLE"; - aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData); + sendError("NOT_UPDATABLE"); return; } @@ -1710,8 +1779,7 @@ this.DOMApplicationRegistry = { this._readManifests([{ id: id }]).then((aResult) => { let manifest = aResult[0].manifest; if (!manifest.appcache_path) { - aData.error = "NOT_UPDATABLE"; - aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData); + sendError("NOT_UPDATABLE"); return; } @@ -1727,7 +1795,7 @@ this.DOMApplicationRegistry = { this._saveApps().then(() => { this.broadcastMessage("Webapps:UpdateState", { app: app, - manifestURL: app.manifestURL + id: app.id }); this.broadcastMessage("Webapps:FireEvent", { eventType: "downloadavailable", @@ -1736,8 +1804,7 @@ this.DOMApplicationRegistry = { }); }); } else { - aData.error = "NOT_UPDATABLE"; - aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData); + sendError("NOT_UPDATABLE"); } } }; @@ -1797,7 +1864,7 @@ this.DOMApplicationRegistry = { : "downloadapplied"; aMm.sendAsyncMessage("Webapps:UpdateState", { app: app, - manifestURL: app.manifestURL + id: app.id }); aMm.sendAsyncMessage("Webapps:FireEvent", { eventType: eventType, @@ -1824,7 +1891,7 @@ this.DOMApplicationRegistry = { : "downloadapplied"; aMm.sendAsyncMessage("Webapps:UpdateState", { app: app, - manifestURL: app.manifestURL + id: app.id }); aMm.sendAsyncMessage("Webapps:FireEvent", { eventType: eventType, @@ -1933,7 +2000,7 @@ this.DOMApplicationRegistry = { this.broadcastMessage("Webapps:UpdateState", { app: aApp, - manifestURL: aApp.manifestURL + id: aApp.id }); this.broadcastMessage("Webapps:FireEvent", { eventType: "downloadavailable", @@ -1999,7 +2066,7 @@ this.DOMApplicationRegistry = { this.broadcastMessage("Webapps:UpdateState", { app: aApp, manifest: aApp.manifest, - manifestURL: aApp.manifestURL + id: aApp.id }); this.broadcastMessage("Webapps:FireEvent", { eventType: "downloadapplied", @@ -2033,7 +2100,7 @@ this.DOMApplicationRegistry = { this.broadcastMessage("Webapps:UpdateState", { app: aApp, manifest: aApp.manifest, - manifestURL: aApp.manifestURL + id: aApp.id }); this.broadcastMessage("Webapps:FireEvent", { eventType: eventType, @@ -2464,7 +2531,8 @@ this.DOMApplicationRegistry = { } this._saveApps().then(() => { - this.broadcastMessage("Webapps:AddApp", { id: app.id, app: app }); + this.broadcastMessage("Webapps:AddApp", + { id: app.id, app: app, manifest: aManifest }); }); }), @@ -2564,6 +2632,8 @@ this.DOMApplicationRegistry = { // saved in the registry. yield this._saveApps(); + aData.isPackage ? appObject.updateManifest = jsonManifest : + appObject.manifest = jsonManifest; this.broadcastMessage("Webapps:AddApp", { id: id, app: appObject }); if (!aData.isPackage) { @@ -2646,7 +2716,8 @@ this.DOMApplicationRegistry = { delete this._manifestCache[aId]; } - this.broadcastMessage("Webapps:AddApp", { id: aId, app: aNewApp }); + this.broadcastMessage("Webapps:AddApp", + { id: aId, app: aNewApp, manifest: aManifest }); Services.obs.notifyObservers(null, "webapps-installed", JSON.stringify({ manifestURL: aNewApp.manifestURL })); @@ -2806,7 +2877,7 @@ this.DOMApplicationRegistry = { // Clear any previous download errors. error: null, app: aOldApp, - manifestURL: aNewApp.manifestURL + id: aId }); let zipFile = yield this._getPackage(requestChannel, aId, aOldApp, aNewApp); @@ -2821,7 +2892,7 @@ this.DOMApplicationRegistry = { // We send an "applied" event right away so code awaiting that event // can proceed to access the app. We also throw an error to alert // the caller that the package wasn't downloaded. - this._sendAppliedEvent(aNewApp, aOldApp, aId); + this._sendAppliedEvent(aOldApp); throw new Error("PACKAGE_UNCHANGED"); } @@ -2957,7 +3028,7 @@ this.DOMApplicationRegistry = { app: { progress: aProgress }, - manifestURL: aNewApp.manifestURL + id: aNewApp.id }); this.broadcastMessage("Webapps:FireEvent", { eventType: "progress", @@ -3074,27 +3145,24 @@ this.DOMApplicationRegistry = { * something similar after updating the app, and we could refactor both cases * to use the same code to send the "applied" event. * - * @param aNewApp {Object} the new app data - * @param aOldApp {Object} the currently stored app data - * @param aId {String} the unique id of the app + * @param aApp {Object} app data */ - _sendAppliedEvent: function(aNewApp, aOldApp, aId) { - aOldApp.downloading = false; - aOldApp.downloadAvailable = false; - aOldApp.downloadSize = 0; - aOldApp.installState = "installed"; - aOldApp.readyToApplyDownload = false; - if (aOldApp.staged && aOldApp.staged.manifestHash) { + _sendAppliedEvent: function(aApp) { + aApp.downloading = false; + aApp.downloadAvailable = false; + aApp.downloadSize = 0; + aApp.installState = "installed"; + aApp.readyToApplyDownload = false; + if (aApp.staged && aApp.staged.manifestHash) { // If we're here then the manifest has changed but the package // hasn't. Let's clear this, so we don't keep offering // a bogus update to the user - aOldApp.manifestHash = aOldApp.staged.manifestHash; - aOldApp.etag = aOldApp.staged.etag || aOldApp.etag; - aOldApp.staged = {}; - - // Move the staged update manifest to a non staged one. + aApp.manifestHash = aApp.staged.manifestHash; + aApp.etag = aApp.staged.etag || aApp.etag; + aApp.staged = {}; + // Move the staged update manifest to a non staged one. try { - let staged = this._getAppDir(aId); + let staged = this._getAppDir(aApp.id); staged.append("staged-update.webapp"); staged.moveTo(staged.parent, "update.webapp"); } catch (ex) { @@ -3105,15 +3173,15 @@ this.DOMApplicationRegistry = { // Save the updated registry, and cleanup the tmp directory. this._saveApps().then(() => { this.broadcastMessage("Webapps:UpdateState", { - app: aOldApp, - manifestURL: aNewApp.manifestURL + app: aApp, + id: aApp.id }); this.broadcastMessage("Webapps:FireEvent", { - manifestURL: aNewApp.manifestURL, + manifestURL: aApp.manifestURL, eventType: ["downloadsuccess", "downloadapplied"] }); }); - let file = FileUtils.getFile("TmpD", ["webapps", aId], false); + let file = FileUtils.getFile("TmpD", ["webapps", aApp.id], false); if (file && file.exists()) { file.remove(true); } @@ -3432,9 +3500,10 @@ this.DOMApplicationRegistry = { dir.moveTo(parent, newId); }); // Signals that we need to swap the old id with the new app. - this.broadcastMessage("Webapps:RemoveApp", { id: oldId }); - this.broadcastMessage("Webapps:AddApp", { id: newId, - app: aOldApp }); + this.broadcastMessage("Webapps:UpdateApp", { oldId: oldId, + newId: newId, + app: aOldApp }); + } } }, @@ -3537,7 +3606,7 @@ this.DOMApplicationRegistry = { this.broadcastMessage("Webapps:UpdateState", { app: aOldApp, error: aError, - manifestURL: aNewApp.manifestURL + id: aNewApp.id }); this.broadcastMessage("Webapps:FireEvent", { eventType: "downloaderror", @@ -3717,9 +3786,13 @@ this.DOMApplicationRegistry = { }, doGetAll: function(aData, aMm) { - this.getAll(function (apps) { - aData.apps = apps; - aMm.sendAsyncMessage("Webapps:GetAll:Return:OK", aData); + // We can't do this until the registry is ready. + debug("doGetAll"); + this.registryReady.then(() => { + this.getAll(function (apps) { + aData.apps = apps; + aMm.sendAsyncMessage("Webapps:GetAll:Return:OK", aData); + }); }); }, @@ -4087,7 +4160,7 @@ AppcacheObserver.prototype = { let app = this.app; DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", { app: app, - manifestURL: app.manifestURL + id: app.id }); DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", { eventType: "progress", @@ -4119,7 +4192,7 @@ AppcacheObserver.prototype = { app.downloadAvailable = false; DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", { app: app, - manifestURL: app.manifestURL + id: app.id }); DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", { eventType: ["downloadsuccess", "downloadapplied"], @@ -4142,7 +4215,7 @@ AppcacheObserver.prototype = { DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", { app: app, error: aError, - manifestURL: app.manifestURL + id: app.id }); DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", { eventType: "downloaderror", diff --git a/dom/apps/tests/test_packaged_app_common.js b/dom/apps/tests/test_packaged_app_common.js index abcdeacf6008..449aa513d78c 100644 --- a/dom/apps/tests/test_packaged_app_common.js +++ b/dom/apps/tests/test_packaged_app_common.js @@ -102,6 +102,7 @@ var PackagedTestHelper = (function PackagedTestHelper() { var aApp = evt.application; aApp.ondownloaderror = function(evt) { var error = aApp.downloadError.name; + ok(true, "Got downloaderror " + error); if (error == aExpectedError) { ok(true, "Got expected " + aExpectedError); var expected = { diff --git a/dom/apps/tests/test_packaged_app_update.html b/dom/apps/tests/test_packaged_app_update.html index 1278e767ca48..3b1fc9957750 100644 --- a/dom/apps/tests/test_packaged_app_update.html +++ b/dom/apps/tests/test_packaged_app_update.html @@ -79,15 +79,15 @@ function updateApp(aExpectedReady, aPreviousVersion, aNextVersion) { checkLastAppState.bind(PackagedTestHelper, miniManifestURL, false, false, aNextVersion, PackagedTestHelper.next); - var ondownloadsuccesshandler = - checkLastAppState.bind(undefined, miniManifestURL, - aExpectedReady, false, aPreviousVersion, - function() { - navigator.mozApps.mgmt.applyDownload(lApp); - }); + var ondownloadsuccesshandler = + checkLastAppState.bind(undefined, miniManifestURL, + aExpectedReady, false, aPreviousVersion, + function() { + navigator.mozApps.mgmt.applyDownload(lApp); + }); - checkForUpdate(true, ondownloadsuccesshandler, ondownloadappliedhandler, null, - true); + checkForUpdate(true, ondownloadsuccesshandler, ondownloadappliedhandler, + null, true); } @@ -254,7 +254,7 @@ var steps = [ "&appName=arandomname" + "&appToFail1"; PackagedTestHelper.checkAppDownloadError(miniManifestURL, - "MANIFEST_MISMATCH", 2, false, true, + "MANIFEST_MISMATCH", 1, false, true, "arandomname", function () { checkForUpdate(false, null, null, null, false, diff --git a/dom/apps/tests/test_receipt_operations.html b/dom/apps/tests/test_receipt_operations.html index 0907e8d964c4..7ebe84ccfae0 100644 --- a/dom/apps/tests/test_receipt_operations.html +++ b/dom/apps/tests/test_receipt_operations.html @@ -243,4 +243,4 @@ addLoadEvent(go); - \ No newline at end of file + diff --git a/dom/audiochannel/AudioChannelService.cpp b/dom/audiochannel/AudioChannelService.cpp index 3b2d8c57456d..99ab9d6c782e 100644 --- a/dom/audiochannel/AudioChannelService.cpp +++ b/dom/audiochannel/AudioChannelService.cpp @@ -586,14 +586,18 @@ AudioChannelService::SendAudioChannelChangedNotification(uint64_t aChildID) kMozAudioChannelAttributeTable[index].value > higher && kMozAudioChannelAttributeTable[index].value > (int16_t)AudioChannel::Normal; --index) { - if (kMozAudioChannelAttributeTable[index].value == (int16_t)AudioChannel::Content && - mPlayableHiddenContentChildID != CONTENT_PROCESS_ID_UNKNOWN) { - higher = kMozAudioChannelAttributeTable[index].value; - } - // Each channel type will be split to fg and bg for recording the state, // so here need to do a translation. - if (!mChannelCounters[index * 2 + 1].IsEmpty()) { + if (mChannelCounters[index * 2 + 1].IsEmpty()) { + continue; + } + + if (kMozAudioChannelAttributeTable[index].value == (int16_t)AudioChannel::Content) { + if (mPlayableHiddenContentChildID != CONTENT_PROCESS_ID_UNKNOWN) { + higher = kMozAudioChannelAttributeTable[index].value; + break; + } + } else { higher = kMozAudioChannelAttributeTable[index].value; break; } diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 4946696fbfca..289b88cf6b8d 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -166,20 +166,24 @@ DOMInterfaces = { 'nativeType': 'mozilla::dom::bluetooth::BluetoothAdapter', }, -'BluetoothDevice': { - 'nativeType': 'mozilla::dom::bluetooth::BluetoothDevice', +'BluetoothClassOfDevice': { + 'nativeType': 'mozilla::dom::bluetooth::BluetoothClassOfDevice', }, -'BluetoothManager': { - 'nativeType': 'mozilla::dom::bluetooth::BluetoothManager', +'BluetoothDevice': { + 'nativeType': 'mozilla::dom::bluetooth::BluetoothDevice', }, 'BluetoothDiscoveryHandle': { 'nativeType': 'mozilla::dom::bluetooth::BluetoothDiscoveryHandle', }, -'BluetoothClassOfDevice': { - 'nativeType': 'mozilla::dom::bluetooth::BluetoothClassOfDevice', +'BluetoothManager': { + 'nativeType': 'mozilla::dom::bluetooth::BluetoothManager', +}, + +'BluetoothPairingHandle': { + 'nativeType': 'mozilla::dom::bluetooth::BluetoothPairingHandle', }, 'CameraCapabilities': { diff --git a/dom/bluetooth/bluedroid/hfp-fallback/BluetoothHfpManager.cpp b/dom/bluetooth/bluedroid/hfp-fallback/BluetoothHfpManager.cpp index abef9b40f717..00101e13cc98 100644 --- a/dom/bluetooth/bluedroid/hfp-fallback/BluetoothHfpManager.cpp +++ b/dom/bluetooth/bluedroid/hfp-fallback/BluetoothHfpManager.cpp @@ -148,7 +148,7 @@ BluetoothHfpManager::Init() // static void -BluetoothHfpManager::InitHfpInterface() +BluetoothHfpManager::InitHfpInterface(BluetoothProfileResultHandler* aRes) { MOZ_ASSERT(NS_IsMainThread()); @@ -157,11 +157,15 @@ BluetoothHfpManager::InitHfpInterface() * Implement InitHfpInterface() for applications that want to create SCO * link without a HFP connection (e.g., VoIP). */ + + if (aRes) { + aRes->Init(); + } } // static void -BluetoothHfpManager::DeinitHfpInterface() +BluetoothHfpManager::DeinitHfpInterface(BluetoothProfileResultHandler* aRes) { MOZ_ASSERT(NS_IsMainThread()); @@ -170,6 +174,10 @@ BluetoothHfpManager::DeinitHfpInterface() * Implement DeinitHfpInterface() for applications that want to create SCO * link without a HFP connection (e.g., VoIP). */ + + if (aRes) { + aRes->Deinit(); + } } void diff --git a/dom/bluetooth/bluedroid/hfp-fallback/BluetoothHfpManager.h b/dom/bluetooth/bluedroid/hfp-fallback/BluetoothHfpManager.h index 12ff8a9ba23c..ad82a3515347 100644 --- a/dom/bluetooth/bluedroid/hfp-fallback/BluetoothHfpManager.h +++ b/dom/bluetooth/bluedroid/hfp-fallback/BluetoothHfpManager.h @@ -30,8 +30,8 @@ public: static BluetoothHfpManager* Get(); virtual ~BluetoothHfpManager() { } - static void InitHfpInterface(); - static void DeinitHfpInterface(); + static void InitHfpInterface(BluetoothProfileResultHandler* aRes); + static void DeinitHfpInterface(BluetoothProfileResultHandler* aRes); bool ConnectSco(); bool DisconnectSco(); diff --git a/dom/bluetooth2/BluetoothAdapter.cpp b/dom/bluetooth2/BluetoothAdapter.cpp index d97bb7409441..b70d6db22e73 100644 --- a/dom/bluetooth2/BluetoothAdapter.cpp +++ b/dom/bluetooth2/BluetoothAdapter.cpp @@ -407,10 +407,9 @@ BluetoothAdapter::StartDiscovery(ErrorResult& aRv) aRv.Throw(NS_ERROR_FAILURE); return nullptr; } + nsRefPtr promise = Promise::Create(global, aRv); - if (aRv.Failed()) { - return nullptr; - } + NS_ENSURE_TRUE(!aRv.Failed(), nullptr); /** * Ensure @@ -451,10 +450,9 @@ BluetoothAdapter::StopDiscovery(ErrorResult& aRv) aRv.Throw(NS_ERROR_FAILURE); return nullptr; } + nsRefPtr promise = Promise::Create(global, aRv); - if (aRv.Failed()) { - return nullptr; - } + NS_ENSURE_TRUE(!aRv.Failed(), nullptr); /** * Ensure @@ -484,14 +482,13 @@ already_AddRefed BluetoothAdapter::SetName(const nsAString& aName, ErrorResult& aRv) { nsCOMPtr global = do_QueryInterface(GetOwner()); - if(!global) { + if (!global) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } + nsRefPtr promise = Promise::Create(global, aRv); - if (aRv.Failed()) { - return nullptr; - } + NS_ENSURE_TRUE(!aRv.Failed(), nullptr); /** * Ensure @@ -525,14 +522,13 @@ already_AddRefed BluetoothAdapter::SetDiscoverable(bool aDiscoverable, ErrorResult& aRv) { nsCOMPtr global = do_QueryInterface(GetOwner()); - if(!global) { + if (!global) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } + nsRefPtr promise = Promise::Create(global, aRv); - if (aRv.Failed()) { - return nullptr; - } + NS_ENSURE_TRUE(!aRv.Failed(), nullptr); /** * Ensure @@ -646,104 +642,17 @@ BluetoothAdapter::Unpair(const nsAString& aDeviceAddress, ErrorResult& aRv) return PairUnpair(false, aDeviceAddress, aRv); } -already_AddRefed -BluetoothAdapter::SetPinCode(const nsAString& aDeviceAddress, - const nsAString& aPinCode, ErrorResult& aRv) -{ - nsCOMPtr win = GetOwner(); - if (!win) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - nsRefPtr request = new DOMRequest(win); - nsRefPtr results = - new BluetoothVoidReplyRunnable(request); - - BluetoothService* bs = BluetoothService::Get(); - if (!bs) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - if (!bs->SetPinCodeInternal(aDeviceAddress, aPinCode, results)) { - BT_WARNING("SetPinCode failed!"); - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - return request.forget(); -} - -already_AddRefed -BluetoothAdapter::SetPasskey(const nsAString& aDeviceAddress, uint32_t aPasskey, - ErrorResult& aRv) -{ - nsCOMPtr win = GetOwner(); - if (!win) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - nsRefPtr request = new DOMRequest(win); - nsRefPtr results = - new BluetoothVoidReplyRunnable(request); - - BluetoothService* bs = BluetoothService::Get(); - if (!bs) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - if (bs->SetPasskeyInternal(aDeviceAddress, aPasskey, results)) { - BT_WARNING("SetPasskeyInternal failed!"); - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - return request.forget(); -} - -already_AddRefed -BluetoothAdapter::SetPairingConfirmation(const nsAString& aDeviceAddress, - bool aConfirmation, ErrorResult& aRv) -{ - nsCOMPtr win = GetOwner(); - if (!win) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - nsRefPtr request = new DOMRequest(win); - nsRefPtr results = - new BluetoothVoidReplyRunnable(request); - - BluetoothService* bs = BluetoothService::Get(); - if (!bs) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - if (!bs->SetPairingConfirmationInternal(aDeviceAddress, - aConfirmation, - results)) { - BT_WARNING("SetPairingConfirmation failed!"); - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - return request.forget(); -} - already_AddRefed BluetoothAdapter::EnableDisable(bool aEnable, ErrorResult& aRv) { nsCOMPtr global = do_QueryInterface(GetOwner()); - if(!global) { + if (!global) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } + nsRefPtr promise = Promise::Create(global, aRv); - if (aRv.Failed()) { - return nullptr; - } + NS_ENSURE_TRUE(!aRv.Failed(), nullptr); // Ensure BluetoothService is available before modifying adapter state BluetoothService* bs = BluetoothService::Get(); diff --git a/dom/bluetooth2/BluetoothAdapter.h b/dom/bluetooth2/BluetoothAdapter.h index 08e4ec951239..b72d231c6121 100644 --- a/dom/bluetooth2/BluetoothAdapter.h +++ b/dom/bluetooth2/BluetoothAdapter.h @@ -99,18 +99,6 @@ public: Unpair(const nsAString& aDeviceAddress, ErrorResult& aRv); already_AddRefed GetPairedDevices(ErrorResult& aRv); - already_AddRefed - SetPinCode(const nsAString& aDeviceAddress, const nsAString& aPinCode, - ErrorResult& aRv); - already_AddRefed - SetPasskey(const nsAString& aDeviceAddress, uint32_t aPasskey, - ErrorResult& aRv); - already_AddRefed - SetPairingConfirmation(const nsAString& aDeviceAddress, bool aConfirmation, - ErrorResult& aRv); - already_AddRefed - SetAuthorization(const nsAString& aDeviceAddress, bool aAllow, - ErrorResult& aRv); already_AddRefed EnableDisable(bool aEnable, ErrorResult& aRv); already_AddRefed Enable(ErrorResult& aRv); diff --git a/dom/bluetooth2/BluetoothCommon.h b/dom/bluetooth2/BluetoothCommon.h index e8b122ce8a57..f4d56736255b 100644 --- a/dom/bluetooth2/BluetoothCommon.h +++ b/dom/bluetooth2/BluetoothCommon.h @@ -148,6 +148,15 @@ extern bool gBluetoothDebugFlag; #define HFP_STATUS_CHANGED_ID "hfpstatuschanged" #define SCO_STATUS_CHANGED_ID "scostatuschanged" +/** + * Types of pairing requests for constructing BluetoothPairingEvent and + * BluetoothPairingHandle. + */ +#define PAIRING_REQ_TYPE_DISPLAYPASSKEY "displaypasskeyreq" +#define PAIRING_REQ_TYPE_ENTERPINCODE "enterpincodereq" +#define PAIRING_REQ_TYPE_CONFIRMATION "pairingconfirmationreq" +#define PAIRING_REQ_TYPE_CONSENT "pairingconsentreq" + /** * When the pair status of a Bluetooth device is changed, we'll dispatch an * event. diff --git a/dom/bluetooth2/BluetoothDevice.cpp b/dom/bluetooth2/BluetoothDevice.cpp index 91abb3ac3a6c..3cfd3e4b7b62 100644 --- a/dom/bluetooth2/BluetoothDevice.cpp +++ b/dom/bluetooth2/BluetoothDevice.cpp @@ -144,9 +144,7 @@ BluetoothDevice::FetchUuids(ErrorResult& aRv) } nsRefPtr promise = Promise::Create(global, aRv); - if (aRv.Failed()) { - return nullptr; - } + NS_ENSURE_TRUE(!aRv.Failed(), nullptr); BluetoothService* bs = BluetoothService::Get(); BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE); diff --git a/dom/bluetooth2/BluetoothPairingHandle.cpp b/dom/bluetooth2/BluetoothPairingHandle.cpp new file mode 100644 index 000000000000..7412c554af08 --- /dev/null +++ b/dom/bluetooth2/BluetoothPairingHandle.cpp @@ -0,0 +1,129 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "BluetoothCommon.h" +#include "BluetoothDevice.h" +#include "BluetoothPairingHandle.h" +#include "BluetoothReplyRunnable.h" +#include "BluetoothService.h" + +#include "mozilla/dom/BluetoothPairingHandleBinding.h" +#include "mozilla/dom/Promise.h" + +using namespace mozilla; +using namespace dom; + +USING_BLUETOOTH_NAMESPACE + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(BluetoothPairingHandle, mOwner) +NS_IMPL_CYCLE_COLLECTING_ADDREF(BluetoothPairingHandle) +NS_IMPL_CYCLE_COLLECTING_RELEASE(BluetoothPairingHandle) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BluetoothPairingHandle) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +BluetoothPairingHandle::BluetoothPairingHandle(nsPIDOMWindow* aOwner, + const nsAString& aDeviceAddress, + const nsAString& aType, + const nsAString& aPasskey) + : mOwner(aOwner) + , mDeviceAddress(aDeviceAddress) + , mType(aType) + , mPasskey(aPasskey) +{ + MOZ_ASSERT(aOwner && !aDeviceAddress.IsEmpty() && !aType.IsEmpty()); + + if (aType.EqualsLiteral(PAIRING_REQ_TYPE_DISPLAYPASSKEY) || + aType.EqualsLiteral(PAIRING_REQ_TYPE_CONFIRMATION)) { + MOZ_ASSERT(!aPasskey.IsEmpty()); + } else { + MOZ_ASSERT(aPasskey.IsEmpty()); + } + + SetIsDOMBinding(); +} + +BluetoothPairingHandle::~BluetoothPairingHandle() +{ +} + +already_AddRefed +BluetoothPairingHandle::Create(nsPIDOMWindow* aOwner, + const nsAString& aDeviceAddress, + const nsAString& aType, + const nsAString& aPasskey) +{ + MOZ_ASSERT(aOwner && !aDeviceAddress.IsEmpty() && !aType.IsEmpty()); + + nsRefPtr handle = + new BluetoothPairingHandle(aOwner, aDeviceAddress, aType, aPasskey); + + return handle.forget(); +} + +already_AddRefed +BluetoothPairingHandle::SetPinCode(const nsAString& aPinCode, ErrorResult& aRv) +{ + nsCOMPtr global = do_QueryInterface(GetParentObject()); + if (!global) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr promise = Promise::Create(global, aRv); + NS_ENSURE_TRUE(!aRv.Failed(), nullptr); + + BT_ENSURE_TRUE_REJECT(mType.EqualsLiteral("enterpincodereq"), + NS_ERROR_DOM_INVALID_STATE_ERR); + + BluetoothService* bs = BluetoothService::Get(); + BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE); + + nsRefPtr result = + new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */, + promise, + NS_LITERAL_STRING("SetPinCode")); + bs->SetPinCodeInternal(mDeviceAddress, aPinCode, result); + + return promise.forget(); +} + +already_AddRefed +BluetoothPairingHandle::SetPairingConfirmation(bool aConfirm, ErrorResult& aRv) +{ + nsCOMPtr global = do_QueryInterface(GetParentObject()); + if (!global) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr promise = Promise::Create(global, aRv); + NS_ENSURE_TRUE(!aRv.Failed(), nullptr); + + BT_ENSURE_TRUE_REJECT(mType.EqualsLiteral("pairingconfirmationreq"), + NS_ERROR_DOM_INVALID_STATE_ERR); + + BluetoothService* bs = BluetoothService::Get(); + BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE); + + nsRefPtr result = + new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */, + promise, + NS_LITERAL_STRING( + "SetPairingConfirmation")); + + bs->SetPairingConfirmationInternal(mDeviceAddress, + aConfirm, + result); + return promise.forget(); +} + +JSObject* +BluetoothPairingHandle::WrapObject(JSContext* aCx) +{ + return BluetoothPairingHandleBinding::Wrap(aCx, this); +} diff --git a/dom/bluetooth2/BluetoothPairingHandle.h b/dom/bluetooth2/BluetoothPairingHandle.h new file mode 100644 index 000000000000..da4f20504346 --- /dev/null +++ b/dom/bluetooth2/BluetoothPairingHandle.h @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_bluetooth_bluetoothpairinghandle_h +#define mozilla_dom_bluetooth_bluetoothpairinghandle_h + +#include "nsWrapperCache.h" + +namespace mozilla { +class ErrorResult; +namespace dom { +class Promise; +} +} + +BEGIN_BLUETOOTH_NAMESPACE + +class BluetoothDevice; + +class BluetoothPairingHandle MOZ_FINAL : public nsISupports, + public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(BluetoothPairingHandle) + + static already_AddRefed + Create(nsPIDOMWindow* aOwner, + const nsAString& aDeviceAddress, + const nsAString& aType, + const nsAString& aPasskey); + + nsPIDOMWindow* GetParentObject() const + { + return mOwner; + } + + virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + void GetPasskey(nsString& aPasskey) const + { + aPasskey = mPasskey; + } + + already_AddRefed + SetPinCode(const nsAString& aPinCode, ErrorResult& aRv); + + already_AddRefed + SetPairingConfirmation(bool aConfirm, ErrorResult& aRv); + +private: + BluetoothPairingHandle(nsPIDOMWindow* aOwner, + const nsAString& aDeviceAddress, + const nsAString& aType, + const nsAString& aPasskey); + ~BluetoothPairingHandle(); + + nsCOMPtr mOwner; + nsString mDeviceAddress; + nsString mType; + nsString mPasskey; +}; + +END_BLUETOOTH_NAMESPACE + +#endif // mozilla_dom_bluetooth_bluetoothpairinghandle_h diff --git a/dom/bluetooth2/BluetoothService.h b/dom/bluetooth2/BluetoothService.h index 51196e0849b3..476aa86cca1e 100644 --- a/dom/bluetooth2/BluetoothService.h +++ b/dom/bluetooth2/BluetoothService.h @@ -219,15 +219,15 @@ public: UpdateSdpRecords(const nsAString& aDeviceAddress, BluetoothProfileManagerBase* aManager) = 0; - virtual bool + virtual void SetPinCodeInternal(const nsAString& aDeviceAddress, const nsAString& aPinCode, BluetoothReplyRunnable* aRunnable) = 0; - virtual bool + virtual void SetPasskeyInternal(const nsAString& aDeviceAddress, uint32_t aPasskey, BluetoothReplyRunnable* aRunnable) = 0; - virtual bool + virtual void SetPairingConfirmationInternal(const nsAString& aDeviceAddress, bool aConfirm, BluetoothReplyRunnable* aRunnable) = 0; diff --git a/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp b/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp index 5f36fbb3d4f1..70cbfc1daf41 100644 --- a/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp +++ b/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp @@ -45,6 +45,15 @@ } \ } while(0) +#define ENSURE_BLUETOOTH_IS_READY_VOID(runnable) \ + do { \ + if (!sBtInterface || !IsEnabled()) { \ + NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth is not ready"); \ + DispatchBluetoothReply(runnable, BluetoothValue(), errorStr); \ + return; \ + } \ + } while(0) + using namespace mozilla; using namespace mozilla::ipc; USING_BLUETOOTH_NAMESPACE @@ -1492,14 +1501,14 @@ private: BluetoothReplyRunnable* mRunnable; }; -bool +void BluetoothServiceBluedroid::SetPinCodeInternal( const nsAString& aDeviceAddress, const nsAString& aPinCode, BluetoothReplyRunnable* aRunnable) { MOZ_ASSERT(NS_IsMainThread()); - ENSURE_BLUETOOTH_IS_READY(aRunnable, false); + ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable); bt_bdaddr_t remoteAddress; StringToBdAddressType(aDeviceAddress, &remoteAddress); @@ -1508,16 +1517,14 @@ BluetoothServiceBluedroid::SetPinCodeInternal( &remoteAddress, true, aPinCode.Length(), (bt_pin_code_t*)NS_ConvertUTF16toUTF8(aPinCode).get(), new PinReplyResultHandler(aRunnable)); - - return true; } -bool +void BluetoothServiceBluedroid::SetPasskeyInternal( const nsAString& aDeviceAddress, uint32_t aPasskey, BluetoothReplyRunnable* aRunnable) { - return true; + return; } class SspReplyResultHandler MOZ_FINAL : public BluetoothResultHandler @@ -1542,21 +1549,23 @@ private: BluetoothReplyRunnable* mRunnable; }; -bool +void BluetoothServiceBluedroid::SetPairingConfirmationInternal( const nsAString& aDeviceAddress, bool aConfirm, BluetoothReplyRunnable* aRunnable) { MOZ_ASSERT(NS_IsMainThread()); - ENSURE_BLUETOOTH_IS_READY(aRunnable, false); + ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable); bt_bdaddr_t remoteAddress; StringToBdAddressType(aDeviceAddress, &remoteAddress); - sBtInterface->SspReply(&remoteAddress, (bt_ssp_variant_t)0, aConfirm, 0, + sBtInterface->SspReply(&remoteAddress, + BT_SSP_VARIANT_PASSKEY_CONFIRMATION, + aConfirm, + 0, /* aPasskey */ new SspReplyResultHandler(aRunnable)); - return true; } static void diff --git a/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.h b/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.h index 5d51000aa673..29ee2ada1304 100644 --- a/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.h +++ b/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.h @@ -66,15 +66,15 @@ public: RemoveDeviceInternal(const nsAString& aDeviceObjectPath, BluetoothReplyRunnable* aRunnable); - virtual bool + virtual void SetPinCodeInternal(const nsAString& aDeviceAddress, const nsAString& aPinCode, BluetoothReplyRunnable* aRunnable); - virtual bool + virtual void SetPasskeyInternal(const nsAString& aDeviceAddress, uint32_t aPasskey, BluetoothReplyRunnable* aRunnable); - virtual bool + virtual void SetPairingConfirmationInternal(const nsAString& aDeviceAddress, bool aConfirm, BluetoothReplyRunnable* aRunnable); diff --git a/dom/bluetooth2/bluedroid/hfp-fallback/BluetoothHfpManager.cpp b/dom/bluetooth2/bluedroid/hfp-fallback/BluetoothHfpManager.cpp index abef9b40f717..00101e13cc98 100644 --- a/dom/bluetooth2/bluedroid/hfp-fallback/BluetoothHfpManager.cpp +++ b/dom/bluetooth2/bluedroid/hfp-fallback/BluetoothHfpManager.cpp @@ -148,7 +148,7 @@ BluetoothHfpManager::Init() // static void -BluetoothHfpManager::InitHfpInterface() +BluetoothHfpManager::InitHfpInterface(BluetoothProfileResultHandler* aRes) { MOZ_ASSERT(NS_IsMainThread()); @@ -157,11 +157,15 @@ BluetoothHfpManager::InitHfpInterface() * Implement InitHfpInterface() for applications that want to create SCO * link without a HFP connection (e.g., VoIP). */ + + if (aRes) { + aRes->Init(); + } } // static void -BluetoothHfpManager::DeinitHfpInterface() +BluetoothHfpManager::DeinitHfpInterface(BluetoothProfileResultHandler* aRes) { MOZ_ASSERT(NS_IsMainThread()); @@ -170,6 +174,10 @@ BluetoothHfpManager::DeinitHfpInterface() * Implement DeinitHfpInterface() for applications that want to create SCO * link without a HFP connection (e.g., VoIP). */ + + if (aRes) { + aRes->Deinit(); + } } void diff --git a/dom/bluetooth2/bluedroid/hfp-fallback/BluetoothHfpManager.h b/dom/bluetooth2/bluedroid/hfp-fallback/BluetoothHfpManager.h index 12ff8a9ba23c..ad82a3515347 100644 --- a/dom/bluetooth2/bluedroid/hfp-fallback/BluetoothHfpManager.h +++ b/dom/bluetooth2/bluedroid/hfp-fallback/BluetoothHfpManager.h @@ -30,8 +30,8 @@ public: static BluetoothHfpManager* Get(); virtual ~BluetoothHfpManager() { } - static void InitHfpInterface(); - static void DeinitHfpInterface(); + static void InitHfpInterface(BluetoothProfileResultHandler* aRes); + static void DeinitHfpInterface(BluetoothProfileResultHandler* aRes); bool ConnectSco(); bool DisconnectSco(); diff --git a/dom/bluetooth2/bluez/BluetoothDBusService.cpp b/dom/bluetooth2/bluez/BluetoothDBusService.cpp index bac73bc1a832..69465fd7d1f2 100644 --- a/dom/bluetooth2/bluez/BluetoothDBusService.cpp +++ b/dom/bluetooth2/bluez/BluetoothDBusService.cpp @@ -3195,7 +3195,7 @@ private: nsRefPtr mRunnable; }; -bool +void BluetoothDBusService::SetPinCodeInternal(const nsAString& aDeviceAddress, const nsAString& aPinCode, BluetoothReplyRunnable* aRunnable) @@ -3204,8 +3204,6 @@ BluetoothDBusService::SetPinCodeInternal(const nsAString& aDeviceAddress, NS_ConvertUTF16toUTF8(aPinCode), aRunnable); DispatchToDBusThread(task); - - return true; } class SetPasskeyTask : public Task @@ -3271,7 +3269,7 @@ private: nsRefPtr mRunnable; }; -bool +void BluetoothDBusService::SetPasskeyInternal(const nsAString& aDeviceAddress, uint32_t aPasskey, BluetoothReplyRunnable* aRunnable) @@ -3280,12 +3278,10 @@ BluetoothDBusService::SetPasskeyInternal(const nsAString& aDeviceAddress, aPasskey, aRunnable); DispatchToDBusThread(task); - - return true; } -bool +void BluetoothDBusService::SetPairingConfirmationInternal( const nsAString& aDeviceAddress, bool aConfirm, @@ -3297,8 +3293,6 @@ BluetoothDBusService::SetPairingConfirmationInternal( aConfirm, aRunnable); DispatchToDBusThread(task); - - return true; } static void diff --git a/dom/bluetooth2/bluez/BluetoothDBusService.h b/dom/bluetooth2/bluez/BluetoothDBusService.h index 0072648f0470..769bf4d15589 100644 --- a/dom/bluetooth2/bluez/BluetoothDBusService.h +++ b/dom/bluetooth2/bluez/BluetoothDBusService.h @@ -93,15 +93,15 @@ public: RemoveDeviceInternal(const nsAString& aDeviceObjectPath, BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE; - virtual bool + virtual nsresult SetPinCodeInternal(const nsAString& aDeviceAddress, const nsAString& aPinCode, BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE; - virtual bool + virtual nsresult SetPasskeyInternal(const nsAString& aDeviceAddress, uint32_t aPasskey, BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE; - virtual bool + virtual nsresult SetPairingConfirmationInternal(const nsAString& aDeviceAddress, bool aConfirm, BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE; diff --git a/dom/bluetooth2/ipc/BluetoothParent.cpp b/dom/bluetooth2/ipc/BluetoothParent.cpp index dd7f5a2d82e8..0872f3954082 100644 --- a/dom/bluetooth2/ipc/BluetoothParent.cpp +++ b/dom/bluetooth2/ipc/BluetoothParent.cpp @@ -466,12 +466,9 @@ BluetoothRequestParent::DoRequest(const SetPinCodeRequest& aRequest) MOZ_ASSERT(mService); MOZ_ASSERT(mRequestType == Request::TSetPinCodeRequest); - bool result = - mService->SetPinCodeInternal(aRequest.path(), - aRequest.pincode(), - mReplyRunnable.get()); - - NS_ENSURE_TRUE(result, false); + mService->SetPinCodeInternal(aRequest.path(), + aRequest.pincode(), + mReplyRunnable.get()); return true; } @@ -482,12 +479,9 @@ BluetoothRequestParent::DoRequest(const SetPasskeyRequest& aRequest) MOZ_ASSERT(mService); MOZ_ASSERT(mRequestType == Request::TSetPasskeyRequest); - bool result = - mService->SetPasskeyInternal(aRequest.path(), - aRequest.passkey(), - mReplyRunnable.get()); - - NS_ENSURE_TRUE(result, false); + mService->SetPasskeyInternal(aRequest.path(), + aRequest.passkey(), + mReplyRunnable.get()); return true; } @@ -499,12 +493,9 @@ BluetoothRequestParent::DoRequest(const ConfirmPairingConfirmationRequest& MOZ_ASSERT(mService); MOZ_ASSERT(mRequestType == Request::TConfirmPairingConfirmationRequest); - bool result = - mService->SetPairingConfirmationInternal(aRequest.path(), - true, - mReplyRunnable.get()); - - NS_ENSURE_TRUE(result, false); + mService->SetPairingConfirmationInternal(aRequest.path(), + true, + mReplyRunnable.get()); return true; } @@ -516,12 +507,9 @@ BluetoothRequestParent::DoRequest(const DenyPairingConfirmationRequest& MOZ_ASSERT(mService); MOZ_ASSERT(mRequestType == Request::TDenyPairingConfirmationRequest); - bool result = - mService->SetPairingConfirmationInternal(aRequest.path(), - false, - mReplyRunnable.get()); - - NS_ENSURE_TRUE(result, false); + mService->SetPairingConfirmationInternal(aRequest.path(), + false, + mReplyRunnable.get()); return true; } diff --git a/dom/bluetooth2/ipc/BluetoothServiceChildProcess.cpp b/dom/bluetooth2/ipc/BluetoothServiceChildProcess.cpp index f87fa3f36f64..6bf6edba9ff7 100644 --- a/dom/bluetooth2/ipc/BluetoothServiceChildProcess.cpp +++ b/dom/bluetooth2/ipc/BluetoothServiceChildProcess.cpp @@ -207,7 +207,7 @@ BluetoothServiceChildProcess::UpdateSdpRecords(const nsAString& aDeviceAddress, MOZ_CRASH("This should never be called!"); } -bool +void BluetoothServiceChildProcess::SetPinCodeInternal( const nsAString& aDeviceAddress, const nsAString& aPinCode, @@ -215,10 +215,9 @@ BluetoothServiceChildProcess::SetPinCodeInternal( { SendRequest(aRunnable, SetPinCodeRequest(nsString(aDeviceAddress), nsString(aPinCode))); - return true; } -bool +void BluetoothServiceChildProcess::SetPasskeyInternal( const nsAString& aDeviceAddress, uint32_t aPasskey, @@ -226,10 +225,9 @@ BluetoothServiceChildProcess::SetPasskeyInternal( { SendRequest(aRunnable, SetPasskeyRequest(nsString(aDeviceAddress), aPasskey)); - return true; } -bool +void BluetoothServiceChildProcess::SetPairingConfirmationInternal( const nsAString& aDeviceAddress, bool aConfirm, @@ -242,7 +240,6 @@ BluetoothServiceChildProcess::SetPairingConfirmationInternal( SendRequest(aRunnable, DenyPairingConfirmationRequest(nsString(aDeviceAddress))); } - return true; } void diff --git a/dom/bluetooth2/ipc/BluetoothServiceChildProcess.h b/dom/bluetooth2/ipc/BluetoothServiceChildProcess.h index f0f8a39343f6..80466d8698b6 100644 --- a/dom/bluetooth2/ipc/BluetoothServiceChildProcess.h +++ b/dom/bluetooth2/ipc/BluetoothServiceChildProcess.h @@ -94,17 +94,17 @@ public: UpdateSdpRecords(const nsAString& aDeviceAddress, BluetoothProfileManagerBase* aManager) MOZ_OVERRIDE; - virtual bool + virtual void SetPinCodeInternal(const nsAString& aDeviceAddress, const nsAString& aPinCode, BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE; - virtual bool + virtual void SetPasskeyInternal(const nsAString& aDeviceAddress, uint32_t aPasskey, BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE; - virtual bool + virtual void SetPairingConfirmationInternal(const nsAString& aDeviceAddress, bool aConfirm, BluetoothReplyRunnable* aRunnable) diff --git a/dom/bluetooth2/moz.build b/dom/bluetooth2/moz.build index bf747fa25af0..657bd8133faa 100644 --- a/dom/bluetooth2/moz.build +++ b/dom/bluetooth2/moz.build @@ -12,6 +12,7 @@ if CONFIG['MOZ_B2G_BT']: 'BluetoothDiscoveryHandle.cpp', 'BluetoothHidManager.cpp', 'BluetoothManager.cpp', + 'BluetoothPairingHandle.cpp', 'BluetoothProfileController.cpp', 'BluetoothReplyRunnable.cpp', 'BluetoothService.cpp', @@ -99,6 +100,7 @@ EXPORTS.mozilla.dom.bluetooth += [ 'BluetoothDevice.h', 'BluetoothDiscoveryHandle.h', 'BluetoothManager.h', + 'BluetoothPairingHandle.h', ] IPDL_SOURCES += [ diff --git a/dom/browser-element/BrowserElementChildPreload.js b/dom/browser-element/BrowserElementChildPreload.js index 82da8310faf1..d278293dfee3 100644 --- a/dom/browser-element/BrowserElementChildPreload.js +++ b/dom/browser-element/BrowserElementChildPreload.js @@ -231,6 +231,7 @@ BrowserElementChild.prototype = { "go-forward": this._recvGoForward, "reload": this._recvReload, "stop": this._recvStop, + "zoom": this._recvZoom, "unblock-modal-prompt": this._recvStopWaiting, "fire-ctx-callback": this._recvFireCtxCallback, "owner-visibility-change": this._recvOwnerVisibilityChange, @@ -1031,6 +1032,10 @@ BrowserElementChild.prototype = { webNav.stop(webNav.STOP_NETWORK); }, + _recvZoom: function(data) { + docShell.contentViewer.fullZoom = data.json.zoom; + }, + _recvSetInputMethodActive: function(data) { let msgData = { id: data.json.id }; if (!this._isContentWindowCreated) { diff --git a/dom/browser-element/BrowserElementParent.jsm b/dom/browser-element/BrowserElementParent.jsm index 1636732e4d52..8e76c0d1e5eb 100644 --- a/dom/browser-element/BrowserElementParent.jsm +++ b/dom/browser-element/BrowserElementParent.jsm @@ -126,6 +126,7 @@ function BrowserElementParent(frameLoader, hasRemoteFrame, isPendingFrame) { defineNoReturnMethod('goForward', this._goForward); defineNoReturnMethod('reload', this._reload); defineNoReturnMethod('stop', this._stop); + defineNoReturnMethod('zoom', this._zoom); defineMethod('download', this._download); defineDOMRequestMethod('purgeHistory', 'purge-history'); defineMethod('getScreenshot', this._getScreenshot); @@ -591,6 +592,16 @@ BrowserElementParent.prototype = { this._sendAsyncMsg('stop'); }, + /* + * The valid range of zoom scale is defined in preference "zoom.maxPercent" and "zoom.minPercent". + */ + _zoom: function(zoom) { + zoom *= 100; + zoom = Math.min(getIntPref("zoom.maxPercent", 300), zoom); + zoom = Math.max(getIntPref("zoom.minPercent", 50), zoom); + this._sendAsyncMsg('zoom', {zoom: zoom / 100.0}); + }, + _download: function(_url, _options) { let ioService = Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService); diff --git a/dom/events/test/test_all_synthetic_events.html b/dom/events/test/test_all_synthetic_events.html index cfc96b4eb9cb..07c5fe413413 100644 --- a/dom/events/test/test_all_synthetic_events.html +++ b/dom/events/test/test_all_synthetic_events.html @@ -60,6 +60,10 @@ const kEventConstructors = { return new BluetoothDiscoveryStateChangedEvent(aName, aProps); }, }, + BluetoothPairingEvent: { create: function (aName, aProps) { + return new BluetoothPairingEvent(aName, aProps); + }, + }, BluetoothStatusChangedEvent: { create: function (aName, aProps) { return new BluetoothStatusChangedEvent(aName, aProps); }, diff --git a/dom/payment/Payment.jsm b/dom/payment/Payment.jsm index d9e981e4c0bd..1a0d519d9f22 100644 --- a/dom/payment/Payment.jsm +++ b/dom/payment/Payment.jsm @@ -198,6 +198,18 @@ let PaymentManager = { } } +#ifdef MOZ_B2G + let appsService = Cc["@mozilla.org/AppsService;1"] + .getService(Ci.nsIAppsService); + let systemAppId = Ci.nsIScriptSecurityManager.NO_APP_ID; + + try { + let manifestURL = Services.prefs.getCharPref("b2g.system_manifest_url"); + systemAppId = appsService.getAppLocalIdByManifestURL(manifestURL); + this.LOG("System app id=" + systemAppId); + } catch(e) {} +#endif + // Now register the payment providers. for (let i in nums) { let branch = prefService @@ -211,12 +223,28 @@ let PaymentManager = { if (type in this.registeredProviders) { continue; } - this.registeredProviders[type] = { + let provider = this.registeredProviders[type] = { name: branch.getCharPref("name"), uri: branch.getCharPref("uri"), description: branch.getCharPref("description"), requestMethod: branch.getCharPref("requestMethod") }; + +#ifdef MOZ_B2G + // Let this payment provider access the firefox-accounts API when + // it's loaded in the trusted UI. + if (systemAppId != Ci.nsIScriptSecurityManager.NO_APP_ID) { + this.LOG("Granting firefox-accounts permission to " + provider.uri); + let uri = Services.io.newURI(provider.uri, null, null); + let principal = Services.scriptSecurityManager + .getAppCodebasePrincipal(uri, systemAppId, true); + + Services.perms.addFromPrincipal(principal, "firefox-accounts", + Ci.nsIPermissionManager.ALLOW_ACTION, + Ci.nsIPermissionManager.EXPIRE_SESSION); + } +#endif + if (this._debug) { this.LOG("Registered Payment Providers: " + JSON.stringify(this.registeredProviders[type])); diff --git a/dom/payment/moz.build b/dom/payment/moz.build index c091dcb5fae1..b037734f9c3e 100644 --- a/dom/payment/moz.build +++ b/dom/payment/moz.build @@ -6,7 +6,7 @@ DIRS += ['interfaces'] -EXTRA_JS_MODULES += [ +EXTRA_PP_JS_MODULES += [ 'Payment.jsm', ] diff --git a/dom/webidl/BluetoothAdapter2.webidl b/dom/webidl/BluetoothAdapter2.webidl index b6e4de8c62f8..197ca6f9ad61 100644 --- a/dom/webidl/BluetoothAdapter2.webidl +++ b/dom/webidl/BluetoothAdapter2.webidl @@ -113,12 +113,6 @@ interface BluetoothAdapter : EventTarget { DOMRequest getPairedDevices(); [NewObject, Throws] DOMRequest getConnectedDevices(unsigned short serviceUuid); - [NewObject, Throws] - DOMRequest setPinCode(DOMString deviceAddress, DOMString pinCode); - [NewObject, Throws] - DOMRequest setPasskey(DOMString deviceAddress, unsigned long passkey); - [NewObject, Throws] - DOMRequest setPairingConfirmation(DOMString deviceAddress, boolean confirmation); /** * Connect/Disconnect to a specific service of a target remote device. diff --git a/dom/webidl/BluetoothPairingEvent.webidl b/dom/webidl/BluetoothPairingEvent.webidl new file mode 100644 index 000000000000..b2e813b7f1e3 --- /dev/null +++ b/dom/webidl/BluetoothPairingEvent.webidl @@ -0,0 +1,20 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +[CheckPermissions="bluetooth", + Constructor(DOMString type, + optional BluetoothPairingEventInit eventInitDict)] +interface BluetoothPairingEvent : Event +{ + readonly attribute BluetoothDevice? device; + readonly attribute BluetoothPairingHandle? handle; +}; + +dictionary BluetoothPairingEventInit : EventInit +{ + BluetoothDevice? device = null; + BluetoothPairingHandle? handle = null; +}; diff --git a/dom/webidl/BluetoothPairingHandle.webidl b/dom/webidl/BluetoothPairingHandle.webidl new file mode 100644 index 000000000000..383b8ee183a4 --- /dev/null +++ b/dom/webidl/BluetoothPairingHandle.webidl @@ -0,0 +1,22 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +[CheckPermissions="bluetooth"] +interface BluetoothPairingHandle +{ + /** + * A 6-digit string ranging from decimal 000000 to 999999. + * This attribute is an empty string for enterpincodereq and + * pairingconsentreq. + */ + readonly attribute DOMString passkey; + + // Promise + [NewObject, Throws] + Promise setPinCode(DOMString aPinCode); + // Promise + [NewObject, Throws] + Promise setPairingConfirmation(boolean aConfirm); +}; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 474fbb1c9edd..8ef8b137efc8 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -571,6 +571,7 @@ if CONFIG['MOZ_B2G_BT']: 'BluetoothDevice2.webidl', 'BluetoothDiscoveryHandle.webidl', 'BluetoothManager2.webidl', + 'BluetoothPairingHandle.webidl', ] else: WEBIDL_FILES += [ @@ -695,6 +696,7 @@ if CONFIG['MOZ_B2G_BT']: GENERATED_EVENTS_WEBIDL_FILES += [ 'BluetoothAdapterEvent.webidl', 'BluetoothAttributeEvent.webidl', + 'BluetoothPairingEvent.webidl', ] else: GENERATED_EVENTS_WEBIDL_FILES += [ diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index 8b62c531f5e2..e724147de71c 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -3642,6 +3642,14 @@ nsTextPaintStyle::InitSelectionColorsAndShadow() eCSSProperty_color; nscoord frameColor = mFrame->GetVisitedDependentColor(property); mSelectionTextColor = EnsureDifferentColors(frameColor, mSelectionBGColor); + } else if (mSelectionTextColor == NS_CHANGE_COLOR_IF_SAME_AS_BG) { + nsCSSProperty property = mFrame->IsSVGText() ? eCSSProperty_fill : + eCSSProperty_color; + nscolor frameColor = mFrame->GetVisitedDependentColor(property); + if (frameColor == mSelectionBGColor) { + mSelectionTextColor = + LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForegroundCustom); + } } else { EnsureSufficientContrast(&mSelectionTextColor, &mSelectionBGColor); } diff --git a/layout/tools/reftest/b2g_start_script.js b/layout/tools/reftest/b2g_start_script.js index 17be2b895b4c..b91dd9e12758 100644 --- a/layout/tools/reftest/b2g_start_script.js +++ b/layout/tools/reftest/b2g_start_script.js @@ -26,6 +26,15 @@ function setPermissions() { perms.add(uri, "allowXULXBL", Ci.nsIPermissionManager.ALLOW_ACTION); } +let cm = Cc["@mozilla.org/categorymanager;1"] + .getService(Components.interfaces.nsICategoryManager); + +// Disable update timers that cause b2g failures. +if (cm) { + cm.deleteCategoryEntry("update-timer", "WebappsUpdateTimer", false); + cm.deleteCategoryEntry("update-timer", "nsUpdateService", false); +} + // Load into any existing windows let wm = Cc["@mozilla.org/appshell/window-mediator;1"] .getService(Ci.nsIWindowMediator); diff --git a/services/mobileid/MobileIdentityClient.jsm b/services/mobileid/MobileIdentityClient.jsm index fe515d8ae9bf..4f62af9d837e 100644 --- a/services/mobileid/MobileIdentityClient.jsm +++ b/services/mobileid/MobileIdentityClient.jsm @@ -54,10 +54,13 @@ this.MobileIdentityClient.prototype = { return this._request(REGISTER, "POST", null, {}); }, - smsMtVerify: function(aSessionToken, aMsisdn, aWantShortCode = false) { + smsMtVerify: function(aSessionToken, aMsisdn, aMcc, aMnc, + aWantShortCode = false) { let credentials = this._deriveHawkCredentials(aSessionToken); return this._request(SMS_MT_VERIFY, "POST", credentials, { msisdn: aMsisdn, + mcc: aMcc, + mnc: aMnc, shortVerificationCode: aWantShortCode }); }, diff --git a/services/mobileid/MobileIdentityManager.jsm b/services/mobileid/MobileIdentityManager.jsm index 4768a7f6b562..c8dc1a89f124 100644 --- a/services/mobileid/MobileIdentityManager.jsm +++ b/services/mobileid/MobileIdentityManager.jsm @@ -450,30 +450,31 @@ this.MobileIdentityManager = { aToVerify.msisdn && aToVerify.verificationDetails && aToVerify.verificationDetails.mtSender) { - this.activeVerificationFlow = new MobileIdentitySmsMtVerificationFlow( - aOrigin, - aToVerify.msisdn, - aToVerify.iccId, - aToVerify.serviceId === undefined, // external: the phone number does - // not seem to belong to any of the - // device SIM cards. - aToVerify.verificationDetails.mtSender, + this.activeVerificationFlow = new MobileIdentitySmsMtVerificationFlow({ + origin: aOrigin, + msisdn: aToVerify.msisdn, + mcc: aToVerify.mcc, + mnc: aToVerify.mnc, + iccId: aToVerify.iccId, + external: aToVerify.serviceId === undefined, + mtSender: aToVerify.verificationDetails.mtSender + }, this.ui, this.client ); #ifdef MOZ_B2G_RIL } else if (aToVerify.verificationMethod.indexOf(SMS_MO_MT) != -1 && - aToVerify.serviceId && - aToVerify.verificationDetails && - aToVerify.verificationDetails.moVerifier && - aToVerify.verificationDetails.mtSender) { - - this.activeVerificationFlow = new MobileIdentitySmsMoMtVerificationFlow( - aOrigin, - aToVerify.serviceId, - aToVerify.iccId, - aToVerify.verificationDetails.mtSender, - aToVerify.verificationDetails.moVerifier, + aToVerify.serviceId && + aToVerify.verificationDetails && + aToVerify.verificationDetails.moVerifier && + aToVerify.verificationDetails.mtSender) { + this.activeVerificationFlow = new MobileIdentitySmsMoMtVerificationFlow({ + origin: aOrigin, + serviceId: aToVerify.serviceId, + iccId: aToVerify.iccId, + mtSender: aToVerify.verificationDetails.mtSender, + moVerifier: aToVerify.verificationDetails.moVerifier + }, this.ui, this.client ); @@ -517,6 +518,8 @@ this.MobileIdentityManager = { toVerify.serviceId = serviceId; toVerify.iccId = this.iccInfo[serviceId].iccId; toVerify.msisdn = this.iccInfo[serviceId].msisdn; + toVerify.mcc = this.iccInfo[serviceId].mcc; + toVerify.mnc = this.iccInfo[serviceId].mnc; toVerify.verificationMethod = this.iccInfo[serviceId].verificationMethods[0]; toVerify.verificationDetails = @@ -524,6 +527,7 @@ this.MobileIdentityManager = { return this._verificationFlow(toVerify, aOrigin); } else { toVerify.msisdn = aUserSelection.msisdn; + toVerify.mcc = aUserSelection.mcc; return this.client.discover(aUserSelection.msisdn, aUserSelection.mcc) .then( diff --git a/services/mobileid/MobileIdentitySmsMoMtVerificationFlow.jsm b/services/mobileid/MobileIdentitySmsMoMtVerificationFlow.jsm index 88589c009186..87f7950bfaeb 100644 --- a/services/mobileid/MobileIdentitySmsMoMtVerificationFlow.jsm +++ b/services/mobileid/MobileIdentitySmsMoMtVerificationFlow.jsm @@ -39,24 +39,14 @@ SilentSmsRequest.prototype = { } }; -this.MobileIdentitySmsMoMtVerificationFlow = function(aOrigin, - aServiceId, - aIccId, - aMtSender, - aMoVerifier, +this.MobileIdentitySmsMoMtVerificationFlow = function(aVerificationOptions, aUI, aClient) { - log.debug("MobileIdentitySmsMoMtVerificationFlow"); + log.debug("MobileIdentitySmsMoMtVerificationFlow ${}", aVerificationOptions); MobileIdentitySmsVerificationFlow.call(this, - aOrigin, - null, //msisdn - aIccId, - aServiceId, - false, // external - aMtSender, - aMoVerifier, + aVerificationOptions, aUI, aClient, this.smsVerifyStrategy); diff --git a/services/mobileid/MobileIdentitySmsMtVerificationFlow.jsm b/services/mobileid/MobileIdentitySmsMtVerificationFlow.jsm index 64ef8254be7f..893b6488904d 100644 --- a/services/mobileid/MobileIdentitySmsMtVerificationFlow.jsm +++ b/services/mobileid/MobileIdentitySmsMtVerificationFlow.jsm @@ -13,25 +13,14 @@ Cu.import("resource://gre/modules/MobileIdentitySmsVerificationFlow.jsm"); Cu.import("resource://gre/modules/Promise.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -this.MobileIdentitySmsMtVerificationFlow = function(aOrigin, - aMsisdn, - aIccId, - aExternal, - aMtSender, +this.MobileIdentitySmsMtVerificationFlow = function(aVerificationOptions, aUI, aClient) { - log.debug("MobileIdentitySmsVerificationFlow " + aMsisdn + ", external: " + - aExternal); + log.debug("MobileIdentitySmsVerificationFlow ${}", aVerificationOptions); MobileIdentitySmsVerificationFlow.call(this, - aOrigin, - aMsisdn, - aIccId, - null, // service ID - aExternal, - aMtSender, - null, // moVerifier + aVerificationOptions, aUI, aClient, this.smsVerifyStrategy); @@ -44,6 +33,8 @@ this.MobileIdentitySmsMtVerificationFlow.prototype = { smsVerifyStrategy: function() { return this.client.smsMtVerify(this.sessionToken, this.verificationOptions.msisdn, + this.verificationOptions.mcc, + this.verificationOptions.mnc, this.verificationOptions.external); } }; diff --git a/services/mobileid/MobileIdentitySmsVerificationFlow.jsm b/services/mobileid/MobileIdentitySmsVerificationFlow.jsm index 2c0663ca7b7c..2e1826a56d9e 100644 --- a/services/mobileid/MobileIdentitySmsVerificationFlow.jsm +++ b/services/mobileid/MobileIdentitySmsVerificationFlow.jsm @@ -19,13 +19,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "smsService", "nsISmsService"); #endif -this.MobileIdentitySmsVerificationFlow = function(aOrigin, - aMsisdn, - aIccId, - aServiceId, - aExternal, - aMtSender, - aMoVerifier, +this.MobileIdentitySmsVerificationFlow = function(aVerificationOptions, aUI, aClient, aVerifyStrategy) { @@ -33,15 +27,9 @@ this.MobileIdentitySmsVerificationFlow = function(aOrigin, // SMS MT or SMS MO+MT specific verify strategy. this.smsVerifyStrategy = aVerifyStrategy; - MobileIdentityVerificationFlow.call(this, { - origin: aOrigin, - msisdn: aMsisdn, - iccId: aIccId, - serviceId: aServiceId, - external: aExternal, - mtSender: aMtSender, - moVerifier: aMoVerifier - }, aUI, aClient, this._verifyStrategy, this._cleanupStrategy); + log.debug("aVerificationOptions ${}", aVerificationOptions); + MobileIdentityVerificationFlow.call(this, aVerificationOptions, aUI, aClient, + this._verifyStrategy, this._cleanupStrategy); }; this.MobileIdentitySmsVerificationFlow.prototype = { diff --git a/services/mobileid/tests/xpcshell/test_mobileid_manager.js b/services/mobileid/tests/xpcshell/test_mobileid_manager.js index 2f46f91982c9..80956283de78 100644 --- a/services/mobileid/tests/xpcshell/test_mobileid_manager.js +++ b/services/mobileid/tests/xpcshell/test_mobileid_manager.js @@ -138,7 +138,8 @@ MockUi.prototype = { __proto__: Mock.prototype, _startFlowResult: { - phoneNumber: PHONE_NUMBER + phoneNumber: PHONE_NUMBER, + mcc: MNC }, _verifyCodePromptResult: { @@ -434,7 +435,9 @@ add_test(function() { client._("smsMtVerify").callsLength(1); client._("smsMtVerify").call(1).arg(1, SESSION_TOKEN); client._("smsMtVerify").call(1).arg(2, PHONE_NUMBER); - client._("smsMtVerify").call(1).arg(3, true); + client._("smsMtVerify").call(1).arg(3, MNC); + client._("smsMtVerify").call(1).arg(4, undefined); + client._("smsMtVerify").call(1).arg(5, true); client._("verifyCode").callsLength(1); client._("verifyCode").call(1).arg(1, SESSION_TOKEN); client._("verifyCode").call(1).arg(2, { @@ -670,7 +673,9 @@ add_test(function() { client._("smsMtVerify").callsLength(1); client._("smsMtVerify").call(1).arg(1, _sessionToken); client._("smsMtVerify").call(1).arg(2, PHONE_NUMBER); - client._("smsMtVerify").call(1).arg(3, true); + client._("smsMtVerify").call(1).arg(3, MNC); + client._("smsMtVerify").call(1).arg(4, undefined); + client._("smsMtVerify").call(1).arg(5, true); client._("verifyCode").callsLength(1); client._("verifyCode").call(1).arg(1, _sessionToken); client._("verifyCode").call(1).arg(2, { diff --git a/toolkit/devtools/server/actors/webapps.js b/toolkit/devtools/server/actors/webapps.js index 8c96990ba2e2..c4221cda95ac 100644 --- a/toolkit/devtools/server/actors/webapps.js +++ b/toolkit/devtools/server/actors/webapps.js @@ -262,7 +262,7 @@ WebappsActor.prototype = { reg.broadcastMessage("Webapps:UpdateState", { app: aApp, manifest: manifest, - manifestURL: aApp.manifestURL + id: aApp.id }); reg.broadcastMessage("Webapps:FireEvent", { eventType: ["downloadsuccess", "downloadapplied"], diff --git a/widget/LookAndFeel.h b/widget/LookAndFeel.h index 823848b67260..41722561b3e6 100644 --- a/widget/LookAndFeel.h +++ b/widget/LookAndFeel.h @@ -39,6 +39,7 @@ public: eColorID_TextForeground, eColorID_TextSelectBackground, eColorID_TextSelectForeground, + eColorID_TextSelectForegroundCustom, eColorID_TextSelectBackgroundDisabled, eColorID_TextSelectBackgroundAttention, eColorID_TextHighlightBackground, @@ -588,6 +589,12 @@ public: // Of course if other plaforms work like the Mac, they can use it too. #define NS_DONT_CHANGE_COLOR NS_RGB(0x01, 0x01, 0x01) +// Similar with NS_DONT_CHANGE_COLOR, except NS_DONT_CHANGE_COLOR would returns +// complementary color if fg color is same as bg color. +// NS_CHANGE_COLOR_IF_SAME_AS_BG would returns eColorID_TextSelectForegroundCustom if +// fg and bg color are the same. +#define NS_CHANGE_COLOR_IF_SAME_AS_BG NS_RGB(0x02, 0x02, 0x02) + // --------------------------------------------------------------------- // Special colors for eColorID_IME* and eColorID_SpellCheckerUnderline // --------------------------------------------------------------------- diff --git a/widget/gonk/nsLookAndFeel.cpp b/widget/gonk/nsLookAndFeel.cpp index 6e503d6661c6..a89ead051e8b 100644 --- a/widget/gonk/nsLookAndFeel.cpp +++ b/widget/gonk/nsLookAndFeel.cpp @@ -97,7 +97,12 @@ nsLookAndFeel::NativeGetColor(ColorID aID, nscolor &aColor) // still used aColor = BASE_SELECTED_COLOR; break; + case eColorID_TextSelectForegroundCustom: + aColor = NS_RGB(0x4d,0x4d,0x4d); + break; case eColorID_TextSelectForeground: + aColor = NS_CHANGE_COLOR_IF_SAME_AS_BG; + break; case eColorID_IMESelectedRawTextForeground: case eColorID_IMESelectedConvertedTextForeground: // still used diff --git a/widget/xpwidgets/nsXPLookAndFeel.cpp b/widget/xpwidgets/nsXPLookAndFeel.cpp index c16133c5b7c3..c1f146feab44 100644 --- a/widget/xpwidgets/nsXPLookAndFeel.cpp +++ b/widget/xpwidgets/nsXPLookAndFeel.cpp @@ -151,6 +151,7 @@ const char nsXPLookAndFeel::sColorPrefs[][38] = "ui.textForeground", "ui.textSelectBackground", "ui.textSelectForeground", + "ui.textSelectForegroundCustom", "ui.textSelectBackgroundDisabled", "ui.textSelectBackgroundAttention", "ui.textHighlightBackground",