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 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27 28#include "LinkHighlight.h" 29 30#include "SkMatrix44.h" 31#include "WebFrameImpl.h" 32#include "WebKit.h" 33#include "WebViewImpl.h" 34#include "core/dom/Node.h" 35#include "core/frame/Frame.h" 36#include "core/frame/FrameView.h" 37#include "core/rendering/CompositedLayerMapping.h" 38#include "core/rendering/RenderLayer.h" 39#include "core/rendering/RenderLayerModelObject.h" 40#include "core/rendering/RenderObject.h" 41#include "core/rendering/RenderView.h" 42#include "core/rendering/style/ShadowData.h" 43#include "platform/graphics/Color.h" 44#include "public/platform/Platform.h" 45#include "public/platform/WebAnimationCurve.h" 46#include "public/platform/WebCompositorSupport.h" 47#include "public/platform/WebFloatAnimationCurve.h" 48#include "public/platform/WebFloatPoint.h" 49#include "public/platform/WebRect.h" 50#include "public/platform/WebSize.h" 51#include "wtf/CurrentTime.h" 52 53using namespace WebCore; 54 55namespace blink { 56 57class WebViewImpl; 58 59PassOwnPtr<LinkHighlight> LinkHighlight::create(Node* node, WebViewImpl* owningWebViewImpl) 60{ 61 return adoptPtr(new LinkHighlight(node, owningWebViewImpl)); 62} 63 64LinkHighlight::LinkHighlight(Node* node, WebViewImpl* owningWebViewImpl) 65 : m_node(node) 66 , m_owningWebViewImpl(owningWebViewImpl) 67 , m_currentGraphicsLayer(0) 68 , m_geometryNeedsUpdate(false) 69 , m_isAnimating(false) 70 , m_startTime(monotonicallyIncreasingTime()) 71{ 72 ASSERT(m_node); 73 ASSERT(owningWebViewImpl); 74 WebCompositorSupport* compositorSupport = Platform::current()->compositorSupport(); 75 m_contentLayer = adoptPtr(compositorSupport->createContentLayer(this)); 76 m_clipLayer = adoptPtr(compositorSupport->createLayer()); 77 m_clipLayer->setAnchorPoint(WebFloatPoint()); 78 m_clipLayer->addChild(m_contentLayer->layer()); 79 m_contentLayer->layer()->setAnimationDelegate(this); 80 m_contentLayer->layer()->setDrawsContent(true); 81 m_contentLayer->layer()->setOpacity(1); 82 m_geometryNeedsUpdate = true; 83 updateGeometry(); 84} 85 86LinkHighlight::~LinkHighlight() 87{ 88 clearGraphicsLayerLinkHighlightPointer(); 89 releaseResources(); 90} 91 92WebContentLayer* LinkHighlight::contentLayer() 93{ 94 return m_contentLayer.get(); 95} 96 97WebLayer* LinkHighlight::clipLayer() 98{ 99 return m_clipLayer.get(); 100} 101 102void LinkHighlight::releaseResources() 103{ 104 m_node.clear(); 105} 106 107RenderLayer* LinkHighlight::computeEnclosingCompositingLayer() 108{ 109 if (!m_node || !m_node->renderer()) 110 return 0; 111 112 // Find the nearest enclosing composited layer and attach to it. We may need to cross frame boundaries 113 // to find a suitable layer. 114 RenderObject* renderer = m_node->renderer(); 115 RenderLayerModelObject* repaintContainer; 116 do { 117 repaintContainer = renderer->containerForRepaint(); 118 if (!repaintContainer) { 119 renderer = renderer->frame()->ownerRenderer(); 120 if (!renderer) 121 return 0; 122 } 123 } while (!repaintContainer); 124 RenderLayer* renderLayer = repaintContainer->layer(); 125 126 if (!renderLayer || renderLayer->compositingState() == NotComposited) 127 return 0; 128 129 GraphicsLayer* newGraphicsLayer = renderLayer->compositedLayerMapping()->mainGraphicsLayer(); 130 m_clipLayer->setSublayerTransform(SkMatrix44()); 131 132 if (!newGraphicsLayer->drawsContent()) { 133 if (renderLayer->scrollableArea() && renderLayer->scrollableArea()->usesCompositedScrolling()) { 134 ASSERT(renderLayer->hasCompositedLayerMapping() && renderLayer->compositedLayerMapping()->scrollingContentsLayer()); 135 newGraphicsLayer = renderLayer->compositedLayerMapping()->scrollingContentsLayer(); 136 } 137 } 138 139 if (m_currentGraphicsLayer != newGraphicsLayer) { 140 if (m_currentGraphicsLayer) 141 clearGraphicsLayerLinkHighlightPointer(); 142 143 m_currentGraphicsLayer = newGraphicsLayer; 144 m_currentGraphicsLayer->addLinkHighlight(this); 145 } 146 147 return renderLayer; 148} 149 150static void convertTargetSpaceQuadToCompositedLayer(const FloatQuad& targetSpaceQuad, RenderObject* targetRenderer, RenderObject* compositedRenderer, FloatQuad& compositedSpaceQuad) 151{ 152 ASSERT(targetRenderer); 153 ASSERT(compositedRenderer); 154 155 for (unsigned i = 0; i < 4; ++i) { 156 IntPoint point; 157 switch (i) { 158 case 0: point = roundedIntPoint(targetSpaceQuad.p1()); break; 159 case 1: point = roundedIntPoint(targetSpaceQuad.p2()); break; 160 case 2: point = roundedIntPoint(targetSpaceQuad.p3()); break; 161 case 3: point = roundedIntPoint(targetSpaceQuad.p4()); break; 162 } 163 164 point = targetRenderer->frame()->view()->contentsToWindow(point); 165 point = compositedRenderer->frame()->view()->windowToContents(point); 166 FloatPoint floatPoint = compositedRenderer->absoluteToLocal(point, UseTransforms); 167 168 switch (i) { 169 case 0: compositedSpaceQuad.setP1(floatPoint); break; 170 case 1: compositedSpaceQuad.setP2(floatPoint); break; 171 case 2: compositedSpaceQuad.setP3(floatPoint); break; 172 case 3: compositedSpaceQuad.setP4(floatPoint); break; 173 } 174 } 175} 176 177static void addQuadToPath(const FloatQuad& quad, Path& path) 178{ 179 // FIXME: Make this create rounded quad-paths, just like the axis-aligned case. 180 path.moveTo(quad.p1()); 181 path.addLineTo(quad.p2()); 182 path.addLineTo(quad.p3()); 183 path.addLineTo(quad.p4()); 184 path.closeSubpath(); 185} 186 187bool LinkHighlight::computeHighlightLayerPathAndPosition(RenderLayer* compositingLayer) 188{ 189 if (!m_node || !m_node->renderer() || !m_currentGraphicsLayer) 190 return false; 191 192 ASSERT(compositingLayer); 193 194 // Get quads for node in absolute coordinates. 195 Vector<FloatQuad> quads; 196 m_node->renderer()->absoluteQuads(quads); 197 ASSERT(quads.size()); 198 199 // Adjust for offset between target graphics layer and the node's renderer. 200 FloatPoint positionAdjust = IntPoint(m_currentGraphicsLayer->offsetFromRenderer()); 201 202 Path newPath; 203 for (unsigned quadIndex = 0; quadIndex < quads.size(); ++quadIndex) { 204 FloatQuad absoluteQuad = quads[quadIndex]; 205 absoluteQuad.move(-positionAdjust.x(), -positionAdjust.y()); 206 207 // Transform node quads in target absolute coords to local coordinates in the compositor layer. 208 FloatQuad transformedQuad; 209 convertTargetSpaceQuadToCompositedLayer(absoluteQuad, m_node->renderer(), compositingLayer->renderer(), transformedQuad); 210 211 // FIXME: for now, we'll only use rounded paths if we have a single node quad. The reason for this is that 212 // we may sometimes get a chain of adjacent boxes (e.g. for text nodes) which end up looking like sausage 213 // links: these should ideally be merged into a single rect before creating the path, but that's 214 // another CL. 215 if (quads.size() == 1 && transformedQuad.isRectilinear()) { 216 FloatSize rectRoundingRadii(3, 3); 217 newPath.addRoundedRect(transformedQuad.boundingBox(), rectRoundingRadii); 218 } else 219 addQuadToPath(transformedQuad, newPath); 220 } 221 222 FloatRect boundingRect = newPath.boundingRect(); 223 newPath.translate(-toFloatSize(boundingRect.location())); 224 225 bool pathHasChanged = !(newPath == m_path); 226 if (pathHasChanged) { 227 m_path = newPath; 228 m_contentLayer->layer()->setBounds(enclosingIntRect(boundingRect).size()); 229 } 230 231 m_contentLayer->layer()->setPosition(boundingRect.location()); 232 233 return pathHasChanged; 234} 235 236void LinkHighlight::paintContents(WebCanvas* canvas, const WebRect& webClipRect, bool, WebFloatRect&) 237{ 238 if (!m_node || !m_node->renderer()) 239 return; 240 241 GraphicsContext gc(canvas); 242 IntRect clipRect(IntPoint(webClipRect.x, webClipRect.y), IntSize(webClipRect.width, webClipRect.height)); 243 gc.clip(clipRect); 244 gc.setFillColor(m_node->renderer()->style()->tapHighlightColor()); 245 gc.fillPath(m_path); 246} 247 248void LinkHighlight::startHighlightAnimationIfNeeded() 249{ 250 if (m_isAnimating) 251 return; 252 253 m_isAnimating = true; 254 const float startOpacity = 1; 255 // FIXME: Should duration be configurable? 256 const float fadeDuration = 0.1f; 257 const float minPreFadeDuration = 0.1f; 258 259 m_contentLayer->layer()->setOpacity(startOpacity); 260 261 WebCompositorSupport* compositorSupport = Platform::current()->compositorSupport(); 262 263 OwnPtr<WebFloatAnimationCurve> curve = adoptPtr(compositorSupport->createFloatAnimationCurve()); 264 265 curve->add(WebFloatKeyframe(0, startOpacity)); 266 // Make sure we have displayed for at least minPreFadeDuration before starting to fade out. 267 float extraDurationRequired = std::max(0.f, minPreFadeDuration - static_cast<float>(monotonicallyIncreasingTime() - m_startTime)); 268 if (extraDurationRequired) 269 curve->add(WebFloatKeyframe(extraDurationRequired, startOpacity)); 270 // For layout tests we don't fade out. 271 curve->add(WebFloatKeyframe(fadeDuration + extraDurationRequired, blink::layoutTestMode() ? startOpacity : 0)); 272 273 OwnPtr<WebAnimation> animation = adoptPtr(compositorSupport->createAnimation(*curve, WebAnimation::TargetPropertyOpacity)); 274 275 m_contentLayer->layer()->setDrawsContent(true); 276 m_contentLayer->layer()->addAnimation(animation.leakPtr()); 277 278 invalidate(); 279 m_owningWebViewImpl->scheduleAnimation(); 280} 281 282void LinkHighlight::clearGraphicsLayerLinkHighlightPointer() 283{ 284 if (m_currentGraphicsLayer) { 285 m_currentGraphicsLayer->removeLinkHighlight(this); 286 m_currentGraphicsLayer = 0; 287 } 288} 289 290void LinkHighlight::notifyAnimationStarted(double, double, blink::WebAnimation::TargetProperty) 291{ 292} 293 294void LinkHighlight::notifyAnimationFinished(double, double, blink::WebAnimation::TargetProperty) 295{ 296 // Since WebViewImpl may hang on to us for a while, make sure we 297 // release resources as soon as possible. 298 clearGraphicsLayerLinkHighlightPointer(); 299 releaseResources(); 300} 301 302void LinkHighlight::updateGeometry() 303{ 304 // To avoid unnecessary updates (e.g. other entities have requested animations from our WebViewImpl), 305 // only proceed if we actually requested an update. 306 if (!m_geometryNeedsUpdate) 307 return; 308 309 m_geometryNeedsUpdate = false; 310 311 RenderLayer* compositingLayer = computeEnclosingCompositingLayer(); 312 if (compositingLayer && computeHighlightLayerPathAndPosition(compositingLayer)) { 313 // We only need to invalidate the layer if the highlight size has changed, otherwise 314 // we can just re-position the layer without needing to repaint. 315 m_contentLayer->layer()->invalidate(); 316 317 if (m_currentGraphicsLayer) 318 m_currentGraphicsLayer->addRepaintRect(FloatRect(layer()->position().x, layer()->position().y, layer()->bounds().width, layer()->bounds().height)); 319 } else if (!m_node || !m_node->renderer()) { 320 clearGraphicsLayerLinkHighlightPointer(); 321 releaseResources(); 322 } 323} 324 325void LinkHighlight::clearCurrentGraphicsLayer() 326{ 327 m_currentGraphicsLayer = 0; 328 m_geometryNeedsUpdate = true; 329} 330 331void LinkHighlight::invalidate() 332{ 333 // Make sure we update geometry on the next callback from WebViewImpl::layout(). 334 m_geometryNeedsUpdate = true; 335} 336 337WebLayer* LinkHighlight::layer() 338{ 339 return clipLayer(); 340} 341 342} // namespace WeKit 343