Title: [145379] trunk
Revision
145379
Author
[email protected]
Date
2013-03-11 12:16:01 -0700 (Mon, 11 Mar 2013)

Log Message

MutationCallback should be a WebIDL 'callback', not a [Callback] interface
https://bugs.webkit.org/show_bug.cgi?id=91406

Reviewed by Adam Barth.

Source/WebCore:

Spec: http://dom.spec.whatwg.org/#mutationcallback

Besides no longer calling handleEvent methods on passed-in objects,
throw a TypeError if a non-function is passed to the MutationObserver constructor.
This is per WebIDL: http://www.w3.org/TR/WebIDL/#es-callback-function

Updated MutationObserver constructor tests to exercise TypeError-throwing behavior.

* bindings/js/JSMutationCallback.cpp:
(WebCore::JSMutationCallback::call): Call the callback directly instead of handing off to JSCallbackData; make return value void.
Use jsArray() to convert from WTF::Vector -> JSArray.
* bindings/js/JSMutationCallback.h:
(JSMutationCallback): Rename handleEvent() to call(), make it void.
* bindings/js/JSMutationObserverCustom.cpp:
(WebCore::JSMutationObserverConstructor::constructJSMutationObserver): Throw if passed a non-function.
* bindings/v8/V8MutationCallback.cpp:
(WebCore::V8MutationCallback::V8MutationCallback): Take a v8::Function instead of a v8::Object.
(WebCore::V8MutationCallback::call): Call the callback directly instead of handing off to invokeCallback(); make return value void.
Use v8Array() to convert form WTF::Vector -> JSArray.
* bindings/v8/V8MutationCallback.h:
(WebCore::V8MutationCallback::create): Take a v8::Function instead of a v8::Object.
(V8MutationCallback): ditto
* bindings/v8/custom/V8MutationObserverCustom.cpp:
(WebCore::V8MutationObserver::constructorCustom): Throw if passed a non-function, cast to a v8::Function when constructing callback.
* dom/MutationCallback.h:
(WebCore): Remove unnecessary typedef.
(MutationCallback): Rename handleEvent() to call(), make it void.
* dom/MutationObserver.cpp:
(WebCore::MutationObserver::deliver): Update MutationCallback method name.

LayoutTests:

* fast/dom/MutationObserver/mutation-observer-constructor-expected.txt:
* fast/dom/MutationObserver/mutation-observer-constructor.html: Add test for TypeError-throwing.
Note that the Number and String cases already threw before this patch.

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (145378 => 145379)


--- trunk/LayoutTests/ChangeLog	2013-03-11 19:07:54 UTC (rev 145378)
+++ trunk/LayoutTests/ChangeLog	2013-03-11 19:16:01 UTC (rev 145379)
@@ -1,3 +1,14 @@
+2013-03-11  Adam Klein  <[email protected]>
+
+        MutationCallback should be a WebIDL 'callback', not a [Callback] interface
+        https://bugs.webkit.org/show_bug.cgi?id=91406
+
+        Reviewed by Adam Barth.
+
+        * fast/dom/MutationObserver/mutation-observer-constructor-expected.txt:
+        * fast/dom/MutationObserver/mutation-observer-constructor.html: Add test for TypeError-throwing.
+        Note that the Number and String cases already threw before this patch.
+
 2013-03-11  Julien Chaffraix  <[email protected]>
 
         [CSS Grid Layout] Handle spanning grid items over specified grid tracks

Modified: trunk/LayoutTests/fast/dom/MutationObserver/mutation-observer-constructor-expected.txt (145378 => 145379)


--- trunk/LayoutTests/fast/dom/MutationObserver/mutation-observer-constructor-expected.txt	2013-03-11 19:07:54 UTC (rev 145378)
+++ trunk/LayoutTests/fast/dom/MutationObserver/mutation-observer-constructor-expected.txt	2013-03-11 19:16:01 UTC (rev 145379)
@@ -8,6 +8,10 @@
 PASS typeof WebKitMutationObserver.prototype.disconnect is "function"
 PASS typeof observer.observe is "function"
 PASS typeof observer.disconnect is "function"
