Web Inspector: track HTML revisions when editing DOM and / or upon free flow edits.
authorpfeldman@chromium.org <pfeldman@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 18 Jan 2012 12:52:46 +0000 (12:52 +0000)
committerpfeldman@chromium.org <pfeldman@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 18 Jan 2012 12:52:46 +0000 (12:52 +0000)
https://bugs.webkit.org/show_bug.cgi?id=76457

Reviewed by Yury Semikhatsky.

Source/WebCore:

Test: inspector/elements/set-html-via-resource.html

* inspector/DOMEditor.cpp:
(WebCore::DOMEditor::patchNode):
* inspector/InspectorDOMAgent.cpp:
(WebCore::InspectorDOMAgent::getOuterHTML):
(WebCore::InspectorDOMAgent::setOuterHTML):
(WebCore::InspectorDOMAgent::buildObjectForNode):
* inspector/front-end/DOMAgent.js:
(WebInspector.DOMNode):
(WebInspector.DOMNode.prototype.setNodeName):
(WebInspector.DOMNode.prototype.setNodeValue):
(WebInspector.DOMNode.prototype.setAttribute):
(WebInspector.DOMNode.prototype.setAttributeValue):
(WebInspector.DOMNode.prototype.removeAttribute):
(WebInspector.DOMNode.prototype.getChildNodes.mycallback):
(WebInspector.DOMNode.prototype.getChildNodes):
(WebInspector.DOMNode.prototype.setOuterHTML):
(WebInspector.DOMNode.prototype.removeNode):
(WebInspector.DOMNode.prototype.moveTo):
(WebInspector.DOMDocument):
(WebInspector.DOMAgent):
(WebInspector.DOMAgent.prototype._setDocument):
(WebInspector.DOMAgent.prototype._buildHighlightConfig):
(WebInspector.DOMAgent.prototype._markRevision):
(WebInspector.DOMAgent.prototype._captureDOM.callback):
(WebInspector.DOMAgent.prototype._captureDOM):
(WebInspector.DOMModelResourceBinding.prototype.setContent):
(WebInspector.DOMModelResourceBinding.prototype.setContent.setOuterHTML):
(WebInspector.DOMModelResourceBinding.prototype.setContent.withDocument):
* inspector/front-end/ElementsTreeOutline.js:
(WebInspector.ElementsTreeElement.prototype._attributeEditingCommitted):

LayoutTests:

* inspector/elements/resources/set-outer-html-body-iframe.html:
* inspector/elements/set-html-via-resource-expected.txt: Added.
* inspector/elements/set-html-via-resource.html: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@105262 268f45cc-cd09-0410-ab3c-d52691b4dbfc

12 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/inspector/inspector-test.js
LayoutTests/inspector/elements/edit-dom-actions-expected.txt
LayoutTests/inspector/elements/set-html-via-resource-expected.txt [new file with mode: 0644]
LayoutTests/inspector/elements/set-html-via-resource.html [new file with mode: 0644]
LayoutTests/inspector/elements/set-outer-html-test.js
Source/WebCore/ChangeLog
Source/WebCore/inspector/DOMEditor.cpp
Source/WebCore/inspector/InspectorDOMAgent.cpp
Source/WebCore/inspector/front-end/DOMAgent.js
Source/WebCore/inspector/front-end/ElementsTreeOutline.js
Source/WebCore/inspector/front-end/Settings.js

index b781b4c..98a8027 100644 (file)
@@ -1,3 +1,14 @@
+2012-01-17  Pavel Feldman  <pfeldman@google.com>
+
+        Web Inspector: track HTML revisions when editing DOM and / or upon free flow edits.
+        https://bugs.webkit.org/show_bug.cgi?id=76457
+
+        Reviewed by Yury Semikhatsky.
+
+        * inspector/elements/resources/set-outer-html-body-iframe.html:
+        * inspector/elements/set-html-via-resource-expected.txt: Added.
+        * inspector/elements/set-html-via-resource.html: Added.
+
 2012-01-18  Philippe Normand  <pnormand@igalia.com>
 
         Unreviewed, GTK rebaseline after r105247 and r105253.
index f09d866..2da353d 100644 (file)
@@ -490,6 +490,7 @@ function output(text)
 
         outputElement = document.createElement("div");
         outputElement.className = "output";
+        outputElement.id = "output";
         outputElement.style.whiteSpace = "pre";
         intermediate2.appendChild(outputElement);
     }
