Implement UndoManager's automatic DOM transactions
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 22 Aug 2012 03:56:30 +0000 (03:56 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 22 Aug 2012 03:56:30 +0000 (03:56 +0000)
https://bugs.webkit.org/show_bug.cgi?id=91812

Patch by Sukolsak Sakshuwong <sukolsak@google.com> on 2012-08-21
Reviewed by Ryosuke Niwa.

Source/WebCore:

This patch implements automatic DOM transactions in UndoManager
by recording changes in tree structure, attributes, and character data
of nodes under undo scope host.

Tests: editing/undomanager/automatic-transaction-attribute.html
       editing/undomanager/automatic-transaction-data.html
       editing/undomanager/automatic-transaction-node.html

* CMakeLists.txt:
* GNUmakefile.list.am:
* WebCore.gypi:
* WebCore.vcproj/WebCore.vcproj:
* WebCore.xcodeproj/project.pbxproj:
* bindings/js/DOMTransaction.h:
(WebCore::DOMTransaction::addTransactionStep):
(DOMTransaction):
* bindings/v8/DOMTransaction.cpp:
(DOMTransactionScope):
(WebCore::DOMTransactionScope::DOMTransactionScope):
(WebCore::DOMTransactionScope::~DOMTransactionScope):
(WebCore):
(WebCore::DOMTransaction::apply):
(WebCore::DOMTransaction::unapply):
(WebCore::DOMTransaction::reapply):
* bindings/v8/DOMTransaction.h:
(WebCore::DOMTransaction::addTransactionStep):
(DOMTransaction):
* css/PropertySetCSSStyleDeclaration.cpp:
(WebCore::PropertySetCSSStyleDeclaration::setCssText):
(WebCore::PropertySetCSSStyleDeclaration::setProperty):
(WebCore::PropertySetCSSStyleDeclaration::removeProperty):
(WebCore::PropertySetCSSStyleDeclaration::setPropertyInternal):
* dom/CharacterData.cpp:
(WebCore::CharacterData::setDataAndUpdate):
* dom/ContainerNode.cpp:
(WebCore::willRemoveChild):
(WebCore::willRemoveChildren):
(WebCore::updateTreeAfterInsertion):
* dom/Element.cpp:
(WebCore::Element::willModifyAttribute):
* editing/DOMTransactionStep.cpp: Added.
(WebCore):
(WebCore::NodeInsertingDOMTransactionStep::NodeInsertingDOMTransactionStep):
(WebCore::NodeInsertingDOMTransactionStep::create):
(WebCore::NodeInsertingDOMTransactionStep::unapply):
(WebCore::NodeInsertingDOMTransactionStep::reapply):
(WebCore::NodeRemovingDOMTransactionStep::NodeRemovingDOMTransactionStep):
(WebCore::NodeRemovingDOMTransactionStep::create):
(WebCore::NodeRemovingDOMTransactionStep::unapply):
(WebCore::NodeRemovingDOMTransactionStep::reapply):
(WebCore::DataReplacingDOMTransactionStep::DataReplacingDOMTransactionStep):
(WebCore::DataReplacingDOMTransactionStep::create):
(WebCore::DataReplacingDOMTransactionStep::unapply):
(WebCore::DataReplacingDOMTransactionStep::reapply):
(WebCore::AttrChangingDOMTransactionStep::AttrChangingDOMTransactionStep):
(WebCore::AttrChangingDOMTransactionStep::create):
(WebCore::AttrChangingDOMTransactionStep::unapply):
(WebCore::AttrChangingDOMTransactionStep::reapply):
* editing/DOMTransactionStep.h: Added.
(WebCore):
(DOMTransactionStep):
(WebCore::DOMTransactionStep::~DOMTransactionStep):
(NodeInsertingDOMTransactionStep):
(NodeRemovingDOMTransactionStep):
(DataReplacingDOMTransactionStep):
(AttrChangingDOMTransactionStep):
* editing/UndoManager.cpp:
(WebCore):
(WebCore::UndoManager::isRecordingAutomaticTransaction):
(WebCore::UndoManager::addTransactionStep):
* editing/UndoManager.h:
(WebCore):
(WebCore::UndoManager::setRecordingDOMTransaction):
(UndoManager):

LayoutTests:

* editing/undomanager/automatic-transaction-attribute-expected.txt: Added.
* editing/undomanager/automatic-transaction-attribute.html: Added.
* editing/undomanager/automatic-transaction-data-expected.txt: Added.
* editing/undomanager/automatic-transaction-data.html: Added.
* editing/undomanager/automatic-transaction-node-expected.txt: Added.
* editing/undomanager/automatic-transaction-node.html: Added.

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

24 files changed:
LayoutTests/ChangeLog
LayoutTests/editing/undomanager/automatic-transaction-attribute-expected.txt [new file with mode: 0644]
LayoutTests/editing/undomanager/automatic-transaction-attribute.html [new file with mode: 0644]
LayoutTests/editing/undomanager/automatic-transaction-data-expected.txt [new file with mode: 0644]
LayoutTests/editing/undomanager/automatic-transaction-data.html [new file with mode: 0644]
LayoutTests/editing/undomanager/automatic-transaction-node-expected.txt [new file with mode: 0644]
LayoutTests/editing/undomanager/automatic-transaction-node.html [new file with mode: 0644]
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/GNUmakefile.list.am
Source/WebCore/WebCore.gypi
Source/WebCore/WebCore.vcproj/WebCore.vcproj
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/bindings/js/DOMTransaction.h
Source/WebCore/bindings/v8/DOMTransaction.cpp
Source/WebCore/bindings/v8/DOMTransaction.h
Source/WebCore/css/PropertySetCSSStyleDeclaration.cpp
Source/WebCore/dom/CharacterData.cpp
Source/WebCore/dom/ContainerNode.cpp
Source/WebCore/dom/Element.cpp
Source/WebCore/editing/DOMTransactionStep.cpp [new file with mode: 0644]
Source/WebCore/editing/DOMTransactionStep.h [new file with mode: 0644]
Source/WebCore/editing/UndoManager.cpp
Source/WebCore/editing/UndoManager.h

index 3d53433..4eb2476 100644 (file)
@@ -1,3 +1,17 @@
+2012-08-21  Sukolsak Sakshuwong  <sukolsak@google.com>
+
+        Implement UndoManager's automatic DOM transactions
+        https://bugs.webkit.org/show_bug.cgi?id=91812
+
+        Reviewed by Ryosuke Niwa.
+
+        * editing/undomanager/automatic-transaction-attribute-expected.txt: Added.
+        * editing/undomanager/automatic-transaction-attribute.html: Added.
+        * editing/undomanager/automatic-transaction-data-expected.txt: Added.
+        * editing/undomanager/automatic-transaction-data.html: Added.
+        * editing/undomanager/automatic-transaction-node-expected.txt: Added.
+        * editing/undomanager/automatic-transaction-node.html: Added.
+
 2012-08-21  Shezan Baig  <sbaig1@bloomberg.net>
 
         Fix cross-direction stretch for replaced elements in row flexbox
diff --git a/LayoutTests/editing/undomanager/automatic-transaction-attribute-expected.txt b/LayoutTests/editing/undomanager/automatic-transaction-attribute-expected.txt
new file mode 100644 (file)
index 0000000..cea0351
--- /dev/null
@@ -0,0 +1,15 @@
+This tests that UndoManager correctly reverts/reapplies automatic DOM transactions on attribute changes. We start with a span that has blue border and doesn't have title or contenteditable attributes. 
+
+PASS transact 1: set title to "test" 
+PASS transact 2: set contenteditable to "true" 
+PASS transact 3: set border color to red 
+PASS transact 4: remove attribute title 
+PASS undo 4: title is back to "test" 
+PASS undo 3: border color is back to blue 
+PASS undo 2: contenteditable is back to "inherit" 
+PASS undo 1: title is back to an empty string 
+PASS redo 1: title is "test" again 
+PASS redo 2: contentEditable is "true" again 
+PASS redo 3: border color is red again 
+PASS redo 4: title is an empty string again 
+
diff --git a/LayoutTests/editing/undomanager/automatic-transaction-attribute.html b/LayoutTests/editing/undomanager/automatic-transaction-attribute.html
new file mode 100644 (file)
index 0000000..205871d
--- /dev/null
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>automatic transaction on attributes</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+</head>
+<body>
+This tests that UndoManager correctly reverts/reapplies automatic DOM transactions
+on attribute changes. We start with a span that has blue border and doesn't have title
+or contenteditable attributes.
+
+<span id="span" style="border: 1px blue solid"></span>
+<script>
+var undoManager = document.undoManager;
+var span = document.getElementById("span");
+
+test(function() {
+    undoManager.transact({
+        "executeAutomatic": function() { span.title = "test"; }
+    });
+    assert_equals(span.title, "test");
+}, "transact 1: set title to \"test\"");
+
+test(function() {
+    undoManager.transact({
+        "executeAutomatic": function() { span.setAttribute("contenteditable", "true"); }
+    });
+    assert_equals(span.contentEditable, "true");
+}, "transact 2: set contenteditable to \"true\"");
+
+test(function() {
+    undoManager.transact({
+        "executeAutomatic": function() { span.style.cssText = "border: 1px red solid"; }
+    });
+    assert_equals(span.style.borderColor, "red");
+}, "transact 3: set border color to red");
+
+test(function() {
+    undoManager.transact({
+        "executeAutomatic": function() { span.removeAttribute("title"); }
+    });
+    assert_equals(span.title, "");
+}, "transact 4: remove attribute title");
+
+test(function() {
+    undoManager.undo();
+    assert_equals(span.title, "test");
+}, "undo 4: title is back to \"test\"");
+
+test(function() {
+    undoManager.undo();
+    assert_equals(span.style.borderColor, "blue");
+}, "undo 3: border color is back to blue");
+
+test(function() {
+    undoManager.undo();
+    assert_equals(span.contentEditable, "inherit");
+}, "undo 2: contenteditable is back to \"inherit\"");
+
+test(function() {
+    undoManager.undo();
+    assert_equals(span.title, "");
+}, "undo 1: title is back to an empty string");
+
+test(function() {
+    undoManager.redo();
+    assert_equals(span.title, "test");
+}, "redo 1: title is \"test\" again");
+
+test(function() {
+    undoManager.redo();
+    assert_equals(span.contentEditable, "true");
+}, "redo 2: contentEditable is \"true\" again");
+
+test(function() {
+    undoManager.redo();
+    assert_equals(span.style.borderColor, "red");
+}, "redo 3: border color is red again");
+
+test(function() {
+    undoManager.redo();
+    assert_equals(span.title, "");
+}, "redo 4: title is an empty string again");
+</script>
+</body>
+</html>
diff --git a/LayoutTests/editing/undomanager/automatic-transaction-data-expected.txt b/LayoutTests/editing/undomanager/automatic-transaction-data-expected.txt
new file mode 100644 (file)
index 0000000..07e71de
--- /dev/null
@@ -0,0 +1,9 @@
+This tests that UndoManager correctly reverts/reapplies automatic DOM transactions on node data.
+
+PASS Started with 123456789. Transacted replaceData(3, 3, 'abcd'). Got 123abcd789 
+PASS undoManager.undo(). Got 123456789 
+PASS undoManager.redo(). Got 123abcd789 
+PASS Started with 1234. Transacted replaceData(2, 10, 'a'). Got 12a 
+PASS undoManager.undo(). Got 1234 
+PASS undoManager.redo(). Got 12a 
+
diff --git a/LayoutTests/editing/undomanager/automatic-transaction-data.html b/LayoutTests/editing/undomanager/automatic-transaction-data.html
new file mode 100644 (file)
index 0000000..201876e
--- /dev/null
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>automatic transaction on node data</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+</head>
+<body>
+This tests that UndoManager correctly reverts/reapplies automatic DOM transactions
+on node data.
+
+<div id="div"></div>
+<script>
+var undoManager = document.undoManager;
+var div = document.getElementById("div");
+
+test(function() {
+       div.innerText = "123456789";
+    undoManager.transact({
+        "executeAutomatic": function() { div.firstChild.replaceData(3, 3, "abcd"); }
+    });
+    assert_equals(div.innerText, "123abcd789");
+}, "Started with 123456789. Transacted replaceData(3, 3, 'abcd'). Got 123abcd789");
+
+test(function() {
+    undoManager.undo();
+    assert_equals(div.innerText, "123456789");
+}, "undoManager.undo(). Got 123456789");
+
+test(function() {
+    undoManager.redo();
+    assert_equals(div.innerText, "123abcd789");
+}, "undoManager.redo(). Got 123abcd789");
+
+test(function() {
+       div.innerText = "1234";
+    undoManager.transact({
+        "executeAutomatic": function() { div.firstChild.replaceData(2, 10, "a"); }
+    });
+    assert_equals(div.innerText, "12a");
+}, "Started with 1234. Transacted replaceData(2, 10, 'a'). Got 12a");
+
+test(function() {
+    undoManager.undo();
+    assert_equals(div.innerText, "1234");
+}, "undoManager.undo(). Got 1234");
+
+test(function() {
+    undoManager.redo();
+    assert_equals(div.innerText, "12a");
+}, "undoManager.redo(). Got 12a");
+
+div.style.display = "none";
+</script>
+</body>
+</html>
diff --git a/LayoutTests/editing/undomanager/automatic-transaction-node-expected.txt b/LayoutTests/editing/undomanager/automatic-transaction-node-expected.txt
new file mode 100644 (file)
index 0000000..948eaa4
--- /dev/null
@@ -0,0 +1,12 @@
+This tests that UndoManager correctly reverts/reapplies automatic DOM transactions on node structural change.
+
+PASS UndoManager called the function returned by executeAutomatic. (appendChild) 
+PASS UndoManager called the function returned by executeAutomatic. (insertBefore) 
+PASS UndoManager called the function returned by executeAutomatic. (removeChild) 
+PASS UndoManager reverted the change made by removeChild. 
+PASS UndoManager reverted the change made by insertBefore. 
+PASS UndoManager reverted the change made by appendChild. 
+PASS UndoManager reapplied the change made by appendChild. 
+PASS UndoManager reapplied the change made by insertBefore. 
+PASS UndoManager reapplied the change made by removeChild. 
+
diff --git a/LayoutTests/editing/undomanager/automatic-transaction-node.html b/LayoutTests/editing/undomanager/automatic-transaction-node.html
new file mode 100644 (file)
index 0000000..f6354ea
--- /dev/null
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>automatic transaction on nodes</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+</head>
+<body>
+This tests that UndoManager correctly reverts/reapplies automatic DOM transactions
+on node structural change.
+
+<div id="edit"></div>
+<script>
+var edit = document.getElementById("edit");
+var undoManager = document.undoManager;
+
+test(function() {
+    undoManager.transact({
+        "executeAutomatic": function() {
+            var d = document.createElement("span");
+            d.innerText = "1";
+            edit.appendChild(d);
+        }
+    });
+    assert_true(edit.innerHTML == "<span>1</span>");
+}, "UndoManager called the function returned by executeAutomatic. (appendChild)");
+
+test(function() {
+    undoManager.transact({
+        "executeAutomatic": function() {
+            var d2 = document.createElement("span");
+            d2.innerHTML = "<b>2</b>";
+            edit.insertBefore(d2, edit.firstChild);
+        }
+    });
+    assert_true(edit.innerHTML == "<span><b>2</b></span><span>1</span>");
+}, "UndoManager called the function returned by executeAutomatic. (insertBefore)");
+
+test(function() {
+    undoManager.transact({
+        "executeAutomatic": function() {
+            edit.removeChild(edit.lastChild);
+        }
+    });
+    assert_true(edit.innerHTML == "<span><b>2</b></span>");
+}, "UndoManager called the function returned by executeAutomatic. (removeChild)");
+
+test(function() {
+    undoManager.undo();
+    assert_true(edit.innerHTML == "<span><b>2</b></span><span>1</span>");
+}, "UndoManager reverted the change made by removeChild.");
+
+test(function() {
+    undoManager.undo();
+    assert_true(edit.innerHTML == "<span>1</span>");
+}, "UndoManager reverted the change made by insertBefore.");
+
+test(function() {
+    undoManager.undo();
+    assert_true(edit.innerHTML == "");
+}, "UndoManager reverted the change made by appendChild.");
+
+test(function() {
+    undoManager.redo();
+    assert_true(edit.innerHTML == "<span>1</span>");
+}, "UndoManager reapplied the change made by appendChild.");
+
+test(function() {
+    undoManager.redo();
+    assert_true(edit.innerHTML == "<span><b>2</b></span><span>1</span>");
+}, "UndoManager reapplied the change made by insertBefore.");
+
+test(function() {
+    undoManager.redo();
+    assert_true(edit.innerHTML == "<span><b>2</b></span>");
+}, "UndoManager reapplied the change made by removeChild.");
+
+edit.style.display = "none";
+</script>
+</body>
+</html>
index 364369f..a7ae94f 100644 (file)
@@ -1218,6 +1218,7 @@ SET(WebCore_SOURCES
     editing/BreakBlockquoteCommand.cpp
     editing/CompositeEditCommand.cpp
     editing/CreateLinkCommand.cpp
+    editing/DOMTransactionStep.cpp
     editing/DeleteButton.cpp
     editing/DeleteButtonController.cpp
     editing/DeleteFromTextNodeCommand.cpp
index f464a69..fa8108d 100644 (file)
@@ -1,3 +1,85 @@
+2012-08-21  Sukolsak Sakshuwong  <sukolsak@google.com>
+
+        Implement UndoManager's automatic DOM transactions
+        https://bugs.webkit.org/show_bug.cgi?id=91812
+
+        Reviewed by Ryosuke Niwa.
+
+        This patch implements automatic DOM transactions in UndoManager
+        by recording changes in tree structure, attributes, and character data
+        of nodes under undo scope host.
+
+        Tests: editing/undomanager/automatic-transaction-attribute.html
+               editing/undomanager/automatic-transaction-data.html
+               editing/undomanager/automatic-transaction-node.html
+
+        * CMakeLists.txt:
+        * GNUmakefile.list.am:
+        * WebCore.gypi:
+        * WebCore.vcproj/WebCore.vcproj:
+        * WebCore.xcodeproj/project.pbxproj:
+        * bindings/js/DOMTransaction.h:
+        (WebCore::DOMTransaction::addTransactionStep):
+        (DOMTransaction):
+        * bindings/v8/DOMTransaction.cpp:
+        (DOMTransactionScope):
+        (WebCore::DOMTransactionScope::DOMTransactionScope):
+        (WebCore::DOMTransactionScope::~DOMTransactionScope):
+        (WebCore):
+        (WebCore::DOMTransaction::apply):
+        (WebCore::DOMTransaction::unapply):
+        (WebCore::DOMTransaction::reapply):
+        * bindings/v8/DOMTransaction.h:
+        (WebCore::DOMTransaction::addTransactionStep):
+        (DOMTransaction):
+        * css/PropertySetCSSStyleDeclaration.cpp:
+        (WebCore::PropertySetCSSStyleDeclaration::setCssText):
+        (WebCore::PropertySetCSSStyleDeclaration::setProperty):
+        (WebCore::PropertySetCSSStyleDeclaration::removeProperty):
+        (WebCore::PropertySetCSSStyleDeclaration::setPropertyInternal):
+        * dom/CharacterData.cpp:
+        (WebCore::CharacterData::setDataAndUpdate):
+        * dom/ContainerNode.cpp:
+        (WebCore::willRemoveChild):
+        (WebCore::willRemoveChildren):
+        (WebCore::updateTreeAfterInsertion):
+        * dom/Element.cpp:
+        (WebCore::Element::willModifyAttribute):
+        * editing/DOMTransactionStep.cpp: Added.
+        (WebCore):
+        (WebCore::NodeInsertingDOMTransactionStep::NodeInsertingDOMTransactionStep):
+        (WebCore::NodeInsertingDOMTransactionStep::create):
+        (WebCore::NodeInsertingDOMTransactionStep::unapply):
+        (WebCore::NodeInsertingDOMTransactionStep::reapply):
+        (WebCore::NodeRemovingDOMTransactionStep::NodeRemovingDOMTransactionStep):
+        (WebCore::NodeRemovingDOMTransactionStep::create):
+        (WebCore::NodeRemovingDOMTransactionStep::unapply):
+        (WebCore::NodeRemovingDOMTransactionStep::reapply):
+        (WebCore::DataReplacingDOMTransactionStep::DataReplacingDOMTransactionStep):
+        (WebCore::DataReplacingDOMTransactionStep::create):
+        (WebCore::DataReplacingDOMTransactionStep::unapply):
+        (WebCore::DataReplacingDOMTransactionStep::reapply):
+        (WebCore::AttrChangingDOMTransactionStep::AttrChangingDOMTransactionStep):
+        (WebCore::AttrChangingDOMTransactionStep::create):
+        (WebCore::AttrChangingDOMTransactionStep::unapply):
+        (WebCore::AttrChangingDOMTransactionStep::reapply):
+        * editing/DOMTransactionStep.h: Added.
+        (WebCore):
+        (DOMTransactionStep):
+        (WebCore::DOMTransactionStep::~DOMTransactionStep):
+        (NodeInsertingDOMTransactionStep):
+        (NodeRemovingDOMTransactionStep):
+        (DataReplacingDOMTransactionStep):
+        (AttrChangingDOMTransactionStep):
+        * editing/UndoManager.cpp:
+        (WebCore):
+        (WebCore::UndoManager::isRecordingAutomaticTransaction):
+        (WebCore::UndoManager::addTransactionStep):
+        * editing/UndoManager.h:
+        (WebCore):
+        (WebCore::UndoManager::setRecordingDOMTransaction):
+        (UndoManager):
+
 2012-08-21  Shezan Baig  <sbaig1@bloomberg.net>
 
         Fix cross-direction stretch for replaced elements in row flexbox
index d487271..b7336cc 100644 (file)
@@ -2938,6 +2938,8 @@ webcore_sources += \
        Source/WebCore/editing/DictationAlternative.h \
        Source/WebCore/editing/DictationCommand.cpp \
        Source/WebCore/editing/DictationCommand.h \
+       Source/WebCore/editing/DOMTransactionStep.cpp \
+       Source/WebCore/editing/DOMTransactionStep.h \
        Source/WebCore/editing/EditAction.h \
        Source/WebCore/editing/EditCommand.cpp \
        Source/WebCore/editing/EditCommand.h \
index 4eed4f6..9dbef6b 100644 (file)
             'editing/CompositeEditCommand.cpp',
             'editing/CreateLinkCommand.cpp',
             'editing/CreateLinkCommand.h',
+            'editing/DOMTransactionStep.cpp',
+            'editing/DOMTransactionStep.h',
             'editing/DeleteButton.cpp',
             'editing/DeleteButton.h',
             'editing/DeleteButtonController.cpp',
index c858bc3..46a2cbd 100755 (executable)
                                >
                        </File>
                        <File
+                               RelativePath="..\editing\DOMTransactionStep.cpp"
+                               >
+                               <FileConfiguration
+                                       Name="Debug|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                               <FileConfiguration
+                                       Name="Release|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                               <FileConfiguration
+                                       Name="Debug_Cairo_CFLite|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                               <FileConfiguration
+                                       Name="Release_Cairo_CFLite|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                               <FileConfiguration
+                                       Name="Debug_All|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                               <FileConfiguration
+                                       Name="Production|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                       </File>
+                       <File
+                               RelativePath="..\editing\DOMTransactionStep.h"
+                               >
+                       </File>
+                       <File
                                RelativePath="..\editing\EditAction.h"
                                >
                        </File>
index b122771..309c21f 100644 (file)
                7B9184D315758E420092AA93 /* UndoManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B9184D015758E420092AA93 /* UndoManager.h */; };
                7BB35AEB15CCDDF400F2A643 /* DOMTransaction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7BB35AE915CCDDF400F2A643 /* DOMTransaction.cpp */; };
                7BB35AEC15CCDDF400F2A643 /* DOMTransaction.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BB35AEA15CCDDF400F2A643 /* DOMTransaction.h */; };
