Home | History | Annotate | Download | only in web
      1 /*
      2  * Copyright (C) 2013 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 "ViewportAnchor.h"
     33 
     34 #include "core/dom/ContainerNode.h"
     35 #include "core/dom/Node.h"
     36 #include "core/page/EventHandler.h"
     37 #include "core/rendering/HitTestResult.h"
     38 
     39 using namespace WebCore;
     40 
     41 namespace blink {
     42 
     43 namespace {
     44 
     45 static const float viewportAnchorRelativeEpsilon = 0.1f;
     46 static const int viewportToNodeMaxRelativeArea = 2;
     47 
     48 template <typename RectType>
     49 int area(const RectType& rect) {
     50     return rect.width() * rect.height();
     51 }
     52 
     53 Node* findNonEmptyAnchorNode(const IntPoint& point, const IntRect& viewRect, EventHandler* eventHandler)
     54 {
     55     Node* node = eventHandler->hitTestResultAtPoint(point, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent).innerNode();
     56 
     57     // If the node bounding box is sufficiently large, make a single attempt to
     58     // find a smaller node; the larger the node bounds, the greater the
     59     // variability under resize.
     60     const int maxNodeArea = area(viewRect) * viewportToNodeMaxRelativeArea;
     61     if (node && area(node->boundingBox()) > maxNodeArea) {
     62         IntSize pointOffset = viewRect.size();
     63         pointOffset.scale(viewportAnchorRelativeEpsilon);
     64         node = eventHandler->hitTestResultAtPoint(point + pointOffset, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent).innerNode();
     65     }
     66 
     67     while (node && node->boundingBox().isEmpty())
     68         node = node->parentNode();
     69 
     70     return node;
     71 }
     72 
     73 } // namespace
     74 
     75 ViewportAnchor::ViewportAnchor(EventHandler* eventHandler)
     76     : m_eventHandler(eventHandler) { }
     77 
     78 void ViewportAnchor::setAnchor(const IntRect& viewRect, const FloatSize& anchorInViewCoords)
     79 {
     80     m_viewRect = viewRect;
     81     m_anchorNode.clear();
     82     m_anchorNodeBounds = LayoutRect();
     83     m_anchorInNodeCoords = FloatSize();
     84     m_anchorInViewCoords = anchorInViewCoords;
     85 
     86     if (viewRect.isEmpty())
     87         return;
     88 
     89     // Preserve origins at the absolute screen origin
     90     if (viewRect.location() == IntPoint::zero())
     91         return;
     92 
     93     FloatSize anchorOffset = viewRect.size();
     94     anchorOffset.scale(anchorInViewCoords.width(), anchorInViewCoords.height());
     95     const FloatPoint anchorPoint = FloatPoint(viewRect.location()) + anchorOffset;
     96 
     97     Node* node = findNonEmptyAnchorNode(flooredIntPoint(anchorPoint), viewRect, m_eventHandler);
     98     if (!node)
     99         return;
    100 
    101     m_anchorNode = node;
    102     m_anchorNodeBounds = node->boundingBox();
    103     m_anchorInNodeCoords = anchorPoint - m_anchorNodeBounds.location();
    104     m_anchorInNodeCoords.scale(1.f / m_anchorNodeBounds.width(), 1.f / m_anchorNodeBounds.height());
    105 }
    106 
    107 IntPoint ViewportAnchor::computeOrigin(const IntSize& currentViewSize) const
    108 {
    109     if (!m_anchorNode || !m_anchorNode->inDocument())
    110         return m_viewRect.location();
    111 
    112     const LayoutRect currentNodeBounds = m_anchorNode->boundingBox();
    113     if (m_anchorNodeBounds == currentNodeBounds)
    114         return m_viewRect.location();
    115 
    116     // Compute the new anchor point relative to the node position
    117     FloatSize anchorOffsetFromNode = currentNodeBounds.size();
    118     anchorOffsetFromNode.scale(m_anchorInNodeCoords.width(), m_anchorInNodeCoords.height());
    119     FloatPoint anchorPoint = currentNodeBounds.location() + anchorOffsetFromNode;
    120 
    121     // Compute the new origin point relative to the new anchor point
    122     FloatSize anchorOffsetFromOrigin = currentViewSize;
    123     anchorOffsetFromOrigin.scale(m_anchorInViewCoords.width(), m_anchorInViewCoords.height());
    124     return flooredIntPoint(anchorPoint - anchorOffsetFromOrigin);
    125 }
    126 
    127 } // namespace blink
    128