author | Marco Castelluccio <mar.castelluccio@studenti.unina.it> |
Tue, 10 Sep 2013 20:59:04 -0400 | |
changeset 146506 | 8a0b5eb332ff958338e5a6bf8e57eff2ff5830fe |
parent 146458 | f73bed2856a8eeae0dbeb395f50718cf7ec23942 |
child 146507 | cdc016c22761a88039747c324f42f08c47df68bb |
push id | 25263 |
push user | cbook@mozilla.com |
push date | Wed, 11 Sep 2013 11:38:18 +0000 |
treeherder | mozilla-central@5417e5da2ceb [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | past |
bugs | 777428 |
milestone | 26.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/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -760,16 +760,17 @@ bin/libfreebl_32int64_3.so @BINPATH@/webapprt/components/CommandLineHandler.js @BINPATH@/webapprt/components/ContentPermission.js @BINPATH@/webapprt/components/DirectoryProvider.js @BINPATH@/webapprt/components/components.manifest @BINPATH@/webapprt/defaults/preferences/prefs.js @BINPATH@/webapprt/modules/Startup.jsm @BINPATH@/webapprt/modules/WebappRT.jsm @BINPATH@/webapprt/modules/WebappsHandler.jsm +@BINPATH@/webapprt/modules/RemoteDebugger.jsm #endif #ifdef MOZ_METRO @BINPATH@/components/MetroUIUtils.js @BINPATH@/components/MetroUIUtils.manifest [metro] ; gre resources @BINPATH@/CommandExecuteHandler@BIN_SUFFIX@
--- a/toolkit/devtools/server/actors/webbrowser.js +++ b/toolkit/devtools/server/actors/webbrowser.js @@ -32,17 +32,17 @@ function appShellDOMWindowType(aWindow) /* This is what nsIWindowMediator's enumerator checks. */ return aWindow.document.documentElement.getAttribute('windowtype'); } /** * Send Debugger:Shutdown events to all "navigator:browser" windows. */ function sendShutdownEvent() { - for (let win of allAppShellDOMWindows("navigator:browser")) { + for (let win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) { let evt = win.document.createEvent("Event"); evt.initEvent("Debugger:Shutdown", true, false); win.document.documentElement.dispatchEvent(evt); } } /** * Construct a root actor appropriate for use in a server running in a @@ -191,30 +191,30 @@ BrowserTabList.prototype._getSelectedBro return aWindow.gBrowser.selectedBrowser; }; BrowserTabList.prototype._getChildren = function(aWindow) { return aWindow.gBrowser.browsers; }; BrowserTabList.prototype.getList = function() { - let topXULWindow = windowMediator.getMostRecentWindow("navigator:browser"); + let topXULWindow = windowMediator.getMostRecentWindow(DebuggerServer.chromeWindowType); // As a sanity check, make sure all the actors presently in our map get // picked up when we iterate over all windows' tabs. let initialMapSize = this._actorByBrowser.size; let foundCount = 0; // To avoid mysterious behavior if tabs are closed or opened mid-iteration, // we update the map first, and then make a second pass over it to yield // the actors. Thus, the sequence yielded is always a snapshot of the // actors that were live when we began the iteration. // Iterate over all navigator:browser XUL windows. - for (let win of allAppShellDOMWindows("navigator:browser")) { + for (let win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) { let selectedBrowser = this._getSelectedBrowser(win); // For each tab in this XUL window, ensure that we have an actor for // it, reusing existing actors where possible. We actually iterate // over 'browser' XUL elements, and BrowserTabActor uses // browser.contentWindow.wrappedJSObject as the debuggee global. for (let browser of this._getChildren(win)) { // Do we have an existing actor for this browser? If not, create one. @@ -328,17 +328,17 @@ BrowserTabList.prototype._checkListening * The name of a guard property of 'this', indicating whether we're * already listening for those events. * @param aEventNames array of strings * An array of event names. */ BrowserTabList.prototype._listenForEventsIf = function(aShouldListen, aGuard, aEventNames) { if (!aShouldListen !== !this[aGuard]) { let op = aShouldListen ? "addEventListener" : "removeEventListener"; - for (let win of allAppShellDOMWindows("navigator:browser")) { + for (let win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) { for (let name of aEventNames) { win[op](name, this, false); } } this[aGuard] = aShouldListen; } }; @@ -386,17 +386,17 @@ BrowserTabList.prototype._listenToMediat */ BrowserTabList.prototype.onWindowTitleChange = () => { }; BrowserTabList.prototype.onOpenWindow = makeInfallible(function(aWindow) { let handleLoad = makeInfallible(() => { /* We don't want any further load events from this window. */ aWindow.removeEventListener("load", handleLoad, false); - if (appShellDOMWindowType(aWindow) !== "navigator:browser") + if (appShellDOMWindowType(aWindow) !== DebuggerServer.chromeWindowType) return; // Listen for future tab activity. if (this._listeningForTabOpen) { aWindow.addEventListener("TabOpen", this, false); aWindow.addEventListener("TabSelect", this, false); } if (this._listeningForTabClose) { @@ -420,17 +420,17 @@ BrowserTabList.prototype.onOpenWindow = aWindow.addEventListener("load", handleLoad, false); }, "BrowserTabList.prototype.onOpenWindow"); BrowserTabList.prototype.onCloseWindow = makeInfallible(function(aWindow) { aWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindow); - if (appShellDOMWindowType(aWindow) !== "navigator:browser") + if (appShellDOMWindowType(aWindow) !== DebuggerServer.chromeWindowType) return; /* * nsIWindowMediator deadlocks if you call its GetEnumerator method from * a nsIWindowMediatorListener's onCloseWindow hook (bug 873589), so * handle the close in a different tick. */ Services.tm.currentThread.dispatch(makeInfallible(() => {
--- a/toolkit/devtools/server/main.js +++ b/toolkit/devtools/server/main.js @@ -345,18 +345,18 @@ var DebuggerServer = { mod.module.unregister(mod.api); mod.api.destroy(); delete gRegisteredModules[id]; }, /** * Install Firefox-specific actors. */ - addBrowserActors: function DS_addBrowserActors() { - this.chromeWindowType = "navigator:browser"; + addBrowserActors: function(aWindowType) { + this.chromeWindowType = aWindowType ? aWindowType : "navigator:browser"; this.addActors("resource://gre/modules/devtools/server/actors/webbrowser.js"); this.addActors("resource://gre/modules/devtools/server/actors/script.js"); this.addGlobalActor(this.ChromeDebuggerActor, "chromeDebugger"); this.addActors("resource://gre/modules/devtools/server/actors/webconsole.js"); this.addActors("resource://gre/modules/devtools/server/actors/gcli.js"); if ("nsIProfiler" in Ci) this.addActors("resource://gre/modules/devtools/server/actors/profiler.js");
--- a/webapprt/CommandLineHandler.js +++ b/webapprt/CommandLineHandler.js @@ -16,16 +16,22 @@ CommandLineHandler.prototype = { QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]), handle: function handle(cmdLine) { let args = Cc["@mozilla.org/hash-property-bag;1"]. createInstance(Ci.nsIWritablePropertyBag); let inTestMode = this._handleTestMode(cmdLine, args); + let debugPort = this._handleDebugMode(cmdLine); + if (!isNaN(debugPort)) { + Cu.import("resource://webapprt/modules/RemoteDebugger.jsm"); + RemoteDebugger.init(debugPort); + } + if (inTestMode) { // Open the mochitest shim window, which configures the runtime for tests. Services.ww.openWindow(null, "chrome://webapprt/content/mochitest.xul", "_blank", "chrome,dialog=no", args); } else { @@ -36,16 +42,44 @@ CommandLineHandler.prototype = { "chrome,dialog=no,resizable,scrollbars,centerscreen", null); // Load the module to start up the app Cu.import("resource://webapprt/modules/Startup.jsm"); startup(window); } }, + /** + * Handle debug command line option. + * + * @param cmdLine A nsICommandLine object. + * + * @returns the port number if it's specified, the default port number if + * the debug option is specified, NaN if the debug option isn't + * specified or the port number isn't valid. + */ + _handleDebugMode: function(cmdLine) { + // -debug [port] + let idx = cmdLine.findFlag("debug", true); + if (idx < 0) { + return NaN; + } + + let port; + let portIdx = idx + 1; + if (portIdx < cmdLine.length) { + port = parseInt(cmdLine.getArgument(portIdx)); + if (port != NaN) { + return port; + } + } + + return Services.prefs.getIntPref('devtools.debugger.remote-port'); + }, + _handleTestMode: function _handleTestMode(cmdLine, args) { // -test-mode [url] let idx = cmdLine.findFlag("test-mode", true); if (idx < 0) return false; let url; let urlIdx = idx + 1; if (urlIdx < cmdLine.length) {
new file mode 100644 --- /dev/null +++ b/webapprt/RemoteDebugger.jsm @@ -0,0 +1,26 @@ +/* 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 = ["RemoteDebugger"]; + +let Cc = Components.classes; +let Ci = Components.interfaces; +let Cu = Components.utils; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import('resource://gre/modules/devtools/dbg-server.jsm'); + +this.RemoteDebugger = { + init: function(port) { + if (!DebuggerServer.initialized) { + DebuggerServer.init(); + DebuggerServer.addBrowserActors("webapprt:webapp"); + DebuggerServer.addActors("chrome://webapprt/content/dbg-webapp-actors.js"); + } + DebuggerServer.openListener(port); + } +}
new file mode 100644 --- /dev/null +++ b/webapprt/content/dbg-webapp-actors.js @@ -0,0 +1,126 @@ +/* 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'; + +let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise; + +/** + * WebappRT-specific actors. + */ + +/** + * Construct a root actor appropriate for use in a server running in the webapp + * runtime. The returned root actor: + * - respects the factories registered with DebuggerServer.addGlobalActor, + * - uses a WebappTabList to supply tab actors, + * - sends all webapprt:webapp window documents a Debugger:Shutdown event + * when it exits. + * + * * @param connection DebuggerServerConnection + * The conection to the client. + */ +function createRootActor(connection) +{ + let parameters = { + tabList: new WebappTabList(connection), + globalActorFactories: DebuggerServer.globalActorFactories, + onShutdown: sendShutdownEvent + }; + return new RootActor(connection, parameters); +} + +/** + * A live list of BrowserTabActors representing the current webapp windows, + * to be provided to the root actor to answer 'listTabs' requests. In the + * webapp runtime, only a single tab per window is ever present. + * + * @param connection DebuggerServerConnection + * The connection in which this list's tab actors may participate. + * + * @see BrowserTabList for more a extensive description of how tab list objects + * work. + */ +function WebappTabList(connection) +{ + BrowserTabList.call(this, connection); +} + +WebappTabList.prototype = Object.create(BrowserTabList.prototype); + +WebappTabList.prototype.constructor = WebappTabList; + +WebappTabList.prototype.getList = function() { + let topXULWindow = windowMediator.getMostRecentWindow(this._windowType); + + // As a sanity check, make sure all the actors presently in our map get + // picked up when we iterate over all windows. + let initialMapSize = this._actorByBrowser.size; + let foundCount = 0; + + // To avoid mysterious behavior if windows are closed or opened mid-iteration, + // we update the map first, and then make a second pass over it to yield + // the actors. Thus, the sequence yielded is always a snapshot of the + // actors that were live when we began the iteration. + + // Iterate over all webapprt:webapp XUL windows. + for (let win of allAppShellDOMWindows(this._windowType)) { + let browser = win.document.getElementById("content"); + if (!browser) { + continue; + } + + // Do we have an existing actor for this browser? If not, create one. + let actor = this._actorByBrowser.get(browser); + if (actor) { + foundCount++; + } else { + actor = new WebappTabActor(this._connection, browser); + this._actorByBrowser.set(browser, actor); + } + + actor.selected = (win == topXULWindow); + } + + if (this._testing && initialMapSize !== foundCount) { + throw Error("_actorByBrowser map contained actors for dead tabs"); + } + + this._mustNotify = true; + this._checkListening(); + + return promise.resolve([actor for ([_, actor] of this._actorByBrowser)]); +}; + +/** + * Creates a tab actor for handling requests to the single tab, like + * attaching and detaching. WebappTabActor respects the actor factories + * registered with DebuggerServer.addTabActor. + * + * We override the title of the XUL window in content/webapp.js so here + * we need to override the title property to avoid confusion to the user. + * We won't return the title of the contained browser, but the title of + * the webapp window. + * + * @param connection DebuggerServerConnection + * The conection to the client. + * @param browser browser + * The browser instance that contains this tab. + */ +function WebappTabActor(connection, browser) +{ + BrowserTabActor.call(this, connection, browser); +} + +WebappTabActor.prototype.constructor = WebappTabActor; + +WebappTabActor.prototype = Object.create(BrowserTabActor.prototype); + +Object.defineProperty(WebappTabActor.prototype, "title", { + get: function() { + return this.browser.ownerDocument.defaultView.document.title; + }, + enumerable: true, + configurable: false +});
--- a/webapprt/jar.mn +++ b/webapprt/jar.mn @@ -4,8 +4,9 @@ webapprt.jar: % content webapprt %content/ * content/webapp.js (content/webapp.js) * content/webapp.xul (content/webapp.xul) content/mochitest-shared.js (content/mochitest-shared.js) content/mochitest.js (content/mochitest.js) content/mochitest.xul (content/mochitest.xul) + content/dbg-webapp-actors.js (content/dbg-webapp-actors.js)
--- a/webapprt/moz.build +++ b/webapprt/moz.build @@ -17,12 +17,13 @@ TEST_DIRS += ['test'] EXTRA_COMPONENTS += [ 'CommandLineHandler.js', 'ContentPermission.js', 'DirectoryProvider.js', 'components.manifest', ] EXTRA_JS_MODULES += [ + 'RemoteDebugger.jsm', 'Startup.jsm', 'WebappRT.jsm', 'WebappsHandler.jsm', ]
--- a/webapprt/prefs.js +++ b/webapprt/prefs.js @@ -46,16 +46,19 @@ pref("dom.mozTCPSocket.enabled", true); // Enable smooth scrolling pref("general.smoothScroll", true); // Enable window resize and move pref("dom.always_allow_move_resize_window", true); pref("plugin.allowed_types", "application/x-shockwave-flash,application/futuresplash"); +pref("devtools.debugger.remote-enabled", true); +pref("devtools.debugger.force-local", true); + // The default for this pref reflects whether the build is capable of IPC. // (Turning it on in a no-IPC build will have no effect.) #ifdef XP_MACOSX // i386 ipc preferences pref("dom.ipc.plugins.enabled.i386", false); pref("dom.ipc.plugins.enabled.i386.flash player.plugin", true); // x86_64 ipc preferences pref("dom.ipc.plugins.enabled.x86_64", true);
--- a/webapprt/test/chrome/Makefile.in +++ b/webapprt/test/chrome/Makefile.in @@ -23,9 +23,13 @@ MOCHITEST_WEBAPPRT_CHROME_FILES = \ browser_geolocation-prompt-perm.js \ browser_geolocation-prompt-noperm.js \ geolocation-prompt-perm.webapp \ geolocation-prompt-perm.webapp^headers^ \ geolocation-prompt-noperm.webapp \ geolocation-prompt-noperm.webapp^headers^ \ geolocation-prompt-perm.html \ geolocation-prompt-noperm.html \ + browser_debugger.js \ + debugger.webapp \ + debugger.webapp^headers^ \ + debugger.html \ $(NULL)
new file mode 100644 --- /dev/null +++ b/webapprt/test/chrome/browser_debugger.js @@ -0,0 +1,39 @@ +Cu.import("resource://gre/modules/Services.jsm"); +let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {}); +let { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {}); +let { RemoteDebugger } = Cu.import("resource://webapprt/modules/RemoteDebugger.jsm", {}); + +function test() { + waitForExplicitFinish(); + + loadWebapp("debugger.webapp", undefined, () => { + RemoteDebugger.init(Services.prefs.getIntPref('devtools.debugger.remote-port')); + + let client = new DebuggerClient(DebuggerServer.connectPipe()); + client.connect(() => { + client.listTabs((aResponse) => { + is(aResponse.tabs[0].title, "Debugger Test Webapp", "Title correct"); + is(aResponse.tabs[0].url, "http://test/webapprtChrome/webapprt/test/chrome/debugger.html", "URL correct"); + ok(aResponse.tabs[0].consoleActor, "consoleActor set"); + ok(aResponse.tabs[0].gcliActor, "gcliActor set"); + ok(aResponse.tabs[0].styleEditorActor, "styleEditorActor set"); + ok(aResponse.tabs[0].inspectorActor, "inspectorActor set"); + ok(aResponse.tabs[0].traceActor, "traceActor set"); + ok(aResponse.chromeDebugger, "chromeDebugger set"); + ok(aResponse.consoleActor, "consoleActor set"); + ok(aResponse.gcliActor, "gcliActor set"); + ok(aResponse.profilerActor, "profilerActor set"); + ok(aResponse.gcliActor, "gcliActor set"); + ok(aResponse.deviceActor, "deviceActor set"); + + client.close(() => { + finish(); + }); + }); + }); + }); + + registerCleanupFunction(function() { + DebuggerServer.destroy(); + }); +}
new file mode 100644 --- /dev/null +++ b/webapprt/test/chrome/debugger.html @@ -0,0 +1,9 @@ +<!DOCTYPE HTML> +<html> + <head> + <meta charset="utf-8"> + </head> + <body> + <p>This is the test webapp.</p> + </body> +</html> \ No newline at end of file