+               7BD117EB15B8DB0100C974A3 /* DOMTransactionStep.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7BD117E915B8DB0100C974A3 /* DOMTransactionStep.cpp */; };
+               7BD117EC15B8DB0100C974A3 /* DOMTransactionStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BD117EA15B8DB0100C974A3 /* DOMTransactionStep.h */; };
                7C522D4B15B477E8009B7C95 /* InspectorOverlay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C522D4915B477E8009B7C95 /* InspectorOverlay.cpp */; };
                7E33CD01127F340D00BE8F17 /* PurgePriority.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E33CD00127F340D00BE8F17 /* PurgePriority.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7E37EF2E1339208800B29250 /* SubresourceLoaderCF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7E37EF2D1339208800B29250 /* SubresourceLoaderCF.cpp */; };
                7B9184D115758E420092AA93 /* UndoManager.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = UndoManager.idl; sourceTree = "<group>"; };
                7BB35AE915CCDDF400F2A643 /* DOMTransaction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DOMTransaction.cpp; sourceTree = "<group>"; };
                7BB35AEA15CCDDF400F2A643 /* DOMTransaction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMTransaction.h; sourceTree = "<group>"; };
+               7BD117E915B8DB0100C974A3 /* DOMTransactionStep.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DOMTransactionStep.cpp; sourceTree = "<group>"; };
+               7BD117EA15B8DB0100C974A3 /* DOMTransactionStep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMTransactionStep.h; sourceTree = "<group>"; };
                7C522D4915B477E8009B7C95 /* InspectorOverlay.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorOverlay.cpp; sourceTree = "<group>"; };
                7C522D4A15B478B2009B7C95 /* InspectorOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorOverlay.h; sourceTree = "<group>"; };
                7C60128060078BB70E367A95 /* DNSResolveQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DNSResolveQueue.cpp; sourceTree = "<group>"; };
                                CECADFC4153778FF00E37068 /* DictationCommand.cpp */,
                                CECADFC5153778FF00E37068 /* DictationCommand.h */,
                                7B8C1FE615CCE3C2004B35DC /* DOMTransaction.idl */,
