Use V8 completion callback API to assert that V8RecursionScope is on the stack whenev...
authorrafaelw@chromium.org <rafaelw@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Apr 2012 00:28:24 +0000 (00:28 +0000)
committerrafaelw@chromium.org <rafaelw@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Apr 2012 00:28:24 +0000 (00:28 +0000)
https://bugs.webkit.org/show_bug.cgi?id=79131

Reviewed by Adam Barth.

Source/WebCore:

End-of-microtask work (cancelling outstanding IDB transactions,
delivering DOM mutations) depend on V8RecursionScope being on the
stack whenever a call is made into script. V8 provides a completion
callback API that could be used to hook these "end-of-microtask"
events, but it turns out that WebKit calls into script for various
reasons besides running script from the page. For example, constructing
wrapper objects from function templates counts as "running script",
yet it's not appropriate to run this callback every time a JS wrapper
is constructed.

Instead, this patch makes use of the V8 completion callback mechanism
only in debug mode, to assert that either a V8RecursionScope (when
calling author script) or a V8RecursionScope::MicrotaskSuppression
(when calling non-author script) is on the stack when V8 thinks it's
finished executing script. This requires dropping MicrotaskSuppression
objects in a bunch of places, most notably the Inspector.
Note that in release mode, this class does nothing and thus should be
optimized away.

No new tests. Existing tests have appropriate coverage.

* Target.pri:
* WebCore.gypi:
* bindings/v8/DateExtension.cpp:
(WebCore::DateExtension::setAllowSleep):
* bindings/v8/PageScriptDebugServer.cpp:
(WebCore::PageScriptDebugServer::addListener):
* bindings/v8/ScriptController.cpp:
(WebCore::ScriptController::callFunctionEvenIfScriptDisabled):
(WebCore):
(WebCore::ScriptController::collectGarbage):
* bindings/v8/ScriptController.h:
(ScriptController):
* bindings/v8/ScriptDebugServer.cpp:
(WebCore::ScriptDebugServer::callDebuggerMethod):
(WebCore):
(WebCore::ScriptDebugServer::pauseOnExceptionsState):
(WebCore::ScriptDebugServer::setPauseOnExceptionsState):
(WebCore::ScriptDebugServer::stepIntoStatement):
(WebCore::ScriptDebugServer::stepOverStatement):
(WebCore::ScriptDebugServer::stepOutOfFunction):
(WebCore::ScriptDebugServer::setScriptSource):
(WebCore::ScriptDebugServer::currentCallFrame):
(WebCore::ScriptDebugServer::handleV8DebugEvent):
(WebCore::ScriptDebugServer::ensureDebuggerScriptCompiled):
* bindings/v8/ScriptDebugServer.h:
(ScriptDebugServer):
* bindings/v8/ScriptFunctionCall.cpp:
(WebCore::ScriptFunctionCall::call):
* bindings/v8/V8Binding.cpp:
(WebCore::V8BindingPerIsolateData::V8BindingPerIsolateData):
* bindings/v8/V8Binding.h:
(V8BindingPerIsolateData):
(WebCore::V8BindingPerIsolateData::internalScriptRecursionLevel):
(WebCore::V8BindingPerIsolateData::incrementInternalScriptRecursionLevel):
(WebCore::V8BindingPerIsolateData::decrementInternalScriptRecursionLevel):
* bindings/v8/V8DOMWindowShell.cpp:
* bindings/v8/V8DOMWrapper.cpp:
* bindings/v8/V8LazyEventListener.cpp:
(WebCore::V8LazyEventListener::prepareListenerObject):
* bindings/v8/V8NPObject.cpp:
* bindings/v8/V8RecursionScope.h:
(WebCore):
(WebCore::V8RecursionScope::recursionLevel):
(V8RecursionScope):
(WebCore::V8RecursionScope::properlyUsed):
(MicrotaskSuppression):
(WebCore::V8RecursionScope::MicrotaskSuppression::MicrotaskSuppression):
(WebCore::V8RecursionScope::MicrotaskSuppression::~MicrotaskSuppression):
* bindings/v8/WorkerContextExecutionProxy.cpp:
* bindings/v8/custom/V8HTMLDocumentCustom.cpp:
(WebCore::V8HTMLDocument::WrapInShadowObject):
* bindings/v8/custom/V8InjectedScriptManager.cpp:
(WebCore::InjectedScriptManager::createInjectedScript):
* bindings/v8/custom/V8ScriptProfileCustom.cpp:
* bindings/v8/custom/V8ScriptProfileNodeCustom.cpp:

Source/WebKit/chromium:

* WebKit.gyp:
* public/WebFrame.h:
(v8):
(WebFrame):
* src/WebFrameImpl.cpp:
(WebKit):
(WebKit::WebFrameImpl::callFunctionEvenIfScriptDisabled):
* src/WebFrameImpl.h:
(WebFrameImpl):
* src/WebKit.cpp:
(WebKit):
(WebKit::assertV8RecursionScope):
(WebKit::initialize):
(WebKit::shutdown):

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

31 files changed:
Source/WebCore/ChangeLog
Source/WebCore/Target.pri
Source/WebCore/WebCore.gypi
Source/WebCore/bindings/v8/DateExtension.cpp
Source/WebCore/bindings/v8/PageScriptDebugServer.cpp
Source/WebCore/bindings/v8/SafeAllocation.h [new file with mode: 0644]
Source/WebCore/bindings/v8/ScriptController.cpp
Source/WebCore/bindings/v8/ScriptController.h
Source/WebCore/bindings/v8/ScriptDebugServer.cpp
Source/WebCore/bindings/v8/ScriptDebugServer.h
Source/WebCore/bindings/v8/ScriptFunctionCall.cpp
Source/WebCore/bindings/v8/V8Binding.cpp
Source/WebCore/bindings/v8/V8Binding.h
Source/WebCore/bindings/v8/V8DOMWindowShell.cpp
Source/WebCore/bindings/v8/V8DOMWrapper.cpp
Source/WebCore/bindings/v8/V8LazyEventListener.cpp
Source/WebCore/bindings/v8/V8NPObject.cpp
Source/WebCore/bindings/v8/V8RecursionScope.h
Source/WebCore/bindings/v8/WorkerContextExecutionProxy.cpp
Source/WebCore/bindings/v8/custom/V8HTMLDocumentCustom.cpp
Source/WebCore/bindings/v8/custom/V8InjectedScriptManager.cpp
Source/WebCore/bindings/v8/custom/V8ScriptProfileCustom.cpp
Source/WebCore/bindings/v8/custom/V8ScriptProfileNodeCustom.cpp
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/WebKit.gyp
Source/WebKit/chromium/public/WebFrame.h
Source/WebKit/chromium/public/WebScopedMicrotaskSuppression.h [new file with mode: 0644]
Source/WebKit/chromium/src/WebFrameImpl.cpp
Source/WebKit/chromium/src/WebFrameImpl.h
Source/WebKit/chromium/src/WebKit.cpp
Source/WebKit/chromium/src/WebScopedMicrotaskSuppression.cpp [new file with mode: 0644]

