Home | History | Annotate | Download | only in tests
      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 itemIcon(unsigned) const { return String(); }
     73     virtual String itemToolTip(unsigned listIndex) const { return itemText(listIndex); }
     74     virtual String itemAccessibilityText(unsigned listIndex) const { return itemText(listIndex); }
     75     virtual bool itemIsEnabled(unsigned listIndex) const { return true; }
     76     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     }
     81     virtual PopupMenuStyle menuStyle() const { return itemStyle(0); }
     82     virtual int clientInsetLeft() const { return 0; }
     83     virtual int clientInsetRight() const { return 0; }
     84     virtual int clientPaddingLeft() const { return 0; }
     85     virtual int clientPaddingRight() const { return 0; }
     86     virtual int listSize() const { return 10; }
     87     virtual int selectedIndex() const { return m_selectIndex; }
     88     virtual void popupDidHide() { }
     89     virtual bool itemIsSeparator(unsigned listIndex) const { return false; }
     90     virtual bool itemIsLabel(unsigned listIndex) const { return false; }
     91     virtual bool itemIsSelected(unsigned listIndex) const { return listIndex == m_selectIndex; }
     92     virtual bool shouldPopOver() const { return false; }
     93     virtual bool valueShouldChangeOnHotTrack() const { return false; }
     94     virtual void setTextFromItem(unsigned listIndex) { }
     95 
     96     virtual FontSelector* fontSelector() const { return 0; }
     97     virtual HostWindow* hostWindow() const { return 0; }
     98 
     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:
    107     ~TestWebWidgetClient() { }
    108 };
    109 
    110 class TestWebPopupMenuImpl : public WebPopupMenuImpl {
    111 public:
    112     static PassRefPtr<TestWebPopupMenuImpl> create(WebWidgetClient* client)
    113     {
    114         return adoptRef(new TestWebPopupMenuImpl(client));
    115     }
    116 
    117     ~TestWebPopupMenuImpl() { }
    118 
    119 private:
    120     TestWebPopupMenuImpl(WebWidgetClient* client) : WebPopupMenuImpl(client) { }
    121 };
    122 
    123 class TestWebWidget : public WebWidget {
    124 public:
    125     virtual ~TestWebWidget() { }
    126     virtual void close() { }
    127     virtual WebSize size() { return WebSize(100, 100); }
    128     virtual void resize(const WebSize&) { }
    129     virtual void layout() { }
    130     virtual void paint(WebCanvas*, const WebRect&) { }
    131     virtual void themeChanged() { }
    132     virtual void composite(bool finish) { }
    133     virtual bool handleInputEvent(const WebInputEvent&) { return true; }
    134     virtual void mouseCaptureLost() { }
    135     virtual void setFocus(bool) { }
    136     virtual bool setComposition(
    137         const WebString& text,
    138         const WebVector<WebCompositionUnderline>& underlines,
    139         int selectionStart,
    140         int selectionEnd) { return true; }
    141     virtual bool confirmComposition() { return true; }
    142     virtual bool confirmComposition(const WebString& text) { return true; }
    143     virtual WebTextInputType textInputType() { return WebKit::WebTextInputTypeNone; }
    144     virtual WebRect caretOrSelectionBounds() { return WebRect(); }
    145     virtual bool selectionRange(WebPoint& start, WebPoint& end) const { return false; }
    146     virtual void setTextDirection(WebTextDirection) { }
    147 };
    148 
    149 class TestWebViewClient : public WebViewClient {
    150 public:
    151     TestWebViewClient() : m_webPopupMenu(TestWebPopupMenuImpl::create(&m_webWidgetClient)) { }
    152     ~TestWebViewClient() { }
    153 
    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).
    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:
    173     ~TestWebFrameClient() { }
    174 };
    175 
    176 class SelectPopupMenuTest : public testing::Test {
    177 public:
    178     SelectPopupMenuTest()
    179     {
    180     }
    181 
    182 protected:
    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 
    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.
    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 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.
    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.
    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.
    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.
    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.
    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.
    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.
    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.
    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