diff options
Diffstat (limited to 'Tools/WebKitTestRunner/UIScriptContext/UIScriptContext.cpp')
-rw-r--r-- | Tools/WebKitTestRunner/UIScriptContext/UIScriptContext.cpp | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/Tools/WebKitTestRunner/UIScriptContext/UIScriptContext.cpp b/Tools/WebKitTestRunner/UIScriptContext/UIScriptContext.cpp new file mode 100644 index 000000000..4d8a8c0b4 --- /dev/null +++ b/Tools/WebKitTestRunner/UIScriptContext/UIScriptContext.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2015 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "UIScriptContext.h" + +#include "StringFunctions.h" +#include "UIScriptController.h" +#include <JavaScriptCore/JSContextRef.h> +#include <JavaScriptCore/JSValueRef.h> +#include <WebKit/WKRetainPtr.h> +#include <WebKit/WKString.h> +#include <WebKit/WKStringPrivate.h> + +using namespace WTR; + +static inline bool isPersistentCallbackID(unsigned callbackID) +{ + return callbackID < firstNonPersistentCallbackID; +} + +UIScriptContext::UIScriptContext(UIScriptContextDelegate& delegate) + : m_context(Adopt, JSGlobalContextCreate(nullptr)) + , m_delegate(delegate) +{ + m_controller = UIScriptController::create(*this); + + JSObjectRef globalObject = JSContextGetGlobalObject(m_context.get()); + + JSValueRef exception = nullptr; + m_controller->makeWindowObject(m_context.get(), globalObject, &exception); +} + +UIScriptContext::~UIScriptContext() +{ + m_controller->contextDestroyed(); +} + +void UIScriptContext::runUIScript(WKStringRef script, unsigned scriptCallbackID) +{ + m_currentScriptCallbackID = scriptCallbackID; + + auto scriptRef = toJS(script); + + JSValueRef exception = nullptr; + JSValueRef result = JSEvaluateScript(m_context.get(), scriptRef.get(), 0, 0, 1, &exception); + + if (!hasOutstandingAsyncTasks()) { + JSValueRef stringifyException = nullptr; + requestUIScriptCompletion(JSValueToStringCopy(m_context.get(), result, &stringifyException)); + tryToCompleteUIScriptForCurrentParentCallback(); + } +} + +unsigned UIScriptContext::nextTaskCallbackID(CallbackType type) +{ + if (type == CallbackTypeNonPersistent) + return ++m_nextTaskCallbackID + firstNonPersistentCallbackID; + + return type; +} + +unsigned UIScriptContext::prepareForAsyncTask(JSValueRef callback, CallbackType type) +{ + unsigned callbackID = nextTaskCallbackID(type); + + JSValueProtect(m_context.get(), callback); + Task task; + task.parentScriptCallbackID = m_currentScriptCallbackID; + task.callback = callback; + + ASSERT(!m_callbacks.contains(callbackID)); + m_callbacks.add(callbackID, task); + + return callbackID; +} + +void UIScriptContext::asyncTaskComplete(unsigned callbackID) +{ + Task task = m_callbacks.take(callbackID); + ASSERT(task.callback); + + JSValueRef exception = nullptr; + JSObjectRef callbackObject = JSValueToObject(m_context.get(), task.callback, &exception); + + m_currentScriptCallbackID = task.parentScriptCallbackID; + + exception = nullptr; + JSObjectCallAsFunction(m_context.get(), callbackObject, JSContextGetGlobalObject(m_context.get()), 0, nullptr, &exception); + JSValueUnprotect(m_context.get(), task.callback); + + tryToCompleteUIScriptForCurrentParentCallback(); + m_currentScriptCallbackID = 0; +} + +unsigned UIScriptContext::registerCallback(JSValueRef taskCallback, CallbackType type) +{ + if (m_callbacks.contains(type)) + unregisterCallback(type); + + return prepareForAsyncTask(taskCallback, type); +} + +void UIScriptContext::unregisterCallback(unsigned callbackID) +{ + Task task = m_callbacks.take(callbackID); + ASSERT(task.callback); + JSValueUnprotect(m_context.get(), task.callback); +} + +JSValueRef UIScriptContext::callbackWithID(unsigned callbackID) +{ + Task task = m_callbacks.get(callbackID); + return task.callback; +} + +void UIScriptContext::fireCallback(unsigned callbackID) +{ + Task task = m_callbacks.get(callbackID); + ASSERT(task.callback); + + JSValueRef exception = nullptr; + JSObjectRef callbackObject = JSValueToObject(m_context.get(), task.callback, &exception); + + m_currentScriptCallbackID = task.parentScriptCallbackID; + + exception = nullptr; + JSObjectCallAsFunction(m_context.get(), callbackObject, JSContextGetGlobalObject(m_context.get()), 0, nullptr, &exception); + + tryToCompleteUIScriptForCurrentParentCallback(); + m_currentScriptCallbackID = 0; +} + +void UIScriptContext::requestUIScriptCompletion(JSStringRef result) +{ + ASSERT(m_currentScriptCallbackID); + if (currentParentCallbackIsPendingCompletion()) + return; + + // This request for the UI script to complete is not fulfilled until the last non-persistent task for the parent callback is finished. + m_uiScriptResultsPendingCompletion.add(m_currentScriptCallbackID, result ? JSStringRetain(result) : nullptr); +} + +void UIScriptContext::tryToCompleteUIScriptForCurrentParentCallback() +{ + if (!currentParentCallbackIsPendingCompletion() || currentParentCallbackHasOutstandingAsyncTasks()) + return; + + JSStringRef result = m_uiScriptResultsPendingCompletion.take(m_currentScriptCallbackID); + WKRetainPtr<WKStringRef> uiScriptResult = adoptWK(WKStringCreateWithJSString(result)); + m_delegate.uiScriptDidComplete(uiScriptResult.get(), m_currentScriptCallbackID); + m_currentScriptCallbackID = 0; + if (result) + JSStringRelease(result); +} + +JSObjectRef UIScriptContext::objectFromRect(const WKRect& rect) const +{ + JSObjectRef object = JSObjectMake(m_context.get(), nullptr, nullptr); + + JSObjectSetProperty(m_context.get(), object, adopt(JSStringCreateWithUTF8CString("left")).get(), JSValueMakeNumber(m_context.get(), rect.origin.x), kJSPropertyAttributeNone, nullptr); + JSObjectSetProperty(m_context.get(), object, adopt(JSStringCreateWithUTF8CString("top")).get(), JSValueMakeNumber(m_context.get(), rect.origin.y), kJSPropertyAttributeNone, nullptr); + JSObjectSetProperty(m_context.get(), object, adopt(JSStringCreateWithUTF8CString("width")).get(), JSValueMakeNumber(m_context.get(), rect.size.width), kJSPropertyAttributeNone, nullptr); + JSObjectSetProperty(m_context.get(), object, adopt(JSStringCreateWithUTF8CString("height")).get(), JSValueMakeNumber(m_context.get(), rect.size.height), kJSPropertyAttributeNone, nullptr); + + return object; +} + +bool UIScriptContext::currentParentCallbackHasOutstandingAsyncTasks() const +{ + for (auto entry : m_callbacks) { + unsigned callbackID = entry.key; + Task task = entry.value; + if (task.parentScriptCallbackID == m_currentScriptCallbackID && !isPersistentCallbackID(callbackID)) + return true; + } + + return false; +} + |