mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
merge m-c to fx-team
This commit is contained in:
commit
646344e386
@ -38,6 +38,7 @@ DeviceRootActor.prototype = new BrowserRootActor();
|
||||
* Disconnects the actor from the browser window.
|
||||
*/
|
||||
DeviceRootActor.prototype.disconnect = function DRA_disconnect() {
|
||||
this._extraActors = null;
|
||||
let actor = this._tabActors.get(this.browser);
|
||||
if (actor) {
|
||||
actor.exit();
|
||||
@ -61,6 +62,8 @@ DeviceRootActor.prototype.onListTabs = function DRA_onListTabs() {
|
||||
let actorPool = new ActorPool(this.conn);
|
||||
actorPool.addActor(actor);
|
||||
|
||||
this._createExtraActors(DebuggerServer.globalActorFactories, actorPool);
|
||||
|
||||
// Now drop the old actorID -> actor map. Actors that still mattered were
|
||||
// added to the new map, others will go away.
|
||||
if (this._tabActorPool) {
|
||||
@ -69,11 +72,13 @@ DeviceRootActor.prototype.onListTabs = function DRA_onListTabs() {
|
||||
this._tabActorPool = actorPool;
|
||||
this.conn.addActorPool(this._tabActorPool);
|
||||
|
||||
return {
|
||||
let response = {
|
||||
'from': 'root',
|
||||
'selected': 0,
|
||||
'tabs': [actor.grip()]
|
||||
};
|
||||
this._appendExtraActors(response);
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -103,11 +108,23 @@ DeviceTabActor.prototype.grip = function DTA_grip() {
|
||||
'grip() should not be called on exited browser actor.');
|
||||
dbg_assert(this.actorID,
|
||||
'tab should have an actorID.');
|
||||
return {
|
||||
|
||||
let response = {
|
||||
'actor': this.actorID,
|
||||
'title': this.browser.title,
|
||||
'url': this.browser.document.documentURI
|
||||
};
|
||||
|
||||
// Walk over tab actors added by extensions and add them to a new ActorPool.
|
||||
let actorPool = new ActorPool(this.conn);
|
||||
this._createExtraActors(DebuggerServer.globalActorFactories, actorPool);
|
||||
if (!actorPool.isEmpty()) {
|
||||
this._tabActorPool = actorPool;
|
||||
this.conn.addActorPool(this._tabActorPool);
|
||||
}
|
||||
|
||||
this._appendExtraActors(response);
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -154,11 +154,8 @@ XPCOMUtils.defineLazyGetter(this, "SafeBrowsing", function() {
|
||||
});
|
||||
#endif
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gBrowserNewTabPreloader", function () {
|
||||
let tmp = {};
|
||||
Cu.import("resource:///modules/BrowserNewTabPreloader.jsm", tmp);
|
||||
return new tmp.BrowserNewTabPreloader();
|
||||
});
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "gBrowserNewTabPreloader",
|
||||
"resource://gre/modules/BrowserNewTabPreloader.jsm", "BrowserNewTabPreloader");
|
||||
|
||||
let gInitialPages = [
|
||||
"about:blank",
|
||||
@ -1409,12 +1406,6 @@ var gBrowserInit = {
|
||||
gSyncUI.init();
|
||||
#endif
|
||||
|
||||
// Don't preload new tab pages when the toolbar is hidden
|
||||
// (i.e. when the current window is a popup window).
|
||||
if (window.toolbar.visible) {
|
||||
gBrowserNewTabPreloader.init(window);
|
||||
}
|
||||
|
||||
gBrowserThumbnails.init();
|
||||
TabView.init();
|
||||
|
||||
@ -1563,10 +1554,6 @@ var gBrowserInit = {
|
||||
|
||||
Services.obs.removeObserver(gPluginHandler.pluginCrashed, "plugin-crashed");
|
||||
|
||||
if (!__lookupGetter__("gBrowserNewTabPreloader")) {
|
||||
gBrowserNewTabPreloader.uninit();
|
||||
}
|
||||
|
||||
try {
|
||||
gBrowser.removeProgressListener(window.XULBrowserWindow);
|
||||
gBrowser.removeTabsProgressListener(window.TabsProgressListener);
|
||||
|
@ -1067,7 +1067,12 @@
|
||||
style="min-width: 18em; width: 22em; max-width: 42em;" persist="width">
|
||||
<toolbar id="devtools-sidebar-toolbar"
|
||||
class="devtools-toolbar"
|
||||
nowindowdrag="true"/>
|
||||
nowindowdrag="true">
|
||||
<spacer flex="1"/>
|
||||
<toolbarbutton tooltiptext="&inspectSidebarCloseButton.tooltiptext;"
|
||||
class="devtools-closebutton"
|
||||
command="Inspector:Sidebar"/>
|
||||
</toolbar>
|
||||
<deck id="devtools-sidebar-deck" flex="1"/>
|
||||
</vbox>
|
||||
<splitter id="social-sidebar-splitter"
|
||||
|
14
browser/base/content/newtab/preload.xhtml
Normal file
14
browser/base/content/newtab/preload.xhtml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
||||
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||
|
||||
<!-- 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/. -->
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title></title>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
@ -46,6 +46,7 @@ browser.jar:
|
||||
content/browser/newtab/newTab.xul (content/newtab/newTab.xul)
|
||||
* content/browser/newtab/newTab.js (content/newtab/newTab.js)
|
||||
content/browser/newtab/newTab.css (content/newtab/newTab.css)
|
||||
content/browser/newtab/preload.xhtml (content/newtab/preload.xhtml)
|
||||
* content/browser/pageinfo/pageInfo.xul (content/pageinfo/pageInfo.xul)
|
||||
* content/browser/pageinfo/pageInfo.js (content/pageinfo/pageInfo.js)
|
||||
content/browser/pageinfo/pageInfo.css (content/pageinfo/pageInfo.css)
|
||||
|
@ -38,6 +38,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
|
||||
"resource:///modules/NewTabUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BrowserNewTabPreloader",
|
||||
"resource:///modules/BrowserNewTabPreloader.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PdfJs",
|
||||
"resource://pdf.js/PdfJs.jsm");
|
||||
|
||||
@ -345,6 +348,7 @@ BrowserGlue.prototype = {
|
||||
webappsUI.init();
|
||||
PageThumbs.init();
|
||||
NewTabUtils.init();
|
||||
BrowserNewTabPreloader.init();
|
||||
SignInToWebsiteUX.init();
|
||||
PdfJs.init();
|
||||
|
||||
@ -370,6 +374,7 @@ BrowserGlue.prototype = {
|
||||
this._shutdownPlaces();
|
||||
this._sanitizer.onShutdown();
|
||||
PageThumbs.uninit();
|
||||
BrowserNewTabPreloader.uninit();
|
||||
},
|
||||
|
||||
// All initial windows have opened.
|
||||
|
@ -466,39 +466,12 @@ ChromeDebuggerProcess.prototype = {
|
||||
*/
|
||||
_initServer: function RDP__initServer() {
|
||||
if (!DebuggerServer.initialized) {
|
||||
DebuggerServer.init(this._allowConnection);
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.addBrowserActors();
|
||||
}
|
||||
DebuggerServer.closeListener();
|
||||
DebuggerServer.openListener(DebuggerPreferences.remotePort);
|
||||
},
|
||||
|
||||
/**
|
||||
* Prompt the user to accept or decline the incoming connection.
|
||||
*
|
||||
* @return true if the connection should be permitted, false otherwise
|
||||
*/
|
||||
_allowConnection: function RDP__allowConnection() {
|
||||
let title = L10N.getStr("remoteIncomingPromptTitle");
|
||||
let msg = L10N.getStr("remoteIncomingPromptMessage");
|
||||
let disableButton = L10N.getStr("remoteIncomingPromptDisable");
|
||||
let prompt = Services.prompt;
|
||||
let flags = prompt.BUTTON_POS_0 * prompt.BUTTON_TITLE_OK +
|
||||
prompt.BUTTON_POS_1 * prompt.BUTTON_TITLE_CANCEL +
|
||||
prompt.BUTTON_POS_2 * prompt.BUTTON_TITLE_IS_STRING +
|
||||
prompt.BUTTON_POS_1_DEFAULT;
|
||||
let result = prompt.confirmEx(null, title, msg, flags, null, null,
|
||||
disableButton, null, { value: false });
|
||||
if (result == 0) {
|
||||
return true;
|
||||
}
|
||||
if (result == 2) {
|
||||
DebuggerServer.closeListener();
|
||||
Services.prefs.setBoolPref("devtools.debugger.remote-enabled", false);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes a profile for the remote debugger process.
|
||||
*/
|
||||
|
@ -20,8 +20,7 @@ MOCHITEST_BROWSER_TESTS = \
|
||||
browser_dbg_listtabs.js \
|
||||
browser_dbg_tabactor-01.js \
|
||||
browser_dbg_tabactor-02.js \
|
||||
browser_dbg_contextactor-01.js \
|
||||
browser_dbg_contextactor-02.js \
|
||||
browser_dbg_globalactor-01.js \
|
||||
testactors.js \
|
||||
browser_dbg_nav-01.js \
|
||||
browser_dbg_propertyview-01.js \
|
||||
|
@ -1,49 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Check extension-added context actor lifetimes.
|
||||
*/
|
||||
|
||||
var gTab1 = null;
|
||||
var gTab1Actor = null;
|
||||
|
||||
var gClient = null;
|
||||
|
||||
function test()
|
||||
{
|
||||
DebuggerServer.addActors("chrome://mochitests/content/browser/browser/devtools/debugger/test/testactors.js");
|
||||
|
||||
let transport = DebuggerServer.connectPipe();
|
||||
gClient = new DebuggerClient(transport);
|
||||
gClient.connect(function(aType, aTraits) {
|
||||
is(aType, "browser", "Root actor should identify itself as a browser.");
|
||||
get_tab();
|
||||
});
|
||||
}
|
||||
|
||||
function get_tab()
|
||||
{
|
||||
gTab1 = addTab(TAB1_URL, function() {
|
||||
attach_tab_actor_for_url(gClient, TAB1_URL, function(aGrip) {
|
||||
gTab1Actor = aGrip.actor;
|
||||
gClient.request({ to: aGrip.actor, type: "testContextActor1" }, function(aResponse) {
|
||||
ok(aResponse.actor, "testContextActor1 request should return an actor.");
|
||||
ok(aResponse.actor.indexOf("testone") >= 0,
|
||||
"testContextActor's actorPrefix should be used.");
|
||||
gClient.request({ to: aResponse.actor, type: "ping" }, function(aResponse) {
|
||||
is(aResponse.pong, "pong", "Actor should response to requests.");
|
||||
finish_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function finish_test()
|
||||
{
|
||||
gClient.close(function() {
|
||||
removeTab(gTab1);
|
||||
finish();
|
||||
});
|
||||
};
|
@ -1,59 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Check extension-added context actor lifetimes.
|
||||
*/
|
||||
|
||||
var gTab1 = null;
|
||||
var gTab1Actor = null;
|
||||
|
||||
var gClient = null;
|
||||
|
||||
function test()
|
||||
{
|
||||
DebuggerServer.addActors("chrome://mochitests/content/browser/browser/devtools/debugger/test/testactors.js");
|
||||
|
||||
let transport = DebuggerServer.connectPipe();
|
||||
gClient = new DebuggerClient(transport);
|
||||
gClient.connect(function(aType, aTraits) {
|
||||
is(aType, "browser", "Root actor should identify itself as a browser.");
|
||||
get_tab();
|
||||
});
|
||||
}
|
||||
|
||||
function get_tab()
|
||||
{
|
||||
gTab1 = addTab(TAB1_URL, function() {
|
||||
get_tab_actor_for_url(gClient, TAB1_URL, function(aGrip) {
|
||||
gTab1Actor = aGrip.actor;
|
||||
gClient.request({ to: gTab1Actor, type: "attach" }, function(aResponse) {
|
||||
gClient.request({ to: gTab1Actor, type: "testContextActor1" }, function(aResponse) {
|
||||
navigate_tab(aResponse.actor);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function navigate_tab(aTestActor)
|
||||
{
|
||||
gClient.addOneTimeListener("tabNavigated", function(aEvent, aResponse) {
|
||||
gClient.request({ to: aTestActor, type: "ping" }, function(aResponse) {
|
||||
// TODO: Currently the client is supposed to clean up after tabNavigated
|
||||
// events. We should remove this check, or even better, remove the whole
|
||||
// test.
|
||||
todo(aResponse.error, "noSuchActor", "testContextActor1 should have gone away with the navigation.");
|
||||
finish_test();
|
||||
});
|
||||
});
|
||||
gTab1.linkedBrowser.loadURI(TAB2_URL);
|
||||
}
|
||||
|
||||
function finish_test()
|
||||
{
|
||||
gClient.close(function() {
|
||||
removeTab(gTab1);
|
||||
finish();
|
||||
});
|
||||
}
|
36
browser/devtools/debugger/test/browser_dbg_globalactor-01.js
Normal file
36
browser/devtools/debugger/test/browser_dbg_globalactor-01.js
Normal file
@ -0,0 +1,36 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Check extension-added global actor API.
|
||||
*/
|
||||
|
||||
var gClient = null;
|
||||
|
||||
function test()
|
||||
{
|
||||
DebuggerServer.addActors("chrome://mochitests/content/browser/browser/devtools/debugger/test/testactors.js");
|
||||
|
||||
let transport = DebuggerServer.connectPipe();
|
||||
gClient = new DebuggerClient(transport);
|
||||
gClient.connect(function(aType, aTraits) {
|
||||
is(aType, "browser", "Root actor should identify itself as a browser.");
|
||||
gClient.listTabs(function(aResponse) {
|
||||
let globalActor = aResponse.testGlobalActor1;
|
||||
ok(globalActor, "Found the test tab actor.")
|
||||
ok(globalActor.indexOf("testone") >= 0,
|
||||
"testTabActor's actorPrefix should be used.");
|
||||
gClient.request({ to: globalActor, type: "ping" }, function(aResponse) {
|
||||
is(aResponse.pong, "pong", "Actor should respond to requests.");
|
||||
finish_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function finish_test()
|
||||
{
|
||||
gClient.close(function() {
|
||||
finish();
|
||||
});
|
||||
}
|
@ -24,17 +24,15 @@ function test()
|
||||
|
||||
function get_tab()
|
||||
{
|
||||
gTab1 = addTab(TAB1_URL, function () {
|
||||
attach_tab_actor_for_url(gClient, TAB1_URL, function (aGrip) {
|
||||
gTab1 = addTab(TAB1_URL, function() {
|
||||
attach_tab_actor_for_url(gClient, TAB1_URL, function(aGrip) {
|
||||
gTab1Actor = aGrip.actor;
|
||||
gClient.request({ to: aGrip.actor, type: "testTabActor1" }, function (aResponse) {
|
||||
ok(aResponse.actor, "testTabActor1 request should return an actor.");
|
||||
ok(aResponse.actor.indexOf("testone") >= 0,
|
||||
"testTabActor's actorPrefix should be used.");
|
||||
gClient.request({ to: aResponse.actor, type: "ping" }, function (aResponse) {
|
||||
is(aResponse.pong, "pong", "Actor should response to requests.");
|
||||
finish_test();
|
||||
});
|
||||
ok(aGrip.testTabActor1, "Found the test tab actor.")
|
||||
ok(aGrip.testTabActor1.indexOf("testone") >= 0,
|
||||
"testTabActor's actorPrefix should be used.");
|
||||
gClient.request({ to: aGrip.testTabActor1, type: "ping" }, function(aResponse) {
|
||||
is(aResponse.pong, "pong", "Actor should respond to requests.");
|
||||
finish_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -24,10 +24,14 @@ function test()
|
||||
|
||||
function get_tab()
|
||||
{
|
||||
gTab1 = addTab(TAB1_URL, function () {
|
||||
attach_tab_actor_for_url(gClient, TAB1_URL, function (aGrip) {
|
||||
gTab1 = addTab(TAB1_URL, function() {
|
||||
attach_tab_actor_for_url(gClient, TAB1_URL, function(aGrip) {
|
||||
gTab1Actor = aGrip.actor;
|
||||
gClient.request({ to: aGrip.actor, type: "testTabActor1" }, function (aResponse) {
|
||||
ok(aGrip.testTabActor1, "Found the test tab actor.")
|
||||
ok(aGrip.testTabActor1.indexOf("testone") >= 0,
|
||||
"testTabActor's actorPrefix should be used.");
|
||||
gClient.request({ to: aGrip.testTabActor1, type: "ping" }, function(aResponse) {
|
||||
is(aResponse.pong, "pong", "Actor should respond to requests.");
|
||||
close_tab(aResponse.actor);
|
||||
});
|
||||
});
|
||||
@ -37,10 +41,16 @@ function get_tab()
|
||||
function close_tab(aTestActor)
|
||||
{
|
||||
removeTab(gTab1);
|
||||
gClient.request({ to: aTestActor, type: "ping" }, function (aResponse) {
|
||||
is(aResponse.error, "noSuchActor", "testTabActor1 should have gone away with the tab.");
|
||||
try {
|
||||
gClient.request({ to: aTestActor, type: "ping" }, function (aResponse) {
|
||||
is(aResponse, undefined, "testTabActor1 didn't go away with the tab.");
|
||||
finish_test();
|
||||
});
|
||||
} catch (e) {
|
||||
is(e.message, "'ping' request packet has no destination.",
|
||||
"testTabActor1 should have gone away with the tab.");
|
||||
finish_test();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function finish_test()
|
||||
|
@ -24,6 +24,9 @@ let gEnableRemote = Services.prefs.getBoolPref("devtools.debugger.remote-enabled
|
||||
Services.prefs.setBoolPref("devtools.debugger.remote-enabled", true);
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.setBoolPref("devtools.debugger.remote-enabled", gEnableRemote);
|
||||
|
||||
// Properly shut down the server to avoid memory leaks.
|
||||
DebuggerServer.destroy();
|
||||
});
|
||||
|
||||
if (!DebuggerServer.initialized) {
|
||||
|
@ -1,20 +1,15 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function TestActor1(aConnection, aTab, aOnDisconnect)
|
||||
function TestActor1(aConnection, aTab)
|
||||
{
|
||||
this.conn = aConnection;
|
||||
this.tab = aTab;
|
||||
this.onDisconnect = aOnDisconnect;
|
||||
}
|
||||
|
||||
TestActor1.prototype = {
|
||||
actorPrefix: "testone",
|
||||
|
||||
disconnect: function TA1_disconnect() {
|
||||
this.onDisconnect();
|
||||
},
|
||||
|
||||
grip: function TA1_grip() {
|
||||
return { actor: this.actorID,
|
||||
test: "TestActor1" };
|
||||
@ -29,30 +24,8 @@ TestActor1.prototype.requestTypes = {
|
||||
"ping": TestActor1.prototype.onPing
|
||||
};
|
||||
|
||||
DebuggerServer.addTabRequest("testTabActor1", function (aTab) {
|
||||
if (aTab._testTabActor1) {
|
||||
return aTab._testTabActor1.grip();
|
||||
}
|
||||
|
||||
let actor = new TestActor1(aTab.conn, aTab.browser, function () {
|
||||
delete aTab._testTabActor1;
|
||||
});
|
||||
aTab.tabActorPool.addActor(actor);
|
||||
aTab._testTabActor1 = actor;
|
||||
return actor.grip();
|
||||
});
|
||||
|
||||
|
||||
DebuggerServer.addTabRequest("testContextActor1", function (aTab, aRequest) {
|
||||
if (aTab._testContextActor1) {
|
||||
return aTab._testContextActor1.grip();
|
||||
}
|
||||
|
||||
let actor = new TestActor1(aTab.conn, aTab.browser, function () {
|
||||
delete aTab._testContextActor1;
|
||||
});
|
||||
aTab.contextActorPool.addActor(actor);
|
||||
aTab._testContextActor1 = actor;
|
||||
return actor.grip();
|
||||
});
|
||||
DebuggerServer.removeTabActor(TestActor1);
|
||||
DebuggerServer.removeGlobalActor(TestActor1);
|
||||
|
||||
DebuggerServer.addTabActor(TestActor1, "testTabActor1");
|
||||
DebuggerServer.addGlobalActor(TestActor1, "testGlobalActor1");
|
||||
|
@ -1647,8 +1647,9 @@ InspectorStyleSidebar.prototype = {
|
||||
btn.setAttribute("image", aRegObj.icon || "");
|
||||
btn.setAttribute("type", "radio");
|
||||
btn.setAttribute("group", "sidebar-tools");
|
||||
this._toolbar.appendChild(btn);
|
||||
|
||||
let spacer = this._toolbar.querySelector("spacer");
|
||||
this._toolbar.insertBefore(btn, spacer);
|
||||
// create tool iframe
|
||||
let frame = this._chromeDoc.createElement("iframe");
|
||||
frame.setAttribute("flex", "1");
|
||||
|
@ -235,6 +235,7 @@ These should match what Safari and other Apple applications use on OS X Lion. --
|
||||
<!ENTITY scratchpad.keytext "F4">
|
||||
|
||||
<!ENTITY inspectCloseButton.tooltiptext "Close Inspector">
|
||||
<!ENTITY inspectSidebarCloseButton.tooltiptext "Close sidebar">
|
||||
|
||||
<!ENTITY devToolbarCloseButton.tooltiptext "Close Developer Toolbar">
|
||||
<!ENTITY devToolbarMenu.label "Developer Toolbar">
|
||||
|
@ -126,19 +126,6 @@ loadingText=Loading\u2026
|
||||
# %1$S=URL, %2$S=status code
|
||||
loadingError=Error loading %1$S: %2$S
|
||||
|
||||
# LOCALIZATION NOTE (remoteIncomingPromptTitle): The title displayed on the
|
||||
# dialog that prompts the user to allow the incoming connection.
|
||||
remoteIncomingPromptTitle=Incoming Connection
|
||||
|
||||
# LOCALIZATION NOTE (remoteIncomingPromptMessage): The message displayed on the
|
||||
# dialog that prompts the user to allow the incoming connection.
|
||||
remoteIncomingPromptMessage=An incoming request to permit remote debugging connection was detected. A remote client can take complete control over your browser! Allow connection?
|
||||
|
||||
# LOCALIZATION NOTE (remoteIncomingPromptDisable): The label displayed on the
|
||||
# third button in the incoming connection dialog that lets the user disable the
|
||||
# remote debugger server.
|
||||
remoteIncomingPromptDisable=Disable
|
||||
|
||||
# LOCALIZATION NOTE (emptyVariablesText): The text that is displayed in the
|
||||
# variables pane when there are no variables to display.
|
||||
emptyVariablesText=No variables to display.
|
||||
|
@ -7,154 +7,220 @@
|
||||
let EXPORTED_SYMBOLS = ["BrowserNewTabPreloader"];
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
const PREF_NEWTAB_URL = "browser.newtab.url";
|
||||
const PREF_NEWTAB_PRELOAD = "browser.newtab.preload";
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
const PREF_BRANCH = "browser.newtab.";
|
||||
|
||||
function BrowserNewTabPreloader() {
|
||||
}
|
||||
let BrowserNewTabPreloader = {
|
||||
init: function Preloader_init() {
|
||||
Preferences.init();
|
||||
|
||||
BrowserNewTabPreloader.prototype = {
|
||||
_url: null,
|
||||
_window: null,
|
||||
_browser: null,
|
||||
_enabled: null,
|
||||
|
||||
init: function Preloader_init(aWindow) {
|
||||
if (this._window) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._window = aWindow;
|
||||
this._enabled = Preferences.enabled;
|
||||
this._url = Preferences.url;
|
||||
Preferences.addObserver(this);
|
||||
|
||||
if (this._enabled) {
|
||||
this._createBrowser();
|
||||
if (Preferences.enabled) {
|
||||
HiddenBrowser.create();
|
||||
}
|
||||
},
|
||||
|
||||
uninit: function Preloader_uninit() {
|
||||
if (!this._window) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._browser) {
|
||||
this._browser.parentNode.removeChild(this._browser);
|
||||
this._browser = null;
|
||||
}
|
||||
|
||||
this._window = null;
|
||||
Preferences.removeObserver(this);
|
||||
HostFrame.destroy();
|
||||
Preferences.uninit();
|
||||
HiddenBrowser.destroy();
|
||||
},
|
||||
|
||||
newTab: function Preloader_newTab(aTab) {
|
||||
if (!this._window || !this._enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tabbrowser = this._window.gBrowser;
|
||||
if (tabbrowser && this._isPreloaded()) {
|
||||
tabbrowser.swapNewTabWithBrowser(aTab, this._browser);
|
||||
}
|
||||
},
|
||||
|
||||
observe: function Preloader_observe(aEnabled, aURL) {
|
||||
if (this._url != aURL) {
|
||||
this._url = aURL;
|
||||
|
||||
if (this._enabled && aEnabled) {
|
||||
// We're still enabled but the newtab URL has changed.
|
||||
this._browser.setAttribute("src", aURL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._enabled && !aEnabled) {
|
||||
// We got disabled. Remove the browser.
|
||||
this._browser.parentNode.removeChild(this._browser);
|
||||
this._browser = null;
|
||||
this._enabled = false;
|
||||
} else if (!this._enabled && aEnabled) {
|
||||
// We got enabled. Create a browser and start preloading.
|
||||
this._createBrowser();
|
||||
this._enabled = true;
|
||||
}
|
||||
},
|
||||
|
||||
_createBrowser: function Preloader_createBrowser() {
|
||||
let document = this._window.document;
|
||||
this._browser = document.createElement("browser");
|
||||
this._browser.setAttribute("type", "content");
|
||||
this._browser.setAttribute("src", this._url);
|
||||
this._browser.collapsed = true;
|
||||
|
||||
let panel = document.getElementById("browser-panel");
|
||||
panel.appendChild(this._browser);
|
||||
},
|
||||
|
||||
_isPreloaded: function Preloader_isPreloaded() {
|
||||
return this._browser &&
|
||||
this._browser.contentDocument &&
|
||||
this._browser.contentDocument.readyState == "complete" &&
|
||||
this._browser.currentURI.spec == this._url;
|
||||
HiddenBrowser.swapWithNewTab(aTab);
|
||||
}
|
||||
};
|
||||
|
||||
let Preferences = {
|
||||
_observers: [],
|
||||
Object.freeze(BrowserNewTabPreloader);
|
||||
|
||||
get _branch() {
|
||||
delete this._branch;
|
||||
return this._branch = Services.prefs.getBranch("browser.newtab.");
|
||||
},
|
||||
let Preferences = {
|
||||
_enabled: null,
|
||||
_branch: null,
|
||||
_url: null,
|
||||
|
||||
get enabled() {
|
||||
if (!this._branch.getBoolPref("preload")) {
|
||||
return false;
|
||||
if (this._enabled === null) {
|
||||
this._enabled = this._branch.getBoolPref("preload") &&
|
||||
!this._branch.prefHasUserValue("url") &&
|
||||
this.url && this.url != "about:blank";
|
||||
}
|
||||
|
||||
if (this._branch.prefHasUserValue("url")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let url = this.url;
|
||||
return url && url != "about:blank";
|
||||
return this._enabled;
|
||||
},
|
||||
|
||||
get url() {
|
||||
return this._branch.getCharPref("url");
|
||||
},
|
||||
|
||||
addObserver: function Preferences_addObserver(aObserver) {
|
||||
let index = this._observers.indexOf(aObserver);
|
||||
if (index == -1) {
|
||||
if (this._observers.length == 0) {
|
||||
this._branch.addObserver("", this, false);
|
||||
}
|
||||
this._observers.push(aObserver);
|
||||
if (this._url === null) {
|
||||
this._url = this._branch.getCharPref("url");
|
||||
}
|
||||
|
||||
return this._url;
|
||||
},
|
||||
|
||||
removeObserver: function Preferences_removeObserver(aObserver) {
|
||||
let index = this._observers.indexOf(aObserver);
|
||||
if (index > -1) {
|
||||
if (this._observers.length == 1) {
|
||||
this._branch.removeObserver("", this);
|
||||
}
|
||||
this._observers.splice(index, 1);
|
||||
init: function Preferences_init() {
|
||||
this._branch = Services.prefs.getBranch(PREF_BRANCH);
|
||||
this._branch.addObserver("", this, false);
|
||||
},
|
||||
|
||||
uninit: function Preferences_uninit() {
|
||||
if (this._branch) {
|
||||
this._branch.removeObserver("", this);
|
||||
this._branch = null;
|
||||
}
|
||||
},
|
||||
|
||||
observe: function Preferences_observe(aSubject, aTopic, aData) {
|
||||
let url = this.url;
|
||||
let enabled = this.enabled;
|
||||
let {url, enabled} = this;
|
||||
this._url = this._enabled = null;
|
||||
|
||||
for (let obs of this._observers) {
|
||||
obs.observe(enabled, url);
|
||||
if (enabled && !this.enabled) {
|
||||
HiddenBrowser.destroy();
|
||||
} else if (!enabled && this.enabled) {
|
||||
HiddenBrowser.create();
|
||||
} else if (this._browser && url != this.url) {
|
||||
HiddenBrowser.update(this.url);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let HiddenBrowser = {
|
||||
get isPreloaded() {
|
||||
return this._browser &&
|
||||
this._browser.contentDocument &&
|
||||
this._browser.contentDocument.readyState == "complete" &&
|
||||
this._browser.currentURI.spec == Preferences.url;
|
||||
},
|
||||
|
||||
swapWithNewTab: function HiddenBrowser_swapWithNewTab(aTab) {
|
||||
if (this.isPreloaded) {
|
||||
let tabbrowser = aTab.ownerDocument.defaultView.gBrowser;
|
||||
if (tabbrowser) {
|
||||
tabbrowser.swapNewTabWithBrowser(aTab, this._browser);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
create: function HiddenBrowser_create() {
|
||||
HostFrame.getFrame(function (aFrame) {
|
||||
let doc = aFrame.document;
|
||||
this._browser = doc.createElementNS(XUL_NS, "browser");
|
||||
this._browser.setAttribute("type", "content");
|
||||
this._browser.setAttribute("src", Preferences.url);
|
||||
doc.documentElement.appendChild(this._browser);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
update: function HiddenBrowser_update(aURL) {
|
||||
this._browser.setAttribute("src", aURL);
|
||||
},
|
||||
|
||||
destroy: function HiddenBrowser_destroy() {
|
||||
if (this._browser) {
|
||||
this._browser.parentNode.removeChild(this._browser);
|
||||
this._browser = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let HostFrame = {
|
||||
_listener: null,
|
||||
_privilegedFrame: null,
|
||||
|
||||
_privilegedContentTypes: {
|
||||
"application/vnd.mozilla.xul+xml": true,
|
||||
"application/xhtml+xml": true
|
||||
},
|
||||
|
||||
get _frame() {
|
||||
delete this._frame;
|
||||
return this._frame = Services.appShell.hiddenDOMWindow;
|
||||
},
|
||||
|
||||
get _isReady() {
|
||||
let readyState = this._frame.document.readyState;
|
||||
return (readyState == "complete" || readyState == "interactive");
|
||||
},
|
||||
|
||||
get _isPrivileged() {
|
||||
return (this._frame.location.protocol == "chrome:" &&
|
||||
this._frame.document.contentType in this._privilegedContentTypes);
|
||||
},
|
||||
|
||||
getFrame: function HostFrame_getFrame(aCallback) {
|
||||
if (this._isReady && !this._isPrivileged) {
|
||||
this._createPrivilegedFrame();
|
||||
}
|
||||
|
||||
if (this._isReady) {
|
||||
aCallback(this._frame);
|
||||
} else {
|
||||
this._waitUntilLoaded(aCallback);
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function HostFrame_destroy() {
|
||||
delete this._frame;
|
||||
this._listener = null;
|
||||
},
|
||||
|
||||
_createPrivilegedFrame: function HostFrame_createPrivilegedFrame() {
|
||||
let doc = this._frame.document;
|
||||
let iframe = doc.createElement("iframe");
|
||||
iframe.setAttribute("src", "chrome://browser/content/newtab/preload.xhtml");
|
||||
doc.documentElement.appendChild(iframe);
|
||||
this._frame = iframe.contentWindow;
|
||||
},
|
||||
|
||||
_waitUntilLoaded: function HostFrame_waitUntilLoaded(aCallback) {
|
||||
this._listener = new HiddenWindowLoadListener(this._frame, function () {
|
||||
HostFrame.getFrame(aCallback);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function HiddenWindowLoadListener(aWindow, aCallback) {
|
||||
this._window = aWindow;
|
||||
this._callback = aCallback;
|
||||
|
||||
let docShell = Services.appShell.hiddenWindow.docShell;
|
||||
this._webProgress = docShell.QueryInterface(Ci.nsIWebProgress);
|
||||
this._webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_ALL);
|
||||
}
|
||||
|
||||
HiddenWindowLoadListener.prototype = {
|
||||
_window: null,
|
||||
_callback: null,
|
||||
_webProgress: null,
|
||||
|
||||
_destroy: function HiddenWindowLoadListener_destroy() {
|
||||
this._webProgress.removeProgressListener(this);
|
||||
this._window = null;
|
||||
this._callback = null;
|
||||
this._webProgress = null;
|
||||
},
|
||||
|
||||
onStateChange:
|
||||
function HiddenWindowLoadListener_onStateChange(aWebProgress, aRequest,
|
||||
aStateFlags, aStatus) {
|
||||
if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
|
||||
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
|
||||
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW &&
|
||||
this._window == aWebProgress.DOMWindow) {
|
||||
this._callback();
|
||||
this._destroy();
|
||||
}
|
||||
},
|
||||
|
||||
onStatusChange: function () {},
|
||||
onLocationChange: function () {},
|
||||
onProgressChange: function () {},
|
||||
onSecurityChange: function () {},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
|
||||
Ci.nsISupportsWeakReference])
|
||||
};
|
||||
|
@ -43,7 +43,7 @@ DeviceRootActor.prototype.onListTabs = function DRA_onListTabs() {
|
||||
// an ActorPool.
|
||||
|
||||
let actorPool = new ActorPool(this.conn);
|
||||
let actorList = [];
|
||||
let tabActorList = [];
|
||||
|
||||
let win = windowMediator.getMostRecentWindow("navigator:browser");
|
||||
this.browser = win.BrowserApp.selectedBrowser;
|
||||
@ -59,7 +59,7 @@ DeviceRootActor.prototype.onListTabs = function DRA_onListTabs() {
|
||||
let browser = tab.browser;
|
||||
|
||||
if (browser == this.browser) {
|
||||
selected = actorList.length;
|
||||
selected = tabActorList.length;
|
||||
}
|
||||
|
||||
let actor = this._tabActors.get(browser);
|
||||
@ -70,9 +70,11 @@ DeviceRootActor.prototype.onListTabs = function DRA_onListTabs() {
|
||||
}
|
||||
|
||||
actorPool.addActor(actor);
|
||||
actorList.push(actor);
|
||||
tabActorList.push(actor);
|
||||
}
|
||||
|
||||
this._createExtraActors(DebuggerServer.globalActorFactories, actorPool);
|
||||
|
||||
// Now drop the old actorID -> actor map. Actors that still
|
||||
// mattered were added to the new map, others will go
|
||||
// away.
|
||||
@ -83,10 +85,13 @@ DeviceRootActor.prototype.onListTabs = function DRA_onListTabs() {
|
||||
this._tabActorPool = actorPool;
|
||||
this.conn.addActorPool(this._tabActorPool);
|
||||
|
||||
return { "from": "root",
|
||||
"selected": selected,
|
||||
"tabs": [actor.grip()
|
||||
for each (actor in actorList)] };
|
||||
let response = {
|
||||
"from": "root",
|
||||
"selected": selected,
|
||||
"tabs": [actor.grip() for (actor of tabActorList)]
|
||||
};
|
||||
this._appendExtraActors(response);
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -166,7 +166,8 @@ const ThreadStateTypes = {
|
||||
const UnsolicitedNotifications = {
|
||||
"newScript": "newScript",
|
||||
"tabDetached": "tabDetached",
|
||||
"tabNavigated": "tabNavigated"
|
||||
"tabNavigated": "tabNavigated",
|
||||
"profilerStateChanged": "profilerStateChanged"
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -31,20 +31,24 @@ function BrowserRootActor(aConnection)
|
||||
this.conn = aConnection;
|
||||
this._tabActors = new WeakMap();
|
||||
this._tabActorPool = null;
|
||||
this._actorFactories = null;
|
||||
// A map of actor names to actor instances provided by extensions.
|
||||
this._extraActors = {};
|
||||
|
||||
this.onTabClosed = this.onTabClosed.bind(this);
|
||||
windowMediator.addListener(this);
|
||||
}
|
||||
|
||||
BrowserRootActor.prototype = {
|
||||
|
||||
/**
|
||||
* Return a 'hello' packet as specified by the Remote Debugging Protocol.
|
||||
*/
|
||||
sayHello: function BRA_sayHello() {
|
||||
return { from: "root",
|
||||
applicationType: "browser",
|
||||
traits: [] };
|
||||
return {
|
||||
from: "root",
|
||||
applicationType: "browser",
|
||||
traits: {}
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
@ -52,6 +56,7 @@ BrowserRootActor.prototype = {
|
||||
*/
|
||||
disconnect: function BRA_disconnect() {
|
||||
windowMediator.removeListener(this);
|
||||
this._extraActors = null;
|
||||
|
||||
// We may have registered event listeners on browser windows to
|
||||
// watch for tab closes, remove those.
|
||||
@ -77,7 +82,7 @@ BrowserRootActor.prototype = {
|
||||
// an ActorPool.
|
||||
|
||||
let actorPool = new ActorPool(this.conn);
|
||||
let actorList = [];
|
||||
let tabActorList = [];
|
||||
|
||||
// Walk over open browser windows.
|
||||
let e = windowMediator.getEnumerator("navigator:browser");
|
||||
@ -95,7 +100,7 @@ BrowserRootActor.prototype = {
|
||||
let browsers = win.getBrowser().browsers;
|
||||
for each (let browser in browsers) {
|
||||
if (browser == selectedBrowser && win == top) {
|
||||
selected = actorList.length;
|
||||
selected = tabActorList.length;
|
||||
}
|
||||
let actor = this._tabActors.get(browser);
|
||||
if (!actor) {
|
||||
@ -104,10 +109,12 @@ BrowserRootActor.prototype = {
|
||||
this._tabActors.set(browser, actor);
|
||||
}
|
||||
actorPool.addActor(actor);
|
||||
actorList.push(actor);
|
||||
tabActorList.push(actor);
|
||||
}
|
||||
}
|
||||
|
||||
this._createExtraActors(DebuggerServer.globalActorFactories, actorPool);
|
||||
|
||||
// Now drop the old actorID -> actor map. Actors that still
|
||||
// mattered were added to the new map, others will go
|
||||
// away.
|
||||
@ -117,10 +124,40 @@ BrowserRootActor.prototype = {
|
||||
this._tabActorPool = actorPool;
|
||||
this.conn.addActorPool(this._tabActorPool);
|
||||
|
||||
return { "from": "root",
|
||||
"selected": selected,
|
||||
"tabs": [actor.grip()
|
||||
for each (actor in actorList)] };
|
||||
let response = {
|
||||
"from": "root",
|
||||
"selected": selected,
|
||||
"tabs": [actor.grip() for (actor of tabActorList)]
|
||||
};
|
||||
this._appendExtraActors(response);
|
||||
return response;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds dynamically-added actors from add-ons to the provided pool.
|
||||
*/
|
||||
_createExtraActors: function BRA_createExtraActors(aFactories, aPool) {
|
||||
// Walk over global actors added by extensions.
|
||||
for (let name in aFactories) {
|
||||
let actor = this._extraActors[name];
|
||||
if (!actor) {
|
||||
actor = aFactories[name].bind(null, this.conn);
|
||||
actor.prototype = aFactories[name].prototype;
|
||||
actor.parentID = this.actorID;
|
||||
this._extraActors[name] = actor;
|
||||
}
|
||||
aPool.addActor(actor);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Appends the extra actors to the specified object.
|
||||
*/
|
||||
_appendExtraActors: function BRA_appendExtraActors(aObject) {
|
||||
for (let name in this._extraActors) {
|
||||
let actor = this._extraActors[name];
|
||||
aObject[name] = actor.actorID;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -203,7 +240,12 @@ function BrowserTabActor(aConnection, aBrowser, aTabBrowser)
|
||||
this.conn = aConnection;
|
||||
this._browser = aBrowser;
|
||||
this._tabbrowser = aTabBrowser;
|
||||
this._tabActorPool = null;
|
||||
// A map of actor names to actor instances provided by extensions.
|
||||
this._extraActors = {};
|
||||
|
||||
this._createExtraActors = BrowserRootActor.prototype._createExtraActors.bind(this);
|
||||
this._appendExtraActors = BrowserRootActor.prototype._appendExtraActors.bind(this);
|
||||
this._onWindowCreated = this.onWindowCreated.bind(this);
|
||||
}
|
||||
|
||||
@ -245,6 +287,7 @@ BrowserTabActor.prototype = {
|
||||
this.conn.removeActor(aActor);
|
||||
},
|
||||
|
||||
// A constant prefix that will be used to form the actor ID by the server.
|
||||
actorPrefix: "tab",
|
||||
|
||||
grip: function BTA_grip() {
|
||||
@ -252,9 +295,23 @@ BrowserTabActor.prototype = {
|
||||
"grip() shouldn't be called on exited browser actor.");
|
||||
dbg_assert(this.actorID,
|
||||
"tab should have an actorID.");
|
||||
return { actor: this.actorID,
|
||||
title: this.browser.contentTitle,
|
||||
url: this.browser.currentURI.spec }
|
||||
|
||||
let response = {
|
||||
actor: this.actorID,
|
||||
title: this.browser.contentTitle,
|
||||
url: this.browser.currentURI.spec
|
||||
};
|
||||
|
||||
// Walk over tab actors added by extensions and add them to a new ActorPool.
|
||||
let actorPool = new ActorPool(this.conn);
|
||||
this._createExtraActors(DebuggerServer.tabActorFactories, actorPool);
|
||||
if (!actorPool.isEmpty()) {
|
||||
this._tabActorPool = actorPool;
|
||||
this.conn.addActorPool(this._tabActorPool);
|
||||
}
|
||||
|
||||
this._appendExtraActors(response);
|
||||
return response;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -266,6 +323,7 @@ BrowserTabActor.prototype = {
|
||||
if (this._progressListener) {
|
||||
this._progressListener.destroy();
|
||||
}
|
||||
this._extraActors = null;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -370,6 +428,10 @@ BrowserTabActor.prototype = {
|
||||
// Shut down actors that belong to this tab's pool.
|
||||
this.conn.removeActorPool(this._tabPool);
|
||||
this._tabPool = null;
|
||||
if (this._tabActorPool) {
|
||||
this.conn.removeActorPool(this._tabActorPool);
|
||||
this._tabActorPool = null;
|
||||
}
|
||||
|
||||
this._attached = false;
|
||||
},
|
||||
@ -535,9 +597,14 @@ DebuggerProgressListener.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
// DebuggerServer extension API.
|
||||
|
||||
/**
|
||||
* Registers handlers for new request types defined dynamically. This is used
|
||||
* for example by add-ons to augment the functionality of the tab actor.
|
||||
* Registers handlers for new tab-scoped request types defined dynamically.
|
||||
* This is used for example by add-ons to augment the functionality of the tab
|
||||
* actor.
|
||||
* TODO: remove this API in the next release after bug 753401 lands, once all
|
||||
* our experimental add-ons have been converted to the new API.
|
||||
*
|
||||
* @param aName string
|
||||
* The name of the new request type.
|
||||
@ -552,3 +619,79 @@ DebuggerServer.addTabRequest = function DS_addTabRequest(aName, aFunction) {
|
||||
return aFunction(this, aRequest);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers handlers for new tab-scoped request types defined dynamically.
|
||||
* This is used for example by add-ons to augment the functionality of the tab
|
||||
* actor.
|
||||
*
|
||||
* @param aFunction function
|
||||
* The constructor function for this request type.
|
||||
* @param aName string [optional]
|
||||
* The name of the new request type. If this is not present, the
|
||||
* actorPrefix property of the constructor prototype is used.
|
||||
*/
|
||||
DebuggerServer.addTabActor = function DS_addTabActor(aFunction, aName) {
|
||||
let name = aName ? aName : aFunction.prototype.actorPrefix;
|
||||
if (["title", "url", "actor"].indexOf(name) != -1) {
|
||||
throw Error(name + " is not allowed");
|
||||
}
|
||||
if (DebuggerServer.tabActorFactories.hasOwnProperty(name)) {
|
||||
throw Error(name + " already exists");
|
||||
}
|
||||
DebuggerServer.tabActorFactories[name] = aFunction;
|
||||
};
|
||||
|
||||
/**
|
||||
* Unregisters the handler for the specified tab-scoped request type.
|
||||
* This may be used for example by add-ons when shutting down or upgrading.
|
||||
*
|
||||
* @param aFunction function
|
||||
* The constructor function for this request type.
|
||||
*/
|
||||
DebuggerServer.removeTabActor = function DS_removeTabActor(aFunction) {
|
||||
for (let name in DebuggerServer.tabActorFactories) {
|
||||
let handler = DebuggerServer.tabActorFactories[name];
|
||||
if (handler.name == aFunction.name) {
|
||||
delete DebuggerServer.tabActorFactories[name];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers handlers for new browser-scoped request types defined dynamically.
|
||||
* This is used for example by add-ons to augment the functionality of the root
|
||||
* actor.
|
||||
*
|
||||
* @param aFunction function
|
||||
* The constructor function for this request type.
|
||||
* @param aName string [optional]
|
||||
* The name of the new request type. If this is not present, the
|
||||
* actorPrefix property of the constructor prototype is used.
|
||||
*/
|
||||
DebuggerServer.addGlobalActor = function DS_addGlobalActor(aFunction, aName) {
|
||||
let name = aName ? aName : aFunction.prototype.actorPrefix;
|
||||
if (["from", "tabs", "selected"].indexOf(name) != -1) {
|
||||
throw Error(name + " is not allowed");
|
||||
}
|
||||
if (DebuggerServer.globalActorFactories.hasOwnProperty(name)) {
|
||||
throw Error(name + " already exists");
|
||||
}
|
||||
DebuggerServer.globalActorFactories[name] = aFunction;
|
||||
};
|
||||
|
||||
/**
|
||||
* Unregisters the handler for the specified browser-scoped request type.
|
||||
* This may be used for example by add-ons when shutting down or upgrading.
|
||||
*
|
||||
* @param aFunction function
|
||||
* The constructor function for this request type.
|
||||
*/
|
||||
DebuggerServer.removeGlobalActor = function DS_removeGlobalActor(aFunction) {
|
||||
for (let name in DebuggerServer.globalActorFactories) {
|
||||
let handler = DebuggerServer.globalActorFactories[name];
|
||||
if (handler.name == aFunction.name) {
|
||||
delete DebuggerServer.globalActorFactories[name];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
90
toolkit/devtools/debugger/server/dbg-profiler-actors.js
Normal file
90
toolkit/devtools/debugger/server/dbg-profiler-actors.js
Normal file
@ -0,0 +1,90 @@
|
||||
/* 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";
|
||||
|
||||
/**
|
||||
* Creates a ProfilerActor. ProfilerActor provides remote access to the
|
||||
* built-in profiler module.
|
||||
*/
|
||||
function ProfilerActor(aConnection)
|
||||
{
|
||||
this._conn = aConnection;
|
||||
this._profiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
|
||||
this._started = false;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Services.obs.addObserver(this, "profiler-started", false);
|
||||
Services.obs.addObserver(this, "profiler-stopped", false);
|
||||
}
|
||||
|
||||
ProfilerActor.prototype = {
|
||||
actorPrefix: "profiler",
|
||||
|
||||
disconnect: function() {
|
||||
if (this._profiler && this._started) {
|
||||
this._profiler.StopProfiler();
|
||||
}
|
||||
this._profiler = null;
|
||||
},
|
||||
|
||||
onStartProfiler: function(aRequest) {
|
||||
this._profiler.StartProfiler(aRequest.entries, aRequest.interval,
|
||||
aRequest.features, aRequest.features.length);
|
||||
this._started = true;
|
||||
return { "msg": "profiler started" }
|
||||
},
|
||||
onStopProfiler: function(aRequest) {
|
||||
this._profiler.StopProfiler();
|
||||
this._started = false;
|
||||
return { "msg": "profiler stopped" }
|
||||
},
|
||||
onGetProfileStr: function(aRequest) {
|
||||
var profileStr = this._profiler.GetProfile();
|
||||
return { "profileStr": profileStr }
|
||||
},
|
||||
onGetProfile: function(aRequest) {
|
||||
var profile = this._profiler.getProfileData();
|
||||
return { "profile": profile }
|
||||
},
|
||||
onIsActive: function(aRequest) {
|
||||
var isActive = this._profiler.IsActive();
|
||||
return { "isActive": isActive }
|
||||
},
|
||||
onGetResponsivenessTimes: function(aRequest) {
|
||||
var times = this._profiler.GetResponsivenessTimes({});
|
||||
return { "responsivenessTimes": times }
|
||||
},
|
||||
onGetFeatures: function(aRequest) {
|
||||
var features = this._profiler.GetFeatures([]);
|
||||
return { "features": features }
|
||||
},
|
||||
onGetSharedLibraryInformation: function(aRequest) {
|
||||
var sharedLibraries = this._profiler.getSharedLibraryInformation();
|
||||
return { "sharedLibraryInformation": sharedLibraries }
|
||||
},
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic == "profiler-started") {
|
||||
this.conn.send({ from: this.actorID, type: "profilerStateChanged", isActive: true });
|
||||
} else if (aTopic == "profiler-stopped") {
|
||||
this.conn.send({ from: this.actorID, type: "profilerStateChanged", isActive: false });
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* The request types this actor can handle.
|
||||
*/
|
||||
ProfilerActor.prototype.requestTypes = {
|
||||
"startProfiler": ProfilerActor.prototype.onStartProfiler,
|
||||
"stopProfiler": ProfilerActor.prototype.onStopProfiler,
|
||||
"getProfileStr": ProfilerActor.prototype.onGetProfileStr,
|
||||
"getProfile": ProfilerActor.prototype.onGetProfile,
|
||||
"isActive": ProfilerActor.prototype.onIsActive,
|
||||
"getResponsivenessTimes": ProfilerActor.prototype.onGetResponsivenessTimes,
|
||||
"getFeatures": ProfilerActor.prototype.onGetFeatures,
|
||||
"getSharedLibraryInformation": ProfilerActor.prototype.onGetSharedLibraryInformation
|
||||
};
|
||||
|
||||
DebuggerServer.addGlobalActor(ProfilerActor, "profilerActor");
|
@ -15,8 +15,10 @@ const Cc = Components.classes;
|
||||
const CC = Components.Constructor;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
const DBG_STRINGS_URI = "chrome://global/locale/devtools/debugger.properties";
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
|
||||
|
||||
Cu.import("resource://gre/modules/jsdebugger.jsm");
|
||||
@ -59,11 +61,42 @@ var DebuggerServer = {
|
||||
_listener: null,
|
||||
_transportInitialized: false,
|
||||
xpcInspector: null,
|
||||
_allowConnection: null,
|
||||
// Number of currently open TCP connections.
|
||||
_socketConnections: 0,
|
||||
// Map of global actor names to actor constructors provided by extensions.
|
||||
globalActorFactories: null,
|
||||
// Map of tab actor names to actor constructors provided by extensions.
|
||||
tabActorFactories: null,
|
||||
|
||||
LONG_STRING_LENGTH: 10000,
|
||||
LONG_STRING_INITIAL_LENGTH: 1000,
|
||||
|
||||
/**
|
||||
* Prompt the user to accept or decline the incoming connection.
|
||||
*
|
||||
* @return true if the connection should be permitted, false otherwise
|
||||
*/
|
||||
_allowConnection: function DH__allowConnection() {
|
||||
let title = L10N.getStr("remoteIncomingPromptTitle");
|
||||
let msg = L10N.getStr("remoteIncomingPromptMessage");
|
||||
let disableButton = L10N.getStr("remoteIncomingPromptDisable");
|
||||
let prompt = Services.prompt;
|
||||
let flags = prompt.BUTTON_POS_0 * prompt.BUTTON_TITLE_OK +
|
||||
prompt.BUTTON_POS_1 * prompt.BUTTON_TITLE_CANCEL +
|
||||
prompt.BUTTON_POS_2 * prompt.BUTTON_TITLE_IS_STRING +
|
||||
prompt.BUTTON_POS_1_DEFAULT;
|
||||
let result = prompt.confirmEx(null, title, msg, flags, null, null,
|
||||
disableButton, null, { value: false });
|
||||
if (result == 0) {
|
||||
return true;
|
||||
}
|
||||
if (result == 2) {
|
||||
DebuggerServer.closeListener(true);
|
||||
Services.prefs.setBoolPref("devtools.debugger.remote-enabled", false);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize the debugger server.
|
||||
*
|
||||
@ -79,6 +112,9 @@ var DebuggerServer = {
|
||||
this.xpcInspector = Cc["@mozilla.org/jsinspector;1"].getService(Ci.nsIJSInspector);
|
||||
this.initTransport(aAllowConnectionCallback);
|
||||
this.addActors("chrome://global/content/devtools/dbg-script-actors.js");
|
||||
|
||||
this.globalActorFactories = {};
|
||||
this.tabActorFactories = {};
|
||||
},
|
||||
|
||||
/**
|
||||
@ -97,10 +133,27 @@ var DebuggerServer = {
|
||||
this._connections = {};
|
||||
this._nextConnID = 0;
|
||||
this._transportInitialized = true;
|
||||
this._allowConnection = aAllowConnectionCallback;
|
||||
if (aAllowConnectionCallback) {
|
||||
this._allowConnection = aAllowConnectionCallback;
|
||||
}
|
||||
},
|
||||
|
||||
get initialized() { return !!this.xpcInspector; },
|
||||
get initialized() { return !!this.globalActorFactories; },
|
||||
|
||||
/**
|
||||
* Performs cleanup tasks before shutting down the debugger server, if no
|
||||
* connections are currently open. Such tasks include clearing any actor
|
||||
* constructors added at runtime. This method should be called whenever a
|
||||
* debugger server is no longer useful, to avoid memory leaks. After this
|
||||
* method returns, the debugger server must be initialized again before use.
|
||||
*/
|
||||
destroy: function DH_destroy() {
|
||||
if (Object.keys(this._connections).length == 0) {
|
||||
dumpn("Shutting down debugger server.");
|
||||
delete this.globalActorFactories;
|
||||
delete this.tabActorFactories;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Load a subscript into the debugging global.
|
||||
@ -119,6 +172,8 @@ var DebuggerServer = {
|
||||
*/
|
||||
addBrowserActors: function DH_addBrowserActors() {
|
||||
this.addActors("chrome://global/content/devtools/dbg-browser-actors.js");
|
||||
if ("nsIProfiler" in Ci)
|
||||
this.addActors("chrome://global/content/devtools/dbg-profiler-actors.js");
|
||||
},
|
||||
|
||||
/**
|
||||
@ -133,8 +188,9 @@ var DebuggerServer = {
|
||||
}
|
||||
this._checkInit();
|
||||
|
||||
// Return early if the server is already listening.
|
||||
if (this._listener) {
|
||||
throw "Debugging listener already open.";
|
||||
return true;
|
||||
}
|
||||
|
||||
let localOnly = false;
|
||||
@ -151,22 +207,32 @@ var DebuggerServer = {
|
||||
dumpn("Could not start debugging listener on port " + aPort + ": " + e);
|
||||
throw Cr.NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
this._socketConnections++;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Close a previously-opened TCP listener.
|
||||
*
|
||||
* @param aForce boolean [optional]
|
||||
* If set to true, then the socket will be closed, regardless of the
|
||||
* number of open connections.
|
||||
*/
|
||||
closeListener: function DH_closeListener() {
|
||||
closeListener: function DH_closeListener(aForce) {
|
||||
this._checkInit();
|
||||
|
||||
if (!this._listener) {
|
||||
if (!this._listener || this._socketConnections == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this._listener.close();
|
||||
this._listener = null;
|
||||
// Only close the listener when the last connection is closed, or if the
|
||||
// aForce flag is passed.
|
||||
if (--this._socketConnections == 0 || aForce) {
|
||||
this._listener.close();
|
||||
this._listener = null;
|
||||
this._socketConnections = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
@ -197,6 +263,9 @@ var DebuggerServer = {
|
||||
// nsIServerSocketListener implementation
|
||||
|
||||
onSocketAccepted: function DH_onSocketAccepted(aSocket, aTransport) {
|
||||
if (!this._allowConnection()) {
|
||||
return;
|
||||
}
|
||||
dumpn("New debugging connection on " + aTransport.host + ":" + aTransport.port);
|
||||
|
||||
try {
|
||||
@ -229,9 +298,6 @@ var DebuggerServer = {
|
||||
* after connectPipe() or after an incoming socket connection.
|
||||
*/
|
||||
_onConnection: function DH_onConnection(aTransport) {
|
||||
if (!this._allowConnection()) {
|
||||
return;
|
||||
}
|
||||
let connID = "conn" + this._nextConnID++ + '.';
|
||||
let conn = new DebuggerServerConnection(connID, aTransport);
|
||||
this._connections[connID] = conn;
|
||||
@ -244,10 +310,12 @@ var DebuggerServer = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove the connection from the debugging server.
|
||||
* Remove the connection from the debugging server and shut down the server
|
||||
* if no other connections are open.
|
||||
*/
|
||||
_connectionClosed: function DH_connectionClosed(aConnection) {
|
||||
delete this._connections[aConnection.prefix];
|
||||
this.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
@ -278,7 +346,11 @@ ActorPool.prototype = {
|
||||
addActor: function AP_addActor(aActor) {
|
||||
aActor.conn = this.conn;
|
||||
if (!aActor.actorID) {
|
||||
aActor.actorID = this.conn.allocID(aActor.actorPrefix || undefined);
|
||||
let prefix = aActor.actorPrefix;
|
||||
if (typeof aActor == "function") {
|
||||
prefix = aActor.prototype.actorPrefix;
|
||||
}
|
||||
aActor.actorID = this.conn.allocID(prefix || undefined);
|
||||
}
|
||||
|
||||
if (aActor.registeredPool) {
|
||||
@ -300,6 +372,13 @@ ActorPool.prototype = {
|
||||
return aActorID in this._actors;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the pool is empty.
|
||||
*/
|
||||
isEmpty: function AP_isEmpty() {
|
||||
return Object.keys(this._actors).length == 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove an actor from the actor pool.
|
||||
*/
|
||||
@ -437,6 +516,24 @@ DebuggerServerConnection.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
// Dyamically-loaded actors have to be created lazily.
|
||||
if (typeof actor == "function") {
|
||||
let instance;
|
||||
try {
|
||||
instance = new actor();
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
this.transport.send({
|
||||
error: "unknownError",
|
||||
message: ("error occurred while creating actor '" + actor.name +
|
||||
"': " + safeErrorString(e))
|
||||
});
|
||||
}
|
||||
actor.registeredPool.addActor(instance);
|
||||
actor.registeredPool.removeActor(actor);
|
||||
actor = instance;
|
||||
}
|
||||
|
||||
var ret = null;
|
||||
|
||||
// Dispatch the request to the actor.
|
||||
@ -487,3 +584,23 @@ DebuggerServerConnection.prototype = {
|
||||
DebuggerServer._connectionClosed(this);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Localization convenience methods.
|
||||
*/
|
||||
let L10N = {
|
||||
|
||||
/**
|
||||
* L10N shortcut function.
|
||||
*
|
||||
* @param string aName
|
||||
* @return string
|
||||
*/
|
||||
getStr: function L10N_getStr(aName) {
|
||||
return this.stringBundle.GetStringFromName(aName);
|
||||
}
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(L10N, "stringBundle", function() {
|
||||
return Services.strings.createBundle(DBG_STRINGS_URI);
|
||||
});
|
||||
|
@ -27,7 +27,12 @@ function really_long() {
|
||||
|
||||
function test_socket_conn()
|
||||
{
|
||||
DebuggerServer.openListener(2929);
|
||||
do_check_eq(DebuggerServer._socketConnections, 0);
|
||||
do_check_true(DebuggerServer.openListener(2929));
|
||||
do_check_eq(DebuggerServer._socketConnections, 1);
|
||||
// Make sure opening the listener twice does nothing.
|
||||
do_check_true(DebuggerServer.openListener(2929));
|
||||
do_check_eq(DebuggerServer._socketConnections, 1);
|
||||
|
||||
let unicodeString = "(╯°□°)╯︵ ┻━┻";
|
||||
let transport = debuggerSocketConnect("127.0.0.1", 2929);
|
||||
@ -54,7 +59,12 @@ function test_socket_conn()
|
||||
|
||||
function test_socket_shutdown()
|
||||
{
|
||||
DebuggerServer.closeListener();
|
||||
do_check_eq(DebuggerServer._socketConnections, 1);
|
||||
do_check_true(DebuggerServer.closeListener());
|
||||
do_check_eq(DebuggerServer._socketConnections, 0);
|
||||
// Make sure closing the listener twice does nothing.
|
||||
do_check_false(DebuggerServer.closeListener());
|
||||
do_check_eq(DebuggerServer._socketConnections, 0);
|
||||
|
||||
let transport = debuggerSocketConnect("127.0.0.1", 2929);
|
||||
transport.hooks = {
|
||||
|
@ -7,3 +7,4 @@ toolkit.jar:
|
||||
content/global/devtools/dbg-server.js (debugger/server/dbg-server.js)
|
||||
content/global/devtools/dbg-script-actors.js (debugger/server/dbg-script-actors.js)
|
||||
content/global/devtools/dbg-browser-actors.js (debugger/server/dbg-browser-actors.js)
|
||||
content/global/devtools/dbg-profiler-actors.js (debugger/server/dbg-profiler-actors.js)
|
||||
|
@ -0,0 +1,24 @@
|
||||
# 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/.
|
||||
|
||||
# LOCALIZATION NOTE These strings are used inside the Debugger
|
||||
# which is available from the Web Developer sub-menu -> 'Debugger'.
|
||||
# The correct localization of this file might be to keep it in
|
||||
# English, or another language commonly spoken among web developers.
|
||||
# You want to make that choice consistent across the developer tools.
|
||||
# A good criteria is the language in which you'd find the best
|
||||
# documentation on web development on the web.
|
||||
|
||||
# LOCALIZATION NOTE (remoteIncomingPromptTitle): The title displayed on the
|
||||
# dialog that prompts the user to allow the incoming connection.
|
||||
remoteIncomingPromptTitle=Incoming Connection
|
||||
|
||||
# LOCALIZATION NOTE (remoteIncomingPromptMessage): The message displayed on the
|
||||
# dialog that prompts the user to allow the incoming connection.
|
||||
remoteIncomingPromptMessage=An incoming request to permit remote debugging connection was detected. A remote client can take complete control over your browser! Allow connection?
|
||||
|
||||
# LOCALIZATION NOTE (remoteIncomingPromptDisable): The label displayed on the
|
||||
# third button in the incoming connection dialog that lets the user disable the
|
||||
# remote debugger server.
|
||||
remoteIncomingPromptDisable=Disable
|
@ -29,6 +29,7 @@
|
||||
locale/@AB_CD@/global/customizeToolbar.properties (%chrome/global/customizeToolbar.properties)
|
||||
locale/@AB_CD@/global/datetimepicker.dtd (%chrome/global/datetimepicker.dtd)
|
||||
locale/@AB_CD@/global/dateFormat.properties (%chrome/global/dateFormat.properties)
|
||||
locale/@AB_CD@/global/devtools/debugger.properties (%chrome/global/devtools/debugger.properties)
|
||||
locale/@AB_CD@/global/dialogOverlay.dtd (%chrome/global/dialogOverlay.dtd)
|
||||
locale/@AB_CD@/global/downloadProgress.properties (%chrome/global/downloadProgress.properties)
|
||||
locale/@AB_CD@/global/editMenuOverlay.dtd (%chrome/global/editMenuOverlay.dtd)
|
||||
|
Loading…
Reference in New Issue
Block a user