index 0a1b0d4..dd07500 100644 (file)
@@ -1,3 +1,87 @@
+2012-04-03  Rafael Weinstein  <rafaelw@chromium.org>
+
+        Use V8 completion callback API to assert that V8RecursionScope is on the stack whenever invoking script
+        https://bugs.webkit.org/show_bug.cgi?id=79131
+
+        Reviewed by Adam Barth.
+
+        End-of-microtask work (cancelling outstanding IDB transactions,
+        delivering DOM mutations) depend on V8RecursionScope being on the
+        stack whenever a call is made into script. V8 provides a completion
+        callback API that could be used to hook these "end-of-microtask"
+        events, but it turns out that WebKit calls into script for various
+        reasons besides running script from the page. For example, constructing
+        wrapper objects from function templates counts as "running script",
+        yet it's not appropriate to run this callback every time a JS wrapper
+        is constructed.
+
+        Instead, this patch makes use of the V8 completion callback mechanism
+        only in debug mode, to assert that either a V8RecursionScope (when
+        calling author script) or a V8RecursionScope::MicrotaskSuppression
+        (when calling non-author script) is on the stack when V8 thinks it's
+        finished executing script. This requires dropping MicrotaskSuppression
+        objects in a bunch of places, most notably the Inspector.
+        Note that in release mode, this class does nothing and thus should be
+        optimized away.
+
+        No new tests. Existing tests have appropriate coverage.
+
+        * Target.pri:
+        * WebCore.gypi:
+        * bindings/v8/DateExtension.cpp:
+        (WebCore::DateExtension::setAllowSleep):
+        * bindings/v8/PageScriptDebugServer.cpp:
+        (WebCore::PageScriptDebugServer::addListener):
+        * bindings/v8/ScriptController.cpp:
+        (WebCore::ScriptController::callFunctionEvenIfScriptDisabled):
+        (WebCore):
+        (WebCore::ScriptController::collectGarbage):
+        * bindings/v8/ScriptController.h:
+        (ScriptController):
+        * bindings/v8/ScriptDebugServer.cpp:
+        (WebCore::ScriptDebugServer::callDebuggerMethod):
+        (WebCore):
+        (WebCore::ScriptDebugServer::pauseOnExceptionsState):
+        (WebCore::ScriptDebugServer::setPauseOnExceptionsState):
+        (WebCore::ScriptDebugServer::stepIntoStatement):
+        (WebCore::ScriptDebugServer::stepOverStatement):
+        (WebCore::ScriptDebugServer::stepOutOfFunction):
+        (WebCore::ScriptDebugServer::setScriptSource):
+        (WebCore::ScriptDebugServer::currentCallFrame):
+        (WebCore::ScriptDebugServer::handleV8DebugEvent):
+        (WebCore::ScriptDebugServer::ensureDebuggerScriptCompiled):
+        * bindings/v8/ScriptDebugServer.h:
+        (ScriptDebugServer):
+        * bindings/v8/ScriptFunctionCall.cpp:
+        (WebCore::ScriptFunctionCall::call):
+        * bindings/v8/V8Binding.cpp:
+        (WebCore::V8BindingPerIsolateData::V8BindingPerIsolateData):
+        * bindings/v8/V8Binding.h:
+        (V8BindingPerIsolateData):
+        (WebCore::V8BindingPerIsolateData::internalScriptRecursionLevel):
+        (WebCore::V8BindingPerIsolateData::incrementInternalScriptRecursionLevel):
+        (WebCore::V8BindingPerIsolateData::decrementInternalScriptRecursionLevel):
+        * bindings/v8/V8DOMWindowShell.cpp:
+        * bindings/v8/V8DOMWrapper.cpp:
+        * bindings/v8/V8LazyEventListener.cpp:
+        (WebCore::V8LazyEventListener::prepareListenerObject):
+        * bindings/v8/V8NPObject.cpp:
+        * bindings/v8/V8RecursionScope.h:
+        (WebCore):
+        (WebCore::V8RecursionScope::recursionLevel):
+        (V8RecursionScope):
+        (WebCore::V8RecursionScope::properlyUsed):
+        (MicrotaskSuppression):
+        (WebCore::V8RecursionScope::MicrotaskSuppression::MicrotaskSuppression):
+        (WebCore::V8RecursionScope::MicrotaskSuppression::~MicrotaskSuppression):
+        * bindings/v8/WorkerContextExecutionProxy.cpp:
+        * bindings/v8/custom/V8HTMLDocumentCustom.cpp:
+        (WebCore::V8HTMLDocument::WrapInShadowObject):
+        * bindings/v8/custom/V8InjectedScriptManager.cpp:
+        (WebCore::InjectedScriptManager::createInjectedScript):
+        * bindings/v8/custom/V8ScriptProfileCustom.cpp:
+        * bindings/v8/custom/V8ScriptProfileNodeCustom.cpp:
+
 2012-04-03  Anna Cavender  <annacc@chromium.org>
 
         Makes sure m_showingByDefault is set to false when TextTrack.mode is set from JS.