+                               7BD117E915B8DB0100C974A3 /* DOMTransactionStep.cpp */,
+                               7BD117EA15B8DB0100C974A3 /* DOMTransactionStep.h */,
                                93309D93099E64910056E581 /* EditAction.h */,
                                93309D94099E64910056E581 /* EditCommand.cpp */,
                                93309D95099E64910056E581 /* EditCommand.h */,
                                05FD69E012845D4300B2BEB3 /* DOMTimeStamp.h in Headers */,
                                76FC2B0C12370DA0006A991A /* DOMTokenList.h in Headers */,
                                7BB35AEC15CCDDF400F2A643 /* DOMTransaction.h in Headers */,
+                               7BD117EC15B8DB0100C974A3 /* DOMTransactionStep.h in Headers */,
                                BC1A37BE097C715F0019F3D8 /* DOMTraversal.h in Headers */,
                                85526CD20AB0B7D9000302EA /* DOMTreeWalker.h in Headers */,
                                850B41C30AD9E7E700A6ED4F /* DOMTreeWalkerInternal.h in Headers */,
                                188604B30F2E654A000B6443 /* DOMTimer.cpp in Sources */,
                                76FC2B0B12370DA0006A991A /* DOMTokenList.cpp in Sources */,
                                7BB35AEB15CCDDF400F2A643 /* DOMTransaction.cpp in Sources */,
