Title: [104586] trunk/Source/WebCore
Revision
104586
Author
[email protected]
Date
2012-01-10 09:01:05 -0800 (Tue, 10 Jan 2012)

Log Message

Web Inspector: add "free flow DOM editing" experiment.
https://bugs.webkit.org/show_bug.cgi?id=75955

This change enables HTML editing from Resources panel experiment.

Reviewed by Yury Semikhatsky.

* CMakeLists.txt:
* English.lproj/localizedStrings.js:
* GNUmakefile.list.am:
* Target.pri:
* WebCore.gypi:
* WebCore.vcproj/WebCore.vcproj:
* WebCore.xcodeproj/project.pbxproj:
* inspector/DOMEditor.cpp: Added.
(WebCore::DOMEditor::DOMEditor):
(WebCore::DOMEditor::~DOMEditor):
(WebCore::DOMEditor::patch):
(WebCore::DOMEditor::NodeDigest::NodeDigest):
(WebCore::DOMEditor::patchElement):
(WebCore::DOMEditor::patchNode):
(WebCore::DOMEditor::patchChildren):
(WebCore::addStringToSHA1):
(WebCore::DOMEditor::createNodeDigest):
* inspector/DOMEditor.h: Added.
* inspector/InspectorDOMAgent.cpp:
(WebCore::InspectorDOMAgent::setOuterHTML):
* inspector/front-end/DOMAgent.js:
(WebInspector.DOMAgent):
(WebInspector.DOMModelResourceBinding):
(WebInspector.DOMModelResourceBinding.prototype.setContent):
(WebInspector.DOMModelResourceBinding.prototype.canSetContent):
* inspector/front-end/Settings.js:
(WebInspector.ExperimentsSettings):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/CMakeLists.txt (104585 => 104586)


--- trunk/Source/WebCore/CMakeLists.txt	2012-01-10 16:54:29 UTC (rev 104585)
+++ trunk/Source/WebCore/CMakeLists.txt	2012-01-10 17:01:05 UTC (rev 104586)
@@ -870,6 +870,7 @@
 
     inspector/ConsoleMessage.cpp
     inspector/ContentSearchUtils.cpp
+    inspector/DOMEditor.cpp
     inspector/DOMNodeHighlighter.cpp
     inspector/IdentifiersFactory.cpp
     inspector/InjectedScript.cpp

Modified: trunk/Source/WebCore/ChangeLog (104585 => 104586)


--- trunk/Source/WebCore/ChangeLog	2012-01-10 16:54:29 UTC (rev 104585)
+++ trunk/Source/WebCore/ChangeLog	2012-01-10 17:01:05 UTC (rev 104586)
@@ -1,3 +1,40 @@
+2012-01-10  Pavel Feldman  <[email protected]>
+
+        Web Inspector: add "free flow DOM editing" experiment.
+        https://bugs.webkit.org/show_bug.cgi?id=75955
+
+        This change enables HTML editing from Resources panel experiment.
+
+        Reviewed by Yury Semikhatsky.
+
+        * CMakeLists.txt:
+        * English.lproj/localizedStrings.js:
+        * GNUmakefile.list.am:
+        * Target.pri:
+        * WebCore.gypi:
+        * WebCore.vcproj/WebCore.vcproj:
+        * WebCore.xcodeproj/project.pbxproj:
+        * inspector/DOMEditor.cpp: Added.
+        (WebCore::DOMEditor::DOMEditor):
+        (WebCore::DOMEditor::~DOMEditor):
+        (WebCore::DOMEditor::patch):
+        (WebCore::DOMEditor::NodeDigest::NodeDigest):
+        (WebCore::DOMEditor::patchElement):
+        (WebCore::DOMEditor::patchNode):
+        (WebCore::DOMEditor::patchChildren):
+        (WebCore::addStringToSHA1):
+        (WebCore::DOMEditor::createNodeDigest):
+        * inspector/DOMEditor.h: Added.
+        * inspector/InspectorDOMAgent.cpp:
+        (WebCore::InspectorDOMAgent::setOuterHTML):
+        * inspector/front-end/DOMAgent.js:
+        (WebInspector.DOMAgent):
+        (WebInspector.DOMModelResourceBinding):
+        (WebInspector.DOMModelResourceBinding.prototype.setContent):
+        (WebInspector.DOMModelResourceBinding.prototype.canSetContent):
+        * inspector/front-end/Settings.js:
+        (WebInspector.ExperimentsSettings):
+
 2012-01-10  Stephen Chenney  <[email protected]>
 
         [Chromium] Shift PathSkia to use Skia's new RawIter