index b007c72..cef67a1 100644 (file)
@@ -1407,6 +1407,7 @@ v8 {
         bindings/v8/PageScriptDebugServer.h \
         bindings/v8/RetainedDOMInfo.h \
         bindings/v8/RetainedObjectInfo.h \
+        bindings/v8/SafeAllocation.h \
         bindings/v8/ScheduledAction.h \
         bindings/v8/ScopedDOMDataStore.h \
         bindings/v8/ScriptCachedFrameData.h \
index 99c90f2..699e1b9 100644 (file)
             'bindings/v8/RetainedDOMInfo.cpp',
             'bindings/v8/RetainedDOMInfo.h',
             'bindings/v8/RetainedObjectInfo.h',
+            'bindings/v8/SafeAllocation.h',
             'bindings/v8/ScheduledAction.cpp',
             'bindings/v8/ScheduledAction.h',
             'bindings/v8/ScopedDOMDataStore.cpp',
index 332828b..ca363c2 100644 (file)
@@ -31,8 +31,9 @@
 #include "config.h"
 #include "DateExtension.h"
 
-#include "V8Proxy.h"
 #include "V8HiddenPropertyName.h"
+#include "V8Proxy.h"
+#include "V8RecursionScope.h"
 
 namespace WebCore {
 
@@ -89,6 +90,7 @@ void DateExtension::setAllowSleep(bool allow)
 
     v8::Handle<v8::Value> argv[1];
     argv[0] = v8::Boolean::New(!allow);
+    V8RecursionScope::MicrotaskSuppression scope;
     v8::Handle<v8::Function>::Cast(sleepFunctionHandle)->Call(v8::Object::New(), 1, argv);
 }
 
index 18f7df7..d9d97e7 100755 (executable)
@@ -39,6 +39,7 @@
 #include "V8Binding.h"
 #include "V8DOMWindow.h"
 #include "V8Proxy.h"
+#include "V8RecursionScope.h"
 #include <wtf/OwnPtr.h>
 #include <wtf/PassOwnPtr.h>
 #include <wtf/StdLibExtras.h>
@@ -100,7 +101,11 @@ void PageScriptDebugServer::addListener(ScriptDebugListener* listener, Page* pag
     v8::Handle<v8::Context> context = shell->context();
     v8::Handle<v8::Function> getScriptsFunction = v8::Local<v8::Function>::Cast(m_debuggerScript.get()->Get(v8::String::New("getScripts")));
     v8::Handle<v8::Value> argv[] = { context->GetData() };
-    v8::Handle<v8::Value> value = getScriptsFunction->Call(m_debuggerScript.get(), 1, argv);
+    v8::Handle<v8::Value> value;
+    {
+        V8RecursionScope::MicrotaskSuppression scope;
+        value = getScriptsFunction->Call(m_debuggerScript.get(), 1, argv);
+    }
     if (value.IsEmpty())
         return;
     ASSERT(!value->IsUndefined() && value->IsArray());
diff --git a/Source/WebCore/bindings/v8/SafeAllocation.h b/Source/WebCore/bindings/v8/SafeAllocation.h
new file mode 100644 (file)
index 0000000..c3b34ae
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+* Copyright (C) 2012 Google Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+*     * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above
+* copyright notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*     * Neither the name of Google Inc. nor the names of its
+* contributors may be used to endorse or promote products derived from
+* this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef SafeAllocation_h
+#define SafeAllocation_h
+
+#include "V8Binding.h"
+#include "V8RecursionScope.h"
+
+#include <v8.h>
+
+namespace WebCore {
+
+class SafeAllocation {
+public:
+    static inline v8::Local<v8::Object> newInstance(v8::Handle<v8::Function>);
+    static inline v8::Local<v8::Object> newInstance(v8::Handle<v8::ObjectTemplate>);
+    static inline v8::Local<v8::Object> newInstance(v8::Handle<v8::Function>, int argc, v8::Handle<v8::Value> argv[]);
+};
+
+v8::Local<v8::Object> SafeAllocation::newInstance(v8::Handle<v8::Function> function)
+{
+    if (function.IsEmpty())
+        return v8::Local<v8::Object>();
+    ConstructorMode constructorMode;
+    V8RecursionScope::MicrotaskSuppression scope;
+    return function->NewInstance();
+}
+
+v8::Local<v8::Object> SafeAllocation::newInstance(v8::Handle<v8::ObjectTemplate> objectTemplate)
+{
+    if (objectTemplate.IsEmpty())
+        return v8::Local<v8::Object>();
+    ConstructorMode constructorMode;
+    V8RecursionScope::MicrotaskSuppression scope;
+    return objectTemplate->NewInstance();
+}
+
+v8::Local<v8::Object> SafeAllocation::newInstance(v8::Handle<v8::Function> function, int argc, v8::Handle<v8::Value> argv[])
+{
+    if (function.IsEmpty())
+        return v8::Local<v8::Object>();
+    ConstructorMode constructorMode;
+    V8RecursionScope::MicrotaskSuppression scope;
+    return function->NewInstance(argc, argv);
+}
+
+} // namespace WebCore
+
+#endif // SafeAllocation_h
index 8de9553..c38425e 100644 (file)
@@ -62,6 +62,7 @@
 #include "V8IsolatedContext.h"
 #include "V8NPObject.h"
 #include "V8Proxy.h"
+#include "V8RecursionScope.h"
 #include "Widget.h"
 #include <wtf/StdLibExtras.h>
 #include <wtf/text/CString.h>
@@ -161,6 +162,12 @@ bool ScriptController::processingUserGesture()
     return UserGestureIndicator::processingUserGesture();
 }
 
+ScriptValue ScriptController::callFunctionEvenIfScriptDisabled(v8::Handle<v8::Function> function, v8::Handle<v8::Object> receiver, int argc, v8::Handle<v8::Value> argv[])
+{
+    // FIXME: This should probably perform the same isPaused check that happens in ScriptController::executeScript.
+    return ScriptValue(m_proxy->callFunction(function, receiver, argc, argv));
+}
+
 void ScriptController::evaluateInIsolatedWorld(unsigned worldID, const Vector<ScriptSourceCode>& sources)
 {
     m_proxy->evaluateInIsolatedWorld(worldID, sources, 0);
@@ -245,8 +252,10 @@ void ScriptController::collectGarbage()
         v8::Local<v8::String> source = v8::String::New("if (gc) gc();");
         v8::Local<v8::String> name = v8::String::New("gc");
         v8::Handle<v8::Script> script = v8::Script::Compile(source, name);
-        if (!script.IsEmpty())
+        if (!script.IsEmpty()) {
+            V8RecursionScope::MicrotaskSuppression scope;
             script->Run();
+        }
     }
     v8Context.Dispose();
 }