+                               7BD117EB15B8DB0100C974A3 /* DOMTransactionStep.cpp in Sources */,
                                85526CD30AB0B7DA000302EA /* DOMTreeWalker.mm in Sources */,
                                85C7F4920AAF79DC004014DD /* DOMUIEvent.mm in Sources */,
                                2E37DFDA12DBAFB800A6B233 /* DOMURL.cpp in Sources */,
index de05e3e..45125be 100644 (file)
@@ -27,6 +27,7 @@
 
 #if ENABLE(UNDO_MANAGER)
 
+#include "DOMTransactionStep.h"
 #include "UndoStep.h"
 #include <wtf/RefPtr.h>
 
@@ -48,10 +49,13 @@ public:
     UndoManager* undoManager() const { return m_undoManager; }
     void setUndoManager(UndoManager* undoManager) { m_undoManager = undoManager; }
 
+    void addTransactionStep(PassRefPtr<DOMTransactionStep> step) { m_transactionSteps.append(step); }
+
 private:
     DOMTransaction();
 
     UndoManager* m_undoManager;
+    Vector<RefPtr<DOMTransactionStep> > m_transactionSteps;
 };
 
 }
index d0b4d4e..d6e36ad 100644 (file)
 
 namespace WebCore {
 
+class DOMTransactionScope {
+public:
+    DOMTransactionScope(DOMTransaction* transaction)
+    {
+        UndoManager::setRecordingDOMTransaction(transaction);
+    }
+    
+    ~DOMTransactionScope()
+    {
+        UndoManager::setRecordingDOMTransaction(0);
+    }
+};
+
 DOMTransaction::DOMTransaction(const WorldContextHandle& worldContext)
     : m_worldContext(worldContext)
     , m_undoManager(0)
@@ -51,12 +64,20 @@ void DOMTransaction::apply()
     m_isAutomatic = !getFunction("executeAutomatic").IsEmpty();
     if (!m_isAutomatic)
         callFunction("execute");
+    else {
+        DOMTransactionScope scope(this);
+        callFunction("executeAutomatic");
+    }
 }
 
 void DOMTransaction::unapply()
 {
     if (!m_isAutomatic)
         callFunction("undo");
+    else {
+        for (size_t i = m_transactionSteps.size(); i > 0; --i)
+            m_transactionSteps[i - 1]->unapply();
+    }
 
     if (m_undoManager)
         m_undoManager->registerRedoStep(this);
@@ -66,6 +87,10 @@ void DOMTransaction::reapply()
 {
     if (!m_isAutomatic)
         callFunction("redo");
+    else {
+        for (size_t i = 0; i < m_transactionSteps.size(); ++i)
+            m_transactionSteps[i]->reapply();
+    }
 
     if (m_undoManager)
         m_undoManager->registerUndoStep(this);
index a967c5a..88fb30d 100644 (file)
@@ -27,6 +27,7 @@
 
 #if ENABLE(UNDO_MANAGER)
 
+#include "DOMTransactionStep.h"
 #include "UndoStep.h"
 #include "WorldContextHandle.h"
 #include <wtf/RefPtr.h>
@@ -49,6 +50,8 @@ public:
     UndoManager* undoManager() const { return m_undoManager; }
     void setUndoManager(UndoManager* undoManager) { m_undoManager = undoManager; }
 
+    void addTransactionStep(PassRefPtr<DOMTransactionStep> step) { m_transactionSteps.append(step); }
+
 private:
     DOMTransaction(const WorldContextHandle&);
     v8::Handle<v8::Function> getFunction(const char*);
@@ -57,6 +60,7 @@ private:
     WorldContextHandle m_worldContext;
     UndoManager* m_undoManager;
     bool m_isAutomatic;
+    Vector<RefPtr<DOMTransactionStep> > m_transactionSteps;
 };
 
 }
