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
44namespace blink {
45
46WebPluginScrollbar* WebPluginScrollbar::createForPlugin(Orientation orientation,
47                                                        WebPluginContainer* pluginContainer,
48                                                        WebPluginScrollbarClient* client)
49{
50    WebPluginContainerImpl* plugin = toWebPluginContainerImpl(pluginContainer);
51    return new WebPluginScrollbarImpl(orientation, plugin->scrollbarGroup(), client);
52}
53
54int WebPluginScrollbar::defaultThickness()
55{
56    return ScrollbarTheme::theme()->scrollbarThickness();
57}
58
59WebPluginScrollbarImpl::WebPluginScrollbarImpl(Orientation orientation,
60                                   ScrollbarGroup* group,
61                                   WebPluginScrollbarClient* client)
62    : m_group(group)
63    , m_client(client)
64    , m_scrollOffset(0)
65{
66    m_scrollbar = Scrollbar::create(
67        static_cast<ScrollableArea*>(m_group),
68        static_cast<ScrollbarOrientation>(orientation),
69        blink::RegularScrollbar);
70    m_group->scrollbarCreated(this);
71}
72
73WebPluginScrollbarImpl::~WebPluginScrollbarImpl()
74{
75    m_group->scrollbarDestroyed(this);
76}
77
78void WebPluginScrollbarImpl::setScrollOffset(int scrollOffset)
79{
80    m_scrollOffset = scrollOffset;
81    m_client->valueChanged(this);
82}
83
84void WebPluginScrollbarImpl::invalidateScrollbarRect(const IntRect& rect)
85{
86    WebRect webrect(rect);
87    webrect.x += m_scrollbar->x();
88    webrect.y += m_scrollbar->y();
89    m_client->invalidateScrollbarRect(this, webrect);
90}
91
92void WebPluginScrollbarImpl::getTickmarks(Vector<IntRect>& tickmarks) const
93{
94    WebVector<WebRect> ticks;
95    m_client->getTickmarks(const_cast<WebPluginScrollbarImpl*>(this), &ticks);
96    tickmarks.resize(ticks.size());
97    for (size_t i = 0; i < ticks.size(); ++i)
98        tickmarks[i] = ticks[i];
99}
100
101IntPoint WebPluginScrollbarImpl::convertFromContainingViewToScrollbar(const IntPoint& parentPoint) const
102{
103    IntPoint offset(parentPoint.x() - m_scrollbar->x(), parentPoint.y() - m_scrollbar->y());
104    return m_scrollbar->Widget::convertFromContainingView(offset);
105}
106
107void WebPluginScrollbarImpl::scrollbarStyleChanged()
108{
109    m_client->overlayChanged(this);
110}
111
112bool WebPluginScrollbarImpl::isOverlay() const
113{
114    return m_scrollbar->isOverlayScrollbar();
115}
116
117int WebPluginScrollbarImpl::value() const
118{
119    return m_scrollOffset;
120}
121
122WebPoint WebPluginScrollbarImpl::location() const
123{
124    return m_scrollbar->frameRect().location();
125}
126
127WebSize WebPluginScrollbarImpl::size() const
128{
129    return m_scrollbar->frameRect().size();
130}
131
132bool WebPluginScrollbarImpl::enabled() const
133{
134    return m_scrollbar->enabled();
135}
136
137int WebPluginScrollbarImpl::maximum() const
138{
139    return m_scrollbar->maximum();
140}
141
142int WebPluginScrollbarImpl::totalSize() const
143{
144    return m_scrollbar->totalSize();
145}
146
147bool WebPluginScrollbarImpl::isScrollViewScrollbar() const
148{
149    return m_scrollbar->isScrollViewScrollbar();
150}
151
152bool WebPluginScrollbarImpl::isScrollableAreaActive() const
153{
154    return m_scrollbar->isScrollableAreaActive();
155}
156
157void WebPluginScrollbarImpl::getTickmarks(WebVector<WebRect>& tickmarks) const
158{
159    m_client->getTickmarks(const_cast<WebPluginScrollbarImpl*>(this), &tickmarks);
160}
161
162WebScrollbar::ScrollbarControlSize WebPluginScrollbarImpl::controlSize() const
163{
164    return static_cast<WebScrollbar::ScrollbarControlSize>(m_scrollbar->controlSize());
165}
166
167WebScrollbar::ScrollbarPart WebPluginScrollbarImpl::pressedPart() const
168{
169    return static_cast<WebScrollbar::ScrollbarPart>(m_scrollbar->pressedPart());
170}
171
172WebScrollbar::ScrollbarPart WebPluginScrollbarImpl::hoveredPart() const
173{
174    return static_cast<WebScrollbar::ScrollbarPart>(m_scrollbar->hoveredPart());
175}
176
177WebScrollbar::ScrollbarOverlayStyle WebPluginScrollbarImpl::scrollbarOverlayStyle() const
178{
179    return static_cast<WebScrollbar::ScrollbarOverlayStyle>(m_scrollbar->scrollbarOverlayStyle());
180}
181
182WebScrollbar::Orientation WebPluginScrollbarImpl::orientation() const
183{
184    if (m_scrollbar->orientation() == HorizontalScrollbar)
185        return WebScrollbar::Horizontal;
186    return WebScrollbar::Vertical;
187}
188
189bool WebPluginScrollbarImpl::isLeftSideVerticalScrollbar() const
190{
191    return false;
192}
193
194bool WebPluginScrollbarImpl::isCustomScrollbar() const
195{
196    return m_scrollbar->isCustomScrollbar();
197}
198
199void WebPluginScrollbarImpl::setLocation(const WebRect& rect)
200{
201    IntRect oldRect = m_scrollbar->frameRect();
202    m_scrollbar->setFrameRect(rect);
203    if (WebRect(oldRect) != rect)
204      m_scrollbar->invalidate();
205
206    int length = m_scrollbar->orientation() == HorizontalScrollbar ? m_scrollbar->width() : m_scrollbar->height();
207    m_scrollbar->setEnabled(m_scrollbar->totalSize() > length);
208    m_scrollbar->setProportion(length, m_scrollbar->totalSize());
209}
210
211void WebPluginScrollbarImpl::setValue(int position)
212{
213    m_group->scrollToOffsetWithoutAnimation(m_scrollbar->orientation(), static_cast<float>(position));
214}
215
216void WebPluginScrollbarImpl::setDocumentSize(int size)
217{
218    int length = m_scrollbar->orientation() == HorizontalScrollbar ? m_scrollbar->width() : m_scrollbar->height();
219    m_scrollbar->setEnabled(size > length);
220    m_scrollbar->setProportion(length, size);
221}
222
223void WebPluginScrollbarImpl::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier)
224{
225    blink::ScrollDirection dir;
226    bool horizontal = m_scrollbar->orientation() == HorizontalScrollbar;
227    if (direction == ScrollForward)
228        dir = horizontal ? ScrollRight : ScrollDown;
229    else
230        dir = horizontal ? ScrollLeft : ScrollUp;
231
232    m_group->scroll(dir, static_cast<blink::ScrollGranularity>(granularity), multiplier);
233}
234
235void WebPluginScrollbarImpl::paint(WebCanvas* canvas, const WebRect& rect)
236{
237    GraphicsContext context(canvas);
238    m_scrollbar->paint(&context, rect);
239}
240
241bool WebPluginScrollbarImpl::handleInputEvent(const WebInputEvent& event)
242{
243    switch (event.type) {
244    case WebInputEvent::MouseDown:
245        return onMouseDown(event);
246    case WebInputEvent::MouseUp:
247        return onMouseUp(event);
248    case WebInputEvent::MouseMove:
249        return onMouseMove(event);
250    case WebInputEvent::MouseLeave:
251        return onMouseLeave(event);
252    case WebInputEvent::MouseWheel:
253        return onMouseWheel(event);
254    case WebInputEvent::KeyDown:
255        return onKeyDown(event);
256    case WebInputEvent::Undefined:
257    case WebInputEvent::MouseEnter:
258    case WebInputEvent::RawKeyDown:
259    case WebInputEvent::KeyUp:
260    case WebInputEvent::Char:
261    case WebInputEvent::TouchStart:
262    case WebInputEvent::TouchMove:
263    case WebInputEvent::TouchEnd:
264    case WebInputEvent::TouchCancel:
265    default:
266         break;
267    }
268    return false;
269}
270
271bool WebPluginScrollbarImpl::isAlphaLocked() const
272{
273    return m_scrollbar->isAlphaLocked();
274}
275
276void WebPluginScrollbarImpl::setIsAlphaLocked(bool flag)
277{
278    return m_scrollbar->setIsAlphaLocked(flag);
279}
280
281bool WebPluginScrollbarImpl::onMouseDown(const WebInputEvent& event)
282{
283    WebMouseEvent mousedown = static_cast<const WebMouseEvent&>(event);
284    if (!m_scrollbar->frameRect().contains(mousedown.x, mousedown.y))
285        return false;
286
287    mousedown.x -= m_scrollbar->x();
288    mousedown.y -= m_scrollbar->y();
289    m_scrollbar->mouseDown(PlatformMouseEventBuilder(m_scrollbar.get(), mousedown));
290    return true;
291}
292
293bool WebPluginScrollbarImpl::onMouseUp(const WebInputEvent& event)
294{
295    WebMouseEvent mouseup = static_cast<const WebMouseEvent&>(event);
296    if (m_scrollbar->pressedPart() == blink::NoPart)
297        return false;
298
299    m_scrollbar->mouseUp(PlatformMouseEventBuilder(m_scrollbar.get(), mouseup));
300    return true;
301}
302
303bool WebPluginScrollbarImpl::onMouseMove(const WebInputEvent& event)
304{
305    WebMouseEvent mousemove = static_cast<const WebMouseEvent&>(event);
306    if (m_scrollbar->frameRect().contains(mousemove.x, mousemove.y)
307        || m_scrollbar->pressedPart() != blink::NoPart) {
308        mousemove.x -= m_scrollbar->x();
309        mousemove.y -= m_scrollbar->y();
310        m_scrollbar->mouseMoved(PlatformMouseEventBuilder(m_scrollbar.get(), mousemove));
311        return true;
312    }
313
314    if (m_scrollbar->hoveredPart() != blink::NoPart && !m_scrollbar->isOverlayScrollbar())
315        m_scrollbar->mouseExited();
316    return false;
317}
318
319bool WebPluginScrollbarImpl::onMouseLeave(const WebInputEvent& event)
320{
321    if (m_scrollbar->hoveredPart() != blink::NoPart)
322        m_scrollbar->mouseExited();
323
324    return false;
325}
326
327bool WebPluginScrollbarImpl::onMouseWheel(const WebInputEvent& event)
328{
329    WebMouseWheelEvent mousewheel = static_cast<const WebMouseWheelEvent&>(event);
330    PlatformWheelEventBuilder platformEvent(m_scrollbar.get(), mousewheel);
331    return m_group->handleWheelEvent(platformEvent);
332}
333
334bool WebPluginScrollbarImpl::onKeyDown(const WebInputEvent& event)
335{
336    WebKeyboardEvent keyboard = static_cast<const WebKeyboardEvent&>(event);
337    int keyCode;
338    // We have to duplicate this logic from WebViewImpl because there it uses
339    // Char and RawKeyDown events, which don't exist at this point.
340    if (keyboard.windowsKeyCode == VKEY_SPACE)
341        keyCode = ((keyboard.modifiers & WebInputEvent::ShiftKey) ? VKEY_PRIOR : VKEY_NEXT);
342    else {
343        if (keyboard.modifiers == WebInputEvent::ControlKey) {
344            // Match FF behavior in the sense that Ctrl+home/end are the only Ctrl
345            // key combinations which affect scrolling. Safari is buggy in the
346            // sense that it scrolls the page for all Ctrl+scrolling key
347            // combinations. For e.g. Ctrl+pgup/pgdn/up/down, etc.
348            switch (keyboard.windowsKeyCode) {
349            case VKEY_HOME:
350            case VKEY_END:
351                break;
352            default:
353                return false;
354            }
355        }
356
357        if (keyboard.isSystemKey || (keyboard.modifiers & WebInputEvent::ShiftKey))
358            return false;
359
360        keyCode = keyboard.windowsKeyCode;
361    }
362    blink::ScrollDirection scrollDirection;
363    blink::ScrollGranularity scrollGranularity;
364    if (WebViewImpl::mapKeyCodeForScroll(keyCode, &scrollDirection, &scrollGranularity)) {
365        // Will return false if scroll direction wasn't compatible with this scrollbar.
366        return m_group->scroll(scrollDirection, scrollGranularity);
367    }
368    return false;
369}
370
371} // namespace blink
372