Title: [295526] trunk
Revision
295526
Author
commit-qu...@webkit.org
Date
2022-06-14 10:09:19 -0700 (Tue, 14 Jun 2022)

Log Message

[GLib] Support for JS scripts evaluation involving Promises
https://bugs.webkit.org/show_bug.cgi?id=241418

Patch by Philippe Normand <ph...@igalia.com> on 2022-06-14
Reviewed by Adrian Perez de Castro and Michael Catanzaro.

The current APIs for `webkit_web_view_run_javascript_*` do not handle scripts involving Promises.
This new API fills the gap.

* Source/_javascript_Core/API/glib/JSCDefines.h:
* Source/WebKit/Shared/API/APISerializedScriptValue.h:
* Source/WebKit/SourcesGTK.txt:
* Source/WebKit/SourcesWPE.txt:
* Source/WebKit/UIProcess/API/glib/APISerializedScriptValueGLib.cpp: Added.
(API::SharedJSContext::SharedJSContext):
(API::SharedJSContext::ensureContext):
(API::SharedJSContext::releaseContextIfNecessary):
(API::sharedContext):
(API::valueFromGVariant):
(API::coreValueFromGVariant):
(API::SerializedScriptValue::createFromGVariant):
* Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp:
(webkit_web_view_run_javascript_in_world):
(webkit_web_view_run_async_javascript_function_in_world):
* Source/WebKit/UIProcess/API/gtk/WebKitWebView.h:
* Source/WebKit/UIProcess/API/wpe/WebKitWebView.h:
* Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebView.cpp:
(testWebViewRunAsyncFunctions):
(beforeAll):
* Tools/TestWebKitAPI/glib/WebKitGLib/WebViewTest.cpp:
(WebViewTest::runJavaScriptInWorldAndWaitUntilFinished):
(WebViewTest::runAsyncJavaScriptFunctionInWorldAndWaitUntilFinished):
* Tools/TestWebKitAPI/glib/WebKitGLib/WebViewTest.h:

Canonical link: https://commits.webkit.org/251531@main

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/API/glib/JSCDefines.h (295525 => 295526)


--- trunk/Source/_javascript_Core/API/glib/JSCDefines.h	2022-06-14 17:07:46 UTC (rev 295525)
+++ trunk/Source/_javascript_Core/API/glib/JSCDefines.h	2022-06-14 17:09:19 UTC (rev 295526)
@@ -23,7 +23,7 @@
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#if !defined(__JSC_H_INSIDE__) && !defined(JSC_COMPILATION)
+#if !defined(__JSC_H_INSIDE__) && !defined(JSC_COMPILATION) && !defined(WEBKIT2_COMPILATION)
 #error "Only <jsc/jsc.h> can be included directly."
 #endif
 

Modified: trunk/Source/WebKit/Shared/API/APISerializedScriptValue.h (295525 => 295526)


--- trunk/Source/WebKit/Shared/API/APISerializedScriptValue.h	2022-06-14 17:07:46 UTC (rev 295525)
+++ trunk/Source/WebKit/Shared/API/APISerializedScriptValue.h	2022-06-14 17:09:19 UTC (rev 295526)
@@ -64,6 +64,10 @@
     static RefPtr<SerializedScriptValue> createFromNSObject(id);
 #endif
 
+#if USE(GLIB)
+    static RefPtr<SerializedScriptValue> createFromGVariant(GVariant*);
+#endif
+
     IPC::DataReference dataReference() const { return m_serializedScriptValue->wireBytes(); }
 
     WebCore::SerializedScriptValue& internalRepresentation() { return m_serializedScriptValue.get(); }

Modified: trunk/Source/WebKit/SourcesGTK.txt (295525 => 295526)


--- trunk/Source/WebKit/SourcesGTK.txt	2022-06-14 17:07:46 UTC (rev 295525)
+++ trunk/Source/WebKit/SourcesGTK.txt	2022-06-14 17:09:19 UTC (rev 295526)
@@ -121,6 +121,7 @@
 UIProcess/API/C/gtk/WKView.cpp
 
 UIProcess/API/glib/APIContentRuleListStoreGLib.cpp @no-unify
