[GTK] TestContextMenu is failing
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebKit2Gtk / TestContextMenu.cpp
1 /*
2  * Copyright (C) 2012 Igalia S.L.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2,1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21 #include "WebViewTest.h"
22 #include <wtf/Vector.h>
23 #include <wtf/glib/GRefPtr.h>
24
25 class ContextMenuTest: public WebViewTest {
26 public:
27     enum ContextMenuItemStateFlags {
28         Visible = 1 << 0,
29         Enabled = 1 << 1,
30         Checked = 1 << 2
31     };
32
33     void checkContextMenuEvent(GdkEvent* event)
34     {
35         g_assert(event);
36         g_assert_cmpint(event->type, ==, GDK_BUTTON_PRESS);
37         g_assert_cmpint(event->button.button, ==, 3);
38         g_assert_cmpint(event->button.x, ==, m_menuPositionX);
39         g_assert_cmpint(event->button.y, ==, m_menuPositionY);
40     }
41
42     static gboolean contextMenuCallback(WebKitWebView* webView, WebKitContextMenu* contextMenu, GdkEvent* event, WebKitHitTestResult* hitTestResult, ContextMenuTest* test)
43     {
44         g_assert(WEBKIT_IS_CONTEXT_MENU(contextMenu));
45         test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(contextMenu));
46         test->checkContextMenuEvent(event);
47         g_assert(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult));
48         test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(hitTestResult));
49
50         return test->contextMenu(contextMenu, event, hitTestResult);
51     }
52
53     static void contextMenuDismissedCallback(WebKitWebView*, ContextMenuTest* test)
54     {
55         test->contextMenuDismissed();
56     }
57
58     ContextMenuTest()
59         : m_menuPositionX(0)
60         , m_menuPositionY(0)
61     {
62         g_signal_connect(m_webView, "context-menu", G_CALLBACK(contextMenuCallback), this);
63         g_signal_connect(m_webView, "context-menu-dismissed", G_CALLBACK(contextMenuDismissedCallback), this);
64     }
65
66     ~ContextMenuTest()
67     {
68         g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
69     }
70
71     virtual bool contextMenu(WebKitContextMenu*, GdkEvent*, WebKitHitTestResult*) = 0;
72
73     virtual void contextMenuDismissed()
74     {
75         quitMainLoop();
76     }
77
78     GtkMenu* getPopupMenu()
79     {
80         GUniquePtr<GList> toplevels(gtk_window_list_toplevels());
81         for (GList* iter = toplevels.get(); iter; iter = g_list_next(iter)) {
82             if (!GTK_IS_WINDOW(iter->data))
83                 continue;
84
85             GtkWidget* child = gtk_bin_get_child(GTK_BIN(iter->data));
86             if (!GTK_IS_MENU(child))
87                 continue;
88
89             if (gtk_menu_get_attach_widget(GTK_MENU(child)) == GTK_WIDGET(m_webView))
90                 return GTK_MENU(child);
91         }
92         g_assert_not_reached();
93         return 0;
94     }
95
96     void checkActionState(GtkAction* action, unsigned state)
97     {
98         if (state & Visible)
99             g_assert(gtk_action_get_visible(action));
100         else
101             g_assert(!gtk_action_get_visible(action));
102
103         if (state & Enabled)
104             g_assert(gtk_action_get_sensitive(action));
105         else
106             g_assert(!gtk_action_get_sensitive(action));
107
108         if (GTK_IS_TOGGLE_ACTION(action)) {
109             if (state & Checked)
110                 g_assert(gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)));
111             else
112                 g_assert(!gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)));
113         }
114     }
115
116     GList* checkCurrentItemIsStockActionAndGetNext(GList* items, WebKitContextMenuAction stockAction, unsigned state)
117     {
118         g_assert(items);
119         g_assert(WEBKIT_IS_CONTEXT_MENU_ITEM(items->data));
120
121         WebKitContextMenuItem* item = WEBKIT_CONTEXT_MENU_ITEM(items->data);
122         assertObjectIsDeletedWhenTestFinishes(G_OBJECT(item));
123
124         GtkAction* action = webkit_context_menu_item_get_action(item);
125         g_assert(GTK_IS_ACTION(action));
126
127         g_assert_cmpint(webkit_context_menu_item_get_stock_action(item), ==, stockAction);
128
129         checkActionState(action, state);
130
131         return g_list_next(items);
132     }
133
134     GList* checkCurrentItemIsCustomActionAndGetNext(GList* items, const char* label, unsigned state)
135     {
136         g_assert(items);
137         g_assert(WEBKIT_IS_CONTEXT_MENU_ITEM(items->data));
138
139         WebKitContextMenuItem* item = WEBKIT_CONTEXT_MENU_ITEM(items->data);
140         assertObjectIsDeletedWhenTestFinishes(G_OBJECT(item));
141
142         GtkAction* action = webkit_context_menu_item_get_action(item);
143         g_assert(GTK_IS_ACTION(action));
144
145         g_assert_cmpint(webkit_context_menu_item_get_stock_action(item), ==, WEBKIT_CONTEXT_MENU_ACTION_CUSTOM);
146         g_assert_cmpstr(gtk_action_get_label(action), ==, label);
147
148         checkActionState(action, state);
149
150         return g_list_next(items);
151     }
152
153     GList* checkCurrentItemIsSubMenuAndGetNext(GList* items, const char* label, unsigned state, GList** subMenuIter)
154     {
155         g_assert(items);
156         g_assert(WEBKIT_IS_CONTEXT_MENU_ITEM(items->data));
157
158         WebKitContextMenuItem* item = WEBKIT_CONTEXT_MENU_ITEM(items->data);
159         assertObjectIsDeletedWhenTestFinishes(G_OBJECT(item));
160
161         GtkAction* action = webkit_context_menu_item_get_action(item);
162         g_assert(GTK_IS_ACTION(action));
163
164         g_assert_cmpstr(gtk_action_get_label(action), ==, label);
165         checkActionState(action, state);
166
167         WebKitContextMenu* subMenu = webkit_context_menu_item_get_submenu(item);
168         g_assert(WEBKIT_IS_CONTEXT_MENU(subMenu));
169         if (subMenuIter)
170             *subMenuIter = webkit_context_menu_get_items(subMenu);
171
172         return g_list_next(items);
173     }
174
175     GList* checkCurrentItemIsSeparatorAndGetNext(GList* items)
176     {
177         g_assert(items);
178         g_assert(WEBKIT_IS_CONTEXT_MENU_ITEM(items->data));
179
180         WebKitContextMenuItem* item = WEBKIT_CONTEXT_MENU_ITEM(items->data);
181         g_assert(webkit_context_menu_item_is_separator(item));
182
183         return g_list_next(items);
184     }
185
186     static gboolean doRightClickIdleCallback(ContextMenuTest* test)
187     {
188         test->clickMouseButton(test->m_menuPositionX, test->m_menuPositionY, 3);
189         return FALSE;
190     }
191
192     void showContextMenuAtPositionAndWaitUntilFinished(int x, int y)
193     {
194         m_menuPositionX = x;
195         m_menuPositionY = y;
196         g_idle_add(reinterpret_cast<GSourceFunc>(doRightClickIdleCallback), this);
197         g_main_loop_run(m_mainLoop);
198     }
199
200     void showContextMenuAndWaitUntilFinished()
201     {
202         showContextMenuAtPositionAndWaitUntilFinished(0, 0);
203     }
204
205     static gboolean simulateEscKeyIdleCallback(ContextMenuTest* test)
206     {
207         test->keyStroke(GDK_KEY_Escape);
208         return FALSE;
209     }
210
211     void dismissContextMenuAndWaitUntilFinished()
212     {
213         g_idle_add(reinterpret_cast<GSourceFunc>(simulateEscKeyIdleCallback), this);
214         g_main_loop_run(m_mainLoop);
215     }
216
217     double m_menuPositionX;
218     double m_menuPositionY;
219 };
220
221 class ContextMenuDefaultTest: public ContextMenuTest {
222 public:
223     MAKE_GLIB_TEST_FIXTURE(ContextMenuDefaultTest);
224
225     enum DefaultMenuType {
226         Navigation,
227         Link,
228         Image,
229         LinkImage,
230         Video,
231         Audio,
232         Editable,
233         Selection
234     };
235
236     ContextMenuDefaultTest()
237         : m_expectedMenuType(Navigation)
238     {
239     }
240
241     bool contextMenu(WebKitContextMenu* contextMenu, GdkEvent* event, WebKitHitTestResult* hitTestResult)
242     {
243         GList* iter = webkit_context_menu_get_items(contextMenu);
244
245         switch (m_expectedMenuType) {
246         case Navigation:
247             g_assert(!webkit_hit_test_result_context_is_link(hitTestResult));
248             g_assert(!webkit_hit_test_result_context_is_image(hitTestResult));
249             g_assert(!webkit_hit_test_result_context_is_media(hitTestResult));
250             g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
251             g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
252             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_GO_BACK, Visible);
253             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_GO_FORWARD, Visible);
254             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_STOP, Visible);
255             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_RELOAD, Visible | Enabled);
256             break;
257         case Link:
258             g_assert(webkit_hit_test_result_context_is_link(hitTestResult));
259             g_assert(!webkit_hit_test_result_context_is_image(hitTestResult));
260             g_assert(!webkit_hit_test_result_context_is_media(hitTestResult));
261             g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
262             g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
263             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK, Visible | Enabled);
264             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK_IN_NEW_WINDOW, Visible | Enabled);
265             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_LINK_TO_DISK, Visible | Enabled);
266             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY_LINK_TO_CLIPBOARD, Visible | Enabled);
267             break;
268         case Image:
269             g_assert(!webkit_hit_test_result_context_is_link(hitTestResult));
270             g_assert(webkit_hit_test_result_context_is_image(hitTestResult));
271             g_assert(!webkit_hit_test_result_context_is_media(hitTestResult));
272             g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
273             g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
274             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_IMAGE_IN_NEW_WINDOW, Visible | Enabled);
275             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_IMAGE_TO_DISK, Visible | Enabled);
276             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY_IMAGE_TO_CLIPBOARD, Visible | Enabled);
277             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY_IMAGE_URL_TO_CLIPBOARD, Visible | Enabled);
278             break;
279         case LinkImage:
280             g_assert(webkit_hit_test_result_context_is_link(hitTestResult));
281             g_assert(webkit_hit_test_result_context_is_image(hitTestResult));
282             g_assert(!webkit_hit_test_result_context_is_media(hitTestResult));
283             g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
284             g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
285             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK, Visible | Enabled);
286             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK_IN_NEW_WINDOW, Visible | Enabled);
287             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_LINK_TO_DISK, Visible | Enabled);
288             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY_LINK_TO_CLIPBOARD, Visible | Enabled);
289             iter = checkCurrentItemIsSeparatorAndGetNext(iter);
290             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_IMAGE_IN_NEW_WINDOW, Visible | Enabled);
291             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_IMAGE_TO_DISK, Visible | Enabled);
292             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY_IMAGE_TO_CLIPBOARD, Visible | Enabled);
293             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY_IMAGE_URL_TO_CLIPBOARD, Visible | Enabled);
294             break;
295         case Video:
296             g_assert(!webkit_hit_test_result_context_is_link(hitTestResult));
297             g_assert(!webkit_hit_test_result_context_is_image(hitTestResult));
298             g_assert(webkit_hit_test_result_context_is_media(hitTestResult));
299             g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
300             g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
301             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_MEDIA_PLAY, Visible | Enabled);
302             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_MEDIA_MUTE, Visible);
303             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_TOGGLE_MEDIA_CONTROLS, Visible | Enabled | Checked);
304             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_TOGGLE_MEDIA_LOOP, Visible | Enabled);
305             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_ENTER_VIDEO_FULLSCREEN, Visible | Enabled);
306             iter = checkCurrentItemIsSeparatorAndGetNext(iter);
307             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY_VIDEO_LINK_TO_CLIPBOARD, Visible | Enabled);
308             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_VIDEO_IN_NEW_WINDOW, Visible | Enabled);
309             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_VIDEO_TO_DISK, Visible | Enabled);
310             break;
311         case Audio:
312             g_assert(!webkit_hit_test_result_context_is_link(hitTestResult));
313             g_assert(!webkit_hit_test_result_context_is_image(hitTestResult));
314             g_assert(webkit_hit_test_result_context_is_media(hitTestResult));
315             g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
316             g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
317             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_MEDIA_PLAY, Visible | Enabled);
318             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_MEDIA_MUTE, Visible);
319             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_TOGGLE_MEDIA_CONTROLS, Visible | Enabled | Checked);
320             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_TOGGLE_MEDIA_LOOP, Visible | Enabled);
321             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_ENTER_VIDEO_FULLSCREEN, Visible);
322             iter = checkCurrentItemIsSeparatorAndGetNext(iter);
323             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY_AUDIO_LINK_TO_CLIPBOARD, Visible | Enabled);
324             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_AUDIO_IN_NEW_WINDOW, Visible | Enabled);
325             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_AUDIO_TO_DISK, Visible | Enabled);
326             break;
327         case Editable:
328             g_assert(!webkit_hit_test_result_context_is_link(hitTestResult));
329             g_assert(!webkit_hit_test_result_context_is_image(hitTestResult));
330             g_assert(!webkit_hit_test_result_context_is_media(hitTestResult));
331             g_assert(webkit_hit_test_result_context_is_editable(hitTestResult));
332             g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult));
333             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_CUT, Visible);
334             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY, Visible);
335             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_PASTE, Visible | Enabled);
336             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_DELETE, Visible);
337             iter = checkCurrentItemIsSeparatorAndGetNext(iter);
338             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_SELECT_ALL, Visible | Enabled);
339             iter = checkCurrentItemIsSeparatorAndGetNext(iter);
340             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_UNICODE, Visible | Enabled);
341             break;
342         case Selection:
343             g_assert(!webkit_hit_test_result_context_is_link(hitTestResult));
344             g_assert(!webkit_hit_test_result_context_is_image(hitTestResult));
345             g_assert(!webkit_hit_test_result_context_is_media(hitTestResult));
346             g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult));
347             g_assert(webkit_hit_test_result_context_is_selection(hitTestResult));
348             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY, Visible | Enabled);
349             break;
350         default:
351             g_assert_not_reached();
352         }
353
354         if (webkit_settings_get_enable_developer_extras(webkit_web_view_get_settings(m_webView))) {
355             iter = checkCurrentItemIsSeparatorAndGetNext(iter);
356             iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_INSPECT_ELEMENT, Visible | Enabled);
357         }
358         g_assert(!iter);
359
360         quitMainLoop();
361
362         return true;
363     }
364
365     DefaultMenuType m_expectedMenuType;
366 };
367
368 static void testContextMenuDefaultMenu(ContextMenuDefaultTest* test, gconstpointer)
369 {
370     test->showInWindowAndWaitUntilMapped();
371
372     const char* linksHTML =
373         "<html><head>"
374         " <script>"
375         "    window.onload = function () {"
376         "      window.getSelection().removeAllRanges();"
377         "      var select_range = document.createRange();"
378         "      select_range.selectNodeContents(document.getElementById('text_to_select'));"
379         "      window.getSelection().addRange(select_range);"
380         "    }"
381         " </script>"
382         "</head><body>"
383         " <a style='position:absolute; left:1; top:1' href='http://www.webkitgtk.org' title='WebKitGTK+ Title'>WebKitGTK+ Website</a>"
384         " <img style='position:absolute; left:1; top:10' src='0xdeadbeef' width=5 height=5></img>"
385         " <a style='position:absolute; left:1; top:20' href='http://www.webkitgtk.org/logo' title='WebKitGTK+ Logo'><img src='0xdeadbeef' width=5 height=5></img></a>"
386         " <input style='position:absolute; left:1; top:30' size='10'></input>"
387         " <video style='position:absolute; left:1; top:50' width='300' height='300' controls='controls' preload='none'><source src='movie.ogg' type='video/ogg' /></video>"
388         " <audio style='position:absolute; left:1; top:60' width='50' height='20' controls='controls' preload='none'><source src='track.ogg' type='audio/ogg' /></audio>"
389         " <p style='position:absolute; left:1; top:90' id='text_to_select'>Lorem ipsum.</p>"
390         "</body></html>";
391     test->loadHtml(linksHTML, "file:///");
392     test->waitUntilLoadFinished();
393
394     // Context menu for selection.
395     // This test should always be the first because any other click removes the selection.
396     test->m_expectedMenuType = ContextMenuDefaultTest::Selection;
397     test->showContextMenuAtPositionAndWaitUntilFinished(2, 115);
398
399     // Context menu for document.
400     test->m_expectedMenuType = ContextMenuDefaultTest::Navigation;
401     test->showContextMenuAtPositionAndWaitUntilFinished(0, 0);
402
403     // Context menu for link.
404     test->m_expectedMenuType = ContextMenuDefaultTest::Link;
405     test->showContextMenuAtPositionAndWaitUntilFinished(1, 1);
406
407     // Context menu for image.
408     test->m_expectedMenuType = ContextMenuDefaultTest::Image;
409     test->showContextMenuAtPositionAndWaitUntilFinished(1, 10);
410
411     // Enable developer extras now, so that inspector element
412     // will be shown in the default context menu.
413     webkit_settings_set_enable_developer_extras(webkit_web_view_get_settings(test->m_webView), TRUE);
414
415     // Context menu for image link.
416     test->m_expectedMenuType = ContextMenuDefaultTest::LinkImage;
417     test->showContextMenuAtPositionAndWaitUntilFinished(1, 20);
418
419     // Context menu for video.
420     test->m_expectedMenuType = ContextMenuDefaultTest::Video;
421     test->showContextMenuAtPositionAndWaitUntilFinished(1, 50);
422
423     // Context menu for audio.
424     test->m_expectedMenuType = ContextMenuDefaultTest::Audio;
425     test->showContextMenuAtPositionAndWaitUntilFinished(1, 60);
426
427     // Context menu for editable.
428     test->m_expectedMenuType = ContextMenuDefaultTest::Editable;
429     test->showContextMenuAtPositionAndWaitUntilFinished(5, 35);
430 }
431
432 class ContextMenuCustomTest: public ContextMenuTest {
433 public:
434     MAKE_GLIB_TEST_FIXTURE(ContextMenuCustomTest);
435
436     ContextMenuCustomTest()
437         : m_itemToActivateLabel(0)
438         , m_activated(false)
439         , m_toggled(false)
440     {
441     }
442
443     bool contextMenu(WebKitContextMenu* contextMenu, GdkEvent*, WebKitHitTestResult* hitTestResult)
444     {
445         // Append our custom item to the default menu.
446         webkit_context_menu_append(contextMenu, webkit_context_menu_item_new(m_action.get()));
447         quitMainLoop();
448
449         return false;
450     }
451
452     GtkMenuItem* getMenuItem(GtkMenu* menu, const gchar* itemLabel)
453     {
454         GUniquePtr<GList> items(gtk_container_get_children(GTK_CONTAINER(menu)));
455         for (GList* iter = items.get(); iter; iter = g_list_next(iter)) {
456             GtkMenuItem* child = GTK_MENU_ITEM(iter->data);
457             if (g_str_equal(itemLabel, gtk_menu_item_get_label(child)))
458                 return child;
459         }
460         g_assert_not_reached();
461         return 0;
462     }
463
464     void activateMenuItem()
465     {
466         g_assert(m_itemToActivateLabel);
467         GtkMenu* menu = getPopupMenu();
468         GtkMenuItem* item = getMenuItem(menu, m_itemToActivateLabel);
469         gtk_menu_shell_activate_item(GTK_MENU_SHELL(menu), GTK_WIDGET(item), TRUE);
470         m_itemToActivateLabel = 0;
471     }
472
473     static gboolean activateMenuItemIdleCallback(gpointer userData)
474     {
475         ContextMenuCustomTest* test = static_cast<ContextMenuCustomTest*>(userData);
476         test->activateMenuItem();
477         return FALSE;
478     }
479
480     void activateCustomMenuItemAndWaitUntilActivated(const char* actionLabel)
481     {
482         m_activated = m_toggled = false;
483         m_itemToActivateLabel = actionLabel;
484         g_idle_add(activateMenuItemIdleCallback, this);
485         g_main_loop_run(m_mainLoop);
486     }
487
488     void toggleCustomMenuItemAndWaitUntilToggled(const char* actionLabel)
489     {
490         activateCustomMenuItemAndWaitUntilActivated(actionLabel);
491     }
492
493     static void actionActivatedCallback(GtkAction*, ContextMenuCustomTest* test)
494     {
495         test->m_activated = true;
496     }
497
498     static void actionToggledCallback(GtkAction*, ContextMenuCustomTest* test)
499     {
500         test->m_toggled = true;
501     }
502
503     void setAction(GtkAction* action)
504     {
505         m_action = action;
506         if (GTK_IS_TOGGLE_ACTION(action))
507             g_signal_connect(action, "toggled", G_CALLBACK(actionToggledCallback), this);
508         else
509             g_signal_connect(action, "activate", G_CALLBACK(actionActivatedCallback), this);
510     }
511
512     GRefPtr<GtkAction> m_action;
513     const char* m_itemToActivateLabel;
514     bool m_activated;
515     bool m_toggled;
516 };
517
518 static void testContextMenuPopulateMenu(ContextMenuCustomTest* test, gconstpointer)
519 {
520     test->showInWindowAndWaitUntilMapped();
521
522     test->loadHtml("<html><body>WebKitGTK+ Context menu tests</body></html>", "file:///");
523     test->waitUntilLoadFinished();
524
525     // Create a custom menu item.
526     GRefPtr<GtkAction> action = adoptGRef(gtk_action_new("WebKitGTK+CustomAction", "Custom _Action", 0, 0));
527     test->setAction(action.get());
528     test->showContextMenuAndWaitUntilFinished();
529     test->activateCustomMenuItemAndWaitUntilActivated(gtk_action_get_label(action.get()));
530     g_assert(test->m_activated);
531     g_assert(!test->m_toggled);
532
533     // Create a custom toggle menu item.
534     GRefPtr<GtkAction> toggleAction = adoptGRef(GTK_ACTION(gtk_toggle_action_new("WebKitGTK+CustomToggleAction", "Custom _Toggle Action", 0, 0)));
535     test->setAction(toggleAction.get());
536     test->showContextMenuAndWaitUntilFinished();
537     test->toggleCustomMenuItemAndWaitUntilToggled(gtk_action_get_label(toggleAction.get()));
538     g_assert(!test->m_activated);
539     g_assert(test->m_toggled);
540 }
541
542 class ContextMenuCustomFullTest: public ContextMenuTest {
543 public:
544     MAKE_GLIB_TEST_FIXTURE(ContextMenuCustomFullTest);
545
546     bool contextMenu(WebKitContextMenu* contextMenu, GdkEvent*, WebKitHitTestResult*)
547     {
548         // Clear proposed menu and build our own.
549         webkit_context_menu_remove_all(contextMenu);
550         g_assert_cmpint(webkit_context_menu_get_n_items(contextMenu), ==, 0);
551
552         // Add actions from stock.
553         webkit_context_menu_prepend(contextMenu, webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_GO_BACK));
554         webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_GO_FORWARD));
555         webkit_context_menu_insert(contextMenu, webkit_context_menu_item_new_separator(), 2);
556
557         // Add custom actions.
558         GRefPtr<GtkAction> action = adoptGRef(gtk_action_new("WebKitGTK+CustomAction", "Custom _Action", 0, 0));
559         gtk_action_set_sensitive(action.get(), FALSE);
560         webkit_context_menu_insert(contextMenu, webkit_context_menu_item_new(action.get()), -1);
561         GRefPtr<GtkAction> toggleAction = adoptGRef(GTK_ACTION(gtk_toggle_action_new("WebKitGTK+CustomToggleAction", "Custom _Toggle Action", 0, 0)));
562         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(toggleAction.get()), TRUE);
563         webkit_context_menu_append(contextMenu, webkit_context_menu_item_new(toggleAction.get()));
564         webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator());
565
566         // Add a submenu.
567         GRefPtr<WebKitContextMenu> subMenu = adoptGRef(webkit_context_menu_new());
568         assertObjectIsDeletedWhenTestFinishes(G_OBJECT(subMenu.get()));
569         webkit_context_menu_insert(subMenu.get(), webkit_context_menu_item_new_from_stock_action_with_label(WEBKIT_CONTEXT_MENU_ACTION_STOP, "Stop Load"), 0);
570         webkit_context_menu_insert(subMenu.get(), webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_RELOAD), -1);
571         webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_with_submenu("Load options", subMenu.get()));
572         webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator());
573
574         // Move Load submenu before custom actions.
575         webkit_context_menu_move_item(contextMenu, webkit_context_menu_last(contextMenu), 3);
576         webkit_context_menu_move_item(contextMenu, webkit_context_menu_last(contextMenu), 3);
577
578         // If last item is a separator, remove it.
579         if (webkit_context_menu_item_is_separator(webkit_context_menu_last(contextMenu)))
580             webkit_context_menu_remove(contextMenu, webkit_context_menu_last(contextMenu));
581
582         // Check the menu.
583         GList* iter = webkit_context_menu_get_items(contextMenu);
584
585         iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_GO_BACK, Visible | Enabled);
586         iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_GO_FORWARD, Visible | Enabled);
587         iter = checkCurrentItemIsSeparatorAndGetNext(iter);
588
589         GList* subMenuIter = 0;
590         iter = checkCurrentItemIsSubMenuAndGetNext(iter, "Load options", Visible | Enabled, &subMenuIter);
591         subMenuIter = checkCurrentItemIsStockActionAndGetNext(subMenuIter, WEBKIT_CONTEXT_MENU_ACTION_STOP, Visible | Enabled);
592         subMenuIter = checkCurrentItemIsStockActionAndGetNext(subMenuIter, WEBKIT_CONTEXT_MENU_ACTION_RELOAD, Visible | Enabled);
593         iter = checkCurrentItemIsSeparatorAndGetNext(iter);
594
595         iter = checkCurrentItemIsCustomActionAndGetNext(iter, "Custom _Action", Visible);
596         iter = checkCurrentItemIsCustomActionAndGetNext(iter, "Custom _Toggle Action", Visible | Enabled | Checked);
597         g_assert(!iter);
598
599         quitMainLoop();
600
601         return true;
602     }
603 };
604
605 static void testContextMenuCustomMenu(ContextMenuCustomFullTest* test, gconstpointer)
606 {
607     test->showInWindowAndWaitUntilMapped();
608
609     test->loadHtml("<html><body>WebKitGTK+ Context menu tests</body></html>", "file:///");
610     test->waitUntilLoadFinished();
611
612     test->showContextMenuAndWaitUntilFinished();
613 }
614
615 class ContextMenuDisabledTest: public ContextMenuTest {
616 public:
617     MAKE_GLIB_TEST_FIXTURE(ContextMenuDisabledTest);
618
619     enum DisableMode {
620         IgnoreClicks,
621         IgnoreDefaultMenu
622     };
623
624     static gboolean buttonPressEventCallback(GtkWidget*, GdkEvent* event, ContextMenuDisabledTest* test)
625     {
626         if (event->button.button != 3)
627             return FALSE;
628         return test->rightButtonPressed();
629     }
630
631     ContextMenuDisabledTest()
632         : m_disableMode(IgnoreClicks)
633     {
634         g_signal_connect(m_webView, "button-press-event", G_CALLBACK(buttonPressEventCallback), this);
635     }
636
637     bool contextMenu(WebKitContextMenu* contextMenu, GdkEvent*, WebKitHitTestResult*)
638     {
639         if (m_disableMode == IgnoreClicks)
640             g_assert_not_reached();
641         else
642             quitMainLoop();
643
644         return true;
645     }
646
647     bool rightButtonPressed()
648     {
649         if (m_disableMode == IgnoreClicks) {
650             quitMainLoopAfterProcessingPendingEvents();
651             return true;
652         }
653         return false;
654     }
655
656     DisableMode m_disableMode;
657 };
658
659 static void testContextMenuDisableMenu(ContextMenuDisabledTest* test, gconstpointer)
660 {
661     test->showInWindowAndWaitUntilMapped();
662
663     test->loadHtml("<html><body>WebKitGTK+ Context menu tests</body></html>", "file:///");
664     test->waitUntilLoadFinished();
665
666     test->m_disableMode = ContextMenuDisabledTest::IgnoreDefaultMenu;
667     test->showContextMenuAndWaitUntilFinished();
668
669     test->m_disableMode = ContextMenuDisabledTest::IgnoreClicks;
670     test->showContextMenuAndWaitUntilFinished();
671 }
672
673 class ContextMenuSubmenuTest: public ContextMenuTest {
674 public:
675     MAKE_GLIB_TEST_FIXTURE(ContextMenuSubmenuTest);
676
677     bool contextMenu(WebKitContextMenu* contextMenu, GdkEvent*, WebKitHitTestResult*)
678     {
679         size_t menuSize = webkit_context_menu_get_n_items(contextMenu);
680         GRefPtr<WebKitContextMenu> subMenu = adoptGRef(webkit_context_menu_new());
681         webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_with_submenu("SubMenuItem", subMenu.get()));
682         g_assert_cmpuint(webkit_context_menu_get_n_items(contextMenu), ==, menuSize + 1);
683
684         GRefPtr<WebKitContextMenu> subMenu2 = adoptGRef(webkit_context_menu_new());
685         GRefPtr<WebKitContextMenuItem> item = webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK);
686
687         // Add submenu to newly created item.
688         g_assert(!webkit_context_menu_item_get_submenu(item.get()));
689         webkit_context_menu_item_set_submenu(item.get(), subMenu2.get());
690         g_assert(webkit_context_menu_item_get_submenu(item.get()) == subMenu2.get());
691
692         // Replace the submenu.
693         webkit_context_menu_item_set_submenu(item.get(), 0);
694         g_assert(!webkit_context_menu_item_get_submenu(item.get()));
695
696         // Try to add a submenu already added to another item.
697         removeLogFatalFlag(G_LOG_LEVEL_WARNING);
698         webkit_context_menu_item_set_submenu(item.get(), subMenu.get());
699         addLogFatalFlag(G_LOG_LEVEL_WARNING);
700         g_assert(!webkit_context_menu_item_get_submenu(item.get()));
701
702         // A removed submenu shouldn't have a parent.
703         webkit_context_menu_item_set_submenu(item.get(), subMenu2.get());
704         g_assert(webkit_context_menu_item_get_submenu(item.get()) == subMenu2.get());
705
706         quitMainLoop();
707
708         return true;
709     }
710 };
711
712 static void testContextMenuSubMenu(ContextMenuSubmenuTest* test, gconstpointer)
713 {
714     test->showInWindowAndWaitUntilMapped();
715
716     test->loadHtml("<html><body>WebKitGTK+ Context menu tests</body></html>", "file:///");
717     test->waitUntilLoadFinished();
718
719     test->showContextMenuAndWaitUntilFinished();
720 }
721
722 class ContextMenuDismissedTest: public ContextMenuTest {
723 public:
724     MAKE_GLIB_TEST_FIXTURE(ContextMenuDismissedTest);
725
726     ContextMenuDismissedTest()
727         : m_dismissed(false)
728     {
729     }
730
731     bool contextMenu(WebKitContextMenu* contextMenu, GdkEvent*, WebKitHitTestResult*)
732     {
733         quitMainLoop();
734         // Show the default context menu.
735         return false;
736     }
737
738     void contextMenuDismissed()
739     {
740         m_dismissed = true;
741         ContextMenuTest::contextMenuDismissed();
742     }
743
744     void showContextMenuAndWaitUntilDismissed()
745     {
746         showContextMenuAndWaitUntilFinished();
747         dismissContextMenuAndWaitUntilFinished();
748     }
749
750     bool m_dismissed;
751 };
752
753 static void testContextMenuDismissed(ContextMenuDismissedTest* test, gconstpointer)
754 {
755     test->showInWindowAndWaitUntilMapped();
756
757     test->loadHtml("<html><body>WebKitGTK+ Context menu tests</body></html>", "file:///");
758     test->waitUntilLoadFinished();
759
760     test->showContextMenuAndWaitUntilDismissed();
761     g_assert(test->m_dismissed);
762 }
763
764 class ContextMenuSmartSeparatorsTest: public ContextMenuTest {
765 public:
766     MAKE_GLIB_TEST_FIXTURE(ContextMenuSmartSeparatorsTest);
767
768     bool contextMenu(WebKitContextMenu* contextMenu, GdkEvent*, WebKitHitTestResult*)
769     {
770         webkit_context_menu_remove_all(contextMenu);
771
772         webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator());
773         webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator());
774         webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_GO_BACK));
775         webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_GO_FORWARD));
776         webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator());
777         webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator());
778         webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_COPY));
779         webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator());
780         webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_INSPECT_ELEMENT));
781         webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator());
782         webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator());
783
784         quitMainLoop();
785
786         return false;
787     }
788
789     GtkMenu* showContextMenuAndGetGtkMenu()
790     {
791         showContextMenuAndWaitUntilFinished();
792         return getPopupMenu();
793     }
794 };
795
796 static void testContextMenuSmartSeparators(ContextMenuSmartSeparatorsTest* test, gconstpointer)
797 {
798     test->showInWindowAndWaitUntilMapped();
799
800     test->loadHtml("<html><body>WebKitGTK+ Context menu tests</body></html>", "file:///");
801     test->waitUntilLoadFinished();
802
803     GtkMenu* menu = test->showContextMenuAndGetGtkMenu();
804     g_assert(menu);
805
806     // Leading and trailing separators are not added to the context menu.
807     GUniquePtr<GList> menuItems(gtk_container_get_children(GTK_CONTAINER(menu)));
808     g_assert_cmpuint(g_list_length(menuItems.get()), ==, 6);
809     GtkWidget* item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 0));
810     g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
811     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 1));
812     g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
813     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 2));
814     g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
815     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 3));
816     g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
817     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 4));
818     g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
819     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 5));
820     g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
821
822     // Hiding a menu item between two separators hides the following separator.
823     GtkAction* action = gtk_activatable_get_related_action(GTK_ACTIVATABLE(g_list_nth_data(menuItems.get(), 3)));
824     gtk_action_set_visible(action, FALSE);
825     menuItems.reset(gtk_container_get_children(GTK_CONTAINER(menu)));
826     g_assert_cmpuint(g_list_length(menuItems.get()), ==, 6);
827     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 0));
828     g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
829     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 1));
830     g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
831     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 2));
832     g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
833     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 3));
834     g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && !gtk_widget_get_visible(item));
835     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 4));
836     g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && !gtk_widget_get_visible(item));
837     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 5));
838     g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
839     gtk_action_set_visible(action, TRUE);
840
841     // Showing an action between two separators shows the hidden separator.
842     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 0));
843     g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
844     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 1));
845     g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
846     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 2));
847     g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
848     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 3));
849     g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
850     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 4));
851     g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
852     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 5));
853     g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
854
855     // Trailing separators are hidden too.
856     action = gtk_activatable_get_related_action(GTK_ACTIVATABLE(g_list_nth_data(menuItems.get(), 5)));
857     gtk_action_set_visible(action, FALSE);
858     menuItems.reset(gtk_container_get_children(GTK_CONTAINER(menu)));
859     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 0));
860     g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
861     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 1));
862     g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
863     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 2));
864     g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
865     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 3));
866     g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item));
867     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 4));
868     g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && !gtk_widget_get_visible(item));
869     item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 5));
870     g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && !gtk_widget_get_visible(item));
871 }
872
873 class ContextMenuWebExtensionTest: public ContextMenuTest {
874 public:
875     MAKE_GLIB_TEST_FIXTURE(ContextMenuWebExtensionTest);
876
877     void deserializeContextMenuFromUserData(GVariant* userData)
878     {
879         m_actions.clear();
880         if (!userData)
881             return;
882
883         GVariantIter iter;
884         g_variant_iter_init(&iter, userData);
885         m_actions.reserveInitialCapacity(g_variant_iter_n_children(&iter));
886
887         uint32_t item;
888         while (g_variant_iter_next(&iter, "u", &item))
889             m_actions.uncheckedAppend(static_cast<WebKitContextMenuAction>(item));
890     }
891
892     bool contextMenu(WebKitContextMenu* menu, GdkEvent*, WebKitHitTestResult*)
893     {
894         deserializeContextMenuFromUserData(webkit_context_menu_get_user_data(menu));
895         GList* items = webkit_context_menu_get_items(menu);
896         g_assert_cmpuint(g_list_length(items), ==, m_actions.size());
897
898         unsigned actionIndex = 0;
899         for (GList* it = items; it; it = g_list_next(it)) {
900             WebKitContextMenuItem* item = WEBKIT_CONTEXT_MENU_ITEM(it->data);
901             g_assert_cmpuint(webkit_context_menu_item_get_stock_action(item), ==, m_actions[actionIndex++]);
902         }
903
904         quitMainLoop();
905
906         return true;
907     }
908
909     Vector<WebKitContextMenuAction> m_actions;
910 };
911
912 static void testContextMenuWebExtensionMenu(ContextMenuWebExtensionTest* test, gconstpointer)
913 {
914     test->showInWindowAndWaitUntilMapped();
915     test->loadHtml("<html><body>WebKitGTK+ Context menu tests<br>"
916         "<a style='position:absolute; left:1; top:10' href='http://www.webkitgtk.org'>WebKitGTK+ Website</a></body></html>",
917         "ContextMenuTestDefault");
918     test->waitUntilLoadFinished();
919
920     // Default context menu.
921     test->showContextMenuAtPositionAndWaitUntilFinished(1, 1);
922     g_assert_cmpuint(test->m_actions.size(), ==, 4);
923     g_assert_cmpuint(test->m_actions[0], ==, WEBKIT_CONTEXT_MENU_ACTION_GO_BACK);
924     g_assert_cmpuint(test->m_actions[1], ==, WEBKIT_CONTEXT_MENU_ACTION_GO_FORWARD);
925     g_assert_cmpuint(test->m_actions[2], ==, WEBKIT_CONTEXT_MENU_ACTION_STOP);
926     g_assert_cmpuint(test->m_actions[3], ==, WEBKIT_CONTEXT_MENU_ACTION_RELOAD);
927
928     // Link menu.
929     test->showContextMenuAtPositionAndWaitUntilFinished(1, 11);
930     g_assert_cmpuint(test->m_actions.size(), ==, 4);
931     g_assert_cmpuint(test->m_actions[0], ==, WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK);
932     g_assert_cmpuint(test->m_actions[1], ==, WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK_IN_NEW_WINDOW);
933     g_assert_cmpuint(test->m_actions[2], ==, WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_LINK_TO_DISK);
934     g_assert_cmpuint(test->m_actions[3], ==, WEBKIT_CONTEXT_MENU_ACTION_COPY_LINK_TO_CLIPBOARD);
935
936     // Custom menu.
937     test->loadHtml("<html><body></body></html>", "ContextMenuTestCustom");
938     test->showContextMenuAndWaitUntilFinished();
939     g_assert_cmpuint(test->m_actions.size(), ==, 4);
940     g_assert_cmpuint(test->m_actions[0], ==, WEBKIT_CONTEXT_MENU_ACTION_STOP);
941     g_assert_cmpuint(test->m_actions[1], ==, WEBKIT_CONTEXT_MENU_ACTION_RELOAD);
942     g_assert_cmpuint(test->m_actions[2], ==, WEBKIT_CONTEXT_MENU_ACTION_NO_ACTION);
943     g_assert_cmpuint(test->m_actions[3], ==, WEBKIT_CONTEXT_MENU_ACTION_INSPECT_ELEMENT);
944
945     // Menu cleared by the web process.
946     test->loadHtml("<html><body></body></html>", "ContextMenuTestClear");
947     test->showContextMenuAndWaitUntilFinished();
948     g_assert_cmpuint(test->m_actions.size(), ==, 0);
949 }
950
951 class ContextMenuWebExtensionNodeTest: public ContextMenuTest {
952 public:
953     MAKE_GLIB_TEST_FIXTURE(ContextMenuWebExtensionNodeTest);
954
955     struct Node {
956         enum {
957             NodeUnknown = 0,
958             NodeElement = 1,
959             NodeText = 3
960         };
961         typedef unsigned Type;
962
963         CString name;
964         Type type;
965         CString contents;
966         CString parentName;
967     };
968
969     void deserializeNodeFromUserData(GVariant* userData)
970     {
971         GVariantIter iter;
972         g_variant_iter_init(&iter, userData);
973
974         const char* key;
975         GVariant* value;
976         while (g_variant_iter_next(&iter, "{&sv}", &key, &value)) {
977             if (!strcmp(key, "Name") && g_variant_classify(value) == G_VARIANT_CLASS_STRING)
978                 m_node.name = g_variant_get_string(value, nullptr);
979             else if (!strcmp(key, "Type") && g_variant_classify(value) == G_VARIANT_CLASS_UINT32)
980                 m_node.type = g_variant_get_uint32(value);
981             else if (!strcmp(key, "Contents") && g_variant_classify(value) == G_VARIANT_CLASS_STRING)
982                 m_node.contents = g_variant_get_string(value, nullptr);
983             else if (!strcmp(key, "Parent") && g_variant_classify(value) == G_VARIANT_CLASS_STRING)
984                 m_node.parentName = g_variant_get_string(value, nullptr);
985             g_variant_unref(value);
986         }
987     }
988
989     bool contextMenu(WebKitContextMenu* menu, GdkEvent*, WebKitHitTestResult*)
990     {
991         deserializeNodeFromUserData(webkit_context_menu_get_user_data(menu));
992         quitMainLoop();
993
994         return true;
995     }
996
997     Node m_node;
998 };
999
1000 static void testContextMenuWebExtensionNode(ContextMenuWebExtensionNodeTest* test, gconstpointer)
1001 {
1002     test->showInWindowAndWaitUntilMapped();
1003     test->loadHtml("<html><body><p style='position:absolute; left:1; top:1'>WebKitGTK+ Context menu tests</p><br>"
1004         "<a style='position:absolute; left:1; top:100' href='http://www.webkitgtk.org'>WebKitGTK+ Website</a></body></html>",
1005         "ContextMenuTestNode");
1006     test->waitUntilLoadFinished();
1007
1008     test->showContextMenuAtPositionAndWaitUntilFinished(0, 0);
1009     g_assert_cmpstr(test->m_node.name.data(), ==, "HTML");
1010     g_assert_cmpuint(test->m_node.type, ==, ContextMenuWebExtensionNodeTest::Node::NodeElement);
1011     g_assert_cmpstr(test->m_node.contents.data(), ==, "WebKitGTK+ Context menu testsWebKitGTK+ Website");
1012     g_assert_cmpstr(test->m_node.parentName.data(), ==, "#document");
1013
1014     test->showContextMenuAtPositionAndWaitUntilFinished(1, 20);
1015     g_assert_cmpstr(test->m_node.name.data(), ==, "#text");
1016     g_assert_cmpuint(test->m_node.type, ==, ContextMenuWebExtensionNodeTest::Node::NodeText);
1017     g_assert_cmpstr(test->m_node.contents.data(), ==, "WebKitGTK+ Context menu tests");
1018     g_assert_cmpstr(test->m_node.parentName.data(), ==, "P");
1019
1020     // Link menu.
1021     test->showContextMenuAtPositionAndWaitUntilFinished(1, 101);
1022     g_assert_cmpstr(test->m_node.name.data(), ==, "#text");
1023     g_assert_cmpuint(test->m_node.type, ==, ContextMenuWebExtensionNodeTest::Node::NodeText);
1024     g_assert_cmpstr(test->m_node.contents.data(), ==, "WebKitGTK+ Website");
1025     g_assert_cmpstr(test->m_node.parentName.data(), ==, "A");
1026 }
1027
1028 void beforeAll()
1029 {
1030     ContextMenuDefaultTest::add("WebKitWebView", "default-menu", testContextMenuDefaultMenu);
1031     ContextMenuCustomTest::add("WebKitWebView", "populate-menu", testContextMenuPopulateMenu);
1032     ContextMenuCustomFullTest::add("WebKitWebView", "custom-menu", testContextMenuCustomMenu);
1033     ContextMenuDisabledTest::add("WebKitWebView", "disable-menu", testContextMenuDisableMenu);
1034     ContextMenuSubmenuTest::add("WebKitWebView", "submenu", testContextMenuSubMenu);
1035     ContextMenuDismissedTest::add("WebKitWebView", "menu-dismissed", testContextMenuDismissed);
1036     ContextMenuSmartSeparatorsTest::add("WebKitWebView", "smart-separators", testContextMenuSmartSeparators);
1037     ContextMenuWebExtensionTest::add("WebKitWebPage", "context-menu", testContextMenuWebExtensionMenu);
1038     ContextMenuWebExtensionNodeTest::add("WebKitWebPage", "context-menu-node", testContextMenuWebExtensionNode);
1039 }
1040
1041 void afterAll()
1042 {
1043 }