index dee9ad3..5fc2d38 100644 (file)
@@ -31,6 +31,7 @@
 #include "MutationRecord.h"
 #include "StylePropertySet.h"
 #include "StyledElement.h"
+#include "UndoManager.h"
 
 using namespace std;
 
@@ -53,15 +54,38 @@ public:
         ASSERT(!s_currentDecl);
         s_currentDecl = decl;
 
-#if ENABLE(MUTATION_OBSERVERS)
+#if ENABLE(MUTATION_OBSERVERS) || ENABLE(UNDO_MANAGER)
         if (!s_currentDecl->parentElement())
             return;
+
+        bool shouldReadOldValue = false;
+
+#if ENABLE(MUTATION_OBSERVERS)
         m_mutationRecipients = MutationObserverInterestGroup::createForAttributesMutation(s_currentDecl->parentElement(), HTMLNames::styleAttr);
-        if (!m_mutationRecipients)
-            return;
+        if (m_mutationRecipients && m_mutationRecipients->isOldValueRequested())
+            shouldReadOldValue = true;
+#endif
+#if ENABLE(UNDO_MANAGER)
+        m_isRecordingAutomaticTransaction = UndoManager::isRecordingAutomaticTransaction(s_currentDecl->parentElement());
+        if (m_isRecordingAutomaticTransaction)
+            shouldReadOldValue = true;
+#endif
+
+        AtomicString oldValue;
+        if (shouldReadOldValue)
+            oldValue = s_currentDecl->parentElement()->getAttribute(HTMLNames::styleAttr);
+
+#if ENABLE(MUTATION_OBSERVERS)
+        if (m_mutationRecipients) {
+            AtomicString requestedOldValue = m_mutationRecipients->isOldValueRequested() ? oldValue : nullAtom;
+            m_mutation = MutationRecord::createAttributes(s_currentDecl->parentElement(), HTMLNames::styleAttr, requestedOldValue);
+        }
+#endif
+#if ENABLE(UNDO_MANAGER)
+        if (m_isRecordingAutomaticTransaction)
+            m_oldValue = oldValue;
+#endif
 
-        AtomicString oldValue = m_mutationRecipients->isOldValueRequested() ? s_currentDecl->parentElement()->getAttribute(HTMLNames::styleAttr) : nullAtom;
-        m_mutation = MutationRecord::createAttributes(s_currentDecl->parentElement(), HTMLNames::styleAttr, oldValue);
 #endif
     }
 
@@ -74,6 +98,14 @@ public:
 #if ENABLE(MUTATION_OBSERVERS)
         if (m_mutation && s_shouldDeliver)
             m_mutationRecipients->enqueueMutationRecord(m_mutation);
