1/*
2 * Copyright (C) 2012 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
6 * are met:
7 * 1.  Redistributions of source code must retain the above copyright
8 *     notice, this list of conditions and the following disclaimer.
9 * 2.  Redistributions in binary form must reproduce the above copyright
10 *     notice, this list of conditions and the following disclaimer in the
11 *     documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "config.h"
26#include "web/WebPluginScrollbarImpl.h"
27
28#include "platform/KeyboardCodes.h"
29#include "platform/graphics/GraphicsContext.h"
30#include "platform/scroll/ScrollAnimator.h"
31#include "platform/scroll/ScrollTypes.h"
32#include "platform/scroll/Scrollbar.h"
33#include "platform/scroll/ScrollbarTheme.h"
34#include "public/platform/WebCanvas.h"
35#include "public/platform/WebRect.h"
36#include "public/platform/WebVector.h"
37#include "public/web/WebInputEvent.h"
38#include "public/web/WebPluginScrollbarClient.h"
39#include "web/ScrollbarGroup.h"
40#include "web/WebInputEventConversion.h"
41#include "web/WebPluginContainerImpl.h"
42#include "web/WebViewImpl.h"
43
44using namespace WebCore;
45
46namespace blink {
47
48WebPluginScrollbar* WebPluginScrollbar::createForPlugin(Orientation orientation,
49                                                        WebPluginContainer* pluginContainer,
50                                                        WebPluginScrollbarClient* client)
51{
52    WebPluginContainerImpl* plugin = toWebPluginContainerImpl(pluginContainer);
53    return new WebPluginScrollbarImpl(orientation, plugin->scrollbarGroup(), client);
54}
55
56int WebPluginScrollbar::defaultThickness()
57{
58    return ScrollbarTheme::theme()->scrollbarThickness();
59}
60
61WebPluginScrollbarImpl::WebPluginScrollbarImpl(Orientation orientation,
62                                   ScrollbarGroup* group,
63                                   WebPluginScrollbarClient* client)
64    : m_group(group)
65    , m_client(client)
66    , m_scrollOffset(0)
67{
68    m_scrollbar = Scrollbar::create(
69        static_cast<ScrollableArea*>(m_group),
70        static_cast<WebCore::ScrollbarOrientation>(orientation),
71        WebCore::RegularScrollbar);
72    m_group->scrollbarCreated(this);
73}
74
75WebPluginScrollbarImpl::~WebPluginScrollbarImpl()
76{
77    m_group->scrollbarDestroyed(this);
78}
79
80void WebPluginScrollbarImpl::setScrollOffset(int scrollOffset)
81{
82    m_scrollOffset = scrollOffset;
83    m_client->valueChanged(this);
84}
85
86void WebPluginScrollbarImpl::invalidateScrollbarRect(const IntRect& rect)
87{
88    WebRect webrect(rect);
89    webrect.x += m_scrollbar->x();
90    webrect.y += m_scrollbar->y();
91    m_client->invalidateScrollbarRect(this, webrect);
92}
93
94void WebPluginScrollbarImpl::getTickmarks(Vector<IntRect>& tickmarks) const
95{
96    WebVector<WebRect> ticks;
97    m_client->getTickmarks(const_cast<WebPluginScrollbarImpl*>(this), &ticks);
98    tickmarks.resize(ticks.size());
99    for (size_t i = 0; i < ticks.size(); ++i)
100        tickmarks[i] = ticks[i];
101}
102
103IntPoint WebPluginScrollbarImpl::convertFromContainingViewToScrollbar(const IntPoint& parentPoint) const
104{
105    IntPoint offset(parentPoint.x() - m_scrollbar->x(), parentPoint.y() - m_scrollbar->y());
106    return m_scrollbar->Widget::convertFromContainingView(offset);
107}
108
109void WebPluginScrollbarImpl::scrollbarStyleChanged()
110{
111    m_client->overlayChanged(this);
112}
113
114bool WebPluginScrollbarImpl::isOverlay() const
115{
116    return m_scrollbar->isOverlayScrollbar();
117}
118
119int WebPluginScrollbarImpl::value() const
120{
121    return m_scrollOffset;
122}
123
124WebPoint WebPluginScrollbarImpl::location() const
125{
126    return m_scrollbar->frameRect().location();
127}
128
129WebSize WebPluginScrollbarImpl::size() const
130{
131    return m_scrollbar->frameRect().size();
132}
133
134bool WebPluginScrollbarImpl::enabled() const
135{
136    return m_scrollbar->enabled();
137}
138
139int WebPluginScrollbarImpl::maximum() const
140{
141    return m_scrollbar->maximum();
142}
143
144int WebPluginScrollbarImpl::totalSize() const
145{
146    return m_scrollbar->totalSize();
147}
148
149bool WebPluginScrollbarImpl::isScrollViewScrollbar() const
150{
151    return m_scrollbar->isScrollViewScrollbar();
152}
153
154bool WebPluginScrollbarImpl::isScrollableAreaActive() const
155{
156    return m_scrollbar->isScrollableAreaActive();
157}
158
159void WebPluginScrollbarImpl::getTickmarks(WebVector<WebRect>& tickmarks) const
160{
161    m_client->getTickmarks(const_cast<WebPluginScrollbarImpl*>(this), &tickmarks);
162}
163
164WebScrollbar::ScrollbarControlSize WebPluginScrollbarImpl::controlSize() const
165{
166    return static_cast<WebScrollbar::ScrollbarControlSize>(m_scrollbar->controlSize());
167}
168
169WebScrollbar::ScrollbarPart WebPluginScrollbarImpl::pressedPart() const
170{
171    return static_cast<WebScrollbar::ScrollbarPart>(m_scrollbar->pressedPart());
172}
173
174WebScrollbar::ScrollbarPart WebPluginScrollbarImpl::hoveredPart() const
175{
176    return static_cast<WebScrollbar::ScrollbarPart>(m_scrollbar->hoveredPart());
177}
178
179WebScrollbar::ScrollbarOverlayStyle WebPluginScrollbarImpl::scrollbarOverlayStyle() const
180{
181    return static_cast<WebScrollbar::ScrollbarOverlayStyle>(m_scrollbar->scrollbarOverlayStyle());
182}
183
184WebScrollbar::Orientation WebPluginScrollbarImpl::orientation() const
185{
186    if (m_scrollbar->orientation() == WebCore::HorizontalScrollbar)
187        return WebScrollbar::Horizontal;
188    return WebScrollbar::Vertical;
189}
190
191bool WebPluginScrollbarImpl::isLeftSideVerticalScrollbar() const
192{
193    return false;
194}
195
196bool WebPluginScrollbarImpl::isCustomScrollbar() const
197{
198    return m_scrollbar->isCustomScrollbar();
199}
200
201void WebPluginScrollbarImpl::setLocation(const WebRect& rect)
202{
203    IntRect oldRect = m_scrollbar->frameRect();
204    m_scrollbar->setFrameRect(rect);
205    if (WebRect(oldRect) != rect)
206      m_scrollbar->invalidate();
207
208    int length = m_scrollbar->orientation() == HorizontalScrollbar ? m_scrollbar->width() : m_scrollbar->height();
209    m_scrollbar->setEnabled(m_scrollbar->totalSize() > length);
210    m_scrollbar->setProportion(length, m_scrollbar->totalSize());
211}
212
213void WebPluginScrollbarImpl::setValue(int position)
214{
215    m_group->scrollToOffsetWithoutAnimation(m_scrollbar->orientation(), static_cast<float>(position));
216}
217
218void WebPluginScrollbarImpl::setDocumentSize(int size)
219{
220    int length = m_scrollbar->orientation() == HorizontalScrollbar ? m_scrollbar->width() : m_scrollbar->height();
221    m_scrollbar->setEnabled(size > length);
222    m_scrollbar->setProportion(length, size);
223}
224
225void WebPluginScrollbarImpl::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier)
226{
227    WebCore::ScrollDirection dir;
228    bool horizontal = m_scrollbar->orientation() == HorizontalScrollbar;
229    if (direction == ScrollForward)
230        dir = horizontal ? ScrollRight : ScrollDown;
231    else
232        dir = horizontal ? ScrollLeft : ScrollUp;
233
234    m_group->scroll(dir, static_cast<WebCore::ScrollGranularity>(granularity), multiplier);
235}
236
237void WebPluginScrollbarImpl::paint(WebCanvas* canvas, const WebRect& rect)
238{
239    GraphicsContext context(canvas);
240    m_scrollbar->paint(&context, rect);
241}
242
243bool WebPluginScrollbarImpl::handleInputEvent(const WebInputEvent& event)
244{
245    switch (event.type) {
246    case WebInputEvent::MouseDown:
247        return onMouseDown(event);
248    case WebInputEvent::MouseUp:
249        return onMouseUp(event);
250    case WebInputEvent::MouseMove:
251        return onMouseMove(event);
252    case WebInputEvent::MouseLeave:
253        return onMouseLeave(event);
254    case WebInputEvent::MouseWheel:
255        return onMouseWheel(event);
256    case WebInputEvent::KeyDown:
257        return onKeyDown(event);
258    case WebInputEvent::Undefined:
259    case WebInputEvent::MouseEnter:
260    case WebInputEvent::RawKeyDown:
261    case WebInputEvent::KeyUp:
262    case WebInputEvent::Char:
263    case WebInputEvent::TouchStart:
264    case WebInputEvent::TouchMove:
265    case WebInputEvent::TouchEnd:
266    case WebInputEvent::TouchCancel:
267    default:
268         break;
269    }
270    return false;
271}
272
273bool WebPluginScrollbarImpl::isAlphaLocked() const
274{
275    return m_scrollbar->isAlphaLocked();
276}
277
278void WebPluginScrollbarImpl::setIsAlphaLocked(bool flag)
279{
280    return m_scrollbar->setIsAlphaLocked(flag);
281}
282
283bool WebPluginScrollbarImpl::onMouseDown(const WebInputEvent& event)
284{
285    WebMouseEvent mousedown = *static_cast<const WebMouseEvent*>(&event);
286    if (!m_scrollbar->frameRect().contains(mousedown.x, mousedown.y))
287        return false;
288
289    mousedown.x -= m_scrollbar->x();
290    mousedown.y -= m_scrollbar->y();
291    m_scrollbar->mouseDown(PlatformMouseEventBuilder(m_scrollbar.get(), mousedown));
292    return true;
293}
294
295bool WebPluginScrollbarImpl::onMouseUp(const WebInputEvent& event)
296{
297    WebMouseEvent mouseup = *static_cast<const WebMouseEvent*>(&event);
298    if (m_scrollbar->pressedPart() == WebCore::NoPart)
299        return false;
300
301    m_scrollbar->mouseUp(PlatformMouseEventBuilder(m_scrollbar.get(), mouseup));
302    return true;
303}
304
305bool WebPluginScrollbarImpl::onMouseMove(const WebInputEvent& event)
306{
307    WebMouseEvent mousemove = *static_cast<const WebMouseEvent*>(&event);
308    if (m_scrollbar->frameRect().contains(mousemove.x, mousemove.y)
309        || m_scrollbar->pressedPart() != WebCore::NoPart) {
310        mousemove.x -= m_scrollbar->x();
311        mousemove.y -= m_scrollbar->y();
312        m_scrollbar->mouseMoved(PlatformMouseEventBuilder(m_scrollbar.get(), mousemove));
313        return true;
314    }
315
316    if (m_scrollbar->hoveredPart() != WebCore::NoPart && !m_scrollbar->isOverlayScrollbar())
317        m_scrollbar->mouseExited();
318    return false;
319}
320
321bool WebPluginScrollbarImpl::onMouseLeave(const WebInputEvent& event)
322{
323    if (m_scrollbar->hoveredPart() != WebCore::NoPart)
324        m_scrollbar->mouseExited();
325
326    return false;
327}
328
329bool WebPluginScrollbarImpl::onMouseWheel(const WebInputEvent& event)
330{
331    WebMouseWheelEvent mousewheel = *static_cast<const WebMouseWheelEvent*>(&event);
332    PlatformWheelEventBuilder platformEvent(m_scrollbar.get(), mousewheel);
333    return m_group->handleWheelEvent(platformEvent);
334}
335
336bool WebPluginScrollbarImpl::onKeyDown(const WebInputEvent& event)
337{
338    WebKeyboardEvent keyboard = *static_cast<const WebKeyboardEvent*>(&event);
339    int keyCode;
340    // We have to duplicate this logic from WebViewImpl because there it uses
341    // Char and RawKeyDown events, which don't exist at this point.
342    if (keyboard.windowsKeyCode == VKEY_SPACE)
343        keyCode = ((keyboard.modifiers & WebInputEvent::ShiftKey) ? VKEY_PRIOR : VKEY_NEXT);
344    else {
345        if (keyboard.modifiers == WebInputEvent::ControlKey) {
346            // Match FF behavior in the sense that Ctrl+home/end are the only Ctrl
347            // key combinations which affect scrolling. Safari is buggy in the
348            // sense that it scrolls the page for all Ctrl+scrolling key
349            // combinations. For e.g. Ctrl+pgup/pgdn/up/down, etc.
350            switch (keyboard.windowsKeyCode) {
351            case VKEY_HOME:
352            case VKEY_END:
353                break;
354            default:
355                return false;
356            }
357        }
358
359        if (keyboard.isSystemKey || (keyboard.modifiers & WebInputEvent::ShiftKey))
360            return false;
361
362        keyCode = keyboard.windowsKeyCode;
363    }
364    WebCore::ScrollDirection scrollDirection;
365    WebCore::ScrollGranularity scrollGranularity;
366    if (WebViewImpl::mapKeyCodeForScroll(keyCode, &scrollDirection, &scrollGranularity)) {
367        // Will return false if scroll direction wasn't compatible with this scrollbar.
368        return m_group->scroll(scrollDirection, scrollGranularity);
369    }
370    return false;
371}
372
373} // namespace blink
374