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