+#endif
+#if ENABLE(UNDO_MANAGER)
+        if (m_isRecordingAutomaticTransaction && s_shouldDeliver) {
+            UndoManager::addTransactionStep(AttrChangingDOMTransactionStep::create(
+                s_currentDecl->parentElement(), HTMLNames::styleAttr, m_oldValue, s_currentDecl->parentElement()->getAttribute(HTMLNames::styleAttr)));
+        }
+#endif
+#if ENABLE(MUTATION_OBSERVERS) || ENABLE(UNDO_MANAGER)
         s_shouldDeliver = false;
 #endif
         if (!s_shouldNotifyInspector) {
@@ -88,7 +120,7 @@ public:
             InspectorInstrumentation::didInvalidateStyleAttr(localCopyStyleDecl->parentElement()->document(), localCopyStyleDecl->parentElement());
     }
 
-#if ENABLE(MUTATION_OBSERVERS)
+#if ENABLE(MUTATION_OBSERVERS) || ENABLE(UNDO_MANAGER)
     void enqueueMutationRecord()
     {
         s_shouldDeliver = true;
@@ -104,18 +136,24 @@ private:
     static unsigned s_scopeCount;
     static PropertySetCSSStyleDeclaration* s_currentDecl;
     static bool s_shouldNotifyInspector;
-#if ENABLE(MUTATION_OBSERVERS)
+#if ENABLE(MUTATION_OBSERVERS) || ENABLE(UNDO_MANAGER)
     static bool s_shouldDeliver;
+#endif
 
+#if ENABLE(MUTATION_OBSERVERS)
     OwnPtr<MutationObserverInterestGroup> m_mutationRecipients;
     RefPtr<MutationRecord> m_mutation;
 #endif
+#if ENABLE(UNDO_MANAGER)
+    bool m_isRecordingAutomaticTransaction;
+    AtomicString m_oldValue;
+#endif
 };
 
 unsigned StyleAttributeMutationScope::s_scopeCount = 0;
 PropertySetCSSStyleDeclaration* StyleAttributeMutationScope::s_currentDecl = 0;
 bool StyleAttributeMutationScope::s_shouldNotifyInspector = false;
-#if ENABLE(MUTATION_OBSERVERS)
+#if ENABLE(MUTATION_OBSERVERS) || ENABLE(UNDO_MANAGER)
 bool StyleAttributeMutationScope::s_shouldDeliver = false;
 #endif
 