Modified: trunk/Source/WebCore/English.lproj/localizedStrings.js


(Binary files differ)

Modified: trunk/Source/WebCore/GNUmakefile.list.am (104585 => 104586)


--- trunk/Source/WebCore/GNUmakefile.list.am	2012-01-10 16:54:29 UTC (rev 104585)
+++ trunk/Source/WebCore/GNUmakefile.list.am	2012-01-10 17:01:05 UTC (rev 104586)
@@ -2327,6 +2327,8 @@
 	Source/WebCore/inspector/ConsoleMessage.h \
 	Source/WebCore/inspector/ContentSearchUtils.cpp \
 	Source/WebCore/inspector/ContentSearchUtils.h \
+	Source/WebCore/inspector/DOMEditor.cpp \
+	Source/WebCore/inspector/DOMEditor.h \
 	Source/WebCore/inspector/DOMNodeHighlighter.cpp \
 	Source/WebCore/inspector/DOMNodeHighlighter.h \
 	Source/WebCore/inspector/DOMWrapperVisitor.h \

Modified: trunk/Source/WebCore/Target.pri (104585 => 104586)


--- trunk/Source/WebCore/Target.pri	2012-01-10 16:54:29 UTC (rev 104585)
+++ trunk/Source/WebCore/Target.pri	2012-01-10 17:01:05 UTC (rev 104586)
@@ -849,6 +849,7 @@
     html/shadow/TextControlInnerElements.cpp \
     inspector/ConsoleMessage.cpp \
     inspector/ContentSearchUtils.cpp \
+    inspector/DOMEditor.cpp \
     inspector/DOMNodeHighlighter.cpp \
     inspector/IdentifiersFactory.cpp \
     inspector/InjectedScript.cpp \
@@ -1907,6 +1908,7 @@
     html/track/WebVTTTokenizer.h \
     inspector/ConsoleMessage.h \
     inspector/ContentSearchUtils.h \
+    inspector/DOMEditor.h \
     inspector/DOMNodeHighlighter.h \
     inspector/DOMWrapperVisitor.h \
     inspector/IdentifiersFactory.h \

Modified: trunk/Source/WebCore/WebCore.gypi (104585 => 104586)


--- trunk/Source/WebCore/WebCore.gypi	2012-01-10 16:54:29 UTC (rev 104585)
+++ trunk/Source/WebCore/WebCore.gypi	2012-01-10 17:01:05 UTC (rev 104586)
@@ -2730,6 +2730,8 @@
             'inspector/ConsoleMessage.h',
             'inspector/ContentSearchUtils.cpp',
             'inspector/ContentSearchUtils.h',
+            'inspector/DOMEditor.cpp',
+            'inspector/DOMEditor.h',
             'inspector/DOMNodeHighlighter.cpp',
             'inspector/DOMNodeHighlighter.h',
             'inspector/DOMWrapperVisitor.h',

Modified: trunk/Source/WebCore/WebCore.vcproj/WebCore.vcproj (104585 => 104586)


--- trunk/Source/WebCore/WebCore.vcproj/WebCore.vcproj	2012-01-10 16:54:29 UTC (rev 104585)
+++ trunk/Source/WebCore/WebCore.vcproj/WebCore.vcproj	2012-01-10 17:01:05 UTC (rev 104586)
@@ -71130,6 +71130,14 @@
 				>
 			</File>
 			<File
+				RelativePath="..\inspector\DOMEditor.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\inspector\DOMEditor.h"
+				>
+			</File>
+			<File
 				RelativePath="..\inspector\DOMNodeHighlighter.cpp"
 				>
 			</File>

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (104585 => 104586)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2012-01-10 16:54:29 UTC (rev 104585)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2012-01-10 17:01:05 UTC (rev 104586)
@@ -1852,6 +1852,8 @@
 		7AA51B6F1483B61600AD2752 /* InspectorBaseAgent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7AA51B6D1483B61600AD2752 /* InspectorBaseAgent.cpp */; };
 		7AA51DD5148506A900AD2752 /* InspectorMemoryAgent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7AA51DD3148506A900AD2752 /* InspectorMemoryAgent.cpp */; };
 		7AA51DD6148506A900AD2752 /* InspectorMemoryAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AA51DD4148506A900AD2752 /* InspectorMemoryAgent.h */; };