index 9d2efde..743b12b 100644 (file)
@@ -73,6 +73,7 @@ public:
 
     ScriptValue executeScript(const ScriptSourceCode&);
     ScriptValue executeScript(const String& script, bool forceUserGesture = false);
+    ScriptValue callFunctionEvenIfScriptDisabled(v8::Handle<v8::Function>, v8::Handle<v8::Object>, int argc, v8::Handle<v8::Value> argv[]);
 
     // Returns true if argument is a JavaScript URL.
     bool executeIfJavaScriptURL(const KURL&, ShouldReplaceDocumentIfJavaScriptURL shouldReplaceDocumentIfJavaScriptURL = ReplaceDocumentIfJavaScriptURL);
index b705523..cd30c64 100644 (file)
@@ -39,6 +39,7 @@
 #include "ScriptObject.h"
 #include "V8Binding.h"
 #include "V8JavaScriptCallFrame.h"
+#include "V8RecursionScope.h"
 #include <wtf/StdLibExtras.h>
 #include <wtf/Vector.h>
 
@@ -57,6 +58,13 @@ private:
 
 }
 
+v8::Local<v8::Value> ScriptDebugServer::callDebuggerMethod(const char* functionName, int argc, v8::Handle<v8::Value> argv[])
+{
+    v8::Handle<v8::Function> function = v8::Local<v8::Function>::Cast(m_debuggerScript.get()->Get(v8::String::New(functionName)));
+    V8RecursionScope::MicrotaskSuppression scope;
+    return function->Call(m_debuggerScript.get(), argc, argv);
+}
+
 ScriptDebugServer::ScriptDebugServer()
     : m_pauseOnExceptionsState(DontPauseOnExceptions)
     , m_breakpointsActivated(true)
@@ -129,9 +137,8 @@ ScriptDebugServer::PauseOnExceptionsState ScriptDebugServer::pauseOnExceptionsSt
     v8::HandleScope scope;
     v8::Context::Scope contextScope(v8::Debug::GetDebugContext());
 
-    v8::Handle<v8::Function> function = v8::Local<v8::Function>::Cast(m_debuggerScript.get()->Get(v8::String::New("pauseOnExceptionsState")));
     v8::Handle<v8::Value> argv[] = { v8::Handle<v8::Value>() };
-    v8::Handle<v8::Value> result = function->Call(m_debuggerScript.get(), 0, argv);
+    v8::Handle<v8::Value> result = callDebuggerMethod("pauseOnExceptionsState", 0, argv);
     return static_cast<ScriptDebugServer::PauseOnExceptionsState>(result->Int32Value());
 }
 
@@ -141,9 +148,8 @@ void ScriptDebugServer::setPauseOnExceptionsState(PauseOnExceptionsState pauseOn
     v8::HandleScope scope;
     v8::Context::Scope contextScope(v8::Debug::GetDebugContext());
 
-    v8::Handle<v8::Function> setPauseOnExceptionsFunction = v8::Local<v8::Function>::Cast(m_debuggerScript.get()->Get(v8::String::New("setPauseOnExceptionsState")));
     v8::Handle<v8::Value> argv[] = { v8::Int32::New(pauseOnExceptionsState) };
-    setPauseOnExceptionsFunction->Call(m_debuggerScript.get(), 1, argv);
+    callDebuggerMethod("setPauseOnExceptionsState", 1, argv);
 }
 
 void ScriptDebugServer::setPauseOnNextStatement(bool pause)
@@ -190,27 +196,24 @@ void ScriptDebugServer::continueProgram()
 void ScriptDebugServer::stepIntoStatement()
 {
     ASSERT(isPaused());
-    v8::Handle<v8::Function> function = v8::Local<v8::Function>::Cast(m_debuggerScript.get()->Get(v8::String::New("stepIntoStatement")));
     v8::Handle<v8::Value> argv[] = { m_executionState.get() };
-    function->Call(m_debuggerScript.get(), 1, argv);
+    callDebuggerMethod("stepIntoStatement", 1, argv);
     continueProgram();
 }
 
 void ScriptDebugServer::stepOverStatement()
 {
     ASSERT(isPaused());
-    v8::Handle<v8::Function> function = v8::Local<v8::Function>::Cast(m_debuggerScript.get()->Get(v8::String::New("stepOverStatement")));
     v8::Handle<v8::Value> argv[] = { m_executionState.get() };
-    function->Call(m_debuggerScript.get(), 1, argv);
+    callDebuggerMethod("stepOverStatement", 1, argv);
     continueProgram();
 }
 
 void ScriptDebugServer::stepOutOfFunction()
 {
     ASSERT(isPaused());
-    v8::Handle<v8::Function> function = v8::Local<v8::Function>::Cast(m_debuggerScript.get()->Get(v8::String::New("stepOutOfFunction")));
     v8::Handle<v8::Value> argv[] = { m_executionState.get() };
-    function->Call(m_debuggerScript.get(), 1, argv);
+    callDebuggerMethod("stepOutOfFunction", 1, argv);
     continueProgram();
 }
 
