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