--- a/inspector-delete-node.diff
+++ b/inspector-delete-node.diff
@@ -1,28 +1,32 @@
# HG changeset patch
# User Dave Camp <dcamp@mozilla.com>
# Date 1371477173 25200
# Mon Jun 17 06:52:53 2013 -0700
# Node ID 877aba69dfd4ad1f638be4691802b721d6ee54c0
-# Parent 2ccc31df1f64ef58ad9db4f76a2a5caa5507d141
+# Parent 0346910d331ca63edf112851e7efe2213c436bf3
imported patch inspector-delete-node.diff
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
-@@ -1376,16 +1376,48 @@ var WalkerActor = protocol.ActorClass({
- request: {
+@@ -1377,16 +1377,56 @@ var WalkerActor = protocol.ActorClass({
node: Arg(0, "domnode")
},
response: {
value: RetVal("longstring")
}
}),
+ /**
++ * Removes a node from its parent node.
++ *
++ * @returns The node's nextSibling before it was removed.
++ */
+ removeNode: method(function(node) {
+ if ((node.rawNode.ownerDocument &&
+ node.rawNode.ownerDocument.documentElement === this.rawNode) ||
+ node.rawNode.nodeType === Ci.nsIDOMNode.DOCUMENT_NODE) {
+ throw Error("Cannot remove document or document elements.");
+ }
+ let nextSibling = this.nextSibling(node);
+ if (node.rawNode.parentNode) {
@@ -34,27 +38,282 @@ diff --git a/toolkit/devtools/server/act
+ request: {
+ node: Arg(0, "domnode")
+ },
+ response: {
+ nextSibling: RetVal("domnode", { optional: true })
+ }
+ }),
+
++ /**
++ * Insert a node into the DOM.
++ */
+ insertBefore: method(function(node, parent, sibling) {
+ parent.rawNode.insertBefore(node.rawNode, sibling ? sibling.rawNode : null);
+ }, {
+ request: {
+ node: Arg(0, "domnode"),
+ parent: Arg(1, "domnode"),
+ sibling: Arg(2, "domnode", { optional: true })
+ },
+ response: {}
+ }),
+
- /**
++ /**
* Get any pending mutation records. Must be called by the client after
* the `new-mutations` notification is received. Returns an array of
* mutation records.
*
* Mutation records have a basic structure:
*
* {
+ * type: attributes|characterData|childList,
+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
+@@ -11,21 +11,23 @@ relativesrcdir = @relativesrcdir@
+
+ include $(DEPTH)/config/autoconf.mk
+
+ MOCHITEST_CHROME_FILES = \
+ inspector-helpers.js \
+ inspector-traversal-data.html \
+ test_inspector-changeattrs.html \
+ 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-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
+@@ -150,16 +150,18 @@ function ownershipTreeSize(tree) {
+ return size;
+ }
+
+ function assertOwnershipTrees(walker) {
+ let serverTree = serverOwnershipTree(walker);
+ let clientTree = clientOwnershipTree(walker);
+ is(JSON.stringify(clientTree, null, ' '), JSON.stringify(serverTree, null, ' '), "Server and client ownership trees should match.");
+
++ dump("ownership tree: " + JSON.stringify(clientTree, null, ' ') + "\n");
++
+ return ownershipTreeSize(clientTree.root);
+ }
+
+ // Verify that an actorID is inaccessible both from the client library and the server.
+ function checkMissing(client, actorID) {
+ let deferred = Promise.defer();
+ let front = client.getActor(actorID);
+ ok(!front, "Front shouldn't be accessible from the client for actorID: " + actorID);
+diff --git a/toolkit/devtools/server/tests/mochitest/test_inspector-insert.html b/toolkit/devtools/server/tests/mochitest/test_inspector-insert.html
+new file mode 100644
+--- /dev/null
++++ b/toolkit/devtools/server/tests/mochitest/test_inspector-insert.html
+@@ -0,0 +1,96 @@
++<!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 Promise = devtools.require("sdk/core/promise");
++const inspector = devtools.require("devtools/server/actors/inspector");
++
++window.onload = function() {
++ SimpleTest.waitForExplicitFinish();
++ runNextTest();
++}
++
++var gWalker = null;
++var gClient = null;
++
++function assertOwnership() {
++ return assertOwnershipTrees(gWalker);
++}
++
++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;
++ }).then(runNextTest));
++ });
++});
++
++addTest(function testRearrange() {
++ let longlist = null;
++ let nodeA = null;
++ let nextNode = null;
++
++ promiseDone(gWalker.querySelector(gWalker.rootNode, "#longlist").then(listFront => {
++ longlist = listFront;
++ }).then(() => {
++ return gWalker.children(longlist);
++ }).then(response => {
++ nodeA = response.nodes[0];
++ is(nodeA.id, "a", "Got the expected node.");
++ // Move nodeA to the end of the list.
++ return gWalker.insertBefore(nodeA, longlist, null);
++ }).then(() => {
++ ok(!gInspectee.querySelector("#a").nextSibling, "a should now be at the end of the list.");
++ return gWalker.children(longlist);
++ }).then(response => {
++ is(nodeA, response.nodes[response.nodes.length - 1], "a should now be the last returned child.");
++ // Now move it to the middle of the list.
++ nextNode = response.nodes[13];
++ return gWalker.insertBefore(nodeA, longlist, nextNode);
++ }).then(response => {
++ let sibling = inspector._documentWalker(gInspectee.querySelector("#a")).nextSibling();
++ is(sibling, nextNode.rawNode(), "Node should match the expected next node.");
++ return gWalker.children(longlist);
++ }).then(response => {
++ is(nodeA, response.nodes[13], "a should be where we expect it.");
++ is(nextNode, response.nodes[14], "next node should be where we expect it.");
++ }).then(runNextTest));
++});
++
++addTest(function cleanup() {
++ delete gWalker;
++ 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>
+diff --git a/toolkit/devtools/server/tests/mochitest/test_inspector-remove.html b/toolkit/devtools/server/tests/mochitest/test_inspector-remove.html
+new file mode 100644
+--- /dev/null
++++ b/toolkit/devtools/server/tests/mochitest/test_inspector-remove.html
+@@ -0,0 +1,96 @@
++<!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 Promise = devtools.require("sdk/core/promise");
++const inspector = devtools.require("devtools/server/actors/inspector");
++
++window.onload = function() {
++ SimpleTest.waitForExplicitFinish();
++ runNextTest();
++}
++
++var gWalker = null;
++var gClient = null;
++
++function assertOwnership() {
++ return assertOwnershipTrees(gWalker);
++}
++
++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;
++ }).then(runNextTest));
++ });
++});
++
++addTest(function testRemoveSubtree() {
++ let originalOwnershipSize = 0;
++ let longlist = null;
++ let longlistID = null;
++
++ let nextSibling = gInspectee.querySelector("#longlist").nextSibling;
++ // Duplicate the walker logic to skip blank nodes...
++ while (nextSibling && nextSibling.nodeType === Components.interfaces.nsIDOMNode.TEXT_NODE && !/[^\s]/.exec(nextSibling.nodeValue)) {
++ nextSibling = nextSibling.nextSibling;
++ }
++
++ promiseDone(gWalker.querySelector(gWalker.rootNode, "#longlist").then(listFront => {
++ longlist = listFront;
++ longlistID = longlist.actorID;
++ }).then(() => {
++ return gWalker.children(longlist);
++ }).then((items)=> {
++ originalOwnershipSize = assertOwnership();
++ ok(originalOwnershipSize > 26, "Should have at least 26 items in our ownership tree");
++ return gWalker.removeNode(longlist);
++ }).then(nextSiblingFront => {
++ is(nextSiblingFront.rawNode(), nextSibling, "Should have returned the next sibling.");
++ return waitForMutation(gWalker, isChildList);
++ }).then(() => {
++ // Our ownership size should now be 26 fewer (we forgot about #longlist + 26 children, but learned about #longlist's next sibling)
++ let newOwnershipSize = assertOwnership();
++ is(newOwnershipSize, originalOwnershipSize - 26, "Ownership tree should have dropped by 27 nodes");
++ // Now verify that some nodes have gone away
++ return checkMissing(gClient, longlistID);
++ }).then(runNextTest));
++});
++
++addTest(function cleanup() {
++ delete gWalker;
++ 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/inspector-remote-delete-node.diff
+++ b/inspector-remote-delete-node.diff
@@ -1,20 +1,20 @@
# HG changeset patch
# User Dave Camp <dcamp@mozilla.com>
# Date 1371477175 25200
# Mon Jun 17 06:52:55 2013 -0700
# Node ID d226c8a4645c57624d721ab57c3b8fb1c98d8822
-# Parent 00aba1b028d43fb8dc1604f6fa3c1df3243feae0
+# Parent 39dfcd31bc12451e6ba0c7eab89fc34e28682c59
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
-@@ -681,18 +681,17 @@ InspectorPanel.prototype = {
+@@ -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
@@ -94,17 +94,17 @@ diff --git a/browser/devtools/markupview
+ 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);
++ this.walker.insertBefore(aNode, parent, sibling);
+ });
+ }).then(null, console.error);
},
/**
* If an editable item is focused, select its container.
*/
_onFocus: function MC__onFocus(aEvent) {