1/*
2 * Copyright (C) 2009 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#include "WebPopupMenuImpl.h"
33
34#include "PopupContainer.h"
35#include "PopupMenuChromium.h"
36#include "WebInputEvent.h"
37#include "WebInputEventConversion.h"
38#include "WebRange.h"
39#include "WebViewClient.h"
40#include "WebWidgetClient.h"
41#include "core/page/FrameView.h"
42#include "core/platform/Cursor.h"
43#include "core/platform/NotImplemented.h"
44#include "core/platform/PlatformGestureEvent.h"
45#include "core/platform/PlatformKeyboardEvent.h"
46#include "core/platform/PlatformMouseEvent.h"
47#include "core/platform/PlatformWheelEvent.h"
48#include "core/platform/chromium/FramelessScrollView.h"
49#include "core/platform/graphics/GraphicsContext.h"
50#include "core/platform/graphics/IntRect.h"
51#include "core/platform/graphics/skia/SkiaUtils.h"
52#include "public/platform/WebRect.h"
53#include <skia/ext/platform_canvas.h>
54
55using namespace WebCore;
56
57namespace WebKit {
58
59// WebPopupMenu ---------------------------------------------------------------
60
61WebPopupMenu* WebPopupMenu::create(WebWidgetClient* client)
62{
63    // Pass the WebPopupMenuImpl's self-reference to the caller.
64    return adoptRef(new WebPopupMenuImpl(client)).leakRef();
65}
66
67// WebWidget ------------------------------------------------------------------
68
69WebPopupMenuImpl::WebPopupMenuImpl(WebWidgetClient* client)
70    : m_client(client)
71    , m_widget(0)
72{
73    // Set to impossible point so we always get the first mouse position.
74    m_lastMousePosition = WebPoint(-1, -1);
75}
76
77WebPopupMenuImpl::~WebPopupMenuImpl()
78{
79    if (m_widget)
80        m_widget->setClient(0);
81}
82
83void WebPopupMenuImpl::initialize(FramelessScrollView* widget, const WebRect& bounds)
84{
85    m_widget = widget;
86    m_widget->setClient(this);
87
88    if (m_client) {
89        m_client->setWindowRect(bounds);
90        m_client->show(WebNavigationPolicy()); // Policy is ignored.
91    }
92}
93
94void WebPopupMenuImpl::handleMouseMove(const WebMouseEvent& event)
95{
96    // Don't send mouse move messages if the mouse hasn't moved.
97    if (event.x != m_lastMousePosition.x || event.y != m_lastMousePosition.y) {
98        m_lastMousePosition = WebPoint(event.x, event.y);
99        m_widget->handleMouseMoveEvent(PlatformMouseEventBuilder(m_widget, event));
100
101        // We cannot call setToolTipText() in PopupContainer, because PopupContainer is in WebCore, and we cannot refer to WebKit from Webcore.
102        WebCore::PopupContainer* container = static_cast<WebCore::PopupContainer*>(m_widget);
103        client()->setToolTipText(container->getSelectedItemToolTip(), container->menuStyle().textDirection() == WebCore::RTL ? WebTextDirectionRightToLeft : WebTextDirectionLeftToRight);
104    }
105}
106
107void WebPopupMenuImpl::handleMouseLeave(const WebMouseEvent& event)
108{
109    m_widget->handleMouseMoveEvent(PlatformMouseEventBuilder(m_widget, event));
110}
111
112void WebPopupMenuImpl::handleMouseDown(const WebMouseEvent& event)
113{
114    m_widget->handleMouseDownEvent(PlatformMouseEventBuilder(m_widget, event));
115}
116
117void WebPopupMenuImpl::handleMouseUp(const WebMouseEvent& event)
118{
119    mouseCaptureLost();
120    m_widget->handleMouseReleaseEvent(PlatformMouseEventBuilder(m_widget, event));
121}
122
123void WebPopupMenuImpl::handleMouseWheel(const WebMouseWheelEvent& event)
124{
125    m_widget->handleWheelEvent(PlatformWheelEventBuilder(m_widget, event));
126}
127
128bool WebPopupMenuImpl::handleGestureEvent(const WebGestureEvent& event)
129{
130    return m_widget->handleGestureEvent(PlatformGestureEventBuilder(m_widget, event));
131}
132
133bool WebPopupMenuImpl::handleTouchEvent(const WebTouchEvent& event)
134{
135
136    PlatformTouchEventBuilder touchEventBuilder(m_widget, event);
137    bool defaultPrevented(m_widget->handleTouchEvent(touchEventBuilder));
138    return defaultPrevented;
139}
140
141bool WebPopupMenuImpl::handleKeyEvent(const WebKeyboardEvent& event)
142{
143    return m_widget->handleKeyEvent(PlatformKeyboardEventBuilder(event));
144}
145
146// WebWidget -------------------------------------------------------------------
147
148void WebPopupMenuImpl::close()
149{
150    if (m_widget)
151        m_widget->hide();
152
153    m_client = 0;
154
155    deref(); // Balances ref() from WebPopupMenu::create.
156}
157
158void WebPopupMenuImpl::willStartLiveResize()
159{
160}
161
162void WebPopupMenuImpl::resize(const WebSize& newSize)
163{
164    if (m_size == newSize)
165        return;
166    m_size = newSize;
167
168    if (m_widget) {
169        IntRect newGeometry(0, 0, m_size.width, m_size.height);
170        m_widget->setFrameRect(newGeometry);
171    }
172
173    if (m_client) {
174        WebRect damagedRect(0, 0, m_size.width, m_size.height);
175        m_client->didInvalidateRect(damagedRect);
176    }
177}
178
179void WebPopupMenuImpl::willEndLiveResize()
180{
181}
182
183void WebPopupMenuImpl::animate(double)
184{
185}
186
187void WebPopupMenuImpl::layout()
188{
189}
190
191void WebPopupMenuImpl::paint(WebCanvas* canvas, const WebRect& rect, PaintOptions)
192{
193    if (!m_widget)
194        return;
195
196    if (!rect.isEmpty()) {
197        GraphicsContext context(canvas);
198        context.applyDeviceScaleFactor(m_client->deviceScaleFactor());
199        m_widget->paint(&context, rect);
200    }
201}
202
203void WebPopupMenuImpl::themeChanged()
204{
205    notImplemented();
206}
207
208bool WebPopupMenuImpl::handleInputEvent(const WebInputEvent& inputEvent)
209{
210    if (!m_widget)
211        return false;
212
213    // FIXME: WebKit seems to always return false on mouse events methods. For
214    // now we'll assume it has processed them (as we are only interested in
215    // whether keyboard events are processed).
216    switch (inputEvent.type) {
217    case WebInputEvent::MouseMove:
218        handleMouseMove(*static_cast<const WebMouseEvent*>(&inputEvent));
219        return true;
220
221    case WebInputEvent::MouseLeave:
222        handleMouseLeave(*static_cast<const WebMouseEvent*>(&inputEvent));
223        return true;
224
225    case WebInputEvent::MouseWheel:
226        handleMouseWheel(*static_cast<const WebMouseWheelEvent*>(&inputEvent));
227        return true;
228
229    case WebInputEvent::MouseDown:
230        handleMouseDown(*static_cast<const WebMouseEvent*>(&inputEvent));
231        return true;
232
233    case WebInputEvent::MouseUp:
234        handleMouseUp(*static_cast<const WebMouseEvent*>(&inputEvent));
235        return true;
236
237    // In Windows, RawKeyDown only has information about the physical key, but
238    // for "selection", we need the information about the character the key
239    // translated into. For English, the physical key value and the character
240    // value are the same, hence, "selection" works for English. But for other
241    // languages, such as Hebrew, the character value is different from the
242    // physical key value. Thus, without accepting Char event type which
243    // contains the key's character value, the "selection" won't work for
244    // non-English languages, such as Hebrew.
245    case WebInputEvent::RawKeyDown:
246    case WebInputEvent::KeyDown:
247    case WebInputEvent::KeyUp:
248    case WebInputEvent::Char:
249        return handleKeyEvent(*static_cast<const WebKeyboardEvent*>(&inputEvent));
250
251    case WebInputEvent::TouchStart:
252    case WebInputEvent::TouchMove:
253    case WebInputEvent::TouchEnd:
254    case WebInputEvent::TouchCancel:
255        return handleTouchEvent(*static_cast<const WebTouchEvent*>(&inputEvent));
256
257    case WebInputEvent::GestureScrollBegin:
258    case WebInputEvent::GestureScrollEnd:
259    case WebInputEvent::GestureScrollUpdate:
260    case WebInputEvent::GestureScrollUpdateWithoutPropagation:
261    case WebInputEvent::GestureFlingStart:
262    case WebInputEvent::GestureFlingCancel:
263    case WebInputEvent::GestureTap:
264    case WebInputEvent::GestureTapUnconfirmed:
265    case WebInputEvent::GestureTapDown:
266    case WebInputEvent::GestureTapCancel:
267    case WebInputEvent::GestureDoubleTap:
268    case WebInputEvent::GestureTwoFingerTap:
269    case WebInputEvent::GestureLongPress:
270    case WebInputEvent::GestureLongTap:
271    case WebInputEvent::GesturePinchBegin:
272    case WebInputEvent::GesturePinchEnd:
273    case WebInputEvent::GesturePinchUpdate:
274        return handleGestureEvent(*static_cast<const WebGestureEvent*>(&inputEvent));
275
276    case WebInputEvent::Undefined:
277    case WebInputEvent::MouseEnter:
278    case WebInputEvent::ContextMenu:
279        return false;
280    }
281    return false;
282}
283
284void WebPopupMenuImpl::mouseCaptureLost()
285{
286}
287
288void WebPopupMenuImpl::setFocus(bool)
289{
290}
291
292void WebPopupMenu::setMinimumRowHeight(int minimumRowHeight)
293{
294    PopupMenuChromium::setMinimumRowHeight(minimumRowHeight);
295}
296
297bool WebPopupMenuImpl::setComposition(const WebString&, const WebVector<WebCompositionUnderline>&, int, int)
298{
299    return false;
300}
301
302bool WebPopupMenuImpl::confirmComposition()
303{
304    return false;
305}
306
307bool WebPopupMenuImpl::confirmComposition(ConfirmCompositionBehavior)
308{
309    return false;
310}
311
312bool WebPopupMenuImpl::confirmComposition(const WebString&)
313{
314    return false;
315}
316
317bool WebPopupMenuImpl::compositionRange(size_t* location, size_t* length)
318{
319    *location = 0;
320    *length = 0;
321    return false;
322}
323
324bool WebPopupMenuImpl::caretOrSelectionRange(size_t* location, size_t* length)
325{
326    *location = 0;
327    *length = 0;
328    return false;
329}
330
331void WebPopupMenuImpl::setTextDirection(WebTextDirection)
332{
333}
334
335
336//-----------------------------------------------------------------------------
337// WebCore::HostWindow
338
339void WebPopupMenuImpl::invalidateContentsAndRootView(const IntRect& paintRect)
340{
341    if (paintRect.isEmpty())
342        return;
343    if (m_client)
344        m_client->didInvalidateRect(paintRect);
345}
346
347void WebPopupMenuImpl::invalidateContentsForSlowScroll(const IntRect& updateRect)
348{
349    invalidateContentsAndRootView(updateRect);
350}
351
352void WebPopupMenuImpl::scheduleAnimation()
353{
354}
355
356void WebPopupMenuImpl::scroll(const IntSize& scrollDelta, const IntRect& scrollRect, const IntRect& clipRect)
357{
358    if (m_client) {
359        int dx = scrollDelta.width();
360        int dy = scrollDelta.height();
361        m_client->didScrollRect(dx, dy, clipRect);
362    }
363}
364
365IntPoint WebPopupMenuImpl::screenToRootView(const IntPoint& point) const
366{
367    notImplemented();
368    return IntPoint();
369}
370
371IntRect WebPopupMenuImpl::rootViewToScreen(const IntRect& rect) const
372{
373    notImplemented();
374    return IntRect();
375}
376
377WebScreenInfo WebPopupMenuImpl::screenInfo() const
378{
379    return WebScreenInfo();
380}
381
382void WebPopupMenuImpl::setCursor(const WebCore::Cursor&)
383{
384}
385
386//-----------------------------------------------------------------------------
387// WebCore::FramelessScrollViewClient
388
389void WebPopupMenuImpl::popupClosed(FramelessScrollView* widget)
390{
391    ASSERT(widget == m_widget);
392    if (m_widget) {
393        m_widget->setClient(0);
394        m_widget = 0;
395    }
396    if (m_client)
397        m_client->closeWidgetSoon();
398}
399
400} // namespace WebKit
401