Implement undoscope attribute.
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 27 Jul 2012 01:28:51 +0000 (01:28 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 27 Jul 2012 01:28:51 +0000 (01:28 +0000)
https://bugs.webkit.org/show_bug.cgi?id=88793

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

Source/WebCore:

undoscope attribute support as specified at
http://dvcs.w3.org/hg/undomanager/raw-file/tip/undomanager.html

Test: editing/undomanager/undoscope-attribute.html

* Target.pri:
* WebCore.gypi:
* WebCore.vcproj/WebCore.vcproj:
* dom/Document.cpp:
(WebCore::Document::~Document):
* dom/Element.cpp:
(WebCore):
(WebCore::Element::undoScope):
(WebCore::Element::setUndoScope):
(WebCore::Element::undoManager):
(WebCore::Element::disconnectUndoManager):
(WebCore::Element::disconnectUndoManagersInSubtree):
* dom/Element.h:
(Element):
* dom/Element.idl:
* dom/ElementRareData.h:
(ElementRareData):
* editing/UndoManager.cpp:
(WebCore::UndoManager::disconnect):
(WebCore::UndoManager::transact):
(WebCore::UndoManager::undo):
(WebCore::UndoManager::redo):
(WebCore::UndoManager::clearUndo):
(WebCore):
(WebCore::UndoManager::clearRedo):
(WebCore::UndoManager::clearUndoRedo):
(WebCore::UndoManager::isConnected):
* editing/UndoManager.h:
(WebCore):
(UndoManager):
(WebCore::UndoManager::length):
* editing/UndoManager.idl:
* html/HTMLAttributeNames.in:
* html/HTMLElement.cpp:
(WebCore::HTMLElement::parseAttribute):
(WebCore::HTMLElement::setContentEditable):

LayoutTests:

* editing/undomanager/undoscope-attribute-expected.txt: Added.
* editing/undomanager/undoscope-attribute.html: Added.

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

17 files changed:
LayoutTests/ChangeLog
LayoutTests/editing/undomanager/undoscope-attribute-expected.txt [new file with mode: 0644]
LayoutTests/editing/undomanager/undoscope-attribute.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Target.pri
Source/WebCore/WebCore.gypi
Source/WebCore/WebCore.vcproj/WebCore.vcproj
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Element.cpp
Source/WebCore/dom/Element.h
Source/WebCore/dom/Element.idl
Source/WebCore/dom/ElementRareData.h
Source/WebCore/editing/UndoManager.cpp
Source/WebCore/editing/UndoManager.h
Source/WebCore/editing/UndoManager.idl
Source/WebCore/html/HTMLAttributeNames.in
Source/WebCore/html/HTMLElement.cpp

index d647204..795b84f 100644 (file)
@@ -1,3 +1,13 @@
+2012-07-26  Sukolsak Sakshuwong  <sukolsak@google.com>
+
+        Implement undoscope attribute.
+        https://bugs.webkit.org/show_bug.cgi?id=88793
+
+        Reviewed by Ryosuke Niwa.
+
+        * editing/undomanager/undoscope-attribute-expected.txt: Added.
+        * editing/undomanager/undoscope-attribute.html: Added.
+
 2012-07-26  Yoshifumi Inoue  <yosin@chromium.org>
 
         [Forms] Introduce runtime feature flags for input type datetime, datetimelocal, month, time, week
diff --git a/LayoutTests/editing/undomanager/undoscope-attribute-expected.txt b/LayoutTests/editing/undomanager/undoscope-attribute-expected.txt
new file mode 100644 (file)
index 0000000..c5de45c
--- /dev/null
@@ -0,0 +1,21 @@
+This tests element's undoscope attribute.
+
+PASS An element has the undoscope attribute set to true. 
+PASS element.undoManager returns an UndoManager object. 
+PASS After element.undoScope = false, element.undoManager returns null. 
+PASS After element.undoScope = true, element.undoManager returns an UndoManager object. 
+PASS After element.removeAttribute('undoscope'), element.undoManager returns null. 
+PASS After element.setAttribute('undoscope', ''), element.undoManager returns an UndoManager object. 
+PASS After element.setAttribute('undoscope', 'undoscope'), element.undoManager returns an UndoManager object. 
+PASS After element.setAttribute('undoscope', ''); element.undoScope = false;, element.undoManager returns null. 
+PASS After element.removeAttribute('undoscope'); element.undoScope = true;, element.undoManager returns an UndoManager object. 
+PASS After element.undoScope = true; element.removeAttribute('undoscope');, element.undoManager returns null. 
+PASS After element.undoScope = false; element.setAttribute('undoscope', '');, element.undoManager returns an UndoManager object. 
+PASS After the parant of element becomes editable, element.undoManager returns null. 
+PASS After the parant of element becomes non-editable, element.undoManager returns an UndoManager object. 
+PASS After the parant of element becomes editable through inheritance, element.undoManager returns null. 
+PASS After the parant of element becomes non-editable and element becomes editable, element.undoManager returns an UndoManager object. 
+PASS element.undoManager is disconnected when element.undoScope = false. 
+PASS element.undoManager is disconnected when its parent becomes editable. 
+PASS Setting contentEditable of an ancestor of element to true doesn't disconnect element.undoManager if element is still an editing host. 
+
diff --git a/LayoutTests/editing/undomanager/undoscope-attribute.html b/LayoutTests/editing/undomanager/undoscope-attribute.html
new file mode 100644 (file)
index 0000000..c7342c4
--- /dev/null
@@ -0,0 +1,159 @@
+<!doctype html>
+<html>
+<head>
+<title>undoscope attribute</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+</head>
+<body>
+This tests element's undoscope attribute.
+
+<div id="outerContainer">
+    <div id="innerContainer">
+        <div id="element" undoscope></div>
+    </div>
+</div>
+
+<script>
+var outerContainer = document.getElementById("outerContainer");
+var innerContainer = document.getElementById("innerContainer");
+var element = document.getElementById("element");
+
+function assert_element_has_undomanager() {
+    assert_equals(element.undoManager.toString(), "[object UndoManager]");
+}
+
+function assert_element_not_have_undomanager() {
+    assert_equals(element.undoManager, null);
+}
+
+test(function() {
+    assert_own_property(element, "undoScope");
+    assert_equals(element.undoScope, true);
+}, "An element has the undoscope attribute set to true.");
+
+test(function() {
+    assert_element_has_undomanager();
+}, "element.undoManager returns an UndoManager object.");
+
+test(function() {
+    element.undoScope = false;
+
+    assert_element_not_have_undomanager();
+}, "After element.undoScope = false, element.undoManager returns null.");
+
+test(function() {
+    element.undoScope = true;
+
+    assert_element_has_undomanager();
+}, "After element.undoScope = true, element.undoManager returns an UndoManager object.");
+
+test(function() {
+    element.setAttribute('undoscope', '');
+    element.removeAttribute('undoscope');
+
+    assert_element_not_have_undomanager();
+}, "After element.removeAttribute('undoscope'), element.undoManager returns null.");
+
+test(function() {
+    element.setAttribute('undoscope', '');
+
+    assert_element_has_undomanager();
+}, "After element.setAttribute('undoscope', ''), element.undoManager returns an UndoManager object.");
+
+test(function() {
+    element.setAttribute('undoscope', 'undoscope');
+
+    assert_element_has_undomanager();
+}, "After element.setAttribute('undoscope', 'undoscope'), element.undoManager returns an UndoManager object.");
+
+test(function() {
+    element.setAttribute('undoscope', '');
+    element.undoScope = false;
+
+    assert_element_not_have_undomanager();
+}, "After element.setAttribute('undoscope', ''); element.undoScope = false;, element.undoManager returns null.");
+
+test(function() {
+    element.removeAttribute('undoscope');
+    element.undoScope = true;
+
+    assert_element_has_undomanager();
+}, "After element.removeAttribute('undoscope'); element.undoScope = true;, element.undoManager returns an UndoManager object.");
+
+test(function() {
+    element.undoScope = true;
+    element.removeAttribute('undoscope');
+
+    assert_element_not_have_undomanager();
+}, "After element.undoScope = true; element.removeAttribute('undoscope');, element.undoManager returns null.");
+
+test(function() {
+    element.undoScope = false;
+    element.setAttribute('undoscope', '');
+
+    assert_element_has_undomanager();
+}, "After element.undoScope = false; element.setAttribute('undoscope', '');, element.undoManager returns an UndoManager object.");
+
+test(function() {
+    innerContainer.contentEditable = "true";
+
+    assert_element_not_have_undomanager();
+}, "After the parant of element becomes editable, element.undoManager returns null.");
+
+test(function() {
+    innerContainer.contentEditable = "false";
+
+    assert_element_has_undomanager();
+}, "After the parant of element becomes non-editable, element.undoManager returns an UndoManager object.");
+
+test(function() {
+    outerContainer.contentEditable = "true";
+    innerContainer.contentEditable = "inherit";
+
+    assert_element_not_have_undomanager();
+}, "After the parant of element becomes editable through inheritance, element.undoManager returns null.");
+
+test(function() {
+    innerContainer.contentEditable = "false";
+    element.contentEdiable = "true";
+
+    assert_element_has_undomanager();
+}, "After the parant of element becomes non-editable and element becomes editable, element.undoManager returns an UndoManager object.");
+
+test(function() {
+    element.undoManager.transact({"execute": function () { }}, false);
+    
+    assert_equals(element.undoManager.length, 1);
+
+    element.undoScope = false;
+    element.undoScope = true;
+
+    assert_equals(element.undoManager.length, 0);
+}, "element.undoManager is disconnected when element.undoScope = false.");
+
+test(function() {
+    element.undoManager.transact({"execute": function () { }}, false);
+    
+    assert_equals(element.undoManager.length, 1);
+
+    innerContainer.contentEditable = "true";
+    innerContainer.contentEditable = "false";
+
+    assert_equals(element.undoManager.length, 0);
+}, "element.undoManager is disconnected when its parent becomes editable.");
+
+test(function() {
+    outerContainer.contentEditable = "false";
+    innerContainer.contentEditable = "false";
+    element.undoManager.transact({"execute": function () { }}, false);
+
+    assert_equals(element.undoManager.length, 1);
+
+    outerContainer.contentEditable = "true";
+
+    assert_equals(element.undoManager.length, 1);
+}, "Setting contentEditable of an ancestor of element to true doesn't disconnect element.undoManager if element is still an editing host.");
+</script>
+</body>
+</html>
index 135532b..b2cbda9 100644 (file)
@@ -1,3 +1,52 @@
+2012-07-26  Sukolsak Sakshuwong  <sukolsak@google.com>
+
+        Implement undoscope attribute.
+        https://bugs.webkit.org/show_bug.cgi?id=88793
+
+        Reviewed by Ryosuke Niwa.
+
+        undoscope attribute support as specified at
+        http://dvcs.w3.org/hg/undomanager/raw-file/tip/undomanager.html
+
+        Test: editing/undomanager/undoscope-attribute.html
+
+        * Target.pri:
+        * WebCore.gypi:
+        * WebCore.vcproj/WebCore.vcproj:
+        * dom/Document.cpp:
+        (WebCore::Document::~Document):
+        * dom/Element.cpp:
+        (WebCore):
+        (WebCore::Element::undoScope):
+        (WebCore::Element::setUndoScope):
+        (WebCore::Element::undoManager):
+        (WebCore::Element::disconnectUndoManager):
+        (WebCore::Element::disconnectUndoManagersInSubtree):
+        * dom/Element.h:
+        (Element):
+        * dom/Element.idl:
+        * dom/ElementRareData.h:
+        (ElementRareData):
+        * editing/UndoManager.cpp:
+        (WebCore::UndoManager::disconnect):
+        (WebCore::UndoManager::transact):
+        (WebCore::UndoManager::undo):
+        (WebCore::UndoManager::redo):
+        (WebCore::UndoManager::clearUndo):
+        (WebCore):
+        (WebCore::UndoManager::clearRedo):
+        (WebCore::UndoManager::clearUndoRedo):
+        (WebCore::UndoManager::isConnected):
+        * editing/UndoManager.h:
+        (WebCore):
+        (UndoManager):
+        (WebCore::UndoManager::length):
+        * editing/UndoManager.idl:
+        * html/HTMLAttributeNames.in:
+        * html/HTMLElement.cpp:
+        (WebCore::HTMLElement::parseAttribute):
+        (WebCore::HTMLElement::setContentEditable):
+
 2012-07-26  Yoshifumi Inoue  <yosin@chromium.org>
 
         [Forms] Introduce runtime feature flags for input type datetime, datetimelocal, month, time, week
index 8f508fe..758ef5a 100644 (file)
@@ -3919,6 +3919,13 @@ contains(DEFINES, ENABLE_MHTML=1) {
         page/PageSerializer.cpp
 }
 
+contains(DEFINES, ENABLE_UNDO_MANAGER=1) {
+    SOURCES += \
+        editing/UndoManager.cpp
+    HEADERS += \
+        editing/UndoManager.h
+}
+
 contains(DEFINES, WTF_USE_LIBPNG=1) {
     SOURCES += platform/image-decoders/ico/ICOImageDecoder.cpp \
                platform/image-decoders/png/PNGImageDecoder.cpp
index 8bc80f0..7ee076c 100644 (file)
             'editing/TextInsertionBaseCommand.h',
             'editing/TextIterator.cpp',
             'editing/TypingCommand.cpp',
+            'editing/UndoManager.cpp',
+            'editing/UndoManager.h',
             'editing/UndoStep.h',
             'editing/UnlinkCommand.cpp',
             'editing/UnlinkCommand.h',
index d75dd86..3a74a3a 100755 (executable)
                                >
                        </File>
                        <File
+                               RelativePath="..\editing\UndoManager.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\UndoManager.h"
+                               >
+                       </File>
+                       <File
                                RelativePath="..\editing\UndoStep.h"
                                >
                        </File>
index 91542a9..169d8a5 100644 (file)
@@ -666,7 +666,7 @@ Document::~Document()
 
 #if ENABLE(UNDO_MANAGER)
     if (m_undoManager)
-        m_undoManager->undoScopeHostDestroyed();
+        m_undoManager->disconnect();
 #endif
 
     // We must call clearRareData() here since a Document class inherits TreeScope
index f2bd1b4..08e9662 100644 (file)
@@ -2200,4 +2200,62 @@ void Element::createMutableAttributeData()
         m_attributeData = m_attributeData->makeMutable();
 }
 
+#if ENABLE(UNDO_MANAGER)
+bool Element::undoScope() const
+{
+    return hasRareData() && elementRareData()->m_undoScope;
+}
+
+void Element::setUndoScope(bool undoScope)
+{
+    ElementRareData* data = ensureElementRareData();
+    data->m_undoScope = undoScope;
+    if (!undoScope)
+        disconnectUndoManager();
+}
+
+PassRefPtr<UndoManager> Element::undoManager()
+{
+    if (!undoScope() || (isContentEditable() && !isRootEditableElement())) {
+        disconnectUndoManager();
+        return 0;
+    }
+    ElementRareData* data = ensureElementRareData();
+    if (!data->m_undoManager)
+        data->m_undoManager = UndoManager::create(this);
+    return data->m_undoManager;
+}
+
+void Element::disconnectUndoManager()
+{
+    if (!hasRareData())
+        return;
+    ElementRareData* data = elementRareData();
+    UndoManager* undoManager = data->m_undoManager.get();
+    if (!undoManager)
+        return;
+    undoManager->clearUndoRedo();
+    undoManager->disconnect();
+    data->m_undoManager.clear();
+}
+
+void Element::disconnectUndoManagersInSubtree()
+{
+    Node* node = firstChild();
+    while (node) {
+        if (node->isElementNode()) {
+            Element* element = toElement(node);
+            if (element->hasRareData() && element->elementRareData()->m_undoManager) {
+                if (!node->isContentEditable()) {
+                    node = node->traverseNextSibling(this);
+                    continue;
+                }
+                element->disconnectUndoManager();
+            }
+        }
+        node = node->traverseNextNode(this);
+    }
+}
+#endif
+
 } // namespace WebCore
