Implement UndoManager's item() method
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 23 Aug 2012 05:58:10 +0000 (05:58 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 23 Aug 2012 05:58:10 +0000 (05:58 +0000)
https://bugs.webkit.org/show_bug.cgi?id=94671

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

Source/WebCore:

This patch implements UndoManager's item() method and its V8 custom binding.
We need to use custom binding here because we need to return user objects
that are stored in a hidden property of DOMTransaction wrappers, not returning
the wrappers themselves.

Test: editing/undomanager/undomanager-item.html

* bindings/js/JSUndoManagerCustom.cpp:
(WebCore::JSUndoManager::item):
(WebCore):
* bindings/v8/DOMTransaction.cpp:
(WebCore::DOMTransaction::data):
(WebCore):
(WebCore::DOMTransaction::setData):
(WebCore::DOMTransaction::getFunction):
* bindings/v8/DOMTransaction.h:
* bindings/v8/custom/V8UndoManagerCustom.cpp:
(WebCore::V8UndoManager::transactCallback):
(WebCore::V8UndoManager::itemCallback):
(WebCore):
* editing/UndoManager.cpp:
(WebCore::UndoManager::item):
(WebCore):
* editing/UndoManager.h:
(UndoManager):
* editing/UndoManager.idl:

LayoutTests:

* editing/undomanager/undomanager-item-expected.txt: Added.
* editing/undomanager/undomanager-item.html: Added.

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

LayoutTests/ChangeLog
LayoutTests/editing/undomanager/undomanager-item-expected.txt [new file with mode: 0644]
LayoutTests/editing/undomanager/undomanager-item.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/JSUndoManagerCustom.cpp
Source/WebCore/bindings/v8/DOMTransaction.cpp
Source/WebCore/bindings/v8/DOMTransaction.h
Source/WebCore/bindings/v8/custom/V8UndoManagerCustom.cpp
Source/WebCore/editing/UndoManager.cpp
Source/WebCore/editing/UndoManager.h
Source/WebCore/editing/UndoManager.idl

index 96c360b..4a3f57f 100644 (file)
@@ -1,3 +1,13 @@
+2012-08-22  Sukolsak Sakshuwong  <sukolsak@google.com>
+
+        Implement UndoManager's item() method
+        https://bugs.webkit.org/show_bug.cgi?id=94671
+
+        Reviewed by Ryosuke Niwa.
+
+        * editing/undomanager/undomanager-item-expected.txt: Added.
+        * editing/undomanager/undomanager-item.html: Added.
+
 2012-08-22  Dominic Mazzoni  <dmazzoni@google.com>
 
         AX: Focusable elements without a role should not be ignored
diff --git a/LayoutTests/editing/undomanager/undomanager-item-expected.txt b/LayoutTests/editing/undomanager/undomanager-item-expected.txt
new file mode 100644 (file)
index 0000000..77703ac
--- /dev/null
@@ -0,0 +1,11 @@
+This tests UndoManager's item() method.
+
+PASS undoManager has item property. 
+PASS After two transact calls that are merged into one, item(0) returns an array of the two transactions in the correct order. 
+PASS item(1) returns null because it's out of range. 
+PASS After an undo call, item(0) stills return an array of the two transactions in the correct order. 
+PASS After a redo call, undoManager's position is back to 0. 
+PASS After executing an editing command, item(0) returns an array of an object with label property. 
+PASS item(1) now returns an array of the two transactions. 
+PASS item(1) returns a different object than item(1). 
+
diff --git a/LayoutTests/editing/undomanager/undomanager-item.html b/LayoutTests/editing/undomanager/undomanager-item.html
new file mode 100644 (file)
index 0000000..c14d085
--- /dev/null
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>undoManager.item()</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+</head>
+<body>
+This tests UndoManager's item() method.
+
+<div id="edit" contentEditable>Hello</div>
+
+<script>
+var undoManager = document.undoManager;
+
+test(function() {
+    assert_idl_attribute(undoManager, "item");
+}, "undoManager has item property.");
+
+var transaction = {"executeAutomatic": function() { }};
+var transaction2 = {"executeAutomatic": function() { }};
+
+test(function() {
+    undoManager.transact(transaction);
+    undoManager.transact(transaction2, true);
+    
+    var entry = undoManager.item(0);
+    assert_equals(entry.length, 2);
+    assert_equals(entry[0], transaction);
+    assert_equals(entry[1], transaction2);
+}, "After two transact calls that are merged into one, item(0) returns an array of "
+    + "the two transactions in the correct order.");
+
+test(function() {
+    var entry = undoManager.item(1);
+    assert_equals(entry, null);
+}, "item(1) returns null because it's out of range.");
+
+test(function() {
+    undoManager.undo();
+
+    var entry = undoManager.item(0);
+    assert_equals(entry.length, 2);
+    assert_equals(entry[0], transaction);
+    assert_equals(entry[1], transaction2);
+}, "After an undo call, item(0) stills return an array of the two transactions in the correct order.");
+
+test(function() {
+    undoManager.redo();
+    assert_equals(undoManager.position, 0);
+}, "After a redo call, undoManager's position is back to 0.");
+
+test(function() {
+    var range = document.createRange();
+    range.selectNodeContents(document.getElementById("edit"));
+    window.getSelection().addRange(range);
+    document.execCommand("Bold");
+    
+    var entry = undoManager.item(0);
+    assert_equals(entry.length, 1);
+    assert_own_property(entry[0], "label");
+}, "After executing an editing command, item(0) returns an array of an object with label property.");
+
+test(function() {
+    var entry = undoManager.item(1);
+    assert_equals(entry.length, 2);
+    assert_equals(entry[0], transaction);
+    assert_equals(entry[1], transaction2);
+}, "item(1) now returns an array of the two transactions.");
+
+test(function() {
+    assert_not_equals(undoManager.item(1), undoManager.item(1));
+}, "item(1) returns a different object than item(1).");
+
+document.getElementById("edit").style.display = "none";
+</script>
+</body>
+</html>
index a34c7df..2083238 100644 (file)
@@ -1,3 +1,37 @@
+2012-08-22  Sukolsak Sakshuwong  <sukolsak@google.com>
+
+        Implement UndoManager's item() method
+        https://bugs.webkit.org/show_bug.cgi?id=94671
+
+        Reviewed by Ryosuke Niwa.
+
+        This patch implements UndoManager's item() method and its V8 custom binding.
+        We need to use custom binding here because we need to return user objects
+        that are stored in a hidden property of DOMTransaction wrappers, not returning
+        the wrappers themselves.
+
+        Test: editing/undomanager/undomanager-item.html
+
+        * bindings/js/JSUndoManagerCustom.cpp:
+        (WebCore::JSUndoManager::item):
+        (WebCore):
+        * bindings/v8/DOMTransaction.cpp:
+        (WebCore::DOMTransaction::data):
+        (WebCore):
+        (WebCore::DOMTransaction::setData):
+        (WebCore::DOMTransaction::getFunction):
+        * bindings/v8/DOMTransaction.h:
+        * bindings/v8/custom/V8UndoManagerCustom.cpp:
+        (WebCore::V8UndoManager::transactCallback):
+        (WebCore::V8UndoManager::itemCallback):
+        (WebCore):
+        * editing/UndoManager.cpp:
+        (WebCore::UndoManager::item):
+        (WebCore):
+        * editing/UndoManager.h:
+        (UndoManager):
+        * editing/UndoManager.idl:
+
 2012-08-22  Dominic Mazzoni  <dmazzoni@google.com>
 
         AX: Focusable elements without a role should not be ignored
index 94cc0b6..8fc09c5 100644 (file)
@@ -38,6 +38,12 @@ JSValue JSUndoManager::transact(ExecState*)
     return jsUndefined();
 }
 
