• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
TestPopupMenuClient()56     TestPopupMenuClient() : m_selectIndex(0) { }
~TestPopupMenuClient()57     virtual ~TestPopupMenuClient() {}
valueChanged(unsigned listIndex,bool fireEvents=true)58     virtual void valueChanged(unsigned listIndex, bool fireEvents = true)
59     {
60         m_selectIndex = listIndex;
61     }
selectionChanged(unsigned,bool)62     virtual void selectionChanged(unsigned, bool) {}
selectionCleared()63     virtual void selectionCleared() {}
64 
itemText(unsigned listIndex) const65     virtual String itemText(unsigned listIndex) const
66     {
67         String str("Item ");
68         str.append(String::number(listIndex));
69         return str;
70     }
itemLabel(unsigned) const71     virtual String itemLabel(unsigned) const { return String(); }
itemIcon(unsigned) const72     virtual String itemIcon(unsigned) const { return String(); }
itemToolTip(unsigned listIndex) const73     virtual String itemToolTip(unsigned listIndex) const { return itemText(listIndex); }
itemAccessibilityText(unsigned listIndex) const74     virtual String itemAccessibilityText(unsigned listIndex) const { return itemText(listIndex); }
itemIsEnabled(unsigned listIndex) const75     virtual bool itemIsEnabled(unsigned listIndex) const { return true; }
itemStyle(unsigned listIndex) const76     virtual PopupMenuStyle itemStyle(unsigned listIndex) const
77     {
78         Font font(FontPlatformData(12.0, false, false), false);
79         return PopupMenuStyle(Color::black, Color::white, font, true, false, Length(), TextDirection(), false /* has text direction override */);
80     }
menuStyle() const81     virtual PopupMenuStyle menuStyle() const { return itemStyle(0); }
clientInsetLeft() const82     virtual int clientInsetLeft() const { return 0; }
clientInsetRight() const83     virtual int clientInsetRight() const { return 0; }
clientPaddingLeft() const84     virtual int clientPaddingLeft() const { return 0; }
clientPaddingRight() const85     virtual int clientPaddingRight() const { return 0; }
listSize() const86     virtual int listSize() const { return 10; }
selectedIndex() const87     virtual int selectedIndex() const { return m_selectIndex; }
popupDidHide()88     virtual void popupDidHide() { }
itemIsSeparator(unsigned listIndex) const89     virtual bool itemIsSeparator(unsigned listIndex) const { return false; }
itemIsLabel(unsigned listIndex) const90     virtual bool itemIsLabel(unsigned listIndex) const { return false; }
itemIsSelected(unsigned listIndex) const91     virtual bool itemIsSelected(unsigned listIndex) const { return listIndex == m_selectIndex; }
shouldPopOver() const92     virtual bool shouldPopOver() const { return false; }
valueShouldChangeOnHotTrack() const93     virtual bool valueShouldChangeOnHotTrack() const { return false; }
setTextFromItem(unsigned listIndex)94     virtual void setTextFromItem(unsigned listIndex) { }
95 
fontSelector() const96     virtual FontSelector* fontSelector() const { return 0; }
hostWindow() const97     virtual HostWindow* hostWindow() const { return 0; }
98 
createScrollbar(ScrollableArea *,ScrollbarOrientation,ScrollbarControlSize)99     virtual PassRefPtr<Scrollbar> createScrollbar(ScrollableArea*, ScrollbarOrientation, ScrollbarControlSize) { return 0; }
100 
101 private:
102     unsigned m_selectIndex;
103 };
104 
105 class TestWebWidgetClient : public WebWidgetClient {
106 public:
~TestWebWidgetClient()107     ~TestWebWidgetClient() { }
108 };
109 
110 class TestWebPopupMenuImpl : public WebPopupMenuImpl {
111 public:
create(WebWidgetClient * client)112     static PassRefPtr<TestWebPopupMenuImpl> create(WebWidgetClient* client)
113     {
114         return adoptRef(new TestWebPopupMenuImpl(client));
115     }
116 
~TestWebPopupMenuImpl()117     ~TestWebPopupMenuImpl() { }
118 
119 private:
TestWebPopupMenuImpl(WebWidgetClient * client)120     TestWebPopupMenuImpl(WebWidgetClient* client) : WebPopupMenuImpl(client) { }
121 };
122 
123 class TestWebWidget : public WebWidget {
124 public:
~TestWebWidget()125     virtual ~TestWebWidget() { }
close()126     virtual void close() { }
size()127     virtual WebSize size() { return WebSize(100, 100); }
resize(const WebSize &)128     virtual void resize(const WebSize&) { }
layout()129     virtual void layout() { }
paint(WebCanvas *,const WebRect &)130     virtual void paint(WebCanvas*, const WebRect&) { }
themeChanged()131     virtual void themeChanged() { }
composite(bool finish)132     virtual void composite(bool finish) { }
handleInputEvent(const WebInputEvent &)133     virtual bool handleInputEvent(const WebInputEvent&) { return true; }
mouseCaptureLost()134     virtual void mouseCaptureLost() { }
setFocus(bool)135     virtual void setFocus(bool) { }
setComposition(const WebString & text,const WebVector<WebCompositionUnderline> & underlines,int selectionStart,int selectionEnd)136     virtual bool setComposition(
137         const WebString& text,
138         const WebVector<WebCompositionUnderline>& underlines,
139         int selectionStart,
140         int selectionEnd) { return true; }
confirmComposition()141     virtual bool confirmComposition() { return true; }
confirmComposition(const WebString & text)142     virtual bool confirmComposition(const WebString& text) { return true; }
textInputType()143     virtual WebTextInputType textInputType() { return WebKit::WebTextInputTypeNone; }
caretOrSelectionBounds()144     virtual WebRect caretOrSelectionBounds() { return WebRect(); }
selectionRange(WebPoint & start,WebPoint & end) const145     virtual bool selectionRange(WebPoint& start, WebPoint& end) const { return false; }
setTextDirection(WebTextDirection)146     virtual void setTextDirection(WebTextDirection) { }
147 };
148 
149 class TestWebViewClient : public WebViewClient {
150 public:
TestWebViewClient()151     TestWebViewClient() : m_webPopupMenu(TestWebPopupMenuImpl::create(&m_webWidgetClient)) { }
~TestWebViewClient()152     ~TestWebViewClient() { }
153 
createPopupMenu(WebPopupType)154     virtual WebWidget* createPopupMenu(WebPopupType) { return m_webPopupMenu.get(); }
155 
156     // We need to override this so that the popup menu size is not 0
157     // (the layout code checks to see if the popup fits on the screen).
screenInfo()158     virtual WebScreenInfo screenInfo()
159     {
160         WebScreenInfo screenInfo;
161         screenInfo.availableRect.height = 2000;
162         screenInfo.availableRect.width = 2000;
163         return screenInfo;
164     }
165 
166 private:
167     TestWebWidgetClient m_webWidgetClient;
168     RefPtr<TestWebPopupMenuImpl> m_webPopupMenu;
169 };
170 
171 class TestWebFrameClient : public WebFrameClient {
172 public:
~TestWebFrameClient()173     ~TestWebFrameClient() { }
174 };
175 
176 class SelectPopupMenuTest : public testing::Test {
177 public:
SelectPopupMenuTest()178     SelectPopupMenuTest()
179     {
180     }
181 
182 protected:
SetUp()183     virtual void SetUp()
184     {
185         m_webView = static_cast<WebViewImpl*>(WebView::create(&m_webviewClient));
186         m_webView->initializeMainFrame(&m_webFrameClient);
187         m_popupMenu = adoptRef(new PopupMenuChromium(&m_popupMenuClient));
188     }
189 
TearDown()190     virtual void TearDown()
191     {
192         m_popupMenu = 0;
193         m_webView->close();
194     }
195 
196     // Returns true if there currently is a select popup in the WebView.
popupOpen() const197     bool popupOpen() const { return m_webView->selectPopup(); }
198 
selectedIndex() const199     int selectedIndex() const { return m_popupMenuClient.selectedIndex(); }
200 
showPopup()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 
hidePopup()209     void hidePopup()
210     {
211         m_popupMenu->hide();
212         EXPECT_FALSE(popupOpen());
213     }
214 
simulateKeyDownEvent(int keyCode)215     void simulateKeyDownEvent(int keyCode)
216     {
217         simulateKeyEvent(WebInputEvent::RawKeyDown, keyCode);
218     }
219 
simulateKeyUpEvent(int keyCode)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.
simulateKeyEvent(WebInputEvent::Type eventType,int keyCode)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.
simulateLeftMouseDownEvent(const IntPoint & point)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     }
simulateLeftMouseUpEvent(const IntPoint & point)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 protected:
250     TestWebViewClient m_webviewClient;
251     WebViewImpl* m_webView;
252     TestWebFrameClient m_webFrameClient;
253     TestPopupMenuClient m_popupMenuClient;
254     RefPtr<PopupMenu> m_popupMenu;
255 };
256 
257 // Tests that show/hide and repeats.  Select popups are reused in web pages when
258 // they are reopened, that what this is testing.
TEST_F(SelectPopupMenuTest,ShowThenHide)259 TEST_F(SelectPopupMenuTest, ShowThenHide)
260 {
261     for (int i = 0; i < 3; i++) {
262         showPopup();
263         hidePopup();
264     }
265 }
266 
267 // Tests that showing a select popup and deleting it does not cause problem.
268 // This happens in real-life if a page navigates while a select popup is showing.
TEST_F(SelectPopupMenuTest,ShowThenDelete)269 TEST_F(SelectPopupMenuTest, ShowThenDelete)
270 {
271     showPopup();
272     // Nothing else to do, TearDown() deletes the popup.
273 }
274 
275 // Tests that losing focus closes the select popup.
TEST_F(SelectPopupMenuTest,ShowThenLoseFocus)276 TEST_F(SelectPopupMenuTest, ShowThenLoseFocus)
277 {
278     showPopup();
279     // Simulate losing focus.
280     m_webView->setFocus(false);
281 
282     // Popup should have closed.
283     EXPECT_FALSE(popupOpen());
284 }
285 
286 // Tests that pressing ESC closes the popup.
TEST_F(SelectPopupMenuTest,ShowThenPressESC)287 TEST_F(SelectPopupMenuTest, ShowThenPressESC)
288 {
289     showPopup();
290     simulateKeyDownEvent(VKEY_ESCAPE);
291     // Popup should have closed.
292     EXPECT_FALSE(popupOpen());
293 }
294 
295 // Tests selecting an item with the arrows and enter/esc/tab.
TEST_F(SelectPopupMenuTest,SelectWithKeys)296 TEST_F(SelectPopupMenuTest, SelectWithKeys)
297 {
298     showPopup();
299     // Simulate selecting the 2nd item by pressing Down, Down, enter.
300     simulateKeyDownEvent(VKEY_DOWN);
301     simulateKeyDownEvent(VKEY_DOWN);
302     simulateKeyDownEvent(VKEY_RETURN);
303 
304     // Popup should have closed.
305     EXPECT_TRUE(!popupOpen());
306     EXPECT_EQ(2, selectedIndex());
307 
308     // It should work as well with ESC.
309     showPopup();
310     simulateKeyDownEvent(VKEY_DOWN);
311     simulateKeyDownEvent(VKEY_ESCAPE);
312     EXPECT_FALSE(popupOpen());
313     EXPECT_EQ(3, selectedIndex());
314 
315     // It should work as well with TAB.
316     showPopup();
317     simulateKeyDownEvent(VKEY_DOWN);
318     simulateKeyDownEvent(VKEY_TAB);
319     EXPECT_FALSE(popupOpen());
320     EXPECT_EQ(4, selectedIndex());
321 }
322 
323 // Tests that selecting an item with the mouse does select the item and close
324 // the popup.
TEST_F(SelectPopupMenuTest,ClickItem)325 TEST_F(SelectPopupMenuTest, ClickItem)
326 {
327     showPopup();
328 
329     // Y of 18 to be on the item at index 1 (12 font plus border and more to be safe).
330     IntPoint row1Point(2, 18);
331     // Simulate a click down/up on the first item.
332     simulateLeftMouseDownEvent(row1Point);
333     simulateLeftMouseUpEvent(row1Point);
334 
335     // Popup should have closed and the item at index 1 selected.
336     EXPECT_FALSE(popupOpen());
337     EXPECT_EQ(1, selectedIndex());
338 }
339 
340 // Tests that moving the mouse over an item and then clicking outside the select popup
341 // leaves the seleted item unchanged.
TEST_F(SelectPopupMenuTest,MouseOverItemClickOutside)342 TEST_F(SelectPopupMenuTest, MouseOverItemClickOutside)
343 {
344     showPopup();
345 
346     // Y of 18 to be on the item at index 1 (12 font plus border and more to be safe).
347     IntPoint row1Point(2, 18);
348     // Simulate the mouse moving over the first item.
349     PlatformMouseEvent mouseEvent(row1Point, row1Point, NoButton, MouseEventMoved,
350                                   1, false, false, false, false, 0);
351     m_webView->selectPopup()->handleMouseMoveEvent(mouseEvent);
352 
353     // Click outside the popup.
354     simulateLeftMouseDownEvent(IntPoint(1000, 1000));
355 
356     // Popup should have closed and item 0 should still be selected.
357     EXPECT_FALSE(popupOpen());
358     EXPECT_EQ(0, selectedIndex());
359 }
360 
361 // Tests that selecting an item with the keyboard and then clicking outside the select
362 // popup does select that item.
TEST_F(SelectPopupMenuTest,SelectItemWithKeyboardItemClickOutside)363 TEST_F(SelectPopupMenuTest, SelectItemWithKeyboardItemClickOutside)
364 {
365     showPopup();
366 
367     // Simulate selecting the 2nd item by pressing Down, Down.
368     simulateKeyDownEvent(VKEY_DOWN);
369     simulateKeyDownEvent(VKEY_DOWN);
370 
371     // Click outside the popup.
372     simulateLeftMouseDownEvent(IntPoint(1000, 1000));
373 
374     // Popup should have closed and the item should have been selected.
375     EXPECT_FALSE(popupOpen());
376     EXPECT_EQ(2, selectedIndex());
377 }
378 
379 } // namespace
380