Merge m-c to fx-team

This commit is contained in:
Panos Astithas 2012-12-19 10:03:48 +02:00
commit 86b4184349
35 changed files with 697 additions and 280 deletions

View File

@ -103,64 +103,26 @@ function DeviceTabActor(connection, browser) {
DeviceTabActor.prototype = new BrowserTabActor();
DeviceTabActor.prototype.grip = function DTA_grip() {
dbg_assert(!this.exited,
'grip() should not be called on exited browser actor.');
dbg_assert(this.actorID,
'tab should have an actorID.');
Object.defineProperty(DeviceTabActor.prototype, "title", {
get: function() {
return this.browser.title;
},
enumerable: true,
configurable: false
});
let response = {
'actor': this.actorID,
'title': this.browser.title,
'url': this.browser.document.documentURI
};
Object.defineProperty(DeviceTabActor.prototype, "url", {
get: function() {
return this.browser.document.documentURI;
},
enumerable: true,
configurable: false
});
// 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;
};
/**
* Creates a thread actor and a pool for context-lifetime actors. It then sets
* up the content window for debugging.
*/
DeviceTabActor.prototype._pushContext = function DTA_pushContext() {
dbg_assert(!this._contextPool, "Can't push multiple contexts");
this._contextPool = new ActorPool(this.conn);
this.conn.addActorPool(this._contextPool);
this.threadActor = new ThreadActor(this, this.browser.wrappedJSObject);
this._contextPool.addActor(this.threadActor);
};
// Protocol Request Handlers
/**
* Prepare to enter a nested event loop by disabling debuggee events.
*/
DeviceTabActor.prototype.preNest = function DTA_preNest() {
let windowUtils = this.browser
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
windowUtils.suppressEventHandling(true);
windowUtils.suspendTimeouts();
};
/**
* Prepare to exit a nested event loop by enabling debuggee events.
*/
DeviceTabActor.prototype.postNest = function DTA_postNest(aNestData) {
let windowUtils = this.browser
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
windowUtils.resumeTimeouts();
windowUtils.suppressEventHandling(false);
};
Object.defineProperty(DeviceTabActor.prototype, "contentWindow", {
get: function() {
return this.browser;
},
enumerable: true,
configurable: false
});

View File

@ -25,7 +25,7 @@ function DebuggerPanel(iframeWindow, toolbox) {
this._controller._target = this.target;
this._bkp = this._controller.Breakpoints;
new EventEmitter(this);
EventEmitter.decorate(this);
}
DebuggerPanel.prototype = {

View File

@ -215,9 +215,18 @@ let DebuggerController = {
/**
* Called for each location change in the debugged tab.
*
* @param string aType
* Packet type.
* @param object aPacket
* Packet received from the server.
*/
_onTabNavigated: function DC__onTabNavigated() {
DebuggerView._handleTabNavigation();
_onTabNavigated: function DC__onTabNavigated(aType, aPacket) {
if (aPacket.state == "start") {
DebuggerView._handleTabNavigation();
return;
}
this.ThreadState._handleTabNavigation();
this.StackFrames._handleTabNavigation();
this.SourceScripts._handleTabNavigation();

View File

@ -40,7 +40,14 @@ function testInitialLoad() {
function testLocationChange()
{
gDebugger.DebuggerController.activeThread.resume(function() {
gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
gDebugger.DebuggerController.client.addListener("tabNavigated", function onTabNavigated(aEvent, aPacket) {
dump("tabNavigated state " + aPacket.state + "\n");
if (aPacket.state == "start") {
return;
}
gDebugger.DebuggerController.client.removeListener("tabNavigated", onTabNavigated);
ok(true, "tabNavigated event was fired.");
info("Still attached to the tab.");
@ -59,7 +66,14 @@ function testLocationChange()
function testBack()
{
gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
gDebugger.DebuggerController.client.addListener("tabNavigated", function onTabNavigated(aEvent, aPacket) {
dump("tabNavigated state " + aPacket.state + "\n");
if (aPacket.state == "start") {
return;
}
gDebugger.DebuggerController.client.removeListener("tabNavigated", onTabNavigated);
ok(true, "tabNavigated event was fired after going back.");
info("Still attached to the tab.");
@ -79,7 +93,14 @@ function testBack()
function testForward()
{
gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
gDebugger.DebuggerController.client.addListener("tabNavigated", function onTabNavigated(aEvent, aPacket) {
dump("tabNavigated state " + aPacket.state + "\n");
if (aPacket.state == "start") {
return;
}
gDebugger.DebuggerController.client.removeListener("tabNavigated", onTabNavigated);
ok(true, "tabNavigated event was fired after going forward.");
info("Still attached to the tab.");

View File

@ -73,7 +73,13 @@ function testSimpleCall() {
function testLocationChange()
{
gDebugger.DebuggerController.activeThread.resume(function() {
gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
gDebugger.DebuggerController.client.addListener("tabNavigated", function onTabNavigated(aEvent, aPacket) {
dump("tabNavigated state " + aPacket.state + "\n");
if (aPacket.state == "start") {
return;
}
gDebugger.DebuggerController.client.removeListener("tabNavigated", onTabNavigated);
ok(true, "tabNavigated event was fired.");
info("Still attached to the tab.");

View File

@ -73,7 +73,13 @@ function testSimpleCall() {
function testLocationChange()
{
gDebugger.DebuggerController.activeThread.resume(function() {
gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
gDebugger.DebuggerController.client.addListener("tabNavigated", function onTabNavigated(aEvent, aPacket) {
dump("tabNavigated state " + aPacket.state + "\n");
if (aPacket.state == "start") {
return;
}
gDebugger.DebuggerController.client.removeListener("tabNavigated", onTabNavigated);
ok(true, "tabNavigated event was fired.");
info("Still attached to the tab.");

View File

@ -50,7 +50,13 @@ function testSimpleCall() {
function testLocationChange()
{
gDebugger.DebuggerController.activeThread.resume(function() {
gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
gDebugger.DebuggerController.client.addListener("tabNavigated", function onTabNavigated(aEvent, aPacket) {
dump("tabNavigated state " + aPacket.state + "\n");
if (aPacket.state == "start") {
return;
}
gDebugger.DebuggerController.client.removeListener("tabNavigated", onTabNavigated);
ok(true, "tabNavigated event was fired.");
info("Still attached to the tab.");

View File

@ -26,7 +26,13 @@ function get_tab()
get_tab_actor_for_url(gClient, TAB1_URL, function(aGrip) {
gTab1Actor = aGrip.actor;
gClient.request({ to: aGrip.actor, type: "attach" }, function(aResponse) {
gClient.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
gClient.addListener("tabNavigated", function onTabNavigated(aEvent, aPacket) {
dump("onTabNavigated state " + aPacket.state + "\n");
if (aPacket.state == "start") {
return;
}
gClient.removeListener("tabNavigated", onTabNavigated);
is(aPacket.url, TAB2_URL, "Got a tab navigation notification.");
gClient.addOneTimeListener("tabDetached", function (aEvent, aPacket) {
ok(true, "Got a tab detach notification.");

View File

@ -26,7 +26,7 @@ const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
*/
this.ToolSidebar = function ToolSidebar(tabbox, panel, showTabstripe=true)
{
new EventEmitter(this);
EventEmitter.decorate(this);
this._tabbox = tabbox;
this._panelDoc = this._tabbox.ownerDocument;

View File

@ -170,7 +170,7 @@ Object.defineProperty(Target.prototype, "version", {
* be web pages served over http(s), but they don't have to be.
*/
function TabTarget(tab) {
new EventEmitter(this);
EventEmitter.decorate(this);
this._tab = tab;
this._setupListeners();
}
@ -201,6 +201,10 @@ TabTarget.prototype = {
return false;
},
get isLocalTab() {
return true;
},
/**
* Listen to the different tabs events.
*/
@ -305,7 +309,7 @@ TabWebProgressListener.prototype = {
* these will have a chrome: URL
*/
function WindowTarget(window) {
new EventEmitter(this);
EventEmitter.decorate(this);
this._window = window;
}
@ -329,6 +333,10 @@ WindowTarget.prototype = {
return false;
},
get isLocalTab() {
return false;
},
/**
* Target is not alive anymore.
*/
@ -354,7 +362,7 @@ WindowTarget.prototype = {
* A RemoteTarget represents a page living in a remote Firefox instance.
*/
function RemoteTarget(form, client, chrome) {
new EventEmitter(this);
EventEmitter.decorate(this);
this._client = client;
this._form = form;
this._chrome = chrome;
@ -362,8 +370,12 @@ function RemoteTarget(form, client, chrome) {
this.destroy = this.destroy.bind(this);
this.client.addListener("tabDetached", this.destroy);
this._onTabNavigated = function onRemoteTabNavigated() {
this.emit("navigate");
this._onTabNavigated = function onRemoteTabNavigated(aType, aPacket) {
if (aPacket.state == "start") {
this.emit("will-navigate", aPacket);
} else {
this.emit("navigate", aPacket);
}
}.bind(this);
this.client.addListener("tabNavigated", this._onTabNavigated);
}
@ -376,14 +388,16 @@ RemoteTarget.prototype = {
get chrome() this._chrome,
get name() this._form._title,
get name() this._form.title,
get url() this._form._url,
get url() this._form.url,
get client() this._client,
get form() this._form,
get isLocalTab() false,
/**
* Target is not alive anymore.
*/

View File

@ -127,7 +127,7 @@ this.Toolbox = function Toolbox(target, selectedTool, hostType) {
this._host = this._createHost(hostType);
new EventEmitter(this);
EventEmitter.decorate(this);
gDevTools.on("tool-registered", this._toolRegistered);
gDevTools.on("tool-unregistered", this._toolUnregistered);
@ -270,6 +270,10 @@ Toolbox.prototype = {
dockBox.removeChild(dockBox.firstChild);
}
if (!this._target.isLocalTab) {
return;
}
let sideEnabled = Services.prefs.getBoolPref(this._prefs.SIDE_ENABLED);
for each (let position in this.HostType) {
@ -476,6 +480,10 @@ Toolbox.prototype = {
return;
}
if (!this._target.isLocalTab) {
return;
}
let newHost = this._createHost(hostType);
return newHost.open().then(function(iframe) {
// change toolbox document's parent to the new host

View File

@ -33,7 +33,7 @@ this.Hosts = {
function BottomHost(hostTab) {
this.hostTab = hostTab;
new EventEmitter(this);
EventEmitter.decorate(this);
}
BottomHost.prototype = {
@ -101,7 +101,7 @@ BottomHost.prototype = {
function SidebarHost(hostTab) {
this.hostTab = hostTab;
new EventEmitter(this);
EventEmitter.decorate(this);
}
SidebarHost.prototype = {
@ -166,7 +166,7 @@ SidebarHost.prototype = {
function WindowHost() {
this._boundUnload = this._boundUnload.bind(this);
new EventEmitter(this);
EventEmitter.decorate(this);
}
WindowHost.prototype = {

View File

@ -31,7 +31,7 @@ this.DevTools = function DevTools() {
// destroy() is an observer's handler so we need to preserve context.
this.destroy = this.destroy.bind(this);
new EventEmitter(this);
EventEmitter.decorate(this);
Services.obs.addObserver(this.destroy, "quit-application", false);

View File

@ -85,7 +85,7 @@ function finishUp() {
* else gives us a place to write documentation.
*/
function DevToolPanel(iframeWindow, toolbox) {
new EventEmitter(this);
EventEmitter.decorate(this);
this._toolbox = toolbox;

View File

@ -13,13 +13,21 @@
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<notificationbox id="toolbox-notificationbox" flex="1">
<toolbar class="devtools-tabbar">
#ifdef XP_MACOSX
<hbox id="toolbox-controls">
<toolbarbutton id="toolbox-close" class="devtools-closebutton"></toolbarbutton>
<hbox id="toolbox-dock-buttons"/>
</hbox>
#endif
<radiogroup id="toolbox-tabs" orient="horizontal">
</radiogroup>
<hbox id="toolbox-buttons" flex="1" pack="end"/>
#ifndef XP_MACOSX
<hbox id="toolbox-controls">
<hbox id="toolbox-dock-buttons"/>
<toolbarbutton id="toolbox-close" class="devtools-closebutton"></toolbarbutton>
</hbox>
#endif
</toolbar>
<deck id="toolbox-deck" flex="1">
</deck>

View File

@ -84,7 +84,7 @@ this.Highlighter = function Highlighter(aTarget, aInspector, aToolbox)
this.chromeWin = this.chromeDoc.defaultView;
this.inspector = aInspector
new EventEmitter(this);
EventEmitter.decorate(this);
this._init();
}
@ -139,8 +139,13 @@ Highlighter.prototype = {
this.onToolSelected = function(event, id) {
if (id != "inspector") {
this.chromeWin.clearTimeout(this.pageEventsMuter);
this.detachMouseListeners();
this.hide();
} else {
if (!this.locked) {
this.attachMouseListeners();
}
this.show();
}
}.bind(this);

View File

@ -42,7 +42,7 @@ this.InspectorPanel = function InspectorPanel(iframeWindow, toolbox) {
this.tabTarget = (this.target.tab != null);
this.winTarget = (this.target.window != null);
new EventEmitter(this);
EventEmitter.decorate(this);
}
InspectorPanel.prototype = {

View File

@ -58,7 +58,7 @@ this.EXPORTED_SYMBOLS = ["Selection"];
*
*/
this.Selection = function Selection(node=null, track={attributes:true,detached:true}) {
new EventEmitter(this);
EventEmitter.decorate(this);
this._onMutations = this._onMutations.bind(this);
this.track = track;
this.setNode(node);

View File

@ -49,7 +49,7 @@ browser.jar:
content/browser/devtools/commandlineoutput.xhtml (commandline/commandlineoutput.xhtml)
content/browser/devtools/commandlinetooltip.xhtml (commandline/commandlinetooltip.xhtml)
content/browser/devtools/framework/toolbox-window.xul (framework/toolbox-window.xul)
content/browser/devtools/framework/toolbox.xul (framework/toolbox.xul)
* content/browser/devtools/framework/toolbox.xul (framework/toolbox.xul)
content/browser/devtools/framework/toolbox.css (framework/toolbox.css)
content/browser/devtools/inspector/inspector.xul (inspector/inspector.xul)
content/browser/devtools/inspector/inspector.css (inspector/inspector.css)

View File

@ -58,7 +58,7 @@ this.MarkupView = function MarkupView(aInspector, aFrame, aControllerWindow)
this._onNewSelection();
this._boundKeyDown = this._onKeyDown.bind(this);
this._frame.addEventListener("keydown", this._boundKeyDown, false);
this._frame.contentWindow.addEventListener("keydown", this._boundKeyDown, false);
this._boundFocus = this._onFocus.bind(this);
this._frame.addEventListener("focus", this._boundFocus, false);
@ -490,7 +490,7 @@ MarkupView.prototype = {
this._frame.contentWindow.removeEventListener("underflow", this._boundResizePreview, true);
delete this._boundUpdatePreview;
this._frame.removeEventListener("keydown", this._boundKeyDown, true);
this._frame.contentWindow.removeEventListener("keydown", this._boundKeyDown, true);
delete this._boundKeyDown;
this._inspector.selection.off("new-node", this._boundOnNewSelection);

View File

@ -40,7 +40,7 @@ function ProfileUI(uid, panel) {
let doc = panel.document;
let win = panel.window;
new EventEmitter(this);
EventEmitter.decorate(this);
this.isReady = false;
this.panel = panel;
@ -183,7 +183,7 @@ function ProfilerPanel(frame, toolbox) {
this.profiles = new Map();
this._uid = 0;
new EventEmitter(this);
EventEmitter.decorate(this);
}
ProfilerPanel.prototype = {

View File

@ -69,16 +69,11 @@ this.ResponsiveUIManager = {
this.toggle(aWindow, aTab);
default:
}
},
get events() {
if (!this._eventEmitter) {
this._eventEmitter = new EventEmitter();
}
return this._eventEmitter;
},
}
}
EventEmitter.decorate(ResponsiveUIManager);
let presets = [
// Phones
{key: "320x480", width: 320, height: 480}, // iPhone, B2G, with <meta viewport>
@ -175,7 +170,7 @@ function ResponsiveUI(aWindow, aTab)
if (this._floatingScrollbars)
switchToFloatingScrollbars(this.tab);
ResponsiveUIManager.events.emit("on", this.tab, this);
ResponsiveUIManager.emit("on", this.tab, this);
}
ResponsiveUI.prototype = {
@ -232,7 +227,7 @@ ResponsiveUI.prototype = {
this.stack.removeAttribute("responsivemode");
delete this.tab.__responsiveUI;
ResponsiveUIManager.events.emit("off", this.tab, this);
ResponsiveUIManager.emit("off", this.tab, this);
},
/**

View File

@ -3,7 +3,7 @@
function test() {
let instance, widthBeforeClose, heightBeforeClose;
let events = ResponsiveUI.ResponsiveUIManager.events;
let mgr = ResponsiveUI.ResponsiveUIManager;
waitForExplicitFinish();
@ -17,7 +17,7 @@ function test() {
function startTest() {
document.getElementById("Tools:ResponsiveUI").removeAttribute("disabled");
events.once("on", function() {executeSoon(onUIOpen)});
mgr.once("on", function() {executeSoon(onUIOpen)});
synthesizeKeyFromKeyTag("key_responsiveUI");
}
@ -121,12 +121,12 @@ function test() {
widthBeforeClose = content.innerWidth;
heightBeforeClose = content.innerHeight;
events.once("off", function() {executeSoon(restart)});
mgr.once("off", function() {executeSoon(restart)});
EventUtils.synthesizeKey("VK_ESCAPE", {});
}
function restart() {
events.once("on", function() {executeSoon(onUIOpen2)});
mgr.once("on", function() {executeSoon(onUIOpen2)});
synthesizeKeyFromKeyTag("key_responsiveUI");
}
@ -140,7 +140,7 @@ function test() {
is(content.innerWidth, widthBeforeClose, "width restored.");
is(content.innerHeight, heightBeforeClose, "height restored.");
events.once("off", function() {executeSoon(finishUp)});
mgr.once("off", function() {executeSoon(finishUp)});
EventUtils.synthesizeKey("VK_ESCAPE", {});
}

View File

@ -6,19 +6,23 @@ this.EXPORTED_SYMBOLS = ["EventEmitter"];
/**
* EventEmitter.
*
* @param Object aObjectToExtend
* If aObjectToExtend is not null, the public methods of EventEmitter
* are bound to the object.
*/
this.EventEmitter = function EventEmitter(aObjectToExtend) {
if (aObjectToExtend) {
aObjectToExtend.on = this.on.bind(this);
aObjectToExtend.off = this.off.bind(this);
aObjectToExtend.once = this.once.bind(this);
aObjectToExtend.emit = this.emit.bind(this);
}
}
this.EventEmitter = function EventEmitter() {};
/**
* Decorate an object with event emitter functionality.
*
* @param Object aObjectToDecorate
* Bind all public methods of EventEmitter to
* the aObjectToDecorate object.
*/
EventEmitter.decorate = function EventEmitter_decorate (aObjectToDecorate) {
let emitter = new EventEmitter();
aObjectToDecorate.on = emitter.on.bind(emitter);
aObjectToDecorate.off = emitter.off.bind(emitter);
aObjectToDecorate.once = emitter.once.bind(emitter);
aObjectToDecorate.emit = emitter.emit.bind(emitter);
};
EventEmitter.prototype = {
/**
@ -103,5 +107,5 @@ EventEmitter.prototype = {
}
}
}
},
}
}
};

View File

@ -14,7 +14,7 @@ function testEmitter(aObject) {
if (aObject) {
emitter = aObject;
new EventEmitter(emitter);
EventEmitter.decorate(emitter);
} else {
emitter = new EventEmitter();
}

View File

@ -16,7 +16,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "StyleEditorChrome",
"resource:///modules/devtools/StyleEditorChrome.jsm");
this.StyleEditorPanel = function StyleEditorPanel(panelWin, toolbox) {
new EventEmitter(this);
EventEmitter.decorate(this);
this._toolbox = toolbox;
this._target = toolbox.target;

View File

@ -177,10 +177,13 @@ TiltVisualizer.prototype = {
let target = TargetFactory.forTab(aTab);
let toolbox = gDevTools.getToolbox(target);
if (toolbox) {
this.inspector = toolbox.getPanel("inspector");
this.inspector.selection.on("new-node", this.onNewNodeFromInspector);
this.inspector.selection.on("detached", this.onNewNodeFromInspector);
this.onNewNodeFromInspector();
let panel = toolbox.getPanel("inspector");
if (panel) {
this.inspector = panel;
this.inspector.selection.on("new-node", this.onNewNodeFromInspector);
this.inspector.selection.on("detached", this.onNewNodeFromInspector);
this.onNewNodeFromInspector();
}
}
},

View File

@ -22,7 +22,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "HUDService",
function WebConsolePanel(iframeWindow, toolbox) {
this._frameWindow = iframeWindow;
this._toolbox = toolbox;
new EventEmitter(this);
EventEmitter.decorate(this);
}
WebConsolePanel.prototype = {

View File

@ -117,6 +117,12 @@ MOCHITEST_BROWSER_FILES = \
head.js \
$(NULL)
ifeq ($(OS_ARCH), Darwin)
MOCHITEST_BROWSER_FILES += \
browser_webconsole_bug_804845_ctrl_key_nav.js \
$(NULL)
endif
ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
MOCHITEST_BROWSER_FILES += \
browser_webconsole_bug_618311_private_browsing.js \

View File

@ -0,0 +1,214 @@
/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*
* Contributor(s):
* zmgmoz <zmgmoz@gmail.com>
*
* ***** END LICENSE BLOCK ***** */
// Test navigation of webconsole contents via ctrl-a, ctrl-e, ctrl-p, ctrl-n
// see https://bugzilla.mozilla.org/show_bug.cgi?id=804845
let jsterm, inputNode;
function test() {
addTab("data:text/html;charset=utf-8,Web Console test for bug 804845 and bug 619598");
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
openConsole(null, doTests);
}, true);
}
function doTests(HUD) {
jsterm = HUD.jsterm;
inputNode = jsterm.inputNode;
ok(!jsterm.inputNode.value, "inputNode.value is empty");
is(jsterm.inputNode.selectionStart, 0);
is(jsterm.inputNode.selectionEnd, 0);
testSingleLineInputNavNoHistory();
testMultiLineInputNavNoHistory();
testNavWithHistory();
jsterm = inputNode = null;
executeSoon(finishTest);
}
function testSingleLineInputNavNoHistory() {
// Single char input
EventUtils.synthesizeKey("1", {});
is(inputNode.selectionStart, 1, "caret location after single char input");
// nav to start/end with ctrl-a and ctrl-e;
EventUtils.synthesizeKey("a", { ctrlKey: true });
is(inputNode.selectionStart, 0, "caret location after single char input and ctrl-a");
EventUtils.synthesizeKey("e", { ctrlKey: true });
is(inputNode.selectionStart, 1, "caret location after single char input and ctrl-e");
// Second char input
EventUtils.synthesizeKey("2", {});
// nav to start/end with up/down keys; verify behaviour using ctrl-p/ctrl-n
EventUtils.synthesizeKey("VK_UP", {});
is(inputNode.selectionStart, 0, "caret location after two char input and VK_UP");
EventUtils.synthesizeKey("VK_DOWN", {});
is(inputNode.selectionStart, 2, "caret location after two char input and VK_DOWN");
EventUtils.synthesizeKey("a", { ctrlKey: true });
is(inputNode.selectionStart, 0, "move caret to beginning of 2 char input with ctrl-a");
EventUtils.synthesizeKey("a", { ctrlKey: true });
is(inputNode.selectionStart, 0, "no change of caret location on repeat ctrl-a");
EventUtils.synthesizeKey("p", { ctrlKey: true });
is(inputNode.selectionStart, 0, "no change of caret location on ctrl-p from beginning of line");
EventUtils.synthesizeKey("e", { ctrlKey: true });
is(inputNode.selectionStart, 2, "move caret to end of 2 char input with ctrl-e");
EventUtils.synthesizeKey("e", { ctrlKey: true });
is(inputNode.selectionStart, 2, "no change of caret location on repeat ctrl-e");
EventUtils.synthesizeKey("n", { ctrlKey: true });
is(inputNode.selectionStart, 2, "no change of caret location on ctrl-n from end of line");
EventUtils.synthesizeKey("p", { ctrlKey: true });
is(inputNode.selectionStart, 0, "ctrl-p moves to start of line");
EventUtils.synthesizeKey("n", { ctrlKey: true });
is(inputNode.selectionStart, 2, "ctrl-n moves to end of line");
}
function testMultiLineInputNavNoHistory() {
let lineValues = ["one", "2", "something longer", "", "", "three!"];
jsterm.setInputValue("");
// simulate shift-return
for (let i = 0; i < lineValues.length; i++) {
jsterm.setInputValue(inputNode.value + lineValues[i]);
EventUtils.synthesizeKey("VK_RETURN", { shiftKey: true });
}
let inputValue = inputNode.value;
is(inputNode.selectionStart, inputNode.selectionEnd);
is(inputNode.selectionStart, inputValue.length, "caret at end of multiline input");
// possibility newline is represented by one ('\r', '\n') or two ('\r\n') chars
let newlineString = inputValue.match(/(\r\n?|\n\r?)$/)[0];
// Ok, test navigating within the multi-line string!
EventUtils.synthesizeKey("VK_UP", {});
let expectedStringAfterCarat = lineValues[5]+newlineString;
is(inputNode.value.slice(inputNode.selectionStart), expectedStringAfterCarat,
"up arrow from end of multiline");
EventUtils.synthesizeKey("VK_DOWN", {});
is(inputNode.value.slice(inputNode.selectionStart), "",
"down arrow from within multiline");
// navigate up through input lines
EventUtils.synthesizeKey("p", { ctrlKey: true });
is(inputNode.value.slice(inputNode.selectionStart), expectedStringAfterCarat,
"ctrl-p from end of multiline");
for (let i = 4; i >= 0; i--) {
EventUtils.synthesizeKey("p", { ctrlKey: true });
expectedStringAfterCarat = lineValues[i] + newlineString + expectedStringAfterCarat;
is(inputNode.value.slice(inputNode.selectionStart), expectedStringAfterCarat,
"ctrl-p from within line " + i + " of multiline input");
}
EventUtils.synthesizeKey("p", { ctrlKey: true });
is(inputNode.selectionStart, 0, "reached start of input");
is(inputNode.value, inputValue,
"no change to multiline input on ctrl-p from beginning of multiline");
// navigate to end of first line
EventUtils.synthesizeKey("e", { ctrlKey: true });
let caretPos = inputNode.selectionStart;
let expectedStringBeforeCarat = lineValues[0];
is(inputNode.value.slice(0, caretPos), expectedStringBeforeCarat,
"ctrl-e into multiline input");
EventUtils.synthesizeKey("e", { ctrlKey: true });
is(inputNode.selectionStart, caretPos,
"repeat ctrl-e doesn't change caret position in multiline input");
// navigate down one line; ctrl-a to the beginning; ctrl-e to end
for (let i = 1; i < lineValues.length; i++) {
EventUtils.synthesizeKey("n", { ctrlKey: true });
EventUtils.synthesizeKey("a", { ctrlKey: true });
caretPos = inputNode.selectionStart;
expectedStringBeforeCarat += newlineString;
is(inputNode.value.slice(0, caretPos), expectedStringBeforeCarat,
"ctrl-a to beginning of line " + (i+1) + " in multiline input");
EventUtils.synthesizeKey("e", { ctrlKey: true });
caretPos = inputNode.selectionStart;
expectedStringBeforeCarat += lineValues[i];
is(inputNode.value.slice(0, caretPos), expectedStringBeforeCarat,
"ctrl-e to end of line " + (i+1) + "in multiline input");
}
}
function testNavWithHistory() {
// NOTE: Tests does NOT currently define behaviour for ctrl-p/ctrl-n with
// caret placed _within_ single line input
let values = ['"single line input"',
'"a longer single-line input to check caret repositioning"',
['"multi-line"', '"input"', '"here!"'].join("\n"),
];
// submit to history
for (let i = 0; i < values.length; i++) {
jsterm.setInputValue(values[i]);
jsterm.execute();
}
is(inputNode.selectionStart, 0, "caret location at start of empty line");
EventUtils.synthesizeKey("p", { ctrlKey: true });
is(inputNode.selectionStart, values[values.length-1].length,
"caret location correct at end of last history input");
// Navigate backwards history with ctrl-p
for (let i = values.length-1; i > 0; i--) {
let match = values[i].match(/(\n)/g);
if (match) {
// multi-line inputs won't update from history unless caret at beginning
EventUtils.synthesizeKey("a", { ctrlKey: true });
for (let i = 0; i < match.length; i++) {
EventUtils.synthesizeKey("p", { ctrlKey: true });
}
EventUtils.synthesizeKey("p", { ctrlKey: true });
} else {
// single-line inputs will update from history from end of line
EventUtils.synthesizeKey("p", { ctrlKey: true });
}
is(inputNode.value, values[i-1],
"ctrl-p updates inputNode from backwards history values[" + i-1 + "]");
}
let inputValue = inputNode.value;
EventUtils.synthesizeKey("p", { ctrlKey: true });
is(inputNode.selectionStart, 0,
"ctrl-p at beginning of history moves caret location to beginning of line");
is(inputNode.value, inputValue,
"no change to input value on ctrl-p from beginning of line");
// Navigate forwards history with ctrl-n
for (let i = 1; i<values.length; i++) {
EventUtils.synthesizeKey("n", { ctrlKey: true });
is(inputNode.value, values[i],
"ctrl-n updates inputNode from forwards history values[" + i + "]");
is(inputNode.selectionStart, values[i].length,
"caret location correct at end of history input for values[" + i + "]");
}
EventUtils.synthesizeKey("n", { ctrlKey: true });
ok(!inputNode.value, "ctrl-n at end of history updates to empty input");
// Simulate editing multi-line
inputValue = "one\nlinebreak";
jsterm.setInputValue(inputValue);
// Attempt nav within input
EventUtils.synthesizeKey("p", { ctrlKey: true });
is(inputNode.value, inputValue,
"ctrl-p from end of multi-line does not trigger history");
EventUtils.synthesizeKey("a", { ctrlKey: true });
EventUtils.synthesizeKey("p", { ctrlKey: true });
is(inputNode.value, values[values.length-1],
"ctrl-p from start of multi-line triggers history");
}

View File

@ -378,6 +378,7 @@ WebConsoleFrame.prototype = {
timeout, Ci.nsITimer.TYPE_ONE_SHOT);
this.proxy.connect(function() {
// Don't complete connection if the connection timed-out.
if (this._connectTimer) {
this._connectTimer.cancel();
this._connectTimer = null;
@ -3181,21 +3182,72 @@ JSTerm.prototype = {
keyPress: function JSTF_keyPress(aEvent)
{
if (aEvent.ctrlKey) {
let inputNode = this.inputNode;
let closePopup = false;
switch (aEvent.charCode) {
case 97:
// control-a
this.inputNode.setSelectionRange(0, 0);
let lineBeginPos = 0;
if (this.hasMultilineInput()) {
// find index of closest newline <= to cursor
for (let i = inputNode.selectionStart-1; i >= 0; i--) {
if (inputNode.value.charAt(i) == "\r" ||
inputNode.value.charAt(i) == "\n") {
lineBeginPos = i+1;
break;
}
}
}
inputNode.setSelectionRange(lineBeginPos, lineBeginPos);
aEvent.preventDefault();
closePopup = true;
break;
case 101:
// control-e
this.inputNode.setSelectionRange(this.inputNode.value.length,
this.inputNode.value.length);
let lineEndPos = inputNode.value.length;
if (this.hasMultilineInput()) {
// find index of closest newline >= cursor
for (let i = inputNode.selectionEnd; i<lineEndPos; i++) {
if (inputNode.value.charAt(i) == "\r" ||
inputNode.value.charAt(i) == "\n") {
lineEndPos = i;
break;
}
}
}
inputNode.setSelectionRange(lineEndPos, lineEndPos);
aEvent.preventDefault();
break;
case 110:
// Control-N differs from down arrow: it ignores autocomplete state.
// Note that we preserve the default 'down' navigation within
// multiline text.
if (Services.appinfo.OS == "Darwin" &&
this.canCaretGoNext() &&
this.historyPeruse(HISTORY_FORWARD)) {
aEvent.preventDefault();
}
closePopup = true;
break;
case 112:
// Control-P differs from up arrow: it ignores autocomplete state.
// Note that we preserve the default 'up' navigation within
// multiline text.
if (Services.appinfo.OS == "Darwin" &&
this.canCaretGoPrevious() &&
this.historyPeruse(HISTORY_BACK)) {
aEvent.preventDefault();
}
closePopup = true;
break;
default:
break;
}
if (closePopup) {
if (this.autocompletePopup.isOpen) {
this.clearCompletion();
}
}
return;
}
else if (aEvent.shiftKey &&
@ -3316,6 +3368,17 @@ JSTerm.prototype = {
return true;
},
/**
* Test for multiline input.
*
* @return boolean
* True if CR or LF found in node value; else false.
*/
hasMultilineInput: function JST_hasMultilineInput()
{
return /[\r\n]/.test(this.inputNode.value);
},
/**
* Check if the caret is at a location that allows selecting the previous item
* in history when the user presses the Up arrow key.
@ -3983,7 +4046,7 @@ function WebConsoleConnectionProxy(aWebConsole, aTarget)
this._onNetworkEvent = this._onNetworkEvent.bind(this);
this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
this._onFileActivity = this._onFileActivity.bind(this);
this._onLocationChange = this._onLocationChange.bind(this);
this._onTabNavigated = this._onTabNavigated.bind(this);
}
WebConsoleConnectionProxy.prototype = {
@ -3995,6 +4058,12 @@ WebConsoleConnectionProxy.prototype = {
*/
owner: null,
/**
* The target that the console connects to.
* @type RemoteTarget
*/
target: null,
/**
* The DebuggerClient object.
*
@ -4011,6 +4080,12 @@ WebConsoleConnectionProxy.prototype = {
*/
webConsoleClient: null,
/**
* The TabClient instance we use.
* @type object
*/
tabClient: null,
/**
* Tells if the connection is established.
* @type boolean
@ -4025,6 +4100,14 @@ WebConsoleConnectionProxy.prototype = {
*/
_consoleActor: null,
/**
* The TabActor ID.
*
* @private
* @type string
*/
_tabActor: null,
/**
* Tells if the window.console object of the remote web page is the native
* object or not.
@ -4069,23 +4152,24 @@ WebConsoleConnectionProxy.prototype = {
client.addListener("networkEvent", this._onNetworkEvent);
client.addListener("networkEventUpdate", this._onNetworkEventUpdate);
client.addListener("fileActivity", this._onFileActivity);
client.addListener("locationChange", this._onLocationChange);
client.addListener("tabNavigated", this._onTabNavigated);
if (this.target.isRemote) {
this._consoleActor = this.target.form.consoleActor;
if (!this.target.chrome) {
this.owner.onLocationChange(this.target.url, this.target.name);
// target.form is a TabActor grip
this._attachTab(this.target.form, aCallback);
}
else {
// target.form is a RootActor grip
this._consoleActor = this.target.form.consoleActor;
this._attachConsole(aCallback);
}
let listeners = ["PageError", "ConsoleAPI", "NetworkActivity",
"FileActivity", "LocationChange"];
this.client.attachConsole(this._consoleActor, listeners,
this._onAttachConsole.bind(this, aCallback));
return;
}
client.connect(function(aType, aTraits) {
client.listTabs(this._onListTabs.bind(this, aCallback));
}.bind(this));
else {
client.connect(function(aType, aTraits) {
client.listTabs(this._onListTabs.bind(this, aCallback));
}.bind(this));
}
},
/**
@ -4099,19 +4183,67 @@ WebConsoleConnectionProxy.prototype = {
*/
_onListTabs: function WCCP__onListTabs(aCallback, aResponse)
{
let selectedTab = aResponse.tabs[aResponse.selected];
if (selectedTab) {
this._consoleActor = selectedTab.consoleActor;
this.owner.onLocationChange(selectedTab.url, selectedTab.title);
}
else {
this._consoleActor = aResponse.consoleActor;
if (aResponse.error) {
Cu.reportError("listTabs failed: " + aResponse.error + " " +
aResponse.message);
return;
}
this.owner._resetConnectionTimeout();
this._attachTab(aResponse.tabs[aResponse.selected], aCallback);
},
/**
* Attach to the tab actor.
*
* @private
* @param object aTab
* Grip for the tab to attach to.
* @param function aCallback
* Function to invoke when the connection is established.
*/
_attachTab: function WCCP__attachTab(aTab, aCallback)
{
this._consoleActor = aTab.consoleActor;
this._tabActor = aTab.actor;
this.owner.onLocationChange(aTab.url, aTab.title);
this.client.attachTab(this._tabActor,
this._onAttachTab.bind(this, aCallback));
},
/**
* The "attachTab" response handler.
*
* @private
* @param function [aCallback]
* Optional function to invoke once the connection is established.
* @param object aResponse
* The JSON response object received from the server.
* @param object aTabClient
* The TabClient instance for the attached tab.
*/
_onAttachTab: function WCCP__onAttachTab(aCallback, aResponse, aTabClient)
{
if (aResponse.error) {
Cu.reportError("attachTab failed: " + aResponse.error + " " +
aResponse.message);
return;
}
this.tabClient = aTabClient;
this._attachConsole(aCallback);
},
/**
* Attach to the Web Console actor.
*
* @private
* @param function aCallback
* Function to invoke when the connection is established.
*/
_attachConsole: function WCCP__attachConsole(aCallback)
{
let listeners = ["PageError", "ConsoleAPI", "NetworkActivity",
"FileActivity", "LocationChange"];
"FileActivity"];
this.client.attachConsole(this._consoleActor, listeners,
this._onAttachConsole.bind(this, aCallback));
},
@ -4260,7 +4392,7 @@ WebConsoleConnectionProxy.prototype = {
},
/**
* The "locationChange" message type handler. We redirect any message to
* The "tabNavigated" message type handler. We redirect any message to
* the UI for displaying.
*
* @private
@ -4269,13 +4401,16 @@ WebConsoleConnectionProxy.prototype = {
* @param object aPacket
* The message received from the server.
*/
_onLocationChange: function WCCP__onLocationChange(aType, aPacket)
_onTabNavigated: function WCCP__onTabNavigated(aType, aPacket)
{
if (!this.owner || aPacket.from != this._consoleActor) {
if (!this.owner || aPacket.from != this._tabActor) {
return;
}
this.owner.onLocationChange(aPacket.uri, aPacket.title);
if (aPacket.url) {
this.owner.onLocationChange(aPacket.url, aPacket.title);
}
if (aPacket.state == "stop" && !aPacket.nativeConsoleAPI) {
this.owner.logWarningAboutReplacedAPI();
}
@ -4319,7 +4454,8 @@ WebConsoleConnectionProxy.prototype = {
};
let timer = null;
if (aOnDisconnect) {
let remoteTarget = this.target.isRemote;
if (aOnDisconnect && !remoteTarget) {
timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
timer.initWithCallback(onDisconnect, 1500, Ci.nsITimer.TYPE_ONE_SHOT);
}
@ -4329,11 +4465,20 @@ WebConsoleConnectionProxy.prototype = {
this.client.removeListener("networkEvent", this._onNetworkEvent);
this.client.removeListener("networkEventUpdate", this._onNetworkEventUpdate);
this.client.removeListener("fileActivity", this._onFileActivity);
this.client.removeListener("locationChange", this._onLocationChange);
this.client.removeListener("tabNavigated", this._onTabNavigated);
let client = this.client;
this.client = null;
this.webConsoleClient = null;
this.tabClient = null;
this.target = null;
this.connected = false;
this.owner = null;
try {
if (!this.target.isRemote) {
this.client.close(onDisconnect);
if (!remoteTarget) {
client.close(onDisconnect);
}
}
catch (ex) {
@ -4342,10 +4487,9 @@ WebConsoleConnectionProxy.prototype = {
onDisconnect();
}
this.client = null;
this.webConsoleClient = null;
this.connected = false;
this.owner = null;
if (remoteTarget) {
onDisconnect();
}
},
};

View File

@ -480,9 +480,10 @@ DebuggerClient.prototype = {
this._threadClients[aPacket.from]._onThreadState(aPacket);
}
// On navigation the server resumes, so the client must resume as well.
// We achive that by generating a fake resumption packet that triggers
// We achieve that by generating a fake resumption packet that triggers
// the client's thread state change listeners.
if (aPacket.type == UnsolicitedNotifications.tabNavigated &&
if (this.activeThread &&
aPacket.type == UnsolicitedNotifications.tabNavigated &&
aPacket.from in this._tabClients) {
let resumption = { from: this.activeThread._actor, type: "resumed" };
this.activeThread._onThreadState(resumption);

View File

@ -354,24 +354,51 @@ BrowserTabActor.prototype = {
// A constant prefix that will be used to form the actor ID by the server.
actorPrefix: "tab",
grip: function BTA_grip() {
dbg_assert(!this.exited,
"grip() shouldn't be called on exited browser actor.");
dbg_assert(this.actorID,
"tab should have an actorID.");
/**
* Getter for the tab title.
* @return string
* Tab title.
*/
get title() {
let title = this.browser.contentTitle;
// If contentTitle is empty (e.g. on a not-yet-restored tab), but there is a
// tabbrowser (i.e. desktop Firefox, but not Fennec), we can use the label
// as the title.
if (!title && this._tabbrowser) {
title = this._tabbrowser
._getTabForContentWindow(this.browser.contentWindow).label;
._getTabForContentWindow(this.contentWindow).label;
}
return title;
},
/**
* Getter for the tab URL.
* @return string
* Tab URL.
*/
get url() {
return this.browser.currentURI.spec;
},
/**
* Getter for the tab content window.
* @return nsIDOMWindow
* Tab content window.
*/
get contentWindow() {
return this.browser.contentWindow;
},
grip: function BTA_grip() {
dbg_assert(!this.exited,
"grip() shouldn't be called on exited browser actor.");
dbg_assert(this.actorID,
"tab should have an actorID.");
let response = {
actor: this.actorID,
title: title,
url: this.browser.currentURI.spec
title: this.title,
url: this.url,
};
// Walk over tab actors added by extensions and add them to a new ActorPool.
@ -391,10 +418,6 @@ BrowserTabActor.prototype = {
*/
disconnect: function BTA_disconnect() {
this._detach();
if (this._progressListener) {
this._progressListener.destroy();
}
this._extraActors = null;
},
@ -412,9 +435,6 @@ BrowserTabActor.prototype = {
type: "tabDetached" });
}
if (this._progressListener) {
this._progressListener.destroy();
}
this._browser = null;
this._tabbrowser = null;
},
@ -455,7 +475,7 @@ BrowserTabActor.prototype = {
this._contextPool = new ActorPool(this.conn);
this.conn.addActorPool(this._contextPool);
this.threadActor = new ThreadActor(this, this.browser.contentWindow.wrappedJSObject);
this.threadActor = new ThreadActor(this, this.contentWindow.wrappedJSObject);
this._contextPool.addActor(this.threadActor);
},
@ -480,6 +500,10 @@ BrowserTabActor.prototype = {
return;
}
if (this._progressListener) {
this._progressListener.destroy();
}
this.browser.removeEventListener("DOMWindowCreated", this._onWindowCreated, true);
this.browser.removeEventListener("pageshow", this._onWindowCreated, true);
@ -489,7 +513,7 @@ BrowserTabActor.prototype = {
this.conn.removeActorPool(this._tabPool);
this._tabPool = null;
if (this._tabActorPool) {
this.conn.removeActorPool(this._tabActorPool);
this.conn.removeActorPool(this._tabActorPool, true);
this._tabActorPool = null;
}
@ -515,10 +539,6 @@ BrowserTabActor.prototype = {
this._detach();
if (this._progressListener) {
this._progressListener.destroy();
}
return { type: "detached" };
},
@ -530,7 +550,7 @@ BrowserTabActor.prototype = {
// The tab is already closed.
return;
}
let windowUtils = this.browser.contentWindow
let windowUtils = this.contentWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
windowUtils.suppressEventHandling(true);
@ -545,7 +565,7 @@ BrowserTabActor.prototype = {
// The tab is already closed.
return;
}
let windowUtils = this.browser.contentWindow
let windowUtils = this.contentWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
windowUtils.resumeTimeouts();
@ -572,11 +592,6 @@ BrowserTabActor.prototype = {
if (this.threadActor.dbg) {
this.threadActor.dbg.enabled = true;
}
if (this._progressListener) {
delete this._progressListener._needsTabNavigated;
}
this.conn.send({ from: this.actorID, type: "tabNavigated",
url: this.browser.contentDocument.URL });
}
}
@ -586,7 +601,26 @@ BrowserTabActor.prototype = {
this.threadActor.findGlobals();
}
}
}
},
/**
* Tells if the window.console object is native or overwritten by script in
* the page.
*
* @param nsIDOMWindow aWindow
* The window object you want to check.
* @return boolean
* True if the window.console object is native, or false otherwise.
*/
hasNativeConsoleAPI: function BTA_hasNativeConsoleAPI(aWindow) {
let isNative = false;
try {
let console = aWindow.wrappedJSObject.console;
isNative = "__mozillaConsole__" in console;
}
catch (ex) { }
return isNative;
},
};
/**
@ -622,33 +656,45 @@ DebuggerProgressListener.prototype = {
let isWindow = aFlag & Ci.nsIWebProgressListener.STATE_IS_WINDOW;
// Skip non-interesting states.
if (isStart && isDocument && isRequest && isNetwork) {
if (!isWindow || !isNetwork ||
aProgress.DOMWindow != this._tabActor.contentWindow) {
return;
}
if (isStart && aRequest instanceof Ci.nsIChannel) {
// If the request is about to happen in a new window, we are not concerned
// about the request.
if (aProgress.DOMWindow != this._tabActor.browser.contentWindow) {
return;
// Proceed normally only if the debuggee is not paused.
if (this._tabActor.threadActor.state == "paused") {
aRequest.suspend();
this._tabActor.threadActor.onResume();
this._tabActor.threadActor.dbg.enabled = false;
this._tabActor._pendingNavigation = aRequest;
}
// If the debuggee is not paused, then proceed normally.
if (this._tabActor.threadActor.state != "paused") {
return;
}
aRequest.suspend();
this._tabActor.threadActor.onResume();
this._tabActor.threadActor.dbg.enabled = false;
this._tabActor._pendingNavigation = aRequest;
this._needsTabNavigated = true;
} else if (isStop && isWindow && isNetwork && this._needsTabNavigated) {
delete this._needsTabNavigated;
this._tabActor.threadActor.dbg.enabled = true;
this._tabActor.conn.send({
from: this._tabActor.actorID,
type: "tabNavigated",
url: this._tabActor.browser.contentDocument.URL
url: aRequest.URI.spec,
title: "",
nativeConsoleAPI: true,
state: "start",
});
} else if (isStop) {
if (this._tabActor.threadActor.state == "running") {
this._tabActor.threadActor.dbg.enabled = true;
}
this.destroy();
let window = this._tabActor.contentWindow;
this._tabActor.conn.send({
from: this._tabActor.actorID,
type: "tabNavigated",
url: this._tabActor.url,
title: this._tabActor.title,
nativeConsoleAPI: this._tabActor.hasNativeConsoleAPI(window),
state: "stop",
});
}
},
@ -657,7 +703,11 @@ DebuggerProgressListener.prototype = {
*/
destroy: function DPL_destroy() {
if (this._tabActor._tabbrowser.removeProgressListener) {
this._tabActor._tabbrowser.removeProgressListener(this);
try {
this._tabActor._tabbrowser.removeProgressListener(this);
} catch (ex) {
// This can throw during browser shutdown.
}
}
this._tabActor._progressListener = null;
this._tabActor = null;

View File

@ -541,11 +541,20 @@ DebuggerServerConnection.prototype = {
/**
* Remove a previously-added pool of actors to the connection.
*
* @param ActorPool aActorPool
* The ActorPool instance you want to remove.
* @param boolean aCleanup
* True if you want to disconnect each actor from the pool, false
* otherwise.
*/
removeActorPool: function DSC_removeActorPool(aActorPool) {
removeActorPool: function DSC_removeActorPool(aActorPool, aCleanup) {
let index = this._extraPools.lastIndexOf(aActorPool);
if (index > -1) {
this._extraPools.splice(index, 1);
let pool = this._extraPools.splice(index, 1);
if (aCleanup) {
pool.map(function(p) { p.cleanup(); });
}
}
},

View File

@ -160,23 +160,7 @@ WebConsoleActor.prototype =
return { actor: this.actorID };
},
/**
* Tells if the window.console object is native or overwritten by script in
* the page.
*
* @return boolean
* True if the window.console object is native, or false otherwise.
*/
hasNativeConsoleAPI: function WCA_hasNativeConsoleAPI()
{
let isNative = false;
try {
let consoleObject = WebConsoleUtils.unwrap(this.window).console;
isNative = "__mozillaConsole__" in consoleObject;
}
catch (ex) { }
return isNative;
},
hasNativeConsoleAPI: BrowserTabActor.prototype.hasNativeConsoleAPI,
/**
* Destroy the current WebConsoleActor instance.
@ -336,20 +320,11 @@ WebConsoleActor.prototype =
MONITOR_FILE_ACTIVITY);
startedListeners.push(listener);
break;
case "LocationChange":
if (!this.consoleProgressListener) {
this.consoleProgressListener =
new ConsoleProgressListener(this.window, this);
}
this.consoleProgressListener.startMonitor(this.consoleProgressListener.
MONITOR_LOCATION_CHANGE);
startedListeners.push(listener);
break;
}
}
return {
startedListeners: startedListeners,
nativeConsoleAPI: this.hasNativeConsoleAPI(),
nativeConsoleAPI: this.hasNativeConsoleAPI(this.window),
};
},
@ -370,7 +345,7 @@ WebConsoleActor.prototype =
// listeners.
let toDetach = aRequest.listeners ||
["PageError", "ConsoleAPI", "NetworkActivity",
"FileActivity", "LocationChange"];
"FileActivity"];
while (toDetach.length > 0) {
let listener = toDetach.shift();
@ -403,13 +378,6 @@ WebConsoleActor.prototype =
}
stoppedListeners.push(listener);
break;
case "LocationChange":
if (this.consoleProgressListener) {
this.consoleProgressListener.stopMonitor(this.consoleProgressListener.
MONITOR_LOCATION_CHANGE);
}
stoppedListeners.push(listener);
break;
}
}
@ -737,34 +705,6 @@ WebConsoleActor.prototype =
this.conn.send(packet);
},
/**
* Handler for location changes. This method sends the new browser location
* to the remote Web Console client.
*
* @see ConsoleProgressListener
* @param string aState
* Tells the location change state:
* - "start" means a load has begun.
* - "stop" means load completed.
* @param string aURI
* The new browser URI.
* @param string aTitle
* The new page title URI.
*/
onLocationChange: function WCA_onLocationChange(aState, aURI, aTitle)
{
// TODO: Bug 792062 - Make the tabNavigated notification reusable by the Web Console
let packet = {
from: this.actorID,
type: "locationChange",
uri: aURI,
title: aTitle,
state: aState,
nativeConsoleAPI: this.hasNativeConsoleAPI(),
};
this.conn.send(packet);
},
//////////////////
// End of event handlers for various listeners.
//////////////////