+JSValue JSUndoManager::item(ExecState*)
+{
+    // FIXME: implement JSC bindings
+    return jsUndefined();
+}
+
 }
 
 #endif
index d6e36ad..225e5ab 100644 (file)
@@ -96,17 +96,29 @@ void DOMTransaction::reapply()
         m_undoManager->registerUndoStep(this);
 }
 
-v8::Handle<v8::Function> DOMTransaction::getFunction(const char* propertyName)
+v8::Handle<v8::Value> DOMTransaction::data()
 {
     v8::Handle<v8::Object> wrapper = v8::Handle<v8::Object>::Cast(toV8(this));
     if (wrapper.IsEmpty())
-        return v8::Handle<v8::Function>();
+        return v8::Handle<v8::Value>();
+    return wrapper->GetHiddenValue(V8HiddenPropertyName::domTransactionData());
+}
 
-    v8::Local<v8::Value> data = wrapper->GetHiddenValue(V8HiddenPropertyName::domTransactionData());
-    if (data.IsEmpty() || !data->IsObject())
-        return v8::Handle<v8::Function>();
+void DOMTransaction::setData(v8::Handle<v8::Value> newData)
+{
+    v8::Handle<v8::Object> wrapper = v8::Handle<v8::Object>::Cast(toV8(this));
+    if (wrapper.IsEmpty())
+        return;
+    wrapper->SetHiddenValue(V8HiddenPropertyName::domTransactionData(), newData);
+}
 