+PASS new MutationObserver({ handleEvent: function() {} }) threw exception TypeError: Callback argument must be a function.
+PASS new MutationObserver({}) threw exception TypeError: Callback argument must be a function.
+PASS new MutationObserver(42) threw exception TypeError: Callback argument must be a function.
+PASS new MutationObserver("foo") threw exception TypeError: Callback argument must be a function.
 PASS successfullyParsed is true
 
 TEST COMPLETE

Modified: trunk/LayoutTests/fast/dom/MutationObserver/mutation-observer-constructor.html (145378 => 145379)


--- trunk/LayoutTests/fast/dom/MutationObserver/mutation-observer-constructor.html	2013-03-11 19:07:54 UTC (rev 145378)
+++ trunk/LayoutTests/fast/dom/MutationObserver/mutation-observer-constructor.html	2013-03-11 19:16:01 UTC (rev 145379)
@@ -1,28 +1,20 @@
 <!DOCTYPE html>
-<html>
-<head>
-<meta charset="utf-8">
-<script src=""
-<title></title>
-</head>
 <body>
-<p id=description></p>
-<div id="console"></div>
+<script src=""
 <script>
-
-function runTest() {
-    shouldBeNonNull('window.WebKitMutationObserver');
-    shouldBeEqualToString('typeof WebKitMutationObserver.prototype.observe', 'function');
-    shouldBeEqualToString('typeof WebKitMutationObserver.prototype.disconnect', 'function');
-    window.observer = new MutationObserver(function(mutations) { });
-    shouldBeEqualToString('typeof observer.observe', 'function');
-    shouldBeEqualToString('typeof observer.disconnect', 'function');
-}
-
 description('Test the constructor of WebKitMutationObserver');
 
-runTest();
+shouldBeNonNull('window.WebKitMutationObserver');
+shouldBeEqualToString('typeof WebKitMutationObserver.prototype.observe', 'function');
+shouldBeEqualToString('typeof WebKitMutationObserver.prototype.disconnect', 'function');
+window.observer = new MutationObserver(function(mutations) { });
+shouldBeEqualToString('typeof observer.observe', 'function');
+shouldBeEqualToString('typeof observer.disconnect', 'function');
+
+shouldThrow('new MutationObserver({ handleEvent: function() {} })');
+shouldThrow('new MutationObserver({})');
+shouldThrow('new MutationObserver(42)');
+shouldThrow('new MutationObserver("foo")');
 </script>
 <script src=""
 </body>
-</html>

Modified: trunk/Source/WebCore/ChangeLog (145378 => 145379)


--- trunk/Source/WebCore/ChangeLog	2013-03-11 19:07:54 UTC (rev 145378)
+++ trunk/Source/WebCore/ChangeLog	2013-03-11 19:16:01 UTC (rev 145379)
@@ -1,3 +1,40 @@
+2013-03-11  Adam Klein  <[email protected]>
+
+        MutationCallback should be a WebIDL 'callback', not a [Callback] interface
+        https://bugs.webkit.org/show_bug.cgi?id=91406
+
+        Reviewed by Adam Barth.
+
+        Spec: http://dom.spec.whatwg.org/#mutationcallback
+
+        Besides no longer calling handleEvent methods on passed-in objects,
+        throw a TypeError if a non-function is passed to the MutationObserver constructor.
+        This is per WebIDL: http://www.w3.org/TR/WebIDL/#es-callback-function
+
+        Updated MutationObserver constructor tests to exercise TypeError-throwing behavior.
+
+        * bindings/js/JSMutationCallback.cpp:
+        (WebCore::JSMutationCallback::call): Call the callback directly instead of handing off to JSCallbackData; make return value void.
+        Use jsArray() to convert from WTF::Vector -> JSArray.
+        * bindings/js/JSMutationCallback.h:
+        (JSMutationCallback): Rename handleEvent() to call(), make it void.
+        * bindings/js/JSMutationObserverCustom.cpp:
+        (WebCore::JSMutationObserverConstructor::constructJSMutationObserver): Throw if passed a non-function.
+        * bindings/v8/V8MutationCallback.cpp:
+        (WebCore::V8MutationCallback::V8MutationCallback): Take a v8::Function instead of a v8::Object.
+        (WebCore::V8MutationCallback::call): Call the callback directly instead of handing off to invokeCallback(); make return value void.
+        Use v8Array() to convert form WTF::Vector -> JSArray.
+        * bindings/v8/V8MutationCallback.h:
+        (WebCore::V8MutationCallback::create): Take a v8::Function instead of a v8::Object.
+        (V8MutationCallback): ditto
+        * bindings/v8/custom/V8MutationObserverCustom.cpp:
+        (WebCore::V8MutationObserver::constructorCustom): Throw if passed a non-function, cast to a v8::Function when constructing callback.
+        * dom/MutationCallback.h:
+        (WebCore): Remove unnecessary typedef.
+        (MutationCallback): Rename handleEvent() to call(), make it void.
+        * dom/MutationObserver.cpp:
+        (WebCore::MutationObserver::deliver): Update MutationCallback method name.
+
 2013-03-11  Julien Chaffraix  <[email protected]>
 
         [CSS Grid Layout] Handle spanning grid items over specified grid tracks

