Title: [193826] trunk
Revision
193826
Author
carlo...@webkit.org
Date
2015-12-09 05:32:54 -0800 (Wed, 09 Dec 2015)

Log Message

[GTK] Add API to handle beforeunload events
https://bugs.webkit.org/show_bug.cgi?id=139090

Reviewed by Gustavo Noronha Silva.

Source/WebKit2:

beforeunload is fired when a page is about to be closed, to ask
the user for confirmation. This happens when reloading a page,
when navigating to another page or when the page is closed by a
user action (a tab or window closed). In the first two cases, the
event is automatically fired by WebCore, but in the case of a user
action we need additional API to ensure the event is fired before
the page is closed. A new script dialog type has been added to
handle beforeunload events and webkit_web_view_try_close() to
allow applications to try to close the page.

* UIProcess/API/gtk/WebKitScriptDialog.cpp:
(webkit_script_dialog_confirm_set_confirmed): BeforeUnloadConfirm
dialogs can also be confirmed.
* UIProcess/API/gtk/WebKitScriptDialog.h: Add
WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM script dialog type.
* UIProcess/API/gtk/WebKitUIClient.cpp: Implement
canRunBeforeUnloadConfirmPanel() and runBeforeUnloadConfirmPanel().
* UIProcess/API/gtk/WebKitWebView.cpp:
(webkitWebViewCreateJavaScriptDialog): Add secondaryText optional
parameter for BeforeUnloadConfirm dialogs. Do not set the default
response if the dialog was created without buttons.
(webkitWebViewScriptDialog): Handle
WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM script dialog type to
create a message dialog for beforeunlos events.
(webkit_web_view_class_init): Update documentation of
WebKitWebView::close and WebKitWebView::script-dialog.
(webkitWebViewRunJavaScriptBeforeUnloadConfirm): Emit WebKitWebView::script-dialog.
(webkit_web_view_try_close): Try to close the page.
* UIProcess/API/gtk/WebKitWebView.h:
* UIProcess/API/gtk/WebKitWebViewPrivate.h:
* UIProcess/API/gtk/docs/webkit2gtk-sections.txt: Add new symbol.

Tools:

* MiniBrowser/gtk/BrowserWindow.c:
(browserWindowConstructed):
(browserWindowDeleteEvent):
(browser_window_class_init):
Handle delete-event to prevent the window from being closed when
the page has beforeunload handlers. Use
webkit_web_view_try_close() when the window is requested to be
closed.
* TestWebKitAPI/Tests/WebKit2Gtk/TestUIClient.cpp:
(testWebViewJavaScriptDialogs):
Add a test case for the WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM
script dialog type.

Modified Paths

Diff

Modified: trunk/Source/WebKit2/ChangeLog (193825 => 193826)


--- trunk/Source/WebKit2/ChangeLog	2015-12-09 13:30:11 UTC (rev 193825)
+++ trunk/Source/WebKit2/ChangeLog	2015-12-09 13:32:54 UTC (rev 193826)
@@ -1,3 +1,42 @@
+2015-12-09  Carlos Garcia Campos  <cgar...@igalia.com>
+
+        [GTK] Add API to handle beforeunload events
+        https://bugs.webkit.org/show_bug.cgi?id=139090
+
+        Reviewed by Gustavo Noronha Silva.
+
+        beforeunload is fired when a page is about to be closed, to ask
+        the user for confirmation. This happens when reloading a page,
+        when navigating to another page or when the page is closed by a
+        user action (a tab or window closed). In the first two cases, the
+        event is automatically fired by WebCore, but in the case of a user
+        action we need additional API to ensure the event is fired before
+        the page is closed. A new script dialog type has been added to
+        handle beforeunload events and webkit_web_view_try_close() to
+        allow applications to try to close the page.
+
+        * UIProcess/API/gtk/WebKitScriptDialog.cpp:
+        (webkit_script_dialog_confirm_set_confirmed): BeforeUnloadConfirm
+        dialogs can also be confirmed.
+        * UIProcess/API/gtk/WebKitScriptDialog.h: Add
+        WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM script dialog type.
+        * UIProcess/API/gtk/WebKitUIClient.cpp: Implement
+        canRunBeforeUnloadConfirmPanel() and runBeforeUnloadConfirmPanel().
+        * UIProcess/API/gtk/WebKitWebView.cpp:
+        (webkitWebViewCreateJavaScriptDialog): Add secondaryText optional
+        parameter for BeforeUnloadConfirm dialogs. Do not set the default
+        response if the dialog was created without buttons.
+        (webkitWebViewScriptDialog): Handle
+        WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM script dialog type to
+        create a message dialog for beforeunlos events.
+        (webkit_web_view_class_init): Update documentation of
+        WebKitWebView::close and WebKitWebView::script-dialog.
+        (webkitWebViewRunJavaScriptBeforeUnloadConfirm): Emit WebKitWebView::script-dialog.
+        (webkit_web_view_try_close): Try to close the page.
+        * UIProcess/API/gtk/WebKitWebView.h:
+        * UIProcess/API/gtk/WebKitWebViewPrivate.h:
+        * UIProcess/API/gtk/docs/webkit2gtk-sections.txt: Add new symbol.
+
 2015-12-09  Gyuyoung Kim  <gyuyoung....@webkit.org>
 
         [EFL] Missing to set ignoreTLSError to NetworkProcess

