initial import
[vuplus_webkit] / Source / WebKit / chromium / tests / PopupMenuTest.cpp
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 #include <webkit/support/webkit_support.h>
35
36 #include "Color.h"
37 #include "Element.h"
38 #include "FrameView.h"
39 #include "KeyboardCodes.h"
40 #include "PopupContainer.h"
41 #include "PopupMenu.h"
42 #include "PopupMenuClient.h"
43 #include "PopupMenuChromium.h"
44 #include "SelectElement.h"
45 #include "WebDocument.h"
46 #include "WebElement.h"
47 #include "WebFrame.h"
48 #include "WebFrameClient.h"
49 #include "WebFrameImpl.h"
50 #include "WebInputEvent.h"
51 #include "WebPopupMenuImpl.h"
52 #include "WebScreenInfo.h"
53 #include "WebSettings.h"
54 #include "WebString.h"
55 #include "WebURL.h"
56 #include "WebURLRequest.h"
57 #include "WebURLResponse.h"
58 #include "WebView.h"
59 #include "WebViewClient.h"
60 #include "WebViewImpl.h"
61 #include "v8.h"
62
63 using namespace WebCore;
64 using namespace WebKit;
65
66 namespace {
67
68 class TestPopupMenuClient : public PopupMenuClient {
69 public:
70     // Item at index 0 is selected by default.
71     TestPopupMenuClient() : m_selectIndex(0), m_node(0) { }
72     virtual ~TestPopupMenuClient() {}
73     virtual void valueChanged(unsigned listIndex, bool fireEvents = true)
74     {
75         m_selectIndex = listIndex;
76         if (m_node) {
77             SelectElement* select = toSelectElement(static_cast<Element*>(m_node));
78             select->setSelectedIndexByUser(select->listToOptionIndex(listIndex), true, fireEvents);
79         }
80     }
81     virtual void selectionChanged(unsigned, bool) {}
82     virtual void selectionCleared() {}
83
84     virtual String itemText(unsigned listIndex) const
85     {
86         String str("Item ");
87         str.append(String::number(listIndex));
88         return str;
89     }
90     virtual String itemLabel(unsigned) const { return String(); }
91     virtual String itemIcon(unsigned) const { return String(); }
92     virtual String itemToolTip(unsigned listIndex) const { return itemText(listIndex); }
93     virtual String itemAccessibilityText(unsigned listIndex) const { return itemText(listIndex); }
94     virtual bool itemIsEnabled(unsigned listIndex) const { return m_disabledIndexSet.find(listIndex) == m_disabledIndexSet.end(); }
95     virtual PopupMenuStyle itemStyle(unsigned listIndex) const
96     {
97         Font font(FontPlatformData(12.0, false, false), false);
98         return PopupMenuStyle(Color::black, Color::white, font, true, false, Length(), TextDirection(), false /* has text direction override */);
99     }
100     virtual PopupMenuStyle menuStyle() const { return itemStyle(0); }
101     virtual int clientInsetLeft() const { return 0; }
102     virtual int clientInsetRight() const { return 0; }
103     virtual int clientPaddingLeft() const { return 0; }
104     virtual int clientPaddingRight() const { return 0; }
105     virtual int listSize() const { return 10; }
106     virtual int selectedIndex() const { return m_selectIndex; }
107     virtual void popupDidHide() { }
108     virtual bool itemIsSeparator(unsigned listIndex) const { return false; }
109     virtual bool itemIsLabel(unsigned listIndex) const { return false; }
110     virtual bool itemIsSelected(unsigned listIndex) const { return listIndex == m_selectIndex; }
111     virtual bool shouldPopOver() const { return false; }
112     virtual bool valueShouldChangeOnHotTrack() const { return false; }
113     virtual void setTextFromItem(unsigned listIndex) { }
114
115     virtual FontSelector* fontSelector() const { return 0; }
116     virtual HostWindow* hostWindow() const { return 0; }
117
118     virtual PassRefPtr<Scrollbar> createScrollbar(ScrollableArea*, ScrollbarOrientation, ScrollbarControlSize) { return 0; }
119
120     void setDisabledIndex(unsigned index) { m_disabledIndexSet.insert(index); }
121     void setFocusedNode(Node* node) { m_node = node; }
122
123 private:
124     unsigned m_selectIndex;
125     std::set<unsigned> m_disabledIndexSet;
126     Node* m_node;
127 };
128
129 class TestWebWidgetClient : public WebWidgetClient {
130 public:
131     ~TestWebWidgetClient() { }
132 };
133
134 class TestWebPopupMenuImpl : public WebPopupMenuImpl {
135 public:
136     static PassRefPtr<TestWebPopupMenuImpl> create(WebWidgetClient* client)
137     {
138         return adoptRef(new TestWebPopupMenuImpl(client));
139     }
140
141     ~TestWebPopupMenuImpl() { }
142
143 private:
144     TestWebPopupMenuImpl(WebWidgetClient* client) : WebPopupMenuImpl(client) { }
145 };
146
147 class TestWebViewClient : public WebViewClient {
148 public:
149     TestWebViewClient() : m_webPopupMenu(TestWebPopupMenuImpl::create(&m_webWidgetClient)) { }
150     ~TestWebViewClient() { }
151
152     virtual WebWidget* createPopupMenu(WebPopupType) { return m_webPopupMenu.get(); }
153
154     // We need to override this so that the popup menu size is not 0
155     // (the layout code checks to see if the popup fits on the screen).
156     virtual WebScreenInfo screenInfo()
157     { 
158         WebScreenInfo screenInfo;
159         screenInfo.availableRect.height = 2000;
160         screenInfo.availableRect.width = 2000;
161         return screenInfo;
162     }
163
164 private:
165     TestWebWidgetClient m_webWidgetClient;
166     RefPtr<TestWebPopupMenuImpl> m_webPopupMenu;
167 };
168
169 class TestWebFrameClient : public WebFrameClient {
170 public:
171     ~TestWebFrameClient() { }
172 };
173
174 class SelectPopupMenuTest : public testing::Test {
175 public:
176     SelectPopupMenuTest()
177         : baseURL("http://www.test.com/")
178     {
179     }
180
181 protected:
182     virtual void SetUp()
183     {
184         m_webView = static_cast<WebViewImpl*>(WebView::create(&m_webviewClient));
185         m_webView->initializeMainFrame(&m_webFrameClient);
186         m_popupMenu = adoptRef(new PopupMenuChromium(&m_popupMenuClient));
187     }
188
189     virtual void TearDown()
190     {
191         m_popupMenu = 0;
192         m_webView->close();
193         webkit_support::UnregisterAllMockedURLs();
194     }
195
196     // Returns true if there currently is a select popup in the WebView.
197     bool popupOpen() const { return m_webView->selectPopup(); }
198
199     int selectedIndex() const { return m_popupMenuClient.selectedIndex(); }
200
201     void showPopup()
202     {
203         m_popupMenu->show(IntRect(0, 0, 100, 100),
204             static_cast<WebFrameImpl*>(m_webView->mainFrame())->frameView(), 0);
205         ASSERT_TRUE(popupOpen());
206         EXPECT_TRUE(m_webView->selectPopup()->popupType() == PopupContainer::Select);
207     }
208
209     void hidePopup()
210     {
211         m_popupMenu->hide();
212         EXPECT_FALSE(popupOpen());
213     }
214
215     void simulateKeyDownEvent(int keyCode)
216     {
217         simulateKeyEvent(WebInputEvent::RawKeyDown, keyCode);
218     }
219
220     void simulateKeyUpEvent(int keyCode)
221     {
222         simulateKeyEvent(WebInputEvent::KeyUp, keyCode);
223     }
224
225     // Simulates a key event on the WebView.
226     // The WebView forwards the event to the select popup if one is open.
227     void simulateKeyEvent(WebInputEvent::Type eventType, int keyCode)
228     {
229         WebKeyboardEvent keyEvent;
230         keyEvent.windowsKeyCode = keyCode;
231         keyEvent.type = eventType;
232         m_webView->handleInputEvent(keyEvent);
233     }
234
235     // Simulates a mouse event on the select popup.
236     void simulateLeftMouseDownEvent(const IntPoint& point)
237     {
238         PlatformMouseEvent mouseEvent(point, point, LeftButton, MouseEventPressed,
239                                       1, false, false, false, false, 0);
240         m_webView->selectPopup()->handleMouseDownEvent(mouseEvent);
241     }
242     void simulateLeftMouseUpEvent(const IntPoint& point)
243     {
244         PlatformMouseEvent mouseEvent(point, point, LeftButton, MouseEventReleased,
245                                       1, false, false, false, false, 0);
246         m_webView->selectPopup()->handleMouseReleaseEvent(mouseEvent);
247     }
248
249     void registerMockedURLLoad(const std::string& fileName)
250     {
251         WebURLResponse response;
252         response.initialize();
253         response.setMIMEType("text/html");
254
255         std::string filePath = webkit_support::GetWebKitRootDir().utf8();
256         filePath += "/Source/WebKit/chromium/tests/data/popup/";
257         filePath += fileName;
258
259         webkit_support::RegisterMockedURL(WebURL(GURL(baseURL + fileName)), response, WebString::fromUTF8(filePath));
260     }
261
262     void serveRequests()
263     {
264         webkit_support::ServeAsynchronousMockedRequests();
265     }
266
267     void loadFrame(WebFrame* frame, const std::string& fileName)
268     {
269         WebURLRequest urlRequest;
270         urlRequest.initialize();
271         urlRequest.setURL(WebURL(GURL(baseURL + fileName)));
272         frame->loadRequest(urlRequest);
273     }
274
275 protected:
276     TestWebViewClient m_webviewClient;
277     WebViewImpl* m_webView;
278     TestWebFrameClient m_webFrameClient;
279     TestPopupMenuClient m_popupMenuClient;
280     RefPtr<PopupMenu> m_popupMenu;
281     std::string baseURL;
282 };
283
284 // Tests that show/hide and repeats.  Select popups are reused in web pages when
285 // they are reopened, that what this is testing.
286 TEST_F(SelectPopupMenuTest, ShowThenHide)
287 {
288     for (int i = 0; i < 3; i++) {
289         showPopup();
290         hidePopup();
291     }
292 }
293
294 // Tests that showing a select popup and deleting it does not cause problem.
295 // This happens in real-life if a page navigates while a select popup is showing.
296 TEST_F(SelectPopupMenuTest, ShowThenDelete)
297 {
298     showPopup();
299     // Nothing else to do, TearDown() deletes the popup.
300 }
301
302 // Tests that losing focus closes the select popup.
303 TEST_F(SelectPopupMenuTest, ShowThenLoseFocus)
304 {
305     showPopup();
306     // Simulate losing focus.
307     m_webView->setFocus(false);
308
309     // Popup should have closed.
310     EXPECT_FALSE(popupOpen());
311 }
312
313 // Tests that pressing ESC closes the popup.
314 TEST_F(SelectPopupMenuTest, ShowThenPressESC)
315 {
316     showPopup();
317     simulateKeyDownEvent(VKEY_ESCAPE);
318     // Popup should have closed.
319     EXPECT_FALSE(popupOpen());
320 }
321
322 // Tests selecting an item with the arrows and enter/esc/tab.
323 TEST_F(SelectPopupMenuTest, SelectWithKeys)
324 {
325     showPopup();
326     // Simulate selecting the 2nd item by pressing Down, Down, enter.
327     simulateKeyDownEvent(VKEY_DOWN);
328     simulateKeyDownEvent(VKEY_DOWN);
329     simulateKeyDownEvent(VKEY_RETURN);
330
331     // Popup should have closed.
332     EXPECT_TRUE(!popupOpen());
333     EXPECT_EQ(2, selectedIndex());
334
335     // It should work as well with ESC.
336     showPopup();
337     simulateKeyDownEvent(VKEY_DOWN);
338     simulateKeyDownEvent(VKEY_ESCAPE);
339     EXPECT_FALSE(popupOpen());
340     EXPECT_EQ(3, selectedIndex());
341
342     // It should work as well with TAB.
343     showPopup();
344     simulateKeyDownEvent(VKEY_DOWN);
345     simulateKeyDownEvent(VKEY_TAB);
346     EXPECT_FALSE(popupOpen());
347     EXPECT_EQ(4, selectedIndex());
348 }
349
350 // Tests that selecting an item with the mouse does select the item and close
351 // the popup.
352 TEST_F(SelectPopupMenuTest, ClickItem)
353 {
354     showPopup();
355
356     // Y of 18 to be on the item at index 1 (12 font plus border and more to be safe).
357     IntPoint row1Point(2, 18);
358     // Simulate a click down/up on the first item.
359     simulateLeftMouseDownEvent(row1Point);
360     simulateLeftMouseUpEvent(row1Point);
361
362     // Popup should have closed and the item at index 1 selected.
363     EXPECT_FALSE(popupOpen());
364     EXPECT_EQ(1, selectedIndex());
365 }
366
367 // Tests that moving the mouse over an item and then clicking outside the select popup
368 // leaves the seleted item unchanged.
369 TEST_F(SelectPopupMenuTest, MouseOverItemClickOutside)
370 {
371     showPopup();
372
373     // Y of 18 to be on the item at index 1 (12 font plus border and more to be safe).
374     IntPoint row1Point(2, 18);
375     // Simulate the mouse moving over the first item.
376     PlatformMouseEvent mouseEvent(row1Point, row1Point, NoButton, MouseEventMoved,
377                                   1, false, false, false, false, 0);
378     m_webView->selectPopup()->handleMouseMoveEvent(mouseEvent);
379
380     // Click outside the popup.
381     simulateLeftMouseDownEvent(IntPoint(1000, 1000));
382
383     // Popup should have closed and item 0 should still be selected.
384     EXPECT_FALSE(popupOpen());
385     EXPECT_EQ(0, selectedIndex());
386 }
387
388 // Tests that selecting an item with the keyboard and then clicking outside the select
389 // popup does select that item.
390 TEST_F(SelectPopupMenuTest, SelectItemWithKeyboardItemClickOutside)
391 {
392     showPopup();
393
394     // Simulate selecting the 2nd item by pressing Down, Down.
395     simulateKeyDownEvent(VKEY_DOWN);
396     simulateKeyDownEvent(VKEY_DOWN);
397
398     // Click outside the popup.
399     simulateLeftMouseDownEvent(IntPoint(1000, 1000));
400
401     // Popup should have closed and the item should have been selected.
402     EXPECT_FALSE(popupOpen());
403     EXPECT_EQ(2, selectedIndex());
404 }
405
406 TEST_F(SelectPopupMenuTest, DISABLED_SelectItemEventFire)
407 {
408     registerMockedURLLoad("select_event.html");
409     m_webView->settings()->setJavaScriptEnabled(true);
410     loadFrame(m_webView->mainFrame(), "select_event.html");
411     serveRequests();
412
413     m_popupMenuClient.setFocusedNode(static_cast<WebFrameImpl*>(m_webView->mainFrame())->frameView()->frame()->document()->focusedNode());
414
415     showPopup();
416
417     int menuHeight = m_webView->selectPopup()->menuItemHeight();
418     // menuHeight * 0.5 means the Y position on the item at index 0.
419     IntPoint row1Point(2, menuHeight * 0.5);
420     simulateLeftMouseDownEvent(row1Point);
421     simulateLeftMouseUpEvent(row1Point);
422
423     WebElement element = m_webView->mainFrame()->document().getElementById("message");
424
425     // mousedown event is held by select node, and we don't simulate the event for the node.
426     // So we can only see mouseup and click event.
427     EXPECT_STREQ("upclick", std::string(element.innerText().utf8()).c_str());
428
429     // Disable the item at index 1.
430     m_popupMenuClient.setDisabledIndex(1);
431
432     showPopup();
433     // menuHeight * 1.5 means the Y position on the item at index 1.
434     row1Point.setY(menuHeight * 1.5);
435     simulateLeftMouseDownEvent(row1Point);
436     simulateLeftMouseUpEvent(row1Point);
437
438     // The item at index 1 is disabled, so the text should not be changed.
439     EXPECT_STREQ("upclick", std::string(element.innerText().utf8()).c_str());
440
441     showPopup();
442     // menuHeight * 2.5 means the Y position on the item at index 2.
443     row1Point.setY(menuHeight * 2.5);
444     simulateLeftMouseDownEvent(row1Point);
445     simulateLeftMouseUpEvent(row1Point);
446
447     // The item is changed to the item at index 2, from index 0, so change event is fired.
448     EXPECT_STREQ("upclickchangeupclick", std::string(element.innerText().utf8()).c_str());
449 }
450
451 TEST_F(SelectPopupMenuTest, FLAKY_SelectItemKeyEvent)
452 {
453     registerMockedURLLoad("select_event.html");
454     m_webView->settings()->setJavaScriptEnabled(true);
455     loadFrame(m_webView->mainFrame(), "select_event.html");
456     serveRequests();
457
458     m_popupMenuClient.setFocusedNode(static_cast<WebFrameImpl*>(m_webView->mainFrame())->frameView()->frame()->document()->focusedNode());
459
460     showPopup();
461
462     // Siumulate to choose the item at index 1 with keyboard.
463     simulateKeyDownEvent(VKEY_DOWN);
464     simulateKeyDownEvent(VKEY_DOWN);
465     simulateKeyDownEvent(VKEY_RETURN);
466
467     WebElement element = m_webView->mainFrame()->document().getElementById("message");
468     // We only can see change event but no other mouse related events.
469     EXPECT_STREQ("change", std::string(element.innerText().utf8()).c_str());
470 }
471
472 TEST_F(SelectPopupMenuTest, SelectItemRemoveSelectOnChange)
473 {
474     // Make sure no crash, even if select node is removed on 'change' event handler.
475     registerMockedURLLoad("select_event_remove_on_change.html");
476     m_webView->settings()->setJavaScriptEnabled(true);
477     loadFrame(m_webView->mainFrame(), "select_event_remove_on_change.html");
478     serveRequests();
479
480     m_popupMenuClient.setFocusedNode(static_cast<WebFrameImpl*>(m_webView->mainFrame())->frameView()->frame()->document()->focusedNode());
481
482     showPopup();
483
484     int menuHeight = m_webView->selectPopup()->menuItemHeight();
485     // menuHeight * 1.5 means the Y position on the item at index 1.
486     IntPoint row1Point(2, menuHeight * 1.5);
487     simulateLeftMouseDownEvent(row1Point);
488     simulateLeftMouseUpEvent(row1Point);
489
490     WebElement element = m_webView->mainFrame()->document().getElementById("message");
491     EXPECT_STREQ("change", std::string(element.innerText().utf8()).c_str());
492 }
493
494 TEST_F(SelectPopupMenuTest, SelectItemRemoveSelectOnClick)
495 {
496     // Make sure no crash, even if select node is removed on 'click' event handler.
497     registerMockedURLLoad("select_event_remove_on_click.html");
498     m_webView->settings()->setJavaScriptEnabled(true);
499     loadFrame(m_webView->mainFrame(), "select_event_remove_on_click.html");
500     serveRequests();
501
502     m_popupMenuClient.setFocusedNode(static_cast<WebFrameImpl*>(m_webView->mainFrame())->frameView()->frame()->document()->focusedNode());
503
504     showPopup();
505
506     int menuHeight = m_webView->selectPopup()->menuItemHeight();
507     // menuHeight * 1.5 means the Y position on the item at index 1.
508     IntPoint row1Point(2, menuHeight * 1.5);
509     simulateLeftMouseDownEvent(row1Point);
510     simulateLeftMouseUpEvent(row1Point);
511
512     WebElement element = m_webView->mainFrame()->document().getElementById("message");
513     EXPECT_STREQ("click", std::string(element.innerText().utf8()).c_str());
514 }
515
516 } // namespace