-    v8::Local<v8::Value> function = v8::Local<v8::Object>::Cast(data)->Get(v8::String::NewSymbol(propertyName));
+v8::Handle<v8::Function> DOMTransaction::getFunction(const char* propertyName)
+{
+    v8::Handle<v8::Value> dictionary = data();
+    if (dictionary.IsEmpty() || !dictionary->IsObject())
+        return v8::Handle<v8::Function>();
+    
+    v8::Local<v8::Value> function = v8::Handle<v8::Object>::Cast(dictionary)->Get(v8::String::NewSymbol(propertyName));
     if (function.IsEmpty() || !function->IsFunction())
         return v8::Handle<v8::Function>();
 
index 88fb30d..96a1f1d 100644 (file)
@@ -47,6 +47,9 @@ public:
     virtual EditAction editingAction() const OVERRIDE { return EditActionUnspecified; }
     virtual bool isDOMTransaction() const OVERRIDE { return true; }
 
+    v8::Handle<v8::Value> data();
+    void setData(v8::Handle<v8::Value>);
+
     UndoManager* undoManager() const { return m_undoManager; }
     void setUndoManager(UndoManager* undoManager) { m_undoManager = undoManager; }
 
index c7088bb..71be095 100644 (file)
@@ -31,7 +31,6 @@
 #include "DOMTransaction.h"
 #include "ExceptionCode.h"
 #include "V8DOMTransaction.h"
-#include "V8HiddenPropertyName.h"
 
 namespace WebCore {
 
@@ -49,9 +48,7 @@ v8::Handle<v8::Value> V8UndoManager::transactCallback(const v8::Arguments& args)
     EXCEPTION_BLOCK(bool, merge, MAYBE_MISSING_PARAMETER(args, 1, DefaultIsUndefined)->BooleanValue());
 
     RefPtr<DOMTransaction> transaction = DOMTransaction::create(WorldContextHandle(UseCurrentWorld));
-    v8::Handle<v8::Object> transactionWrapper = v8::Handle<v8::Object>::Cast(toV8(transaction.get()));
-
-    transactionWrapper->SetHiddenValue(V8HiddenPropertyName::domTransactionData(), dictionary);
+    transaction->setData(dictionary);
 
     ExceptionCode ec = 0;
     imp->transact(transaction, merge, ec);
@@ -60,6 +57,37 @@ v8::Handle<v8::Value> V8UndoManager::transactCallback(const v8::Arguments& args)
     return v8Undefined();
 }
 
+v8::Handle<v8::Value> V8UndoManager::itemCallback(const v8::Arguments& args)
+{
+    INC_STATS("DOM.UndoManager.item");
+    if (args.Length() < 1)
+        return throwNotEnoughArgumentsError(args.GetIsolate());
+    UndoManager* imp = V8UndoManager::toNative(args.Holder());
+
+    EXCEPTION_BLOCK(unsigned, index, toUInt32(MAYBE_MISSING_PARAMETER(args, 0, DefaultIsUndefined)));
+
+    if (index >= imp->length())
+        return v8::Null(args.GetIsolate());
+
+    const UndoManagerEntry& entry = imp->item(index);
+
+    v8::Handle<v8::Array> result = v8::Array::New(entry.size());
+    v8::Isolate* isolate = args.GetIsolate();
+    for (size_t index = 0; index < entry.size(); ++index) {
+        UndoStep* step = entry[index].get();
+        if (step->isDOMTransaction())
+            result->Set(v8Integer(index, isolate), static_cast<DOMTransaction*>(step)->data());
+        else {
+            // FIXME: We shouldn't be creating new object each time we return.
+            // Object for the same native editing command should always be the same.
+            v8::Handle<v8::Object> object = v8::Object::New();
+            object->Set(v8::String::NewSymbol("label"), v8::String::New("[Editing command]"));
+            result->Set(v8Integer(index, isolate), object);
+        }
+    }
+    return result;
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(UNDO_MANAGER)
index e506424..419de31 100644 (file)
@@ -160,6 +160,17 @@ void UndoManager::redo(ExceptionCode& ec)
     m_redoStack.removeLast();
 }
 
+UndoManagerEntry UndoManager::item(unsigned index) const
+{
+    ASSERT(index < length());
+    if (index < m_redoStack.size()) {
+        UndoManagerEntry entry = *m_redoStack[index];
+        entry.reverse();
+        return entry;
+    }
+    return *m_undoStack[length() - index - 1];
+}
+
 void UndoManager::registerUndoStep(PassRefPtr<UndoStep> step)
 {
     if (!m_isInProgress) {
index dc5d67c..f43039e 100644 (file)
@@ -62,6 +62,8 @@ public:
     void undo(ExceptionCode& = ASSERT_NO_EXCEPTION);
     void redo(ExceptionCode& = ASSERT_NO_EXCEPTION);
 
+    UndoManagerEntry item(unsigned index) const;
+
     unsigned length() const { return m_undoStack.size() + m_redoStack.size(); }
     unsigned position() const { return m_redoStack.size(); }
 
index e65677b..c228b9e 100644 (file)
@@ -41,6 +41,8 @@ module core {
         void redo()
             raises(DOMException);
 
+        [Custom] sequence<DOMTransaction> item(in unsigned long index);
+
         readonly attribute unsigned long length;
         readonly attribute unsigned long position;