Modified: trunk/Source/WebKit2/UIProcess/API/gtk/WebKitScriptDialog.cpp (193825 => 193826)


--- trunk/Source/WebKit2/UIProcess/API/gtk/WebKitScriptDialog.cpp	2015-12-09 13:30:11 UTC (rev 193825)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebKitScriptDialog.cpp	2015-12-09 13:32:54 UTC (rev 193826)
@@ -72,17 +72,17 @@
  * @dialog: a #WebKitScriptDialog
  * @confirmed: whether user confirmed the dialog
  *
- * This method is used for %WEBKIT_SCRIPT_DIALOG_CONFIRM dialogs when
+ * This method is used for %WEBKIT_SCRIPT_DIALOG_CONFIRM and %WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM dialogs when
  * #WebKitWebView::script-dialog signal is emitted to set whether the user
  * confirmed the dialog or not. The default implementation of #WebKitWebView::script-dialog
- * signal sets %TRUE when the OK button is clicked and %FALSE otherwise.
+ * signal sets %TRUE when the OK or Stay buttons are clicked and %FALSE otherwise.
  * It's an error to use this method with a #WebKitScriptDialog that is not of type
- * %WEBKIT_SCRIPT_DIALOG_CONFIRM.
+ * %WEBKIT_SCRIPT_DIALOG_CONFIRM or %WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM
  */
 void webkit_script_dialog_confirm_set_confirmed(WebKitScriptDialog* dialog, gboolean confirmed)
 {
     g_return_if_fail(dialog);
-    g_return_if_fail(dialog->type == WEBKIT_SCRIPT_DIALOG_CONFIRM);
+    g_return_if_fail(dialog->type == WEBKIT_SCRIPT_DIALOG_CONFIRM || dialog->type == WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM);
 
     dialog->confirmed = confirmed;
 }

Modified: trunk/Source/WebKit2/UIProcess/API/gtk/WebKitScriptDialog.h (193825 => 193826)


--- trunk/Source/WebKit2/UIProcess/API/gtk/WebKitScriptDialog.h	2015-12-09 13:30:11 UTC (rev 193825)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebKitScriptDialog.h	2015-12-09 13:32:54 UTC (rev 193826)
@@ -41,13 +41,16 @@
  * confirmation to the user.
  * @WEBKIT_SCRIPT_DIALOG_PROMPT: Prompt script dialog, used to ask
  * information to the user.
+ * @WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM: Before unload confirm dialog,
+ * used to ask confirmation to leave the current page to the user. Since 2.12
  *
  * Enum values used for determining the type of #WebKitScriptDialog
  */
 typedef enum {
     WEBKIT_SCRIPT_DIALOG_ALERT,
     WEBKIT_SCRIPT_DIALOG_CONFIRM,
-    WEBKIT_SCRIPT_DIALOG_PROMPT
+    WEBKIT_SCRIPT_DIALOG_PROMPT,
+    WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM
 } WebKitScriptDialogType;
 
 WEBKIT_API GType

Modified: trunk/Source/WebKit2/UIProcess/API/gtk/WebKitUIClient.cpp (193825 => 193826)


