1/*
2 * Copyright (C) 2017 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
23#include <wtf/RunLoop.h>
24
25class OptionMenuTest : public WebViewTest {
26public:
27 MAKE_GLIB_TEST_FIXTURE(OptionMenuTest);
28
29 OptionMenuTest()
30 {
31 g_signal_connect(m_webView, "show-option-menu", G_CALLBACK(showOptionMenuCallback), this);
32 }
33
34 ~OptionMenuTest()
35 {
36 g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
37 if (m_menu)
38 close();
39 }
40
41 void destroyMenu()
42 {
43 if (!m_menu)
44 return;
45
46 g_signal_handlers_disconnect_matched(m_menu.get(), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
47 m_menu = nullptr;
48 }
49
50 static gboolean showOptionMenuCallback(WebKitWebView* webView, WebKitOptionMenu* menu, GdkEvent* event, GdkRectangle* rect, OptionMenuTest* test)
51 {
52 g_assert_true(test->m_webView == webView);
53 g_assert_nonnull(rect);
54 g_assert_true(WEBKIT_IS_OPTION_MENU(menu));
55 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(menu));
56 test->showOptionMenu(menu, rect);
57 return TRUE;
58 }
59
60 static void menuCloseCallback(WebKitOptionMenu* menu, OptionMenuTest* test)
61 {
62 g_assert_true(test->m_menu.get() == menu);
63 test->destroyMenu();
64 }
65
66 void showOptionMenu(WebKitOptionMenu* menu, GdkRectangle* rect)
67 {
68 m_rectangle = *rect;
69 m_menu = menu;
70 g_signal_connect(m_menu.get(), "close", G_CALLBACK(menuCloseCallback), this);
71 g_main_loop_quit(m_mainLoop);
72 }
73
74 void clickAtPositionAndWaitUntilOptionMenuShown(int x, int y)
75 {
76 m_menu = nullptr;
77 RunLoop::main().dispatch([this, x, y] { clickMouseButton(x, y); });
78 g_main_loop_run(m_mainLoop);
79 }
80
81 void close()
82 {
83 g_assert_nonnull(m_menu.get());
84 webkit_option_menu_close(m_menu.get());
85 g_assert_null(m_menu.get());
86 }
87
88 void activateItem(unsigned item)
89 {
90 g_assert_nonnull(m_menu.get());
91 webkit_option_menu_activate_item(m_menu.get(), item);
92 g_assert_nonnull(m_menu.get());
93 }
94
95 void selectItem(unsigned item)
96 {
97 g_assert_nonnull(m_menu.get());
98 webkit_option_menu_select_item(m_menu.get(), item);
99 g_assert_nonnull(m_menu.get());
100 }
101
102 GRefPtr<WebKitOptionMenu> m_menu;
103 GdkRectangle m_rectangle;
104};
105
106static void testOptionMenuSimple(OptionMenuTest* test, gconstpointer)
107{
108 static const char html[] =
109 "<html><body>"
110 " <select style='position:absolute; left:1; top:10'>"
111 " <option title='The Foo Option'>Foo</option>"
112 " <option selected>Bar</option>"
113 " <option disabled>Baz</option>"
114 " </select></body></html>";
115 test->showInWindowAndWaitUntilMapped();
116 test->loadHtml(html, nullptr);
117 test->waitUntilLoadFinished();
118
119 test->clickAtPositionAndWaitUntilOptionMenuShown(5, 15);
120 g_assert_true(WEBKIT_IS_OPTION_MENU(test->m_menu.get()));
121 g_assert_cmpint(webkit_option_menu_get_n_items(test->m_menu.get()), ==, 3);
122 auto* item = webkit_option_menu_get_item(test->m_menu.get(), 0);
123 g_assert_cmpstr(webkit_option_menu_item_get_label(item), ==, "Foo");
124 g_assert_cmpstr(webkit_option_menu_item_get_tooltip(item), ==, "The Foo Option");
125 g_assert_false(webkit_option_menu_item_is_group_label(item));
126 g_assert_false(webkit_option_menu_item_is_group_child(item));
127 g_assert_true(webkit_option_menu_item_is_enabled(item));
128 g_assert_false(webkit_option_menu_item_is_selected(item));
129 item = webkit_option_menu_get_item(test->m_menu.get(), 1);
130 g_assert_cmpstr(webkit_option_menu_item_get_label(item), ==, "Bar");
131 g_assert_null(webkit_option_menu_item_get_tooltip(item));
132 g_assert_false(webkit_option_menu_item_is_group_label(item));
133 g_assert_false(webkit_option_menu_item_is_group_child(item));
134 g_assert_true(webkit_option_menu_item_is_enabled(item));
135 g_assert_true(webkit_option_menu_item_is_selected(item));
136 item = webkit_option_menu_get_item(test->m_menu.get(), 2);
137 g_assert_cmpstr(webkit_option_menu_item_get_label(item), ==, "Baz");
138 g_assert_null(webkit_option_menu_item_get_tooltip(item));
139 g_assert_false(webkit_option_menu_item_is_group_label(item));
140 g_assert_false(webkit_option_menu_item_is_group_child(item));
141 g_assert_false(webkit_option_menu_item_is_enabled(item));
142 g_assert_false(webkit_option_menu_item_is_selected(item));
143
144 test->close();
145 g_assert_null(test->m_menu.get());
146}
147
148static void testOptionMenuGroups(OptionMenuTest* test, gconstpointer)
149{
150 static const char html[] =
151 "<html><body>"
152 " <select style='position:absolute; left:1; top:10'>"
153 " <option>Root</option>"
154 " <optgroup label='Group 1'>"
155 " <option>Child 1-1</option>"
156 " <option disabled>Child 1-2</option>"
157 " </optgroup>"
158 " <optgroup label='Group 2'>"
159 " <option selected>Child 2-1</option>"
160 " <option>Child 2-2</option>"
161 " </optgroup>"
162 " <option>Tail</option>"
163 " </select></body></html>";
164 test->showInWindowAndWaitUntilMapped();
165 test->loadHtml(html, nullptr);
166 test->waitUntilLoadFinished();
167
168 test->clickAtPositionAndWaitUntilOptionMenuShown(5, 15);
169 g_assert_true(WEBKIT_IS_OPTION_MENU(test->m_menu.get()));
170 g_assert_cmpint(webkit_option_menu_get_n_items(test->m_menu.get()), ==, 8);
171 auto* item = webkit_option_menu_get_item(test->m_menu.get(), 0);
172 g_assert_cmpstr(webkit_option_menu_item_get_label(item), ==, "Root");
173 g_assert_null(webkit_option_menu_item_get_tooltip(item));
174 g_assert_false(webkit_option_menu_item_is_group_label(item));
175 g_assert_false(webkit_option_menu_item_is_group_child(item));
176 g_assert_true(webkit_option_menu_item_is_enabled(item));
177 g_assert_false(webkit_option_menu_item_is_selected(item));
178 item = webkit_option_menu_get_item(test->m_menu.get(), 1);
179 g_assert_cmpstr(webkit_option_menu_item_get_label(item), ==, "Group 1");
180 g_assert_null(webkit_option_menu_item_get_tooltip(item));
181 g_assert_true(webkit_option_menu_item_is_group_label(item));
182 g_assert_false(webkit_option_menu_item_is_group_child(item));
183 g_assert_false(webkit_option_menu_item_is_enabled(item));
184 g_assert_false(webkit_option_menu_item_is_selected(item));
185 item = webkit_option_menu_get_item(test->m_menu.get(), 2);
186 g_assert_cmpstr(webkit_option_menu_item_get_label(item), ==, "Child 1-1");
187 g_assert_null(webkit_option_menu_item_get_tooltip(item));
188 g_assert_false(webkit_option_menu_item_is_group_label(item));
189 g_assert_true(webkit_option_menu_item_is_group_child(item));
190 g_assert_true(webkit_option_menu_item_is_enabled(item));
191 g_assert_false(webkit_option_menu_item_is_selected(item));
192 item = webkit_option_menu_get_item(test->m_menu.get(), 3);
193 g_assert_cmpstr(webkit_option_menu_item_get_label(item), ==, "Child 1-2");
194 g_assert_null(webkit_option_menu_item_get_tooltip(item));
195 g_assert_false(webkit_option_menu_item_is_group_label(item));
196 g_assert_true(webkit_option_menu_item_is_group_child(item));
197 g_assert_false(webkit_option_menu_item_is_enabled(item));
198 g_assert_false(webkit_option_menu_item_is_selected(item));
199 item = webkit_option_menu_get_item(test->m_menu.get(), 4);
200 g_assert_cmpstr(webkit_option_menu_item_get_label(item), ==, "Group 2");
201 g_assert_null(webkit_option_menu_item_get_tooltip(item));
202 g_assert_true(webkit_option_menu_item_is_group_label(item));
203 g_assert_false(webkit_option_menu_item_is_group_child(item));
204 g_assert_false(webkit_option_menu_item_is_enabled(item));
205 g_assert_false(webkit_option_menu_item_is_selected(item));
206 item = webkit_option_menu_get_item(test->m_menu.get(), 5);
207 g_assert_cmpstr(webkit_option_menu_item_get_label(item), ==, "Child 2-1");
208 g_assert_null(webkit_option_menu_item_get_tooltip(item));
209 g_assert_false(webkit_option_menu_item_is_group_label(item));
210 g_assert_true(webkit_option_menu_item_is_group_child(item));
211 g_assert_true(webkit_option_menu_item_is_enabled(item));
212 g_assert_true(webkit_option_menu_item_is_selected(item));
213 item = webkit_option_menu_get_item(test->m_menu.get(), 6);
214 g_assert_cmpstr(webkit_option_menu_item_get_label(item), ==, "Child 2-2");
215 g_assert_null(webkit_option_menu_item_get_tooltip(item));
216 g_assert_false(webkit_option_menu_item_is_group_label(item));
217 g_assert_true(webkit_option_menu_item_is_group_child(item));
218 g_assert_true(webkit_option_menu_item_is_enabled(item));
219 g_assert_false(webkit_option_menu_item_is_selected(item));
220 item = webkit_option_menu_get_item(test->m_menu.get(), 7);
221 g_assert_cmpstr(webkit_option_menu_item_get_label(item), ==, "Tail");
222 g_assert_null(webkit_option_menu_item_get_tooltip(item));
223 g_assert_false(webkit_option_menu_item_is_group_label(item));
224 g_assert_false(webkit_option_menu_item_is_group_child(item));
225 g_assert_true(webkit_option_menu_item_is_enabled(item));
226 g_assert_false(webkit_option_menu_item_is_selected(item));
227}
228
229static void testOptionMenuActivate(OptionMenuTest* test, gconstpointer)
230{
231 static const char html[] =
232 "<html><body>"
233 " <select id='combo' style='position:absolute; left:1; top:10'>"
234 " <option>Foo</option>"
235 " <option>Bar</option>"
236 " <option>Baz</option>"
237 " </select></body></html>";
238 test->showInWindowAndWaitUntilMapped();
239 test->loadHtml(html, nullptr);
240 test->waitUntilLoadFinished();
241
242 test->clickAtPositionAndWaitUntilOptionMenuShown(5, 15);
243 g_assert_true(WEBKIT_IS_OPTION_MENU(test->m_menu.get()));
244 g_assert_cmpint(webkit_option_menu_get_n_items(test->m_menu.get()), ==, 3);
245 test->activateItem(1);
246 auto* result = test->runJavaScriptAndWaitUntilFinished("document.getElementById('combo').selectedIndex", nullptr);
247 g_assert_nonnull(result);
248 g_assert_cmpfloat(WebViewTest::javascriptResultToNumber(result), ==, 1);
249
250 // We should close the menu after activate, further activates will be ignored.
251 test->activateItem(2);
252 result = test->runJavaScriptAndWaitUntilFinished("document.getElementById('combo').selectedIndex", nullptr);
253 g_assert_nonnull(result);
254 g_assert_cmpfloat(WebViewTest::javascriptResultToNumber(result), ==, 1);
255
256 test->close();
257}
258
259static void testOptionMenuSelect(OptionMenuTest* test, gconstpointer)
260{
261 static const char html[] =
262 "<html><body>"
263 " <select id='combo' style='position:absolute; left:1; top:10'>"
264 " <option>Foo</option>"
265 " <option>Bar</option>"
266 " <option>Baz</option>"
267 " </select></body></html>";
268 test->showInWindowAndWaitUntilMapped();
269 test->loadHtml(html, nullptr);
270 test->waitUntilLoadFinished();
271
272 test->clickAtPositionAndWaitUntilOptionMenuShown(5, 15);
273 g_assert_true(WEBKIT_IS_OPTION_MENU(test->m_menu.get()));
274 g_assert_cmpint(webkit_option_menu_get_n_items(test->m_menu.get()), ==, 3);
275
276 // Select item changes the combo text, but not the currently selected item.
277 test->selectItem(2);
278 auto* result = test->runJavaScriptAndWaitUntilFinished("document.getElementById('combo').selectedIndex", nullptr);
279 g_assert_nonnull(result);
280 g_assert_cmpfloat(WebViewTest::javascriptResultToNumber(result), ==, 0);
281
282 // It can be called multiple times.
283 test->selectItem(1);
284 result = test->runJavaScriptAndWaitUntilFinished("document.getElementById('combo').selectedIndex", nullptr);
285 g_assert_nonnull(result);
286 g_assert_cmpfloat(WebViewTest::javascriptResultToNumber(result), ==, 0);
287
288 // And closing the menu activates the currently selected item.
289 test->close();
290 result = test->runJavaScriptAndWaitUntilFinished("document.getElementById('combo').selectedIndex", nullptr);
291 g_assert_nonnull(result);
292 g_assert_cmpfloat(WebViewTest::javascriptResultToNumber(result), ==, 1);
293}
294
295void beforeAll()
296{
297 OptionMenuTest::add("WebKitWebView", "option-menu-simple", testOptionMenuSimple);
298 OptionMenuTest::add("WebKitWebView", "option-menu-groups", testOptionMenuGroups);
299 OptionMenuTest::add("WebKitWebView", "option-menu-activate", testOptionMenuActivate);
300 OptionMenuTest::add("WebKitWebView", "option-menu-select", testOptionMenuSelect);
301}
302
303void afterAll()
304{
305}
306