+		7AABA25914BC613300AA9A11 /* DOMEditor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7AABA25714BC613300AA9A11 /* DOMEditor.cpp */; };
+		7AABA25A14BC613300AA9A11 /* DOMEditor.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AABA25814BC613300AA9A11 /* DOMEditor.h */; };
 		7AB0B1C01211A62200A76940 /* InspectorDatabaseAgent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7AB0B1BE1211A62200A76940 /* InspectorDatabaseAgent.cpp */; };
 		7AB0B1C11211A62200A76940 /* InspectorDatabaseAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AB0B1BF1211A62200A76940 /* InspectorDatabaseAgent.h */; };
 		7ADE722610CBBB9B006B3B3A /* ContextMenuProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ADE722510CBBB9B006B3B3A /* ContextMenuProvider.h */; };
@@ -9162,6 +9164,8 @@
 		7AA51B6D1483B61600AD2752 /* InspectorBaseAgent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorBaseAgent.cpp; sourceTree = "<group>"; };
 		7AA51DD3148506A900AD2752 /* InspectorMemoryAgent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorMemoryAgent.cpp; sourceTree = "<group>"; };
 		7AA51DD4148506A900AD2752 /* InspectorMemoryAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorMemoryAgent.h; sourceTree = "<group>"; };
+		7AABA25714BC613300AA9A11 /* DOMEditor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DOMEditor.cpp; sourceTree = "<group>"; };
+		7AABA25814BC613300AA9A11 /* DOMEditor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMEditor.h; sourceTree = "<group>"; };
 		7AB0B1BE1211A62200A76940 /* InspectorDatabaseAgent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorDatabaseAgent.cpp; sourceTree = "<group>"; };
 		7AB0B1BF1211A62200A76940 /* InspectorDatabaseAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorDatabaseAgent.h; sourceTree = "<group>"; };
 		7ADE722510CBBB9B006B3B3A /* ContextMenuProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContextMenuProvider.h; sourceTree = "<group>"; };
@@ -14573,6 +14577,8 @@
 				41F0618C0F5F069800A07EAC /* ConsoleMessage.h */,
 				59102FB914327D3B003C9D04 /* ContentSearchUtils.cpp */,
 				59102FBA14327D3B003C9D04 /* ContentSearchUtils.h */,
+				7AABA25714BC613300AA9A11 /* DOMEditor.cpp */,
+				7AABA25814BC613300AA9A11 /* DOMEditor.h */,
 				4F1442261339FD6200E0D6F8 /* DOMNodeHighlighter.cpp */,
 				4F1442271339FD6200E0D6F8 /* DOMNodeHighlighter.h */,
 				F35AE5AB14925F5B004D5776 /* DOMWrapperVisitor.h */,
@@ -24897,6 +24903,7 @@
 				FD537353137B651800008DCE /* ZeroPole.h in Headers */,
 				974A862314B7ADBB003FDC76 /* FrameDestructionObserver.h in Headers */,
 				97D2AD0414B823A60093DF32 /* DOMWindowProperty.h in Headers */,
+				7AABA25A14BC613300AA9A11 /* DOMEditor.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -27803,6 +27810,7 @@
 				FD537352137B651800008DCE /* ZeroPole.cpp in Sources */,
 				974A862214B7ADBB003FDC76 /* FrameDestructionObserver.cpp in Sources */,
 				97D2AD0314B823A60093DF32 /* DOMWindowProperty.cpp in Sources */,
