[BlackBerry] Make WebOverlayPrivate::scheduleCompositingRun a WebPagePrivate method.
[WebKit-https.git] / Source / WebKit / blackberry / Api / InRegionScroller.cpp
1 /*
2  * Copyright (C) 2011, 2012 Research In Motion Limited. All rights reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 #include "config.h"
20 #include "InRegionScroller.h"
21
22 #include "BackingStoreClient.h"
23 #include "Frame.h"
24 #include "HTMLFrameOwnerElement.h"
25 #include "HitTestResult.h"
26 #include "InRegionScrollableArea.h"
27 #include "InRegionScroller_p.h"
28 #include "LayerCompositingThread.h"
29 #include "Page.h"
30 #include "RenderBox.h"
31 #include "RenderLayer.h"
32 #include "RenderObject.h"
33 #include "RenderView.h"
34 #include "WebPage_p.h"
35
36 using namespace WebCore;
37
38 namespace BlackBerry {
39 namespace WebKit {
40
41 static bool canScrollInnerFrame(Frame*);
42 static bool canScrollRenderBox(RenderBox*);
43 static RenderLayer* parentLayer(RenderLayer*);
44 static Node* enclosingLayerNode(RenderLayer*);
45 static bool isNonRenderViewFixedPositionedContainer(RenderLayer*);
46 static void pushBackInRegionScrollable(std::vector<Platform::ScrollViewBase*>&, InRegionScrollableArea*, InRegionScrollerPrivate*);
47
48 InRegionScroller::InRegionScroller(WebPagePrivate* webPagePrivate)
49     : d(new InRegionScrollerPrivate(webPagePrivate))
50 {
51     ASSERT(webPagePrivate);
52 }
53
54 InRegionScroller::~InRegionScroller()
55 {
56     delete d;
57 }
58
59 bool InRegionScroller::compositedSetScrollPosition(unsigned camouflagedLayer, const Platform::IntPoint& scrollPosition)
60 {
61     ASSERT(Platform::userInterfaceThreadMessageClient()->isCurrentThread());
62     return d->compositedSetScrollPosition(camouflagedLayer, d->m_webPage->mapFromTransformed(scrollPosition));
63 }
64
65 InRegionScrollerPrivate::InRegionScrollerPrivate(WebPagePrivate* webPagePrivate)
66     : m_webPage(webPagePrivate)
67 {
68 }
69
70 void InRegionScrollerPrivate::setNode(WebCore::Node* node)
71 {
72     m_inRegionScrollStartingNode = node;
73 }
74
75 WebCore::Node* InRegionScrollerPrivate::node() const
76 {
77     return m_inRegionScrollStartingNode.get();
78 }
79
80 void InRegionScrollerPrivate::reset()
81 {
82     setNode(0);
83 }
84
85 bool InRegionScrollerPrivate::hasNode() const
86 {
87     return !!m_inRegionScrollStartingNode;
88 }
89
90 bool InRegionScrollerPrivate::canScroll() const
91 {
92     return hasNode();
93 }
94
95 bool InRegionScrollerPrivate::compositedSetScrollPosition(unsigned camouflagedLayer, const WebCore::IntPoint& scrollPosition)
96 {
97     LayerCompositingThread* scrollLayer = reinterpret_cast<LayerCompositingThread*>(camouflagedLayer);
98     scrollLayer->override()->setBoundsOrigin(WebCore::FloatPoint(scrollPosition.x(), scrollPosition.y()));
99
100     m_webPage->scheduleCompositingRun();
101     return true;
102 }
103
104 bool InRegionScrollerPrivate::scrollBy(const Platform::IntSize& delta)
105 {
106     ASSERT(Platform::webkitThreadMessageClient()->isCurrentThread());
107
108     if (!canScroll())
109         return false;
110
111     return scrollNodeRecursively(node(), delta);
112 }
113
114 std::vector<Platform::ScrollViewBase*> InRegionScrollerPrivate::inRegionScrollableAreasForPoint(const WebCore::IntPoint& point)
115 {
116     std::vector<Platform::ScrollViewBase*> validReturn;
117     std::vector<Platform::ScrollViewBase*> emptyReturn;
118
119     HitTestResult result = m_webPage->m_mainFrame->eventHandler()->hitTestResultAtPoint(m_webPage->mapFromViewportToContents(point), false /*allowShadowContent*/);
120     Node* node = result.innerNonSharedNode();
121     if (!node || !node->renderer())
122         return emptyReturn;
123
124     RenderLayer* layer = node->renderer()->enclosingLayer();
125     do {
126         RenderObject* renderer = layer->renderer();
127
128         if (renderer->isRenderView()) {
129             if (RenderView* renderView = toRenderView(renderer)) {
130                 FrameView* view = renderView->frameView();
131                 if (!view)
132                     return emptyReturn;
133
134                 if (canScrollInnerFrame(view->frame())) {
135                     pushBackInRegionScrollable(validReturn, new InRegionScrollableArea(m_webPage, layer), this);
136                     continue;
137                 }
138             }
139         } else if (canScrollRenderBox(layer->renderBox())) {
140             pushBackInRegionScrollable(validReturn, new InRegionScrollableArea(m_webPage, layer), this);
141             continue;
142         }
143
144         // If we run into a fix positioned layer, set the last scrollable in-region object
145         // as not able to propagate scroll to its parent scrollable.
146         if (isNonRenderViewFixedPositionedContainer(layer) && validReturn.size()) {
147             Platform::ScrollViewBase* end = validReturn.back();
148             end->setCanPropagateScrollingToEnclosingScrollable(false);
149         }
150
151     } while (layer = parentLayer(layer));
152
153     if (validReturn.empty())
154         return emptyReturn;
155
156     // Post-calculate the visible window rects in reverse hit test order so
157     // we account for all and any clipping rects.
158     WebCore::IntRect recursiveClippingRect(WebCore::IntPoint::zero(), m_webPage->transformedViewportSize());
159
160     std::vector<Platform::ScrollViewBase*>::reverse_iterator rend = validReturn.rend();
161     for (std::vector<Platform::ScrollViewBase*>::reverse_iterator rit = validReturn.rbegin(); rit != rend; ++rit) {
162
163         InRegionScrollableArea* curr = static_cast<InRegionScrollableArea*>(*rit);
164         RenderLayer* layer = curr->layer();
165
166         if (layer && layer->renderer()->isRenderView()) { // #document case
167             FrameView* view = toRenderView(layer->renderer())->frameView();
168             ASSERT(view);
169             ASSERT(canScrollInnerFrame(view->frame()));
170
171             WebCore::IntRect frameWindowRect = m_webPage->mapToTransformed(m_webPage->getRecursiveVisibleWindowRect(view));
172             frameWindowRect.intersect(recursiveClippingRect);
173             curr->setVisibleWindowRect(frameWindowRect);
174             recursiveClippingRect = frameWindowRect;
175
176         } else { // RenderBox-based elements case (scrollable boxes (div's, p's, textarea's, etc)).
177
178             RenderBox* box = layer->renderBox();
179             ASSERT(box);
180             ASSERT(canScrollRenderBox(box));
181
182             WebCore::IntRect visibleWindowRect = enclosingIntRect(box->absoluteClippedOverflowRect());
183             visibleWindowRect = box->frame()->view()->contentsToWindow(visibleWindowRect);
184             visibleWindowRect = m_webPage->mapToTransformed(visibleWindowRect);
185             visibleWindowRect.intersect(recursiveClippingRect);
186
187             curr->setVisibleWindowRect(visibleWindowRect);
188             recursiveClippingRect = visibleWindowRect;
189         }
190     }
191
192     return validReturn;
193 }
194
195 bool InRegionScrollerPrivate::scrollNodeRecursively(WebCore::Node* node, const WebCore::IntSize& delta)
196 {
197     if (delta.isZero())
198         return true;
199
200     if (!node)
201         return false;
202
203     RenderObject* renderer = node->renderer();
204     if (!renderer)
205         return false;
206
207     FrameView* view = renderer->view()->frameView();
208     if (!view)
209         return false;
210
211     // Try scrolling the renderer.
212     if (scrollRenderer(renderer, delta))
213         return true;
214
215     // We've hit the page, don't scroll it and return false.
216     if (view == m_webPage->m_mainFrame->view())
217         return false;
218
219     // Try scrolling the FrameView.
220     if (canScrollInnerFrame(view->frame())) {
221         IntSize viewDelta = delta;
222         IntPoint newViewOffset = view->scrollPosition();
223         IntPoint maxViewOffset = view->maximumScrollPosition();
224         adjustScrollDelta(maxViewOffset, newViewOffset, viewDelta);
225
226         if (!viewDelta.isZero()) {
227             view->setCanBlitOnScroll(false);
228
229             BackingStoreClient* backingStoreClient = m_webPage->backingStoreClientForFrame(view->frame());
230             if (backingStoreClient) {
231                 backingStoreClient->setIsClientGeneratedScroll(true);
232                 backingStoreClient->setIsScrollNotificationSuppressed(true);
233             }
234
235             setNode(view->frame()->document());
236
237             view->scrollBy(viewDelta);
238
239             if (backingStoreClient) {
240                 backingStoreClient->setIsClientGeneratedScroll(false);
241                 backingStoreClient->setIsScrollNotificationSuppressed(false);
242             }
243
244             return true;
245         }
246     }
247
248     // Try scrolling the node of the enclosing frame.
249     Frame* frame = node->document()->frame();
250     if (frame) {
251         Node* ownerNode = frame->ownerElement();
252         if (scrollNodeRecursively(ownerNode, delta))
253             return true;
254     }
255
256     return false;
257 }
258
259 bool InRegionScrollerPrivate::scrollRenderer(WebCore::RenderObject* renderer, const WebCore::IntSize& delta)
260 {
261     RenderLayer* layer = renderer->enclosingLayer();
262     if (!layer)
263         return false;
264
265     // Try to scroll layer.
266     bool restrictedByLineClamp = false;
267     if (renderer->parent())
268         restrictedByLineClamp = !renderer->parent()->style()->lineClamp().isNone();
269
270     if (renderer->hasOverflowClip() && !restrictedByLineClamp) {
271         IntSize layerDelta = delta;
272         IntPoint maxOffset(layer->scrollWidth() - layer->renderBox()->clientWidth(), layer->scrollHeight() - layer->renderBox()->clientHeight());
273         IntPoint currentOffset(layer->scrollXOffset(), layer->scrollYOffset());
274         adjustScrollDelta(maxOffset, currentOffset, layerDelta);
275         if (!layerDelta.isZero()) {
276             setNode(enclosingLayerNode(layer));
277             IntPoint newOffset = currentOffset + layerDelta;
278             layer->scrollToOffset(IntSize(newOffset.x(), newOffset.y()));
279             renderer->repaint(true);
280             return true;
281         }
282     }
283
284     while (layer = layer->parent()) {
285         if (canScrollRenderBox(layer->renderBox()))
286             return scrollRenderer(layer->renderBox(), delta);
287     }
288
289     return false;
290 }
291
292 void InRegionScrollerPrivate::adjustScrollDelta(const WebCore::IntPoint& maxOffset, const WebCore::IntPoint& currentOffset, WebCore::IntSize& delta) const
293 {
294     if (currentOffset.x() + delta.width() > maxOffset.x())
295         delta.setWidth(std::min(maxOffset.x() - currentOffset.x(), delta.width()));
296
297     if (currentOffset.x() + delta.width() < 0)
298         delta.setWidth(std::max(-currentOffset.x(), delta.width()));
299
300     if (currentOffset.y() + delta.height() > maxOffset.y())
301         delta.setHeight(std::min(maxOffset.y() - currentOffset.y(), delta.height()));
302
303     if (currentOffset.y() + delta.height() < 0)
304         delta.setHeight(std::max(-currentOffset.y(), delta.height()));
305 }
306
307 static bool canScrollInnerFrame(Frame* frame)
308 {
309     if (!frame || !frame->view())
310         return false;
311
312     // Not having an owner element means that we are on the mainframe.
313     if (!frame->ownerElement())
314         return false;
315
316     ASSERT(frame != frame->page()->mainFrame());
317
318     IntSize visibleSize = frame->view()->visibleContentRect().size();
319     IntSize contentsSize = frame->view()->contentsSize();
320
321     bool canBeScrolled = contentsSize.height() > visibleSize.height() || contentsSize.width() > visibleSize.width();
322
323     // Lets also consider the 'overflow-{x,y} property set directly to the {i}frame tag.
324     return canBeScrolled && (frame->ownerElement()->scrollingMode() != ScrollbarAlwaysOff);
325 }
326
327 // The RenderBox::canbeScrolledAndHasScrollableArea method returns true for the
328 // following scenario, for example:
329 // (1) a div that has a vertical overflow but no horizontal overflow
330 //     with overflow-y: hidden and overflow-x: auto set.
331 // The version below fixes it.
332 // FIXME: Fix RenderBox::canBeScrolledAndHasScrollableArea method instead.
333 static bool canScrollRenderBox(RenderBox* box)
334 {
335     if (!box || !box->hasOverflowClip())
336         return false;
337
338     if (box->scrollsOverflowX() && (box->scrollWidth() != box->clientWidth())
339         || box->scrollsOverflowY() && (box->scrollHeight() != box->clientHeight()))
340         return true;
341
342     Node* node = box->node();
343     return node && (node->rendererIsEditable() || node->isDocumentNode());
344 }
345
346 static RenderLayer* parentLayer(RenderLayer* layer)
347 {
348     ASSERT(layer);
349     if (layer->parent())
350         return layer->parent();
351
352     RenderObject* renderer = layer->renderer();
353     if (renderer->document() && renderer->document()->ownerElement() && renderer->document()->ownerElement()->renderer())
354         return renderer->document()->ownerElement()->renderer()->enclosingLayer();
355
356     return 0;
357 }
358
359 // FIXME: Make RenderLayer::enclosingElement public so this one can be removed.
360 static Node* enclosingLayerNode(RenderLayer* layer)
361 {
362     for (RenderObject* r = layer->renderer(); r; r = r->parent()) {
363         if (Node* e = r->node())
364             return e;
365     }
366     ASSERT_NOT_REACHED();
367     return 0;
368 }
369
370 static bool isNonRenderViewFixedPositionedContainer(RenderLayer* layer)
371 {
372     RenderObject* o = layer->renderer();
373     if (o->isRenderView())
374         return false;
375
376     return o->isOutOfFlowPositioned() && o->style()->position() == FixedPosition;
377 }
378
379 static void pushBackInRegionScrollable(std::vector<Platform::ScrollViewBase*>& vector, InRegionScrollableArea* scrollableArea, InRegionScrollerPrivate* scroller)
380 {
381     ASSERT(scroller);
382     ASSERT(!scrollableArea->isNull());
383
384     scrollableArea->setCanPropagateScrollingToEnclosingScrollable(!isNonRenderViewFixedPositionedContainer(scrollableArea->layer()));
385     vector.push_back(scrollableArea);
386     if (vector.size() == 1) {
387         // FIXME: Use RenderLayer::renderBox()->node() instead?
388         scroller->setNode(enclosingLayerNode(scrollableArea->layer()));
389     }
390 }
391
392 }
393 }