Bug 884390 - Add attribute modification to the inspector actor. r=paul
authorDave Camp <dcamp@mozilla.com>
Mon, 10 Jun 2013 21:18:44 -0700
changeset 147513 c082decf4957429a5bea5565854d98397236b265
parent 147512 e84338c0a9ca6d9a6385153316d9d6de3d6872ef
child 147514 808302fb793e890a03e2621697e9cfddabfd5194
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspaul
bugs884390
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 884390 - Add attribute modification to the inspector actor. r=paul
toolkit/devtools/server/actors/inspector.js
toolkit/devtools/server/tests/mochitest/Makefile.in
toolkit/devtools/server/tests/mochitest/test_inspector-changeattrs.html
toolkit/devtools/server/tests/mochitest/test_inspector-mutations-attr.html
--- a/toolkit/devtools/server/actors/inspector.js
+++ b/toolkit/devtools/server/actors/inspector.js
@@ -202,16 +202,54 @@ var NodeActor = protocol.ActorClass({
    * Set the node's value to a given string.
    */
   setNodeValue: method(function(value) {
     this.rawNode.nodeValue = value;
   }, {
     request: { value: Arg(0) },
     response: {}
   }),
+
+  /**
+   * Modify a node's attributes.  Passed an array of modifications
+   * similar in format to "attributes" mutations.
+   * {
+   *   attributeName: <string>
+   *   attributeNamespace: <optional string>
+   *   newValue: <optional string> - If null or undefined, the attribute
+   *     will be removed.
+   * }
+   *
+   * Returns when the modifications have been made.  Mutations will
+   * be queued for any changes made.
+   */
+  modifyAttributes: method(function(modifications) {
+    let rawNode = this.rawNode;
+    for (let change of modifications) {
+      if (change.newValue == null) {
+        if (change.attributeNamespace) {
+          rawNode.removeAttributeNS(change.attributeNamespace, change.attributeName);
+        } else {
+          rawNode.removeAttribute(change.attributeName);
+        }
+      } else {
+        if (change.attributeNamespace) {
+          rawNode.setAttributeNS(change.attributeNamespace, change.attributeName, change.newValue);
+        } else {
+          rawNode.setAttribute(change.attributeName, change.newValue);
+        }
+      }
+    }
+  }, {
+    request: {
+      modifications: Arg(0, "array:json")
+    },
+    response: {}
+  }),
+
 });
 
 /**
  * Client side of the node actor.
  *
  * Node fronts are strored in a tree that mirrors the DOM tree on the
  * server, but with a few key differences:
  *  - Not all children will be necessary loaded for each node.
@@ -359,16 +397,23 @@ let NodeFront = protocol.FrontClass(Node
       return delayedResolve(new ShortLongString(this.shortValue));
     } else {
       return this._getNodeValue();
     }
   }, {
     impl: "_getNodeValue"
   }),
 
+  /**
+   * Return a new AttributeModificationList for this node.
+   */
+  startModifyingAttributes: function() {
+    return AttributeModificationList(this);
+  },
+
   _cacheAttributes: function() {
     if (typeof(this._attrMap) != "undefined") {
       return;
     }
     this._attrMap = {};
     for (let attr of this.attributes) {
       this._attrMap[attr.name] = attr;
     }
@@ -1802,16 +1847,57 @@ var WalkerFront = exports.WalkerFront = 
       this._orphaned.add(top);
       walkerActor._orphaned.add(this.conn._transport._serverConnection.getActor(top.actorID));
     }
     return returnNode;
   }
 });
 
 /**
+ * Convenience API for building a list of attribute modifications
+ * for the `modifyAttributes` request.
+ */
+var AttributeModificationList = Class({
+  initialize: function(node) {
+    this.node = node;
+    this.modifications = [];
+  },
+
+  apply: function() {
+    let ret = this.node.modifyAttributes(this.modifications);
+    return ret;
+  },
+
+  destroy: function() {
+    this.node = null;
+    this.modification = null;
+  },
+
+  setAttributeNS: function(ns, name, value) {
+    this.modifications.push({
+      attributeNamespace: ns,
+      attributeName: name,
+      newValue: value
+    });
+  },
+
+  setAttribute: function(name, value) {
+    this.setAttributeNS(undefined, name, value);
+  },
+
+  removeAttributeNS: function(ns, name) {
+    this.setAttributeNS(ns, name, undefined);
+  },
+
+  removeAttribute: function(name) {
+    this.setAttributeNS(undefined, name, undefined);
+  }
+})
+
+/**
  * Server side of the inspector actor, which is used to create
  * inspector-related actors, including the walker.
  */
 var InspectorActor = protocol.ActorClass({
   typeName: "inspector",
   initialize: function(conn, tabActor) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this.tabActor = tabActor;
--- a/toolkit/devtools/server/tests/mochitest/Makefile.in
+++ b/toolkit/devtools/server/tests/mochitest/Makefile.in
@@ -9,16 +9,17 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir	= @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_CHROME_FILES	= \
 	inspector-helpers.js \
 	inspector-traversal-data.html \
+	test_inspector-changeattrs.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-retain.html \
 	test_inspector-pseudoclass-lock.html \
 	test_inspector-traversal.html \
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/mochitest/test_inspector-changeattrs.html
@@ -0,0 +1,102 @@
+<!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 gInspectee = null;
+var gClient = null;
+var gWalker = null;
+var checkActorIDs = [];
+
+function assertOwnership() {
+  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 testChangeAttrs() {
+  let attrNode = gInspectee.querySelector("#a");
+  let attrFront;
+  promiseDone(gWalker.querySelector(gWalker.rootNode, "#a").then(front => {
+    attrFront = front;
+    dump("attrFront is: " + attrFront + "\n");
+    // Add a few attributes.
+    let list = attrFront.startModifyingAttributes();
+    list.setAttribute("data-newattr", "newvalue");
+    list.setAttribute("data-newattr2", "newvalue");
+    return list.apply();
+  }).then(() => {
+    // We're only going to test that the change hit the document.
+    // There are other tests that make sure changes are propagated
+    // to the client.
+    is(attrNode.getAttribute("data-newattr"), "newvalue", "Node should have the first new attribute");
+    is(attrNode.getAttribute("data-newattr2"), "newvalue", "Node should have the second new attribute.");
+  }).then(() => {
+    // Change an attribute.
+    let list = attrFront.startModifyingAttributes();
+    list.setAttribute("data-newattr", "changedvalue");
+    return list.apply();
+  }).then(() => {
+    is(attrNode.getAttribute("data-newattr"), "changedvalue", "Node should have the changed first value.");
+    is(attrNode.getAttribute("data-newattr2"), "newvalue", "Second value should remain unchanged.");
+  }).then(() => {
+    let list = attrFront.startModifyingAttributes();
+    list.removeAttribute("data-newattr2");
+    return list.apply();
+  }).then(() => {
+    is(attrNode.getAttribute("data-newattr"), "changedvalue", "Node should have the changed first value.");
+    ok(!attrNode.hasAttribute("data-newattr2"), "Second value should be removed.");
+  }).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/toolkit/devtools/server/tests/mochitest/test_inspector-mutations-attr.html
+++ b/toolkit/devtools/server/tests/mochitest/test_inspector-mutations-attr.html
@@ -45,17 +45,16 @@ addTest(setupAttrTest);
 addTest(testAddAttribute);
 addTest(testChangeAttribute);
 addTest(testRemoveAttribute);
 addTest(setupFrameAttrTest);
 addTest(testAddAttribute);
 addTest(testChangeAttribute);
 addTest(testRemoveAttribute);
 
-
 function setupAttrTest() {
   attrNode = gInspectee.querySelector("#a")
   promiseDone(gWalker.querySelector(gWalker.rootNode, "#a").then(node => {
     attrFront = node;
   }).then(runNextTest));
 }
 
 function setupFrameAttrTest() {