index 34ff99d..c14c542 100644 (file)
@@ -440,6 +440,14 @@ public:
         info.addInstrumentedMember(attributeData());
     }
 
+#if ENABLE(UNDO_MANAGER)
+    bool undoScope() const;
+    void setUndoScope(bool);
+    PassRefPtr<UndoManager> undoManager();
+    void disconnectUndoManager();
+    void disconnectUndoManagersInSubtree();
+#endif
+
 protected:
     Element(const QualifiedName& tagName, Document* document, ConstructionType type)
         : ContainerNode(document, type)
index fdf6f36..17a4a7e 100644 (file)
@@ -142,6 +142,11 @@ module core {
         // CSS Regions API
         readonly attribute DOMString webkitRegionOverflow;
 
+#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT
+        readonly attribute [Conditional=UNDO_MANAGER] UndoManager undoManager;
+        attribute [Conditional=UNDO_MANAGER, Reflect] boolean undoScope;
+#endif
+
 #if !defined(LANGUAGE_OBJECTIVE_C) || !LANGUAGE_OBJECTIVE_C
         // Event handler DOM attributes
         attribute [NotEnumerable] EventListener onabort;
index 07394eb..666cc46 100644 (file)
@@ -29,6 +29,7 @@
 #include "HTMLCollection.h"
 #include "NamedNodeMap.h"
 #include "NodeRareData.h"
+#include "UndoManager.h"
 #include <wtf/OwnPtr.h>
 
 namespace WebCore {
@@ -118,6 +119,11 @@ public:
 #if ENABLE(FULLSCREEN_API)
     bool m_containsFullScreenElement;
 #endif
+
+#if ENABLE(UNDO_MANAGER)
+    RefPtr<UndoManager> m_undoManager;
+    bool m_undoScope;
+#endif
 };
 
 inline IntSize defaultMinimumSizeForResizing()
index 9357a1e..55c5509 100644 (file)
@@ -33,6 +33,7 @@
 
 #if ENABLE(UNDO_MANAGER)
 
+#include "Element.h"
 #include "Node.h"
 
 namespace WebCore {
@@ -47,25 +48,74 @@ UndoManager::UndoManager(Node* host)
 {
 }
 
-void UndoManager::undoScopeHostDestroyed()
+void UndoManager::disconnect()
 {
     m_undoScopeHost = 0;
 }
 
-void UndoManager::undo()
+void UndoManager::transact(const Dictionary&, bool, ExceptionCode& ec)
 {
+    if (!isConnected()) {
+        ec = INVALID_ACCESS_ERR;
+        return;
+    }
+    RefPtr<UndoStep> step;
+    m_undoStack.append(step);
 }
 
-void UndoManager::redo()
+void UndoManager::undo(ExceptionCode& ec)
 {
+    if (!isConnected()) {
+        ec = INVALID_ACCESS_ERR;
+        return;
+    }
 }
 
-void UndoManager::clearUndo()
+void UndoManager::redo(ExceptionCode& ec)
 {
+    if (!isConnected()) {
+        ec = INVALID_ACCESS_ERR;
+        return;
+    }
 }
 
-void UndoManager::clearRedo()
+void UndoManager::clearUndo(ExceptionCode& ec)
 {
+    if (!isConnected()) {
+        ec = INVALID_ACCESS_ERR;
+        return;
+    }
+    m_undoStack.clear();
+}
+
+void UndoManager::clearRedo(ExceptionCode& ec)
+{
+    if (!isConnected()) {
+        ec = INVALID_ACCESS_ERR;
+        return;
+    }
+    m_redoStack.clear();
+}
+
+void UndoManager::clearUndoRedo()
+{
+    m_undoStack.clear();
+    m_redoStack.clear();
+}
+
+bool UndoManager::isConnected()
+{
+    if (!m_undoScopeHost)
+        return false;
+    if (!m_undoScopeHost->isElementNode())
+        return true;
+    Element* element = toElement(m_undoScopeHost);
+    ASSERT(element->undoScope());
+    if (element->isContentEditable() && !element->isRootEditableElement()) {
+        element->disconnectUndoManager();
+        return false;
+    }
+    return true;
 }
 
 }
index 56638cd..c318c9e 100644 (file)
 
 #if ENABLE(UNDO_MANAGER)
 
+#include "ExceptionCode.h"
+#include "UndoStep.h"
 #include <wtf/PassRefPtr.h>
 #include <wtf/RefCounted.h>
 #include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
 
 namespace WebCore {
 
+class Dictionary;
 class Node;
 
 class UndoManager : public RefCounted<UndoManager> {
 public:
     static PassRefPtr<UndoManager> create(Node* host);
-    void undoScopeHostDestroyed();
+    void disconnect();
 
-    void undo();
-    void redo();
+    void transact(const Dictionary& transaction, bool merge, ExceptionCode&);
 
-    void clearUndo();
-    void clearRedo();
+    void undo(ExceptionCode&);
+    void redo(ExceptionCode&);
+
+    unsigned length() const { return m_undoStack.size() + m_redoStack.size(); }
+
+    void clearUndo(ExceptionCode&);
+    void clearRedo(ExceptionCode&);
+    void clearUndoRedo();
 
 private:
     explicit UndoManager(Node* host);
+    bool isConnected();
+    
     Node* m_undoScopeHost;
+    Vector<RefPtr<UndoStep> > m_undoStack;
+    Vector<RefPtr<UndoStep> > m_redoStack;
 };
     
 }
index 5ee7bea..b4d0f81 100644 (file)
@@ -33,11 +33,20 @@ module core {
     interface [
         Conditional=UNDO_MANAGER
     ] UndoManager {
-        void undo();
-        void redo();
+        void transact(in Dictionary transaction, in boolean merge)
+            raises(DOMException);
 
-        void clearUndo();
-        void clearRedo();
+        void undo()
+            raises(DOMException);
+        void redo()
+            raises(DOMException);
+
+        readonly attribute unsigned long length;
+
+        void clearUndo()
+            raises(DOMException);
+        void clearRedo()
+            raises(DOMException);
     };
 
 }
index 459078f..9bc1fa4 100644 (file)
@@ -319,6 +319,7 @@ topmargin
 translate
 truespeed
 type
+undoscope
 usemap
 valign
 value
index 8633f3f..d873d9b 100644 (file)
@@ -235,6 +235,10 @@ void HTMLElement::parseAttribute(const Attribute& attribute)
     } else if (attribute.name() == itemtypeAttr) {
         setItemType(attribute.value());
 #endif
+#if ENABLE(UNDO_MANAGER)
+    } else if (attribute.name() == undoscopeAttr) {
+        setUndoScope(!attribute.isNull());
+#endif
     }
 // standard events
     else if (attribute.name() == onclickAttr) {
@@ -659,8 +663,14 @@ void HTMLElement::setContentEditable(const String& enabled, ExceptionCode& ec)
         setAttribute(contenteditableAttr, "plaintext-only");
     else if (equalIgnoringCase(enabled, "inherit"))
         removeAttribute(contenteditableAttr);
-    else
+    else {
         ec = SYNTAX_ERR;
+        return;
+    }
+#if ENABLE(UNDO_MANAGER)
+    if (isContentEditable())
+        disconnectUndoManagersInSubtree();
+#endif
 }
 
 bool HTMLElement::draggable() const