index 0a5b891..3e8e8f7 100644 (file)
@@ -23,7 +23,7 @@ Running: testSetNodeName
   </div>
 ==== after ====
 - <div id="testSetNodeName">
-      <span id="node-to-set-name" ></span>
+      <span id="node-to-set-name"></span>
   </div>
 
 Running: testSetNodeNameInput
@@ -33,7 +33,7 @@ Running: testSetNodeNameInput
   </div>
 ==== after ====
 - <div id="testSetNodeNameInput">
-      <input id="node-to-set-name-input" >
+      <input id="node-to-set-name-input">
   </div>
 
 Running: testSetNodeValue
diff --git a/LayoutTests/inspector/elements/set-html-via-resource-expected.txt b/LayoutTests/inspector/elements/set-html-via-resource-expected.txt
new file mode 100644 (file)
index 0000000..0d00bfe
--- /dev/null
@@ -0,0 +1,5 @@
+Tests that the DOM content can be set via editing resource content.
+
+
+<html><head></head><body>New iframe content</body></html>
+
diff --git a/LayoutTests/inspector/elements/set-html-via-resource.html b/LayoutTests/inspector/elements/set-html-via-resource.html
new file mode 100644 (file)
index 0000000..b632e7d
--- /dev/null
@@ -0,0 +1,51 @@
+<html>
+<head>
+
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../http/tests/inspector/elements-test.js"></script>
+<script src="../../http/tests/inspector/resources-test.js"></script>
+
+<script>
+
+function test()
+{
+    WebInspector.experimentsSettings.freeFlowDOMEditing.enableForTest();
+    new WebInspector.DOMModelResourceBinding(WebInspector.domAgent);
+
+    InspectorTest.runAfterResourcesAreFinished(["set-html-via-resource.html", "set-html-via-resource-iframe.html"], step1);
+
+    function step1()
+    {
+        WebInspector.resourceTreeModel.forAllResources(formatter);
+
+        function formatter(resource)
+        {
+            if (resource.url.indexOf("set-html-via-resource-iframe.html") !== -1)
+                resource.setContent("<body>New iframe content</body>", false, step2.bind(this, resource.url));
+        }
+    }
+
+    function step2(url)
+    {
+        var doc = WebInspector.domAgent._documentURLToDocument[url];
+        DOMAgent.getOuterHTML(doc.id, step3);
+    }
+
+    function step3(error, text)
+    {
+        InspectorTest.addResult(text);
+        InspectorTest.completeTest();
+    }
+}
+</script>
+</head>
+
+<body>
+<p>
+Tests that the DOM content can be set via editing resource content.
+</p>
+
+<iframe src="resources/set-html-via-resource-iframe.html" onload="runTest()"></iframe>
+
+</body>
+</html>
index 38ff1c7..e1db1e3 100644 (file)
@@ -1,6 +1,7 @@
 var initialize_SetOuterHTMLTest = function() {
 
 InspectorTest.events = [];
+InspectorTest.containerId;
 
 InspectorTest.setUpTestSuite = function(next)
 {
@@ -33,6 +34,11 @@ InspectorTest.setUpTestSuite = function(next)
 InspectorTest.recordEvent = function(eventName, event)
 {
     var node = event.data.node || event.data;
+    var parent = event.data.parent;
+    for (var currentNode = parent || node; currentNode; currentNode = currentNode.parentNode) {
+        if (currentNode.getAttribute("id") === "output")
+            return;
+    }
     InspectorTest.events.push("Event " + eventName + ": " + node.nodeName());
 }
 
index 41fdad5..2334491 100644 (file)
@@ -1,3 +1,43 @@
+2012-01-17  Pavel Feldman  <pfeldman@google.com>
+
+        Web Inspector: track HTML revisions when editing DOM and / or upon free flow edits.
+        https://bugs.webkit.org/show_bug.cgi?id=76457
+
+        Reviewed by Yury Semikhatsky.
+
+        Test: inspector/elements/set-html-via-resource.html
+
+        * inspector/DOMEditor.cpp:
+        (WebCore::DOMEditor::patchNode):
+        * inspector/InspectorDOMAgent.cpp:
+        (WebCore::InspectorDOMAgent::getOuterHTML):
+        (WebCore::InspectorDOMAgent::setOuterHTML):
+        (WebCore::InspectorDOMAgent::buildObjectForNode):
+        * inspector/front-end/DOMAgent.js:
+        (WebInspector.DOMNode):
+        (WebInspector.DOMNode.prototype.setNodeName):
+        (WebInspector.DOMNode.prototype.setNodeValue):
+        (WebInspector.DOMNode.prototype.setAttribute):
+        (WebInspector.DOMNode.prototype.setAttributeValue):
+        (WebInspector.DOMNode.prototype.removeAttribute):
+        (WebInspector.DOMNode.prototype.getChildNodes.mycallback):
+        (WebInspector.DOMNode.prototype.getChildNodes):
+        (WebInspector.DOMNode.prototype.setOuterHTML):
+        (WebInspector.DOMNode.prototype.removeNode):
+        (WebInspector.DOMNode.prototype.moveTo):
+        (WebInspector.DOMDocument):
+        (WebInspector.DOMAgent):
+        (WebInspector.DOMAgent.prototype._setDocument):
+        (WebInspector.DOMAgent.prototype._buildHighlightConfig):
+        (WebInspector.DOMAgent.prototype._markRevision):
+        (WebInspector.DOMAgent.prototype._captureDOM.callback):
+        (WebInspector.DOMAgent.prototype._captureDOM):
+        (WebInspector.DOMModelResourceBinding.prototype.setContent):
+        (WebInspector.DOMModelResourceBinding.prototype.setContent.setOuterHTML):
+        (WebInspector.DOMModelResourceBinding.prototype.setContent.withDocument):
+        * inspector/front-end/ElementsTreeOutline.js:
+        (WebInspector.ElementsTreeElement.prototype._attributeEditingCommitted):
+
 2012-01-18  Andrey Kosyakov  <caseq@chromium.org>
 
         Web Inspector: Popover does not disappear, causes debugger failure.
index a472cfd..3c9d89b 100644 (file)
@@ -93,7 +93,7 @@ void DOMEditor::patchDocument(const String& markup)
 Node* DOMEditor::patchNode(Node* node, const String& markup, ExceptionCode& ec)
 {
     // Don't parse <html> as a fragment.
-    if (node == node->ownerDocument()->documentElement()) {
+    if (node->isDocumentNode() || (node->parentNode() && node->parentNode()->isDocumentNode())) {
         patchDocument(markup);
         return 0;
     }
index e8efa90..d9941dd 100644 (file)
@@ -616,22 +616,7 @@ void InspectorDOMAgent::getOuterHTML(ErrorString* errorString, int nodeId, WTF::
     if (!node)
         return;
 
-    if (node->isHTMLElement()) {
-        *outerHTML = static_cast<HTMLElement*>(node)->outerHTML();
-        return;
-    }
-
-    if (node->isCommentNode()) {
-        *outerHTML = "<!--" + node->nodeValue() + "-->";
-        return;
-    }
-
-    if (node->isTextNode()) {
-        *outerHTML = node->nodeValue();
-        return;
-    }
-
-    *errorString = "Only HTMLElements, Comments, and Text nodes are supported";
+    *outerHTML = createMarkup(node);
 }
 
 void InspectorDOMAgent::setOuterHTML(ErrorString* errorString, int nodeId, const String& outerHTML)
@@ -646,8 +631,8 @@ void InspectorDOMAgent::setOuterHTML(ErrorString* errorString, int nodeId, const
     if (!node)
         return;
 
-    Document* document = node->ownerDocument();
-    if (!document->isHTMLDocument()) {
+    Document* document = node->isDocumentNode() ? static_cast<Document*>(node) : node->ownerDocument();
+    if (!document || !document->isHTMLDocument()) {
         *errorString = "Not an HTML document";
         return;
     }
@@ -1136,33 +1121,33 @@ PassRefPtr<InspectorObject> InspectorDOMAgent::buildObjectForNode(Node* node, in
         .setLocalName(localName)
         .setNodeValue(nodeValue);
 
-    if (node->nodeType() == Node::ELEMENT_NODE || node->nodeType() == Node::DOCUMENT_NODE || node->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) {
+    if (node->isContainerNode()) {
         int nodeCount = innerChildNodeCount(node);
         value->setChildNodeCount(nodeCount);
         RefPtr<InspectorArray> children = buildArrayForContainerChildren(node, depth, nodesMap);
         if (children->length() > 0)
             value->setArray("children", children.release());
+    }
 
-        if (node->nodeType() == Node::ELEMENT_NODE) {
-            Element* element = static_cast<Element*>(node);
-            value->setArray("attributes", buildArrayForElementAttributes(element));
-            if (node->isFrameOwnerElement()) {
-                HTMLFrameOwnerElement* frameOwner = static_cast<HTMLFrameOwnerElement*>(node);
-                Document* doc = frameOwner->contentDocument();
-                if (doc)
-                    value->setContentDocument(buildObjectForNode(doc, 0, nodesMap));
-            }
-        } else if (node->nodeType() == Node::DOCUMENT_NODE) {
-            Document* document = static_cast<Document*>(node);
-            value->setDocumentURL(documentURLString(document));
-            value->setXmlVersion(document->xmlVersion());
+    if (node->isElementNode()) {
+        Element* element = static_cast<Element*>(node);
+        value->setArray("attributes", buildArrayForElementAttributes(element));
+        if (node->isFrameOwnerElement()) {
+            HTMLFrameOwnerElement* frameOwner = static_cast<HTMLFrameOwnerElement*>(node);
+            Document* doc = frameOwner->contentDocument();
+            if (doc)
+                value->setContentDocument(buildObjectForNode(doc, 0, nodesMap));
         }
+    } else if (node->isDocumentNode()) {
+        Document* document = static_cast<Document*>(node);
+        value->setDocumentURL(documentURLString(document));
+        value->setXmlVersion(document->xmlVersion());
     } else if (node->nodeType() == Node::DOCUMENT_TYPE_NODE) {
         DocumentType* docType = static_cast<DocumentType*>(node);
         value->setPublicId(docType->publicId());
         value->setSystemId(docType->systemId());
         value->setInternalSubset(docType->internalSubset());
-    } else if (node->nodeType() == Node::ATTRIBUTE_NODE) {
+    } else if (node->isAttributeNode()) {
         Attr* attribute = static_cast<Attr*>(node);
         value->setName(attribute->name());
         value->setValue(attribute->value());
index 88da82e..9f0f826 100644 (file)
@@ -64,9 +64,13 @@ WebInspector.DOMNode = function(domAgent, doc, payload) {
 
     if (payload.contentDocument) {
         this._contentDocument = new WebInspector.DOMDocument(domAgent, payload.contentDocument);
+        for (var i = 0; i < this._attributes.length; ++i) {
+            // Only bind document to URL when src attribute is set.
+            if (this._attributes[i].name.toLowerCase() === "src")
+                this._domAgent._documentURLToDocument[this._contentDocument.documentURL] = this._contentDocument;
+        }
         this.children = [this._contentDocument];
         this._renumber();
-        this._domAgent._idToDOMNode[this._contentDocument.id] = this._contentDocument;
     }
 
     if (this._nodeType === Node.ELEMENT_NODE) {
@@ -75,15 +79,10 @@ WebInspector.DOMNode = function(domAgent, doc, payload) {
             this.ownerDocument.documentElement = this;
         if (this.ownerDocument && !this.ownerDocument.body && this._nodeName === "BODY")
             this.ownerDocument.body = this;
-        if (payload.documentURL)
-            this.documentURL = payload.documentURL;
     } else if (this._nodeType === Node.DOCUMENT_TYPE_NODE) {
         this.publicId = payload.publicId;
         this.systemId = payload.systemId;
         this.internalSubset = payload.internalSubset;
-    } else if (this._nodeType === Node.DOCUMENT_NODE) {
-        this.documentURL = payload.documentURL;
-        this.xmlVersion = payload.xmlVersion;
     } else if (this._nodeType === Node.ATTRIBUTE_NODE) {
         this.name = payload.name;
         this.value = payload.value;
@@ -133,11 +132,11 @@ WebInspector.DOMNode.prototype = {
 
     /**
      * @param {string} name
-     * @param {function()=} callback
+     * @param {function(?Protocol.Error)=} callback
      */
     setNodeName: function(name, callback)
     {
-        DOMAgent.setNodeName(this.id, name, callback);
+        DOMAgent.setNodeName(this.id, name, WebInspector.domAgent._markRevision(this, callback));
     },
 
     /**
@@ -162,7 +161,7 @@ WebInspector.DOMNode.prototype = {
      */
     setNodeValue: function(value, callback)
     {
-        DOMAgent.setNodeValue(this.id, value, callback);
+        DOMAgent.setNodeValue(this.id, value, WebInspector.domAgent._markRevision(this, callback));
     },
 
     /**
@@ -178,21 +177,21 @@ WebInspector.DOMNode.prototype = {
     /**
      * @param {string} name
      * @param {string} text
-     * @param {function()=} callback
+     * @param {function(?Protocol.Error)=} callback
      */
     setAttribute: function(name, text, callback)
     {
-        DOMAgent.setAttributesAsText(this.id, text, name, callback);
+        DOMAgent.setAttributesAsText(this.id, text, name, WebInspector.domAgent._markRevision(this, callback));
     },
 
     /**
      * @param {string} name
      * @param {string} value
-     * @param {function()=} callback
+     * @param {function(?Protocol.Error)=} callback
      */
     setAttributeValue: function(name, value, callback)
     {
-        DOMAgent.setAttributeValue(this.id, name, value, callback);
+        DOMAgent.setAttributeValue(this.id, name, value, WebInspector.domAgent._markRevision(this, callback));
     },
 
     /**
@@ -205,7 +204,7 @@ WebInspector.DOMNode.prototype = {
 
     /**
      * @param {string} name
-     * @param {function()=} callback
+     * @param {function(?Protocol.Error)=} callback
      */
     removeAttribute: function(name, callback)
     {
@@ -221,8 +220,7 @@ WebInspector.DOMNode.prototype = {
                 }
             }
 
-            if (callback)
-                callback();
+            WebInspector.domAgent._markRevision(this, callback)(error);
         }
         DOMAgent.removeAttribute(this.id, name, mycallback.bind(this));
     },
@@ -242,7 +240,8 @@ WebInspector.DOMNode.prototype = {
          * @this {WebInspector.DOMNode}
          * @param {?Protocol.Error} error
          */
-        function mycallback(error) {
+        function mycallback(error)
+        {
             if (!error && callback)
                 callback(this.children);
         }
@@ -264,7 +263,7 @@ WebInspector.DOMNode.prototype = {
      */
     setOuterHTML: function(html, callback)
     {
-        DOMAgent.setOuterHTML(this.id, html, callback);
+        DOMAgent.setOuterHTML(this.id, html, WebInspector.domAgent._markRevision(this, callback));
     },
 
     /**
@@ -272,7 +271,7 @@ WebInspector.DOMNode.prototype = {
      */
     removeNode: function(callback)
     {
-        DOMAgent.removeNode(this.id, callback);
+        DOMAgent.removeNode(this.id, WebInspector.domAgent._markRevision(this, callback));
     },
 
     /**
@@ -480,7 +479,7 @@ WebInspector.DOMNode.prototype = {
      */
     moveTo: function(targetNode, anchorNode, callback)
     {
-        DOMAgent.moveTo(this.id, targetNode.id, anchorNode ? anchorNode.id : undefined, callback);
+        DOMAgent.moveTo(this.id, targetNode.id, anchorNode ? anchorNode.id : undefined, WebInspector.domAgent._markRevision(this, callback));
     },
 
     /**
@@ -501,6 +500,9 @@ WebInspector.DOMNode.prototype = {
 WebInspector.DOMDocument = function(domAgent, payload)
 {
     WebInspector.DOMNode.call(this, domAgent, this, payload);
+    this.documentURL = payload.documentURL;
+    this.xmlVersion = payload.xmlVersion;
+    domAgent._idToDOMNode[this.id] = this;
 
     /**
      * @type {string} Document nodes always have documentURL
@@ -518,6 +520,7 @@ WebInspector.DOMDocument.prototype.__proto__ = WebInspector.DOMNode.prototype;
 WebInspector.DOMAgent = function() {
     /** @type {Object|undefined} */
     this._idToDOMNode = {};
+    this._documentURLToDocument = {};
     this._document = null;
     this._attributeLoadNodeIds = {};
     InspectorBackend.registerDOMDispatcher(new WebInspector.DOMDispatcher(this));
@@ -733,9 +736,10 @@ WebInspector.DOMAgent.prototype = {
     _setDocument: function(payload)
     {
         this._idToDOMNode = {};
+        this._documentURLToDocument = {};
         if (payload && "nodeId" in payload) {
             this._document = new WebInspector.DOMDocument(this, payload);
-            this._idToDOMNode[payload.nodeId] = this._document;
+            this._documentURLToDocument[this._document.documentURL] = this._document;
             if (this._document.children)
                 this._bindNodes(this._document.children);
         } else
@@ -969,6 +973,56 @@ WebInspector.DOMAgent.prototype = {
             highlightConfig.marginColor = WebInspector.Color.PageHighlight.Margin.toProtocolRGBA();
 
         return highlightConfig;
+    },
+
+    /**
+     * @param {WebInspector.DOMNode} node
+     * @param {function(?Protocol.Error)=} callback
+     * @return {function(?Protocol.Error)}
+     */
+    _markRevision: function(node, callback)
+    {
+        function wrapperFunction(error)
+        {
+            if (callback)
+                callback(error);
+            if (error || !WebInspector.experimentsSettings.freeFlowDOMEditing.isEnabled())
+                return;
+            if (this._captureDOMTimer)
+               clearTimeout(this._captureDOMTimer);
+            this._captureDOMTimer = setTimeout(this._captureDOM.bind(this, node), 500);
+        }
+        return wrapperFunction.bind(this);
+    },
+
+    /**
+     * @param {WebInspector.DOMNode} node
+     */
+    _captureDOM: function(node)
+    {
+        delete this._captureDOMTimer;
+        if (!node.ownerDocument)
+            return;
+
+        function callback(error, text)
+        {
+            if (error) {
+                console.error(error);
+                return;
+            }
+
+            var url = node.ownerDocument.documentURL;
+            if (!url)
+                return;
+
+            var resource = WebInspector.resourceForURL(url);
+            if (!resource)
+                return;
+
+            resource.addRevision(text);
+        }
+        DOMAgent.getOuterHTML(node.ownerDocument.id, callback);
+        
     }
 }
 
@@ -1082,7 +1136,37 @@ WebInspector.DOMModelResourceBinding = function(domAgent)
 WebInspector.DOMModelResourceBinding.prototype = {
     setContent: function(resource, content, majorChange, userCallback)
     {
-        DOMAgent.setOuterHTML(0, content, userCallback);
+        function callbackWrapper(error)
+        {
+            if (majorChange)
+                resource.addRevision(content);
+            if (userCallback)
+                userCallback(error);
+        }
+
+        function setOuterHTML(reportError)
+        {
+            var doc = this._domAgent._documentURLToDocument[resource.url];
+            if (doc) {
+                DOMAgent.setOuterHTML(doc.id, content, callbackWrapper.bind(this));
+                return true;
+            }
+            if (reportError)
+                callback("No document with given URL found");
+            return false;
+        }
+    
+        this._domAgent.requestDocument(withDocument.bind(this));
+
+        function withDocument(doc)
+        {
+            if (setOuterHTML.call(this, false))
+                return;
+
+            // We are editing one of the iframes, but it has not yet been loaded in the DOM tree.
+            // Load all iframe nodes here.
+            DOMAgent.querySelectorAll(doc.id, "iframe[src]", setOuterHTML.bind(this, true));
+        }
     },
 
     canSetContent: function()
index bf0ebda..8ce014d 100644 (file)
@@ -1320,7 +1320,10 @@ WebInspector.ElementsTreeElement.prototype = {
             }
         }
 
-        this.representedObject.setAttribute(attributeName, newText, moveToNextAttributeIfNeeded.bind(this));
+        if (oldText !== newText)
+            this.representedObject.setAttribute(attributeName, newText, moveToNextAttributeIfNeeded.bind(this));
+        else
+            moveToNextAttributeIfNeeded.call(this);
     },
 
     _tagNameEditingCommitted: function(element, newText, oldText, tagName, moveDirection)
index 8180518..bc425ec 100644 (file)
@@ -164,6 +164,7 @@ WebInspector.ExperimentsSettings = function()
 {
     this._setting = WebInspector.settings.createSetting("experiments", {});
     this._experiments = [];
+    this._enabledForTest = {};
     
     // Add currently running experiments here.
     // FIXME: Move out from experiments once navigator is production-ready.
@@ -209,6 +210,9 @@ WebInspector.ExperimentsSettings.prototype = {
      */
     isEnabled: function(experimentName)
     {
+        if (this._enabledForTest[experimentName])
+            return true;
+
         if (!this.experimentsEnabled)
             return false;
         
@@ -226,7 +230,15 @@ WebInspector.ExperimentsSettings.prototype = {
         experimentsSetting[experimentName] = enabled;
         this._setting.set(experimentsSetting);
     },
-    
+
+    /**
+     * @param {string} experimentName
+     */
+    _enableForTest: function(experimentName)
+    {
+        this._enabledForTest[experimentName] = true;
+    },
+
     _cleanUpSetting: function()
     {
         var experimentsSetting = this._setting.get();
@@ -284,6 +296,11 @@ WebInspector.Experiment.prototype = {
     setEnabled: function(enabled)
     {
         return this._experimentsSettings.setEnabled(this._name, enabled);
+    },
+
+    enableForTest: function()
+    {
+        this._experimentsSettings._enableForTest(this._name);
     }
 }