--- trunk/Source/WebKit2/UIProcess/API/gtk/WebKitUIClient.cpp	2015-12-09 13:30:11 UTC (rev 193825)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebKitUIClient.cpp	2015-12-09 13:32:54 UTC (rev 193826)
@@ -84,6 +84,13 @@
         completionHandler(String::fromUTF8(result.data()));
     }
 
+    virtual bool canRunBeforeUnloadConfirmPanel() const override { return true; }
+
+    virtual bool runBeforeUnloadConfirmPanel(WebPageProxy*, const String& message, WebFrameProxy*) override
+    {
+        return webkitWebViewRunJavaScriptBeforeUnloadConfirm(m_webView, message.utf8());
+    }
+
     virtual void mouseDidMoveOverElement(WebPageProxy*, const WebHitTestResultData& data, WebEvent::Modifiers modifiers, API::Object*) override
     {
         webkitWebViewMouseTargetChanged(m_webView, data, toGdkModifiers(modifiers));

Modified: trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebView.cpp (193825 => 193826)


--- trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebView.cpp	2015-12-09 13:30:11 UTC (rev 193825)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebView.cpp	2015-12-09 13:32:54 UTC (rev 193826)
@@ -325,14 +325,17 @@
     return nullptr;
 }
 
-static GtkWidget* webkitWebViewCreateJavaScriptDialog(WebKitWebView* webView, GtkMessageType type, GtkButtonsType buttons, int defaultResponse, const char* message)
+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));
-    GtkWidget* dialog = gtk_message_dialog_new(widgetIsOnscreenToplevelWindow(parent) ? GTK_WINDOW(parent) : 0,
-                                               GTK_DIALOG_DESTROY_WITH_PARENT, type, buttons, "%s", message);
+    GtkWidget* dialog = gtk_message_dialog_new(widgetIsOnscreenToplevelWindow(parent) ? GTK_WINDOW(parent) : nullptr,
+        GTK_DIALOG_DESTROY_WITH_PARENT, type, buttons, "%s", primaryText);
+    if (secondaryText)
+        gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", secondaryText);
     GUniquePtr<char> title(g_strdup_printf("_javascript_ - %s", webkit_web_view_get_uri(webView)));
     gtk_window_set_title(GTK_WINDOW(dialog), title.get());
-    gtk_dialog_set_default_response(GTK_DIALOG(dialog), defaultResponse);
+    if (buttons != GTK_BUTTONS_NONE)
+        gtk_dialog_set_default_response(GTK_DIALOG(dialog), defaultResponse);
 
     return dialog;
 }
@@ -350,7 +353,7 @@
         dialog = webkitWebViewCreateJavaScriptDialog(webView, GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, GTK_RESPONSE_OK, scriptDialog->message.data());
         scriptDialog->confirmed = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK;
         break;
-    case WEBKIT_SCRIPT_DIALOG_PROMPT:
+    case WEBKIT_SCRIPT_DIALOG_PROMPT: {
         dialog = webkitWebViewCreateJavaScriptDialog(webView, GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, GTK_RESPONSE_OK, scriptDialog->message.data());
         GtkWidget* entry = gtk_entry_new();
         gtk_entry_set_text(GTK_ENTRY(entry), scriptDialog->defaultText.data());
@@ -361,6 +364,14 @@
             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());
+        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);
 
@@ -1201,10 +1212,11 @@
 
     /**
      * WebKitWebView::close:
-     * @webView: the #WebKitWebView on which the signal is emitted
+     * @web_view: the #WebKitWebView on which the signal is emitted
      *
      * Emitted when closing a #WebKitWebView is requested. This occurs when a
-     * call is made from _javascript_'s <function>window.close</function> function.
+     * call is made from _javascript_'s <function>window.close</function> function or
+     * after trying to close the @web_view with webkit_web_view_try_close().
      * It is the owner's responsibility to handle this signal to hide or
      * destroy the #WebKitWebView, if necessary.
      */
@@ -1223,7 +1235,8 @@
      * @dialog: the #WebKitScriptDialog to show
      *
      * Emitted when _javascript_ code calls <function>window.alert</function>,
-     * <function>window.confirm</function> or <function>window.prompt</function>.
+     * <function>window.confirm</function> or <function>window.prompt</function>,
+     * or when <function>onbeforeunload</function> event is fired.
      * The @dialog parameter should be used to build the dialog.
      * If the signal is not handled a different dialog will be built and shown depending
      * on the dialog type:
