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
+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.
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 \
'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',
#include "config.h"
#include "DateExtension.h"
-#include "V8Proxy.h"
#include "V8HiddenPropertyName.h"
+#include "V8Proxy.h"
+#include "V8RecursionScope.h"
namespace WebCore {
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);
}
#include "V8Binding.h"
#include "V8DOMWindow.h"
#include "V8Proxy.h"
+#include "V8RecursionScope.h"
#include <wtf/OwnPtr.h>
#include <wtf/PassOwnPtr.h>
#include <wtf/StdLibExtras.h>
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());
--- /dev/null
+/*
+* 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
#include "V8IsolatedContext.h"
#include "V8NPObject.h"
#include "V8Proxy.h"
+#include "V8RecursionScope.h"
#include "Widget.h"
#include <wtf/StdLibExtras.h>
#include <wtf/text/CString.h>
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);
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();
}
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);
#include "ScriptObject.h"
#include "V8Binding.h"
#include "V8JavaScriptCallFrame.h"
+#include "V8RecursionScope.h"
#include <wtf/StdLibExtras.h>
#include <wtf/Vector.h>
}
+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)
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());
}
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)
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();
}
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())
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())
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);
}
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()));
}
}
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;
#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>
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();
: m_domDataStore(0)
, m_constructorMode(ConstructorMode::CreateNewObject)
, m_recursionLevel(0)
+#ifndef NDEBUG
+ , m_internalScriptRecursionLevel(0)
+#endif
{
}
#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:
#ifndef NDEBUG
GlobalHandleMap m_globalHandleMap;
+ int m_internalScriptRecursionLevel;
#endif
};
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,
#include "Page.h"
#include "PageGroup.h"
#include "RuntimeEnabledFeatures.h"
+#include "SafeAllocation.h"
#include "ScriptCallStack.h"
#include "ScriptCallStackFactory.h"
#include "ScriptProfiler.h"
#include "EventTargetHeaders.h"
#include "EventTargetInterfaces.h"
#include "FrameLoaderClient.h"
+#include "SVGElementInstance.h"
+#include "SafeAllocation.h"
#include "StylePropertySet.h"
#include "V8AbstractEventListener.h"
#include "V8Binding.h"
#include "V8HiddenPropertyName.h"
#include "V8Node.h"
#include "V8Proxy.h"
+#include "V8RecursionScope.h"
#include "WorldContextHandle.h"
#include <wtf/StdLibExtras.h>
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;
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;
#include "HTMLPlugInElement.h"
#include "NPV8Object.h"
+#include "SafeAllocation.h"
#include "V8Binding.h"
#include "V8DOMMap.h"
#include "V8HTMLAppletElement.h"
#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:
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();
#include "DedicatedWorkerContext.h"
#include "Event.h"
+#include "SafeAllocation.h"
#include "ScriptCallStack.h"
#include "SharedWorker.h"
#include "SharedWorkerContext.h"
#include "V8IsolatedContext.h"
#include "V8Node.h"
#include "V8Proxy.h"
+#include "V8RecursionScope.h"
#include <wtf/OwnArrayPtr.h>
#include <wtf/RefPtr.h>
#include <wtf/StdLibExtras.h>
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);
#include "DOMWindow.h"
#include "InjectedScript.h"
#include "InjectedScriptHost.h"
+#include "SafeAllocation.h"
#include "ScriptValue.h"
#include "V8Binding.h"
#include "V8BindingState.h"
#include "V8HiddenPropertyName.h"
#include "V8InjectedScriptHost.h"
#include "V8Proxy.h"
+#include "V8RecursionScope.h"
#include <wtf/RefPtr.h>
namespace WebCore {
// 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());
#if ENABLE(JAVASCRIPT_DEBUGGER)
#include "V8ScriptProfile.h"
+#include "SafeAllocation.h"
#include "ScriptProfile.h"
#include "V8Binding.h"
#include "V8Proxy.h"
#if ENABLE(JAVASCRIPT_DEBUGGER)
#include "V8ScriptProfileNode.h"
+#include "SafeAllocation.h"
#include "ScriptProfileNode.h"
#include "V8Binding.h"
#include "V8Proxy.h"
+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
'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',
#if WEBKIT_USING_V8
namespace v8 {
class Context;
+class Function;
+class Object;
class Value;
template <class T> class Handle;
template <class T> class Local;
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;
--- /dev/null
+/*
+ * 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
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
{
#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,
#include "Settings.h"
#include "TextEncoding.h"
#include "V8Binding.h"
+#include "V8RecursionScope.h"
#include "WebKitMutationObserver.h"
#include "WebMediaPlayerClientImpl.h"
#include "WebSocket.h"
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);
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);
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);
--- /dev/null
+/*
+ * 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