+				7AABA25914BC613300AA9A11 /* DOMEditor.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

Added: trunk/Source/WebCore/inspector/DOMEditor.cpp (0 => 104586)


--- trunk/Source/WebCore/inspector/DOMEditor.cpp	                        (rev 0)
+++ trunk/Source/WebCore/inspector/DOMEditor.cpp	2012-01-10 17:01:05 UTC (rev 104586)
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DOMEditor.h"
+
+#if ENABLE(INSPECTOR)
+
+#include "Base64.h"
+#include "Document.h"
+#include "HTMLDocument.h"
+#include "HTMLDocumentParser.h"
+#include "HTMLElement.h"
+#include "HTMLHeadElement.h"
+#include "Node.h"
+
+#include <wtf/RefPtr.h>
+#include <wtf/SHA1.h>
+#include <wtf/text/CString.h>
+
+using namespace std;
+
+namespace WebCore {
+
+struct DOMEditor::NodeDigest {
+    NodeDigest(Node* node) : m_node(node) { }
+
+    String m_digest;
+    String m_attrsDigest;
+    Node* m_node;
+    Vector<OwnPtr<NodeDigest> > m_children;
+};
+
+DOMEditor::DOMEditor(Document* document) : m_document(document) { }
+
+DOMEditor::~DOMEditor() { }
+
+void DOMEditor::patch(const String& markup)
+{
+    RefPtr<HTMLDocument> newDocument = HTMLDocument::create(0, KURL());
+    RefPtr<DocumentParser> parser = HTMLDocumentParser::create(newDocument.get(), false);
+    parser->insert(markup); // Use insert() so that the parser will not yield.
+    parser->finish();
+    parser->detach();
+
+    if (!patchElement(m_document->head(), newDocument->head()) || !patchElement(m_document->body(), newDocument->body())) {
+        // Fall back to rewrite.
+        m_document->write(markup);
+        m_document->close();
+    }
+}
+
+bool DOMEditor::patchElement(Element* oldElement, Element* newElement)
+{
+    if (oldElement)  {
+        if (newElement) {
+            OwnPtr<NodeDigest> oldInfo = createNodeDigest(oldElement);
+            OwnPtr<NodeDigest> newInfo = createNodeDigest(newElement);
+            return patchNode(oldInfo.get(), newInfo.get());
+        }
+        oldElement->removeAllChildren();
+        return true;
+    }
+    if (newElement) {
+        ExceptionCode ec = 0;
+        m_document->documentElement()->appendChild(newElement, ec);
+        return !ec;
+    }
+    return true;
+}
+
+bool DOMEditor::patchNode(NodeDigest* oldDigest, NodeDigest* newDigest)
+{
+    if (oldDigest->m_digest == newDigest->m_digest)
+        return true;
+
+    Node* oldNode = oldDigest->m_node;
+    Node* newNode = newDigest->m_node;
+
+    if (newNode->nodeType() != oldNode->nodeType() || newNode->nodeName() != oldNode->nodeName()) {
+        ExceptionCode ec = 0;
+        oldNode->parentNode()->replaceChild(newNode, oldNode, ec);
+        if (ec)
+            return false;
+        return true;
+    }
+
+    ExceptionCode ec = 0;
+    if (oldNode->nodeValue() != newNode->nodeValue())
+        oldNode->setNodeValue(newNode->nodeValue(), ec);
+    if (ec)
+        return false;
+
+    if (oldNode->nodeType() == Node::ELEMENT_NODE && oldDigest->m_attrsDigest != newDigest->m_attrsDigest) {
+        Element* oldElement = static_cast<Element*>(oldNode);
+        Element* newElement = static_cast<Element*>(newNode);
+        oldElement->setAttributesFromElement(*newElement);
+    }
+    if (oldDigest->m_node->nodeType() != Node::ELEMENT_NODE)
+        return true;
+
+    return patchChildren(static_cast<ContainerNode*>(oldNode), oldDigest->m_children, newDigest->m_children);
+}
+
+bool DOMEditor::patchChildren(ContainerNode* oldParent, Vector<OwnPtr<NodeDigest> >& oldList, Vector<OwnPtr<NodeDigest> >& newList)
+{
+    // Trim tail.
+    size_t offset = 0;
+    for (; offset < oldList.size() && offset < newList.size() && oldList[oldList.size() - offset - 1]->m_digest == newList[newList.size() - offset - 1]->m_digest; ++offset) { }
+    if (offset > 0) {
+        oldList.resize(oldList.size() - offset);
+        newList.resize(newList.size() - offset);
+    }
+
+    // Trim head.
+    for (offset = 0; offset < oldList.size() && offset < newList.size() && oldList[offset]->m_digest == newList[offset]->m_digest; ++offset) { }
+    if (offset > 0) {
+        oldList.remove(0, offset);
+        newList.remove(0, offset);
+    }
+
+    // Diff the children lists.
+    typedef Vector<pair<NodeDigest*, size_t> > ResultMap;
+    ResultMap newMap(newList.size());
+    ResultMap oldMap(oldList.size());
+
+    for (size_t i = 0; i < oldMap.size(); ++i)
+        oldMap[i].first = 0;
+    for (size_t i = 0; i < newMap.size(); ++i)
+        newMap[i].first = 0;
+
+    typedef HashMap<String, Vector<size_t> > DiffTable;
+    DiffTable newTable;
+    DiffTable oldTable;
+
+    for (size_t i = 0; i < newList.size(); ++i) {
+        DiffTable::iterator it = newTable.add(newList[i]->m_digest, Vector<size_t>()).first;
+        it->second.append(i);
+    }
+
+    for (size_t i = 0; i < oldList.size(); ++i) {
+        DiffTable::iterator it = oldTable.add(oldList[i]->m_digest, Vector<size_t>()).first;
+        it->second.append(i);
+    }
+
+    for (DiffTable::iterator newIt = newTable.begin(); newIt != newTable.end(); ++newIt) {
+        if (newIt->second.size() != 1)
+            continue;
+
+        DiffTable::iterator oldIt = oldTable.find(newIt->first);
+        if (oldIt == oldTable.end() || oldIt->second.size() != 1)
+            continue;
+        make_pair(newList[newIt->second[0]].get(), oldIt->second[0]);
+        newMap[newIt->second[0]] = make_pair(newList[newIt->second[0]].get(), oldIt->second[0]);
+        oldMap[oldIt->second[0]] = make_pair(oldList[oldIt->second[0]].get(), newIt->second[0]);
+    }
+
+    for (size_t i = 0; newList.size() > 0 && i < newList.size() - 1; ++i) {
+        if (!newMap[i].first || newMap[i + 1].first)
+            continue;
+
+        size_t j = newMap[i].second + 1;
+        if (j < oldMap.size() && !oldMap[j].first && newList[i + 1]->m_digest == oldList[j]->m_digest) {
+            newMap[i + 1] = make_pair(newList[i + 1].get(), j);
+            oldMap[j] = make_pair(oldList[j].get(), i + 1);
+        }
+    }
+
+    for (size_t i = newList.size() - 1; newList.size() > 0 && i > 0; --i) {
+        if (!newMap[i].first || newMap[i - 1].first || newMap[i].second <= 0)
+            continue;
+
+        size_t j = newMap[i].second - 1;
+        if (!oldMap[j].first && newList[i - 1]->m_digest == oldList[j]->m_digest) {
+            newMap[i - 1] = make_pair(newList[i - 1].get(), j);
+            oldMap[j] = make_pair(oldList[j].get(), i - 1);
+        }
+    }
+
+    HashSet<NodeDigest*> merges;
+    for (size_t i = 0; i < oldList.size(); ++i) {
+        if (oldMap[i].first)
+            continue;
+
+        // Check if this change is between stable nodes. If it is, consider it as "modified".
+        if ((!i || oldMap[i - 1].first) && (i == oldMap.size() - 1 || oldMap[i + 1].first)) {
+            size_t anchorBefore = i ? oldMap[i - 1].second : -1;
+            size_t anchorAfter = i == oldMap.size() - 1 ? anchorBefore + 2 : oldMap[i + 1].second;
+            if (anchorAfter - anchorBefore == 2 && anchorBefore + 1 < newList.size()) {
+                if (!patchNode(oldList[i].get(), newList[anchorBefore + 1].get()))
+                    return false;
+
+                merges.add(newList[anchorBefore + 1].get());
+            } else
+                oldList[i]->m_node->parentNode()->removeChild(oldList[i]->m_node);
+        } else {
+            ContainerNode* parentNode = static_cast<ContainerNode*>(oldList[i]->m_node->parentNode());
+            parentNode->removeChild(oldList[i]->m_node);
+        }
+    }
+
+    for (size_t i = 0; i < newMap.size(); ++i) {
+        if (newMap[i].first || merges.contains(newList[i].get()))
+            continue;
+
+        ExceptionCode ec = 0;
+        oldParent->insertBefore(newList[i]->m_node, oldParent->childNode(i + offset), ec);
+        return !ec;
+    }
+
+    return true;
+}
+
+static void addStringToSHA1(SHA1& sha1, const String& string)
+{
+    CString cString = string.utf8();
+    sha1.addBytes(reinterpret_cast<const uint8_t*>(cString.data()), cString.length());
+}
+
+PassOwnPtr<DOMEditor::NodeDigest> DOMEditor::createNodeDigest(Node* node)
+{
+    NodeDigest* nodeDigest = new NodeDigest(node);
+
+    SHA1 sha1;
+
+    Node::NodeType nodeType = node->nodeType();
+    sha1.addBytes(reinterpret_cast<const uint8_t*>(&nodeType), sizeof(nodeType));
+    addStringToSHA1(sha1, node->nodeName());
+    addStringToSHA1(sha1, node->nodeValue());
+
+    if (node->nodeType() == Node::ELEMENT_NODE) {
+        Node* child = node->firstChild();
+        while (child) {
+            OwnPtr<NodeDigest> childInfo = createNodeDigest(child);
+            addStringToSHA1(sha1, childInfo->m_digest);
+            child = child->nextSibling();
+            nodeDigest->m_children.append(childInfo.release());
+        }
+
+        Element* element = static_cast<Element*>(node);
+        const NamedNodeMap* attrMap = element->attributes(true);
+        if (attrMap && attrMap->length()) {
+            unsigned numAttrs = attrMap->length();
+            SHA1 attrsSHA1;
+            for (unsigned i = 0; i < numAttrs; ++i) {
+                const Attribute* attribute = attrMap->attributeItem(i);
+                addStringToSHA1(attrsSHA1, attribute->name().toString());
+                addStringToSHA1(attrsSHA1, attribute->value());
+            }
+            Vector<uint8_t, 20> attrsHash;
+            attrsSHA1.computeHash(attrsHash);
+            nodeDigest->m_attrsDigest = base64Encode(reinterpret_cast<const char*>(attrsHash.data()), 10);
+            addStringToSHA1(sha1, nodeDigest->m_attrsDigest);
+        }
+    }
+
+    Vector<uint8_t, 20> hash;
+    sha1.computeHash(hash);
+    nodeDigest->m_digest = base64Encode(reinterpret_cast<const char*>(hash.data()), 10);
+    return adoptPtr(nodeDigest);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INSPECTOR)
Property changes on: trunk/Source/WebCore/inspector/DOMEditor.cpp
___________________________________________________________________

Added: svn:eol-style

Added: trunk/Source/WebCore/inspector/DOMEditor.h (0 => 104586)


--- trunk/Source/WebCore/inspector/DOMEditor.h	                        (rev 0)
+++ trunk/Source/WebCore/inspector/DOMEditor.h	2012-01-10 17:01:05 UTC (rev 104586)
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DOMEditor_h
+#define DOMEditor_h
+
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class ContainerNode;
+class Document;
+class Element;
+class Node;
+
+#if ENABLE(INSPECTOR)
+
+class DOMEditor {
+public:
+    explicit DOMEditor(Document*);
+    virtual ~DOMEditor();
+
+    void patch(const String& markup);
+
+private:
+    struct NodeDigest;
+
+    bool patchElement(Element* oldElement, Element* newElement);
+    bool patchNode(NodeDigest* oldNode, NodeDigest* newNode);
+    bool patchChildren(ContainerNode* oldParent, Vector<OwnPtr<NodeDigest> >& oldChildren, Vector<OwnPtr<NodeDigest> >& newChildren);
+    PassOwnPtr<NodeDigest> createNodeDigest(Node*);
+
+    Document* m_document;
+};
+
+#endif // ENABLE(INSPECTOR)
+
+} // namespace WebCore
+
+#endif // !defined(DOMEditor_h)
Property changes on: trunk/Source/WebCore/inspector/DOMEditor.h
___________________________________________________________________

Added: svn:eol-style

Modified: trunk/Source/WebCore/inspector/InspectorDOMAgent.cpp (104585 => 104586)


--- trunk/Source/WebCore/inspector/InspectorDOMAgent.cpp	2012-01-10 16:54:29 UTC (rev 104585)
+++ trunk/Source/WebCore/inspector/InspectorDOMAgent.cpp	2012-01-10 17:01:05 UTC (rev 104586)
@@ -47,6 +47,7 @@
 #include "ContainerNode.h"
 #include "Cookie.h"
 #include "CookieJar.h"
+#include "DOMEditor.h"
 #include "DOMNodeHighlighter.h"
 #include "DOMWindow.h"
 #include "Document.h"
@@ -632,13 +633,22 @@
 
 void InspectorDOMAgent::setOuterHTML(ErrorString* errorString, int nodeId, const String& outerHTML, int* newId)
 {
+    if (!nodeId) {
+        DOMEditor domEditor(m_document.get());
+        domEditor.patch(outerHTML);
+        *newId = 0;
+        return;
+    }
+
     Node* node = assertNode(errorString, nodeId);
     if (!node)
         return;
 
-    Element* parentElement = node->parentElement();
-    if (!parentElement)
+    Node* parentNode = node->parentNode();
+    if (!parentNode) {
+        *errorString = "Editing of the detached nodes is not supported";
         return;
+    }
 
     Document* document = node->ownerDocument();
     if (!document->isHTMLDocument()) {
@@ -649,10 +659,10 @@
     Node* previousSibling = node->previousSibling(); // Remember previous sibling before replacing node.
 
     RefPtr<DocumentFragment> fragment = DocumentFragment::create(document);
-    fragment->parseHTML(outerHTML, parentElement);
+    fragment->parseHTML(outerHTML, parentNode->nodeType() == Node::ELEMENT_NODE ? static_cast<Element*>(parentNode) : document->documentElement());
 
     ExceptionCode ec = 0;
-    parentElement->replaceChild(fragment.release(), node, ec);
+    parentNode->replaceChild(fragment.release(), node, ec);
     if (ec) {
         *errorString = "Failed to replace Node with new contents";
         return;
@@ -670,7 +680,7 @@
         return;
     }
 
-    Node* newNode = previousSibling ? previousSibling->nextSibling() : parentElement->firstChild();
+    Node* newNode = previousSibling ? previousSibling->nextSibling() : parentNode->firstChild();
     if (!newNode) {
         // The only child node has been deleted.
         *newId = 0;

Modified: trunk/Source/WebCore/inspector/front-end/DOMAgent.js (104585 => 104586)


--- trunk/Source/WebCore/inspector/front-end/DOMAgent.js	2012-01-10 16:54:29 UTC (rev 104585)
+++ trunk/Source/WebCore/inspector/front-end/DOMAgent.js	2012-01-10 17:01:05 UTC (rev 104586)
@@ -529,6 +529,8 @@
     this._document = null;
     this._attributeLoadNodeIds = {};
     InspectorBackend.registerDOMDispatcher(new WebInspector.DOMDispatcher(this));
+    if (WebInspector.experimentsSettings.freeFlowDOMEditing.isEnabled())
+        new WebInspector.DOMModelResourceBinding(this);
 }
 
 WebInspector.DOMAgent.Events = {
@@ -1074,3 +1076,27 @@
  * @type {?WebInspector.DOMAgent}
  */
 WebInspector.domAgent = null;
