InRegionScroller.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. /*
  2. * Copyright (C) 2011, 2012, 2013 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. #include "config.h"
  19. #include "InRegionScroller.h"
  20. #include "BackingStoreClient.h"
  21. #include "DOMSupport.h"
  22. #include "Frame.h"
  23. #include "HTMLFrameOwnerElement.h"
  24. #include "HitTestResult.h"
  25. #include "InRegionScrollableArea.h"
  26. #include "InRegionScroller_p.h"
  27. #include "LayerCompositingThread.h"
  28. #include "LayerWebKitThread.h"
  29. #include "Page.h"
  30. #include "RenderBox.h"
  31. #include "RenderLayer.h"
  32. #include "RenderLayerBacking.h"
  33. #include "RenderLayerCompositor.h"
  34. #include "RenderObject.h"
  35. #include "RenderView.h"
  36. #include "SelectionHandler.h"
  37. #include "WebKitThreadViewportAccessor.h"
  38. #include "WebPage_p.h"
  39. #include <BlackBerryPlatformViewportAccessor.h>
  40. using namespace WebCore;
  41. namespace BlackBerry {
  42. namespace WebKit {
  43. static bool canScrollInnerFrame(Frame*);
  44. static RenderLayer* parentLayer(RenderLayer*);
  45. static bool isNonRenderViewFixedPositionedContainer(RenderLayer*);
  46. InRegionScroller::InRegionScroller(WebPagePrivate* webPagePrivate)
  47. : d(new InRegionScrollerPrivate(webPagePrivate))
  48. {
  49. ASSERT(webPagePrivate);
  50. }
  51. InRegionScroller::~InRegionScroller()
  52. {
  53. delete d;
  54. }
  55. bool InRegionScroller::setDocumentScrollPositionCompositingThread(unsigned camouflagedLayer, const Platform::IntPoint& documentScrollPosition)
  56. {
  57. ASSERT(Platform::userInterfaceThreadMessageClient()->isCurrentThread());
  58. return d->setScrollPositionCompositingThread(camouflagedLayer, documentScrollPosition);
  59. }
  60. bool InRegionScroller::setDocumentScrollPositionWebKitThread(unsigned camouflagedLayer, const Platform::IntPoint& documentScrollPosition,
  61. bool supportsAcceleratedScrolling, Platform::ScrollViewBase::ScrollTarget scrollTarget)
  62. {
  63. ASSERT(Platform::webKitThreadMessageClient()->isCurrentThread());
  64. return d->setScrollPositionWebKitThread(camouflagedLayer, documentScrollPosition, supportsAcceleratedScrolling, scrollTarget);
  65. }
  66. InRegionScrollerPrivate::InRegionScrollerPrivate(WebPagePrivate* webPagePrivate)
  67. : m_webPage(webPagePrivate)
  68. , m_needsActiveScrollableAreaCalculation(false)
  69. , m_selectionScrollView(0)
  70. {
  71. }
  72. void InRegionScrollerPrivate::reset()
  73. {
  74. // Notify the client side to clear InRegion scrollable areas before we destroy them here.
  75. std::vector<Platform::ScrollViewBase*> emptyInRegionScrollableAreas;
  76. m_webPage->m_client->notifyInRegionScrollableAreasChanged(emptyInRegionScrollableAreas);
  77. m_needsActiveScrollableAreaCalculation = false;
  78. for (size_t i = 0; i < m_activeInRegionScrollableAreas.size(); ++i)
  79. delete m_activeInRegionScrollableAreas[i];
  80. m_activeInRegionScrollableAreas.clear();
  81. }
  82. void InRegionScrollerPrivate::resetSelectionScrollView()
  83. {
  84. m_webPage->m_client->notifySelectionScrollView(0);
  85. m_webPage->m_selectionHandler->setSelectionSubframeViewportRect(WebCore::IntRect());
  86. if (m_selectionScrollView) {
  87. delete m_selectionScrollView;
  88. m_selectionScrollView = 0;
  89. }
  90. }
  91. bool InRegionScrollerPrivate::isActive() const
  92. {
  93. return m_activeInRegionScrollableAreas.size() > 0;
  94. }
  95. void InRegionScrollerPrivate::clearDocumentData(const Document* documentGoingAway)
  96. {
  97. InRegionScrollableArea* scrollableArea = static_cast<InRegionScrollableArea*>(m_selectionScrollView);
  98. if (scrollableArea && scrollableArea->document() == documentGoingAway)
  99. resetSelectionScrollView();
  100. if (m_needsActiveScrollableAreaCalculation) {
  101. reset();
  102. return;
  103. }
  104. scrollableArea = static_cast<InRegionScrollableArea*>(m_activeInRegionScrollableAreas[0]);
  105. ASSERT(scrollableArea);
  106. if (scrollableArea->document() == documentGoingAway)
  107. reset();
  108. }
  109. bool InRegionScrollerPrivate::setScrollPositionCompositingThread(unsigned camouflagedLayer, const WebCore::IntPoint& scrollPosition)
  110. {
  111. LayerWebKitThread* layerWebKitThread = reinterpret_cast<LayerWebKitThread*>(camouflagedLayer);
  112. if (!isValidScrollableLayerWebKitThread(layerWebKitThread))
  113. return false;
  114. LayerCompositingThread* scrollLayer = layerWebKitThread->layerCompositingThread();
  115. // FIXME: Clamp maximum and minimum scroll positions as a last attempt to fix round errors.
  116. FloatPoint anchor;
  117. if (scrollLayer->override()->isAnchorPointSet())
  118. anchor = scrollLayer->override()->anchorPoint();
  119. else
  120. anchor = scrollLayer->anchorPoint();
  121. FloatSize bounds;
  122. if (scrollLayer->override()->isBoundsSet())
  123. bounds = scrollLayer->override()->bounds();
  124. else
  125. bounds = scrollLayer->bounds();
  126. // Position is offset on the layer by the layer anchor point.
  127. FloatPoint layerPosition(-scrollPosition.x() + anchor.x() * bounds.width(), -scrollPosition.y() + anchor.y() * bounds.height());
  128. scrollLayer->override()->setPosition(FloatPoint(layerPosition.x(), layerPosition.y()));
  129. // The client is going to blitVisibleContens, which allow us benefit from "defer blits" technique.
  130. return true;
  131. }
  132. bool InRegionScrollerPrivate::setScrollPositionWebKitThread(unsigned camouflagedLayer, const WebCore::IntPoint& scrollPosition,
  133. bool supportsAcceleratedScrolling, Platform::ScrollViewBase::ScrollTarget scrollTarget)
  134. {
  135. RenderLayer* layer = 0;
  136. if (supportsAcceleratedScrolling) {
  137. LayerWebKitThread* layerWebKitThread = reinterpret_cast<LayerWebKitThread*>(camouflagedLayer);
  138. if (!isValidScrollableLayerWebKitThread(layerWebKitThread))
  139. return false;
  140. if (layerWebKitThread->owner()) {
  141. GraphicsLayer* graphicsLayer = layerWebKitThread->owner();
  142. if (scrollTarget == Platform::ScrollViewBase::BlockElement) {
  143. RenderLayerBacking* backing = static_cast<RenderLayerBacking*>(graphicsLayer->client());
  144. layer = backing->owningLayer();
  145. } else {
  146. RenderLayerCompositor* compositor = static_cast<RenderLayerCompositor*>(graphicsLayer->client());
  147. layer = compositor->rootRenderLayer();
  148. }
  149. }
  150. } else {
  151. Node* node = reinterpret_cast<Node*>(camouflagedLayer);
  152. if (!isValidScrollableNode(node) || !node->renderer())
  153. return false;
  154. layer = node->renderer()->enclosingLayer();
  155. }
  156. if (!layer)
  157. return false;
  158. calculateActiveAndShrinkCachedScrollableAreas(layer);
  159. // FIXME: Clamp maximum and minimum scroll positions as a last attempt to fix round errors.
  160. return setLayerScrollPosition(layer, scrollPosition);
  161. }
  162. void InRegionScrollerPrivate::calculateActiveAndShrinkCachedScrollableAreas(RenderLayer* layer)
  163. {
  164. if (!m_needsActiveScrollableAreaCalculation)
  165. return;
  166. ASSERT(layer);
  167. std::vector<Platform::ScrollViewBase*>::iterator end = m_activeInRegionScrollableAreas.end();
  168. std::vector<Platform::ScrollViewBase*>::iterator it = m_activeInRegionScrollableAreas.begin();
  169. while (it != end) {
  170. InRegionScrollableArea* curr = static_cast<InRegionScrollableArea*>(*it);
  171. if (layer == curr->layer()) {
  172. ++it;
  173. continue;
  174. }
  175. delete *it;
  176. it = m_activeInRegionScrollableAreas.erase(it);
  177. // ::erase invalidates the iterators.
  178. end = m_activeInRegionScrollableAreas.end();
  179. }
  180. ASSERT(m_activeInRegionScrollableAreas.size() == 1);
  181. m_needsActiveScrollableAreaCalculation = false;
  182. }
  183. WebCore::IntRect InRegionScrollerPrivate::clipToRect(const WebCore::IntRect& clippingRect, InRegionScrollableArea* scrollable)
  184. {
  185. RenderLayer* layer = scrollable->layer();
  186. if (!layer)
  187. return clippingRect;
  188. const Platform::ViewportAccessor* viewportAccessor = m_webPage->m_webkitThreadViewportAccessor;
  189. if (layer->renderer()->isRenderView()) { // #document case
  190. FrameView* view = toRenderView(layer->renderer())->frameView();
  191. ASSERT(view);
  192. ASSERT(canScrollInnerFrame(view->frame()));
  193. WebCore::IntRect frameWindowRect = viewportAccessor->roundToPixelFromDocumentContents(WebCore::FloatRect(m_webPage->getRecursiveVisibleWindowRect(view)));
  194. frameWindowRect.intersect(clippingRect);
  195. return frameWindowRect;
  196. }
  197. RenderBox* box = layer->renderBox();
  198. ASSERT(box);
  199. ASSERT(canScrollRenderBox(box));
  200. // We want the window rect in pixel viewport coordinates clipped to the clipping rect.
  201. WebCore::IntRect visibleWindowRect = enclosingIntRect(box->absoluteClippedOverflowRect());
  202. visibleWindowRect = box->frame()->view()->contentsToWindow(visibleWindowRect);
  203. visibleWindowRect = viewportAccessor->roundToPixelFromDocumentContents(WebCore::FloatRect(visibleWindowRect));
  204. visibleWindowRect.intersect(clippingRect);
  205. return visibleWindowRect;
  206. }
  207. void InRegionScrollerPrivate::calculateInRegionScrollableAreasForPoint(const WebCore::IntPoint& documentPoint)
  208. {
  209. ASSERT(m_activeInRegionScrollableAreas.empty());
  210. m_needsActiveScrollableAreaCalculation = false;
  211. const HitTestResult& result = m_webPage->hitTestResult(documentPoint);
  212. Node* node = result.innerNonSharedNode();
  213. if (!node || !node->renderer())
  214. return;
  215. RenderLayer* layer = node->renderer()->enclosingLayer();
  216. if (!layer)
  217. return;
  218. do {
  219. RenderObject* renderer = layer->renderer();
  220. if (renderer && renderer->isRenderView()) {
  221. if (RenderView* renderView = toRenderView(renderer)) {
  222. FrameView* view = renderView->frameView();
  223. if (!view) {
  224. reset();
  225. return;
  226. }
  227. if (!renderView->compositor()->scrollLayer())
  228. continue;
  229. if (canScrollInnerFrame(view->frame())) {
  230. pushBackInRegionScrollable(new InRegionScrollableArea(m_webPage, layer));
  231. continue;
  232. }
  233. }
  234. } else if (canScrollRenderBox(layer->renderBox())) {
  235. pushBackInRegionScrollable(new InRegionScrollableArea(m_webPage, layer));
  236. continue;
  237. }
  238. // If we run into a fix positioned layer, set the last scrollable in-region object
  239. // as not able to propagate scroll to its parent scrollable.
  240. if (isNonRenderViewFixedPositionedContainer(layer) && m_activeInRegionScrollableAreas.size()) {
  241. Platform::ScrollViewBase* end = m_activeInRegionScrollableAreas.back();
  242. end->setCanPropagateScrollingToEnclosingScrollable(false);
  243. }
  244. } while ((layer = parentLayer(layer)));
  245. if (m_activeInRegionScrollableAreas.empty())
  246. return;
  247. m_needsActiveScrollableAreaCalculation = true;
  248. // Post-calculate the visible window rects in reverse hit test order so
  249. // we account for all and any clipping rects.
  250. WebCore::IntRect recursiveClippingRect(WebCore::IntPoint::zero(), m_webPage->transformedViewportSize());
  251. for (int i = m_activeInRegionScrollableAreas.size() - 1; i >= 0; --i) {
  252. InRegionScrollableArea* scrollable = static_cast<InRegionScrollableArea*>(m_activeInRegionScrollableAreas[i]);
  253. scrollable->setVisibleWindowRect(clipToRect(recursiveClippingRect, scrollable));
  254. recursiveClippingRect = scrollable->visibleWindowRect();
  255. }
  256. }
  257. void InRegionScrollerPrivate::updateSelectionScrollView(const Node* node)
  258. {
  259. // TODO: don't notify the client if the node didn't change.
  260. m_selectionScrollView = firstScrollableInRegionForNode(node);
  261. m_webPage->m_client->notifySelectionScrollView(m_selectionScrollView);
  262. // If there's no subframe set an empty rect so that we default to the main frame.
  263. m_webPage->m_selectionHandler->setSelectionSubframeViewportRect(m_selectionScrollView ? WebCore::IntRect(m_selectionScrollView->documentViewportRect()) : WebCore::IntRect());
  264. }
  265. Platform::ScrollViewBase* InRegionScrollerPrivate::firstScrollableInRegionForNode(const Node* node)
  266. {
  267. if (!node || !node->renderer())
  268. return 0;
  269. RenderLayer* layer = node->renderer()->enclosingLayer();
  270. if (!layer)
  271. return 0;
  272. do {
  273. RenderObject* renderer = layer->renderer();
  274. if (renderer->isRenderView()) {
  275. if (RenderView* renderView = toRenderView(renderer)) {
  276. FrameView* view = renderView->frameView();
  277. if (!view) {
  278. resetSelectionScrollView();
  279. return 0;
  280. }
  281. if (!renderView->compositor()->scrollLayer())
  282. continue;
  283. if (canScrollInnerFrame(view->frame()))
  284. return clipAndCreateInRegionScrollableArea(layer);
  285. }
  286. } else if (canScrollRenderBox(layer->renderBox()))
  287. return clipAndCreateInRegionScrollableArea(layer);
  288. // If we run into a fix positioned layer, set the last scrollable in-region object
  289. // as not able to propagate scroll to its parent scrollable.
  290. if (isNonRenderViewFixedPositionedContainer(layer) && m_activeInRegionScrollableAreas.size()) {
  291. Platform::ScrollViewBase* end = m_activeInRegionScrollableAreas.back();
  292. end->setCanPropagateScrollingToEnclosingScrollable(false);
  293. }
  294. } while ((layer = parentLayer(layer)));
  295. return 0;
  296. }
  297. Platform::ScrollViewBase* InRegionScrollerPrivate::clipAndCreateInRegionScrollableArea(RenderLayer* layer)
  298. {
  299. WebCore::IntRect recursiveClippingRect(WebCore::IntPoint::zero(), m_webPage->transformedViewportSize());
  300. InRegionScrollableArea* scrollable = new InRegionScrollableArea(m_webPage, layer);
  301. scrollable->setVisibleWindowRect(clipToRect(recursiveClippingRect, scrollable));
  302. return scrollable;
  303. }
  304. const std::vector<Platform::ScrollViewBase*>& InRegionScrollerPrivate::activeInRegionScrollableAreas() const
  305. {
  306. return m_activeInRegionScrollableAreas;
  307. }
  308. bool InRegionScrollerPrivate::setLayerScrollPosition(RenderLayer* layer, const IntPoint& scrollPosition)
  309. {
  310. RenderObject* layerRenderer = layer->renderer();
  311. ASSERT(layerRenderer);
  312. if (layerRenderer->isRenderView()) { // #document case.
  313. FrameView* view = toRenderView(layerRenderer)->frameView();
  314. ASSERT(view);
  315. Frame* frame = view->frame();
  316. ASSERT_UNUSED(frame, frame);
  317. ASSERT(canScrollInnerFrame(frame));
  318. view->setCanBlitOnScroll(false);
  319. view->setScrollPosition(scrollPosition);
  320. } else {
  321. // RenderBox-based elements case (scrollable boxes (div's, p's, textarea's, etc)).
  322. layer->scrollToOffset(toIntSize(scrollPosition));
  323. }
  324. layer->renderer()->frame()->selection()->updateAppearance();
  325. // FIXME: We have code in place to handle scrolling and clipping tap highlight
  326. // on in-region scrolling. As soon as it is fast enough (i.e. we have it backed by
  327. // a backing store), we can reliably make use of it in the real world.
  328. // m_touchEventHandler->drawTapHighlight();
  329. return true;
  330. }
  331. void InRegionScrollerPrivate::adjustScrollDelta(const WebCore::IntPoint& maxOffset, const WebCore::IntPoint& currentOffset, WebCore::IntSize& delta) const
  332. {
  333. if (currentOffset.x() + delta.width() > maxOffset.x())
  334. delta.setWidth(std::min(maxOffset.x() - currentOffset.x(), delta.width()));
  335. if (currentOffset.x() + delta.width() < 0)
  336. delta.setWidth(std::max(-currentOffset.x(), delta.width()));
  337. if (currentOffset.y() + delta.height() > maxOffset.y())
  338. delta.setHeight(std::min(maxOffset.y() - currentOffset.y(), delta.height()));
  339. if (currentOffset.y() + delta.height() < 0)
  340. delta.setHeight(std::max(-currentOffset.y(), delta.height()));
  341. }
  342. static bool canScrollInnerFrame(Frame* frame)
  343. {
  344. if (!frame || !frame->view())
  345. return false;
  346. // Not having an owner element means that we are on the mainframe.
  347. if (!frame->ownerElement())
  348. return false;
  349. ASSERT(frame != frame->page()->mainFrame());
  350. IntSize visibleSize = frame->view()->visibleContentRect().size();
  351. IntSize contentsSize = frame->view()->contentsSize();
  352. bool canBeScrolled = contentsSize.height() > visibleSize.height() || contentsSize.width() > visibleSize.width();
  353. // Lets also consider the 'overflow-{x,y} property set directly to the {i}frame tag.
  354. return canBeScrolled && (frame->ownerElement()->scrollingMode() != ScrollbarAlwaysOff);
  355. }
  356. // The RenderBox::canbeScrolledAndHasScrollableArea method returns true for the
  357. // following scenario, for example:
  358. // (1) a div that has a vertical overflow but no horizontal overflow
  359. // with overflow-y: hidden and overflow-x: auto set.
  360. // The version below fixes it.
  361. // FIXME: Fix RenderBox::canBeScrolledAndHasScrollableArea method instead.
  362. bool InRegionScrollerPrivate::canScrollRenderBox(RenderBox* box)
  363. {
  364. if (!box)
  365. return false;
  366. // We use this to make non-overflown contents layers to actually
  367. // be overscrollable.
  368. if (box->layer() && box->layer()->usesCompositedScrolling()) {
  369. if (box->style()->overflowScrolling() == OSBlackberryTouch)
  370. return true;
  371. }
  372. if (!box->hasOverflowClip())
  373. return false;
  374. if (box->scrollHeight() == box->clientHeight() && box->scrollWidth() == box->clientWidth())
  375. return false;
  376. if ((box->scrollsOverflowX() && (box->scrollWidth() != box->clientWidth()))
  377. || (box->scrollsOverflowY() && (box->scrollHeight() != box->clientHeight())))
  378. return true;
  379. Node* node = box->node();
  380. return node && (DOMSupport::isShadowHostTextInputElement(node) || node->isDocumentNode());
  381. }
  382. static RenderLayer* parentLayer(RenderLayer* layer)
  383. {
  384. ASSERT(layer);
  385. if (layer->parent())
  386. return layer->parent();
  387. RenderObject* renderer = layer->renderer();
  388. Document* document = renderer->document();
  389. if (document) {
  390. HTMLFrameOwnerElement* ownerElement = document->ownerElement();
  391. if (ownerElement) {
  392. RenderObject* subRenderer = ownerElement->renderer();
  393. if (subRenderer)
  394. return subRenderer->enclosingLayer();
  395. }
  396. }
  397. return 0;
  398. }
  399. static bool isNonRenderViewFixedPositionedContainer(RenderLayer* layer)
  400. {
  401. RenderObject* o = layer->renderer();
  402. if (o->isRenderView())
  403. return false;
  404. return o->isOutOfFlowPositioned() && o->style()->position() == FixedPosition;
  405. }
  406. void InRegionScrollerPrivate::pushBackInRegionScrollable(InRegionScrollableArea* scrollableArea)
  407. {
  408. ASSERT(!scrollableArea->isNull());
  409. scrollableArea->setCanPropagateScrollingToEnclosingScrollable(!isNonRenderViewFixedPositionedContainer(scrollableArea->layer()));
  410. m_activeInRegionScrollableAreas.push_back(scrollableArea);
  411. }
  412. bool InRegionScrollerPrivate::isValidScrollableLayerWebKitThread(LayerWebKitThread* layerWebKitThread) const
  413. {
  414. if (!layerWebKitThread)
  415. return false;
  416. for (unsigned i = 0; i < m_activeInRegionScrollableAreas.size(); ++i) {
  417. if (static_cast<InRegionScrollableArea*>(m_activeInRegionScrollableAreas[i])->cachedScrollableLayer() == layerWebKitThread)
  418. return true;
  419. }
  420. return false;
  421. }
  422. bool InRegionScrollerPrivate::isValidScrollableNode(Node* node) const
  423. {
  424. if (!node)
  425. return false;
  426. for (unsigned i = 0; i < m_activeInRegionScrollableAreas.size(); ++i) {
  427. if (static_cast<InRegionScrollableArea*>(m_activeInRegionScrollableAreas[i])->cachedScrollableNode() == node)
  428. return true;
  429. }
  430. return false;
  431. }
  432. }
  433. }