[GTK][WPE] Allow to run script dialogs asynchronously in the UI process
authorcarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 13 Sep 2018 08:24:17 +0000 (08:24 +0000)
committercarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 13 Sep 2018 08:24:17 +0000 (08:24 +0000)
https://bugs.webkit.org/show_bug.cgi?id=189544

Reviewed by Michael Catanzaro.

Source/WebKit:

Script dialogs are sync in the WebProcess, but we don't need to block the UI process while they are running. Our
current API doesn't allow it, because it always expects the dialog to be closed in the signal handler. API
changes are backwards compatible.

* UIProcess/API/glib/WebKitScriptDialog.cpp:
(webkitScriptDialogCreate): Added to heap allocate a WebKitScriptDialog.
(webkitScriptDialogIsRunning): Common implementation here, a script dialog is considered to be running if it has
a competion handler pending.
(webkit_script_dialog_ref): WebKitScriptDialog is now refcounted.
(webkit_script_dialog_unref): Ditto.
(webkit_script_dialog_close): New method to notify that we are done with the dialog.
* UIProcess/API/glib/WebKitScriptDialogPrivate.h:
(_WebKitScriptDialog::_WebKitScriptDialog): Use a single constructor and keep the completion handler.
* UIProcess/API/glib/WebKitUIClient.cpp: Do not call the completion handler, pass it to the web view.
* UIProcess/API/glib/WebKitWebView.cpp:
(webkitWebViewDispose): Close the current script dialog if there's any.
(webkit_web_view_class_init): Document how to handle dialogs asynchronously.
(webkitWebViewRunJavaScriptAlert): Do not stack allocate the WebKitScriptDialog, create it with
webkitScriptDialogCreate() passing the completion handler.
(webkitWebViewRunJavaScriptConfirm): Ditto.
(webkitWebViewRunJavaScriptPrompt): Ditto.
(webkitWebViewRunJavaScriptBeforeUnloadConfirm): Ditto.
* UIProcess/API/glib/WebKitWebViewPrivate.h:
* UIProcess/API/gtk/WebKitScriptDialog.h:
* UIProcess/API/gtk/WebKitScriptDialogGtk.cpp:
(scriptDialogResponseCallback): Hnadle the response callback.
(webkitScriptDialogRun): Do not use gtk_dialog_run(), connect to response signal and show the dialogs instead.
* UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt:
* UIProcess/API/gtk/docs/webkit2gtk-docs.sgml:
* UIProcess/API/wpe/WebKitScriptDialogWPE.cpp:

Tools:

Add test cases for the new API.

* TestWebKitAPI/Tests/WebKitGLib/TestUIClient.cpp:
(testWebViewJavaScriptDialogs):

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

14 files changed:
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/API/glib/WebKitScriptDialog.cpp
Source/WebKit/UIProcess/API/glib/WebKitScriptDialogPrivate.h
Source/WebKit/UIProcess/API/glib/WebKitUIClient.cpp
Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp
Source/WebKit/UIProcess/API/glib/WebKitWebViewPrivate.h
Source/WebKit/UIProcess/API/gtk/WebKitScriptDialog.h
Source/WebKit/UIProcess/API/gtk/WebKitScriptDialogGtk.cpp
Source/WebKit/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt
Source/WebKit/UIProcess/API/gtk/docs/webkit2gtk-docs.sgml
Source/WebKit/UIProcess/API/wpe/WebKitScriptDialog.h
Source/WebKit/UIProcess/API/wpe/WebKitScriptDialogWPE.cpp
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WebKitGLib/TestUIClient.cpp