+
+/**
+ * @constructor
+ * @implements {WebInspector.ResourceDomainModelBinding}
+ */
+WebInspector.DOMModelResourceBinding = function(domAgent)
+{
+    this._domAgent = domAgent;
+    WebInspector.Resource.registerDomainModelBinding(WebInspector.Resource.Type.Document, this);
+}
+
+WebInspector.DOMModelResourceBinding.prototype = {
+    setContent: function(resource, content, majorChange, userCallback)
+    {
+        DOMAgent.setOuterHTML(0, content, userCallback);
+    },
+
+    canSetContent: function()
+    {
+        return true;
+    }
+}
+
+WebInspector.DOMModelResourceBinding.prototype.__proto__ = WebInspector.ResourceDomainModelBinding.prototype;

Modified: trunk/Source/WebCore/inspector/front-end/Settings.js (104585 => 104586)


--- trunk/Source/WebCore/inspector/front-end/Settings.js	2012-01-10 16:54:29 UTC (rev 104585)
+++ trunk/Source/WebCore/inspector/front-end/Settings.js	2012-01-10 17:01:05 UTC (rev 104586)
@@ -169,6 +169,7 @@
     // FIXME: Move out from experiments once navigator is production-ready.
     this.useScriptsNavigator = this._createExperiment("useScriptsNavigator", "Use file navigator and tabbed editor container in scripts panel");
     this.sourceFrameAlwaysEditable = this._createExperiment("sourceFrameAlwaysEditable", "Make resources always editable");
+    this.freeFlowDOMEditing = this._createExperiment("freeFlowDOMEditing", "Enable free flow DOM editing");
 
     this._cleanUpSetting();
 }
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to