summaryrefslogtreecommitdiffstats
path: root/WebKit/chromium/tests/PopupMenuTest.cpp
blob: aef29cf2d04124b4c87ef07a2e5f385096782b68 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
/*
 * Copyright (C) 2010 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"

#include <gtest/gtest.h>

#include "Color.h"
#include "KeyboardCodes.h"
#include "PopupMenu.h"
#include "PopupMenuClient.h"
#include "PopupMenuChromium.h"
#include "WebFrameClient.h"
#include "WebFrameImpl.h"
#include "WebInputEvent.h"
#include "WebPopupMenuImpl.h"
#include "WebScreenInfo.h"
#include "WebViewClient.h"
#include "WebViewImpl.h"

using namespace WebCore;
using namespace WebKit;

namespace {

class TestPopupMenuClient : public PopupMenuClient {
public:
    // Item at index 0 is selected by default.
    TestPopupMenuClient() : m_selectIndex(0) { }
    virtual ~TestPopupMenuClient() {}
    virtual void valueChanged(unsigned listIndex, bool fireEvents = true)
    {
        m_selectIndex = listIndex;
    }
    virtual void selectionChanged(unsigned, bool) {}
    virtual void selectionCleared() {}

    virtual String itemText(unsigned listIndex) const
    {
        String str("Item ");
        str.append(String::number(listIndex));
        return str;
    }
    virtual String itemLabel(unsigned) const { return String(); }
    virtual String itemIcon(unsigned) const { return String(); }
    virtual String itemToolTip(unsigned listIndex) const { return itemText(listIndex); }
    virtual String itemAccessibilityText(unsigned listIndex) const { return itemText(listIndex); }
    virtual bool itemIsEnabled(unsigned listIndex) const { return true; }
    virtual PopupMenuStyle itemStyle(unsigned listIndex) const
    {
        Font font(FontPlatformData(12.0, false, false), false);
        return PopupMenuStyle(Color::black, Color::white, font, true, Length(), TextDirection());
    }
    virtual PopupMenuStyle menuStyle() const { return itemStyle(0); }
    virtual int clientInsetLeft() const { return 0; }
    virtual int clientInsetRight() const { return 0; }
    virtual int clientPaddingLeft() const { return 0; }
    virtual int clientPaddingRight() const { return 0; }
    virtual int listSize() const { return 10; }
    virtual int selectedIndex() const { return m_selectIndex; }
    virtual void popupDidHide() { }
    virtual bool itemIsSeparator(unsigned listIndex) const { return false; }
    virtual bool itemIsLabel(unsigned listIndex) const { return false; }
    virtual bool itemIsSelected(unsigned listIndex) const { return listIndex == m_selectIndex; }
    virtual bool shouldPopOver() const { return false; }
    virtual bool valueShouldChangeOnHotTrack() const { return false; }
    virtual void setTextFromItem(unsigned listIndex) { }
    
    virtual FontSelector* fontSelector() const { return 0; }
    virtual HostWindow* hostWindow() const { return 0; }
    
    virtual PassRefPtr<Scrollbar> createScrollbar(ScrollbarClient*, ScrollbarOrientation, ScrollbarControlSize) { return 0; }

private:
    unsigned m_selectIndex;
};

class TestWebWidgetClient : public WebWidgetClient {
public:
    ~TestWebWidgetClient() { }
};

class TestWebPopupMenuImpl : public WebPopupMenuImpl {
public:
    static PassRefPtr<TestWebPopupMenuImpl> create(WebWidgetClient* client)
    {
        return adoptRef(new TestWebPopupMenuImpl(client));
    }

    ~TestWebPopupMenuImpl() { }

private:
    TestWebPopupMenuImpl(WebWidgetClient* client) : WebPopupMenuImpl(client) { }
};

class TestWebWidget : public WebWidget {
public:
    virtual ~TestWebWidget() { }
    virtual void close() { }
    virtual WebSize size() { return WebSize(100, 100); }
    virtual void resize(const WebSize&) { }
    virtual void layout() { }
    virtual void paint(WebCanvas*, const WebRect&) { }
    virtual void themeChanged() { }
    virtual void composite(bool finish) { }
    virtual bool handleInputEvent(const WebInputEvent&) { return true; }
    virtual void mouseCaptureLost() { }
    virtual void setFocus(bool) { }
    virtual bool setComposition(
        const WebString& text,
        const WebVector<WebCompositionUnderline>& underlines,
        int selectionStart,
        int selectionEnd) { return true; }
    virtual bool confirmComposition() { return true; }
    virtual WebTextInputType textInputType() { return WebKit::WebTextInputTypeNone; }
    virtual WebRect caretOrSelectionBounds() { return WebRect(); }
    virtual void setTextDirection(WebTextDirection) { }
};

class TestWebViewClient : public WebViewClient {
public:
    TestWebViewClient() : m_webPopupMenu(TestWebPopupMenuImpl::create(&m_webWidgetClient)) { }
    ~TestWebViewClient() { }

    virtual WebWidget* createPopupMenu(WebPopupType) { return m_webPopupMenu.get(); }

    // We need to override this so that the popup menu size is not 0
    // (the layout code checks to see if the popup fits on the screen).
    virtual WebScreenInfo screenInfo()
    { 
        WebScreenInfo screenInfo;
        screenInfo.availableRect.height = 2000;
        screenInfo.availableRect.width = 2000;
        return screenInfo;
    }

private:
    TestWebWidgetClient m_webWidgetClient;
    RefPtr<TestWebPopupMenuImpl> m_webPopupMenu;
};

class TestWebFrameClient : public WebFrameClient {
public:
    ~TestWebFrameClient() { }
};

class SelectPopupMenuTest : public testing::Test {
public:
    SelectPopupMenuTest()
    {
    }

protected:
    virtual void SetUp()
    {
        m_webView = static_cast<WebViewImpl*>(WebView::create(&m_webviewClient, 0));
        m_webView->initializeMainFrame(&m_webFrameClient);
        m_popupMenu = adoptRef(new PopupMenuChromium(&m_popupMenuClient));
    }

    virtual void TearDown()
    {
        m_popupMenu = 0;
        m_webView->close();
    }

    // Returns true if there currently is a select popup in the WebView.
    bool popupOpen() const { return m_webView->selectPopup(); }

    int selectedIndex() const { return m_popupMenuClient.selectedIndex(); }

    void showPopup()
    {
        m_popupMenu->show(IntRect(0, 0, 100, 100),
            static_cast<WebFrameImpl*>(m_webView->mainFrame())->frameView(), 0);
        ASSERT_TRUE(popupOpen());
        EXPECT_TRUE(m_webView->selectPopup()->popupType() == PopupContainer::Select);
    }

    void hidePopup()
    {
        m_popupMenu->hide();
        EXPECT_FALSE(popupOpen());
    }

    void simulateKeyDownEvent(int keyCode)
    {
        simulateKeyEvent(WebInputEvent::RawKeyDown, keyCode);
    }

    void simulateKeyUpEvent(int keyCode)
    {
        simulateKeyEvent(WebInputEvent::KeyUp, keyCode);
    }

    // Simulates a key event on the WebView.
    // The WebView forwards the event to the select popup if one is open.
    void simulateKeyEvent(WebInputEvent::Type eventType, int keyCode)
    {
        WebKeyboardEvent keyEvent;
        keyEvent.windowsKeyCode = keyCode;
        keyEvent.type = eventType;
        m_webView->handleInputEvent(keyEvent);
    }

    // Simulates a mouse event on the select popup.
    void simulateLeftMouseDownEvent(const IntPoint& point)
    {
        PlatformMouseEvent mouseEvent(point, point, LeftButton, MouseEventPressed,
                                      1, false, false, false, false, 0);
        m_webView->selectPopup()->handleMouseDownEvent(mouseEvent);
    }
    void simulateLeftMouseUpEvent(const IntPoint& point)
    {
        PlatformMouseEvent mouseEvent(point, point, LeftButton, MouseEventReleased,
                                      1, false, false, false, false, 0);
        m_webView->selectPopup()->handleMouseReleaseEvent(mouseEvent);
    }

protected:
    TestWebViewClient m_webviewClient;
    WebViewImpl* m_webView;
    TestWebFrameClient m_webFrameClient;
    TestPopupMenuClient m_popupMenuClient;
    RefPtr<PopupMenu> m_popupMenu;
};

// Tests that show/hide and repeats.  Select popups are reused in web pages when
// they are reopened, that what this is testing.
TEST_F(SelectPopupMenuTest, ShowThenHide)
{
    for (int i = 0; i < 3; i++) {
        showPopup();
        hidePopup();
    }
}

// Tests that showing a select popup and deleting it does not cause problem.
// This happens in real-life if a page navigates while a select popup is showing.
TEST_F(SelectPopupMenuTest, ShowThenDelete)
{
    showPopup();
    // Nothing else to do, TearDown() deletes the popup.
}

// Tests that losing focus closes the select popup.
TEST_F(SelectPopupMenuTest, ShowThenLoseFocus)
{
    showPopup();
    // Simulate losing focus.
    m_webView->setFocus(false);

    // Popup should have closed.
    EXPECT_FALSE(popupOpen());
}

// Tests that pressing ESC closes the popup.
TEST_F(SelectPopupMenuTest, ShowThenPressESC)
{
    showPopup();
    simulateKeyDownEvent(VKEY_ESCAPE);
    // Popup should have closed.
    EXPECT_FALSE(popupOpen());
}

// Tests selecting an item with the arrows and enter/esc/tab.
TEST_F(SelectPopupMenuTest, SelectWithKeys)
{
    showPopup();
    // Simulate selecting the 2nd item by pressing Down, Down, enter.
    simulateKeyDownEvent(VKEY_DOWN);
    simulateKeyDownEvent(VKEY_DOWN);
    simulateKeyDownEvent(VKEY_RETURN);

    // Popup should have closed.
    EXPECT_TRUE(!popupOpen());
    EXPECT_EQ(2, selectedIndex());

    // It should work as well with ESC.
    showPopup();
    simulateKeyDownEvent(VKEY_DOWN);
    simulateKeyDownEvent(VKEY_ESCAPE);
    EXPECT_FALSE(popupOpen());
    EXPECT_EQ(3, selectedIndex());

    // It should work as well with TAB.
    showPopup();
    simulateKeyDownEvent(VKEY_DOWN);
    simulateKeyDownEvent(VKEY_TAB);
    EXPECT_FALSE(popupOpen());
    EXPECT_EQ(4, selectedIndex());
}

// Tests that selecting an item with the mouse does select the item and close
// the popup.
TEST_F(SelectPopupMenuTest, ClickItem)
{
    showPopup();

    // Y of 18 to be on the item at index 1 (12 font plus border and more to be safe).
    IntPoint row1Point(2, 18);
    // Simulate a click down/up on the first item.
    simulateLeftMouseDownEvent(row1Point);
    simulateLeftMouseUpEvent(row1Point);

    // Popup should have closed and the item at index 1 selected.
    EXPECT_FALSE(popupOpen());
    EXPECT_EQ(1, selectedIndex());
}

// Tests that moving the mouse over an item and then clicking outside the select popup
// leaves the seleted item unchanged.
TEST_F(SelectPopupMenuTest, MouseOverItemClickOutside)
{
    showPopup();

    // Y of 18 to be on the item at index 1 (12 font plus border and more to be safe).
    IntPoint row1Point(2, 18);
    // Simulate the mouse moving over the first item.
    PlatformMouseEvent mouseEvent(row1Point, row1Point, NoButton, MouseEventMoved,
                                  1, false, false, false, false, 0);
    m_webView->selectPopup()->handleMouseMoveEvent(mouseEvent);

    // Click outside the popup.
    simulateLeftMouseDownEvent(IntPoint(1000, 1000));

    // Popup should have closed and item 0 should still be selected.
    EXPECT_FALSE(popupOpen());
    EXPECT_EQ(0, selectedIndex());
}

// Tests that selecting an item with the keyboard and then clicking outside the select
// popup does select that item.
TEST_F(SelectPopupMenuTest, SelectItemWithKeyboardItemClickOutside)
{
    showPopup();

    // Simulate selecting the 2nd item by pressing Down, Down.
    simulateKeyDownEvent(VKEY_DOWN);
    simulateKeyDownEvent(VKEY_DOWN);

    // Click outside the popup.
    simulateLeftMouseDownEvent(IntPoint(1000, 1000));

    // Popup should have closed and the item should have been selected.
    EXPECT_FALSE(popupOpen());
    EXPECT_EQ(2, selectedIndex());
}

} // namespace