index 224beac..e0b90ce 100644 (file)
@@ -1,3 +1,41 @@
+2018-09-13  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [GTK][WPE] Allow to run script dialogs asynchronously in the UI process
+        https://bugs.webkit.org/show_bug.cgi?id=189544
+
+        Reviewed by Michael Catanzaro.
+
+        Script dialogs are sync in the WebProcess, but we don't need to block the UI process while they are running. Our
+        current API doesn't allow it, because it always expects the dialog to be closed in the signal handler. API
+        changes are backwards compatible.
+
+        * UIProcess/API/glib/WebKitScriptDialog.cpp:
+        (webkitScriptDialogCreate): Added to heap allocate a WebKitScriptDialog.
+        (webkitScriptDialogIsRunning): Common implementation here, a script dialog is considered to be running if it has
+        a competion handler pending.
+        (webkit_script_dialog_ref): WebKitScriptDialog is now refcounted.
+        (webkit_script_dialog_unref): Ditto.
+        (webkit_script_dialog_close): New method to notify that we are done with the dialog.
+        * UIProcess/API/glib/WebKitScriptDialogPrivate.h:
+        (_WebKitScriptDialog::_WebKitScriptDialog): Use a single constructor and keep the completion handler.
+        * UIProcess/API/glib/WebKitUIClient.cpp: Do not call the completion handler, pass it to the web view.
+        * UIProcess/API/glib/WebKitWebView.cpp:
+        (webkitWebViewDispose): Close the current script dialog if there's any.
+        (webkit_web_view_class_init): Document how to handle dialogs asynchronously.
+        (webkitWebViewRunJavaScriptAlert): Do not stack allocate the WebKitScriptDialog, create it with
+        webkitScriptDialogCreate() passing the completion handler.
+        (webkitWebViewRunJavaScriptConfirm): Ditto.
+        (webkitWebViewRunJavaScriptPrompt): Ditto.
+        (webkitWebViewRunJavaScriptBeforeUnloadConfirm): Ditto.
+        * UIProcess/API/glib/WebKitWebViewPrivate.h:
+        * UIProcess/API/gtk/WebKitScriptDialog.h:
+        * UIProcess/API/gtk/WebKitScriptDialogGtk.cpp:
+        (scriptDialogResponseCallback): Hnadle the response callback.
+        (webkitScriptDialogRun): Do not use gtk_dialog_run(), connect to response signal and show the dialogs instead.
+        * UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt:
+        * UIProcess/API/gtk/docs/webkit2gtk-docs.sgml:
+        * UIProcess/API/wpe/WebKitScriptDialogWPE.cpp:
+
 2018-09-12  Dan Bernstein  <mitz@apple.com>
 
         Update availability annotations to match the macOS and iOS SDKs in the Xcode 10 GM seed
index 5f97724..856683d 100644 (file)
 
 #include "WebKitScriptDialogPrivate.h"
 
-static WebKitScriptDialog* webkitScriptDialogCopy(WebKitScriptDialog* dialog)
+G_DEFINE_BOXED_TYPE(WebKitScriptDialog, webkit_script_dialog, webkit_script_dialog_ref, webkit_script_dialog_unref)
+
+WebKitScriptDialog* webkitScriptDialogCreate(unsigned type, const CString& message, const CString& defaultText, Function<void(bool, const String&)>&& completionHandler)
 {
-    WebKitScriptDialog* copy = static_cast<WebKitScriptDialog*>(fastZeroedMalloc(sizeof(WebKitScriptDialog)));
-    new (copy) WebKitScriptDialog(dialog);
-    return copy;
+    auto* dialog = static_cast<WebKitScriptDialog*>(fastMalloc(sizeof(WebKitScriptDialog)));
+    new (dialog) WebKitScriptDialog(type, message, defaultText, WTFMove(completionHandler));
+    return dialog;
 }
 
-static void webkitScriptDialogFree(WebKitScriptDialog* dialog)
+bool webkitScriptDialogIsRunning(WebKitScriptDialog* scriptDialog)
 {
-    dialog->~WebKitScriptDialog();
-    fastFree(dialog);
+    return !!scriptDialog->completionHandler;
 }
 
