|
1 /* |
|
2 * Copyright (C) 2010 Google Inc. All rights reserved. |
|
3 * |
|
4 * Redistribution and use in source and binary forms, with or without |
|
5 * modification, are permitted provided that the following conditions are |
|
6 * met: |
|
7 * |
|
8 * * Redistributions of source code must retain the above copyright |
|
9 * notice, this list of conditions and the following disclaimer. |
|
10 * * Redistributions in binary form must reproduce the above |
|
11 * copyright notice, this list of conditions and the following disclaimer |
|
12 * in the documentation and/or other materials provided with the |
|
13 * distribution. |
|
14 * * Neither the name of Google Inc. nor the names of its |
|
15 * contributors may be used to endorse or promote products derived from |
|
16 * this software without specific prior written permission. |
|
17 * |
|
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
29 */ |
|
30 |
|
31 #include "config.h" |
|
32 |
|
33 #include <gtest/gtest.h> |
|
34 |
|
35 #include "Color.h" |
|
36 #include "KeyboardCodes.h" |
|
37 #include "PopupMenu.h" |
|
38 #include "PopupMenuClient.h" |
|
39 #include "PopupMenuChromium.h" |
|
40 #include "WebFrameClient.h" |
|
41 #include "WebFrameImpl.h" |
|
42 #include "WebInputEvent.h" |
|
43 #include "WebPopupMenuImpl.h" |
|
44 #include "WebScreenInfo.h" |
|
45 #include "WebViewClient.h" |
|
46 #include "WebViewImpl.h" |
|
47 |
|
48 using namespace WebCore; |
|
49 using namespace WebKit; |
|
50 |
|
51 namespace { |
|
52 |
|
53 class TestPopupMenuClient : public PopupMenuClient { |
|
54 public: |
|
55 // Item at index 0 is selected by default. |
|
56 TestPopupMenuClient() : m_selectIndex(0) { } |
|
57 virtual ~TestPopupMenuClient() {} |
|
58 virtual void valueChanged(unsigned listIndex, bool fireEvents = true) |
|
59 { |
|
60 m_selectIndex = listIndex; |
|
61 } |
|
62 virtual void selectionChanged(unsigned, bool) {} |
|
63 virtual void selectionCleared() {} |
|
64 |
|
65 virtual String itemText(unsigned listIndex) const |
|
66 { |
|
67 String str("Item "); |
|
68 str.append(String::number(listIndex)); |
|
69 return str; |
|
70 } |
|
71 virtual String itemLabel(unsigned) const { return String(); } |
|
72 virtual String itemToolTip(unsigned listIndex) const { return itemText(listIndex); } |
|
73 virtual String itemAccessibilityText(unsigned listIndex) const { return itemText(listIndex); } |
|
74 virtual bool itemIsEnabled(unsigned listIndex) const { return true; } |
|
75 virtual PopupMenuStyle itemStyle(unsigned listIndex) const |
|
76 { |
|
77 Font font(FontPlatformData(12.0, false, false), false); |
|
78 return PopupMenuStyle(Color::black, Color::white, font, true, Length(), TextDirection()); |
|
79 } |
|
80 virtual PopupMenuStyle menuStyle() const { return itemStyle(0); } |
|
81 virtual int clientInsetLeft() const { return 0; } |
|
82 virtual int clientInsetRight() const { return 0; } |
|
83 virtual int clientPaddingLeft() const { return 0; } |
|
84 virtual int clientPaddingRight() const { return 0; } |
|
85 virtual int listSize() const { return 10; } |
|
86 virtual int selectedIndex() const { return m_selectIndex; } |
|
87 virtual void popupDidHide() { } |
|
88 virtual bool itemIsSeparator(unsigned listIndex) const { return false; } |
|
89 virtual bool itemIsLabel(unsigned listIndex) const { return false; } |
|
90 virtual bool itemIsSelected(unsigned listIndex) const { return listIndex == m_selectIndex; } |
|
91 virtual bool shouldPopOver() const { return false; } |
|
92 virtual bool valueShouldChangeOnHotTrack() const { return false; } |
|
93 virtual void setTextFromItem(unsigned listIndex) { } |
|
94 |
|
95 virtual FontSelector* fontSelector() const { return 0; } |
|
96 virtual HostWindow* hostWindow() const { return 0; } |
|
97 |
|
98 virtual PassRefPtr<Scrollbar> createScrollbar(ScrollbarClient*, ScrollbarOrientation, ScrollbarControlSize) { return 0; } |
|
99 |
|
100 private: |
|
101 unsigned m_selectIndex; |
|
102 }; |
|
103 |
|
104 class TestWebWidgetClient : public WebWidgetClient { |
|
105 public: |
|
106 ~TestWebWidgetClient() { } |
|
107 }; |
|
108 |
|
109 class TestWebPopupMenuImpl : public WebPopupMenuImpl { |
|
110 public: |
|
111 static PassRefPtr<TestWebPopupMenuImpl> create(WebWidgetClient* client) |
|
112 { |
|
113 return adoptRef(new TestWebPopupMenuImpl(client)); |
|
114 } |
|
115 |
|
116 ~TestWebPopupMenuImpl() { } |
|
117 |
|
118 private: |
|
119 TestWebPopupMenuImpl(WebWidgetClient* client) : WebPopupMenuImpl(client) { } |
|
120 }; |
|
121 |
|
122 class TestWebWidget : public WebWidget { |
|
123 public: |
|
124 virtual ~TestWebWidget() { } |
|
125 virtual void close() { } |
|
126 virtual WebSize size() { return WebSize(100, 100); } |
|
127 virtual void resize(const WebSize&) { } |
|
128 virtual void layout() { } |
|
129 virtual void paint(WebCanvas*, const WebRect&) { } |
|
130 virtual bool handleInputEvent(const WebInputEvent&) { return true; } |
|
131 virtual void mouseCaptureLost() { } |
|
132 virtual void setFocus(bool) { } |
|
133 virtual bool setComposition( |
|
134 const WebString& text, |
|
135 const WebVector<WebCompositionUnderline>& underlines, |
|
136 int selectionStart, |
|
137 int selectionEnd) { return true; } |
|
138 virtual bool confirmComposition() { return true; } |
|
139 virtual WebTextInputType textInputType() { return WebKit::WebTextInputTypeNone; } |
|
140 virtual WebRect caretOrSelectionBounds() { return WebRect(); } |
|
141 virtual void setTextDirection(WebTextDirection) { } |
|
142 }; |
|
143 |
|
144 class TestWebViewClient : public WebViewClient { |
|
145 public: |
|
146 TestWebViewClient() : m_webPopupMenu(TestWebPopupMenuImpl::create(&m_webWidgetClient)) { } |
|
147 ~TestWebViewClient() { } |
|
148 |
|
149 virtual WebWidget* createPopupMenu(WebPopupType) { return m_webPopupMenu.get(); } |
|
150 |
|
151 // We need to override this so that the popup menu size is not 0 |
|
152 // (the layout code checks to see if the popup fits on the screen). |
|
153 virtual WebScreenInfo screenInfo() |
|
154 { |
|
155 WebScreenInfo screenInfo; |
|
156 screenInfo.availableRect.height = 2000; |
|
157 screenInfo.availableRect.width = 2000; |
|
158 return screenInfo; |
|
159 } |
|
160 |
|
161 private: |
|
162 TestWebWidgetClient m_webWidgetClient; |
|
163 RefPtr<TestWebPopupMenuImpl> m_webPopupMenu; |
|
164 }; |
|
165 |
|
166 class TestWebFrameClient : public WebFrameClient { |
|
167 public: |
|
168 ~TestWebFrameClient() { } |
|
169 }; |
|
170 |
|
171 class SelectPopupMenuTest : public testing::Test { |
|
172 public: |
|
173 SelectPopupMenuTest() |
|
174 { |
|
175 } |
|
176 |
|
177 protected: |
|
178 virtual void SetUp() |
|
179 { |
|
180 m_webView = static_cast<WebViewImpl*>(WebView::create(&m_webviewClient, 0)); |
|
181 m_webView->initializeMainFrame(&m_webFrameClient); |
|
182 m_popupMenu = PopupMenu::create(&m_popupMenuClient); |
|
183 } |
|
184 |
|
185 virtual void TearDown() |
|
186 { |
|
187 m_popupMenu = 0; |
|
188 m_webView->close(); |
|
189 } |
|
190 |
|
191 // Returns true if there currently is a select popup in the WebView. |
|
192 bool popupOpen() const { return m_webView->selectPopup(); } |
|
193 |
|
194 int selectedIndex() const { return m_popupMenuClient.selectedIndex(); } |
|
195 |
|
196 void showPopup() |
|
197 { |
|
198 m_popupMenu->show(IntRect(0, 0, 100, 100), |
|
199 static_cast<WebFrameImpl*>(m_webView->mainFrame())->frameView(), 0); |
|
200 ASSERT_TRUE(popupOpen()); |
|
201 EXPECT_TRUE(m_webView->selectPopup()->popupType() == PopupContainer::Select); |
|
202 } |
|
203 |
|
204 void hidePopup() |
|
205 { |
|
206 m_popupMenu->hide(); |
|
207 EXPECT_FALSE(popupOpen()); |
|
208 } |
|
209 |
|
210 void simulateKeyDownEvent(int keyCode) |
|
211 { |
|
212 simulateKeyEvent(WebInputEvent::RawKeyDown, keyCode); |
|
213 } |
|
214 |
|
215 void simulateKeyUpEvent(int keyCode) |
|
216 { |
|
217 simulateKeyEvent(WebInputEvent::KeyUp, keyCode); |
|
218 } |
|
219 |
|
220 // Simulates a key event on the WebView. |
|
221 // The WebView forwards the event to the select popup if one is open. |
|
222 void simulateKeyEvent(WebInputEvent::Type eventType, int keyCode) |
|
223 { |
|
224 WebKeyboardEvent keyEvent; |
|
225 keyEvent.windowsKeyCode = keyCode; |
|
226 keyEvent.type = eventType; |
|
227 m_webView->handleInputEvent(keyEvent); |
|
228 } |
|
229 |
|
230 // Simulates a mouse event on the select popup. |
|
231 void simulateLeftMouseDownEvent(const IntPoint& point) |
|
232 { |
|
233 PlatformMouseEvent mouseEvent(point, point, LeftButton, MouseEventPressed, |
|
234 1, false, false, false, false, 0); |
|
235 m_webView->selectPopup()->handleMouseDownEvent(mouseEvent); |
|
236 } |
|
237 void simulateLeftMouseUpEvent(const IntPoint& point) |
|
238 { |
|
239 PlatformMouseEvent mouseEvent(point, point, LeftButton, MouseEventReleased, |
|
240 1, false, false, false, false, 0); |
|
241 m_webView->selectPopup()->handleMouseReleaseEvent(mouseEvent); |
|
242 } |
|
243 |
|
244 protected: |
|
245 TestWebViewClient m_webviewClient; |
|
246 WebViewImpl* m_webView; |
|
247 TestWebFrameClient m_webFrameClient; |
|
248 TestPopupMenuClient m_popupMenuClient; |
|
249 RefPtr<PopupMenu> m_popupMenu; |
|
250 }; |
|
251 |
|
252 // Tests that show/hide and repeats. Select popups are reused in web pages when |
|
253 // they are reopened, that what this is testing. |
|
254 TEST_F(SelectPopupMenuTest, ShowThenHide) |
|
255 { |
|
256 for (int i = 0; i < 3; i++) { |
|
257 showPopup(); |
|
258 hidePopup(); |
|
259 } |
|
260 } |
|
261 |
|
262 // Tests that showing a select popup and deleting it does not cause problem. |
|
263 // This happens in real-life if a page navigates while a select popup is showing. |
|
264 TEST_F(SelectPopupMenuTest, ShowThenDelete) |
|
265 { |
|
266 showPopup(); |
|
267 // Nothing else to do, TearDown() deletes the popup. |
|
268 } |
|
269 |
|
270 // Tests that losing focus closes the select popup. |
|
271 TEST_F(SelectPopupMenuTest, ShowThenLoseFocus) |
|
272 { |
|
273 showPopup(); |
|
274 // Simulate losing focus. |
|
275 m_webView->setFocus(false); |
|
276 |
|
277 // Popup should have closed. |
|
278 EXPECT_FALSE(popupOpen()); |
|
279 } |
|
280 |
|
281 // Tests that pressing ESC closes the popup. |
|
282 TEST_F(SelectPopupMenuTest, ShowThenPressESC) |
|
283 { |
|
284 showPopup(); |
|
285 simulateKeyDownEvent(VKEY_ESCAPE); |
|
286 // Popup should have closed. |
|
287 EXPECT_FALSE(popupOpen()); |
|
288 } |
|
289 |
|
290 // Tests selecting an item with the arrows and enter/esc/tab. |
|
291 TEST_F(SelectPopupMenuTest, SelectWithKeys) |
|
292 { |
|
293 showPopup(); |
|
294 // Simulate selecting the 2nd item by pressing Down, Down, enter. |
|
295 simulateKeyDownEvent(VKEY_DOWN); |
|
296 simulateKeyDownEvent(VKEY_DOWN); |
|
297 simulateKeyDownEvent(VKEY_RETURN); |
|
298 |
|
299 // Popup should have closed. |
|
300 EXPECT_TRUE(!popupOpen()); |
|
301 EXPECT_EQ(2, selectedIndex()); |
|
302 |
|
303 // It should work as well with ESC. |
|
304 showPopup(); |
|
305 simulateKeyDownEvent(VKEY_DOWN); |
|
306 simulateKeyDownEvent(VKEY_ESCAPE); |
|
307 EXPECT_FALSE(popupOpen()); |
|
308 EXPECT_EQ(3, selectedIndex()); |
|
309 |
|
310 // It should work as well with TAB. |
|
311 showPopup(); |
|
312 simulateKeyDownEvent(VKEY_DOWN); |
|
313 simulateKeyDownEvent(VKEY_TAB); |
|
314 EXPECT_FALSE(popupOpen()); |
|
315 EXPECT_EQ(4, selectedIndex()); |
|
316 } |
|
317 |
|
318 // Tests that selecting an item with the mouse does select the item and close |
|
319 // the popup. |
|
320 TEST_F(SelectPopupMenuTest, ClickItem) |
|
321 { |
|
322 showPopup(); |
|
323 |
|
324 // Y of 18 to be on the item at index 1 (12 font plus border and more to be safe). |
|
325 IntPoint row1Point(2, 18); |
|
326 // Simulate a click down/up on the first item. |
|
327 simulateLeftMouseDownEvent(row1Point); |
|
328 simulateLeftMouseUpEvent(row1Point); |
|
329 |
|
330 // Popup should have closed and the item at index 1 selected. |
|
331 EXPECT_FALSE(popupOpen()); |
|
332 EXPECT_EQ(1, selectedIndex()); |
|
333 } |
|
334 |
|
335 // Tests that moving the mouse over an item and then clicking outside the select popup |
|
336 // leaves the seleted item unchanged. |
|
337 TEST_F(SelectPopupMenuTest, MouseOverItemClickOutside) |
|
338 { |
|
339 showPopup(); |
|
340 |
|
341 // Y of 18 to be on the item at index 1 (12 font plus border and more to be safe). |
|
342 IntPoint row1Point(2, 18); |
|
343 // Simulate the mouse moving over the first item. |
|
344 PlatformMouseEvent mouseEvent(row1Point, row1Point, NoButton, MouseEventMoved, |
|
345 1, false, false, false, false, 0); |
|
346 m_webView->selectPopup()->handleMouseMoveEvent(mouseEvent); |
|
347 |
|
348 // Click outside the popup. |
|
349 simulateLeftMouseDownEvent(IntPoint(1000, 1000)); |
|
350 |
|
351 // Popup should have closed and item 0 should still be selected. |
|
352 EXPECT_FALSE(popupOpen()); |
|
353 EXPECT_EQ(0, selectedIndex()); |
|
354 } |
|
355 |
|
356 // Tests that selecting an item with the keyboard and then clicking outside the select |
|
357 // popup does select that item. |
|
358 TEST_F(SelectPopupMenuTest, SelectItemWithKeyboardItemClickOutside) |
|
359 { |
|
360 showPopup(); |
|
361 |
|
362 // Simulate selecting the 2nd item by pressing Down, Down. |
|
363 simulateKeyDownEvent(VKEY_DOWN); |
|
364 simulateKeyDownEvent(VKEY_DOWN); |
|
365 |
|
366 // Click outside the popup. |
|
367 simulateLeftMouseDownEvent(IntPoint(1000, 1000)); |
|
368 |
|
369 // Popup should have closed and the item should have been selected. |
|
370 EXPECT_FALSE(popupOpen()); |
|
371 EXPECT_EQ(2, selectedIndex()); |
|
372 } |
|
373 |
|
374 } // namespace |