Modified: trunk/Source/WebCore/bindings/js/JSMutationCallback.cpp (145378 => 145379)


--- trunk/Source/WebCore/bindings/js/JSMutationCallback.cpp	2013-03-11 19:07:54 UTC (rev 145378)
+++ trunk/Source/WebCore/bindings/js/JSMutationCallback.cpp	2013-03-11 19:16:01 UTC (rev 145379)
@@ -27,6 +27,8 @@
 #include "config.h"
 #include "JSMutationCallback.h"
 
+#include "JSDOMGlobalObject.h"
+#include "JSMainThreadExecState.h"
 #include "JSMutationObserver.h"
 #include "JSMutationRecord.h"
 #include "ScriptExecutionContext.h"
@@ -47,39 +49,50 @@
 {
 }
 
-bool JSMutationCallback::handleEvent(MutationRecordArray* mutations, MutationObserver* observer)
+void JSMutationCallback::call(const Vector<RefPtr<MutationRecord> >& mutations, MutationObserver* observer)
 {
     if (!canInvokeCallback())
-        return true;
+        return;
 
     RefPtr<JSMutationCallback> protect(this);
 
     JSLockHolder lock(m_isolatedWorld->globalData());
 
     if (!m_callback)
-        return false;
+        return;
 
+    JSValue callback = m_callback.get();
+    CallData callData;
+    CallType callType = getCallData(callback, callData);
+    if (callType == CallTypeNone) {
+        ASSERT_NOT_REACHED();
+        return;
+    }
+
     ScriptExecutionContext* context = scriptExecutionContext();
     if (!context)
-        return false;
+        return;
+    ASSERT(context->isDocument());
 
     JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(context, m_isolatedWorld.get());
     ExecState* exec = globalObject->globalExec();
 
-    MarkedArgumentBuffer mutationList;
-    for (size_t i = 0; i < mutations->size(); ++i)
-        mutationList.append(toJS(exec, globalObject, mutations->at(i).get()));
-
     JSValue jsObserver = toJS(exec, globalObject, observer);
 
     MarkedArgumentBuffer args;
-    args.append(constructArray(exec, 0, globalObject, mutationList));
+    args.append(jsArray(exec, globalObject, mutations));
     args.append(jsObserver);
 
-    bool raisedException = false;
-    // FIXME: Extract invokeCallback() method.
-    JSCallbackData(m_callback.get(), globalObject).invokeCallback(jsObserver, args, &raisedException);
-    return !raisedException;
+    globalObject->globalData().timeoutChecker.start();
+    InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionCall(context, callType, callData);
+
+    JSMainThreadExecState::call(exec, callback, callType, callData, jsObserver, args);
+
+    InspectorInstrumentation::didCallFunction(cookie);
+    globalObject->globalData().timeoutChecker.stop();
+
+    if (exec->hadException())
+        reportCurrentException(exec);
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/bindings/js/JSMutationCallback.h (145378 => 145379)


--- trunk/Source/WebCore/bindings/js/JSMutationCallback.h	2013-03-11 19:07:54 UTC (rev 145378)
+++ trunk/Source/WebCore/bindings/js/JSMutationCallback.h	2013-03-11 19:16:01 UTC (rev 145379)
@@ -27,12 +27,16 @@
 #define JSMutationCallback_h
 
 #include "ActiveDOMCallback.h"
-#include "JSCallbackData.h"
+#include "DOMWrapperWorld.h"
 #include "MutationCallback.h"
+#include <heap/Weak.h>
+#include <runtime/JSObject.h>
 #include <wtf/Forward.h>
 
 namespace WebCore {
 
+class JSDOMGlobalObject;
+
 class JSMutationCallback : public MutationCallback, public ActiveDOMCallback {
 public:
     static PassRefPtr<JSMutationCallback> create(JSC::JSObject* callback, JSDOMGlobalObject* globalObject)
@@ -42,7 +46,7 @@
 
     virtual ~JSMutationCallback();
 
-    virtual bool handleEvent(MutationRecordArray* mutations, MutationObserver*) OVERRIDE;
+    virtual void call(const Vector<RefPtr<MutationRecord> >&, MutationObserver*) OVERRIDE;
 
     virtual ScriptExecutionContext* scriptExecutionContext() const OVERRIDE { return ContextDestructionObserver::scriptExecutionContext(); }
 

Modified: trunk/Source/WebCore/bindings/js/JSMutationObserverCustom.cpp (145378 => 145379)


--- trunk/Source/WebCore/bindings/js/JSMutationObserverCustom.cpp	2013-03-11 19:07:54 UTC (rev 145378)
+++ trunk/Source/WebCore/bindings/js/JSMutationObserverCustom.cpp	2013-03-11 19:16:01 UTC (rev 145379)
@@ -49,10 +49,9 @@
         return throwVMError(exec, createNotEnoughArgumentsError(exec));
 
     JSObject* object = exec->argument(0).getObject();
-    if (!object) {
-        setDOMException(exec, TYPE_MISMATCH_ERR);
-        return JSValue::encode(jsUndefined());
-    }
+    CallData callData;
+    if (!object || object->methodTable()->getCallData(object, callData) == CallTypeNone)
+        return throwVMError(exec, createTypeError(exec, "Callback argument must be a function"));
 
     JSMutationObserverConstructor* jsConstructor = jsCast<JSMutationObserverConstructor*>(exec->callee());
     RefPtr<JSMutationCallback> callback = JSMutationCallback::create(object, jsConstructor->globalObject());

Modified: trunk/Source/WebCore/bindings/v8/V8MutationCallback.cpp (145378 => 145379)


--- trunk/Source/WebCore/bindings/v8/V8MutationCallback.cpp	2013-03-11 19:07:54 UTC (rev 145378)
+++ trunk/Source/WebCore/bindings/v8/V8MutationCallback.cpp	2013-03-11 19:16:01 UTC (rev 145379)
@@ -26,16 +26,16 @@
 #include "config.h"
 #include "V8MutationCallback.h"
 
+#include "ScriptController.h"
 #include "ScriptExecutionContext.h"
 #include "V8Binding.h"
-#include "V8Callback.h"
 #include "V8MutationObserver.h"
 #include "V8MutationRecord.h"
 #include <wtf/Assertions.h>
 
 namespace WebCore {
 
-V8MutationCallback::V8MutationCallback(v8::Handle<v8::Object> callback, ScriptExecutionContext* context, v8::Handle<v8::Object> owner, v8::Isolate* isolate)
+V8MutationCallback::V8MutationCallback(v8::Handle<v8::Function> callback, ScriptExecutionContext* context, v8::Handle<v8::Object> owner, v8::Isolate* isolate)
     : ActiveDOMCallback(context)
     , m_callback(callback)
     , m_world(DOMWrapperWorld::isolatedWorld(v8::Context::GetCurrent()))
@@ -44,44 +44,38 @@
     m_callback.get().MakeWeak(isolate, this, &V8MutationCallback::weakCallback);
 }
 
-bool V8MutationCallback::handleEvent(MutationRecordArray* mutations, MutationObserver* observer)
+void V8MutationCallback::call(const Vector<RefPtr<MutationRecord> >& mutations, MutationObserver* observer)
 {
-    ASSERT(mutations);
-    if (!mutations)
-        return true;
-
     if (!canInvokeCallback())
-        return true;
+        return;
 
     v8::HandleScope handleScope;
 
     v8::Handle<v8::Context> v8Context = toV8Context(scriptExecutionContext(), m_world.get());
     if (v8Context.IsEmpty())
-        return true;
+        return;
 
     v8::Context::Scope scope(v8Context);
+    v8::Isolate* isolate = v8Context->GetIsolate();
 
-    v8::Local<v8::Array> mutationsArray = v8::Array::New(mutations->size());
-    for (size_t i = 0; i < mutations->size(); ++i)
-        mutationsArray->Set(v8Integer(i, v8Context->GetIsolate()), toV8(mutations->at(i).get(), v8::Handle<v8::Object>(), v8Context->GetIsolate()));
+    v8::Handle<v8::Function> callback = v8::Local<v8::Function>::New(isolate, m_callback.get());
 
-    v8::Handle<v8::Value> observerHandle = toV8(observer, v8::Handle<v8::Object>(), v8Context->GetIsolate());
+    v8::Handle<v8::Value> observerHandle = toV8(observer, v8::Handle<v8::Object>(), isolate);
     if (observerHandle.IsEmpty()) {
         if (!isScriptControllerTerminating())
             CRASH();
-        return true;
+        return;
     }
 
     if (!observerHandle->IsObject())
-        return true;
+        return;
 
-    v8::Handle<v8::Value> argv[] = {
-        mutationsArray,
-        observerHandle
-    };
+    v8::Handle<v8::Object> thisObject = v8::Handle<v8::Object>::Cast(observerHandle);
+    v8::Handle<v8::Value> argv[] = { v8Array(mutations, isolate), observerHandle };
 
-    bool callbackReturnValue = false;
-    return !invokeCallback(m_callback.get(), v8::Handle<v8::Object>::Cast(observerHandle), 2, argv, callbackReturnValue, scriptExecutionContext());
+    v8::TryCatch exceptionCatcher;
+    exceptionCatcher.SetVerbose(true);
+    ScriptController::callFunctionWithInstrumentation(scriptExecutionContext(), callback, thisObject, 2, argv);
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/bindings/v8/V8MutationCallback.h (145378 => 145379)


--- trunk/Source/WebCore/bindings/v8/V8MutationCallback.h	2013-03-11 19:07:54 UTC (rev 145378)
+++ trunk/Source/WebCore/bindings/v8/V8MutationCallback.h	2013-03-11 19:16:01 UTC (rev 145379)
@@ -39,18 +39,17 @@
 
 class V8MutationCallback : public MutationCallback, public ActiveDOMCallback {
 public:
-    static PassRefPtr<V8MutationCallback> create(v8::Handle<v8::Value> value, ScriptExecutionContext* context, v8::Handle<v8::Object> owner, v8::Isolate* isolate)
+    static PassRefPtr<V8MutationCallback> create(v8::Handle<v8::Function> callback, ScriptExecutionContext* context, v8::Handle<v8::Object> owner, v8::Isolate* isolate)
     {
-        ASSERT(value->IsObject());
         ASSERT(context);
-        return adoptRef(new V8MutationCallback(v8::Handle<v8::Object>::Cast(value), context, owner, isolate));
+        return adoptRef(new V8MutationCallback(callback, context, owner, isolate));
     }
 
-    virtual bool handleEvent(MutationRecordArray*, MutationObserver*) OVERRIDE;
+    virtual void call(const Vector<RefPtr<MutationRecord> >&, MutationObserver*) OVERRIDE;
     virtual ScriptExecutionContext* scriptExecutionContext() const OVERRIDE { return ContextDestructionObserver::scriptExecutionContext(); }
 
 private:
-    V8MutationCallback(v8::Handle<v8::Object>, ScriptExecutionContext*, v8::Handle<v8::Object>, v8::Isolate*);
+    V8MutationCallback(v8::Handle<v8::Function>, ScriptExecutionContext*, v8::Handle<v8::Object>, v8::Isolate*);
 
     static void weakCallback(v8::Isolate* isolate, v8::Persistent<v8::Value> wrapper, void* parameter)
     {
@@ -58,7 +57,7 @@
         object->m_callback.clear();
     }
 
-    ScopedPersistent<v8::Object> m_callback;
+    ScopedPersistent<v8::Function> m_callback;
     RefPtr<DOMWrapperWorld> m_world;
 };
 

Modified: trunk/Source/WebCore/bindings/v8/custom/V8MutationObserverCustom.cpp (145378 => 145379)


--- trunk/Source/WebCore/bindings/v8/custom/V8MutationObserverCustom.cpp	2013-03-11 19:07:54 UTC (rev 145378)
+++ trunk/Source/WebCore/bindings/v8/custom/V8MutationObserverCustom.cpp	2013-03-11 19:16:01 UTC (rev 145379)
@@ -47,13 +47,13 @@
         return throwNotEnoughArgumentsError(args.GetIsolate());
 
     v8::Local<v8::Value> arg = args[0];
-    if (!arg->IsObject())
-        return setDOMException(TYPE_MISMATCH_ERR, args.GetIsolate());
+    if (!arg->IsFunction())
+        return throwTypeError("Callback argument must be a function", args.GetIsolate());
 
     ScriptExecutionContext* context = getScriptExecutionContext();
     v8::Handle<v8::Object> wrapper = args.Holder();
 
-    RefPtr<MutationCallback> callback = V8MutationCallback::create(arg, context, wrapper, args.GetIsolate());
+    RefPtr<MutationCallback> callback = V8MutationCallback::create(v8::Handle<v8::Function>::Cast(arg), context, wrapper, args.GetIsolate());
     RefPtr<MutationObserver> observer = MutationObserver::create(callback.release());
 
     V8DOMWrapper::associateObjectWithWrapper(observer.release(), &info, wrapper, args.GetIsolate(), WrapperConfiguration::Dependent);

Modified: trunk/Source/WebCore/dom/MutationCallback.h (145378 => 145379)


--- trunk/Source/WebCore/dom/MutationCallback.h	2013-03-11 19:07:54 UTC (rev 145378)
+++ trunk/Source/WebCore/dom/MutationCallback.h	2013-03-11 19:16:01 UTC (rev 145379)
@@ -40,13 +40,11 @@
 class MutationRecord;
 class MutationObserver;
 
-typedef Vector<RefPtr<MutationRecord> > MutationRecordArray;
-
 class MutationCallback : public RefCounted<MutationCallback> {
 public:
     virtual ~MutationCallback() { }
 
-    virtual bool handleEvent(MutationRecordArray*, MutationObserver*) = 0;
+    virtual void call(const Vector<RefPtr<MutationRecord> >&, MutationObserver*) = 0;
     virtual ScriptExecutionContext* scriptExecutionContext() const = 0;
 };
 

Modified: trunk/Source/WebCore/dom/MutationObserver.cpp (145378 => 145379)


--- trunk/Source/WebCore/dom/MutationObserver.cpp	2013-03-11 19:07:54 UTC (rev 145378)
+++ trunk/Source/WebCore/dom/MutationObserver.cpp	2013-03-11 19:16:01 UTC (rev 145379)
@@ -204,7 +204,7 @@
     Vector<RefPtr<MutationRecord> > records;
     records.swap(m_records);
 
-    m_callback->handleEvent(&records, this);
+    m_callback->call(records, this);
 }
 
 void MutationObserver::deliverAllMutations()
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to