author | Alexandre Poirot <poirot.alex@gmail.com> |
Mon, 30 Jun 2014 06:16:00 -0400 | |
changeset 191503 | c94f041648c656e75e005f81c51fee4acff4363f |
parent 191502 | 78517fe424721a392e0d7f78cdcfb315036a55a4 |
child 191504 | 1b526b7613379e2f78e7777c63711cc18bc83777 |
push id | 27052 |
push user | kwierso@gmail.com |
push date | Tue, 01 Jul 2014 01:22:26 +0000 |
treeherder | mozilla-central@a3af97c421d3 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | vingtetun, janx, jryans |
bugs | 963490 |
milestone | 33.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/b2g/chrome/content/devtools.js +++ b/b2g/chrome/content/devtools.js @@ -22,26 +22,26 @@ XPCOMUtils.defineLazyGetter(this, 'WebCo XPCOMUtils.defineLazyGetter(this, 'EventLoopLagFront', function() { return devtools.require('devtools/server/actors/eventlooplag').EventLoopLagFront; }); XPCOMUtils.defineLazyGetter(this, 'MemoryFront', function() { return devtools.require('devtools/server/actors/memory').MemoryFront; }); +Cu.import('resource://gre/modules/AppFrames.jsm'); /** * The Developer HUD is an on-device developer tool that displays widgets, * showing visual debug information about apps. Each widget corresponds to a * metric as tracked by a metric watcher (e.g. consoleWatcher). */ let developerHUD = { _targets: new Map(), - _frames: new Map(), _client: null, _conn: null, _watchers: [], _logging: true, /** * This method registers a metric watcher that will watch one or more metrics * on app frames that are being tracked. A watcher must implement the @@ -72,44 +72,36 @@ let developerHUD = { this._client = new DebuggerClient(transport); for (let w of this._watchers) { if (w.init) { w.init(this._client); } } - Services.obs.addObserver(this, 'remote-browser-shown', false); - Services.obs.addObserver(this, 'inprocess-browser-shown', false); - Services.obs.addObserver(this, 'message-manager-disconnect', false); + AppFrames.addObserver(this); - let systemapp = document.querySelector('#systemapp'); - this.trackFrame(systemapp); - - let frames = systemapp.contentWindow.document.querySelectorAll('iframe[mozapp]'); - for (let frame of frames) { + for (let frame of AppFrames.list()) { this.trackFrame(frame); } SettingsListener.observe('hud.logging', this._logging, enabled => { this._logging = enabled; }); }, uninit: function dwp_uninit() { if (!this._client) return; for (let frame of this._targets.keys()) { this.untrackFrame(frame); } - Services.obs.removeObserver(this, 'remote-browser-shown'); - Services.obs.removeObserver(this, 'inprocess-browser-shown'); - Services.obs.removeObserver(this, 'message-manager-disconnect'); + AppFrames.removeObserver(this); this._client.close(); delete this._client; }, /** * This method will ask all registered watchers to track and update metrics * on an app frame. @@ -135,51 +127,22 @@ let developerHUD = { w.untrackTarget(target); } target.destroy(); this._targets.delete(frame); } }, - observe: function dwp_observe(subject, topic, data) { - if (!this._client) - return; - - let frame; - - switch(topic) { + onAppFrameCreated: function (frame, isFirstAppFrame) { + this.trackFrame(frame); + }, - // listen for frame creation in OOP (device) as well as in parent process (b2g desktop) - case 'remote-browser-shown': - case 'inprocess-browser-shown': - let frameLoader = subject; - // get a ref to the app <iframe> - frameLoader.QueryInterface(Ci.nsIFrameLoader); - // Ignore notifications that aren't from a BrowserOrApp - if (!frameLoader.ownerIsBrowserOrAppFrame) { - return; - } - frame = frameLoader.ownerElement; - if (!frame.appManifestURL) // Ignore all frames but app frames - return; - this.trackFrame(frame); - this._frames.set(frameLoader.messageManager, frame); - break; - - // Every time an iframe is destroyed, its message manager also is - case 'message-manager-disconnect': - let mm = subject; - frame = this._frames.get(mm); - if (!frame) - return; - this.untrackFrame(frame); - this._frames.delete(mm); - break; - } + onAppFrameDestroyed: function (frame, isLastAppFrame) { + this.untrackFrame(frame); }, log: function dwp_log(message) { if (this._logging) { dump(DEVELOPER_HUD_LOG_PREFIX + ': ' + message + '\n'); } }
new file mode 100644 --- /dev/null +++ b/b2g/components/AppFrames.jsm @@ -0,0 +1,136 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict'; + +this.EXPORTED_SYMBOLS = ['AppFrames']; + +const Cu = Components.utils; +const Ci = Components.interfaces; + +Cu.import('resource://gre/modules/Services.jsm'); +Cu.import('resource://gre/modules/SystemAppProxy.jsm'); + +const listeners = []; + +const Observer = { + // Save a map of (MessageManager => Frame) to be able to dispatch + // the FrameDestroyed event with a frame reference. + _frames: new Map(), + + // Also save current number of iframes opened by app + _apps: new Map(), + + start: function () { + Services.obs.addObserver(this, 'remote-browser-shown', false); + Services.obs.addObserver(this, 'inprocess-browser-shown', false); + Services.obs.addObserver(this, 'message-manager-disconnect', false); + + SystemAppProxy.getAppFrames().forEach((frame) => { + let mm = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager; + this._frames.set(mm, frame); + let mozapp = frame.getAttribute('mozapp'); + this._apps.set(mozapp, (this._apps.get(mozapp) || 0) + 1); + }); + }, + + stop: function () { + Services.obs.removeObserver(this, 'remote-browser-shown'); + Services.obs.removeObserver(this, 'inprocess-browser-shown'); + Services.obs.removeObserver(this, 'message-manager-disconnect'); + this._frames.clear(); + this._apps.clear(); + }, + + observe: function (subject, topic, data) { + switch(topic) { + + // Listen for frame creation in OOP (device) as well as in parent process (b2g desktop) + case 'remote-browser-shown': + case 'inprocess-browser-shown': + let frameLoader = subject; + + // get a ref to the app <iframe> + frameLoader.QueryInterface(Ci.nsIFrameLoader); + let frame = frameLoader.ownerElement; + let mm = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager; + this.onMessageManagerCreated(mm, frame); + break; + + // Every time an iframe is destroyed, its message manager also is + case 'message-manager-disconnect': + this.onMessageManagerDestroyed(subject); + break; + } + }, + + onMessageManagerCreated: function (mm, frame) { + this._frames.set(mm, frame); + + let mozapp = frame.getAttribute('mozapp'); + let count = (this._apps.get(mozapp) || 0) + 1; + this._apps.set(mozapp, count); + + let isFirstAppFrame = (count === 1); + listeners.forEach(function (listener) { + try { + listener.onAppFrameCreated(frame, isFirstAppFrame); + } catch(e) { + dump('Exception while calling Frames.jsm listener:' + e + '\n' + e.stack + '\n'); + } + }); + }, + + onMessageManagerDestroyed: function (mm) { + let frame = this._frames.get(mm); + if (!frame) { + // We receive an event for a non mozapp message manager + return; + } + + this._frames.delete(mm); + + let mozapp = frame.getAttribute('mozapp'); + let count = (this._apps.get(mozapp) || 0) - 1; + this._apps.set(mozapp, count); + + let isLastAppFrame = (count === 0); + listeners.forEach(function (listener) { + try { + listener.onAppFrameDestroyed(frame, isLastAppFrame); + } catch(e) { + dump('Exception while calling Frames.jsm listener:' + e + '\n' + e.stack + '\n'); + } + }); + } + +}; + +const AppFrames = this.AppFrames = { + + list: () => SystemAppProxy.getAppFrames(), + + addObserver: function (listener) { + if (listeners.indexOf(listener) !== -1) { + return; + } + + listeners.push(listener); + if (listeners.length == 1) { + Observer.start(); + } + }, + + removeObserver: function (listener) { + let idx = listeners.indexOf(listener); + if (idx !== -1) { + listeners.splice(idx, 1); + } + if (listeners.length === 0) { + Observer.stop(); + } + } + +}; +
--- a/b2g/components/SystemAppProxy.jsm +++ b/b2g/components/SystemAppProxy.jsm @@ -110,13 +110,32 @@ let SystemAppProxy = { if (content) { content.removeEventListener.apply(content, arguments); } else { let idx = this._pendingListeners.indexOf(listener); if (idx != -1) { this._pendingListeners.splice(idx, 1); } } + }, + + getAppFrames: function systemApp_getAppFrames() { + let systemAppFrame = this._frame; + if (!systemAppFrame) { + return []; + } + + let list = [systemAppFrame]; + + // List all app frames hosted in the system app: the homescreen, + // all regular apps, activities, rocket bar, attention screen and the keyboard. + // Bookmark apps and other system app internal frames like captive portal + // are also hosted in system app, but they are not using mozapp attribute. + let frames = systemAppFrame.contentDocument.querySelectorAll("iframe[mozapp]"); + for (let i = 0; i < frames.length; i++) { + list.push(frames[i]); + } + + return list; } - }; this.SystemAppProxy = SystemAppProxy;
--- a/b2g/components/moz.build +++ b/b2g/components/moz.build @@ -40,16 +40,17 @@ EXTRA_PP_COMPONENTS += [ if CONFIG['MOZ_UPDATER']: EXTRA_PP_COMPONENTS += [ 'UpdatePrompt.js', ] EXTRA_JS_MODULES += [ 'AlertsHelper.jsm', + 'AppFrames.jsm', 'ContentRequestHelper.jsm', 'ErrorPage.jsm', 'SignInToWebsite.jsm', 'SystemAppProxy.jsm', 'TelURIParser.jsm', 'WebappsUpdater.jsm', ]
--- a/toolkit/devtools/server/actors/webapps.js +++ b/toolkit/devtools/server/actors/webapps.js @@ -11,16 +11,23 @@ let CC = Components.Constructor; Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/osfile.jsm"); Cu.import("resource://gre/modules/FileUtils.jsm"); let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); +DevToolsUtils.defineLazyGetter(this, "AppFrames", () => { + try { + return Cu.import("resource://gre/modules/AppFrames.jsm", {}).AppFrames; + } catch(e) {} + return null; +}); + function debug(aMsg) { /* Cc["@mozilla.org/consoleservice;1"] .getService(Ci.nsIConsoleService) .logStringMessage("--*-- WebappsActor : " + aMsg); */ } @@ -836,66 +843,71 @@ WebappsActor.prototype = { } reg.close(app); return {}; }, _appFrames: function () { - // For now, we only support app frames on b2g - if (Services.appinfo.ID != "{3c2e2abc-06d4-11e1-ac3b-374f68613e61}") { - return; - } - // Register the system app - let chromeWindow = Services.wm.getMostRecentWindow('navigator:browser'); - let systemAppFrame = chromeWindow.shell.contentBrowser; - yield systemAppFrame; - - // Register apps hosted in the system app. i.e. the homescreen, all regular - // apps and the keyboard. - // Bookmark apps and other system app internal frames like captive portal - // are also hosted in system app, but they are not using mozapp attribute. - let frames = systemAppFrame.contentDocument.querySelectorAll("iframe[mozapp]"); - for (let i = 0; i < frames.length; i++) { - yield frames[i]; + // Try to filter on b2g and mulet + if (AppFrames) { + return AppFrames.list(); + } else { + return []; } }, listRunningApps: function (aRequest) { debug("listRunningApps\n"); let appPromises = []; let apps = []; - for each (let frame in this._appFrames()) { + for (let frame of this._appFrames()) { let manifestURL = frame.getAttribute("mozapp"); + // _appFrames can return more than one frame with the same manifest url + if (apps.indexOf(manifestURL) != -1) { + continue; + } + appPromises.push(this._isAppAllowedForURL(manifestURL).then(allowed => { if (allowed) { apps.push(manifestURL); } })); } return promise.all(appPromises).then(() => { return { apps: apps }; }); }, getAppActor: function ({ manifestURL }) { debug("getAppActor\n"); + // Connects to the main app frame, whose `name` attribute + // is set to 'main' by gaia. If for any reason, gaia doesn't set any + // frame as main, no frame matches, then we connect arbitrary + // to the first app frame... let appFrame = null; - for each (let frame in this._appFrames()) { + let frames = []; + for (let frame of this._appFrames()) { if (frame.getAttribute("mozapp") == manifestURL) { - appFrame = frame; - break; + if (frame.name == "main") { + appFrame = frame; + break; + } + frames.push(frame); } } + if (!appFrame && frames.length > 0) { + appFrame = frames[0]; + } let notFoundError = { error: "appNotFound", message: "Unable to find any opened app whose manifest " + "is '" + manifestURL + "'" }; if (!appFrame) { @@ -926,84 +938,76 @@ WebappsActor.prototype = { .then(onConnect); } return { actor: actor }; }); }, watchApps: function () { - this._openedApps = new Set(); // For now, app open/close events are only implement on b2g - if (Services.appinfo.ID == "{3c2e2abc-06d4-11e1-ac3b-374f68613e61}") { - let chromeWindow = Services.wm.getMostRecentWindow('navigator:browser'); - let systemAppFrame = chromeWindow.getContentWindow(); - systemAppFrame.addEventListener("appwillopen", this); - systemAppFrame.addEventListener("appterminated", this); + if (AppFrames) { + AppFrames.addObserver(this); } Services.obs.addObserver(this, "webapps-installed", false); Services.obs.addObserver(this, "webapps-uninstall", false); return {}; }, unwatchApps: function () { - this._openedApps = null; - if (Services.appinfo.ID == "{3c2e2abc-06d4-11e1-ac3b-374f68613e61}") { - let chromeWindow = Services.wm.getMostRecentWindow('navigator:browser'); - let systemAppFrame = chromeWindow.getContentWindow(); - systemAppFrame.removeEventListener("appwillopen", this); - systemAppFrame.removeEventListener("appterminated", this); + if (AppFrames) { + AppFrames.removeObserver(this); } Services.obs.removeObserver(this, "webapps-installed", false); Services.obs.removeObserver(this, "webapps-uninstall", false); return {}; }, - handleEvent: function (event) { - let manifestURL; - switch(event.type) { - case "appwillopen": - manifestURL = event.detail.manifestURL; + onAppFrameCreated: function (frame, isFirstAppFrame) { + if (!isFirstAppFrame) { + return; + } - // Ignore the event if we already received an appwillopen for this app - // (appwillopen is also fired when the app has been moved to background - // and get back to foreground) - if (this._openedApps.has(manifestURL)) { - return; - } - this._openedApps.add(manifestURL); + let manifestURL = frame.appManifestURL; + // Only track app frames + if (!manifestURL) { + return; + } - this._isAppAllowedForURL(manifestURL).then(allowed => { - if (allowed) { - this.conn.send({ from: this.actorID, - type: "appOpen", - manifestURL: manifestURL - }); - } - }); + this._isAppAllowedForURL(manifestURL).then(allowed => { + if (allowed) { + this.conn.send({ from: this.actorID, + type: "appOpen", + manifestURL: manifestURL + }); + } + }); + }, - break; + onAppFrameDestroyed: function (frame, isLastAppFrame) { + if (!isLastAppFrame) { + return; + } - case "appterminated": - manifestURL = event.detail.manifestURL; - this._openedApps.delete(manifestURL); + let manifestURL = frame.appManifestURL; + // Only track app frames + if (!manifestURL) { + return; + } - this._isAppAllowedForURL(manifestURL).then(allowed => { - if (allowed) { - this.conn.send({ from: this.actorID, - type: "appClose", - manifestURL: manifestURL - }); - } - }); - - break; - } + this._isAppAllowedForURL(manifestURL).then(allowed => { + if (allowed) { + this.conn.send({ from: this.actorID, + type: "appClose", + manifestURL: manifestURL + }); + } + }); }, observe: function (subject, topic, data) { let app = JSON.parse(data); if (topic == "webapps-installed") { this.conn.send({ from: this.actorID, type: "appInstall", manifestURL: app.manifestURL