-G_DEFINE_BOXED_TYPE(WebKitScriptDialog, webkit_script_dialog, webkitScriptDialogCopy, webkitScriptDialogFree)
+/**
+ * webkit_script_dialog_ref:
+ * @dialog: a #WebKitScriptDialog
+ *
+ * Atomically increments the reference count of @dialog by one. This
+ * function is MT-safe and may be called from any thread.
+ *
+ * Returns: The passed in #WebKitScriptDialog
+ *
+ * Since: 2.24
+ */
+WebKitScriptDialog* webkit_script_dialog_ref(WebKitScriptDialog* dialog)
+{
+    g_atomic_int_inc(&dialog->referenceCount);
+    return dialog;
+}
+
+/**
+ * webkit_script_dialog_unref:
+ * @dialog: a #WebKitScriptDialog
+ *
+ * Atomically decrements the reference count of @dialog by one. If the
+ * reference count drops to 0, all memory allocated by the #WebKitScriptdialog is
+ * released. This function is MT-safe and may be called from any
+ * thread.
+ *
+ * Since: 2.24
+ */
+void webkit_script_dialog_unref(WebKitScriptDialog* dialog)
+{
+    if (g_atomic_int_dec_and_test(&dialog->referenceCount)) {
+        webkit_script_dialog_close(dialog);
+        dialog->~WebKitScriptDialog();
+        fastFree(dialog);
+    }
+}
 
 /**
  * webkit_script_dialog_get_dialog_type:
@@ -124,3 +160,37 @@ void webkit_script_dialog_prompt_set_text(WebKitScriptDialog* dialog, const char
 
     dialog->text = text;
 }
+
+/**
+ * webkit_script_dialog_close:
+ * @dialog: a #WebKitScriptDialog
+ *
+ * Close @dialog. When handling a #WebKitScriptDialog asynchronously (webkit_script_dialog_ref()
+ * was called in #WebKitWebView::script-dialog callback), this function needs to be called to notify
+ * that we are done with the script dialog. The dialog will be closed on destruction if this function
+ * hasn't been called before.
+ *
+ * Since: 2.24
+ */
+void webkit_script_dialog_close(WebKitScriptDialog* dialog)
+{
+    g_return_if_fail(dialog);
+
+    if (!dialog->completionHandler)
+        return;
+
+    auto completionHandler = std::exchange(dialog->completionHandler, nullptr);
+
+    switch (dialog->type) {
+    case WEBKIT_SCRIPT_DIALOG_ALERT:
+        completionHandler(false, emptyString());
+        break;
+    case WEBKIT_SCRIPT_DIALOG_CONFIRM:
+    case WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM:
+        completionHandler(dialog->confirmed, emptyString());
+        break;
+    case WEBKIT_SCRIPT_DIALOG_PROMPT:
+        completionHandler(false, String::fromUTF8(dialog->text.data()));
+        break;
+    }
+}
index 00da6ed..58d77cb 100644 (file)
 #include <wtf/text/WTFString.h>
 
 struct _WebKitScriptDialog {
-    _WebKitScriptDialog(unsigned type, const CString& message)
-        : type(type)
-        , message(message)
-        , confirmed(false)
-    {
-    }
-
-    _WebKitScriptDialog(unsigned type, const CString& message, const CString& defaultText)
+    _WebKitScriptDialog(unsigned type, const CString& message, const CString& defaultText, Function<void(bool, const String&)>&& completionHandler)
         : type(type)
         , message(message)
         , defaultText(defaultText)
-        , confirmed(false)
-    {
-        ASSERT(type == WEBKIT_SCRIPT_DIALOG_PROMPT);
-    }
-
-    _WebKitScriptDialog(WebKitScriptDialog* dialog)
-        : type(dialog->type)
-        , message(dialog->message)
-        , defaultText(dialog->defaultText)
-        , confirmed(dialog->confirmed)
-        , text(dialog->text)
+        , completionHandler(WTFMove(completionHandler))
     {
     }
 
@@ -54,14 +37,19 @@ struct _WebKitScriptDialog {
     CString message;
     CString defaultText;
 
-    bool confirmed;
+    bool confirmed { false };
     CString text;
 
+    Function<void(bool, const String&)> completionHandler;
+
 #if PLATFORM(GTK)
     GtkWidget* nativeDialog { nullptr };
 #endif
+
+    int referenceCount { 1 };
 };
 
+WebKitScriptDialog* webkitScriptDialogCreate(unsigned type, const CString& message, const CString& defaultText, Function<void(bool, const String&)>&& completionHandler);
 void webkitScriptDialogRun(WebKitScriptDialog*, WebKitWebView*);
 bool webkitScriptDialogIsRunning(WebKitScriptDialog*);
 void webkitScriptDialogAccept(WebKitScriptDialog*);
index c6c21d0..0275431 100644 (file)
@@ -67,31 +67,24 @@ private:
 
     void runJavaScriptAlert(WebPageProxy*, const String& message, WebFrameProxy*, const WebCore::SecurityOriginData&, Function<void()>&& completionHandler) final
     {
-        webkitWebViewRunJavaScriptAlert(m_webView, message.utf8());
-        completionHandler();
+        webkitWebViewRunJavaScriptAlert(m_webView, message.utf8(), WTFMove(completionHandler));
     }
 
     void runJavaScriptConfirm(WebPageProxy*, const String& message, WebFrameProxy*, const WebCore::SecurityOriginData&, Function<void(bool)>&& completionHandler) final
     {
-        completionHandler(webkitWebViewRunJavaScriptConfirm(m_webView, message.utf8()));
+        webkitWebViewRunJavaScriptConfirm(m_webView, message.utf8(), WTFMove(completionHandler));
     }
 
     void runJavaScriptPrompt(WebPageProxy*, const String& message, const String& defaultValue, WebFrameProxy*, const WebCore::SecurityOriginData&, Function<void(const String&)>&& completionHandler) final
     {
-        CString result = webkitWebViewRunJavaScriptPrompt(m_webView, message.utf8(), defaultValue.utf8());
-        if (result.isNull()) {
-            completionHandler(String());
-            return;
-        }
-
-        completionHandler(String::fromUTF8(result.data()));
+        webkitWebViewRunJavaScriptPrompt(m_webView, message.utf8(), defaultValue.utf8(), WTFMove(completionHandler));
     }
 
     bool canRunBeforeUnloadConfirmPanel() const final { return true; }
 
     void runBeforeUnloadConfirmPanel(WebPageProxy*, const String& message, WebFrameProxy*, const WebCore::SecurityOriginData&, Function<void(bool)>&& completionHandler) final
     {
-        completionHandler(webkitWebViewRunJavaScriptBeforeUnloadConfirm(m_webView, message.utf8()));
+        webkitWebViewRunJavaScriptBeforeUnloadConfirm(m_webView, message.utf8(), WTFMove(completionHandler));
     }
 
     void mouseDidMoveOverElement(WebPageProxy&, const WebHitTestResultData& data, WebEvent::Modifiers modifiers, API::Object*) final
index 666b101..0fa4a18 100644 (file)
@@ -71,7 +71,6 @@
 #include <WebCore/RefPtrCairo.h>
 #include <WebCore/URL.h>
 #include <glib/gi18n-lib.h>
-#include <wtf/SetForScope.h>
 #include <wtf/glib/GRefPtr.h>
 #include <wtf/glib/WTFGType.h>
 #include <wtf/text/CString.h>
@@ -807,6 +806,11 @@ static void webkitWebViewDispose(GObject* object)
         webView->priv->websiteDataManager = nullptr;
     }
 
