--- a/android-browser.diff
+++ b/android-browser.diff
@@ -1,22 +1,22 @@
# HG changeset patch
# User Dave Camp <dcamp@mozilla.com>
# Date 1372178155 25200
# Tue Jun 25 09:35:55 2013 -0700
-# Node ID aad4f3eb5b6c8fee5ab2e94c17c784d4ac9bcc7d
-# Parent 00ddc3161ebd3ab028c7344c198696f9f0c0d27b
+# Node ID db733687ce263d6c9683464df3ca65d632d52687
+# Parent 4502bf53830e85ec59f874d2856695ab53de8a40
Bug 888492 - Use the tab actor's _browser rather than _tabbrowser for load notifications. r=jwalker
diff --git a/toolkit/devtools/server/actors/inspector.js b/toolkit/devtools/server/actors/inspector.js
--- a/toolkit/devtools/server/actors/inspector.js
+++ b/toolkit/devtools/server/actors/inspector.js
-@@ -1984,17 +1984,17 @@ var InspectorActor = protocol.ActorClass
- getWalker: method(function(options={}) {
+@@ -2034,17 +2034,17 @@ var InspectorActor = protocol.ActorClass
let deferred = promise.defer();
+ this._walkerPromise = deferred.promise;
let window = this.window;
var domReady = () => {
let tabActor = this.tabActor;
window.removeEventListener("DOMContentLoaded", domReady, true);
- deferred.resolve(WalkerActor(this.conn, window.document, tabActor._tabbrowser, options));
+ deferred.resolve(WalkerActor(this.conn, window.document, tabActor._browser, options));
deleted file mode 100644
--- a/copy-html.diff
+++ /dev/null
@@ -1,58 +0,0 @@
-# HG changeset patch
-# User Dave Camp <dcamp@mozilla.com>
-# Date 1371477175 25200
-# Mon Jun 17 06:52:55 2013 -0700
-# Node ID a5aab8dafa96ddb3551c06ffcd701ba122a63bce
-# Parent c62a389a817a1fdf7126044c2ac535a6c91723aa
-imported patch copy-html.diff
-
-diff --git a/browser/devtools/inspector/inspector-panel.js b/browser/devtools/inspector/inspector-panel.js
---- a/browser/devtools/inspector/inspector-panel.js
-+++ b/browser/devtools/inspector/inspector-panel.js
-@@ -630,34 +630,38 @@ InspectorPanel.prototype = {
- /**
- * Copy the innerHTML of the selected Node to the clipboard.
- */
- copyInnerHTML: function InspectorPanel_copyInnerHTML()
- {
- if (!this.selection.isNode()) {
- return;
- }
-- let toCopy = this.selection.node.innerHTML;
-- if (toCopy) {
-- clipboardHelper.copyString(toCopy);
-- }
-+ this._copyLongStr(this.walker.innerHTML(this.selection.nodeFront));
- },
-
- /**
- * Copy the outerHTML of the selected Node to the clipboard.
- */
- copyOuterHTML: function InspectorPanel_copyOuterHTML()
- {
- if (!this.selection.isNode()) {
- return;
- }
-- let toCopy = this.selection.node.outerHTML;
-- if (toCopy) {
-- clipboardHelper.copyString(toCopy);
-- }
-+
-+ this._copyLongStr(this.walker.outerHTML(this.selection.nodeFront));
-+ },
-+
-+ _copyLongStr: function(promise) {
-+ return promise.then(longstr => {
-+ return longstr.string().then(toCopy => {
-+ longstr.release().then(null, console.error);
-+ clipboardHelper.copyString(toCopy);
-+ });
-+ }).then(null, console.error);
- },
-
- /**
- * Copy a unique selector of the selected Node to the clipboard.
- */
- copyUniqueSelector: function InspectorPanel_copyUniqueSelector()
- {
- if (!this.selection.isNode()) {
deleted file mode 100644
--- a/inspector-panel-default-node.diff
+++ /dev/null
@@ -1,85 +0,0 @@
-# HG changeset patch
-# User Dave Camp <dcamp@mozilla.com>
-# Date 1371477174 25200
-# Mon Jun 17 06:52:54 2013 -0700
-# Node ID c62a389a817a1fdf7126044c2ac535a6c91723aa
-# Parent dc0232e8d114bb05987546bfcebdeee2f082b671
-imported patch inspector-panel-default-node.diff
-
-diff --git a/browser/devtools/inspector/inspector-panel.js b/browser/devtools/inspector/inspector-panel.js
---- a/browser/devtools/inspector/inspector-panel.js
-+++ b/browser/devtools/inspector/inspector-panel.js
-@@ -152,24 +152,34 @@ InspectorPanel.prototype = {
-
- return deferred.promise;
- },
-
- /**
- * Return a promise that will resolve to the default node for selection.
- */
- _getDefaultNodeForSelection : function() {
-+ if (this._defaultNode) {
-+ return this._defaultNode;
-+ }
-+ let walker = this.walker;
- // if available set body node as default selected node
- // else set documentElement
-- return this.walker.querySelector(this.walker.rootNode, "body").then(front => {
-+ return walker.querySelector(this.walker.rootNode, "body").then(front => {
- if (front) {
- return front;
- }
- return this.walker.documentElement(this.walker.rootNode);
-- });
-+ }).then(node => {
-+ if (walker !== this.walker) {
-+ promise.reject(null);
-+ }
-+ this._defaultNode = node;
-+ return node;
-+ })
- },
-
- /**
- * Selection object (read only)
- */
- get selection() {
- return this._selection;
- },
-@@ -271,16 +281,17 @@ InspectorPanel.prototype = {
-
- /**
- * Reset the inspector on navigate away.
- */
- onNavigatedAway: function InspectorPanel_onNavigatedAway(event, payload) {
- let newWindow = payload._navPayload || payload;
- this.walker.release().then(null, console.error);
- this.walker = null;
-+ this._defaultNode = null;
- this.selection.setNodeFront(null);
- this.selection.setWalker(null);
- this._destroyMarkup();
- this.isDirty = false;
-
- this.target.inspector.getWalker().then(walker => {
- if (this._destroyPromise) {
- walker.release().then(null, console.error);
-@@ -386,17 +397,17 @@ InspectorPanel.prototype = {
- },
-
- /**
- * When a node is deleted, select its parent node.
- */
- onDetached: function InspectorPanel_onDetached(event, parentNode) {
- this.cancelLayoutChange();
- this.breadcrumbs.cutAfter(this.breadcrumbs.indexOf(parentNode));
-- this.selection.setNodeFront(parentNode, "detached");
-+ this.selection.setNodeFront(parentNode ? parentNode : this._defaultNode, "detached");
- },
-
- /**
- * Destroy the inspector.
- */
- destroy: function InspectorPanel__destroy() {
- if (this._destroyPromise) {
- return this._destroyPromise;
deleted file mode 100644
--- a/inspector-remote-delete-node.diff
+++ /dev/null
@@ -1,132 +0,0 @@
-# HG changeset patch
-# User Dave Camp <dcamp@mozilla.com>
-# Date 1371477175 25200
-# Mon Jun 17 06:52:55 2013 -0700
-# Node ID 00ddc3161ebd3ab028c7344c198696f9f0c0d27b
-# Parent f2cb449dbdfbb5f006856d91943417c22bbd230e
-imported patch inspector-remote-delete-node.diff
-
-diff --git a/browser/devtools/inspector/inspector-panel.js b/browser/devtools/inspector/inspector-panel.js
---- a/browser/devtools/inspector/inspector-panel.js
-+++ b/browser/devtools/inspector/inspector-panel.js
-@@ -685,18 +685,17 @@ InspectorPanel.prototype = {
- }
-
- // If the markup panel is active, use the markup panel to delete
- // the node, making this an undoable action.
- if (this.markup) {
- this.markup.deleteNode(this.selection.nodeFront);
- } else {
- // remove the node from content
-- let parent = this.selection.node.parentNode;
-- parent.removeChild(this.selection.node);
-+ this.walker.removeNode(this.selection.nodeFront);
- }
- },
-
- /**
- * Schedule a low-priority change event for things like paint
- * and resize.
- */
- scheduleLayoutChange: function Inspector_scheduleLayoutChange()
-diff --git a/browser/devtools/inspector/test/browser_inspector_menu.js b/browser/devtools/inspector/test/browser_inspector_menu.js
---- a/browser/devtools/inspector/test/browser_inspector_menu.js
-+++ b/browser/devtools/inspector/test/browser_inspector_menu.js
-@@ -98,17 +98,17 @@ function test() {
- function() { copyUniqueSelector.doCommand(); },
- testDeleteNode, testDeleteNode);
- }
-
- function testDeleteNode() {
- let deleteNode = inspector.panelDoc.getElementById("node-menu-delete");
- ok(deleteNode, "the popup menu has a delete menu item");
-
-- inspector.selection.once("detached", deleteTest);
-+ inspector.once("markupmutation", deleteTest);
-
- let commandEvent = document.createEvent("XULCommandEvent");
- commandEvent.initCommandEvent("command", true, true, window, 0, false, false,
- false, false, null);
- deleteNode.dispatchEvent(commandEvent);
- }
-
- function deleteTest() {
-diff --git a/browser/devtools/markupview/markup-view.js b/browser/devtools/markupview/markup-view.js
---- a/browser/devtools/markupview/markup-view.js
-+++ b/browser/devtools/markupview/markup-view.js
-@@ -225,38 +225,38 @@ MarkupView.prototype = {
- },
-
- /**
- * Delete a node from the DOM.
- * This is an undoable action.
- */
- deleteNode: function MC__deleteNode(aNode)
- {
-- aNode = aNode.rawNode();
-- if (!aNode) {
-- return;
-- }
-- let doc = nodeDocument(aNode);
-- if (aNode === doc ||
-- aNode === doc.documentElement ||
-+ if (aNode.isDocumentElement ||
- aNode.nodeType == Ci.nsIDOMNode.DOCUMENT_TYPE_NODE) {
- return;
- }
-
-- let parentNode = aNode.parentNode;
-- let sibling = aNode.nextSibling;
-+ let container = this._containers.get(aNode);
-
-- this.undo.do(function() {
-- if (aNode.selected) {
-- this.navigate(this._containers.get(parentNode));
-- }
-- parentNode.removeChild(aNode);
-- }.bind(this), function() {
-- parentNode.insertBefore(aNode, sibling);
-- });
-+ // Retain the node so we can undo this...
-+ this.walker.retainNode(aNode).then(() => {
-+ let parent = aNode.parentNode();
-+ let sibling = null;
-+ this.undo.do(() => {
-+ if (container.selected) {
-+ this.navigate(this._containers.get(parent));
-+ }
-+ this.walker.removeNode(aNode).then(nextSibling => {
-+ sibling = nextSibling;
-+ });
-+ }, () => {
-+ this.walker.insertBefore(aNode, parent, sibling);
-+ });
-+ }).then(null, console.error);
- },
-
- /**
- * If an editable item is focused, select its container.
- */
- _onFocus: function MC__onFocus(aEvent) {
- let parent = aEvent.target;
- while (!parent.container) {
-@@ -588,16 +588,19 @@ MarkupView.prototype = {
- // centered node.
- let centered = this._checkSelectionVisible(aContainer);
-
- // Children aren't updated yet, but clear the childrenDirty flag anyway.
- // If the dirty flag is re-set while we're fetching we'll need to fetch
- // again.
- aContainer.childrenDirty = false;
- let updatePromise = this._getVisibleChildren(aContainer, centered).then(children => {
-+ if (!this._containers) {
-+ return promise.reject("markup view destroyed");
-+ }
- this._queuedChildUpdates.delete(aContainer);
-
- // If children are dirty, we got a change notification for this node
- // while the request was in progress, we need to do it again.
- if (aContainer.childrenDirty) {
- return this._updateChildren(aContainer, centered);
- }
-
new file mode 100644
--- /dev/null
+++ b/promise-return.diff
@@ -0,0 +1,76 @@
+# HG changeset patch
+# User Dave Camp <dcamp@mozilla.com>
+# Date 1374270262 25200
+# Fri Jul 19 14:44:22 2013 -0700
+# Node ID 05edf651c461763fb584de9a6b9ac87c1f71ec3f
+# Parent a3432a61411e4585061f69e75b0a12d7324ecb11
+[mq]: promise-return.diff
+
+diff --git a/toolkit/devtools/server/main.js b/toolkit/devtools/server/main.js
+--- a/toolkit/devtools/server/main.js
++++ b/toolkit/devtools/server/main.js
+@@ -644,16 +644,18 @@ function DebuggerServerConnection(aPrefi
+ {
+ this._prefix = aPrefix;
+ this._transport = aTransport;
+ this._transport.hooks = this;
+ this._nextID = 1;
+
+ this._actorPool = new ActorPool(this);
+ this._extraPools = [];
++
++ this._actorResponses = new Map;
+ }
+
+ DebuggerServerConnection.prototype = {
+ _prefix: null,
+ get prefix() { return this._prefix },
+
+ _transport: null,
+ get transport() { return this._transport },
+@@ -817,29 +819,32 @@ DebuggerServerConnection.prototype = {
+ }
+
+ if (!ret) {
+ // This should become an error once we've converted every user
+ // of this to promises in bug 794078.
+ return;
+ }
+
+- resolve(ret)
+- .then(null, (e) => {
+- return this._unknownError(
+- "error occurred while processing '" + aPacket.type,
+- e);
+- })
+- .then(function (aResponse) {
+- if (!aResponse.from) {
+- aResponse.from = aPacket.to;
+- }
+- return aResponse;
+- })
+- .then(this.transport.send.bind(this.transport));
++ let pendingResponse = this._actorResponses.get(actor.actorID) || promise.resolve(null);
++
++ let response = pendingResponse.then(() => {
++ return resolve(ret);
++ }).then(null, (e) => {
++ return this._unknownError(
++ "error occurred while processing '" + aPacket.type,
++ e);
++ }).then(function (aResponse) {
++ if (!aResponse.from) {
++ aResponse.from = aPacket.to;
++ }
++ return aResponse;
++ }).then(this.transport.send.bind(this.transport));
++ this._actorResponses.set(actorID, response);
++ return response;
+ },
+
+ /**
+ * Called by DebuggerTransport when the underlying stream is closed.
+ *
+ * @param aStatus nsresult
+ * The status code that corresponds to the reason for closing
+ * the stream.
deleted file mode 100644
--- a/remote-edit-attributes.diff
+++ /dev/null
@@ -1,325 +0,0 @@
-# HG changeset patch
-# User Dave Camp <dcamp@mozilla.com>
-# Date 1371477175 25200
-# Mon Jun 17 06:52:55 2013 -0700
-# Node ID 6155b1a4a5a5b04ba68ea367f36721c00bf3f307
-# Parent 78398f24d7703ef98e71055b3d9db45731a55c31
-Bug 886035 - Port markup view attribute editing to the inspector actor. r=paul
-
-diff --git a/browser/devtools/markupview/markup-view.js b/browser/devtools/markupview/markup-view.js
---- a/browser/devtools/markupview/markup-view.js
-+++ b/browser/devtools/markupview/markup-view.js
-@@ -1101,47 +1101,53 @@ function ElementEditor(aContainer, aNode
- this.template("elementContentSummary", this);
- }
-
- // Create the closing tag
- this.template("elementClose", this);
-
- this.rawNode = aNode.rawNode();
-
-- // Make the tag name editable (unless this is a document element)
-- if (this.rawNode) {
-- if (!aNode.isDocumentElement) {
-- this.tag.setAttribute("tabindex", "0");
-- editableField({
-- element: this.tag,
-- trigger: "dblclick",
-- stopOnReturn: true,
-- done: this.onTagEdit.bind(this),
-- });
-- }
--
-- // Make the new attribute space editable.
-+ // Make the tag name editable (unless this is a remote node or
-+ // a document element)
-+ if (this.rawNode && !aNode.isDocumentElement) {
-+ this.tag.setAttribute("tabindex", "0");
- editableField({
-- element: this.newAttr,
-+ element: this.tag,
- trigger: "dblclick",
- stopOnReturn: true,
-- done: function EE_onNew(aVal, aCommit) {
-- if (!aCommit) {
-- return;
-- }
--
-- try {
-- this._applyAttributes(aVal);
-- } catch (x) {
-- return;
-- }
-- }.bind(this)
-+ done: this.onTagEdit.bind(this),
- });
- }
-
-+ // Make the new attribute space editable.
-+ editableField({
-+ element: this.newAttr,
-+ trigger: "dblclick",
-+ stopOnReturn: true,
-+ done: (aVal, aCommit) => {
-+ if (!aCommit) {
-+ return;
-+ }
-+
-+ try {
-+ let doMods = this._startModifyingAttributes();
-+ let undoMods = this._startModifyingAttributes();
-+ this._applyAttributes(aVal, null, doMods, undoMods);
-+ this.undo.do(() => {
-+ doMods.apply();
-+ }, function() {
-+ undoMods.apply();
-+ });
-+ } catch(x) {
-+ console.log(x);
-+ }
-+ }
-+ });
-+
- let tagName = this.node.nodeName.toLowerCase();
- this.tag.textContent = tagName;
- this.closeTag.textContent = tagName;
-
- this.update();
- }
-
- ElementEditor.prototype = {
-@@ -1170,16 +1176,20 @@ ElementEditor.prototype = {
- for (let i = 0; i < attrs.length; i++) {
- let attr = this._createAttribute(attrs[i]);
- if (!attr.inplaceEditor) {
- attr.style.removeProperty("display");
- }
- }
- },
-
-+ _startModifyingAttributes: function() {
-+ return this.node.startModifyingAttributes();
-+ },
-+
- _createAttribute: function EE_createAttribute(aAttr, aBefore)
- {
- if (this.attrs.indexOf(aAttr.name) !== -1) {
- var attr = this.attrs[aAttr.name];
- var name = attr.querySelector(".attrname");
- var val = attr.querySelector(".attrvalue");
- } else {
- // Create the template editor, which will save some variables here.
-@@ -1194,59 +1204,61 @@ ElementEditor.prototype = {
- if (aAttr.name == "id") {
- before = this.attrList.firstChild;
- } else if (aAttr.name == "class") {
- let idNode = this.attrs["id"];
- before = idNode ? idNode.nextSibling : this.attrList.firstChild;
- }
- this.attrList.insertBefore(attr, before);
-
-- if (this.rawNode) {
-+ // Make the attribute editable.
-+ editableField({
-+ element: inner,
-+ trigger: "dblclick",
-+ stopOnReturn: true,
-+ selectAll: false,
-+ start: (aEditor, aEvent) => {
-+ // If the editing was started inside the name or value areas,
-+ // select accordingly.
-+ if (aEvent && aEvent.target === name) {
-+ aEditor.input.setSelectionRange(0, name.textContent.length);
-+ } else if (aEvent && aEvent.target === val) {
-+ let length = val.textContent.length;
-+ let editorLength = aEditor.input.value.length;
-+ let start = editorLength - (length + 1);
-+ aEditor.input.setSelectionRange(start, start + length);
-+ } else {
-+ aEditor.input.select();
-+ }
-+ },
-+ done: (aVal, aCommit) => {
-+ if (!aCommit) {
-+ return;
-+ }
-
-- // Make the attribute editable.
-- editableField({
-- element: inner,
-- trigger: "dblclick",
-- stopOnReturn: true,
-- selectAll: false,
-- start: function EE_editAttribute_start(aEditor, aEvent) {
-- // If the editing was started inside the name or value areas,
-- // select accordingly.
-- if (aEvent && aEvent.target === name) {
-- aEditor.input.setSelectionRange(0, name.textContent.length);
-- } else if (aEvent && aEvent.target === val) {
-- let length = val.textContent.length;
-- let editorLength = aEditor.input.value.length;
-- let start = editorLength - (length + 1);
-- aEditor.input.setSelectionRange(start, start + length);
-- } else {
-- aEditor.input.select();
-- }
-- },
-- done: function EE_editAttribute_done(aVal, aCommit) {
-- if (!aCommit) {
-- return;
-- }
-+ let doMods = this._startModifyingAttributes();
-+ let undoMods = this._startModifyingAttributes();
-
-- this.undo.startBatch();
--
-- // Remove the attribute stored in this editor and re-add any attributes
-- // parsed out of the input element. Restore original attribute if
-- // parsing fails.
-- this._removeAttribute(this.rawNode, aAttr.name);
-- try {
-- this._applyAttributes(aVal, attr);
-- this.undo.endBatch();
-- } catch (e) {
-- this.undo.endBatch();
-- this.undo.undo();
-- }
-- }.bind(this)
-- });
-- }
-+ // Remove the attribute stored in this editor and re-add any attributes
-+ // parsed out of the input element. Restore original attribute if
-+ // parsing fails.
-+ try {
-+ this._saveAttribute(aAttr.name, undoMods);
-+ doMods.removeAttribute(aAttr.name);
-+ this._applyAttributes(aVal, attr, doMods, undoMods);
-+ this.undo.do(() => {
-+ doMods.apply();
-+ }, () => {
-+ undoMods.apply();
-+ })
-+ } catch(ex) {
-+ console.error(ex);
-+ }
-+ }
-+ });
-
- this.attrs[aAttr.name] = attr;
- }
-
- name.textContent = aAttr.name;
- val.textContent = aAttr.value;
-
- return attr;
-@@ -1256,94 +1268,45 @@ ElementEditor.prototype = {
- * Parse a user-entered attribute string and apply the resulting
- * attributes to the node. This operation is undoable.
- *
- * @param string aValue the user-entered value.
- * @param Element aAttrNode the attribute editor that created this
- * set of attributes, used to place new attributes where the
- * user put them.
- */
-- _applyAttributes: function EE__applyAttributes(aValue, aAttrNode)
-+ _applyAttributes: function EE__applyAttributes(aValue, aAttrNode, aDoMods, aUndoMods)
- {
- let attrs = escapeAttributeValues(aValue);
-
-- this.undo.startBatch();
-+ for (let attr of attrs) {
-+ // Create an attribute editor next to the current attribute if needed.
-+ this._createAttribute(attr, aAttrNode ? aAttrNode.nextSibling : null);
-
-- for (let attr of attrs) {
-- let attribute = {
-- name: attr.name,
-- value: attr.value
-- };
-- // Create an attribute editor next to the current attribute if needed.
-- this._createAttribute(attribute, aAttrNode ? aAttrNode.nextSibling : null);
-- this._setAttribute(this.rawNode, attr.name, attr.value);
-- }
--
-- this.undo.endBatch();
-- },
--
-- /**
-- * Helper function for _setAttribute and _removeAttribute,
-- * returns a function that puts an attribute back the way it was.
-- */
-- _restoreAttribute: function EE_restoreAttribute(aNode, aName)
-- {
-- if (aNode.hasAttribute(aName)) {
-- let oldValue = aNode.getAttribute(aName);
-- return function() {
-- aNode.setAttribute(aName, oldValue);
-- this.markup.nodeChanged(aNode);
-- }.bind(this);
-- } else {
-- return function() {
-- aNode.removeAttribute(aName);
-- this.markup.nodeChanged(aNode);
-- }.bind(this);
-+ this._saveAttribute(attr.name, aUndoMods);
-+ aDoMods.setAttribute(attr.name, attr.value);
- }
- },
-
- /**
-- * Sets an attribute. This operation is undoable.
-+ * Saves the current state of the given attribute into an attribute
-+ * modification list.
- */
-- _setAttribute: function EE_setAttribute(aNode, aName, aValue)
-+ _saveAttribute: function(aName, aUndoMods)
- {
-- this.undo.do(function() {
-- aNode.setAttribute(aName, aValue);
-- this.markup.nodeChanged(aNode);
-- }.bind(this), this._restoreAttribute(aNode, aName));
-+ let node = this.node;
-+ if (node.hasAttribute(aName)) {
-+ let oldValue = node.getAttribute(aName);
-+ aUndoMods.setAttribute(aName, oldValue);
-+ } else {
-+ aUndoMods.removeAttribute(aName);
-+ }
- },
-
- /**
-- * Removes an attribute. This operation is undoable.
-- */
-- _removeAttribute: function EE_removeAttribute(aNode, aName)
-- {
-- this.undo.do(function() {
-- aNode.removeAttribute(aName);
-- this.markup.nodeChanged(aNode);
-- }.bind(this), this._restoreAttribute(aNode, aName));
-- },
--
-- /**
-- * Handler for the new attribute editor.
-- */
-- _onNewAttribute: function EE_onNewAttribute(aValue, aCommit)
-- {
-- if (!aValue || !aCommit) {
-- return;
-- }
--
-- this._setAttribute(this.rawNode, aValue, "");
-- let attr = this._createAttribute({ name: aValue, value: ""});
-- attr.style.removeAttribute("display");
-- attr.querySelector("attrvalue").click();
-- },
--
--
-- /**
- * Called when the tag name editor has is done editing.
- */
- onTagEdit: function EE_onTagEdit(aVal, aCommit) {
- if (!aCommit || aVal == this.rawNode.tagName) {
- return;
- }
-
- // Create a new element with the same attributes as the
deleted file mode 100644
--- a/remote-edit-value.diff
+++ /dev/null
@@ -1,76 +0,0 @@
-# HG changeset patch
-# User Dave Camp <dcamp@mozilla.com>
-# Date 1371477175 25200
-# Mon Jun 17 06:52:55 2013 -0700
-# Node ID dc0232e8d114bb05987546bfcebdeee2f082b671
-# Parent 9c16b8a6b6cec44b4eac86c486ca441380dd13bd
-Bug 886033 - Port markup view text value editing to inspector actor. r=paul
-
-diff --git a/browser/devtools/markupview/markup-view.js b/browser/devtools/markupview/markup-view.js
---- a/browser/devtools/markupview/markup-view.js
-+++ b/browser/devtools/markupview/markup-view.js
-@@ -1003,38 +1003,42 @@ function DoctypeEditor(aContainer, aNode
- */
- function TextEditor(aContainer, aNode, aTemplate)
- {
- this.node = aNode;
- this._selected = false;
-
- aContainer.markup.template(aTemplate, this);
-
-- let rawNode = aNode.rawNode();
-- if (rawNode) {
-- editableField({
-- element: this.value,
-- stopOnReturn: true,
-- trigger: "dblclick",
-- multiline: true,
-- done: function TE_done(aVal, aCommit) {
-- if (!aCommit) {
-- return;
-- }
-- let oldValue = rawNode.nodeValue;
-- aContainer.undo.do(function() {
-- rawNode.nodeValue = aVal;
-- aContainer.markup.nodeChanged(this.node);
-- }.bind(this), function() {
-- rawNode.nodeValue = oldValue;
-- aContainer.markup.nodeChanged(this.node);
-- }.bind(this));
-- }.bind(this)
-- });
-- }
-+ editableField({
-+ element: this.value,
-+ stopOnReturn: true,
-+ trigger: "dblclick",
-+ multiline: true,
-+ done: (aVal, aCommit) => {
-+ if (!aCommit) {
-+ return;
-+ }
-+ this.node.getNodeValue().then(longstr => {
-+ longstr.string().then(oldValue => {
-+ longstr.release().then(null, console.error);
-+
-+ aContainer.undo.do(() => {
-+ this.node.setNodeValue(aVal).then(() => {
-+ aContainer.markup.nodeChanged(this.node);
-+ });
-+ }, () => {
-+ this.node.setNodeValue(oldValue).then(() => {
-+ aContainer.markup.nodeChanged(this.node);
-+ })
-+ });
-+ });
-+ });
-+ }
-+ });
-
- this.update();
- }
-
- TextEditor.prototype = {
- get selected() this._selected,
- set selected(aValue) {
- if (aValue === this._selected) {
deleted file mode 100644
--- a/remote-markup.diff
+++ /dev/null
@@ -1,2330 +0,0 @@
-# HG changeset patch
-# User Dave Camp <dcamp@mozilla.com>
-# Date 1370924326 25200
-# Mon Jun 10 21:18:46 2013 -0700
-# Node ID 78398f24d7703ef98e71055b3d9db45731a55c31
-# Parent 20848adc99809d3d24ae2425732017fd8d3ecb25
-Bug 886032 - Port the markup view to the inspector actor (readonly). r=paul
-
-diff --git a/browser/devtools/inspector/inspector-panel.js b/browser/devtools/inspector/inspector-panel.js
---- a/browser/devtools/inspector/inspector-panel.js
-+++ b/browser/devtools/inspector/inspector-panel.js
-@@ -136,17 +136,17 @@ InspectorPanel.prototype = {
-
- // All the components are initialized. Let's select a node.
- this._selection.setNodeFront(defaultSelection);
-
- if (this.highlighter) {
- this.highlighter.unlock();
- }
-
-- this.markup.expandNode(this.selection.node);
-+ this.markup.expandNode(this.selection.nodeFront);
-
- this.emit("ready");
- deferred.resolve(this);
- }.bind(this));
-
- this.setupSearchBox();
- this.setupSidebar();
-
-@@ -292,17 +292,17 @@ InspectorPanel.prototype = {
- this._getDefaultNodeForSelection().then(defaultNode => {
- if (this._destroyPromise) {
- return;
- }
- this.selection.setNodeFront(defaultNode, "navigateaway");
-
- this._initMarkup();
- this.once("markuploaded", () => {
-- this.markup.expandNode(this.selection.node);
-+ this.markup.expandNode(this.selection.nodeFront);
- this.setupSearchBox();
- });
- });
- });
- },
-
- /**
- * When a new node is selected.
-@@ -663,27 +663,24 @@ InspectorPanel.prototype = {
- * Delete the selected node.
- */
- deleteNode: function IUI_deleteNode() {
- if (!this.selection.isNode() ||
- this.selection.isRoot()) {
- return;
- }
-
-- let toDelete = this.selection.node;
--
-- let parent = this.selection.node.parentNode;
--
- // If the markup panel is active, use the markup panel to delete
- // the node, making this an undoable action.
- if (this.markup) {
-- this.markup.deleteNode(toDelete);
-+ this.markup.deleteNode(this.selection.nodeFront);
- } else {
- // remove the node from content
-- parent.removeChild(toDelete);
-+ let parent = this.selection.node.parentNode;
-+ parent.removeChild(this.selection.node);
- }
- },
-
- /**
- * Schedule a low-priority change event for things like paint
- * and resize.
- */
- scheduleLayoutChange: function Inspector_scheduleLayoutChange()
-diff --git a/browser/devtools/inspector/test/browser_inspector_bug_817558_delete_node.js b/browser/devtools/inspector/test/browser_inspector_bug_817558_delete_node.js
---- a/browser/devtools/inspector/test/browser_inspector_bug_817558_delete_node.js
-+++ b/browser/devtools/inspector/test/browser_inspector_bug_817558_delete_node.js
-@@ -22,30 +22,31 @@ function test()
- node = iframe.contentDocument.querySelector("span");
- openInspector(runTests);
- }
-
- function runTests(aInspector)
- {
- inspector = aInspector;
- inspector.selection.setNode(node);
-+ inspector.once("inspector-updated", () => {
-+ let parentNode = node.parentNode;
-+ parentNode.removeChild(node);
-
-- let parentNode = node.parentNode;
-- parentNode.removeChild(node);
-+ let tmp = {};
-+ Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tmp);
-+ ok(!tmp.LayoutHelpers.isNodeConnected(node), "Node considered as disconnected.");
-
-- let tmp = {};
-- Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tmp);
-- ok(!tmp.LayoutHelpers.isNodeConnected(node), "Node considered as disconnected.");
--
-- // Wait for the selection to process the mutation
-- inspector.walker.on("mutations", () => {
-- is(inspector.selection.node, parentNode, "parent of selection got selected");
-- finishUp();
-+ // Wait for the inspector to process the mutation
-+ inspector.once("inspector-updated", () => {
-+ is(inspector.selection.node, parentNode, "parent of selection got selected");
-+ finishUp();
-+ });
- });
-- }
-+ };
-
- function finishUp() {
- node = null;
- gBrowser.removeCurrentTab();
- finish();
- }
- }
-
-diff --git a/browser/devtools/inspector/test/browser_inspector_initialization.js b/browser/devtools/inspector/test/browser_inspector_initialization.js
---- a/browser/devtools/inspector/test/browser_inspector_initialization.js
-+++ b/browser/devtools/inspector/test/browser_inspector_initialization.js
-@@ -67,36 +67,47 @@ function startInspectorTests(toolbox)
-
-
- function testHighlighter(node)
- {
- ok(isHighlighting(), "Highlighter is highlighting");
- is(getHighlitNode(), node, "Right node is highlighted");
- }
-
-+let callNo = 0;
- function testMarkupView(node)
- {
- let i = getActiveInspector();
-- is(i.markup._selectedContainer.node, node, "Right node is selected in the markup view");
-+ try {
-+ is(i.markup._selectedContainer.node.rawNode(), node, "Right node is selected in the markup view");
-+ } catch(ex) { console.error(ex); }
- }
-
- function testBreadcrumbs(node)
- {
- let b = getActiveInspector().breadcrumbs;
- let expectedText = b.prettyPrintNodeAsText(getNodeFront(node));
- let button = b.container.querySelector("button[checked=true]");
- ok(button, "A crumbs is checked=true");
- is(button.getAttribute("tooltiptext"), expectedText, "Crumb refers to the right node");
- }
-
- function _clickOnInspectMenuItem(node) {
- document.popupNode = node;
- var contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
- var contextMenu = new nsContextMenu(contentAreaContextMenu);
-- return contextMenu.inspectNode();
-+ var promise = devtools.require("sdk/core/promise");
-+ var deferred = promise.defer();
-+ contextMenu.inspectNode().then(() => {
-+ let i = getActiveInspector();
-+ i.once("inspector-updated", () => {
-+ deferred.resolve(undefined);
-+ });
-+ });
-+ return deferred.promise;
- }
-
- function runContextMenuTest()
- {
- salutation = doc.getElementById("salutation");
- _clickOnInspectMenuItem(salutation).then(testInitialNodeIsSelected);
- }
-
-diff --git a/browser/devtools/inspector/test/browser_inspector_menu.js b/browser/devtools/inspector/test/browser_inspector_menu.js
---- a/browser/devtools/inspector/test/browser_inspector_menu.js
-+++ b/browser/devtools/inspector/test/browser_inspector_menu.js
-@@ -48,31 +48,33 @@ function test() {
-
- checkElementMenuItems();
- });
- }
-
- function checkElementMenuItems() {
- info("Checking context menu entries for p tag");
- inspector.selection.setNode(doc.querySelector("p"));
-- let tag = getMarkupTagNodeContaining("p");
-+ inspector.once("inspector-updated", () => {
-+ let tag = getMarkupTagNodeContaining("p");
-
-- // Right-click p tag
-- contextMenuClick(tag);
-+ // Right-click p tag
-+ contextMenuClick(tag);
-
-- checkEnabled("node-menu-copyinner");
-- checkEnabled("node-menu-copyouter");
-- checkEnabled("node-menu-copyuniqueselector");
-- checkEnabled("node-menu-delete");
-+ checkEnabled("node-menu-copyinner");
-+ checkEnabled("node-menu-copyouter");
-+ checkEnabled("node-menu-copyuniqueselector");
-+ checkEnabled("node-menu-delete");
-
-- for (let name of ["hover", "active", "focus"]) {
-- checkEnabled("node-menu-pseudo-" + name);
-- }
-+ for (let name of ["hover", "active", "focus"]) {
-+ checkEnabled("node-menu-pseudo-" + name);
-+ }
-
-- testCopyInnerMenu();
-+ testCopyInnerMenu();
-+ });
- }
-
- function testCopyInnerMenu() {
- let copyInner = inspector.panelDoc.getElementById("node-menu-copyinner");
- ok(copyInner, "the popup menu has a copy inner html menu item");
-
- waitForClipboard("This is some example text",
- function() { copyInner.doCommand(); },
-diff --git a/browser/devtools/markupview/markup-view.js b/browser/devtools/markupview/markup-view.js
---- a/browser/devtools/markupview/markup-view.js
-+++ b/browser/devtools/markupview/markup-view.js
-@@ -10,16 +10,17 @@ const {Cc, Cu, Ci} = require("chrome");
- const PAGE_SIZE = 10;
-
- const PREVIEW_AREA = 700;
- const DEFAULT_MAX_CHILDREN = 100;
-
- let {UndoStack} = require("devtools/shared/undo");
- let EventEmitter = require("devtools/shared/event-emitter");
- let {editableField, InplaceEditor} = require("devtools/shared/inplace-editor");
-+let promise = require("sdk/core/promise");
-
- Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
- Cu.import("resource://gre/modules/devtools/Templater.jsm");
- Cu.import("resource://gre/modules/Services.jsm");
-
- /**
- * Vocabulary for the purposes of this file:
- *
-@@ -36,35 +37,37 @@ Cu.import("resource://gre/modules/Servic
- * @param Inspector aInspector
- * The inspector we're watching.
- * @param iframe aFrame
- * An iframe in which the caller has kindly loaded markup-view.xhtml.
- */
- function MarkupView(aInspector, aFrame, aControllerWindow)
- {
- this._inspector = aInspector;
-+ this.walker = this._inspector.walker;
- this._frame = aFrame;
- this.doc = this._frame.contentDocument;
- this._elt = this.doc.querySelector("#root");
-
- try {
- this.maxChildren = Services.prefs.getIntPref("devtools.markup.pagesize");
- } catch(ex) {
- this.maxChildren = DEFAULT_MAX_CHILDREN;
- }
-
- this.undo = new UndoStack();
- this.undo.installController(aControllerWindow);
-
- this._containers = new WeakMap();
-
-- this._observer = new this.doc.defaultView.MutationObserver(this._mutationObserver.bind(this));
-+ this._boundMutationObserver = this._mutationObserver.bind(this);
-+ this.walker.on("mutations", this._boundMutationObserver)
-
- this._boundOnNewSelection = this._onNewSelection.bind(this);
-- this._inspector.selection.on("new-node", this._boundOnNewSelection);
-+ this._inspector.selection.on("new-node-front", this._boundOnNewSelection);
- this._onNewSelection();
-
- this._boundKeyDown = this._onKeyDown.bind(this);
- this._frame.contentWindow.addEventListener("keydown", this._boundKeyDown, false);
-
- this._boundFocus = this._onFocus.bind(this);
- this._frame.addEventListener("focus", this._boundFocus, false);
-
-@@ -93,35 +96,41 @@ MarkupView.prototype = {
- return this._containers.get(aNode);
- },
-
- /**
- * Highlight the inspector selected node.
- */
- _onNewSelection: function MT__onNewSelection()
- {
-+ let done = this._inspector.updating("markup-view");
- if (this._inspector.selection.isNode()) {
-- this.showNode(this._inspector.selection.node, true);
-- this.markNodeAsSelected(this._inspector.selection.node);
-+ this.showNode(this._inspector.selection.nodeFront, true).then(() => {
-+ this.markNodeAsSelected(this._inspector.selection.nodeFront);
-+ done();
-+ });
- } else {
- this.unmarkSelectedNode();
-+ done();
- }
- },
-
- /**
- * Create a TreeWalker to find the next/previous
- * node for selection.
- */
- _selectionWalker: function MT__seletionWalker(aStart)
- {
- let walker = this.doc.createTreeWalker(
- aStart || this._elt,
- Ci.nsIDOMNodeFilter.SHOW_ELEMENT,
- function(aElement) {
-- if (aElement.container && aElement.container.visible) {
-+ if (aElement.container &&
-+ aElement.container.elt === aElement &&
-+ aElement.container.visible) {
- return Ci.nsIDOMNodeFilter.FILTER_ACCEPT;
- }
- return Ci.nsIDOMNodeFilter.FILTER_SKIP;
- }
- );
- walker.currentNode = this._selectedContainer.elt;
- return walker;
- },
-@@ -140,31 +149,32 @@ MarkupView.prototype = {
- }
-
- switch(aEvent.keyCode) {
- case Ci.nsIDOMKeyEvent.DOM_VK_DELETE:
- case Ci.nsIDOMKeyEvent.DOM_VK_BACK_SPACE:
- this.deleteNode(this._selectedContainer.node);
- break;
- case Ci.nsIDOMKeyEvent.DOM_VK_HOME:
-- this.navigate(this._containers.get(this._rootNode.firstChild));
-+ let rootContainer = this._containers.get(this._rootNode);
-+ this.navigate(rootContainer.children.firstChild.container);
- break;
- case Ci.nsIDOMKeyEvent.DOM_VK_LEFT:
- if (this._selectedContainer.expanded) {
- this.collapseNode(this._selectedContainer.node);
- } else {
- let parent = this._selectionWalker().parentNode();
- if (parent) {
- this.navigate(parent.container);
- }
- }
- break;
- case Ci.nsIDOMKeyEvent.DOM_VK_RIGHT:
- if (!this._selectedContainer.expanded) {
-- this.expandNode(this._selectedContainer.node);
-+ this._expandContainer(this._selectedContainer);
- } else {
- let next = this._selectionWalker().nextNode();
- if (next) {
- this.navigate(next.container);
- }
- }
- break;
- case Ci.nsIDOMKeyEvent.DOM_VK_UP:
-@@ -215,16 +225,20 @@ MarkupView.prototype = {
- },
-
- /**
- * Delete a node from the DOM.
- * This is an undoable action.
- */
- deleteNode: function MC__deleteNode(aNode)
- {
-+ aNode = aNode.rawNode();
-+ if (!aNode) {
-+ return;
-+ }
- let doc = nodeDocument(aNode);
- if (aNode === doc ||
- aNode === doc.documentElement ||
- aNode.nodeType == Ci.nsIDOMNode.DOCUMENT_TYPE_NODE) {
- return;
- }
-
- let parentNode = aNode.parentNode;
-@@ -264,136 +278,129 @@ MarkupView.prototype = {
- */
- navigate: function MT__navigate(aContainer, aIgnoreFocus)
- {
- if (!aContainer) {
- return;
- }
-
- let node = aContainer.node;
-- this.showNode(node, false);
--
-- this._inspector.selection.setNode(node, "treepanel");
-+ this.markNodeAsSelected(node);
-+ this._inspector.selection.setNodeFront(node, "treepanel");
- // This event won't be fired if the node is the same. But the highlighter
- // need to lock the node if it wasn't.
- this._inspector.selection.emit("new-node");
-+ this._inspector.selection.emit("new-node-front");
-
- if (!aIgnoreFocus) {
- aContainer.focus();
- }
- },
-
- /**
- * Make sure a node is included in the markup tool.
- *
- * @param DOMNode aNode
- * The node in the content document.
-- *
- * @returns MarkupContainer The MarkupContainer object for this element.
- */
-- importNode: function MT_importNode(aNode, aExpand)
-+ importNode: function MT_importNode(aNode)
- {
- if (!aNode) {
- return null;
- }
-
- if (this._containers.has(aNode)) {
- return this._containers.get(aNode);
- }
-
-- this._observer.observe(aNode, {
-- attributes: true,
-- childList: true,
-- characterData: true,
-- });
--
-- let walker = documentWalker(aNode);
-- let parent = walker.parentNode();
-- if (parent) {
-- var container = new MarkupContainer(this, aNode);
-- } else {
-+ if (aNode === this.walker.rootNode) {
- var container = new RootContainer(this, aNode);
- this._elt.appendChild(container.elt);
--
-- if (this._rootNode) {
-- this._rootNode.removeEventListener("load", this, true);
-- }
--
- this._rootNode = aNode;
-- aNode.addEventListener("load", this, true);
-+ } else {
-+ var container = new MarkupContainer(this, aNode);
- }
-
- this._containers.set(aNode, container);
-- // FIXME: set an expando to prevent the the wrapper from disappearing
-- // See bug 819131 for details.
-- aNode.__preserveHack = true;
-- container.expanded = aExpand;
-+ container.childrenDirty = true;
-
-- container.childrenDirty = true;
- this._updateChildren(container);
-
-- if (parent) {
-- this.importNode(parent, true);
-- }
- return container;
- },
-
-- handleEvent: function MT_handleEvent(aEvent) {
-- // Fake a childList mutation here.
-- this._mutationObserver([{target: aEvent.target, type: "childList"}]);
-- },
-
- /**
- * Mutation observer used for included nodes.
- */
- _mutationObserver: function MT__mutationObserver(aMutations)
- {
- for (let mutation of aMutations) {
-- let container = this._containers.get(mutation.target);
-+ let type = mutation.type;
-+ let target = mutation.target;
-+
-+ if (mutation.type === "documentUnload") {
-+ // Treat this as a childList change of the child (maybe the protocol
-+ // should do this).
-+ type = "childList"
-+ target = mutation.targetParent;
-+ if (!target) {
-+ continue;
-+ }
-+ }
-+
-+ let container = this._containers.get(target);
- if (!container) {
-- // Container might not exist if this came from a load event for an iframe
-+ // Container might not exist if this came from a load event for a node
- // we're not viewing.
- continue;
- }
-- if (mutation.type === "attributes" || mutation.type === "characterData") {
-+ if (type === "attributes" || type === "characterData") {
- container.update();
-- } else if (mutation.type === "childList") {
-+ } else if (type === "childList") {
- container.childrenDirty = true;
- this._updateChildren(container);
- }
- }
-- this._inspector.emit("markupmutation");
-+ this._waitForChildren().then(() => {
-+ this._inspector.emit("markupmutation");
-+ });
- },
-
-+
- /**
- * Make sure the given node's parents are expanded and the
- * node is scrolled on to screen.
- */
- showNode: function MT_showNode(aNode, centered)
- {
- let container = this.importNode(aNode);
-- this._updateChildren(container);
-- let walker = documentWalker(aNode);
-- let parent;
-- while (parent = walker.parentNode()) {
-- this._updateChildren(this.getContainer(parent));
-+ let parent = aNode;
-+ while ((parent = parent.parentNode())) {
-+ this.importNode(parent);
- this.expandNode(parent);
- }
-- LayoutHelpers.scrollIntoViewIfNeeded(this._containers.get(aNode).editor.elt, centered);
-+
-+ return this._waitForChildren().then(() => {
-+ return this._ensureVisible(aNode);
-+ }).then(() => {
-+ // Why is this not working?
-+ LayoutHelpers.scrollIntoViewIfNeeded(this._containers.get(aNode).editor.elt, centered);
-+ });
- },
-
- /**
- * Expand the container's children.
- */
- _expandContainer: function MT__expandContainer(aContainer)
- {
-- if (aContainer.hasChildren && !aContainer.expanded) {
-+ return this._updateChildren(aContainer, true).then(() => {
- aContainer.expanded = true;
-- this._updateChildren(aContainer);
-- }
-+ })
- },
-
- /**
- * Expand the node's children.
- */
- expandNode: function MT_expandNode(aNode)
- {
- let container = this._containers.get(aNode);
-@@ -402,45 +409,57 @@ MarkupView.prototype = {
-
- /**
- * Expand the entire tree beneath a container.
- *
- * @param aContainer The container to expand.
- */
- _expandAll: function MT_expandAll(aContainer)
- {
-- this._expandContainer(aContainer);
-- let child = aContainer.children.firstChild;
-- while (child) {
-- this._expandAll(child.container);
-- child = child.nextSibling;
-- }
-+ return this._expandContainer(aContainer).then(() => {
-+ let child = aContainer.children.firstChild;
-+ let promises = [];
-+ while (child) {
-+ promises.push(this._expandAll(child.container));
-+ child = child.nextSibling;
-+ }
-+ return promise.all(promises);
-+ }).then(null, console.error);
- },
-
- /**
- * Expand the entire tree beneath a node.
- *
- * @param aContainer The node to expand, or null
- * to start from the top.
- */
- expandAll: function MT_expandAll(aNode)
- {
- aNode = aNode || this._rootNode;
-- this._expandAll(this._containers.get(aNode));
-+ return this._expandAll(this._containers.get(aNode));
- },
-
- /**
- * Collapse the node's children.
- */
- collapseNode: function MT_collapseNode(aNode)
- {
- let container = this._containers.get(aNode);
- container.expanded = false;
- },
-
-+ setNodeExpanded: function(aNode, aExpanded)
-+ {
-+ if (aExpanded) {
-+ this.expandNode(aNode);
-+ } else {
-+ this.collapseNode(aNode);
-+ }
-+ },
-+
- /**
- * Mark the given node selected.
- */
- markNodeAsSelected: function MT_markNodeAsSelected(aNode)
- {
- let container = this._containers.get(aNode);
- if (this._selectedContainer === container) {
- return false;
-@@ -448,40 +467,37 @@ MarkupView.prototype = {
- if (this._selectedContainer) {
- this._selectedContainer.selected = false;
- }
- this._selectedContainer = container;
- if (aNode) {
- this._selectedContainer.selected = true;
- }
-
-- this._ensureSelectionVisible();
--
- return true;
- },
-
- /**
- * Make sure that every ancestor of the selection are updated
- * and included in the list of visible children.
- */
-- _ensureSelectionVisible: function MT_ensureSelectionVisible()
-+ _ensureVisible: function(node)
- {
-- let node = this._selectedContainer.node;
-- let walker = documentWalker(node);
- while (node) {
- let container = this._containers.get(node);
-- let parent = walker.parentNode();
-+ let parent = node.parentNode();
- if (!container.elt.parentNode) {
- let parentContainer = this._containers.get(parent);
- parentContainer.childrenDirty = true;
- this._updateChildren(parentContainer, node);
- }
-
- node = parent;
- }
-+ return this._waitForChildren();
- },
-
- /**
- * Unmark selected node (no node selected).
- */
- unmarkSelectedNode: function MT_unmarkSelectedNode()
- {
- if (this._selectedContainer) {
-@@ -496,190 +512,199 @@ MarkupView.prototype = {
- nodeChanged: function MT_nodeChanged(aNode)
- {
- if (aNode === this._inspector.selection) {
- this._inspector.change("markupview");
- }
- },
-
- /**
-+ * Check if the current selection is a descendent of the container.
-+ * if so, make sure it's among the visible set for the container,
-+ * and set the dirty flag if needed.
-+ * @returns The node that should be made visible, if any.
-+ */
-+ _checkSelectionVisible: function(aContainer) {
-+ let centered = null;
-+ let node = this._inspector.selection.nodeFront;
-+ while (node) {
-+ if (node.parentNode() === aContainer.node) {
-+ centered = node;
-+ break;
-+ }
-+ node = node.parentNode();
-+ }
-+
-+ return centered;
-+ },
-+
-+ /**
- * Make sure all children of the given container's node are
- * imported and attached to the container in the right order.
-- * @param aCentered If provided, this child will be included
-- * in the visible subset, and will be roughly centered
-- * in that list.
-+ *
-+ * Children need to be updated only in the following circumstances:
-+ * a) We just imported this node and have never seen its children.
-+ * container.childrenDirty will be set by importNode in this case.
-+ * b) We received a childList mutation on the node.
-+ * container.childrenDirty will be set in that case too.
-+ * c) We have changed the selection, and the path to that selection
-+ * wasn't loaded in a previous children request (because we only
-+ * grab a subset).
-+ * container.childrenDirty should be set in that case too!
-+ *
-+ * This method returns a promise that will be resolved when the children
-+ * are ready (which may be immediately).
- */
-- _updateChildren: function MT__updateChildren(aContainer, aCentered)
-+ _updateChildren: function(aContainer, aExpand)
- {
-- if (!aContainer.childrenDirty) {
-- return false;
-+ aContainer.hasChildren = aContainer.node.hasChildren;
-+
-+ if (!this._queuedChildUpdates) {
-+ this._queuedChildUpdates = new Map();
- }
-
-- // Get a tree walker pointing at the first child of the node.
-- let treeWalker = documentWalker(aContainer.node);
-- let child = treeWalker.firstChild();
-- aContainer.hasChildren = !!child;
--
-- if (!aContainer.expanded) {
-- return;
-+ if (this._queuedChildUpdates.has(aContainer)) {
-+ return this._queuedChildUpdates.get(aContainer);
- }
-
-- aContainer.childrenDirty = false;
--
-- let children = this._getVisibleChildren(aContainer, aCentered);
-- let fragment = this.doc.createDocumentFragment();
--
-- for (child of children.children) {
-- let container = this.importNode(child, false);
-- fragment.appendChild(container.elt);
-+ if (!aContainer.childrenDirty) {
-+ return promise.resolve(aContainer);
- }
-
-- while (aContainer.children.firstChild) {
-- aContainer.children.removeChild(aContainer.children.firstChild);
-+ if (!aContainer.hasChildren) {
-+ while (aContainer.children.firstChild) {
-+ aContainer.children.removeChild(aContainer.children.firstChild);
-+ }
-+ aContainer.childrenDirty = false;
-+ return promise.resolve(aContainer);
- }
-
-- if (!(children.hasFirst && children.hasLast)) {
-- let data = {
-- showing: this.strings.GetStringFromName("markupView.more.showing"),
-- showAll: this.strings.formatStringFromName(
-- "markupView.more.showAll",
-- [aContainer.node.children.length.toString()], 1),
-- allButtonClick: function() {
-- aContainer.maxChildren = -1;
-- aContainer.childrenDirty = true;
-- this._updateChildren(aContainer);
-- }.bind(this)
-- };
--
-- if (!children.hasFirst) {
-- let span = this.template("more-nodes", data);
-- fragment.insertBefore(span, fragment.firstChild);
-- }
-- if (!children.hasLast) {
-- let span = this.template("more-nodes", data);
-- fragment.appendChild(span);
-- }
-+ // If we're not expanded (or asked to update anyway), we're done for
-+ // now. Note that this will leave the childrenDirty flag set, so when
-+ // expanded we'll refresh the child list.
-+ if (!(aContainer.expanded || aExpand)) {
-+ return promise.resolve(aContainer);
- }
-
-- aContainer.children.appendChild(fragment);
-+ // We're going to issue a children request, make sure it includes the
-+ // centered node.
-+ let centered = this._checkSelectionVisible(aContainer);
-
-- return true;
-+ // Children aren't updated yet, but clear the childrenDirty flag anyway.
-+ // If the dirty flag is re-set while we're fetching we'll need to fetch
-+ // again.
-+ aContainer.childrenDirty = false;
-+ let updatePromise = this._getVisibleChildren(aContainer, centered).then(children => {
-+ this._queuedChildUpdates.delete(aContainer);
-+
-+ // If children are dirty, we got a change notification for this node
-+ // while the request was in progress, we need to do it again.
-+ if (aContainer.childrenDirty) {
-+ return this._updateChildren(aContainer, centered);
-+ }
-+
-+ let fragment = this.doc.createDocumentFragment();
-+
-+ for (let child of children.nodes) {
-+ let container = this.importNode(child);
-+ fragment.appendChild(container.elt);
-+ }
-+
-+ while (aContainer.children.firstChild) {
-+ aContainer.children.removeChild(aContainer.children.firstChild);
-+ }
-+
-+ if (!(children.hasFirst && children.hasLast)) {
-+ let data = {
-+ showing: this.strings.GetStringFromName("markupView.more.showing"),
-+ showAll: this.strings.formatStringFromName(
-+ "markupView.more.showAll",
-+ [aContainer.node.numChildren.toString()], 1),
-+ allButtonClick: () => {
-+ aContainer.maxChildren = -1;
-+ aContainer.childrenDirty = true;
-+ this._updateChildren(aContainer);
-+ }
-+ };
-+
-+ if (!children.hasFirst) {
-+ let span = this.template("more-nodes", data);
-+ fragment.insertBefore(span, fragment.firstChild);
-+ }
-+ if (!children.hasLast) {
-+ let span = this.template("more-nodes", data);
-+ fragment.appendChild(span);
-+ }
-+ }
-+
-+ aContainer.children.appendChild(fragment);
-+ return aContainer;
-+ }).then(null, console.error);
-+ this._queuedChildUpdates.set(aContainer, updatePromise);
-+ return updatePromise;
-+ },
-+
-+ _waitForChildren: function() {
-+ if (!this._queuedChildUpdates) {
-+ return promise.resolve(undefined);
-+ }
-+ return promise.all([updatePromise for (updatePromise of this._queuedChildUpdates.values())]);
- },
-
- /**
- * Return a list of the children to display for this container.
- */
- _getVisibleChildren: function MV__getVisibleChildren(aContainer, aCentered)
- {
- let maxChildren = aContainer.maxChildren || this.maxChildren;
- if (maxChildren == -1) {
-- maxChildren = Number.MAX_VALUE;
-- }
-- let firstChild = documentWalker(aContainer.node).firstChild();
-- let lastChild = documentWalker(aContainer.node).lastChild();
--
-- if (!firstChild) {
-- // No children, we're done.
-- return { hasFirst: true, hasLast: true, children: [] };
-+ maxChildren = undefined;
- }
-
-- // By default try to put the selected child in the middle of the list.
-- let start = aCentered || firstChild;
--
-- // Start by reading backward from the starting point....
-- let nodes = [];
-- let backwardWalker = documentWalker(start);
-- if (backwardWalker.previousSibling()) {
-- let backwardCount = Math.floor(maxChildren / 2);
-- let backwardNodes = this._readBackward(backwardWalker, backwardCount);
-- nodes = backwardNodes;
-- }
--
-- // Then read forward by any slack left in the max children...
-- let forwardWalker = documentWalker(start);
-- let forwardCount = maxChildren - nodes.length;
-- nodes = nodes.concat(this._readForward(forwardWalker, forwardCount));
--
-- // If there's any room left, it means we've run all the way to the end.
-- // In that case, there might still be more items at the front.
-- let remaining = maxChildren - nodes.length;
-- if (remaining > 0 && nodes[0] != firstChild) {
-- let firstNodes = this._readBackward(backwardWalker, remaining);
--
-- // Then put it all back together.
-- nodes = firstNodes.concat(nodes);
-- }
--
-- return {
-- hasFirst: nodes[0] == firstChild,
-- hasLast: nodes[nodes.length - 1] == lastChild,
-- children: nodes
-- };
-- },
--
-- _readForward: function MV__readForward(aWalker, aCount)
-- {
-- let ret = [];
-- let node = aWalker.currentNode;
-- do {
-- ret.push(node);
-- node = aWalker.nextSibling();
-- } while (node && --aCount);
-- return ret;
-- },
--
-- _readBackward: function MV__readBackward(aWalker, aCount)
-- {
-- let ret = [];
-- let node = aWalker.currentNode;
-- do {
-- ret.push(node);
-- node = aWalker.previousSibling();
-- } while(node && --aCount);
-- ret.reverse();
-- return ret;
-+ return this.walker.children(aContainer.node, {
-+ maxNodes: maxChildren,
-+ center: aCentered
-+ });
- },
-
- /**
- * Tear down the markup panel.
- */
- destroy: function MT_destroy()
- {
- this.undo.destroy();
- delete this.undo;
-
- this._frame.removeEventListener("focus", this._boundFocus, false);
- delete this._boundFocus;
-
-- this._frame.contentWindow.removeEventListener("scroll", this._boundUpdatePreview, true);
-- this._frame.contentWindow.removeEventListener("resize", this._boundResizePreview, true);
-- this._frame.contentWindow.removeEventListener("overflow", this._boundResizePreview, true);
-- this._frame.contentWindow.removeEventListener("underflow", this._boundResizePreview, true);
-- delete this._boundUpdatePreview;
-+ if (this._boundUpdatePreview) {
-+ this._frame.contentWindow.removeEventListener("scroll", this._boundUpdatePreview, true);
-+ delete this._boundUpdatePreview;
-+ }
-+
-+ if (this._boundResizePreview) {
-+ this._frame.contentWindow.removeEventListener("resize", this._boundResizePreview, true);
-+ this._frame.contentWindow.removeEventListener("overflow", this._boundResizePreview, true);
-+ this._frame.contentWindow.removeEventListener("underflow", this._boundResizePreview, true);
-+ delete this._boundResizePreview;
-+ }
-
- this._frame.contentWindow.removeEventListener("keydown", this._boundKeyDown, false);
- delete this._boundKeyDown;
-
-- this._inspector.selection.off("new-node", this._boundOnNewSelection);
-+ this._inspector.selection.off("new-node-front", this._boundOnNewSelection);
- delete this._boundOnNewSelection;
-
-+ this.walker.off("mutations", this._boundMutationObserver)
-+ delete this._boundMutationObserver;
-+
- delete this._elt;
-
- delete this._containers;
-- this._observer.disconnect();
-- delete this._observer;
--
-- if (this._rootNode) {
-- try {
-- this._rootNode.removeEventListener("load", this, true);
-- } catch(e) {
-- // this._rootNode might be a dead object.
-- }
-- delete this._rootNode;
-- }
- },
-
- /**
- * Initialize the preview panel.
- */
- _initPreview: function MT_initPreview()
- {
- if (!Services.prefs.getBoolPref("devtools.inspector.markupPreview")) {
-@@ -789,25 +814,22 @@ function MarkupContainer(aMarkupView, aN
-
- // The template will fill the following properties
- this.elt = null;
- this.expander = null;
- this.codeBox = null;
- this.children = null;
- this.markup.template("container", this);
- this.elt.container = this;
-+ this.children.container = this;
-
- this.expander.addEventListener("click", function() {
- this.markup.navigate(this);
-
-- if (this.expanded) {
-- this.markup.collapseNode(this.node);
-- } else {
-- this.markup.expandNode(this.node);
-- }
-+ this.markup.setNodeExpanded(this.node, !this.expanded);
- }.bind(this));
-
- this.codeBox.insertBefore(this.editor.elt, this.children);
-
- this.editor.elt.addEventListener("mousedown", function(evt) {
- this.markup.navigate(this);
- }.bind(this), false);
-
-@@ -820,20 +842,21 @@ function MarkupContainer(aMarkupView, aN
- }
-
- if (this.editor.closeElt) {
- this.editor.closeElt.addEventListener("mousedown", function(evt) {
- this.markup.navigate(this);
- }.bind(this), false);
- this.codeBox.appendChild(this.editor.closeElt);
- }
--
- }
-
- MarkupContainer.prototype = {
-+ toString: function() { return "[MarkupContainer for " + this.node + "]" },
-+
- /**
- * True if the current node has children. The MarkupView
- * will set this attribute for the MarkupContainer.
- */
- _hasChildren: false,
-
- get hasChildren() {
- return this._hasChildren;
-@@ -843,16 +866,20 @@ MarkupContainer.prototype = {
- this._hasChildren = aValue;
- if (aValue) {
- this.expander.style.visibility = "visible";
- } else {
- this.expander.style.visibility = "hidden";
- }
- },
-
-+ parentContainer: function() {
-+ return this.elt.parentNode ? this.elt.parentNode.container : null;
-+ },
-+
- /**
- * True if the node has been visually expanded in the tree.
- */
- get expanded() {
- return this.children.hasAttribute("expanded");
- },
-
- set expanded(aValue) {
-@@ -885,16 +912,17 @@ MarkupContainer.prototype = {
- _selected: false,
-
- get selected() {
- return this._selected;
- },
-
- set selected(aValue) {
- this._selected = aValue;
-+ this.editor.selected = aValue;
- if (this._selected) {
- this.editor.elt.classList.add("theme-selected");
- if (this.editor.closeElt) {
- this.editor.closeElt.classList.add("theme-selected");
- }
- } else {
- this.editor.elt.classList.remove("theme-selected");
- if (this.editor.closeElt) {
-@@ -928,18 +956,20 @@ MarkupContainer.prototype = {
-
- /**
- * Dummy container node used for the root document element.
- */
- function RootContainer(aMarkupView, aNode)
- {
- this.doc = aMarkupView.doc;
- this.elt = this.doc.createElement("ul");
-+ this.elt.container = this;
- this.children = this.elt;
- this.node = aNode;
-+ this.toString = function() { return "[root container]"}
- }
-
- /**
- * Creates an editor for simple nodes.
- */
- function GenericEditor(aContainer, aNode)
- {
- this.elt = aContainer.doc.createElement("span");
-@@ -969,46 +999,77 @@ function DoctypeEditor(aContainer, aNode
- *
- * @param MarkupContainer aContainer The container owning this editor.
- * @param DOMNode aNode The node being edited.
- * @param string aTemplate The template id to use to build the editor.
- */
- function TextEditor(aContainer, aNode, aTemplate)
- {
- this.node = aNode;
-+ this._selected = false;
-
- aContainer.markup.template(aTemplate, this);
-
-- editableField({
-- element: this.value,
-- stopOnReturn: true,
-- trigger: "dblclick",
-- multiline: true,
-- done: function TE_done(aVal, aCommit) {
-- if (!aCommit) {
-- return;
-- }
-- let oldValue = this.node.nodeValue;
-- aContainer.undo.do(function() {
-- this.node.nodeValue = aVal;
-- aContainer.markup.nodeChanged(this.node);
-- }.bind(this), function() {
-- this.node.nodeValue = oldValue;
-- aContainer.markup.nodeChanged(this.node);
-- }.bind(this));
-- }.bind(this)
-- });
-+ let rawNode = aNode.rawNode();
-+ if (rawNode) {
-+ editableField({
-+ element: this.value,
-+ stopOnReturn: true,
-+ trigger: "dblclick",
-+ multiline: true,
-+ done: function TE_done(aVal, aCommit) {
-+ if (!aCommit) {
-+ return;
-+ }
-+ let oldValue = rawNode.nodeValue;
-+ aContainer.undo.do(function() {
-+ rawNode.nodeValue = aVal;
-+ aContainer.markup.nodeChanged(this.node);
-+ }.bind(this), function() {
-+ rawNode.nodeValue = oldValue;
-+ aContainer.markup.nodeChanged(this.node);
-+ }.bind(this));
-+ }.bind(this)
-+ });
-+ }
-
- this.update();
- }
-
- TextEditor.prototype = {
-+ get selected() this._selected,
-+ set selected(aValue) {
-+ if (aValue === this._selected) {
-+ return;
-+ }
-+ this._selected = aValue;
-+ this.update();
-+ },
-+
- update: function TE_update()
- {
-- this.value.textContent = this.node.nodeValue;
-+ if (!this.selected || !this.node.incompleteValue) {
-+ let text = this.node.shortValue;
-+ // XXX: internationalize the elliding
-+ if (this.node.incompleteValue) {
-+ text += "…";
-+ }
-+ this.value.textContent = text;
-+ } else {
-+ let longstr = null;
-+ this.node.getNodeValue().then(ret => {
-+ longstr = ret;
-+ return longstr.string();
-+ }).then(str => {
-+ longstr.release().then(null, console.error);
-+ if (this.selected) {
-+ this.value.textContent = str;
-+ }
-+ }).then(null, console.error);
-+ }
- }
- };
-
- /**
- * Creates an editor for an Element node.
- *
- * @param MarkupContainer aContainer The container owning this editor.
- * @param Element aNode The node being edited.
-@@ -1030,53 +1091,57 @@ function ElementEditor(aContainer, aNode
- this.attrList = null;
- this.newAttr = null;
- this.summaryElt = null;
- this.closeElt = null;
-
- // Create the main editor
- this.template("element", this);
-
-- if (this.node.firstChild || this.node.textContent.length > 0) {
-+ if (this.node.hasChildren) {
- // Create the summary placeholder
- this.template("elementContentSummary", this);
- }
-
- // Create the closing tag
- this.template("elementClose", this);
-
-+ this.rawNode = aNode.rawNode();
-+
- // Make the tag name editable (unless this is a document element)
-- if (aNode != aNode.ownerDocument.documentElement) {
-- this.tag.setAttribute("tabindex", "0");
-+ if (this.rawNode) {
-+ if (!aNode.isDocumentElement) {
-+ this.tag.setAttribute("tabindex", "0");
-+ editableField({
-+ element: this.tag,
-+ trigger: "dblclick",
-+ stopOnReturn: true,
-+ done: this.onTagEdit.bind(this),
-+ });
-+ }
-+
-+ // Make the new attribute space editable.
- editableField({
-- element: this.tag,
-+ element: this.newAttr,
- trigger: "dblclick",
- stopOnReturn: true,
-- done: this.onTagEdit.bind(this),
-+ done: function EE_onNew(aVal, aCommit) {
-+ if (!aCommit) {
-+ return;
-+ }
-+
-+ try {
-+ this._applyAttributes(aVal);
-+ } catch (x) {
-+ return;
-+ }
-+ }.bind(this)
- });
- }
-
-- // Make the new attribute space editable.
-- editableField({
-- element: this.newAttr,
-- trigger: "dblclick",
-- stopOnReturn: true,
-- done: function EE_onNew(aVal, aCommit) {
-- if (!aCommit) {
-- return;
-- }
--
-- try {
-- this._applyAttributes(aVal);
-- } catch (x) {
-- return;
-- }
-- }.bind(this)
-- });
--
- let tagName = this.node.nodeName.toLowerCase();
- this.tag.textContent = tagName;
- this.closeTag.textContent = tagName;
-
- this.update();
- }
-
- ElementEditor.prototype = {
-@@ -1129,56 +1194,59 @@ ElementEditor.prototype = {
- if (aAttr.name == "id") {
- before = this.attrList.firstChild;
- } else if (aAttr.name == "class") {
- let idNode = this.attrs["id"];
- before = idNode ? idNode.nextSibling : this.attrList.firstChild;
- }
- this.attrList.insertBefore(attr, before);
-
-- // Make the attribute editable.
-- editableField({
-- element: inner,
-- trigger: "dblclick",
-- stopOnReturn: true,
-- selectAll: false,
-- start: function EE_editAttribute_start(aEditor, aEvent) {
-- // If the editing was started inside the name or value areas,
-- // select accordingly.
-- if (aEvent && aEvent.target === name) {
-- aEditor.input.setSelectionRange(0, name.textContent.length);
-- } else if (aEvent && aEvent.target === val) {
-- let length = val.textContent.length;
-- let editorLength = aEditor.input.value.length;
-- let start = editorLength - (length + 1);
-- aEditor.input.setSelectionRange(start, start + length);
-- } else {
-- aEditor.input.select();
-- }
-- },
-- done: function EE_editAttribute_done(aVal, aCommit) {
-- if (!aCommit) {
-- return;
-- }
-+ if (this.rawNode) {
-
-- this.undo.startBatch();
-+ // Make the attribute editable.
-+ editableField({
-+ element: inner,
-+ trigger: "dblclick",
-+ stopOnReturn: true,
-+ selectAll: false,
-+ start: function EE_editAttribute_start(aEditor, aEvent) {
-+ // If the editing was started inside the name or value areas,
-+ // select accordingly.
-+ if (aEvent && aEvent.target === name) {
-+ aEditor.input.setSelectionRange(0, name.textContent.length);
-+ } else if (aEvent && aEvent.target === val) {
-+ let length = val.textContent.length;
-+ let editorLength = aEditor.input.value.length;
-+ let start = editorLength - (length + 1);
-+ aEditor.input.setSelectionRange(start, start + length);
-+ } else {
-+ aEditor.input.select();
-+ }
-+ },
-+ done: function EE_editAttribute_done(aVal, aCommit) {
-+ if (!aCommit) {
-+ return;
-+ }
-
-- // Remove the attribute stored in this editor and re-add any attributes
-- // parsed out of the input element. Restore original attribute if
-- // parsing fails.
-- this._removeAttribute(this.node, aAttr.name);
-- try {
-- this._applyAttributes(aVal, attr);
-- this.undo.endBatch();
-- } catch (e) {
-- this.undo.endBatch();
-- this.undo.undo();
-- }
-- }.bind(this)
-- });
-+ this.undo.startBatch();
-+
-+ // Remove the attribute stored in this editor and re-add any attributes
-+ // parsed out of the input element. Restore original attribute if
-+ // parsing fails.
-+ this._removeAttribute(this.rawNode, aAttr.name);
-+ try {
-+ this._applyAttributes(aVal, attr);
-+ this.undo.endBatch();
-+ } catch (e) {
-+ this.undo.endBatch();
-+ this.undo.undo();
-+ }
-+ }.bind(this)
-+ });
-+ }
-
- this.attrs[aAttr.name] = attr;
- }
-
- name.textContent = aAttr.name;
- val.textContent = aAttr.value;
-
- return attr;
-@@ -1201,17 +1269,17 @@ ElementEditor.prototype = {
-
- for (let attr of attrs) {
- let attribute = {
- name: attr.name,
- value: attr.value
- };
- // Create an attribute editor next to the current attribute if needed.
- this._createAttribute(attribute, aAttrNode ? aAttrNode.nextSibling : null);
-- this._setAttribute(this.node, attr.name, attr.value);
-+ this._setAttribute(this.rawNode, attr.name, attr.value);
- }
-
- this.undo.endBatch();
- },
-
- /**
- * Helper function for _setAttribute and _removeAttribute,
- * returns a function that puts an attribute back the way it was.
-@@ -1258,187 +1326,93 @@ ElementEditor.prototype = {
- * Handler for the new attribute editor.
- */
- _onNewAttribute: function EE_onNewAttribute(aValue, aCommit)
- {
- if (!aValue || !aCommit) {
- return;
- }
-
-- this._setAttribute(this.node, aValue, "");
-+ this._setAttribute(this.rawNode, aValue, "");
- let attr = this._createAttribute({ name: aValue, value: ""});
- attr.style.removeAttribute("display");
- attr.querySelector("attrvalue").click();
- },
-
-
- /**
- * Called when the tag name editor has is done editing.
- */
- onTagEdit: function EE_onTagEdit(aVal, aCommit) {
-- if (!aCommit || aVal == this.node.tagName) {
-+ if (!aCommit || aVal == this.rawNode.tagName) {
- return;
- }
-
- // Create a new element with the same attributes as the
- // current element and prepare to replace the current node
- // with it.
- try {
-- var newElt = nodeDocument(this.node).createElement(aVal);
-+ var newElt = nodeDocument(this.rawNode).createElement(aVal);
- } catch(x) {
- // Failed to create a new element with that tag name, ignore
- // the change.
- return;
- }
-
-- let attrs = this.node.attributes;
-+ let attrs = this.rawNode.attributes;
-
- for (let i = 0 ; i < attrs.length; i++) {
- newElt.setAttribute(attrs[i].name, attrs[i].value);
- }
-+ let newFront = this.markup.walker.frontForRawNode(newElt);
-+ let newContainer = this.markup.importNode(newFront);
-
-- function swapNodes(aOld, aNew) {
-- while (aOld.firstChild) {
-- aNew.appendChild(aOld.firstChild);
-+ // Retain the two nodes we care about here so we can undo.
-+ let walker = this.markup.walker;
-+ promise.all([
-+ walker.retainNode(newFront), walker.retainNode(this.node)
-+ ]).then(() => {
-+ function swapNodes(aOld, aNew) {
-+ aOld.parentNode.insertBefore(aNew, aOld);
-+ while (aOld.firstChild) {
-+ aNew.appendChild(aOld.firstChild);
-+ }
-+ aOld.parentNode.removeChild(aOld);
- }
-- aOld.parentNode.insertBefore(aNew, aOld);
-- aOld.parentNode.removeChild(aOld);
-- }
-
-- let markup = this.container.markup;
--
-- // Queue an action to swap out the element.
-- this.undo.do(function() {
-- swapNodes(this.node, newElt);
--
-- // Make sure the new node is imported and is expanded/selected
-- // the same as the current node.
-- let newContainer = markup.importNode(newElt, this.container.expanded);
-- newContainer.expanded = this.container.expanded;
-- if (this.container.selected) {
-- markup.navigate(newContainer);
-- }
-- }.bind(this), function() {
-- swapNodes(newElt, this.node);
--
-- let newContainer = markup._containers.get(newElt);
-- this.container.expanded = newContainer.expanded;
-- if (newContainer.selected) {
-- markup.navigate(this.container);
-- }
-- }.bind(this));
-- },
-+ this.undo.do(() => {
-+ swapNodes(this.rawNode, newElt);
-+ this.markup.setNodeExpanded(newFront, this.container.expanded);
-+ if (this.container.selected) {
-+ this.markup.navigate(newContainer);
-+ }
-+ }, () => {
-+ swapNodes(newElt, this.rawNode);
-+ this.markup.setNodeExpanded(this.node, newContainer.expanded);
-+ if (newContainer.selected) {
-+ this.markup.navigate(this.container);
-+ }
-+ });
-+ }).then(null, console.error);
-+ }
- }
-
-
-
- RootContainer.prototype = {
- hasChildren: true,
- expanded: true,
- update: function RC_update() {}
- };
-
--function documentWalker(node) {
-- return new DocumentWalker(node, Ci.nsIDOMNodeFilter.SHOW_ALL, whitespaceTextFilter);
--}
--
- function nodeDocument(node) {
- return node.ownerDocument || (node.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE ? node : null);
- }
-
- /**
-- * Similar to a TreeWalker, except will dig in to iframes and it doesn't
-- * implement the good methods like previousNode and nextNode.
-- *
-- * See TreeWalker documentation for explanations of the methods.
-- */
--function DocumentWalker(aNode, aShow, aFilter)
--{
-- let doc = nodeDocument(aNode);
-- this.walker = doc.createTreeWalker(nodeDocument(aNode), aShow, aFilter);
-- this.walker.currentNode = aNode;
-- this.filter = aFilter;
--}
--
--DocumentWalker.prototype = {
-- get node() this.walker.node,
-- get whatToShow() this.walker.whatToShow,
-- get expandEntityReferences() this.walker.expandEntityReferences,
-- get currentNode() this.walker.currentNode,
-- set currentNode(aVal) this.walker.currentNode = aVal,
--
-- /**
-- * Called when the new node is in a different document than
-- * the current node, creates a new treewalker for the document we've
-- * run in to.
-- */
-- _reparentWalker: function DW_reparentWalker(aNewNode) {
-- if (!aNewNode) {
-- return null;
-- }
-- let doc = nodeDocument(aNewNode);
-- let walker = doc.createTreeWalker(doc,
-- this.whatToShow, this.filter, this.expandEntityReferences);
-- walker.currentNode = aNewNode;
-- this.walker = walker;
-- return aNewNode;
-- },
--
-- parentNode: function DW_parentNode()
-- {
-- let currentNode = this.walker.currentNode;
-- let parentNode = this.walker.parentNode();
--
-- if (!parentNode) {
-- if (currentNode && currentNode.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE
-- && currentNode.defaultView) {
-- let embeddingFrame = currentNode.defaultView.frameElement;
-- if (embeddingFrame) {
-- return this._reparentWalker(embeddingFrame);
-- }
-- }
-- return null;
-- }
--
-- return parentNode;
-- },
--
-- firstChild: function DW_firstChild()
-- {
-- let node = this.walker.currentNode;
-- if (!node)
-- return;
-- if (node.contentDocument) {
-- return this._reparentWalker(node.contentDocument);
-- } else if (node.getSVGDocument) {
-- return this._reparentWalker(node.getSVGDocument());
-- }
-- return this.walker.firstChild();
-- },
--
-- lastChild: function DW_lastChild()
-- {
-- let node = this.walker.currentNode;
-- if (!node)
-- return;
-- if (node.contentDocument) {
-- return this._reparentWalker(node.contentDocument);
-- } else if (node.getSVGDocument) {
-- return this._reparentWalker(node.getSVGDocument());
-- }
-- return this.walker.lastChild();
-- },
--
-- previousSibling: function DW_previousSibling() this.walker.previousSibling(),
-- nextSibling: function DW_nextSibling() this.walker.nextSibling(),
--
-- // XXX bug 785143: not doing previousNode or nextNode, which would sure be useful.
--};
--
--/**
- * Properly escape attribute values.
- *
- * @param {String} attr
- * The attributes for which the values are to be escaped.
- * @return {Array}
- * An array of attribute names and their escaped values.
- */
- function escapeAttributeValues(attr) {
-diff --git a/browser/devtools/markupview/test/browser_inspector_markup_edit.js b/browser/devtools/markupview/test/browser_inspector_markup_edit.js
---- a/browser/devtools/markupview/test/browser_inspector_markup_edit.js
-+++ b/browser/devtools/markupview/test/browser_inspector_markup_edit.js
-@@ -73,42 +73,41 @@ function test() {
- before: function() {
- assertAttributes(doc.querySelector("#node1"), {
- id: "node1",
- class: "node1"
- });
- },
- execute: function(after) {
- inspector.once("markupmutation", after);
-- let editor = markup.getContainer(doc.querySelector("#node1")).editor;
-+ let editor = getContainerForRawNode(markup, doc.querySelector("#node1")).editor;
- let attr = editor.attrs["class"].querySelector(".editable");
- editField(attr, 'class="changednode1"');
- },
- after: function() {
- assertAttributes(doc.querySelector("#node1"), {
- id: "node1",
- class: "changednode1"
- });
- }
- },
--
- {
- desc: 'Try changing an attribute to a quote (") - this should result ' +
- 'in it being set to an empty string',
- before: function() {
- assertAttributes(doc.querySelector("#node22"), {
- id: "node22",
- class: "unchanged"
- });
- },
- execute: function(after) {
-- let editor = markup.getContainer(doc.querySelector("#node22")).editor;
-+ let editor = getContainerForRawNode(markup, doc.querySelector("#node22")).editor;
- let attr = editor.attrs["class"].querySelector(".editable");
- editField(attr, 'class="""');
-- executeSoon(after);
-+ inspector.once("markupmutation", after);
- },
- after: function() {
- assertAttributes(doc.querySelector("#node22"), {
- id: "node22",
- class: ""
- });
- }
- },
-@@ -118,17 +117,17 @@ function test() {
- before: function() {
- assertAttributes(doc.querySelector("#node4"), {
- id: "node4",
- class: "node4"
- });
- },
- execute: function(after) {
- inspector.once("markupmutation", after);
-- let editor = markup.getContainer(doc.querySelector("#node4")).editor;
-+ let editor = getContainerForRawNode(markup, doc.querySelector("#node4")).editor;
- let attr = editor.attrs["class"].querySelector(".editable");
- editField(attr, '');
- },
- after: function() {
- assertAttributes(doc.querySelector("#node4"), {
- id: "node4",
- });
- }
-@@ -138,17 +137,17 @@ function test() {
- desc: "Add an attribute by clicking the empty space after a node",
- before: function() {
- assertAttributes(doc.querySelector("#node14"), {
- id: "node14",
- });
- },
- execute: function(after) {
- inspector.once("markupmutation", after);
-- let editor = markup.getContainer(doc.querySelector("#node14")).editor;
-+ let editor = getContainerForRawNode(markup, doc.querySelector("#node14")).editor;
- let attr = editor.newAttr;
- editField(attr, 'class="newclass" style="color:green"');
- },
- after: function() {
- assertAttributes(doc.querySelector("#node14"), {
- id: "node14",
- class: "newclass",
- style: "color:green"
-@@ -161,20 +160,20 @@ function test() {
- 'clicking the empty space after a node - this should result ' +
- 'in it being set to an empty string',
- before: function() {
- assertAttributes(doc.querySelector("#node23"), {
- id: "node23",
- });
- },
- execute: function(after) {
-- let editor = markup.getContainer(doc.querySelector("#node23")).editor;
-+ let editor = getContainerForRawNode(markup, doc.querySelector("#node23")).editor;
- let attr = editor.newAttr;
- editField(attr, 'class="newclass" style="""');
-- executeSoon(after);
-+ inspector.once("markupmutation", after);
- },
- after: function() {
- assertAttributes(doc.querySelector("#node23"), {
- id: "node23",
- class: "newclass",
- style: ""
- });
- }
-@@ -183,20 +182,20 @@ function test() {
- {
- desc: "Try add attributes by adding to an existing attribute's entry",
- before: function() {
- assertAttributes(doc.querySelector("#node24"), {
- id: "node24",
- });
- },
- execute: function(after) {
-- let editor = markup.getContainer(doc.querySelector("#node24")).editor;
-+ let editor = getContainerForRawNode(markup, doc.querySelector("#node24")).editor;
- let attr = editor.attrs["id"].querySelector(".editable");
- editField(attr, attr.textContent + ' class="""');
-- executeSoon(after);
-+ inspector.once("markupmutation", after);
- },
- after: function() {
- assertAttributes(doc.querySelector("#node24"), {
- id: "node24",
- class: ""
- });
- }
- },
-@@ -205,17 +204,17 @@ function test() {
- desc: "Edit text",
- before: function() {
- let node = doc.querySelector('.node6').firstChild;
- is(node.nodeValue, "line6", "Text should be unchanged");
- },
- execute: function(after) {
- inspector.once("markupmutation", after);
- let node = doc.querySelector('.node6').firstChild;
-- let editor = markup.getContainer(node).editor;
-+ let editor = getContainerForRawNode(markup, node).editor;
- let field = editor.elt.querySelector("pre");
- editField(field, "New text");
- },
- after: function() {
- let node = doc.querySelector('.node6').firstChild;
- is(node.nodeValue, "New text", "Text should be changed.");
- },
- },
-@@ -224,17 +223,17 @@ function test() {
- desc: "Add an attribute value containing < > ü \" & '",
- before: function() {
- assertAttributes(doc.querySelector("#node25"), {
- id: "node25",
- });
- },
- execute: function(after) {
- inspector.once("markupmutation", after);
-- let editor = markup.getContainer(doc.querySelector("#node25")).editor;
-+ let editor = getContainerForRawNode(markup, doc.querySelector("#node25")).editor;
- let attr = editor.newAttr;
- editField(attr, 'src="somefile.html?param1=<a>¶m2=ü"bl\'ah"');
- },
- after: function() {
- assertAttributes(doc.querySelector("#node25"), {
- id: "node25",
- src: "somefile.html?param1=<a>¶m2=ü"bl'ah"
- });
-@@ -255,122 +254,120 @@ function test() {
- var target = TargetFactory.forTab(gBrowser.selectedTab);
- gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
- inspector = toolbox.getCurrentPanel();
- startTests();
- });
- }
-
- function startTests() {
-- let startNode = doc.documentElement.cloneNode();
- markup = inspector.markup;
-- markup.expandAll();
-+ markup.expandAll().then(() => {
-
-- let cursor = 0;
-+ let cursor = 0;
-
-- function nextEditTest() {
-- executeSoon(function() {
-- if (cursor >= edits.length) {
-- addAttributes();
-- } else {
-- let step = edits[cursor++];
-- info("START " + step.desc);
-- if (step.setup) {
-- step.setup();
-+ function nextEditTest() {
-+ executeSoon(function() {
-+ if (cursor >= edits.length) {
-+ addAttributes();
-+ } else {
-+ let step = edits[cursor++];
-+ info("START " + step.desc);
-+ if (step.setup) {
-+ step.setup();
-+ }
-+ step.before();
-+ info("before execute");
-+ step.execute(function() {
-+ info("after execute");
-+ step.after();
-+ ok(markup.undo.canUndo(), "Should be able to undo.");
-+ markup.undo.undo();
-+ inspector.once("markupmutation", () => {
-+ step.before();
-+ ok(markup.undo.canRedo(), "Should be able to redo.");
-+ markup.undo.redo();
-+ inspector.once("markupmutation", () => {
-+ step.after();
-+ info("END " + step.desc);
-+ nextEditTest();
-+ });
-+ });
-+ });
- }
-- step.before();
-- info("before execute");
-- step.execute(function() {
-- info("after execute");
-- step.after();
-- ok(markup.undo.canUndo(), "Should be able to undo.");
-- markup.undo.undo();
-- step.before();
-- ok(markup.undo.canRedo(), "Should be able to redo.");
-- markup.undo.redo();
-- step.after();
-- info("END " + step.desc);
-- nextEditTest();
-- });
-- }
-- });
-- }
-- nextEditTest();
-+ });
-+ }
-+ nextEditTest();
-+ });
- }
-
- function addAttributes() {
- let test = {
- desc: "Add attributes by adding to an existing attribute's entry",
- setup: function() {
- inspector.selection.setNode(doc.querySelector("#node18"));
- },
- before: function() {
- assertAttributes(doc.querySelector("#node18"), {
- id: "node18",
- });
-
-- /**
-- * XXX: disabled until the remote markup view is enabled
-- * is(inspector.highlighter.nodeInfo.classesBox.textContent, "",
-- * "No classes in the infobar before edit.");
-- */
-+ is(inspector.highlighter.nodeInfo.classesBox.textContent, "",
-+ "No classes in the infobar before edit.");
- },
- execute: function(after) {
- inspector.once("markupmutation", function() {
- // needed because we need to make sure the infobar is updated
- // not just the markupview (which happens in this event loop)
- executeSoon(after);
- });
-- let editor = markup.getContainer(doc.querySelector("#node18")).editor;
-+ let editor = getContainerForRawNode(markup, doc.querySelector("#node18")).editor;
- let attr = editor.attrs["id"].querySelector(".editable");
- editField(attr, attr.textContent + ' class="newclass" style="color:green"');
- },
- after: function() {
- assertAttributes(doc.querySelector("#node18"), {
- id: "node18",
- class: "newclass",
- style: "color:green"
- });
-
-- /**
-- * XXX: disabled until the remote markup view is enabled
-- *is(inspector.highlighter.nodeInfo.classesBox.textContent, ".newclass",
-- * "Correct classes in the infobar after edit.");
-- */
-+ is(inspector.highlighter.nodeInfo.classesBox.textContent, ".newclass",
-+ "Correct classes in the infobar after edit.");
- }
- };
- testAsyncSetup(test, editTagName);
- }
-
- function editTagName() {
- let test = {
- desc: "Edit the tag name",
- setup: function() {
- inspector.selection.setNode(doc.querySelector("#retag-me"));
- },
- before: function() {
- let node = doc.querySelector("#retag-me");
-- let container = markup.getContainer(node);
-+ let container = getContainerForRawNode(markup, node);
-
- is(node.tagName, "DIV", "retag-me should be a div.");
- ok(container.selected, "retag-me should be selected.");
- ok(container.expanded, "retag-me should be expanded.");
- is(doc.querySelector("#retag-me-2").parentNode, node,
- "retag-me-2 should be a child of the old element.");
- },
- execute: function(after) {
- inspector.once("markupmutation", after);
- let node = doc.querySelector("#retag-me");
-- let editor = markup.getContainer(node).editor;
-+ let editor = getContainerForRawNode(markup, node).editor;
- let field = editor.tag;
- editField(field, "p");
- },
- after: function() {
- let node = doc.querySelector("#retag-me");
-- let container = markup.getContainer(node);
-+ let container = getContainerForRawNode(markup, node);
- is(node.tagName, "P", "retag-me should be a p.");
- ok(container.selected, "retag-me should be selected.");
- ok(container.expanded, "retag-me should be expanded.");
- is(doc.querySelector("#retag-me-2").parentNode, node,
- "retag-me-2 should be a child of the new element.");
- }
- };
- testAsyncSetup(test, removeElementWithDelete);
-@@ -394,49 +391,51 @@ function test() {
- };
- testAsyncExecute(test, finishUp);
- }
-
- function testAsyncExecute(test, callback) {
- info("START " + test.desc);
-
- test.before();
-- inspector.selection.once("new-node", function BIMET_testAsyncExecNewNode() {
-+ inspector.once("inspector-updated", function BIMET_testAsyncExecNewNode() {
- test.executeCont();
-- test.after();
-- undoRedo(test, callback);
-+ inspector.once("markupmutation", () => {
-+ test.after();
-+ undoRedo(test, callback);
-+ });
- });
- executeSoon(function BIMET_setNode1() {
- test.execute();
- });
- }
-
- function testAsyncSetup(test, callback) {
- info("START " + test.desc);
-
-- inspector.selection.once("new-node", function BIMET_testAsyncSetupNewNode() {
-+ inspector.once("inspector-updated", function BIMET_testAsyncSetupNewNode() {
- test.before();
- test.execute(function() {
- test.after();
- undoRedo(test, callback);
- });
- });
- executeSoon(function BIMET_setNode2() {
- test.setup();
- });
- }
-
- function undoRedo(test, callback) {
- ok(markup.undo.canUndo(), "Should be able to undo.");
- markup.undo.undo();
-- executeSoon(function() {
-+ inspector.once("markupmutation", () => {
- test.before();
- ok(markup.undo.canRedo(), "Should be able to redo.");
- markup.undo.redo();
-- executeSoon(function() {
-+ inspector.once("markupmutation", () => {
- test.after();
- info("END " + test.desc);
- callback();
- });
- });
- }
-
- function finishUp() {
-diff --git a/browser/devtools/markupview/test/browser_inspector_markup_mutation.js b/browser/devtools/markupview/test/browser_inspector_markup_mutation.js
---- a/browser/devtools/markupview/test/browser_inspector_markup_mutation.js
-+++ b/browser/devtools/markupview/test/browser_inspector_markup_mutation.js
-@@ -5,16 +5,20 @@ http://creativecommons.org/publicdomain/
- * Tests that various mutations to the dom update the markup tool correctly.
- * The test for comparing the markup tool to the real dom is a bit weird:
- * - Select the text in the markup tool
- * - Parse that as innerHTML in a document we've created for the purpose.
- * - Remove extraneous whitespace in that tree
- * - Compare it to the real dom with isEqualNode.
- */
-
-+function fail(err) {
-+ ok(false, err)
-+}
-+
- function test() {
- waitForExplicitFinish();
-
- // Will hold the doc we're viewing
- let contentTab;
- let doc;
-
- // Holds the MarkupTool object we're testing.
-@@ -40,20 +44,23 @@ function test() {
- node.parentNode.removeChild(node);
- }
- }
- }
-
- // Verify that the markup in the tool is the same as the markup in the document.
- function checkMarkup()
- {
-- markup.expandAll();
-+ return markup.expandAll().then(checkMarkup2);
-+ }
-
-+ function checkMarkup2()
-+ {
- let contentNode = doc.querySelector("body");
-- let panelNode = markup._containers.get(contentNode).elt;
-+ let panelNode = getContainerForRawNode(markup, contentNode).elt;
- let parseNode = parseDoc.querySelector("body");
-
- // Grab the text from the markup panel...
- let sel = panelNode.ownerDocument.defaultView.getSelection();
- sel.selectAllChildren(panelNode);
-
- // Parse it
- parseNode.outerHTML = sel;
-@@ -149,30 +156,32 @@ function test() {
- gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
- inspector = toolbox.getCurrentPanel();
- startTests();
- });
- }
-
- function startTests() {
- markup = inspector.markup;
-- checkMarkup();
-- nextStep(0);
-+ checkMarkup().then(() => {
-+ nextStep(0);
-+ }).then(null, fail);
- }
-
- function nextStep(cursor) {
- if (cursor >= mutations.length) {
- finishUp();
- return;
- }
- mutations[cursor]();
- inspector.once("markupmutation", function() {
- executeSoon(function() {
-- checkMarkup();
-- nextStep(cursor + 1);
-+ checkMarkup().then(() => {
-+ nextStep(cursor + 1);
-+ }).then(null, fail);
- });
- });
- }
-
- function finishUp() {
- doc = inspector = null;
- gBrowser.removeTab(contentTab);
- gBrowser.removeTab(parseTab);
-diff --git a/browser/devtools/markupview/test/browser_inspector_markup_navigation.js b/browser/devtools/markupview/test/browser_inspector_markup_navigation.js
---- a/browser/devtools/markupview/test/browser_inspector_markup_navigation.js
-+++ b/browser/devtools/markupview/test/browser_inspector_markup_navigation.js
-@@ -79,17 +79,17 @@ function test() {
- }, true);
-
- content.location = "http://mochi.test:8888/browser/browser/devtools/markupview/test/browser_inspector_markup_navigation.html";
-
- function setupTest() {
- var target = TargetFactory.forTab(gBrowser.selectedTab);
- gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
- inspector = toolbox.getCurrentPanel();
-- startNavigation();
-+ inspector.once("inspector-updated", startNavigation);
- });
- }
-
- function startNavigation() {
- nextStep(0);
- }
-
- function nextStep(cursor) {
-@@ -121,31 +121,31 @@ function test() {
- case "pagedown":
- EventUtils.synthesizeKey("VK_PAGE_DOWN", {});
- break;
- case "home":
- EventUtils.synthesizeKey("VK_HOME", {});
- break;
- }
-
-- executeSoon(function BIMNT_newNode() {
-+ inspector.markup._waitForChildren().then(() => executeSoon(function BIMNT_newNode() {
- let node = inspector.selection.node;
-
- if (className == "*comment*") {
- is(node.nodeType, Node.COMMENT_NODE, "[" + cursor + "] should be a comment after moving " + key);
- } else if (className == "*text*") {
- is(node.nodeType, Node.TEXT_NODE, "[" + cursor + "] should be text after moving " + key);
- } else if (className == "*doctype*") {
- is(node.nodeType, Node.DOCUMENT_TYPE_NODE, "[" + cursor + "] should be doctype after moving " + key);
- } else {
- is(node.className, className, "[" + cursor + "] right node selected: " + className + " after moving " + key);
- }
-
- nextStep(cursor + 1);
-- });
-+ }));
- }
-
- function finishUp() {
- doc = inspector = null;
- gBrowser.removeCurrentTab();
- finish();
- }
- }
-diff --git a/browser/devtools/markupview/test/browser_inspector_markup_subset.js b/browser/devtools/markupview/test/browser_inspector_markup_subset.js
---- a/browser/devtools/markupview/test/browser_inspector_markup_subset.js
-+++ b/browser/devtools/markupview/test/browser_inspector_markup_subset.js
-@@ -20,36 +20,44 @@ function test() {
-
- let inspector;
-
- // Holds the MarkupTool object we're testing.
- let markup;
-
- function assertChildren(expected)
- {
-- let container = markup.getContainer(doc.querySelector("body"));
-+ let container = getContainerForRawNode(markup, doc.querySelector("body"));
- let found = [];
- for (let child of container.children.children) {
- if (child.classList.contains("more-nodes")) {
- found += "*more*";
- } else {
- found += child.container.node.getAttribute("id");
- }
- }
-- is(expected, found, "Got the expected children.");
-+ is(found, expected, "Got the expected children.");
- }
-
- function forceReload()
- {
-- let container = markup.getContainer(doc.querySelector("body"));
-+ let container = getContainerForRawNode(markup, doc.querySelector("body"));
- container.childrenDirty = true;
- }
-
- let selections = [
- {
-+ desc: "Select the last item",
-+ selector: "#z",
-+ before: function() {},
-+ after: function() {
-+ assertChildren("*more*vwxyz");
-+ }
-+ },
-+ {
- desc: "Select the first item",
- selector: "#a",
- before: function() {
- },
- after: function() {
- assertChildren("abcde*more*");
- }
- },
-@@ -92,55 +100,48 @@ function test() {
- doc = content.document;
- waitForFocus(setupTest, content);
- }, true);
- content.location = "http://mochi.test:8888/browser/browser/devtools/markupview/test/browser_inspector_markup_subset.html";
-
- function setupTest() {
- var target = TargetFactory.forTab(gBrowser.selectedTab);
- let toolbox = gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
-- toolbox.once("inspector-selected", function SE_selected(id, aInspector) {
-- inspector = aInspector;
-- markup = inspector.markup;
-- runNextSelection();
-- });
-- });
-- }
--
-- function runTests() {
-- inspector.selection.once("new-node", startTests);
-- executeSoon(function() {
-- inspector.selection.setNode(doc.body);
-+ inspector = toolbox.getCurrentPanel();
-+ markup = inspector.markup;
-+ inspector.once("inspector-updated", runNextSelection);
- });
- }
-
- function runNextSelection() {
- let selection = selections.shift();
- if (!selection) {
- clickMore();
- return;
- }
-
- info(selection.desc);
- selection.before();
-- inspector.selection.once("new-node", function() {
-+ inspector.once("inspector-updated", function() {
- selection.after();
- runNextSelection();
- });
- inspector.selection.setNode(doc.querySelector(selection.selector));
- }
-
- function clickMore() {
- info("Check that clicking more loads the whole thing.");
- // Make sure that clicking the "more" button loads all the nodes.
-- let container = markup.getContainer(doc.querySelector("body"));
-+ let container = getContainerForRawNode(markup, doc.querySelector("body"));
- let button = container.elt.querySelector("button");
- button.click();
-- assertChildren("abcdefghijklmnopqrstuvwxyz");
-- finishUp();
-+ markup._waitForChildren().then(() => {
-+ assertChildren("abcdefghijklmnopqrstuvwxyz");
-+ finishUp();
-+ });
- }
-
- function finishUp() {
- doc = inspector = null;
- gBrowser.removeCurrentTab();
- finish();
- }
- }
-diff --git a/browser/devtools/markupview/test/head.js b/browser/devtools/markupview/test/head.js
---- a/browser/devtools/markupview/test/head.js
-+++ b/browser/devtools/markupview/test/head.js
-@@ -11,8 +11,44 @@ let TargetFactory = devtools.TargetFacto
- function clearUserPrefs()
- {
- Services.prefs.clearUserPref("devtools.inspector.htmlPanelOpen");
- Services.prefs.clearUserPref("devtools.inspector.sidebarOpen");
- Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
- }
-
- registerCleanupFunction(clearUserPrefs);
-+
-+Services.prefs.setBoolPref("devtools.debugger.log", true);
-+SimpleTest.registerCleanupFunction(() => {
-+ Services.prefs.clearUserPref("devtools.debugger.log");
-+});
-+
-+function getContainerForRawNode(markupView, rawNode) {
-+ let front = markupView.walker.frontForRawNode(rawNode);
-+ let container = markupView.getContainer(front);
-+ return container;
-+}
-+
-+
-+Services.prefs.setBoolPref("devtools.debugger.log", true);
-+SimpleTest.registerCleanupFunction(() => {
-+ Services.prefs.clearUserPref("devtools.debugger.log");
-+});
-+
-+function getContainerForRawNode(markupView, rawNode) {
-+ let front = markupView.walker.frontForRawNode(rawNode);
-+ let container = markupView.getContainer(front);
-+ return container;
-+}
-+
-+
-+Services.prefs.setBoolPref("devtools.debugger.log", true);
-+SimpleTest.registerCleanupFunction(() => {
-+ Services.prefs.clearUserPref("devtools.debugger.log");
-+});
-+
-+function getContainerForRawNode(markupView, rawNode) {
-+ let front = markupView.walker.frontForRawNode(rawNode);
-+ let container = markupView.getContainer(front);
-+ return container;
-+}
-+
deleted file mode 100644
--- a/search-box-remote.diff
+++ /dev/null
@@ -1,482 +0,0 @@
-# HG changeset patch
-# User Dave Camp <dcamp@mozilla.com>
-# Date 1370924326 25200
-# Mon Jun 10 21:18:46 2013 -0700
-# Node ID f2cb449dbdfbb5f006856d91943417c22bbd230e
-# Parent a5aab8dafa96ddb3551c06ffcd701ba122a63bce
-imported patch search-box-remote.diff
-* * *
-imported patch search-fixes-2.diff
-* * *
-imported patch selector-search-fix.diff
-
-diff --git a/browser/devtools/inspector/inspector-panel.js b/browser/devtools/inspector/inspector-panel.js
---- a/browser/devtools/inspector/inspector-panel.js
-+++ b/browser/devtools/inspector/inspector-panel.js
-@@ -219,28 +219,29 @@ InspectorPanel.prototype = {
- */
- setupSearchBox: function InspectorPanel_setupSearchBox() {
- let searchDoc;
- if (this.target.isLocalTab) {
- searchDoc = this.browser.contentDocument;
- } else if (this.target.window) {
- searchDoc = this.target.window.document;
- } else {
-- return;
-+ searchDoc = null;
- }
- // Initiate the selectors search object.
-- let setNodeFunction = function(node) {
-- this.selection.setNode(node, "selectorsearch");
-+ let setNodeFunction = function(eventName, node) {
-+ this.selection.setNodeFront(node, "selectorsearch");
- }.bind(this);
- if (this.searchSuggestions) {
- this.searchSuggestions.destroy();
- this.searchSuggestions = null;
- }
- this.searchBox = this.panelDoc.getElementById("inspector-searchbox");
-- this.searchSuggestions = new SelectorSearch(searchDoc, this.searchBox, setNodeFunction);
-+ this.searchSuggestions = new SelectorSearch(this, searchDoc, this.searchBox);
-+ this.searchSuggestions.on("node-selected", setNodeFunction);
- },
-
- /**
- * Build the sidebar.
- */
- setupSidebar: function InspectorPanel_setupSidebar() {
- let tabbox = this.panelDoc.querySelector("#inspector-sidebar");
- this.sidebar = new ToolSidebar(tabbox, this, "inspector");
-diff --git a/browser/devtools/inspector/selector-search.js b/browser/devtools/inspector/selector-search.js
---- a/browser/devtools/inspector/selector-search.js
-+++ b/browser/devtools/inspector/selector-search.js
-@@ -1,39 +1,42 @@
- /* 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";
-
- const {Cu} = require("chrome");
-+const EventEmitter = require("devtools/shared/event-emitter");
-+const promise = require("sdk/core/promise");
-
- loader.lazyGetter(this, "AutocompletePopup", () => {
- return Cu.import("resource:///modules/devtools/AutocompletePopup.jsm", {}).AutocompletePopup;
- });
-
- // Maximum number of selector suggestions shown in the panel.
- const MAX_SUGGESTIONS = 15;
-
- /**
- * Converts any input box on a page to a CSS selector search and suggestion box.
- *
- * @constructor
-+ * @param InspectorPanel aInspector
-+ * The InspectorPanel whose `walker` attribute should be used for
-+ * document traversal.
- * @param nsIDOMDocument aContentDocument
-- * The content document which inspector is attached to.
-+ * The content document which inspector is attached to, or null if
-+ * a remote document.
- * @param nsiInputElement aInputNode
- * The input element to which the panel will be attached and from where
- * search input will be taken.
-- * @param Function aCallback
-- * The method to callback when a search is available.
-- * This method is called with the matched node as the first argument.
- */
--function SelectorSearch(aContentDocument, aInputNode, aCallback) {
-+function SelectorSearch(aInspector, aContentDocument, aInputNode) {
-+ this.inspector = aInspector;
- this.doc = aContentDocument;
-- this.callback = aCallback;
- this.searchBox = aInputNode;
- this.panelDoc = this.searchBox.ownerDocument;
-
- // initialize variables.
- this._lastSearched = null;
- this._lastValidSearch = "";
- this._lastToLastValidSearch = null;
- this._searchResults = null;
-@@ -57,22 +60,30 @@ function SelectorSearch(aContentDocument
- onClick: this._onListBoxKeypress,
- onKeypress: this._onListBoxKeypress,
- };
- this.searchPopup = new AutocompletePopup(this.panelDoc, options);
-
- // event listeners.
- this.searchBox.addEventListener("command", this._onHTMLSearch, true);
- this.searchBox.addEventListener("keypress", this._onSearchKeypress, true);
-+
-+ // For testing, we need to be able to wait for the most recent node request
-+ // to finish. Tests can watch this promise for that.
-+ this._lastQuery = promise.resolve(null);
-+
-+ EventEmitter.decorate(this);
- }
-
- exports.SelectorSearch = SelectorSearch;
-
- SelectorSearch.prototype = {
-
-+ get walker() this.inspector.walker,
-+
- // The possible states of the query.
- States: {
- CLASS: "class",
- ID: "id",
- TAG: "tag",
- },
-
- // The current state of the query.
-@@ -163,101 +174,122 @@ SelectorSearch.prototype = {
- this.searchBox.removeEventListener("keypress", this._onSearchKeypress, true);
- this.searchPopup.destroy();
- this.searchPopup = null;
- this.searchBox = null;
- this.doc = null;
- this.panelDoc = null;
- this._searchResults = null;
- this._searchSuggestions = null;
-- this.callback = null;
-+ EventEmitter.decorate(this);
-+ },
-+
-+ _selectResult: function(index) {
-+ return this._searchResults.item(index).then(node => {
-+ this.emit("node-selected", node);
-+ });
- },
-
- /**
- * The command callback for the input box. This function is automatically
- * invoked as the user is typing if the input box type is search.
- */
- _onHTMLSearch: function SelectorSearch__onHTMLSearch() {
- let query = this.searchBox.value;
- if (query == this._lastSearched) {
- return;
- }
- this._lastSearched = query;
-+ this._searchResults = null;
- this._searchIndex = 0;
-
- if (query.length == 0) {
- this._lastValidSearch = "";
- this.searchBox.removeAttribute("filled");
- this.searchBox.classList.remove("devtools-no-search-result");
- if (this.searchPopup.isOpen) {
- this.searchPopup.hidePopup();
- }
- return;
- }
-
- this.searchBox.setAttribute("filled", true);
-- try {
-- this._searchResults = this.doc.querySelectorAll(query);
-- }
-- catch (ex) {
-- this._searchResults = [];
-- }
-- if (this._searchResults.length > 0) {
-- this._lastValidSearch = query;
-- // Even though the selector matched atleast one node, there is still
-- // possibility of suggestions.
-- if (query.match(/[\s>+]$/)) {
-- // If the query has a space or '>' at the end, create a selector to match
-- // the children of the selector inside the search box by adding a '*'.
-- this._lastValidSearch += "*";
-- }
-- else if (query.match(/[\s>+][\.#a-zA-Z][\.#>\s+]*$/)) {
-- // If the query is a partial descendant selector which does not matches
-- // any node, remove the last incomplete part and add a '*' to match
-- // everything. For ex, convert 'foo > b' to 'foo > *' .
-- let lastPart = query.match(/[\s>+][\.#a-zA-Z][^>\s+]*$/)[0];
-- this._lastValidSearch = query.slice(0, -1 * lastPart.length + 1) + "*";
-+ let queryList = null;
-+
-+ this._lastQuery = this.walker.querySelectorAll(this.walker.rootNode, query).then(list => {
-+ return list;
-+ }, (err) => {
-+ // Failures are ok here, just use a null item list;
-+ return null;
-+ }).then(queryList => {
-+ // Value has changed since we started this request, we're done.
-+ if (query != this.searchBox.value) {
-+ if (queryList) {
-+ queryList.release();
-+ }
-+ return promise.reject(null);
- }
-
-- if (!query.slice(-1).match(/[\.#\s>+]/)) {
-- // Hide the popup if we have some matching nodes and the query is not
-- // ending with [.# >] which means that the selector is not at the
-- // beginning of a new class, tag or id.
-- if (this.searchPopup.isOpen) {
-- this.searchPopup.hidePopup();
-+ this._searchResults = queryList;
-+ if (this._searchResults && this._searchResults.length > 0) {
-+ this._lastValidSearch = query;
-+ // Even though the selector matched atleast one node, there is still
-+ // possibility of suggestions.
-+ if (query.match(/[\s>+]$/)) {
-+ // If the query has a space or '>' at the end, create a selector to match
-+ // the children of the selector inside the search box by adding a '*'.
-+ this._lastValidSearch += "*";
- }
-+ else if (query.match(/[\s>+][\.#a-zA-Z][\.#>\s+]*$/)) {
-+ // If the query is a partial descendant selector which does not matches
-+ // any node, remove the last incomplete part and add a '*' to match
-+ // everything. For ex, convert 'foo > b' to 'foo > *' .
-+ let lastPart = query.match(/[\s>+][\.#a-zA-Z][^>\s+]*$/)[0];
-+ this._lastValidSearch = query.slice(0, -1 * lastPart.length + 1) + "*";
-+ }
-+
-+ if (!query.slice(-1).match(/[\.#\s>+]/)) {
-+ // Hide the popup if we have some matching nodes and the query is not
-+ // ending with [.# >] which means that the selector is not at the
-+ // beginning of a new class, tag or id.
-+ if (this.searchPopup.isOpen) {
-+ this.searchPopup.hidePopup();
-+ }
-+ }
-+ else {
-+ this.showSuggestions();
-+ }
-+ this.searchBox.classList.remove("devtools-no-search-result");
-+
-+ return this._selectResult(0);
- }
- else {
-+ if (query.match(/[\s>+]$/)) {
-+ this._lastValidSearch = query + "*";
-+ }
-+ else if (query.match(/[\s>+][\.#a-zA-Z][\.#>\s+]*$/)) {
-+ let lastPart = query.match(/[\s+>][\.#a-zA-Z][^>\s+]*$/)[0];
-+ this._lastValidSearch = query.slice(0, -1 * lastPart.length + 1) + "*";
-+ }
-+ this.searchBox.classList.add("devtools-no-search-result");
- this.showSuggestions();
- }
-- this.searchBox.classList.remove("devtools-no-search-result");
-- this.callback(this._searchResults[0]);
-- }
-- else {
-- if (query.match(/[\s>+]$/)) {
-- this._lastValidSearch = query + "*";
-- }
-- else if (query.match(/[\s>+][\.#a-zA-Z][\.#>\s+]*$/)) {
-- let lastPart = query.match(/[\s+>][\.#a-zA-Z][^>\s+]*$/)[0];
-- this._lastValidSearch = query.slice(0, -1 * lastPart.length + 1) + "*";
-- }
-- this.searchBox.classList.add("devtools-no-search-result");
-- this.showSuggestions();
-- }
-+ return undefined;
-+ });
- },
-
- /**
- * Handles keypresses inside the input box.
- */
- _onSearchKeypress: function SelectorSearch__onSearchKeypress(aEvent) {
- let query = this.searchBox.value;
- switch(aEvent.keyCode) {
- case aEvent.DOM_VK_ENTER:
- case aEvent.DOM_VK_RETURN:
-- if (query == this._lastSearched) {
-+ if (query == this._lastSearched && this._searchResults) {
- this._searchIndex = (this._searchIndex + 1) % this._searchResults.length;
- }
- else {
- this._onHTMLSearch();
- return;
- }
- break;
-
-@@ -310,18 +342,18 @@ SelectorSearch.prototype = {
- return;
-
- default:
- return;
- }
-
- aEvent.preventDefault();
- aEvent.stopPropagation();
-- if (this._searchResults.length > 0) {
-- this.callback(this._searchResults[this._searchIndex]);
-+ if (this._searchResults && this._searchResults.length > 0) {
-+ this._lastQuery = this._selectResult(this._searchIndex);
- }
- },
-
- /**
- * Handles keypress and mouse click on the suggestions richlistbox.
- */
- _onListBoxKeypress: function SelectorSearch__onListBoxKeypress(aEvent) {
- switch(aEvent.keyCode || aEvent.button) {
-@@ -437,16 +469,19 @@ SelectorSearch.prototype = {
- }
- },
-
- /**
- * Suggests classes,ids and tags based on the user input as user types in the
- * searchbox.
- */
- showSuggestions: function SelectorSearch_showSuggestions() {
-+ if (!this.walker.isLocal()) {
-+ return;
-+ }
- let query = this.searchBox.value;
- if (this._lastValidSearch != "" &&
- this._lastToLastValidSearch != this._lastValidSearch) {
- this._searchSuggestions = {
- ids: new Map(),
- classes: new Map(),
- tags: new Map(),
- };
-diff --git a/browser/devtools/inspector/test/browser_inspector_bug_650804_search.js b/browser/devtools/inspector/test/browser_inspector_bug_650804_search.js
---- a/browser/devtools/inspector/test/browser_inspector_bug_650804_search.js
-+++ b/browser/devtools/inspector/test/browser_inspector_bug_650804_search.js
-@@ -63,16 +63,17 @@ function test()
- {
- openInspector(startTest);
- }
-
- function startTest(aInspector)
- {
- inspector = aInspector;
- inspector.selection.setNode($("b1"));
-+
- searchBox =
- inspector.panelWin.document.getElementById("inspector-searchbox");
-
- focusSearchBoxUsingShortcut(inspector.panelWin, function() {
- searchBox.addEventListener("command", checkState, true);
- searchBox.addEventListener("keypress", checkState, true);
- checkStateAndMoveOn(0);
- });
-@@ -90,25 +91,28 @@ function test()
- info("pressing key " + key + " to get id " + id);
- EventUtils.synthesizeKey(key, {}, inspector.panelWin);
- }
-
- function checkState(event) {
- if (event.type == "keypress" && keypressStates.indexOf(state) == -1) {
- return;
- }
-- executeSoon(function() {
-- let [key, id, isValid] = keyStates[state];
-- info(inspector.selection.node.id + " is selected with text " +
-- inspector.searchBox.value);
-- is(inspector.selection.node, $(id),
-- "Correct node is selected for state " + state);
-- is(!searchBox.classList.contains("devtools-no-search-result"), isValid,
-- "Correct searchbox result state for state " + state);
-- checkStateAndMoveOn(state + 1);
-+
-+ inspector.searchSuggestions._lastQuery.then(() => {
-+ executeSoon(() => {
-+ let [key, id, isValid] = keyStates[state];
-+ info(inspector.selection.node.id + " is selected with text " +
-+ inspector.searchBox.value);
-+ is(inspector.selection.node, $(id),
-+ "Correct node is selected for state " + state);
-+ is(!searchBox.classList.contains("devtools-no-search-result"), isValid,
-+ "Correct searchbox result state for state " + state);
-+ checkStateAndMoveOn(state + 1);
-+ });
- });
- }
-
- function finishUp() {
- searchBox = null;
- gBrowser.removeCurrentTab();
- finish();
- }
-diff --git a/browser/devtools/inspector/test/browser_inspector_bug_831693_combinator_suggestions.js b/browser/devtools/inspector/test/browser_inspector_bug_831693_combinator_suggestions.js
---- a/browser/devtools/inspector/test/browser_inspector_bug_831693_combinator_suggestions.js
-+++ b/browser/devtools/inspector/test/browser_inspector_bug_831693_combinator_suggestions.js
-@@ -55,16 +55,17 @@ function test()
- function setupTest()
- {
- openInspector(startTest);
- }
-
- function startTest(aInspector)
- {
- inspector = aInspector;
-+
- searchBox =
- inspector.panelWin.document.getElementById("inspector-searchbox");
- popup = inspector.searchSuggestions.searchPopup;
-
- focusSearchBoxUsingShortcut(inspector.panelWin, function() {
- searchBox.addEventListener("command", checkState, true);
- checkStateAndMoveOn(0);
- });
-@@ -80,17 +81,17 @@ function test()
- state = index;
-
- info("pressing key " + key + " to get suggestions " +
- JSON.stringify(suggestions));
- EventUtils.synthesizeKey(key, {}, inspector.panelWin);
- }
-
- function checkState(event) {
-- executeSoon(function() {
-+ inspector.searchSuggestions._lastQuery.then(() => {
- let [key, suggestions] = keyStates[state];
- let actualSuggestions = popup.getItems();
- is(popup._panel.state == "open" || popup._panel.state == "showing"
- ? actualSuggestions.length: 0, suggestions.length,
- "There are expected number of suggestions at " + state + "th step.");
- actualSuggestions = actualSuggestions.reverse();
- for (let i = 0; i < suggestions.length; i++) {
- is(suggestions[i][0], actualSuggestions[i].label,
-diff --git a/browser/devtools/inspector/test/browser_inspector_bug_831693_input_suggestion.js b/browser/devtools/inspector/test/browser_inspector_bug_831693_input_suggestion.js
---- a/browser/devtools/inspector/test/browser_inspector_bug_831693_input_suggestion.js
-+++ b/browser/devtools/inspector/test/browser_inspector_bug_831693_input_suggestion.js
-@@ -82,17 +82,17 @@ function test()
- state = index;
-
- info("pressing key " + key + " to get suggestions " +
- JSON.stringify(suggestions));
- EventUtils.synthesizeKey(key, {}, inspector.panelWin);
- }
-
- function checkState(event) {
-- executeSoon(function() {
-+ inspector.searchSuggestions._lastQuery.then(() => {
- let [key, suggestions] = keyStates[state];
- let actualSuggestions = popup.getItems();
- is(popup._panel.state == "open" || popup._panel.state == "showing"
- ? actualSuggestions.length: 0, suggestions.length,
- "There are expected number of suggestions at " + state + "th step.");
- actualSuggestions = actualSuggestions.reverse();
- for (let i = 0; i < suggestions.length; i++) {
- is(suggestions[i][0], actualSuggestions[i].label,
-diff --git a/toolkit/devtools/server/actors/inspector.js b/toolkit/devtools/server/actors/inspector.js
---- a/toolkit/devtools/server/actors/inspector.js
-+++ b/toolkit/devtools/server/actors/inspector.js
-@@ -2038,17 +2038,17 @@ function nodeDocument(node) {
- * Similar to a TreeWalker, except will dig in to iframes and it doesn't
- * implement the good methods like previousNode and nextNode.
- *
- * See TreeWalker documentation for explanations of the methods.
- */
- function DocumentWalker(aNode, aShow, aFilter, aExpandEntityReferences)
- {
- let doc = nodeDocument(aNode);
-- this.walker = doc.createTreeWalker(nodeDocument(aNode),
-+ this.walker = doc.createTreeWalker(doc,
- aShow, aFilter, aExpandEntityReferences);
- this.walker.currentNode = aNode;
- this.filter = aFilter;
- }
-
- DocumentWalker.prototype = {
- get node() this.walker.node,
- get whatToShow() this.walker.whatToShow,
--- a/series
+++ b/series
@@ -1,14 +1,8 @@
-remote-markup.diff
-remote-edit-attributes.diff
-remote-edit-value.diff
-inspector-panel-default-node.diff
-copy-html.diff
-search-box-remote.diff
-inspector-remote-delete-node.diff
+walker-reload.diff
+warning-fixes.diff
+style-actor.diff
android-browser.diff
-warning-fixes.diff
-walker-reload.diff
-style-actor.diff
window-targets.diff
inspector-retain-root.diff #+obsolete
protocol-clientserver-marshallers.diff #+experimental
+promise-return.diff #+experimental
--- a/style-actor.diff
+++ b/style-actor.diff
@@ -1,14 +1,14 @@
# HG changeset patch
# User Dave Camp <dcamp@mozilla.com>
# Date 1371506345 25200
# Mon Jun 17 14:59:05 2013 -0700
-# Node ID 50fc0e0dd0aae7f4d0436580dfa9c55a41b2650b
-# Parent c0b89ed707e700d850f91c82e321b06878e96ee7
+# Node ID 496a475072d70e5f9fafc8d03dfd3d627ece92c9
+# Parent d09c94392c1a3093065309079d3d7071c0312782
[mq]: style-actor.diff
* * *
[mq]: computed-view-remote.diff
diff --git a/browser/devtools/inspector/inspector-panel.js b/browser/devtools/inspector/inspector-panel.js
--- a/browser/devtools/inspector/inspector-panel.js
+++ b/browser/devtools/inspector/inspector-panel.js
@@ -40,22 +40,18 @@ function InspectorPanel(iframeWindow, to
@@ -31,17 +31,17 @@ diff --git a/browser/devtools/inspector/
return this._getDefaultNodeForSelection();
}).then(defaultSelection => {
return this._deferredOpen(defaultSelection);
}).then(null, console.error);
},
_deferredOpen: function(defaultSelection) {
let deferred = promise.defer();
-@@ -317,24 +313,21 @@ InspectorPanel.prototype = {
+@@ -311,24 +307,21 @@ InspectorPanel.prototype = {
* When a new node is selected.
*/
onNewSelection: function InspectorPanel_onNewSelection() {
this.cancelLayoutChange();
// Wait for all the known tools to finish updating and then let the
// client know.
let selection = this.selection.nodeFront;
@@ -59,17 +59,17 @@ diff --git a/browser/devtools/inspector/
},
/**
* Delay the "inspector-updated" notification while a tool
* is updating itself. Returns a function that must be
* invoked when the tool is done updating with the node
* that the tool is viewing.
*/
-@@ -408,16 +401,17 @@ InspectorPanel.prototype = {
+@@ -402,16 +395,17 @@ InspectorPanel.prototype = {
*/
destroy: function InspectorPanel__destroy() {
if (this._destroyPromise) {
return this._destroyPromise;
}
if (this.walker) {
this._destroyPromise = this.walker.release().then(null, console.error);
delete this.walker;
@@ -5189,17 +5189,17 @@ diff --git a/toolkit/devtools/gcli/Templ
if (capture) {
node.removeAttribute('capture' + name.substring(2));
}
}
else {
diff --git a/toolkit/devtools/server/actors/inspector.js b/toolkit/devtools/server/actors/inspector.js
--- a/toolkit/devtools/server/actors/inspector.js
+++ b/toolkit/devtools/server/actors/inspector.js
-@@ -47,16 +47,18 @@ const protocol = require("devtools/serve
+@@ -56,16 +56,18 @@ const protocol = require("devtools/serve
const {Arg, Option, method, RetVal, types} = protocol;
const {LongStringActor, ShortLongString} = require("devtools/server/actors/string");
const promise = require("sdk/core/promise");
const object = require("sdk/util/object");
const events = require("sdk/event/core");
const { Unknown } = require("sdk/platform/xpcom");
const { Class } = require("sdk/core/heritage");
@@ -5208,17 +5208,17 @@ diff --git a/toolkit/devtools/server/act
const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
Cu.import("resource://gre/modules/Services.jsm");
exports.register = function(handle) {
handle.addTabActor(InspectorActor, "inspectorActor");
};
-@@ -120,31 +122,35 @@ var NodeActor = protocol.ActorClass({
+@@ -129,31 +131,35 @@ var NodeActor = protocol.ActorClass({
/**
* Instead of storing a connection object, the NodeActor gets its connection
* from its associated walker.
*/
get conn() this.walker.conn,
// Returns the JSON representation of this object over the wire.
form: function(detail) {
@@ -5245,17 +5245,17 @@ diff --git a/toolkit/devtools/server/act
nodeName: this.rawNode.nodeName,
numChildren: numChildren,
// doctype attributes
name: this.rawNode.name,
publicId: this.rawNode.publicId,
systemId: this.rawNode.systemId,
-@@ -167,16 +173,29 @@ var NodeActor = protocol.ActorClass({
+@@ -176,16 +182,29 @@ var NodeActor = protocol.ActorClass({
} else {
form.shortValue = this.rawNode.nodeValue;
}
}
return form;
},
@@ -5275,17 +5275,17 @@ diff --git a/toolkit/devtools/server/act
writeAttrs: function() {
if (!this.rawNode.attributes) {
return undefined;
}
return [{namespace: attr.namespace, name: attr.name, value: attr.value }
for (attr of this.rawNode.attributes)];
},
-@@ -290,16 +309,20 @@ let NodeFront = protocol.FrontClass(Node
+@@ -299,16 +318,20 @@ let NodeFront = protocol.FrontClass(Node
this._observer = null;
}
protocol.Front.prototype.destroy.call(this);
},
// Update the object given a form representation off the wire.
form: function(form, detail, ctx) {
@@ -5296,40 +5296,89 @@ diff --git a/toolkit/devtools/server/act
// Shallow copy of the form. We could just store a reference, but
// eventually we'll want to update some of the data.
this._form = object.merge(form);
this._form.attrs = this._form.attrs ? this._form.attrs.slice() : [];
if (form.parent) {
// Get the owner actor for this actor (the walker), and find the
// parent node of this actor from it, creating a standin node if
-@@ -814,16 +837,23 @@ var WalkerActor = protocol.ActorClass({
- actor.observer.observe(node, {
- attributes: true,
- characterData: true,
- childList: true,
- subtree: true
- });
- },
-
+@@ -2054,33 +2077,71 @@ var InspectorActor = protocol.ActorClass
+ }
+
+ return this._walkerPromise;
+ }, {
+ request: {},
+ response: {
+ walker: RetVal("domwalker")
+ }
++ }),
++
+ getNodeStyle: method(function() {
-+ return NodeStyleActor(this);
++ if (this._nodeStylePromise) {
++ return this._nodeStylePromise;
++ }
++
++ this._nodeStylePromise = this.getWalker().then(walker => {
++ return NodeStyleActor(walker);
++ });
++ return this._nodeStylePromise;
+ }, {
+ request: {},
+ response: { nodeStyle: RetVal("nodestyle") }
+ })
+ });
+
+ /**
+ * Client side of the inspector actor, which is used to create
+ * inspector-related actors, including the walker.
+ */
+ var InspectorFront = exports.InspectorFront = protocol.FrontClass(InspectorActor, {
+ initialize: function(client, tabForm) {
+ protocol.Front.prototype.initialize.call(this, client);
+ this.actorID = tabForm.inspectorActor;
+
+ // XXX: This is the first actor type in its hierarchy to use the protocol
+ // library, so we're going to self-own on the client side for now.
+ client.addActorPool(this);
+ this.manage(this);
+- }
++ },
++
++ getWalker: custom(function() {
++ return this._getWalker().then(walker => {
++ this.walker = walker;
++ return walker;
++ });
++ }, {
++ impl: "_getWalker"
+ }),
+
- /**
- * Return the document node that contains the given node,
- * or the root node if no node is specified.
- * @param NodeActor node
- * The node whose document is needed, or null to
- * return the root.
- */
- document: method(function(node) {
++ getNodeStyle: custom(function() {
++ this._getNodeStyle().then(nodeStyle => {
++ // We need a walker to understand node references from the
++ // node style.
++ if (this.walker) {
++ return nodeStyle;
++ }
++ return this.getWalker().then(() => {
++ return nodeStyle;
++ });
++ }
++ }, {
++ impl: "_getNodeStyle"
++ })
+ });
+
+ function documentWalker(node, whatToShow=Ci.nsIDOMNodeFilter.SHOW_ALL) {
+ return new DocumentWalker(node, whatToShow, whitespaceTextFilter, false);
+ }
+
+ // Exported for test purposes.
+ exports._documentWalker = documentWalker;
diff --git a/toolkit/devtools/server/actors/styleeditor.js b/toolkit/devtools/server/actors/styleeditor.js
--- a/toolkit/devtools/server/actors/styleeditor.js
+++ b/toolkit/devtools/server/actors/styleeditor.js
@@ -115,17 +115,17 @@ StyleEditorActor.prototype = {
},
/**
* Get the BaseURI for the document.
@@ -5346,17 +5395,17 @@ diff --git a/toolkit/devtools/server/act
* Adds load listeners to document.
*/
onNewDocument: function() {
// delete previous document's actors
diff --git a/toolkit/devtools/server/actors/styles.js b/toolkit/devtools/server/actors/styles.js
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/actors/styles.js
-@@ -0,0 +1,559 @@
+@@ -0,0 +1,553 @@
+/* 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";
+
+const {Cc, Ci} = require("chrome");
+const protocol = require("devtools/server/protocol");
@@ -5374,21 +5423,21 @@ new file mode 100644
+// Predeclare the domnode actor type for use in requests.
+types.addActorType("domnode");
+
+types.addDictType("appliedstyle", {
+ rule: "domstylerule#actorid",
+ inherited: "nullable:domnode#actorid"
+});
+
-+types.addLifetime("requestWalker", "requestWalker");
++types.addLifetime("walker", "walker");
+
+types.addDictType("matchedselector", {
+ rule: "domstylerule#actorid",
-+ sourceElement: "nullable:requestWalker:domnode",
++ sourceElement: "nullable:walker:domnode",
+ selector: "string",
+ value: "string",
+ status: "number"
+});
+
+var NodeStyleActor = protocol.ActorClass({
+ typeName: "nodestyle",
+ initialize: function(walker) {
@@ -5400,17 +5449,16 @@ new file mode 100644
+
+ get conn() this.walker.conn,
+
+ // item can be a nsIDOMCSSRule or an Element.
+ _styleRef: function(item) {
+ if (this._refMap.has(item)) {
+ return this._refMap.get(item);
+ }
-+ dump("item: " + item + "\n");
+ let actor = StyleRuleActor(this, item);
+ this.manage(actor);
+ this._refMap.set(item, actor);
+
+ return actor;
+ },
+
+ _sheetRef: function(sheet) {
@@ -5519,18 +5567,16 @@ new file mode 100644
+ selector: selectorInfo.selector.text,
+ value: selectorInfo.value,
+ status: selectorInfo.status
+ });
+ }
+
+ this.expandSets(rules, sheets);
+
-+ this.requestWalker = walker;
-+
+ return {
+ matched: matched,
+ rules: [...rules],
+ sheets: [...sheets]
+ }
+ }, {
+ request: {
+ node: Arg(0, "domnode"),
@@ -5559,31 +5605,26 @@ new file mode 100644
+
+ getApplied: method(function(node, options) {
+ let entries = [];
+
+ this.addElementRules(node.rawNode, undefined, options, entries);
+
+ if (options.inherited) {
+ let parent = this.walker.parentNode(node);
-+ dump("TRYING TO ADD INHERITED RULES: " + parent + "\n");
+ while (parent && parent.rawNode.nodeType != Ci.nsIDOMNode.DOCUMENT_NODE) {
-+ dump("TRYING TO ADD INHERITED RULES: " + parent + "\n");
+ this.addElementRules(parent.rawNode, parent, options, entries);
+ parent = this.walker.parentNode(parent);
+ }
+ }
+
+
+ if (options.matchedSelectors) {
-+ dump("matched selectors provided except it isn't.");
+ for (let entry of entries) {
-+ dump("Checking entry\n");
+ if (entry.rule.type === ELEMENT_STYLE) {
-+ dump("element style!\n");
+ continue;
+ }
+
+ let domRule = entry.rule.rawRule;
+ let selectors = CssLogic.getSelectors(domRule);
+ let element = entry.inherited ? entry.inherited.rawNode : node.rawNode;
+ entry.matchedSelectors = [];
+ for (let i = 0; i < selectors.length; i++) {
@@ -5645,24 +5686,28 @@ new file mode 100644
+ }
+ },
+});
+exports.NodeStyleActor = NodeStyleActor;
+
+var NodeStyleFront = protocol.FrontClass(NodeStyleActor, {
+ initialize: function(conn, form, ctx, detail) {
+ protocol.Front.prototype.initialize.call(this, conn, form, ctx, detail);
++ this.inspector = this.parent();
+ },
+
+ destroy: function() {
+ protocol.Front.prototype.destroy.call(this);
+ },
+
++ get walker() {
++ return this.inspector.walker;
++ },
++
+ getMatchedSelectors: protocol.custom(function(node, property) {
-+ this.requestWalker = node.parent();
+ return this._getMatchedSelectors(node, property).then(ret => {
+ return ret.matched;
+ });
+ }, {
+ impl: "_getMatchedSelectors"
+ }),
+
+ getApplied: protocol.custom(function(node, options={}) {
@@ -5732,17 +5777,16 @@ new file mode 100644
+ this.rawRule = item;
+ if (this.rawRule instanceof Ci.nsIDOMCSSStyleRule && this.rawRule.parentStyleSheet) {
+ this.line = DOMUtils.getRuleLine(this.rawRule);
+ }
+
+ } else {
+ this.type = ELEMENT_STYLE;
+ this.rawNode = item;
-+ dump("item: " + item + "\n");
+ this.rawRule = {
+ style: item.style,
+ toString: function() "[element rule " + this.style + "]"
+ }
+ }
+ },
+
+ get conn() this.nodeStyle.conn,
@@ -5764,17 +5808,16 @@ new file mode 100644
+ if (this.rawRule.parentRule) {
+ form.parentRule = this.nodeStyle._styleRef(this.rawRule.parentRule).actorID;
+ }
+ if (this.rawRule.parentStyleSheet) {
+ form.parentStyleSheet = this.nodeStyle._sheetRef(this.rawRule.parentStyleSheet).actorID;
+ }
+
+ if (this.type === ELEMENT_STYLE) {
-+ dump("raw node: " + this.rawNode + "\n");
+ form.href = this.rawNode.ownerDocument.location.href;
+ }
+
+ switch (this.type) {
+ case Ci.nsIDOMCSSRule.STYLE_RULE:
+ form.selectors = CssLogic.getSelectors(this.rawRule);
+ /* fall through */
+ case ELEMENT_STYLE:
--- a/walker-reload.diff
+++ b/walker-reload.diff
@@ -1,48 +1,50 @@
# HG changeset patch
# User Dave Camp <dcamp@mozilla.com>
# Date 1374258100 25200
# Fri Jul 19 11:21:40 2013 -0700
-# Node ID c0b89ed707e700d850f91c82e321b06878e96ee7
-# Parent 193c735dba54ac6efcd67cbd39e33b37d3115fbe
+# Node ID 4502bf53830e85ec59f874d2856695ab53de8a40
+# Parent 05ff12c00114de0b595cab891fb42336753b8462
imported patch walker-reload.diff
diff --git a/browser/devtools/inspector/inspector-panel.js b/browser/devtools/inspector/inspector-panel.js
--- a/browser/devtools/inspector/inspector-panel.js
+++ b/browser/devtools/inspector/inspector-panel.js
-@@ -158,17 +158,26 @@ InspectorPanel.prototype = {
+@@ -151,24 +151,27 @@ InspectorPanel.prototype = {
+ this.setupSidebar();
+
+ return deferred.promise;
+ },
+
+ /**
+ * Return a promise that will resolve to the default node for selection.
*/
- _getDefaultNodeForSelection : function() {
+- _getDefaultNodeForSelection : function() {
++ _getDefaultNodeForSelection: function() {
if (this._defaultNode) {
return this._defaultNode;
}
let walker = this.walker;
++
// if available set body node as default selected node
// else set documentElement
- return walker.querySelector(this.walker.rootNode, "body").then(front => {
-+ let root;
-+ if (walker.rootNode.actorID) {
-+ root = promise.resolve(walker.rootNode);
-+ } else {
-+ root = walker.document();
-+ }
-+
-+ return root.then(rootNode => {
++ return walker.getRootNode().then(rootNode => {
+ return walker.querySelector(rootNode, "body");
+ }).then(front => {
if (front) {
return front;
}
return this.walker.documentElement(this.walker.rootNode);
}).then(node => {
if (walker !== this.walker) {
promise.reject(null);
}
-@@ -280,43 +289,31 @@ InspectorPanel.prototype = {
+@@ -280,43 +283,31 @@ InspectorPanel.prototype = {
this.sidebar.show();
},
/**
* Reset the inspector on navigate away.
*/
onNavigatedAway: function InspectorPanel_onNavigatedAway(event, payload) {
let newWindow = payload._navPayload || payload;
@@ -85,17 +87,17 @@ diff --git a/browser/devtools/inspector/
/**
* When a new node is selected.
*/
onNewSelection: function InspectorPanel_onNewSelection() {
diff --git a/toolkit/devtools/server/actors/inspector.js b/toolkit/devtools/server/actors/inspector.js
--- a/toolkit/devtools/server/actors/inspector.js
+++ b/toolkit/devtools/server/actors/inspector.js
-@@ -1567,16 +1567,24 @@ var WalkerActor = protocol.ActorClass({
+@@ -1576,16 +1576,24 @@ var WalkerActor = protocol.ActorClass({
mutation.added = addedActors;
}
this.queueMutation(mutation);
}
},
onFrameLoad: function(window) {
let frame = window.frameElement;
@@ -110,17 +112,17 @@ diff --git a/toolkit/devtools/server/act
let frameActor = this._refMap.get(frame);
if (!frameActor) {
return;
}
this.queueMutation({
type: "frameLoad",
target: frameActor.actorID,
-@@ -1627,16 +1635,21 @@ var WalkerActor = protocol.ActorClass({
+@@ -1636,16 +1644,21 @@ var WalkerActor = protocol.ActorClass({
}
let doc = window.document;
let documentActor = this._refMap.get(doc);
if (!documentActor) {
return;
}
@@ -132,39 +134,114 @@ diff --git a/toolkit/devtools/server/act
this.queueMutation({
type: "documentUnload",
target: documentActor.actorID
});
let walker = documentWalker(doc);
let parentNode = walker.parentNode();
if (parentNode) {
-@@ -1778,16 +1791,21 @@ var WalkerFront = exports.WalkerFront =
+@@ -1668,29 +1681,41 @@ var WalkerActor = protocol.ActorClass({
+ /**
+ * Client side of the DOM walker.
+ */
+ var WalkerFront = exports.WalkerFront = protocol.FrontClass(WalkerActor, {
+ // Set to true if cleanup should be requested after every mutation list.
+ autoCleanup: true,
+
+ initialize: function(client, form) {
++ this._rootNodeDeferred = promise.defer();
+ protocol.Front.prototype.initialize.call(this, client, form);
+ this._orphaned = new Set();
+ this._retainedOrphans = new Set();
+ },
+
+ destroy: function() {
+ protocol.Front.prototype.destroy.call(this);
+ },
+ // Update the object given a form representation off the wire.
+ form: function(json) {
+- this.actorID = json.actorID;
++ this.actorID = json.actor;
+ this.rootNode = types.getType("domnode").read(json.root, this);
++ this._rootNodeDeferred.resolve(this.rootNode);
++ },
++
++ /**
++ * Clients can use walker.rootNode to get the current root node of the
++ * walker, but during a reload the root node might be null. This
++ * method returns a promise that will resolve to the root node when it is
++ * set.
++ */
++ getRootNode: function() {
++ return this._rootNodeDeferred.promise;
+ },
+
+ /**
+ * When reading an actor form off the wire, we want to hook it up to its
+ * parent front. The protocol guarantees that the parent will be seen
+ * by the client in either a previous or the current request.
+ * So if we've already seen this parent return it, otherwise create
+ * a bare-bones stand-in node. The stand-in node will be updated
+@@ -1788,18 +1813,29 @@ var WalkerFront = exports.WalkerFront =
/**
* Get any unprocessed mutation records and process them.
*/
getMutations: protocol.custom(function(options={}) {
return this._getMutations(options).then(mutations => {
let emitMutations = [];
for (let change of mutations) {
+ // The target is only an actorID, get the associated front.
+- let targetID = change.target;
+- let targetFront = this.get(targetID);
++ let targetID;
++ let targetFront;
++
+ if (change.type === "newRoot") {
+ this.rootNode = types.getType("domnode").read(change.target, this);
-+ continue;
++ this._rootNodeDeferred.resolve(this.rootNode);
++ targetID = this.rootNode.actorID;
++ targetFront = this.rootNode;
++ } else {
++ targetID = change.target;
++ targetFront = this.get(targetID);
+ }
+
- // The target is only an actorID, get the associated front.
- let targetID = change.target;
- let targetFront = this.get(targetID);
if (!targetFront) {
console.trace("Got a mutation for an unexpected actor: " + targetID + ", please file a bug on bugzilla.mozilla.org!");
continue;
}
-@@ -1977,33 +1995,38 @@ var InspectorActor = protocol.ActorClass
+ let emittedMutation = object.merge(change, { target: targetFront });
+
+ if (change.type === "childList") {
+@@ -1843,16 +1879,21 @@ var WalkerFront = exports.WalkerFront =
+ // document children, because we should have gotten a documentUnload
+ // first.
+ for (let child of targetFront.treeChildren()) {
+ if (child.nodeType === Ci.nsIDOMNode.DOCUMENT_NODE) {
+ console.trace("Got an unexpected frameLoad in the inspector, please file a bug on bugzilla.mozilla.org!");
+ }
+ }
+ } else if (change.type === "documentUnload") {
++ if (targetFront === this.rootNode) {
++ this.rootNode = null;
++ this._rootNodeDeferred = promise.defer();
++ }
++
+ // We try to give fronts instead of actorIDs, but these fronts need
+ // to be destroyed now.
+ emittedMutation.target = targetFront.actorID;
+ emittedMutation.targetParent = targetFront.parentNode();
+
+ // Release the document node and all of its children, even retained.
+ this._releaseFront(targetFront, true);
+ } else if (change.type === "unretained") {
+@@ -1986,33 +2027,38 @@ var InspectorActor = protocol.ActorClass
return tabActor.browser;
} else if (tabActor.browser instanceof Ci.nsIDOMElement) {
return tabActor.browser.contentWindow;
}
return null;
},
getWalker: method(function(options={}) {
@@ -175,17 +252,17 @@ diff --git a/toolkit/devtools/server/act
let deferred = promise.defer();
+ this._walkerPromise = deferred.promise;
let window = this.window;
var domReady = () => {
let tabActor = this.tabActor;
window.removeEventListener("DOMContentLoaded", domReady, true);
- deferred.resolve(WalkerActor(this.conn, window.document, tabActor._browser, options));
+ deferred.resolve(WalkerActor(this.conn, window.document, tabActor._tabbrowser, options));
};
if (window.document.readyState === "loading") {
window.addEventListener("DOMContentLoaded", domReady, true);
} else {
domReady();
}
@@ -194,8 +271,141 @@ diff --git a/toolkit/devtools/server/act
}, {
request: {},
response: {
walker: RetVal("domwalker")
}
})
});
+diff --git a/toolkit/devtools/server/tests/mochitest/Makefile.in b/toolkit/devtools/server/tests/mochitest/Makefile.in
+--- a/toolkit/devtools/server/tests/mochitest/Makefile.in
++++ b/toolkit/devtools/server/tests/mochitest/Makefile.in
+@@ -18,16 +18,17 @@ MOCHITEST_CHROME_FILES = \
+ test_inspector-changevalue.html \
+ test_inspector-insert.html \
+ test_inspector-mutations-attr.html \
+ test_inspector-mutations-childlist.html \
+ test_inspector-mutations-frameload.html \
+ test_inspector-mutations-value.html \
+ test_inspector-release.html \
+ test_inspector-remove.html \
++ test_inspector-reload.html \
+ test_inspector-retain.html \
+ test_inspector-pseudoclass-lock.html \
+ test_inspector-traversal.html \
+ test_unsafeDereference.html \
+ nonchrome_unsafeDereference.html \
+ $(NULL)
+
+ include $(topsrcdir)/config/rules.mk
+diff --git a/toolkit/devtools/server/tests/mochitest/inspector-helpers.js b/toolkit/devtools/server/tests/mochitest/inspector-helpers.js
+--- a/toolkit/devtools/server/tests/mochitest/inspector-helpers.js
++++ b/toolkit/devtools/server/tests/mochitest/inspector-helpers.js
+@@ -230,16 +230,20 @@ function isFrameLoad(change) {
+ function isUnretained(change) {
+ return change.type === "unretained";
+ }
+
+ function isChildList(change) {
+ return change.type === "childList";
+ }
+
++function isNewRoot(change) {
++ return change.type === "newRoot";
++}
++
+ // Make sure an iframe's src attribute changed and then
+ // strip that mutation out of the list.
+ function assertSrcChange(mutations) {
+ return assertAndStrip(mutations, "Should have had an iframe source change.", isSrcChange);
+ }
+
+ // Make sure there's an unload in the mutation list and strip
+ // that mutation out of the list
+diff --git a/toolkit/devtools/server/tests/mochitest/test_inspector-reload.html b/toolkit/devtools/server/tests/mochitest/test_inspector-reload.html
+new file mode 100644
+--- /dev/null
++++ b/toolkit/devtools/server/tests/mochitest/test_inspector-reload.html
+@@ -0,0 +1,83 @@
++<!DOCTYPE HTML>
++<html>
++<!--
++https://bugzilla.mozilla.org/show_bug.cgi?id=
++-->
++<head>
++ <meta charset="utf-8">
++ <title>Test for Bug </title>
++
++ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
++ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
++ <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
++ <script type="application/javascript;version=1.8">
++Components.utils.import("resource://gre/modules/devtools/Loader.jsm");
++const Ci = Components.interfaces;
++const promise = devtools.require("sdk/core/promise");
++const inspector = devtools.require("devtools/server/actors/inspector");
++
++window.onload = function() {
++ SimpleTest.waitForExplicitFinish();
++ runNextTest();
++}
++
++var gInspectee = null;
++var gClient = null;
++var gWalker = null;
++
++addTest(function setup() {
++ let url = document.getElementById("inspectorContent").href;
++ attachURL(url, function(err, client, tab, doc) {
++ gInspectee = doc;
++ let {InspectorFront} = devtools.require("devtools/server/actors/inspector");
++ let inspector = InspectorFront(client, tab);
++ promiseDone(inspector.getWalker().then(walker => {
++ ok(walker, "getWalker() should return an actor.");
++ gClient = client;
++ gWalker = walker;
++ return inspector.getWalker();
++ }).then(walker => {
++ dump(walker.actorID + "\n");
++ ok(walker === gWalker, "getWalker() twice should return the same walker.");
++ }).then(runNextTest));
++ });
++});
++
++addTest(function testReload() {
++ let nodeFront;
++ let oldRootID = gWalker.rootNode.actorID;
++ // Load a node to populate the tree a bit.
++ promiseDone(gWalker.querySelector(gWalker.rootNode, "#a").then(front => {
++ gInspectee.defaultView.location.reload();
++ return waitForMutation(gWalker, isNewRoot);
++ }).then(() => {
++ ok(gWalker.rootNode.actorID != oldRootID, "Root node should have changed.");
++ }).then(() => {
++ // Make sure we can still access the document
++ return gWalker.querySelector(gWalker.rootNode, "#a");
++ }).then(front => {
++ ok(front.actorID, "Got a new actor ID");
++ }).then(runNextTest));
++});
++
++addTest(function cleanup() {
++ delete gWalker;
++ delete gInspectee;
++ delete gClient;
++ runNextTest();
++});
++
++
++ </script>
++</head>
++<body>
++<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
++<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a>
++<p id="display"></p>
++<div id="content" style="display: none">
++
++</div>
++<pre id="test">
++</pre>
++</body>
++</html>
--- a/warning-fixes.diff
+++ b/warning-fixes.diff
@@ -1,14 +1,14 @@
# HG changeset patch
# User Dave Camp <dcamp@mozilla.com>
# Date 1371477174 25200
# Mon Jun 17 06:52:54 2013 -0700
-# Node ID 193c735dba54ac6efcd67cbd39e33b37d3115fbe
-# Parent aad4f3eb5b6c8fee5ab2e94c17c784d4ac9bcc7d
+# Node ID 913ee6f167d93faa1014ae436f9018909d26e094
+# Parent db733687ce263d6c9683464df3ca65d632d52687
imported patch warning-fixes.diff
diff --git a/browser/devtools/shared/inplace-editor.js b/browser/devtools/shared/inplace-editor.js
--- a/browser/devtools/shared/inplace-editor.js
+++ b/browser/devtools/shared/inplace-editor.js
@@ -398,17 +398,17 @@ InplaceEditor.prototype = {
--selStart;
}
--- a/window-targets.diff
+++ b/window-targets.diff
@@ -1,14 +1,14 @@
# HG changeset patch
# User Dave Camp <dcamp@mozilla.com>
# Date 1372178155 25200
# Tue Jun 25 09:35:55 2013 -0700
-# Node ID 2f0bf3ecb8b591aa233d7685323ec441ef1fc089
-# Parent e4edef1b0ea25d1cee3c49716ca00c7673377dd3
+# Node ID c91491dba5ec6cb90c3024bac1bb36678c2d18cd
+# Parent 496a475072d70e5f9fafc8d03dfd3d627ece92c9
imported patch window-targets.diff
* * *
imported patch custom-windows.diff
diff --git a/browser/devtools/framework/target.js b/browser/devtools/framework/target.js
--- a/browser/devtools/framework/target.js
+++ b/browser/devtools/framework/target.js
@@ -77,17 +77,19 @@ exports.TargetFactory = {
@@ -201,17 +201,17 @@ diff --git a/browser/devtools/framework/
attachTab();
} else {
// Remote chrome debugging doesn't need anything at this point.
this._remote.resolve(null);
}
diff --git a/toolkit/devtools/server/actors/inspector.js b/toolkit/devtools/server/actors/inspector.js
--- a/toolkit/devtools/server/actors/inspector.js
+++ b/toolkit/devtools/server/actors/inspector.js
-@@ -695,17 +695,17 @@ let traversalMethod = {
+@@ -704,17 +704,17 @@ let traversalMethod = {
*/
var ProgressListener = Class({
extends: Unknown,
interfaces: ["nsIWebProgressListener", "nsISupportsWeakReference"],
initialize: function(webProgress) {
Unknown.prototype.initialize.call(this);
this.webProgress = webProgress;
@@ -220,17 +220,17 @@ diff --git a/toolkit/devtools/server/act
},
destroy: function() {
this.webProgress.removeProgressListener(this);
},
onStateChange: makeInfallible(function stateChange(progress, request, flag, status) {
let isWindow = flag & Ci.nsIWebProgressListener.STATE_IS_WINDOW;
-@@ -2034,20 +2034,27 @@ var InspectorActor = protocol.ActorClass
+@@ -2061,20 +2061,27 @@ var InspectorActor = protocol.ActorClass
return this._walkerPromise;
}
let deferred = promise.defer();
this._walkerPromise = deferred.promise;
let window = this.window;