+UIProcess/API/glib/APISerializedScriptValueGLib.cpp @no-unify
 UIProcess/API/glib/IconDatabase.cpp @no-unify
 UIProcess/API/glib/InputMethodFilter.cpp @no-unify
 UIProcess/API/glib/WebKitApplicationInfo.cpp @no-unify

Modified: trunk/Source/WebKit/SourcesWPE.txt (295525 => 295526)


--- trunk/Source/WebKit/SourcesWPE.txt	2022-06-14 17:07:46 UTC (rev 295525)
+++ trunk/Source/WebKit/SourcesWPE.txt	2022-06-14 17:09:19 UTC (rev 295526)
@@ -114,6 +114,7 @@
 UIProcess/API/C/wpe/WKView.cpp
 
 UIProcess/API/glib/APIContentRuleListStoreGLib.cpp @no-unify
+UIProcess/API/glib/APISerializedScriptValueGLib.cpp @no-unify
 UIProcess/API/glib/IconDatabase.cpp @no-unify
 UIProcess/API/glib/InputMethodFilter.cpp @no-unify
 UIProcess/API/glib/WebKitApplicationInfo.cpp @no-unify

Added: trunk/Source/WebKit/UIProcess/API/glib/APISerializedScriptValueGLib.cpp (0 => 295526)


--- trunk/Source/WebKit/UIProcess/API/glib/APISerializedScriptValueGLib.cpp	                        (rev 0)
+++ trunk/Source/WebKit/UIProcess/API/glib/APISerializedScriptValueGLib.cpp	2022-06-14 17:09:19 UTC (rev 295526)
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2022 Igalia S.L.
+ *
+ * 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 "APISerializedScriptValue.h"
+
+#include <_javascript_Core/APICast.h>
+#include <_javascript_Core/JSBase.h>
+#include <_javascript_Core/JSContextPrivate.h>
+#include <_javascript_Core/JSGlobalObjectInlines.h>
+#include <_javascript_Core/JSRemoteInspector.h>
+#include <jsc/JSCContextPrivate.h>
+#include <jsc/JSCValuePrivate.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/RunLoop.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
+
+namespace API {
+
+static constexpr auto sharedJSContextMaxIdleTime = 10_s;
+
+class SharedJSContext {
+public:
+    SharedJSContext()
+        : m_timer(RunLoop::main(), this, &SharedJSContext::releaseContextIfNecessary)
+    {
+    }
+
+    JSCContext* ensureContext()
+    {
+        m_lastUseTime = MonotonicTime::now();
+        if (!m_context) {
+            bool previous = JSRemoteInspectorGetInspectionEnabledByDefault();
+            JSRemoteInspectorSetInspectionEnabledByDefault(false);
+            m_context = adoptGRef(jsc_context_new());
+            JSRemoteInspectorSetInspectionEnabledByDefault(previous);
+
+            m_timer.startOneShot(sharedJSContextMaxIdleTime);
+        }
+        return m_context.get();
+    }
+
+    void releaseContextIfNecessary()
+    {
+        auto idleTime = MonotonicTime::now() - m_lastUseTime;
+        if (idleTime < sharedJSContextMaxIdleTime) {
+            // We lazily restart the timer if needed every 10 seconds instead of doing so every time ensureContext()
+            // is called, for performance reasons.
+            m_timer.startOneShot(sharedJSContextMaxIdleTime - idleTime);
+            return;
+        }
+        m_context.clear();
+    }
+
+private:
+    GRefPtr<JSCContext> m_context;
+    RunLoop::Timer<SharedJSContext> m_timer;
+    MonotonicTime m_lastUseTime;
+};
+
+static SharedJSContext& sharedContext()
+{
+    static NeverDestroyed<SharedJSContext> sharedContext;
+    return sharedContext.get();
+}
+
+static GRefPtr<JSCValue> valueFromGVariant(JSCContext* context, GVariant* variant)
+{
+    if (g_variant_is_container(variant)) {
+        auto result = adoptGRef(jsc_value_new_object(context, nullptr, nullptr));
+        GVariantIter iter;
+        g_variant_iter_init(&iter, variant);
+        const char* key;
+        GVariant* value;
+        while (g_variant_iter_loop(&iter, "{&sv}", &key, &value)) {
+            if (!key)
+                continue;
+            auto jsValue = valueFromGVariant(context, value);
+            if (jsValue)
+                jsc_value_object_set_property(result.get(), key, jsValue.get());
+        }
+        return result;
+    }
+
+    if (g_variant_is_of_type(variant, G_VARIANT_TYPE_UINT32))
+        return adoptGRef(jsc_value_new_number(context, g_variant_get_uint32(variant)));
+    if (g_variant_is_of_type(variant, G_VARIANT_TYPE_INT32))
+        return adoptGRef(jsc_value_new_number(context, g_variant_get_int32(variant)));
+    if (g_variant_is_of_type(variant, G_VARIANT_TYPE_UINT64))
+        return adoptGRef(jsc_value_new_number(context, g_variant_get_uint64(variant)));
+    if (g_variant_is_of_type(variant, G_VARIANT_TYPE_INT64))
+        return adoptGRef(jsc_value_new_number(context, g_variant_get_int64(variant)));
+    if (g_variant_is_of_type(variant, G_VARIANT_TYPE_INT16))
+        return adoptGRef(jsc_value_new_number(context, g_variant_get_int16(variant)));
+    if (g_variant_is_of_type(variant, G_VARIANT_TYPE_UINT16))
+        return adoptGRef(jsc_value_new_number(context, g_variant_get_uint16(variant)));
+    if (g_variant_is_of_type(variant, G_VARIANT_TYPE_DOUBLE))
+        return adoptGRef(jsc_value_new_number(context, g_variant_get_double(variant)));
+
+    if (g_variant_is_of_type(variant, G_VARIANT_TYPE_STRING))
+        return adoptGRef(jsc_value_new_string(context, g_variant_get_string(variant, nullptr)));
+
+    g_warning("Unhandled %s GVariant for conversion to JSCValue", g_variant_get_type_string(variant));
+    return nullptr;
+}
+
+static RefPtr<WebCore::SerializedScriptValue> coreValueFromGVariant(GVariant* variant)
+{
+    if (!variant)
+        return nullptr;
+
+    ASSERT(RunLoop::isMain());
+    auto* context = sharedContext().ensureContext();
+    auto value = valueFromGVariant(context, variant);
+    if (!value)
+        return nullptr;
+
+    auto globalObject = toJS(jscContextGetJSContext(context));
+    ASSERT(globalObject);
+    JSC::JSLockHolder lock(globalObject);
+
+    return WebCore::SerializedScriptValue::create(*globalObject, toJS(globalObject, jscValueGetJSValue(value.get())));
+}
+
+RefPtr<SerializedScriptValue> SerializedScriptValue::createFromGVariant(GVariant* object)
+{
+    auto coreValue = coreValueFromGVariant(object);
+    if (!coreValue)
+        return nullptr;
+    return create(coreValue.releaseNonNull());
+}
+
+}; // namespace API