+    if (webView->priv->currentScriptDialog) {
+        webkit_script_dialog_close(webView->priv->currentScriptDialog);
+        ASSERT(!webView->priv->currentScriptDialog);
+    }
+
 #if PLATFORM(WPE)
     webView->priv->view->close();
 #endif
@@ -1355,6 +1359,12 @@ static void webkit_web_view_class_init(WebKitWebViewClass* webViewClass)
      * </para></listitem>
      * </itemizedlist>
      *
+     * It is possible to handle the script dialog request asynchronously, by simply
+     * caling webkit_script_dialog_ref() on the @dialog argument and calling
+     * webkit_script_dialog_close() when done.
+     * If the last reference is removed on a #WebKitScriptDialog and the dialog has not been
+     * closed, webkit_script_dialog_close() will be called.
+     *
      * Returns: %TRUE to stop other handlers from being invoked for the event.
      *    %FALSE to propagate the event further.
      */
@@ -1366,7 +1376,7 @@ static void webkit_web_view_class_init(WebKitWebViewClass* webViewClass)
         g_signal_accumulator_true_handled, nullptr,
         g_cclosure_marshal_generic,
         G_TYPE_BOOLEAN, 1,
-        WEBKIT_TYPE_SCRIPT_DIALOG | G_SIGNAL_TYPE_STATIC_SCOPE);
+        WEBKIT_TYPE_SCRIPT_DIALOG);
 
     /**
      * WebKitWebView::decide-policy:
@@ -2101,39 +2111,52 @@ void webkitWebViewClosePage(WebKitWebView* webView)
     g_signal_emit(webView, signals[CLOSE], 0, NULL);
 }
 
-void webkitWebViewRunJavaScriptAlert(WebKitWebView* webView, const CString& message)
+void webkitWebViewRunJavaScriptAlert(WebKitWebView* webView, const CString& message, Function<void()>&& completionHandler)
 {
-    WebKitScriptDialog dialog(WEBKIT_SCRIPT_DIALOG_ALERT, message);
-    SetForScope<WebKitScriptDialog*> change(webView->priv->currentScriptDialog, &dialog);
+    ASSERT(!webView->priv->currentScriptDialog);
+    webView->priv->currentScriptDialog = webkitScriptDialogCreate(WEBKIT_SCRIPT_DIALOG_ALERT, message, { }, [webView, completionHandler = WTFMove(completionHandler)](bool, const String&) {
+        completionHandler();
+        webView->priv->currentScriptDialog = nullptr;
+    });
     gboolean returnValue;
-    g_signal_emit(webView, signals[SCRIPT_DIALOG], 0, &dialog, &returnValue);
+    g_signal_emit(webView, signals[SCRIPT_DIALOG], 0, webView->priv->currentScriptDialog, &returnValue);
+    webkit_script_dialog_unref(webView->priv->currentScriptDialog);
 }
 
-bool webkitWebViewRunJavaScriptConfirm(WebKitWebView* webView, const CString& message)
+void webkitWebViewRunJavaScriptConfirm(WebKitWebView* webView, const CString& message, Function<void(bool)>&& completionHandler)
 {
-    WebKitScriptDialog dialog(WEBKIT_SCRIPT_DIALOG_CONFIRM, message);
-    SetForScope<WebKitScriptDialog*> change(webView->priv->currentScriptDialog, &dialog);
+    ASSERT(!webView->priv->currentScriptDialog);
+    webView->priv->currentScriptDialog = webkitScriptDialogCreate(WEBKIT_SCRIPT_DIALOG_CONFIRM, message, { }, [webView, completionHandler = WTFMove(completionHandler)](bool result, const String&) {
+        completionHandler(result);
+        webView->priv->currentScriptDialog = nullptr;
+    });
     gboolean returnValue;
-    g_signal_emit(webView, signals[SCRIPT_DIALOG], 0, &dialog, &returnValue);
-    return dialog.confirmed;
+    g_signal_emit(webView, signals[SCRIPT_DIALOG], 0, webView->priv->currentScriptDialog, &returnValue);
+    webkit_script_dialog_unref(webView->priv->currentScriptDialog);
 }
 
-CString webkitWebViewRunJavaScriptPrompt(WebKitWebView* webView, const CString& message, const CString& defaultText)
+void webkitWebViewRunJavaScriptPrompt(WebKitWebView* webView, const CString& message, const CString& defaultText, Function<void(const String&)>&& completionHandler)
 {
-    WebKitScriptDialog dialog(WEBKIT_SCRIPT_DIALOG_PROMPT, message, defaultText);
-    SetForScope<WebKitScriptDialog*> change(webView->priv->currentScriptDialog, &dialog);
+    ASSERT(!webView->priv->currentScriptDialog);
+    webView->priv->currentScriptDialog = webkitScriptDialogCreate(WEBKIT_SCRIPT_DIALOG_PROMPT, message, defaultText, [webView, completionHandler = WTFMove(completionHandler)](bool, const String& result) {
+        completionHandler(result);
+        webView->priv->currentScriptDialog = nullptr;
+    });
     gboolean returnValue;
-    g_signal_emit(webView, signals[SCRIPT_DIALOG], 0, &dialog, &returnValue);
-    return dialog.text;
+    g_signal_emit(webView, signals[SCRIPT_DIALOG], 0, webView->priv->currentScriptDialog, &returnValue);
+    webkit_script_dialog_unref(webView->priv->currentScriptDialog);
 }
 
-bool webkitWebViewRunJavaScriptBeforeUnloadConfirm(WebKitWebView* webView, const CString& message)
+void webkitWebViewRunJavaScriptBeforeUnloadConfirm(WebKitWebView* webView, const CString& message, Function<void(bool)>&& completionHandler)
 {
-    WebKitScriptDialog dialog(WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM, message);
-    SetForScope<WebKitScriptDialog*> change(webView->priv->currentScriptDialog, &dialog);
+    ASSERT(!webView->priv->currentScriptDialog);
+    webView->priv->currentScriptDialog = webkitScriptDialogCreate(WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM, message, { }, [webView, completionHandler = WTFMove(completionHandler)](bool result, const String&) {
+        completionHandler(result);
+        webView->priv->currentScriptDialog = nullptr;
+    });
     gboolean returnValue;
-    g_signal_emit(webView, signals[SCRIPT_DIALOG], 0, &dialog, &returnValue);
-    return dialog.confirmed;
+    g_signal_emit(webView, signals[SCRIPT_DIALOG], 0, webView->priv->currentScriptDialog, &returnValue);
+    webkit_script_dialog_unref(webView->priv->currentScriptDialog);
 }
 
 bool webkitWebViewIsShowingScriptDialog(WebKitWebView* webView)
index 51ee3b0..1e599fc 100644 (file)
@@ -52,10 +52,10 @@ WebKit::WebPageProxy* webkitWebViewCreateNewPage(WebKitWebView*, const WebCore::
 void webkitWebViewReadyToShowPage(WebKitWebView*);
 void webkitWebViewRunAsModal(WebKitWebView*);
 void webkitWebViewClosePage(WebKitWebView*);
-void webkitWebViewRunJavaScriptAlert(WebKitWebView*, const CString& message);
-bool webkitWebViewRunJavaScriptConfirm(WebKitWebView*, const CString& message);
-CString webkitWebViewRunJavaScriptPrompt(WebKitWebView*, const CString& message, const CString& defaultText);
-bool webkitWebViewRunJavaScriptBeforeUnloadConfirm(WebKitWebView*, const CString& message);
+void webkitWebViewRunJavaScriptAlert(WebKitWebView*, const CString& message, Function<void()>&& completionHandler);
+void webkitWebViewRunJavaScriptConfirm(WebKitWebView*, const CString& message, Function<void(bool)>&& completionHandler);
+void webkitWebViewRunJavaScriptPrompt(WebKitWebView*, const CString& message, const CString& defaultText, Function<void(const String&)>&& completionHandler);
+void webkitWebViewRunJavaScriptBeforeUnloadConfirm(WebKitWebView*, const CString& message, Function<void(bool)>&& completionHandler);
 bool webkitWebViewIsShowingScriptDialog(WebKitWebView*);
 bool webkitWebViewIsScriptDialogRunning(WebKitWebView*, WebKitScriptDialog*);
 String webkitWebViewGetCurrentScriptDialogMessage(WebKitWebView*);
index 71095be..6d03fba 100644 (file)
@@ -56,6 +56,12 @@ typedef enum {
 WEBKIT_API GType
 webkit_script_dialog_get_type                (void);
 
+WEBKIT_API WebKitScriptDialog *
+webkit_script_dialog_ref                     (WebKitScriptDialog *dialog);
+
+WEBKIT_API void
+webkit_script_dialog_unref                   (WebKitScriptDialog *dialog);
+
 WEBKIT_API WebKitScriptDialogType
 webkit_script_dialog_get_dialog_type         (WebKitScriptDialog *dialog);
 
@@ -73,6 +79,9 @@ WEBKIT_API void
 webkit_script_dialog_prompt_set_text         (WebKitScriptDialog *dialog,
                                               const gchar        *text);
 
+WEBKIT_API void
+webkit_script_dialog_close                   (WebKitScriptDialog *dialog);
+
 G_END_DECLS
 
 #endif
index da40482..48b1314 100644 (file)
 #include <gtk/gtk.h>
 #include <wtf/glib/GUniquePtr.h>
 
+static void scriptDialogResponseCallback(GtkWidget* dialog, int responseID, WebKitScriptDialog* scriptDialog)
+{
+    switch (scriptDialog->type) {
+    case WEBKIT_SCRIPT_DIALOG_ALERT:
+        break;
+    case WEBKIT_SCRIPT_DIALOG_CONFIRM:
+    case WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM:
+        scriptDialog->confirmed = responseID == GTK_RESPONSE_OK;
+        break;
+    case WEBKIT_SCRIPT_DIALOG_PROMPT:
+        if (responseID == GTK_RESPONSE_OK) {
+            if (auto* entry = g_object_get_data(G_OBJECT(dialog), "wk-script-dialog-entry"))
+                scriptDialog->text = gtk_entry_get_text(GTK_ENTRY(entry));
+        }
+        break;
+    }
+
+    scriptDialog->nativeDialog = nullptr;
+    webkit_script_dialog_close(scriptDialog);
+    webkit_script_dialog_unref(scriptDialog);
+    gtk_widget_destroy(dialog);
+}
+
 static GtkWidget* webkitWebViewCreateJavaScriptDialog(WebKitWebView* webView, GtkMessageType type, GtkButtonsType buttons, int defaultResponse, const char* primaryText, const char* secondaryText = nullptr)
 {
     GtkWidget* parent = gtk_widget_get_toplevel(GTK_WIDGET(webView));
@@ -49,43 +72,32 @@ void webkitScriptDialogRun(WebKitScriptDialog* scriptDialog, WebKitWebView* webV
     switch (scriptDialog->type) {
     case WEBKIT_SCRIPT_DIALOG_ALERT:
         dialog = webkitWebViewCreateJavaScriptDialog(webView, GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE, GTK_RESPONSE_CLOSE, scriptDialog->message.data());
-        scriptDialog->nativeDialog = dialog;
-        gtk_dialog_run(GTK_DIALOG(dialog));
         break;
     case WEBKIT_SCRIPT_DIALOG_CONFIRM:
         dialog = webkitWebViewCreateJavaScriptDialog(webView, GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, GTK_RESPONSE_OK, scriptDialog->message.data());
-        scriptDialog->nativeDialog = dialog;
-        scriptDialog->confirmed = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK;
         break;
     case WEBKIT_SCRIPT_DIALOG_PROMPT: {
         dialog = webkitWebViewCreateJavaScriptDialog(webView, GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, GTK_RESPONSE_OK, scriptDialog->message.data());
-        scriptDialog->nativeDialog = dialog;
         GtkWidget* entry = gtk_entry_new();
+        g_object_set_data(G_OBJECT(dialog), "wk-script-dialog-entry", entry);
         gtk_entry_set_text(GTK_ENTRY(entry), scriptDialog->defaultText.data());
         gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), entry);
         gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
         gtk_widget_show(entry);
-        if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
-            scriptDialog->text = gtk_entry_get_text(GTK_ENTRY(entry));
         break;
     }
     case WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM:
         dialog = webkitWebViewCreateJavaScriptDialog(webView, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, GTK_RESPONSE_OK,
             _("Are you sure you want to leave this page?"), scriptDialog->message.data());
-        scriptDialog->nativeDialog = dialog;
         gtk_dialog_add_buttons(GTK_DIALOG(dialog), _("Stay on Page"), GTK_RESPONSE_CLOSE, _("Leave Page"), GTK_RESPONSE_OK, nullptr);
         gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
-        scriptDialog->confirmed = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK;
         break;
     }
 
-    gtk_widget_destroy(dialog);
-    scriptDialog->nativeDialog = nullptr;
-}
-
-bool webkitScriptDialogIsRunning(WebKitScriptDialog* scriptDialog)
-{
-    return !!scriptDialog->nativeDialog;
+    ASSERT(dialog);
+    scriptDialog->nativeDialog = dialog;
+    g_signal_connect(dialog, "response", G_CALLBACK(scriptDialogResponseCallback), webkit_script_dialog_ref(scriptDialog));
+    gtk_widget_show(dialog);
 }
 
 void webkitScriptDialogAccept(WebKitScriptDialog* scriptDialog)
index 55605db..874c43a 100644 (file)
@@ -231,6 +231,7 @@ webkit_web_view_is_editable
 webkit_web_view_get_editor_state
 webkit_web_view_get_session_state
 webkit_web_view_restore_session_state
+webkit_web_view_get_main_resource
 
 <SUBSECTION WebKitJavascriptResult>
 WebKitJavascriptResult
@@ -243,12 +244,14 @@ webkit_javascript_result_get_js_value
 <SUBSECTION WebKitScriptDialog>
 WebKitScriptDialog
 WebKitScriptDialogType
+webkit_script_dialog_ref
+webkit_script_dialog_unref
 webkit_script_dialog_get_dialog_type
 webkit_script_dialog_get_message
 webkit_script_dialog_confirm_set_confirmed
 webkit_script_dialog_prompt_get_default_text
 webkit_script_dialog_prompt_set_text
-webkit_web_view_get_main_resource
+webkit_script_dialog_close
 
 <SUBSECTION WebKitWebViewSessionState>
 WebKitWebViewSessionState
index 30d97d6..bbf967e 100644 (file)
     <xi:include href="xml/api-index-2.22.xml"><xi:fallback /></xi:include>
   </index>
 
+  <index id="api-index-2-24" role="2.24">
+    <title>Index of new symbols in 2.24</title>
+    <xi:include href="xml/api-index-2.24.xml"><xi:fallback /></xi:include>
+  </index>
+
   <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
 </book>
index f50dabe..c960916 100644 (file)
@@ -56,6 +56,12 @@ typedef enum {
 WEBKIT_API GType
 webkit_script_dialog_get_type                (void);
 
+WEBKIT_API WebKitScriptDialog *
+webkit_script_dialog_ref                     (WebKitScriptDialog *dialog);
+
+WEBKIT_API void
+webkit_script_dialog_unref                   (WebKitScriptDialog *dialog);
+
 WEBKIT_API WebKitScriptDialogType
 webkit_script_dialog_get_dialog_type         (WebKitScriptDialog *dialog);
 
@@ -73,6 +79,9 @@ WEBKIT_API void
 webkit_script_dialog_prompt_set_text         (WebKitScriptDialog *dialog,
                                               const gchar        *text);
 
+WEBKIT_API void
+webkit_script_dialog_close                   (WebKitScriptDialog *dialog);
+
 G_END_DECLS
 
 #endif
index 698a23f..424db91 100644 (file)
@@ -26,11 +26,6 @@ void webkitScriptDialogRun(WebKitScriptDialog*, WebKitWebView*)
 {
 }
 
-bool webkitScriptDialogIsRunning(WebKitScriptDialog*)
-{
-    return false;
-}
-
 void webkitScriptDialogAccept(WebKitScriptDialog*)
 {
 }
index deb8454..97a1c41 100644 (file)
@@ -1,3 +1,15 @@
+2018-09-13  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [GTK][WPE] Allow to run script dialogs asynchronously in the UI process
+        https://bugs.webkit.org/show_bug.cgi?id=189544
+
+        Reviewed by Michael Catanzaro.
+
+        Add test cases for the new API.
+
+        * TestWebKitAPI/Tests/WebKitGLib/TestUIClient.cpp:
+        (testWebViewJavaScriptDialogs):
+
 2018-09-12  Fujii Hironori  <Hironori.Fujii@sony.com>
 
         [Win][Clang][ImageDiff] Fix compilation error and warning of PlatformImageCairo.cpp
index 6939102..6611377 100644 (file)
@@ -20,6 +20,7 @@
 #include "config.h"
 #include "WebViewTest.h"
 #include <wtf/HashSet.h>
+#include <wtf/RunLoop.h>
 #include <wtf/glib/GRefPtr.h>
 #include <wtf/text/StringHash.h>
 
@@ -158,7 +159,15 @@ public:
             break;
         }
 
-        g_main_loop_quit(m_mainLoop);
+        if (m_delayedScriptDialogs) {
+            webkit_script_dialog_ref(dialog);
+            RunLoop::main().dispatch([this, dialog] {
+                webkit_script_dialog_close(dialog);
+                webkit_script_dialog_unref(dialog);
+                g_main_loop_quit(m_mainLoop);
+            });
+        } else
+            g_main_loop_quit(m_mainLoop);
     }
 
     void scriptConfirm(WebKitScriptDialog* dialog)
@@ -365,6 +374,7 @@ public:
     Vector<WebViewEvents> m_webViewEvents;
     WebKitScriptDialogType m_scriptDialogType;
     bool m_scriptDialogConfirmed;
+    bool m_delayedScriptDialogs { false };
     bool m_allowPermissionRequests;
     gboolean m_verifyMediaTypes;
     gboolean m_expectedAudioMedia;
@@ -558,29 +568,35 @@ static void testWebViewJavaScriptDialogs(UIClientTest* test, gconstpointer)
         "<html><body onbeforeunload=\"return beforeUnloadHandler();\"><input id=\"testInput\" type=\"text\"></input><script>function beforeUnloadHandler() { return \"%s\"; }</script></body></html>";
 #endif
 
-    test->m_scriptDialogType = WEBKIT_SCRIPT_DIALOG_ALERT;
-    GUniquePtr<char> alertDialogMessage(g_strdup_printf(jsAlertFormat, kAlertDialogMessage));
-    GUniquePtr<char> alertHTML(g_strdup_printf(htmlOnLoadFormat, alertDialogMessage.get()));
-    test->loadHtml(alertHTML.get(), 0);
-    test->waitUntilMainLoopFinishes();
-    webkit_web_view_stop_loading(test->m_webView);
-    test->waitUntilLoadFinished();
-
-    test->m_scriptDialogType = WEBKIT_SCRIPT_DIALOG_CONFIRM;
-    GUniquePtr<char> confirmDialogMessage(g_strdup_printf(jsConfirmFormat, kConfirmDialogMessage));
-    GUniquePtr<char> confirmHTML(g_strdup_printf(htmlOnLoadFormat, confirmDialogMessage.get()));
-    test->loadHtml(confirmHTML.get(), 0);
-    test->waitUntilMainLoopFinishes();
-    webkit_web_view_stop_loading(test->m_webView);
-    test->waitUntilLoadFinished();
+    for (unsigned i = 0; i <= 1; ++i) {
+        test->m_delayedScriptDialogs = !!i;
+
+        test->m_scriptDialogType = WEBKIT_SCRIPT_DIALOG_ALERT;
+        GUniquePtr<char> alertDialogMessage(g_strdup_printf(jsAlertFormat, kAlertDialogMessage));
+        GUniquePtr<char> alertHTML(g_strdup_printf(htmlOnLoadFormat, alertDialogMessage.get()));
+        test->loadHtml(alertHTML.get(), nullptr);
+        test->waitUntilMainLoopFinishes();
+        webkit_web_view_stop_loading(test->m_webView);
+        test->waitUntilLoadFinished();
+
+        test->m_scriptDialogType = WEBKIT_SCRIPT_DIALOG_CONFIRM;
+        GUniquePtr<char> confirmDialogMessage(g_strdup_printf(jsConfirmFormat, kConfirmDialogMessage));
+        GUniquePtr<char> confirmHTML(g_strdup_printf(htmlOnLoadFormat, confirmDialogMessage.get()));
+        test->loadHtml(confirmHTML.get(), nullptr);
+        test->waitUntilMainLoopFinishes();
+        webkit_web_view_stop_loading(test->m_webView);
+        test->waitUntilLoadFinished();
+
+        test->m_scriptDialogType = WEBKIT_SCRIPT_DIALOG_PROMPT;
+        GUniquePtr<char> promptDialogMessage(g_strdup_printf(jsPromptFormat, kPromptDialogMessage));
+        GUniquePtr<char> promptHTML(g_strdup_printf(htmlOnLoadFormat, promptDialogMessage.get()));
+        test->loadHtml(promptHTML.get(), nullptr);
+        test->waitUntilMainLoopFinishes();
+        webkit_web_view_stop_loading(test->m_webView);
+        test->waitUntilLoadFinished();
+    }
 
-    test->m_scriptDialogType = WEBKIT_SCRIPT_DIALOG_PROMPT;
-    GUniquePtr<char> promptDialogMessage(g_strdup_printf(jsPromptFormat, kPromptDialogMessage));
-    GUniquePtr<char> promptHTML(g_strdup_printf(htmlOnLoadFormat, promptDialogMessage.get()));
-    test->loadHtml(promptHTML.get(), 0);
-    test->waitUntilMainLoopFinishes();
-    webkit_web_view_stop_loading(test->m_webView);
-    test->waitUntilLoadFinished();
+    test->m_delayedScriptDialogs = false;
 
     // FIXME: implement simulateUserInteraction in WPE.
 #if PLATFORM(GTK)