@@ -1238,6 +1251,9 @@
      *  %WEBKIT_SCRIPT_DIALOG_PROMPT: message dialog with OK and Cancel buttons and
      *  a text entry with the default text.
      * </para></listitem>
+     * <listitem><para>
+     *  %WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM: message dialog with Stay and Leave buttons.
+     * </para></listitem>
      * </itemizedlist>
      *
      * Returns: %TRUE to stop other handlers from being invoked for the event.
@@ -1929,6 +1945,14 @@
     return dialog.text;
 }
 
+bool webkitWebViewRunJavaScriptBeforeUnloadConfirm(WebKitWebView* webView, const CString& message)
+{
+    WebKitScriptDialog dialog(WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM, message);
+    gboolean returnValue;
+    g_signal_emit(webView, signals[SCRIPT_DIALOG], 0, &dialog, &returnValue);
+    return dialog.confirmed;
+}
+
 void webkitWebViewMakePolicyDecision(WebKitWebView* webView, WebKitPolicyDecisionType type, WebKitPolicyDecision* decision)
 {
     gboolean returnValue;
@@ -2251,6 +2275,24 @@
 }
 
 /**
+ * webkit_web_view_try_close:
+ * @web_view: a #WebKitWebView
+ *
+ * Tries to close the @web_view. This will fire the onbeforeunload event
+ * to ask the user for confirmation to close the page. If there isn't an
+ * onbeforeunload event handler or the user confirms to close the page,
+ * the #WebKitWebView::close signal is emitted, otherwise nothing happens.
+ *
+ * Since: 2.12
+ */
+void webkit_web_view_try_close(WebKitWebView *webView)
+{
+    g_return_if_fail(WEBKIT_IS_WEB_VIEW(webView));
+    if (getPage(webView)->tryClose())
+        webkitWebViewClosePage(webView);
+}
+
+/**
  * webkit_web_view_load_uri:
  * @web_view: a #WebKitWebView
  * @uri: an URI string

Modified: trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebView.h (193825 => 193826)


--- trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebView.h	2015-12-09 13:30:11 UTC (rev 193825)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebView.h	2015-12-09 13:32:54 UTC (rev 193826)
@@ -277,6 +277,9 @@
 webkit_web_view_get_context                          (WebKitWebView             *web_view);
 
 WEBKIT_API void
+webkit_web_view_try_close                            (WebKitWebView             *web_view);
+
+WEBKIT_API void
 webkit_web_view_load_uri                             (WebKitWebView             *web_view,
                                                       const gchar               *uri);
 

Modified: trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebViewPrivate.h (193825 => 193826)


--- trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebViewPrivate.h	2015-12-09 13:30:11 UTC (rev 193825)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebViewPrivate.h	2015-12-09 13:32:54 UTC (rev 193826)
@@ -44,6 +44,7 @@
 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 webkitWebViewMakePermissionRequest(WebKitWebView*, WebKitPermissionRequest*);
 void webkitWebViewMakePolicyDecision(WebKitWebView*, WebKitPolicyDecisionType, WebKitPolicyDecision*);
 void webkitWebViewMouseTargetChanged(WebKitWebView*, const WebKit::WebHitTestResultData&, unsigned modifiers);

Modified: trunk/Source/WebKit2/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt (193825 => 193826)


--- trunk/Source/WebKit2/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt	2015-12-09 13:30:11 UTC (rev 193825)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt	2015-12-09 13:32:54 UTC (rev 193826)
@@ -157,6 +157,7 @@
 webkit_web_view_new_with_user_content_manager
 webkit_web_view_get_context
 webkit_web_view_get_user_content_manager
+webkit_web_view_try_close
 webkit_web_view_load_uri
 webkit_web_view_load_html
 webkit_web_view_load_alternate_html

Modified: trunk/Tools/ChangeLog (193825 => 193826)


--- trunk/Tools/ChangeLog	2015-12-09 13:30:11 UTC (rev 193825)
+++ trunk/Tools/ChangeLog	2015-12-09 13:32:54 UTC (rev 193826)
@@ -1,3 +1,23 @@
+2015-12-09  Carlos Garcia Campos  <cgar...@igalia.com>
+
+        [GTK] Add API to handle beforeunload events
+        https://bugs.webkit.org/show_bug.cgi?id=139090
+
+        Reviewed by Gustavo Noronha Silva.
+
+        * MiniBrowser/gtk/BrowserWindow.c:
+        (browserWindowConstructed):
+        (browserWindowDeleteEvent):
+        (browser_window_class_init):
+        Handle delete-event to prevent the window from being closed when
+        the page has beforeunload handlers. Use
+        webkit_web_view_try_close() when the window is requested to be
+        closed.
+        * TestWebKitAPI/Tests/WebKit2Gtk/TestUIClient.cpp:
+        (testWebViewJavaScriptDialogs):
+        Add a test case for the WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM
+        script dialog type.
+
 2015-12-09  Mario Sanchez Prada  <ma...@endlessm.com>
 
         Refactored initialization code in LoadTrackingTest.

Modified: trunk/Tools/MiniBrowser/gtk/BrowserWindow.c (193825 => 193826)


--- trunk/Tools/MiniBrowser/gtk/BrowserWindow.c	2015-12-09 13:30:11 UTC (rev 193825)
+++ trunk/Tools/MiniBrowser/gtk/BrowserWindow.c	2015-12-09 13:32:54 UTC (rev 193826)
@@ -1096,6 +1096,7 @@
     g_signal_connect(window->webView, "notify::estimated-load-progress", G_CALLBACK(webViewLoadProgressChanged), window);
     g_signal_connect(window->webView, "notify::title", G_CALLBACK(webViewTitleChanged), window);
     g_signal_connect(window->webView, "create", G_CALLBACK(webViewCreate), window);
+    g_signal_connect(window->webView, "close", G_CALLBACK(webViewClose), window);
     g_signal_connect(window->webView, "load-failed", G_CALLBACK(webViewLoadFailed), window);
     g_signal_connect(window->webView, "load-failed-with-tls-errors", G_CALLBACK(webViewLoadFailedWithTLSerrors), window);
     g_signal_connect(window->webView, "decide-policy", G_CALLBACK(webViewDecidePolicy), window);
@@ -1150,6 +1151,13 @@
         webkit_web_view_load_html(window->webView, "<html></html>", "file:///");
 }
 
+static gboolean browserWindowDeleteEvent(GtkWidget *widget, GdkEventAny* event)
+{
+    BrowserWindow *window = BROWSER_WINDOW(widget);
+    webkit_web_view_try_close(window->webView);
+    return TRUE;
+}
+
 static void browser_window_class_init(BrowserWindowClass *klass)
 {
     GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
@@ -1159,6 +1167,9 @@
     gobjectClass->set_property = browserWindowSetProperty;
     gobjectClass->finalize = browserWindowFinalize;
 
+    GtkWidgetClass *widgetClass = GTK_WIDGET_CLASS(klass);
+    widgetClass->delete_event = browserWindowDeleteEvent;
+
     g_object_class_install_property(gobjectClass,
                                     PROP_VIEW,
                                     g_param_spec_object("view",

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestUIClient.cpp (193825 => 193826)


--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestUIClient.cpp	2015-12-09 13:30:11 UTC (rev 193825)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestUIClient.cpp	2015-12-09 13:32:54 UTC (rev 193826)
@@ -27,6 +27,7 @@
 static const char* kConfirmDialogMessage = "WebKitGTK+ confirm dialog message";
 static const char* kPromptDialogMessage = "WebKitGTK+ prompt dialog message";
 static const char* kPromptDialogReturnedText = "WebKitGTK+ prompt dialog returned text";
+static const char* kBeforeUnloadConfirmDialogMessage = "WebKitGTK+ beforeunload dialog message";
 
 class UIClientTest: public WebViewTest {
 public:
@@ -146,6 +147,9 @@
         case WEBKIT_SCRIPT_DIALOG_PROMPT:
             g_assert_cmpstr(webkit_script_dialog_get_message(dialog), ==, kPromptDialogReturnedText);
             break;
+        case WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM:
+            g_assert_not_reached();
+            break;
         }
 
         g_main_loop_quit(m_mainLoop);
@@ -165,6 +169,13 @@
         webkit_script_dialog_prompt_set_text(dialog, kPromptDialogReturnedText);
     }
 
+    void scriptBeforeUnloadConfirm(WebKitScriptDialog* dialog)
+    {
+        g_assert_cmpstr(webkit_script_dialog_get_message(dialog), ==, kBeforeUnloadConfirmDialogMessage);
+        m_scriptDialogConfirmed = true;
+        webkit_script_dialog_confirm_set_confirmed(dialog, m_scriptDialogConfirmed);
+    }
+
     static gboolean scriptDialog(WebKitWebView*, WebKitScriptDialog* dialog, UIClientTest* test)
     {
         switch (webkit_script_dialog_get_dialog_type(dialog)) {
@@ -177,6 +188,9 @@
         case WEBKIT_SCRIPT_DIALOG_PROMPT:
             test->scriptPrompt(dialog);
             break;
+        case WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM:
+            test->scriptBeforeUnloadConfirm(dialog);
+            break;
         }
 
         return TRUE;
@@ -232,6 +246,24 @@
         g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
     }
 
+    static void tryWebViewCloseCallback(UIClientTest* test)
+    {
+        g_main_loop_quit(test->m_mainLoop);
+    }
+
+    void tryCloseAndWaitUntilClosed()
+    {
+        gulong handler = g_signal_connect_swapped(m_webView, "close", G_CALLBACK(tryWebViewCloseCallback), this);
+        // Use an idle because webkit_web_view_try_close can emit the close signal in the
+        // current run loop iteration.
+        g_idle_add([](gpointer data) -> gboolean {
+            webkit_web_view_try_close(WEBKIT_WEB_VIEW(data));
+            return G_SOURCE_REMOVE;
+        }, m_webView);
+        g_main_loop_run(m_mainLoop);
+        g_signal_handler_disconnect(m_webView, handler);
+    }
+
     void waitUntilMainLoopFinishes()
     {
         g_main_loop_run(m_mainLoop);
@@ -473,24 +505,71 @@
     static const char* jsAlertFormat = "alert('%s')";
     static const char* jsConfirmFormat = "do { confirmed = confirm('%s'); } while (!confirmed); alert('confirmed');";
     static const char* jsPromptFormat = "alert(prompt('%s', 'default'));";
+    static const char* htmlOnBeforeUnloadFormat =
+        "<html><body _onbeforeunload_=\"return beforeUnloadHandler();\"><script>function beforeUnloadHandler() { return \"%s\"; }</script></body></html>";
 
     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();
 
     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_scriptDialogType = WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM;
+    GUniquePtr<char> beforeUnloadDialogHTML(g_strdup_printf(htmlOnBeforeUnloadFormat, kBeforeUnloadConfirmDialogMessage));
+    test->loadHtml(beforeUnloadDialogHTML.get(), nullptr);
+    test->waitUntilLoadFinished();
+
+    // Reload should trigger onbeforeunload.
+#if 0
+    // FIXME: reloading HTML data doesn't emit finished load event.
+    // See https://bugs.webkit.org/show_bug.cgi?id=139089.
+    test->m_scriptDialogConfirmed = false;
+    webkit_web_view_reload(test->m_webView);
+    test->waitUntilLoadFinished();
+    g_assert(test->m_scriptDialogConfirmed);
+#endif
+
+    // Navigation should trigger onbeforeunload.
+    test->m_scriptDialogConfirmed = false;
+    test->loadHtml("<html></html>", nullptr);
+    test->waitUntilLoadFinished();
+    g_assert(test->m_scriptDialogConfirmed);
+
+    // Try close should trigger onbeforeunload.
+    test->m_scriptDialogConfirmed = false;
+    test->loadHtml(beforeUnloadDialogHTML.get(), nullptr);
+    test->waitUntilLoadFinished();
+    test->tryCloseAndWaitUntilClosed();
+    g_assert(test->m_scriptDialogConfirmed);
+
+    // Try close on a page with no unload handlers should not trigger onbeforeunload,
+    // but should actually close the page.
+    test->m_scriptDialogConfirmed = false;
+    test->loadHtml("<html><body></body></html>", nullptr);
+    test->waitUntilLoadFinished();
+    // We got a onbeforeunload of the previous page.
+    g_assert(test->m_scriptDialogConfirmed);
+    test->m_scriptDialogConfirmed = false;
+    test->tryCloseAndWaitUntilClosed();
+    g_assert(!test->m_scriptDialogConfirmed);
 }
 
 static void testWebViewWindowProperties(UIClientTest* test, gconstpointer)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to