@@ -158,7 +196,7 @@ String PropertySetCSSStyleDeclaration::cssText() const
     
 void PropertySetCSSStyleDeclaration::setCssText(const String& text, ExceptionCode& ec)
 {
-#if ENABLE(MUTATION_OBSERVERS)
+#if ENABLE(MUTATION_OBSERVERS) || ENABLE(UNDO_MANAGER)
     StyleAttributeMutationScope mutationScope(this);
 #endif
     willMutate();
@@ -169,7 +207,7 @@ void PropertySetCSSStyleDeclaration::setCssText(const String& text, ExceptionCod
 
     didMutate(PropertyChanged);
 
-#if ENABLE(MUTATION_OBSERVERS)
+#if ENABLE(MUTATION_OBSERVERS) || ENABLE(UNDO_MANAGER)
     mutationScope.enqueueMutationRecord();    
 #endif
 }
@@ -219,7 +257,7 @@ bool PropertySetCSSStyleDeclaration::isPropertyImplicit(const String& propertyNa
 
 void PropertySetCSSStyleDeclaration::setProperty(const String& propertyName, const String& value, const String& priority, ExceptionCode& ec)
 {
-#if ENABLE(MUTATION_OBSERVERS)
+#if ENABLE(MUTATION_OBSERVERS) || ENABLE(UNDO_MANAGER)
     StyleAttributeMutationScope mutationScope(this);
 #endif
     CSSPropertyID propertyID = cssPropertyID(propertyName);
@@ -238,7 +276,7 @@ void PropertySetCSSStyleDeclaration::setProperty(const String& propertyName, con
     if (changed) {
         // CSS DOM requires raising SYNTAX_ERR of parsing failed, but this is too dangerous for compatibility,
         // see <http://bugs.webkit.org/show_bug.cgi?id=7296>.
-#if ENABLE(MUTATION_OBSERVERS)
+#if ENABLE(MUTATION_OBSERVERS) || ENABLE(UNDO_MANAGER)
         mutationScope.enqueueMutationRecord();
 #endif
     }
@@ -246,7 +284,7 @@ void PropertySetCSSStyleDeclaration::setProperty(const String& propertyName, con
 
 String PropertySetCSSStyleDeclaration::removeProperty(const String& propertyName, ExceptionCode& ec)
 {
-#if ENABLE(MUTATION_OBSERVERS)
+#if ENABLE(MUTATION_OBSERVERS) || ENABLE(UNDO_MANAGER)
     StyleAttributeMutationScope mutationScope(this);
 #endif
     CSSPropertyID propertyID = cssPropertyID(propertyName);
@@ -262,7 +300,7 @@ String PropertySetCSSStyleDeclaration::removeProperty(const String& propertyName
     didMutate(changed ? PropertyChanged : NoChanges);
 
     if (changed) {
-#if ENABLE(MUTATION_OBSERVERS)
+#if ENABLE(MUTATION_OBSERVERS) || ENABLE(UNDO_MANAGER)
         mutationScope.enqueueMutationRecord();
 #endif
     }
@@ -281,7 +319,7 @@ String PropertySetCSSStyleDeclaration::getPropertyValueInternal(CSSPropertyID pr
 
 void PropertySetCSSStyleDeclaration::setPropertyInternal(CSSPropertyID propertyID, const String& value, bool important, ExceptionCode& ec)
 { 
-#if ENABLE(MUTATION_OBSERVERS)
+#if ENABLE(MUTATION_OBSERVERS) || ENABLE(UNDO_MANAGER)
     StyleAttributeMutationScope mutationScope(this);
 #endif
     willMutate();
@@ -292,7 +330,7 @@ void PropertySetCSSStyleDeclaration::setPropertyInternal(CSSPropertyID propertyI
     didMutate(changed ? PropertyChanged : NoChanges);
 
     if (changed) {
-#if ENABLE(MUTATION_OBSERVERS)
+#if ENABLE(MUTATION_OBSERVERS) || ENABLE(UNDO_MANAGER)
         mutationScope.enqueueMutationRecord();
 #endif
     }
index dbc6ce1..b613ab9 100644 (file)
@@ -33,6 +33,7 @@
 #include "NodeRenderingContext.h"
 #include "RenderText.h"
 #include "TextBreakIterator.h"
+#include "UndoManager.h"
 
 using namespace std;
 
@@ -183,6 +184,13 @@ void CharacterData::setNodeValue(const String& nodeValue, ExceptionCode& ec)
 
 void CharacterData::setDataAndUpdate(const String& newData, unsigned offsetOfReplacedData, unsigned oldLength, unsigned newLength)
 {
+#if ENABLE(UNDO_MANAGER)
+    if (UndoManager::isRecordingAutomaticTransaction(this)) {
+        const String& replacingData = newData.substring(offsetOfReplacedData, newLength);
+        const String& replacedData = m_data.substring(offsetOfReplacedData, oldLength);
+        UndoManager::addTransactionStep(DataReplacingDOMTransactionStep::create(this, offsetOfReplacedData, oldLength, replacingData, replacedData));
+    }
+#endif
     String oldData = m_data;
     m_data = newData;
 
index add53ce..adafeb6 100644 (file)
@@ -40,6 +40,7 @@
 #include "RenderBox.h"
 #include "RenderTheme.h"
 #include "RootInlineBox.h"
+#include "UndoManager.h"
 #include <wtf/CurrentTime.h>
 #include <wtf/Vector.h>
 
@@ -327,6 +328,10 @@ static void willRemoveChild(Node* child)
     ChildListMutationScope(child->parentNode()).willRemoveChild(child);
     child->notifyMutationObserversNodeWillDetach();
 #endif
+#if ENABLE(UNDO_MANAGER)
+    if (UndoManager::isRecordingAutomaticTransaction(child->parentNode()))
+        UndoManager::addTransactionStep(NodeRemovingDOMTransactionStep::create(child->parentNode(), child));
+#endif
 
     dispatchChildRemovalEvents(child);
     child->document()->nodeWillBeRemoved(child); // e.g. mutation event listener can create a new range.
@@ -351,6 +356,10 @@ static void willRemoveChildren(ContainerNode* container)
         mutation.willRemoveChild(child);
         child->notifyMutationObserversNodeWillDetach();
 #endif
+#if ENABLE(UNDO_MANAGER)
+        if (UndoManager::isRecordingAutomaticTransaction(container))
+            UndoManager::addTransactionStep(NodeRemovingDOMTransactionStep::create(container, child));
+#endif
 
         // fire removed from document mutation events.
         dispatchChildRemovalEvents(child);
@@ -979,6 +988,11 @@ static void updateTreeAfterInsertion(ContainerNode* parent, Node* child, bool sh
     ChildListMutationScope(parent).childAdded(child);
 #endif
 
+#if ENABLE(UNDO_MANAGER)
+    if (UndoManager::isRecordingAutomaticTransaction(parent))
+        UndoManager::addTransactionStep(NodeInsertingDOMTransactionStep::create(parent, child));
+#endif
+
     parent->childrenChanged(false, child->previousSibling(), child->nextSibling(), 1);
 
     ChildNodeInsertionNotifier(parent).notify(child);
index 123d82f..d07b4af 100644 (file)
@@ -70,6 +70,7 @@
 #include "StyleResolver.h"
 #include "Text.h"
 #include "TextIterator.h"
+#include "UndoManager.h"
 #include "VoidCallback.h"
 #include "WebKitAnimationList.h"
 #include "XMLNSNames.h"
@@ -2048,6 +2049,11 @@ void Element::willModifyAttribute(const QualifiedName& name, const AtomicString&
         recipients->enqueueMutationRecord(MutationRecord::createAttributes(this, name, oldValue));
 #endif
 
+#if ENABLE(UNDO_MANAGER)
+    if (UndoManager::isRecordingAutomaticTransaction(this))
+        UndoManager::addTransactionStep(AttrChangingDOMTransactionStep::create(this, name, oldValue, newValue));
+#endif
+
 #if ENABLE(INSPECTOR)
     InspectorInstrumentation::willModifyDOMAttr(document(), this, oldValue, newValue);
 #endif
diff --git a/Source/WebCore/editing/DOMTransactionStep.cpp b/Source/WebCore/editing/DOMTransactionStep.cpp
new file mode 100644 (file)
index 0000000..3fb8302
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * 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:
+ * 1.  Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2.  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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS 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 GOOGLE INC. OR ITS 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"
+
+#if ENABLE(UNDO_MANAGER)
+
+#include "DOMTransactionStep.h"
+
+#include "CharacterData.h"
+#include "ContainerNode.h"
+#include "Element.h"
+#include "ExceptionCode.h"
+#include "Node.h"
+
+namespace WebCore {
+
+NodeInsertingDOMTransactionStep::NodeInsertingDOMTransactionStep(Node* node, Node* child)
+    : m_node(node)
+    , m_refChild(child->nextSibling())
+    , m_child(child)
+{
+}
+
+PassRefPtr<NodeInsertingDOMTransactionStep> NodeInsertingDOMTransactionStep::create(Node* node, Node* child)
+{
+    return adoptRef(new NodeInsertingDOMTransactionStep(node, child));
+}
+
+void NodeInsertingDOMTransactionStep::unapply()
+{
+    if (m_child && m_child->parentNode() != m_node.get())
+        return;
+    if (m_refChild && m_refChild->parentNode() != m_node.get())
+        return;
+    if (m_refChild && m_refChild->previousSibling() != m_child.get())
+        return;
+    
+    ExceptionCode ec;
+    m_node->removeChild(m_child.get(), ec);
+}
+
+void NodeInsertingDOMTransactionStep::reapply()
+{
+    if (m_child && m_child->parentNode())
+        return;
+    if (m_refChild && m_refChild->parentNode() != m_node.get())
+        return;
+    
+    ExceptionCode ec;
+    m_node->insertBefore(m_child, m_refChild.get(), ec);
+}
+
+NodeRemovingDOMTransactionStep::NodeRemovingDOMTransactionStep(Node* node, Node* child)
+    : m_node(node)
+    , m_refChild(child->nextSibling())
+    , m_child(child)
+{
+}
+
+PassRefPtr<NodeRemovingDOMTransactionStep> NodeRemovingDOMTransactionStep::create(Node* node, Node* child)
+{
+    return adoptRef(new NodeRemovingDOMTransactionStep(node, child));
+}
+
+void NodeRemovingDOMTransactionStep::unapply()
+{
+    if (m_child && m_child->parentNode())
+        return;
+    if (m_refChild && m_refChild->parentNode() != m_node.get())
+        return;
+
+    ExceptionCode ec;
+    m_node->insertBefore(m_child, m_refChild.get(), ec);
+}
+
+void NodeRemovingDOMTransactionStep::reapply()
+{
+    if (m_child && m_child->parentNode() != m_node.get())
+        return;
+    if (m_refChild && m_refChild->parentNode() != m_node.get())
+        return;
+    if (m_refChild && m_refChild->previousSibling() != m_child.get())
+        return;
+    
+    ExceptionCode ec;
+    m_node->removeChild(m_child.get(), ec);
+}
+
+DataReplacingDOMTransactionStep::DataReplacingDOMTransactionStep(CharacterData* node, unsigned offset, unsigned count, const String& data, const String& replacedData)
+    : m_node(node)
+    , m_offset(offset)
+    , m_count(count)
+    , m_data(data)
+    , m_replacedData(replacedData)
+{
+}
+
+PassRefPtr<DataReplacingDOMTransactionStep> DataReplacingDOMTransactionStep::create(CharacterData* node, unsigned offset, unsigned count, const String& data, const String& replacedData)
+{
+    return adoptRef(new DataReplacingDOMTransactionStep(node, offset, count, data, replacedData));
+}
+
+void DataReplacingDOMTransactionStep::unapply()
+{
+    if (m_node->length() < m_offset)
+        return;
+    
+    ExceptionCode ec;
+    m_node->replaceData(m_offset, m_data.length(), m_replacedData, ec);
+}
+
+void DataReplacingDOMTransactionStep::reapply()
+{
+    if (m_node->length() < m_offset)
+        return;
+    
+    ExceptionCode ec;
+    m_node->replaceData(m_offset, m_count, m_data, ec);
+}
+
+AttrChangingDOMTransactionStep::AttrChangingDOMTransactionStep(Element* element, const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue)
+    : m_element(element)
+    , m_name(name)
+    , m_oldValue(oldValue)
+    , m_newValue(newValue)
+{
+}
+
+PassRefPtr<AttrChangingDOMTransactionStep> AttrChangingDOMTransactionStep::create(Element* element, const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue)
+{
+    return adoptRef(new AttrChangingDOMTransactionStep(element, name, oldValue, newValue));
+}
+
+void AttrChangingDOMTransactionStep::unapply()
+{
+    m_element->setAttribute(m_name, m_oldValue);
+}
+
+void AttrChangingDOMTransactionStep::reapply()
+{
+    m_element->setAttribute(m_name, m_newValue);
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/editing/DOMTransactionStep.h b/Source/WebCore/editing/DOMTransactionStep.h
new file mode 100644 (file)
index 0000000..459ed5d
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * 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:
+ * 1.  Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2.  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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS 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 GOOGLE INC. OR ITS 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 DOMTransactionStep_h
+#define DOMTransactionStep_h
+
+#if ENABLE(UNDO_MANAGER)
+
+#include "PlatformString.h"
+#include "QualifiedName.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class CharacterData;
+class Element;
+class Node;
+
+class DOMTransactionStep : public RefCounted<DOMTransactionStep> {
+public:
+    virtual ~DOMTransactionStep() { }
+
+    virtual void unapply() = 0;
+    virtual void reapply() = 0;
+};
+
+class NodeInsertingDOMTransactionStep : public DOMTransactionStep {
+public:
+    static PassRefPtr<NodeInsertingDOMTransactionStep> create(Node*, Node* child);
+    virtual void unapply() OVERRIDE;
+    virtual void reapply() OVERRIDE;
+
+private:
+    NodeInsertingDOMTransactionStep(Node*, Node* child);
+    
+    RefPtr<Node> m_node;
+    RefPtr<Node> m_refChild;
+    RefPtr<Node> m_child;
+};
+
+class NodeRemovingDOMTransactionStep : public DOMTransactionStep {
+public:
+    static PassRefPtr<NodeRemovingDOMTransactionStep> create(Node*, Node* child);
+    virtual void unapply() OVERRIDE;
+    virtual void reapply() OVERRIDE;
+    
+private:
+    NodeRemovingDOMTransactionStep(Node*, Node* child);
+    
+    RefPtr<Node> m_node;
+    RefPtr<Node> m_refChild;
+    RefPtr<Node> m_child;
+};
+
+class DataReplacingDOMTransactionStep : public DOMTransactionStep {
+public:
+    static PassRefPtr<DataReplacingDOMTransactionStep> create(CharacterData*, unsigned offset, unsigned count, const String& data, const String& replacedData);
+    virtual void unapply() OVERRIDE;
+    virtual void reapply() OVERRIDE;
+    
+private:
+    DataReplacingDOMTransactionStep(CharacterData*, unsigned offset, unsigned count, const String& data, const String& replacedData);
+    
+    RefPtr<CharacterData> m_node;
+    unsigned m_offset;
+    unsigned m_count;
+    String m_data;
+    String m_replacedData;
+};
+
+class AttrChangingDOMTransactionStep : public DOMTransactionStep {
+public:
+    static PassRefPtr<AttrChangingDOMTransactionStep> create(Element*, const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue);
+    virtual void unapply() OVERRIDE;
+    virtual void reapply() OVERRIDE;
+    
+private:
+    AttrChangingDOMTransactionStep(Element*, const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue);
+    
+    RefPtr<Element> m_element;
+    QualifiedName m_name;
+    AtomicString m_oldValue;
+    AtomicString m_newValue;
+};
+
+}
+
+#endif
+
+#endif
index 084ab29..e506424 100644 (file)
 
 #include "UndoManager.h"
 
-#include "DOMTransaction.h"
 #include "ExceptionCode.h"
 
 namespace WebCore {
 
+DOMTransaction* UndoManager::s_recordingDOMTransaction = 0;
+
 PassRefPtr<UndoManager> UndoManager::create(Document* document)
 {
     RefPtr<UndoManager> undoManager = adoptRef(new UndoManager(document));
@@ -199,6 +200,22 @@ void UndoManager::clearRedo(ExceptionCode& ec)
     clearStack(m_redoStack);
 }
 
+bool UndoManager::isRecordingAutomaticTransaction(Node* node)
+{
+    // We need to check that transaction still has its undomanager because
+    // transaction can disconnect its undomanager, which will clear the undo/redo stacks.
+    if (!s_recordingDOMTransaction || !s_recordingDOMTransaction->undoManager())
+        return false;
+    Document* document = s_recordingDOMTransaction->undoManager()->document();
+    return document && node->document() == document;
+}
+
+void UndoManager::addTransactionStep(PassRefPtr<DOMTransactionStep> step)
+{
+    ASSERT(s_recordingDOMTransaction);
+    s_recordingDOMTransaction->addTransactionStep(step);
+}
+
 }
 
 #endif
index 18341e9..dc5d67c 100644 (file)
@@ -34,6 +34,7 @@
 #if ENABLE(UNDO_MANAGER)
 
 #include "ActiveDOMObject.h"
+#include "DOMTransaction.h"
 #include "Document.h"
 #include "ExceptionCodePlaceholder.h"
 #include "UndoStep.h"
@@ -46,8 +47,6 @@
 
 namespace WebCore {
 
-class DOMTransaction;
-
 typedef Vector<RefPtr<UndoStep> > UndoManagerEntry;
 typedef Vector<OwnPtr<UndoManagerEntry> > UndoManagerStack;
 
@@ -78,6 +77,10 @@ public:
     Document* document() const { return m_document; }
     Node* ownerNode() const { return m_document; }
 
+    static void setRecordingDOMTransaction(DOMTransaction* transaction) { s_recordingDOMTransaction = transaction; }
+    static bool isRecordingAutomaticTransaction(Node*);
+    static void addTransactionStep(PassRefPtr<DOMTransactionStep>);
+
 private:
     explicit UndoManager(Document*);
     
@@ -86,6 +89,8 @@ private:
     UndoManagerStack m_redoStack;
     bool m_isInProgress;
     OwnPtr<UndoManagerEntry> m_inProgressEntry;
+
+    static DOMTransaction* s_recordingDOMTransaction;
 };
     
 }