@@ -228,12 +231,11 @@ bool ScriptDebugServer::setScriptSource(const String& sourceID, const String& ne
     if (!isPaused())
         contextScope = adoptPtr(new v8::Context::Scope(v8::Debug::GetDebugContext()));
 
-    v8::Handle<v8::Function> function = v8::Local<v8::Function>::Cast(m_debuggerScript.get()->Get(v8::String::New("setScriptSource")));
     v8::Handle<v8::Value> argv[] = { v8String(sourceID), v8String(newContent), v8Boolean(preview) };
 
     v8::TryCatch tryCatch;
     tryCatch.SetVerbose(false);
-    v8::Handle<v8::Value> v8result = function->Call(m_debuggerScript.get(), 3, argv);
+    v8::Local<v8::Value> v8result = callDebuggerMethod("setScriptSource", 3, argv);
     if (tryCatch.HasCaught()) {
         v8::Local<v8::Message> message = tryCatch.Message();
         if (!message.IsEmpty())
@@ -255,9 +257,8 @@ bool ScriptDebugServer::setScriptSource(const String& sourceID, const String& ne
 ScriptValue ScriptDebugServer::currentCallFrame()
 {
     ASSERT(isPaused());
-    v8::Handle<v8::Function> currentCallFrameFunction = v8::Local<v8::Function>::Cast(m_debuggerScript.get()->Get(v8::String::New("currentCallFrame")));
     v8::Handle<v8::Value> argv[] = { m_executionState.get() };
-    v8::Handle<v8::Value> currentCallFrameV8 = currentCallFrameFunction->Call(m_debuggerScript.get(), 1, argv);
+    v8::Handle<v8::Value> currentCallFrameV8 = callDebuggerMethod("currentCallFrame", 1, argv);
 
     ASSERT(!currentCallFrameV8.IsEmpty());
     if (!currentCallFrameV8->IsObject())
@@ -355,6 +356,7 @@ void ScriptDebugServer::handleV8DebugEvent(const v8::Debug::EventDetails& eventD
                 v8::Handle<v8::Value> exceptionGetterValue = eventData->Get(v8::String::New("exception"));
                 ASSERT(!exceptionGetterValue.IsEmpty() && exceptionGetterValue->IsFunction());
                 v8::Handle<v8::Value> argv[] = { v8::Handle<v8::Value>() };
+                V8RecursionScope::MicrotaskSuppression scope;
                 exception = v8::Handle<v8::Function>::Cast(exceptionGetterValue)->Call(eventData, 0, argv);
             }
 
@@ -389,6 +391,7 @@ void ScriptDebugServer::ensureDebuggerScriptCompiled()
         v8::Local<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
         v8::Context::Scope contextScope(debuggerContext);
         String debuggerScriptSource(reinterpret_cast<const char*>(DebuggerScriptSource_js), sizeof(DebuggerScriptSource_js));
+        V8RecursionScope::MicrotaskSuppression recursionScope;
         m_debuggerScript.set(v8::Handle<v8::Object>::Cast(v8::Script::Compile(v8String(debuggerScriptSource))->Run()));
     }
 }
index de84651..a4fb612 100644 (file)
@@ -113,7 +113,9 @@ protected:
     void dispatchDidParseSource(ScriptDebugListener* listener, v8::Handle<v8::Object> sourceObject);
 
     void ensureDebuggerScriptCompiled();
-    
+
+    v8::Local<v8::Value> callDebuggerMethod(const char* functionName, int argc, v8::Handle<v8::Value> argv[]);
+
     PauseOnExceptionsState m_pauseOnExceptionsState;
     OwnHandle<v8::Object> m_debuggerScript;
     OwnHandle<v8::Object> m_executionState;
index 5c96b56..06f55a9 100644 (file)
 #include "config.h"
 #include "ScriptFunctionCall.h"
 
+#include "SafeAllocation.h"
 #include "ScriptScope.h"
 #include "ScriptState.h"
 #include "ScriptValue.h"
-
 #include "V8Binding.h"
 #include "V8Proxy.h"
+#include "V8RecursionScope.h"
 #include "V8Utilities.h"
 
 #include <v8.h>
@@ -130,7 +131,11 @@ ScriptValue ScriptFunctionCall::call(bool& hadException, bool reportExceptions)
     for (size_t i = 0; i < m_arguments.size(); ++i)
         args[i] = m_arguments[i].v8Value();
 
-    v8::Local<v8::Value> result = function->Call(thisObject, m_arguments.size(), args.get());
+    v8::Local<v8::Value> result;
+    {
+        V8RecursionScope scope(getScriptExecutionContext());
+        result = function->Call(thisObject, m_arguments.size(), args.get());
+    }
     if (!scope.success()) {
         hadException = true;
         return ScriptValue();
index 798676e..f7dbe05 100644 (file)
@@ -56,6 +56,9 @@ V8BindingPerIsolateData::V8BindingPerIsolateData(v8::Isolate* isolate)
     : m_domDataStore(0)
     , m_constructorMode(ConstructorMode::CreateNewObject)
     , m_recursionLevel(0)
+#ifndef NDEBUG
+    , m_internalScriptRecursionLevel(0)
+#endif
 {
 }
 
index 84ff237..e77765f 100644 (file)
@@ -153,6 +153,10 @@ namespace WebCore {
 
 #ifndef NDEBUG
         GlobalHandleMap& globalHandleMap() { return m_globalHandleMap; }
+
+        int internalScriptRecursionLevel() const { return m_internalScriptRecursionLevel; }
+        int incrementInternalScriptRecursionLevel() { return ++m_internalScriptRecursionLevel; }
+        int decrementInternalScriptRecursionLevel() { return --m_internalScriptRecursionLevel; }
 #endif
 
     private:
@@ -179,6 +183,7 @@ namespace WebCore {
 
 #ifndef NDEBUG
         GlobalHandleMap m_globalHandleMap;
+        int m_internalScriptRecursionLevel;
 #endif
     };
 
@@ -208,38 +213,6 @@ namespace WebCore {
         bool m_previous;
     };
 
-    class SafeAllocation {
-    public:
-        static inline v8::Local<v8::Object> newInstance(v8::Handle<v8::Function>);
-        static inline v8::Local<v8::Object> newInstance(v8::Handle<v8::ObjectTemplate>);
-        static inline v8::Local<v8::Object> newInstance(v8::Handle<v8::Function>, int argc, v8::Handle<v8::Value> argv[]);
-    };
-
-    v8::Local<v8::Object> SafeAllocation::newInstance(v8::Handle<v8::Function> function)
-    {
-        if (function.IsEmpty())
-            return v8::Local<v8::Object>();
-        ConstructorMode constructorMode;
-        return function->NewInstance();
-    }
-
-    v8::Local<v8::Object> SafeAllocation::newInstance(v8::Handle<v8::ObjectTemplate> objectTemplate)
-    {
-        if (objectTemplate.IsEmpty())
-            return v8::Local<v8::Object>();
-        ConstructorMode constructorMode;
-        return objectTemplate->NewInstance();
-    }
-
-    v8::Local<v8::Object> SafeAllocation::newInstance(v8::Handle<v8::Function> function, int argc, v8::Handle<v8::Value> argv[])
-    {
-        if (function.IsEmpty())
-            return v8::Local<v8::Object>();
-        ConstructorMode constructorMode;
-        return function->NewInstance(argc, argv);
-    }
-
-
 
     enum ExternalMode {
         Externalize,
index d512d2a..9b5611b 100644 (file)
@@ -39,6 +39,7 @@
 #include "Page.h"
 #include "PageGroup.h"
 #include "RuntimeEnabledFeatures.h"
+#include "SafeAllocation.h"
 #include "ScriptCallStack.h"
 #include "ScriptCallStackFactory.h"
 #include "ScriptProfiler.h"
index 30e7306..8ce64db 100644 (file)
@@ -36,6 +36,8 @@
 #include "EventTargetHeaders.h"
 #include "EventTargetInterfaces.h"
 #include "FrameLoaderClient.h"
+#include "SVGElementInstance.h"
+#include "SafeAllocation.h"
 #include "StylePropertySet.h"
 #include "V8AbstractEventListener.h"
 #include "V8Binding.h"
index 642d3e1..e634613 100644 (file)
@@ -44,6 +44,7 @@
 #include "V8HiddenPropertyName.h"
 #include "V8Node.h"
 #include "V8Proxy.h"
+#include "V8RecursionScope.h"
 #include "WorldContextHandle.h"
 
 #include <wtf/StdLibExtras.h>
@@ -152,9 +153,12 @@ void V8LazyEventListener::prepareListenerObject(ScriptExecutionContext* context)
     if (script.IsEmpty())
         return;
 
-    // Call v8::Script::Run() directly to avoid an erroneous call to V8RecursionScope::didLeaveScriptContext().
     // FIXME: Remove this code when we stop doing the 'with' hack above.
-    v8::Local<v8::Value> value = script->Run();
+    v8::Local<v8::Value> value;
+    {
+        V8RecursionScope::MicrotaskSuppression scope;
+        value = script->Run();
+    }
     if (value.IsEmpty())
         return;
 
@@ -172,10 +176,12 @@ void V8LazyEventListener::prepareListenerObject(ScriptExecutionContext* context)
 
     v8::Handle<v8::Value> parameters[3] = { nodeWrapper, formWrapper, documentWrapper };
 
-    // Use Call directly to avoid an erroneous call to V8RecursionScope::didLeaveScriptContext().
     // FIXME: Remove this code when we stop doing the 'with' hack above.
-    v8::Local<v8::Value> innerValue = intermediateFunction->Call(v8Context->Global(), 3, parameters);
-
+    v8::Local<v8::Value> innerValue;
+    {
+        V8RecursionScope::MicrotaskSuppression scope;
+        innerValue = intermediateFunction->Call(v8Context->Global(), 3, parameters);
+    }
     if (innerValue.IsEmpty() || !innerValue->IsFunction())
         return;
 
index e43d27e..f6dd1f8 100644 (file)
@@ -34,6 +34,7 @@
 
 #include "HTMLPlugInElement.h"
 #include "NPV8Object.h"
+#include "SafeAllocation.h"
 #include "V8Binding.h"
 #include "V8DOMMap.h"
 #include "V8HTMLAppletElement.h"
index 3b9ad6e..0e776e1 100644 (file)
 
 #include "ScriptExecutionContext.h"
 #include "V8Binding.h"
+#include <wtf/Noncopyable.h>
 
 namespace WebCore {
 
+// C++ calls into script contexts which are "owned" by WebKit (created in a
+// process where WebKit.cpp initializes v8) must declare their type:
+//
+//   1. Calls into page/author script from a frame
+//   2. Calls into page/author script from a worker
+//   3. Calls into internal script (typically setup/teardown work)
+//
+// Debug-time checking of this is enforced via this class.
+//
+// Calls of type (1) should generally go through V8Proxy, as inspector
+// instrumentation is needed. Calls of type (2) should always stack-allocate a
+// V8RecursionScope in the same block as the call into script. Calls of type (3)
+// should stack allocate a V8RecursionScope::MicrotaskSuppression -- this
+// skips work that is spec'd to happen at the end of the outer-most script stack
+// frame of calls into page script:
+//
+// http://www.whatwg.org/specs/web-apps/current-work/#perform-a-microtask-checkpoint
 class V8RecursionScope {
     WTF_MAKE_NONCOPYABLE(V8RecursionScope);
 public:
@@ -51,7 +69,34 @@ public:
             didLeaveScriptContext();
     }
 
-    static int recursionLevel() { return V8BindingPerIsolateData::current()->recursionLevel(); }
+    static int recursionLevel()
+    {
+        return V8BindingPerIsolateData::current()->recursionLevel();
+    }
+
+#ifndef NDEBUG
+    static bool properlyUsed()
+    {
+        return recursionLevel() > 0 || V8BindingPerIsolateData::current()->internalScriptRecursionLevel() > 0;
+    }
+#endif
+
+    class MicrotaskSuppression {
+    public:
+        MicrotaskSuppression()
+        {
+#ifndef NDEBUG
+            V8BindingPerIsolateData::current()->incrementInternalScriptRecursionLevel();
+#endif
+        }
+
+        ~MicrotaskSuppression()
+        {
+#ifndef NDEBUG
+            V8BindingPerIsolateData::current()->decrementInternalScriptRecursionLevel();
+#endif
+        }
+    };
 
 private:
     void didLeaveScriptContext();
index 51be5ca..162d794 100644 (file)
@@ -37,6 +37,7 @@
 
 #include "DedicatedWorkerContext.h"
 #include "Event.h"
+#include "SafeAllocation.h"
 #include "ScriptCallStack.h"
 #include "SharedWorker.h"
 #include "SharedWorkerContext.h"
index 41ae071..4cf1bca 100644 (file)
@@ -44,6 +44,7 @@
 #include "V8IsolatedContext.h"
 #include "V8Node.h"
 #include "V8Proxy.h"
+#include "V8RecursionScope.h"
 #include <wtf/OwnArrayPtr.h>
 #include <wtf/RefPtr.h>
 #include <wtf/StdLibExtras.h>
@@ -66,7 +67,11 @@ v8::Local<v8::Object> V8HTMLDocument::WrapInShadowObject(v8::Local<v8::Object> w
     if (shadowConstructor.IsEmpty())
         return v8::Local<v8::Object>();
 
-    v8::Local<v8::Object> shadow = shadowConstructor->NewInstance();
+    v8::Local<v8::Object> shadow;
+    {
+        V8RecursionScope::MicrotaskSuppression scope;
+        shadow = shadowConstructor->NewInstance();
+    }
     if (shadow.IsEmpty())
         return v8::Local<v8::Object>();
     V8DOMWrapper::setDOMWrapper(shadow, &V8HTMLDocument::info, impl);
index 8bb4281..7d9493f 100644 (file)
@@ -34,6 +34,7 @@
 #include "DOMWindow.h"
 #include "InjectedScript.h"
 #include "InjectedScriptHost.h"
+#include "SafeAllocation.h"
 #include "ScriptValue.h"
 #include "V8Binding.h"
 #include "V8BindingState.h"
@@ -41,6 +42,7 @@
 #include "V8HiddenPropertyName.h"
 #include "V8InjectedScriptHost.h"
 #include "V8Proxy.h"
+#include "V8RecursionScope.h"
 #include <wtf/RefPtr.h>
 
 namespace WebCore {
@@ -96,6 +98,7 @@ ScriptObject InjectedScriptManager::createInjectedScript(const String& scriptSou
     // injected script id and explicit reference to the inspected global object. The function is expected
     // to create and configure InjectedScript instance that is going to be used by the inspector.
     v8::Local<v8::Script> script = v8::Script::Compile(v8String(scriptSource));
+    V8RecursionScope::MicrotaskSuppression recursionScope;
     v8::Local<v8::Value> v = script->Run();
     ASSERT(!v.IsEmpty());
     ASSERT(v->IsFunction());
index 32d8726..6421855 100644 (file)
@@ -32,6 +32,7 @@
 #if ENABLE(JAVASCRIPT_DEBUGGER)
 #include "V8ScriptProfile.h"
 
+#include "SafeAllocation.h"
 #include "ScriptProfile.h"
 #include "V8Binding.h"
 #include "V8Proxy.h"
index c13400e..78d9210 100644 (file)
@@ -32,6 +32,7 @@
 #if ENABLE(JAVASCRIPT_DEBUGGER)
 #include "V8ScriptProfileNode.h"
 
+#include "SafeAllocation.h"
 #include "ScriptProfileNode.h"
 #include "V8Binding.h"
 #include "V8Proxy.h"
index b3cb1dd..aac03c5 100644 (file)
@@ -1,3 +1,25 @@
+2012-04-03  Rafael Weinstein  <rafaelw@chromium.org>
+
+        Use V8 completion callback API to assert that V8RecursionScope is on the stack whenever invoking script
+        https://bugs.webkit.org/show_bug.cgi?id=79131
+
+        Reviewed by Adam Barth.
+
+        * WebKit.gyp:
+        * public/WebFrame.h:
+        (v8):
+        (WebFrame):
+        * src/WebFrameImpl.cpp:
+        (WebKit):
+        (WebKit::WebFrameImpl::callFunctionEvenIfScriptDisabled):
+        * src/WebFrameImpl.h:
+        (WebFrameImpl):
+        * src/WebKit.cpp:
+        (WebKit):
+        (WebKit::assertV8RecursionScope):
+        (WebKit::initialize):
+        (WebKit::shutdown):
+
 2012-04-03  Adam Barth  <abarth@webkit.org>
 
         Unreviewed. This patch adds an ugly, ugly hack to bandaid over the
index e02a23c..e00ff52 100644 (file)
                 'public/WebReferrerPolicy.h',
                 'public/WebRegularExpression.h',
                 'public/WebRuntimeFeatures.h',
+                'public/WebScopedMicrotaskSuppression.h',
                 'public/WebScopedUserGesture.h',
                 'public/WebScreenInfo.h',
                 'public/WebScriptController.h',
                 'src/WebRange.cpp',
                 'src/WebRegularExpression.cpp',
                 'src/WebRuntimeFeatures.cpp',
+                'src/WebScopedMicrotaskSuppression.cpp',
                 'src/WebScopedUserGesture.cpp',
                 'src/WebScriptController.cpp',
                 'src/WebScrollbarImpl.cpp',
index 0d5406d..19a7c37 100644 (file)
@@ -44,6 +44,8 @@ struct NPObject;
 #if WEBKIT_USING_V8
 namespace v8 {
 class Context;
+class Function;
+class Object;
 class Value;
 template <class T> class Handle;
 template <class T> class Local;
@@ -274,6 +276,14 @@ public:
     virtual v8::Handle<v8::Value> executeScriptAndReturnValue(
         const WebScriptSource&) = 0;
 
+    // Call the function with the given receiver and arguments, bypassing
+    // canExecute().
+    virtual v8::Handle<v8::Value> callFunctionEvenIfScriptDisabled(
+        v8::Handle<v8::Function>,
+        v8::Handle<v8::Object>,
+        int argc,
+        v8::Handle<v8::Value> argv[]) = 0;
+
     // Returns the V8 context for this frame, or an empty handle if there
     // is none.
     virtual v8::Local<v8::Context> mainWorldScriptContext() const = 0;
diff --git a/Source/WebKit/chromium/public/WebScopedMicrotaskSuppression.h b/Source/WebKit/chromium/public/WebScopedMicrotaskSuppression.h
new file mode 100644 (file)
index 0000000..826db69
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WebScopedMicrotaskSuppression_h
+#define WebScopedMicrotaskSuppression_h
+
+#include "platform/WebPrivateOwnPtr.h"
+
+namespace WebKit {
+
+// This class wraps V8RecursionScope::BypassMicrotaskCheckpoint. Please
+// see V8RecursionScope.h for full usage. Short story: Embedder calls into
+// script contexts which also host page script must do one of two things:
+//
+//   1. If the call may cause any page/author script to run, it must be
+//      captured for pre/post work (e.g. inspector instrumentation/microtask
+//      delivery) and thus be invoked through WebFrame (e.g. executeScript*,
+//      callFunction*).
+//   2. If the call will not cause any page/author script to run, the call
+//      should be made directly via the v8 context, but the callsite must be
+//      accompanied by a stack allocated WebScopedMicrotaskSuppression, e.g.:
+//
+//        ...
+//        {
+//            WebKit::WebScopedMicrotaskSuppression suppression;
+//            func->Call(global, argv, args);
+//        }
+//        ...
+//
+class WebScopedMicrotaskSuppression {
+public:
+    WebScopedMicrotaskSuppression() { initialize(); }
+    ~WebScopedMicrotaskSuppression() { reset(); }
+
+private:
+    WEBKIT_EXPORT void initialize();
+    WEBKIT_EXPORT void reset();
+
+#ifndef NDEBUG
+    class Impl;
+    WebPrivateOwnPtr<Impl> m_impl;
+#endif
+};
+
+} // WebKit
+
+#endif
index 0e58aa1..813c9d7 100644 (file)
@@ -896,6 +896,15 @@ v8::Handle<v8::Value> WebFrameImpl::executeScriptAndReturnValue(const WebScriptS
     return m_frame->script()->executeScript(ScriptSourceCode(source.code, source.url, position)).v8Value();
 }
 
+// Call the function with the given receiver and arguments, bypassing canExecuteScripts.
+v8::Handle<v8::Value> WebFrameImpl::callFunctionEvenIfScriptDisabled(v8::Handle<v8::Function> function,
+                                                                     v8::Handle<v8::Object> receiver,
+                                                                     int argc,
+                                                                     v8::Handle<v8::Value> argv[])
+{
+    return m_frame->script()->callFunctionEvenIfScriptDisabled(function, receiver, argc, argv).v8Value();
+}
+
 // Returns the V8 context for this frame, or an empty handle if there is none.
 v8::Local<v8::Context> WebFrameImpl::mainWorldScriptContext() const
 {
index 700f6ea..9c86d75 100644 (file)
@@ -110,6 +110,11 @@ public:
 #if WEBKIT_USING_V8
     virtual v8::Handle<v8::Value> executeScriptAndReturnValue(
         const WebScriptSource&);
+    virtual v8::Handle<v8::Value> callFunctionEvenIfScriptDisabled(
+        v8::Handle<v8::Function>,
+        v8::Handle<v8::Object>,
+        int argc,
+        v8::Handle<v8::Value> argv[]);
     virtual v8::Local<v8::Context> mainWorldScriptContext() const;
     virtual v8::Handle<v8::Value> createFileSystem(WebFileSystem::Type,
                                                    const WebString& name,
index 1670699..1d8ae91 100644 (file)
@@ -37,6 +37,7 @@
 #include "Settings.h"
 #include "TextEncoding.h"
 #include "V8Binding.h"
+#include "V8RecursionScope.h"
 #include "WebKitMutationObserver.h"
 #include "WebMediaPlayerClientImpl.h"
 #include "WebSocket.h"
@@ -89,6 +90,14 @@ static bool generateEntropy(unsigned char* buffer, size_t length)
     return false;
 }
 
+#ifndef NDEBUG
+static void assertV8RecursionScope()
+{
+    // FIXME: Enable when chromium usage of WebScriptInvocation has landed.
+    // ASSERT(!isMainThread() || WebCore::V8RecursionScope::properlyUsed());
+}
+#endif
+
 void initialize(WebKitPlatformSupport* webKitPlatformSupport)
 {
     initializeWithoutV8(webKitPlatformSupport);
@@ -98,6 +107,10 @@ void initialize(WebKitPlatformSupport* webKitPlatformSupport)
     WebCore::V8BindingPerIsolateData::ensureInitialized(v8::Isolate::GetCurrent());
 
 #if ENABLE(MUTATION_OBSERVERS)
+#ifndef NDEBUG
+    v8::V8::AddCallCompletedCallback(&assertV8RecursionScope);
+#endif
+
     // currentThread will always be non-null in production, but can be null in Chromium unit tests.
     if (WebThread* currentThread = webKitPlatformSupport->currentThread()) {
         ASSERT(!s_endOfTaskRunner);
@@ -139,6 +152,10 @@ void initializeWithoutV8(WebKitPlatformSupport* webKitPlatformSupport)
 void shutdown()
 {
 #if ENABLE(MUTATION_OBSERVERS)
+#ifndef NDEBUG
+    v8::V8::RemoveCallCompletedCallback(&assertV8RecursionScope);
+#endif
+
     if (s_endOfTaskRunner) {
         ASSERT(s_webKitPlatformSupport->currentThread());
         s_webKitPlatformSupport->currentThread()->removeTaskObserver(s_endOfTaskRunner);
diff --git a/Source/WebKit/chromium/src/WebScopedMicrotaskSuppression.cpp b/Source/WebKit/chromium/src/WebScopedMicrotaskSuppression.cpp
new file mode 100644 (file)
index 0000000..7d165c1
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WebScopedMicrotaskSuppression.h"
+
+#include "V8RecursionScope.h"
+#include <wtf/OwnPtr.h>
+
+namespace WebKit {
+
+#ifndef NDEBUG
+class WebScopedMicrotaskSuppression::Impl : public WebCore::V8RecursionScope::MicrotaskSuppression { };
+#endif
+
+void WebScopedMicrotaskSuppression::initialize()
+{
+#ifndef NDEBUG
+    m_impl.reset(new Impl());
+#endif
+}
+
+void WebScopedMicrotaskSuppression::reset()
+{
+#ifndef NDEBUG
+    m_impl.reset(0);
+#endif
+}
+
+} // namespace WebKit