blob: df2bc6ff1f42e0f101704be75b8770ed836562a0 [file] [log] [blame]
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001/*
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
Torne (Richard Coles)926b0012013-03-28 15:32:48 +000030#include "SkMatrix44.h"
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000031#include "WebFrameImpl.h"
32#include "WebKit.h"
33#include "WebViewImpl.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010034#include "core/dom/Node.h"
Torne (Richard Coles)1e202182013-10-18 15:46:42 +010035#include "core/frame/FrameView.h"
Torne (Richard Coles)d5428f32014-03-18 10:21:16 +000036#include "core/frame/LocalFrame.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010037#include "core/rendering/RenderLayer.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010038#include "core/rendering/RenderLayerModelObject.h"
39#include "core/rendering/RenderObject.h"
40#include "core/rendering/RenderView.h"
Torne (Richard Coles)d5428f32014-03-18 10:21:16 +000041#include "core/rendering/compositing/CompositedLayerMapping.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010042#include "core/rendering/style/ShadowData.h"
Torne (Richard Coles)f79f16f2013-10-31 11:16:44 +000043#include "platform/graphics/Color.h"
Torne (Richard Coles)5267f702013-06-11 10:57:24 +010044#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"
Ben Murdoch591b9582013-07-10 11:41:44 +010051#include "wtf/CurrentTime.h"
Torne (Richard Coles)09380292014-02-21 12:17:33 +000052#include "wtf/Vector.h"
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000053
54using namespace WebCore;
55
Torne (Richard Coles)51b29062013-11-28 11:56:03 +000056namespace blink {
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000057
58class WebViewImpl;
59
60PassOwnPtr<LinkHighlight> LinkHighlight::create(Node* node, WebViewImpl* owningWebViewImpl)
61{
62 return adoptPtr(new LinkHighlight(node, owningWebViewImpl));
63}
64
65LinkHighlight::LinkHighlight(Node* node, WebViewImpl* owningWebViewImpl)
66 : m_node(node)
67 , m_owningWebViewImpl(owningWebViewImpl)
68 , m_currentGraphicsLayer(0)
69 , m_geometryNeedsUpdate(false)
70 , m_isAnimating(false)
Torne (Richard Coles)926b0012013-03-28 15:32:48 +000071 , m_startTime(monotonicallyIncreasingTime())
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000072{
73 ASSERT(m_node);
74 ASSERT(owningWebViewImpl);
75 WebCompositorSupport* compositorSupport = Platform::current()->compositorSupport();
76 m_contentLayer = adoptPtr(compositorSupport->createContentLayer(this));
77 m_clipLayer = adoptPtr(compositorSupport->createLayer());
78 m_clipLayer->setAnchorPoint(WebFloatPoint());
79 m_clipLayer->addChild(m_contentLayer->layer());
80 m_contentLayer->layer()->setAnimationDelegate(this);
81 m_contentLayer->layer()->setDrawsContent(true);
82 m_contentLayer->layer()->setOpacity(1);
83 m_geometryNeedsUpdate = true;
84 updateGeometry();
85}
86
87LinkHighlight::~LinkHighlight()
88{
89 clearGraphicsLayerLinkHighlightPointer();
90 releaseResources();
91}
92
93WebContentLayer* LinkHighlight::contentLayer()
94{
95 return m_contentLayer.get();
96}
97
98WebLayer* LinkHighlight::clipLayer()
99{
100 return m_clipLayer.get();
101}
102
103void LinkHighlight::releaseResources()
104{
105 m_node.clear();
106}
107
108RenderLayer* LinkHighlight::computeEnclosingCompositingLayer()
109{
110 if (!m_node || !m_node->renderer())
111 return 0;
112
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000113 // Find the nearest enclosing composited layer and attach to it. We may need to cross frame boundaries
114 // to find a suitable layer.
Torne (Richard Coles)1e202182013-10-18 15:46:42 +0100115 RenderObject* renderer = m_node->renderer();
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100116 RenderLayerModelObject* repaintContainer;
117 do {
118 repaintContainer = renderer->containerForRepaint();
119 if (!repaintContainer) {
120 renderer = renderer->frame()->ownerRenderer();
121 if (!renderer)
122 return 0;
123 }
124 } while (!repaintContainer);
125 RenderLayer* renderLayer = repaintContainer->layer();
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000126
Torne (Richard Coles)bfe35902013-10-22 16:41:51 +0100127 if (!renderLayer || renderLayer->compositingState() == NotComposited)
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000128 return 0;
129
Torne (Richard Coles)1e202182013-10-18 15:46:42 +0100130 GraphicsLayer* newGraphicsLayer = renderLayer->compositedLayerMapping()->mainGraphicsLayer();
Torne (Richard Coles)09380292014-02-21 12:17:33 +0000131 m_clipLayer->setTransform(SkMatrix44());
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100132
133 if (!newGraphicsLayer->drawsContent()) {
Torne (Richard Coles)09380292014-02-21 12:17:33 +0000134 if (renderLayer->scrollableArea() && renderLayer->scrollableArea()->usesCompositedScrolling()) {
Torne (Richard Coles)51b29062013-11-28 11:56:03 +0000135 ASSERT(renderLayer->hasCompositedLayerMapping() && renderLayer->compositedLayerMapping()->scrollingContentsLayer());
Torne (Richard Coles)1e202182013-10-18 15:46:42 +0100136 newGraphicsLayer = renderLayer->compositedLayerMapping()->scrollingContentsLayer();
Torne (Richard Coles)09380292014-02-21 12:17:33 +0000137 }
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000138 }
139
140 if (m_currentGraphicsLayer != newGraphicsLayer) {
141 if (m_currentGraphicsLayer)
142 clearGraphicsLayerLinkHighlightPointer();
143
144 m_currentGraphicsLayer = newGraphicsLayer;
Torne (Richard Coles)9bbd2f52013-09-19 22:37:05 +0100145 m_currentGraphicsLayer->addLinkHighlight(this);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000146 }
147
148 return renderLayer;
149}
150
151static void convertTargetSpaceQuadToCompositedLayer(const FloatQuad& targetSpaceQuad, RenderObject* targetRenderer, RenderObject* compositedRenderer, FloatQuad& compositedSpaceQuad)
152{
153 ASSERT(targetRenderer);
154 ASSERT(compositedRenderer);
155
156 for (unsigned i = 0; i < 4; ++i) {
157 IntPoint point;
158 switch (i) {
159 case 0: point = roundedIntPoint(targetSpaceQuad.p1()); break;
160 case 1: point = roundedIntPoint(targetSpaceQuad.p2()); break;
161 case 2: point = roundedIntPoint(targetSpaceQuad.p3()); break;
162 case 3: point = roundedIntPoint(targetSpaceQuad.p4()); break;
163 }
164
165 point = targetRenderer->frame()->view()->contentsToWindow(point);
166 point = compositedRenderer->frame()->view()->windowToContents(point);
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000167 FloatPoint floatPoint = compositedRenderer->absoluteToLocal(point, UseTransforms);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000168
169 switch (i) {
170 case 0: compositedSpaceQuad.setP1(floatPoint); break;
171 case 1: compositedSpaceQuad.setP2(floatPoint); break;
172 case 2: compositedSpaceQuad.setP3(floatPoint); break;
173 case 3: compositedSpaceQuad.setP4(floatPoint); break;
174 }
175 }
176}
177
178static void addQuadToPath(const FloatQuad& quad, Path& path)
179{
180 // FIXME: Make this create rounded quad-paths, just like the axis-aligned case.
181 path.moveTo(quad.p1());
182 path.addLineTo(quad.p2());
183 path.addLineTo(quad.p3());
184 path.addLineTo(quad.p4());
185 path.closeSubpath();
186}
187
Torne (Richard Coles)09380292014-02-21 12:17:33 +0000188void LinkHighlight::computeQuads(Node* node, Vector<FloatQuad>& outQuads) const
189{
190 if (!node || !node->renderer())
191 return;
192
193 RenderObject* renderer = node->renderer();
194
195 // For inline elements, absoluteQuads will return a line box based on the line-height
196 // and font metrics, which is technically incorrect as replaced elements like images
197 // should use their intristic height and expand the linebox as needed. To get an
198 // appropriately sized highlight we descend into the children and have them add their
199 // boxes.
200 if (renderer->isRenderInline()) {
201 for (Node* child = node->firstChild(); child; child = child->nextSibling())
202 computeQuads(child, outQuads);
203 } else {
204 renderer->absoluteQuads(outQuads);
205 }
206
207}
208
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000209bool LinkHighlight::computeHighlightLayerPathAndPosition(RenderLayer* compositingLayer)
210{
Torne (Richard Coles)09380292014-02-21 12:17:33 +0000211 if (!m_node || !m_node->renderer() || !m_currentGraphicsLayer)
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000212 return false;
213
214 ASSERT(compositingLayer);
215
216 // Get quads for node in absolute coordinates.
217 Vector<FloatQuad> quads;
Torne (Richard Coles)09380292014-02-21 12:17:33 +0000218 computeQuads(m_node.get(), quads);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000219 ASSERT(quads.size());
220
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100221 // Adjust for offset between target graphics layer and the node's renderer.
222 FloatPoint positionAdjust = IntPoint(m_currentGraphicsLayer->offsetFromRenderer());
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100223
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000224 Path newPath;
Torne (Richard Coles)09380292014-02-21 12:17:33 +0000225 for (size_t quadIndex = 0; quadIndex < quads.size(); ++quadIndex) {
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100226 FloatQuad absoluteQuad = quads[quadIndex];
227 absoluteQuad.move(-positionAdjust.x(), -positionAdjust.y());
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000228
229 // Transform node quads in target absolute coords to local coordinates in the compositor layer.
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100230 FloatQuad transformedQuad;
231 convertTargetSpaceQuadToCompositedLayer(absoluteQuad, m_node->renderer(), compositingLayer->renderer(), transformedQuad);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000232
233 // FIXME: for now, we'll only use rounded paths if we have a single node quad. The reason for this is that
234 // we may sometimes get a chain of adjacent boxes (e.g. for text nodes) which end up looking like sausage
235 // links: these should ideally be merged into a single rect before creating the path, but that's
236 // another CL.
237 if (quads.size() == 1 && transformedQuad.isRectilinear()) {
238 FloatSize rectRoundingRadii(3, 3);
239 newPath.addRoundedRect(transformedQuad.boundingBox(), rectRoundingRadii);
240 } else
241 addQuadToPath(transformedQuad, newPath);
242 }
243
244 FloatRect boundingRect = newPath.boundingRect();
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000245 newPath.translate(-toFloatSize(boundingRect.location()));
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000246
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100247 bool pathHasChanged = !(newPath == m_path);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000248 if (pathHasChanged) {
249 m_path = newPath;
250 m_contentLayer->layer()->setBounds(enclosingIntRect(boundingRect).size());
251 }
252
253 m_contentLayer->layer()->setPosition(boundingRect.location());
254
255 return pathHasChanged;
256}
257
258void LinkHighlight::paintContents(WebCanvas* canvas, const WebRect& webClipRect, bool, WebFloatRect&)
259{
260 if (!m_node || !m_node->renderer())
261 return;
262
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100263 GraphicsContext gc(canvas);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000264 IntRect clipRect(IntPoint(webClipRect.x, webClipRect.y), IntSize(webClipRect.width, webClipRect.height));
265 gc.clip(clipRect);
Torne (Richard Coles)8abfc582013-09-12 12:10:38 +0100266 gc.setFillColor(m_node->renderer()->style()->tapHighlightColor());
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000267 gc.fillPath(m_path);
268}
269
270void LinkHighlight::startHighlightAnimationIfNeeded()
271{
272 if (m_isAnimating)
273 return;
274
275 m_isAnimating = true;
276 const float startOpacity = 1;
277 // FIXME: Should duration be configurable?
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000278 const float fadeDuration = 0.1f;
279 const float minPreFadeDuration = 0.1f;
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000280
281 m_contentLayer->layer()->setOpacity(startOpacity);
282
283 WebCompositorSupport* compositorSupport = Platform::current()->compositorSupport();
284
285 OwnPtr<WebFloatAnimationCurve> curve = adoptPtr(compositorSupport->createFloatAnimationCurve());
286
287 curve->add(WebFloatKeyframe(0, startOpacity));
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000288 // Make sure we have displayed for at least minPreFadeDuration before starting to fade out.
289 float extraDurationRequired = std::max(0.f, minPreFadeDuration - static_cast<float>(monotonicallyIncreasingTime() - m_startTime));
290 if (extraDurationRequired)
291 curve->add(WebFloatKeyframe(extraDurationRequired, startOpacity));
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000292 // For layout tests we don't fade out.
Torne (Richard Coles)51b29062013-11-28 11:56:03 +0000293 curve->add(WebFloatKeyframe(fadeDuration + extraDurationRequired, blink::layoutTestMode() ? startOpacity : 0));
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000294
Torne (Richard Coles)bfe35902013-10-22 16:41:51 +0100295 OwnPtr<WebAnimation> animation = adoptPtr(compositorSupport->createAnimation(*curve, WebAnimation::TargetPropertyOpacity));
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000296
297 m_contentLayer->layer()->setDrawsContent(true);
Torne (Richard Coles)bfe35902013-10-22 16:41:51 +0100298 m_contentLayer->layer()->addAnimation(animation.leakPtr());
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000299
300 invalidate();
301 m_owningWebViewImpl->scheduleAnimation();
302}
303
304void LinkHighlight::clearGraphicsLayerLinkHighlightPointer()
305{
306 if (m_currentGraphicsLayer) {
Torne (Richard Coles)9bbd2f52013-09-19 22:37:05 +0100307 m_currentGraphicsLayer->removeLinkHighlight(this);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000308 m_currentGraphicsLayer = 0;
309 }
310}
311
Torne (Richard Coles)d5428f32014-03-18 10:21:16 +0000312void LinkHighlight::notifyAnimationStarted(double, blink::WebAnimation::TargetProperty)
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000313{
314}
315
Torne (Richard Coles)d5428f32014-03-18 10:21:16 +0000316void LinkHighlight::notifyAnimationFinished(double, blink::WebAnimation::TargetProperty)
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000317{
318 // Since WebViewImpl may hang on to us for a while, make sure we
319 // release resources as soon as possible.
320 clearGraphicsLayerLinkHighlightPointer();
321 releaseResources();
322}
323
324void LinkHighlight::updateGeometry()
325{
326 // To avoid unnecessary updates (e.g. other entities have requested animations from our WebViewImpl),
327 // only proceed if we actually requested an update.
328 if (!m_geometryNeedsUpdate)
329 return;
330
331 m_geometryNeedsUpdate = false;
332
333 RenderLayer* compositingLayer = computeEnclosingCompositingLayer();
334 if (compositingLayer && computeHighlightLayerPathAndPosition(compositingLayer)) {
335 // We only need to invalidate the layer if the highlight size has changed, otherwise
336 // we can just re-position the layer without needing to repaint.
337 m_contentLayer->layer()->invalidate();
338
339 if (m_currentGraphicsLayer)
340 m_currentGraphicsLayer->addRepaintRect(FloatRect(layer()->position().x, layer()->position().y, layer()->bounds().width, layer()->bounds().height));
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100341 } else if (!m_node || !m_node->renderer()) {
Torne (Richard Coles)81a51572013-05-13 16:52:28 +0100342 clearGraphicsLayerLinkHighlightPointer();
343 releaseResources();
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000344 }
345}
346
347void LinkHighlight::clearCurrentGraphicsLayer()
348{
349 m_currentGraphicsLayer = 0;
350 m_geometryNeedsUpdate = true;
351}
352
353void LinkHighlight::invalidate()
354{
355 // Make sure we update geometry on the next callback from WebViewImpl::layout().
356 m_geometryNeedsUpdate = true;
357}
358
359WebLayer* LinkHighlight::layer()
360{
361 return clipLayer();
362}
363
364} // namespace WeKit