Modified: trunk/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp (295525 => 295526)


--- trunk/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp	2022-06-14 17:07:46 UTC (rev 295525)
+++ trunk/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp	2022-06-14 17:09:19 UTC (rev 295526)
@@ -3994,6 +3994,119 @@
     });
 }
 
+/*
+ * webkit_web_view_run_async_javascript_function_in_world:
+ * @web_view: a #WebKitWebView
+ * @body: the _javascript_ function body
+ * @arguments: a #GVariant with format `{&sv}` storing the function arguments. Function argument values must be one of the following types, or contain only the following GVariant types: number, string, array, and dictionary.
+ * @world_name (nullable): the name of a #WebKitScriptWorld, if no name is provided, the default world is used.
+ * @cancellable: (allow-none): a #GCancellable or %NULL to ignore
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the script finished
+ * @user_data: (closure): the data to pass to callback function
+ *
+ * Asynchronously run @body in the script world with name @world_name of the current page context in
+ * @web_view. If WebKitSettings:enable-_javascript_ is FALSE, this method will do nothing. This API
+ * differs from webkit_web_view_run_javascript_in_world() in that the _javascript_ function can return a
+ * Promise and its result will be properly passed to the callback.
+ *
+ * When the operation is finished, @callback will be called. You can then call
+ * webkit_web_view_run_javascript_in_world_finish() to get the result of the operation.
+ *
+ * For instance here is a dummy example that shows how to pass arguments to a JS function that
+ * returns a Promise that resolves with the passed argument:
+ *
+ * ```c
+ * static void
+ * web_view_javascript_finished (GObject      *object,
+ *                               GAsyncResult *result,
+ *                               gpointer      user_data)
+ * {
+ *     WebKitJavascriptResult *js_result;
+ *     JSCValue               *value;
+ *     GError                 *error = NULL;
+ *
+ *     js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (object), result, &error);
+ *     if (!js_result) {
+ *         g_warning ("Error running _javascript_: %s", error->message);
+ *         g_error_free (error);
+ *         return;
+ *     }
+ *
+ *     value = webkit_javascript_result_get_js_value (js_result);
+ *     if (jsc_value_is_number (value)) {
+ *         gint32        int_value = jsc_value_to_string (value);
+ *         JSCException *exception = jsc_context_get_exception (jsc_value_get_context (value));
+ *         if (exception)
+ *             g_warning ("Error running _javascript_: %s", jsc_exception_get_message (exception));
+ *         else
+ *             g_print ("Script result: %d\n", int_value);
+ *         g_free (str_value);
+ *     } else {
+ *         g_warning ("Error running _javascript_: unexpected return value");
+ *     }
+ *     webkit_javascript_result_unref (js_result);
+ * }
+ *
+ * static void
+ * web_view_evaluate_promise (WebKitWebView *web_view)
+ * {
+ *     GVariantDict dict;
+ *     g_variant_dict_init (&dict, NULL);
+ *     g_variant_dict_insert (&dict, "count", "u", 42);
+ *     GVariant *args = g_variant_dict_end (&dict);
+ *     const gchar *body = "return new Promise((resolve) => { resolve(count); });";
+ *     webkit_web_view_run_async_javascript_function_in_world (web_view, body, arguments, NULL, NULL, web_view_javascript_finished, NULL);
+ * }
+ * ```
+ *
+ * Since: 2.38
+ */
+void webkit_web_view_run_async_javascript_function_in_world(WebKitWebView* webView, const gchar* body, GVariant* arguments, const char* worldName, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData)
+{
+    g_return_if_fail(WEBKIT_IS_WEB_VIEW(webView));
+    g_return_if_fail(body);
+    g_return_if_fail(worldName);
+
+    auto task = adoptGRef(g_task_new(webView, cancellable, callback, userData));
+    auto world = API::ContentWorld::sharedWorldWithName(String::fromUTF8(worldName ? worldName : ""));
+    bool hasInvalidArgument = false;
+    auto argumentsMap = WebCore::ArgumentWireBytesMap { };
+
+    if (arguments) {
+        GVariantIter iter;
+        g_variant_iter_init(&iter, arguments);
+        const char* key;
+        GVariant* value;
+        while (g_variant_iter_loop(&iter, "{&sv}", &key, &value)) {
+            if (!key)
+                continue;
+            auto serializedValue = API::SerializedScriptValue::createFromGVariant(value);
+            if (!serializedValue) {
+                hasInvalidArgument = true;
+                break;
+            }
+            argumentsMap.set(String::fromUTF8(key), serializedValue->internalRepresentation().wireBytes());
+        }
+    }
+
+    if (hasInvalidArgument) {
+        ExceptionDetails exceptionDetails;
+        exceptionDetails.message = "Function argument values must be one of the following types, or contain only the following GVariant types: number, string, array, and dictionary"_s;
+        webkitWebViewRunJavaScriptCallback(nullptr, exceptionDetails, task.get());
+        return;
+    }
+
+    getPage(webView).runJavaScriptInFrameInScriptWorld({ String::fromUTF8(body), URL { }, RunAsAsyncFunction::Yes, WTFMove(argumentsMap), ForceUserGesture::Yes }, std::nullopt, world.get(), [task = WTFMove(task)](auto&& result) {
+        RefPtr<API::SerializedScriptValue> serializedScriptValue;
+        ExceptionDetails exceptionDetails;
+        if (result.has_value())
+            serializedScriptValue = WTFMove(result.value());
+        else
+            exceptionDetails = WTFMove(result.error());
+        webkitWebViewRunJavaScriptCallback(serializedScriptValue.get(), exceptionDetails, task.get());
+    });
+}
+
 /**
  * webkit_web_view_run_javascript_in_world_finish:
  * @web_view: a #WebKitWebView

Modified: trunk/Source/WebKit/UIProcess/API/gtk/WebKitWebView.h (295525 => 295526)


--- trunk/Source/WebKit/UIProcess/API/gtk/WebKitWebView.h	2022-06-14 17:07:46 UTC (rev 295525)
+++ trunk/Source/WebKit/UIProcess/API/gtk/WebKitWebView.h	2022-06-14 17:09:19 UTC (rev 295526)
@@ -491,6 +491,15 @@
                                                       GError                   **error);
 
 WEBKIT_API void
+webkit_web_view_run_async_javascript_function_in_world (WebKitWebView           *web_view,
+                                                        const gchar             *body,
+                                                        GVariant                *arguments,
+                                                        const char              *world_name,
+                                                        GCancellable            *cancellable,
+                                                        GAsyncReadyCallback      callback,
+                                                        gpointer                 user_data);
+
+WEBKIT_API void
 webkit_web_view_run_javascript_from_gresource        (WebKitWebView             *web_view,
                                                       const gchar               *resource,
                                                       GCancellable              *cancellable,

Modified: trunk/Source/WebKit/UIProcess/API/wpe/WebKitWebView.h (295525 => 295526)


--- trunk/Source/WebKit/UIProcess/API/wpe/WebKitWebView.h	2022-06-14 17:07:46 UTC (rev 295525)
+++ trunk/Source/WebKit/UIProcess/API/wpe/WebKitWebView.h	2022-06-14 17:09:19 UTC (rev 295526)
@@ -474,6 +474,15 @@
                                                       GError                   **error);
 
 WEBKIT_API void
+webkit_web_view_run_async_javascript_function_in_world (WebKitWebView            *web_view,
+                                                        const gchar              *body,
+                                                        GVariant                 *arguments,
+                                                        const char               *world_name,
+                                                        GCancellable*             cancellable,
+                                                        GAsyncReadyCallback       callback,
+                                                        gpointer                  user_data);
+
+WEBKIT_API void
 webkit_web_view_run_javascript_from_gresource        (WebKitWebView             *web_view,
                                                       const gchar               *resource,
                                                       GCancellable              *cancellable,

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebView.cpp (295525 => 295526)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebView.cpp	2022-06-14 17:07:46 UTC (rev 295525)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebView.cpp	2022-06-14 17:09:19 UTC (rev 295526)
@@ -319,6 +319,125 @@
     g_assert_cmpfloat(webkit_web_view_get_zoom_level(test->m_webView), ==, 2.5);
 }
 
+static void testWebViewRunAsyncFunctions(WebViewTest* test, gconstpointer)
+{
+    GUniqueOutPtr<GError> error;
+
+    WebKitJavascriptResult* _javascript_Result = test->runAsyncJavaScriptFunctionInWorldAndWaitUntilFinished("return new Promise((resolve) => { resolve(42); });", nullptr, "", &error.outPtr());
+    g_assert_nonnull(_javascript_Result);
+    test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(_javascript_Result)));
+    g_assert_no_error(error.get());
+    g_assert_cmpfloat(WebViewTest::_javascript_ResultToNumber(_javascript_Result), ==, 42);
+
+    GVariantDict dict;
+    g_variant_dict_init(&dict, nullptr);
+    g_variant_dict_insert(&dict, "count", "u", 42);
+    auto* args = g_variant_dict_end(&dict);
+    _javascript_Result = test->runAsyncJavaScriptFunctionInWorldAndWaitUntilFinished("return new Promise((resolve) => { resolve(count); });", args, "", &error.outPtr());
+    g_assert_nonnull(_javascript_Result);
+    test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(_javascript_Result)));
+    g_assert_no_error(error.get());
+    g_assert_cmpfloat(WebViewTest::_javascript_ResultToNumber(_javascript_Result), ==, 42);
+
+    g_variant_dict_init(&dict, nullptr);
+    g_variant_dict_insert(&dict, "motto", "s", "Never gonna give you up");
+    args = g_variant_dict_end(&dict);
+    _javascript_Result = test->runAsyncJavaScriptFunctionInWorldAndWaitUntilFinished("return new Promise((resolve) => { resolve(motto); });", args, "", &error.outPtr());
+    g_assert_nonnull(_javascript_Result);
+    test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(_javascript_Result)));
+    g_assert_no_error(error.get());
+    GUniquePtr<char> valueString(WebViewTest::_javascript_ResultToCString(_javascript_Result));
+    g_assert_cmpstr(valueString.get(), ==, "Never gonna give you up");
+
+    _javascript_Result = test->runAsyncJavaScriptFunctionInWorldAndWaitUntilFinished("return new Promise(function(resolve, reject) { setTimeout(function(){ reject('Rejected!') }, 0); })", nullptr, "", &error.outPtr());
+    g_assert_null(_javascript_Result);
+    g_assert_error(error.get(), WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED);
+    g_assert_true(g_strstr_len(error->message, strlen(error->message), "Rejected!") != nullptr);
+
+    g_variant_dict_init(&dict, nullptr);
+    g_variant_dict_insert(&dict, "countt", "u", 42);
+    args = g_variant_dict_end(&dict);
+    _javascript_Result = test->runAsyncJavaScriptFunctionInWorldAndWaitUntilFinished("return new Promise((resolve) => { resolve(count); });", args, "", &error.outPtr());
+    g_assert_null(_javascript_Result);
+    g_assert_error(error.get(), WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED);
+
+    g_variant_dict_init(&dict, nullptr);
+    g_variant_dict_insert(&dict, "count", "u", 42);
+    args = g_variant_dict_end(&dict);
+    _javascript_Result = test->runAsyncJavaScriptFunctionInWorldAndWaitUntilFinished("return count", args, "", &error.outPtr());
+    g_assert_nonnull(_javascript_Result);
+    test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(_javascript_Result)));
+    g_assert_no_error(error.get());
+    g_assert_cmpfloat(WebViewTest::_javascript_ResultToNumber(_javascript_Result), ==, 42);
+
+    {
+        // Set a value in main world.
+        WebKitJavascriptResult* _javascript_Result = test->runJavaScriptInWorldAndWaitUntilFinished("a = 25;", "", &error.outPtr());
+        g_assert_nonnull(_javascript_Result);
+        test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(_javascript_Result)));
+        g_assert_no_error(error.get());
+        g_assert_cmpfloat(WebViewTest::_javascript_ResultToNumber(_javascript_Result), ==, 25);
+
+        // Read back value from main world.
+        _javascript_Result = test->runAsyncJavaScriptFunctionInWorldAndWaitUntilFinished("return a", nullptr, "", &error.outPtr());
+        g_assert_nonnull(_javascript_Result);
+        test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(_javascript_Result)));
+        g_assert_no_error(error.get());
+        g_assert_cmpfloat(WebViewTest::_javascript_ResultToNumber(_javascript_Result), ==, 25);
+
+        // Values of the main world are not available in the isolated one.
+        _javascript_Result = test->runAsyncJavaScriptFunctionInWorldAndWaitUntilFinished("return a", nullptr, "WebExtensionTestScriptWorld", &error.outPtr());
+        g_assert_null(_javascript_Result);
+        g_assert_error(error.get(), WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED);
+
+        // Running a script in a world that doesn't exist should fail.
+        _javascript_Result = test->runAsyncJavaScriptFunctionInWorldAndWaitUntilFinished("return a", nullptr, "InvalidScriptWorld", &error.outPtr());
+        g_assert_null(_javascript_Result);
+        g_assert_error(error.get(), WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED);
+    }
+
+    {
+        // Disable JS support and expect an error when attempting to evaluate JS code.
+        WebKitSettings* defaultSettings = webkit_web_view_get_settings(test->m_webView);
+        test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(defaultSettings));
+        g_assert_nonnull(defaultSettings);
+        g_assert_true(webkit_settings_get_enable_javascript(defaultSettings));
+
+        GRefPtr<WebKitSettings> newSettings = adoptGRef(webkit_settings_new());
+        test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(newSettings.get()));
+        g_object_set(G_OBJECT(newSettings.get()), "enable-_javascript_", FALSE, NULL);
+        webkit_web_view_set_settings(test->m_webView, newSettings.get());
+
+        WebKitJavascriptResult* _javascript_Result = test->runAsyncJavaScriptFunctionInWorldAndWaitUntilFinished("return new Promise((resolve) => { resolve(42); });", nullptr, "", &error.outPtr());
+        g_assert_null(_javascript_Result);
+        g_assert_error(error.get(), WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED);
+
+        g_object_set(G_OBJECT(newSettings.get()), "enable-_javascript_", TRUE, NULL);
+        webkit_web_view_set_settings(test->m_webView, newSettings.get());
+    }
+
+    {
+        // Disable JS markup support and expect no error when attempting to evaluate JS code.
+        WebKitSettings* defaultSettings = webkit_web_view_get_settings(test->m_webView);
+        test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(defaultSettings));
+        g_assert_nonnull(defaultSettings);
+        g_assert_true(webkit_settings_get_enable_javascript_markup(defaultSettings));
+
+        GRefPtr<WebKitSettings> newSettings = adoptGRef(webkit_settings_new());
+        test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(newSettings.get()));
+        g_object_set(G_OBJECT(newSettings.get()), "enable-_javascript_-markup", FALSE, NULL);
+        webkit_web_view_set_settings(test->m_webView, newSettings.get());
+
+        WebKitJavascriptResult* _javascript_Result = test->runAsyncJavaScriptFunctionInWorldAndWaitUntilFinished("return new Promise((resolve) => { resolve(42); });", nullptr, "", &error.outPtr());
+        g_assert_nonnull(_javascript_Result);
+        test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(_javascript_Result)));
+        g_assert_no_error(error.get());
+
+        g_object_set(G_OBJECT(newSettings.get()), "enable-_javascript_-markup", TRUE, NULL);
+        webkit_web_view_set_settings(test->m_webView, newSettings.get());
+    }
+}
+
 static void testWebViewRunJavaScript(WebViewTest* test, gconstpointer)
 {
     static const char* html = "<html><body><a id='WebKitLink' href='' title='WebKitGTK Title'>WebKitGTK Website</a></body></html>";
@@ -420,6 +539,47 @@
     _javascript_Result = test->runJavaScriptInWorldAndWaitUntilFinished("a", "InvalidScriptWorld", &error.outPtr());
     g_assert_null(_javascript_Result);
     g_assert_error(error.get(), WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED);
+
+    {
+        // Disable JS support and expect an error when attempting to evaluate JS code.
+        WebKitSettings* defaultSettings = webkit_web_view_get_settings(test->m_webView);
+        test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(defaultSettings));
+        g_assert_nonnull(defaultSettings);
+        g_assert_true(webkit_settings_get_enable_javascript(defaultSettings));
+
+        GRefPtr<WebKitSettings> newSettings = adoptGRef(webkit_settings_new());
+        test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(newSettings.get()));
+        g_object_set(G_OBJECT(newSettings.get()), "enable-_javascript_", FALSE, NULL);
+        webkit_web_view_set_settings(test->m_webView, newSettings.get());
+
+        WebKitJavascriptResult* _javascript_Result = test->runJavaScriptInWorldAndWaitUntilFinished("console.log(\"Hi\");", "", &error.outPtr());
+        g_assert_null(_javascript_Result);
+        g_assert_error(error.get(), WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED);
+
+        g_object_set(G_OBJECT(newSettings.get()), "enable-_javascript_", TRUE, NULL);
+        webkit_web_view_set_settings(test->m_webView, newSettings.get());
+    }
+
+    {
+        // Disable JS markup support and expect no error when attempting to evaluate JS code.
+        WebKitSettings* defaultSettings = webkit_web_view_get_settings(test->m_webView);
+        test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(defaultSettings));
+        g_assert_nonnull(defaultSettings);
+        g_assert_true(webkit_settings_get_enable_javascript_markup(defaultSettings));
+
+        GRefPtr<WebKitSettings> newSettings = adoptGRef(webkit_settings_new());
+        test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(newSettings.get()));
+        g_object_set(G_OBJECT(newSettings.get()), "enable-_javascript_-markup", FALSE, NULL);
+        webkit_web_view_set_settings(test->m_webView, newSettings.get());
+
+        WebKitJavascriptResult* _javascript_Result = test->runJavaScriptInWorldAndWaitUntilFinished("console.log(\"Hi\");", "", &error.outPtr());
+        g_assert_nonnull(_javascript_Result);
+        test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(_javascript_Result)));
+        g_assert_no_error(error.get());
+
+        g_object_set(G_OBJECT(newSettings.get()), "enable-_javascript_-markup", TRUE, NULL);
+        webkit_web_view_set_settings(test->m_webView, newSettings.get());
+    }
 }
 
 class FullScreenClientTest: public WebViewTest {
@@ -1731,6 +1891,7 @@
     WebViewTest::add("WebKitWebView", "settings", testWebViewSettings);
     WebViewTest::add("WebKitWebView", "zoom-level", testWebViewZoomLevel);
     WebViewTest::add("WebKitWebView", "run-_javascript_", testWebViewRunJavaScript);
+    WebViewTest::add("WebKitWebView", "run-async-js-functions", testWebViewRunAsyncFunctions);
 #if ENABLE(FULLSCREEN_API)
     FullScreenClientTest::add("WebKitWebView", "fullscreen", testWebViewFullScreen);
 #endif

Modified: trunk/Tools/TestWebKitAPI/glib/WebKitGLib/WebViewTest.cpp (295525 => 295526)


--- trunk/Tools/TestWebKitAPI/glib/WebKitGLib/WebViewTest.cpp	2022-06-14 17:07:46 UTC (rev 295525)
+++ trunk/Tools/TestWebKitAPI/glib/WebKitGLib/WebViewTest.cpp	2022-06-14 17:09:19 UTC (rev 295526)
@@ -364,6 +364,18 @@
     return m_javascriptResult;
 }
 
+WebKitJavascriptResult* WebViewTest::runAsyncJavaScriptFunctionInWorldAndWaitUntilFinished(const char* body, GVariant* arguments, const char* world, GError** error)
+{
+    if (m_javascriptResult)
+        webkit_javascript_result_unref(m_javascriptResult);
+    m_javascriptResult = 0;
+    m_javascriptError = error;
+    webkit_web_view_run_async_javascript_function_in_world(m_webView, body, arguments, world, nullptr, reinterpret_cast<GAsyncReadyCallback>(runJavaScriptInWorldReadyCallback), this);
+    g_main_loop_run(m_mainLoop);
+
+    return m_javascriptResult;
+}
+
 WebKitJavascriptResult* WebViewTest::runJavaScriptWithoutForcedUserGesturesAndWaitUntilFinished(const char* _javascript_, GError** error)
 {
     if (m_javascriptResult)

Modified: trunk/Tools/TestWebKitAPI/glib/WebKitGLib/WebViewTest.h (295525 => 295526)


--- trunk/Tools/TestWebKitAPI/glib/WebKitGLib/WebViewTest.h	2022-06-14 17:07:46 UTC (rev 295525)
+++ trunk/Tools/TestWebKitAPI/glib/WebKitGLib/WebViewTest.h	2022-06-14 17:09:19 UTC (rev 295526)
@@ -76,6 +76,7 @@
     WebKitJavascriptResult* runJavaScriptAndWaitUntilFinished(const char* _javascript_, GError**, WebKitWebView* = nullptr);
     WebKitJavascriptResult* runJavaScriptFromGResourceAndWaitUntilFinished(const char* resource, GError**);
     WebKitJavascriptResult* runJavaScriptInWorldAndWaitUntilFinished(const char* _javascript_, const char* world, GError**);
+    WebKitJavascriptResult* runAsyncJavaScriptFunctionInWorldAndWaitUntilFinished(const char* body, GVariant* arguments, const char* world, GError**);
     WebKitJavascriptResult* runJavaScriptWithoutForcedUserGesturesAndWaitUntilFinished(const char* _javascript_, GError**);
 
     // _javascript_ result helpers.
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to