summaryrefslogtreecommitdiff
path: root/Tools/WebKitTestRunner/UIScriptContext/UIScriptContext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/WebKitTestRunner/UIScriptContext/UIScriptContext.cpp')
-rw-r--r--Tools/WebKitTestRunner/UIScriptContext/UIScriptContext.cpp202
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;
+}
+