Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / web / LinkHighlightImpl.cpp
blob1305fbc5a1d701e9f48723420ab592232b94b642
1 /*
2 * Copyright (C) 2012 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
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.
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.
26 #include "config.h"
27 #include "web/LinkHighlightImpl.h"
29 #include "core/dom/LayoutTreeBuilderTraversal.h"
30 #include "core/dom/Node.h"
31 #include "core/frame/FrameView.h"
32 #include "core/frame/LocalFrame.h"
33 #include "core/layout/LayoutBoxModelObject.h"
34 #include "core/layout/LayoutObject.h"
35 #include "core/layout/LayoutView.h"
36 #include "core/layout/compositing/CompositedDeprecatedPaintLayerMapping.h"
37 #include "core/paint/DeprecatedPaintLayer.h"
38 #include "platform/RuntimeEnabledFeatures.h"
39 #include "platform/graphics/Color.h"
40 #include "platform/graphics/GraphicsLayer.h"
41 #include "platform/graphics/paint/DrawingRecorder.h"
42 #include "public/platform/Platform.h"
43 #include "public/platform/WebCompositorAnimationCurve.h"
44 #include "public/platform/WebCompositorSupport.h"
45 #include "public/platform/WebContentLayer.h"
46 #include "public/platform/WebDisplayItemList.h"
47 #include "public/platform/WebFloatAnimationCurve.h"
48 #include "public/platform/WebFloatPoint.h"
49 #include "public/platform/WebLayer.h"
50 #include "public/platform/WebRect.h"
51 #include "public/platform/WebSize.h"
52 #include "public/web/WebKit.h"
53 #include "third_party/skia/include/core/SkCanvas.h"
54 #include "third_party/skia/include/core/SkPictureRecorder.h"
55 #include "third_party/skia/include/utils/SkMatrix44.h"
56 #include "web/WebLocalFrameImpl.h"
57 #include "web/WebSettingsImpl.h"
58 #include "web/WebViewImpl.h"
59 #include "wtf/CurrentTime.h"
60 #include "wtf/Vector.h"
62 namespace blink {
64 PassOwnPtr<LinkHighlightImpl> LinkHighlightImpl::create(Node* node, WebViewImpl* owningWebViewImpl)
66 return adoptPtr(new LinkHighlightImpl(node, owningWebViewImpl));
69 LinkHighlightImpl::LinkHighlightImpl(Node* node, WebViewImpl* owningWebViewImpl)
70 : m_node(node)
71 , m_owningWebViewImpl(owningWebViewImpl)
72 , m_currentGraphicsLayer(0)
73 , m_geometryNeedsUpdate(false)
74 , m_isAnimating(false)
75 , m_startTime(monotonicallyIncreasingTime())
77 ASSERT(m_node);
78 ASSERT(owningWebViewImpl);
79 WebCompositorSupport* compositorSupport = Platform::current()->compositorSupport();
80 ASSERT(compositorSupport);
81 m_contentLayer = adoptPtr(compositorSupport->createContentLayer(this));
82 m_clipLayer = adoptPtr(compositorSupport->createLayer());
83 m_clipLayer->setTransformOrigin(WebFloatPoint3D());
84 m_clipLayer->addChild(m_contentLayer->layer());
85 if (RuntimeEnabledFeatures::compositorAnimationTimelinesEnabled() && Platform::current()->isThreadedAnimationEnabled()) {
86 m_compositorPlayer = adoptPtr(compositorSupport->createAnimationPlayer());
87 ASSERT(m_compositorPlayer);
88 m_compositorPlayer->setAnimationDelegate(this);
89 m_owningWebViewImpl->linkHighlightsTimeline()->playerAttached(*this);
90 m_compositorPlayer->attachLayer(m_contentLayer->layer());
91 } else {
92 owningWebViewImpl->registerForAnimations(m_contentLayer->layer());
93 m_contentLayer->layer()->setAnimationDelegate(this);
95 m_contentLayer->layer()->setDrawsContent(true);
96 m_contentLayer->layer()->setOpacity(1);
97 m_geometryNeedsUpdate = true;
98 updateGeometry();
101 LinkHighlightImpl::~LinkHighlightImpl()
103 if (m_compositorPlayer) {
104 m_compositorPlayer->detachLayer();
105 m_owningWebViewImpl->linkHighlightsTimeline()->playerDestroyed(*this);
106 m_compositorPlayer->setAnimationDelegate(nullptr);
108 m_compositorPlayer.clear();
110 clearGraphicsLayerLinkHighlightPointer();
111 releaseResources();
114 WebContentLayer* LinkHighlightImpl::contentLayer()
116 return m_contentLayer.get();
119 WebLayer* LinkHighlightImpl::clipLayer()
121 return m_clipLayer.get();
124 void LinkHighlightImpl::releaseResources()
126 m_node.clear();
129 void LinkHighlightImpl::attachLinkHighlightToCompositingLayer(const LayoutBoxModelObject* paintInvalidationContainer)
131 GraphicsLayer* newGraphicsLayer = paintInvalidationContainer->layer()->graphicsLayerBacking();
132 // FIXME: There should always be a GraphicsLayer. See crbug.com/431961.
133 if (newGraphicsLayer && !newGraphicsLayer->drawsContent())
134 newGraphicsLayer = paintInvalidationContainer->layer()->graphicsLayerBackingForScrolling();
135 if (!newGraphicsLayer)
136 return;
138 m_clipLayer->setTransform(SkMatrix44(SkMatrix44::kIdentity_Constructor));
140 if (m_currentGraphicsLayer != newGraphicsLayer) {
141 if (m_currentGraphicsLayer)
142 clearGraphicsLayerLinkHighlightPointer();
144 m_currentGraphicsLayer = newGraphicsLayer;
145 m_currentGraphicsLayer->addLinkHighlight(this);
149 static void convertTargetSpaceQuadToCompositedLayer(const FloatQuad& targetSpaceQuad, LayoutObject* targetLayoutObject, const LayoutBoxModelObject* paintInvalidationContainer, FloatQuad& compositedSpaceQuad)
151 ASSERT(targetLayoutObject);
152 ASSERT(paintInvalidationContainer);
153 for (unsigned i = 0; i < 4; ++i) {
154 IntPoint point;
155 switch (i) {
156 case 0: point = roundedIntPoint(targetSpaceQuad.p1()); break;
157 case 1: point = roundedIntPoint(targetSpaceQuad.p2()); break;
158 case 2: point = roundedIntPoint(targetSpaceQuad.p3()); break;
159 case 3: point = roundedIntPoint(targetSpaceQuad.p4()); break;
162 // FIXME: this does not need to be absolute, just in the paint invalidation container's space.
163 point = targetLayoutObject->frame()->view()->contentsToRootFrame(point);
164 point = paintInvalidationContainer->frame()->view()->rootFrameToContents(point);
165 FloatPoint floatPoint = paintInvalidationContainer->absoluteToLocal(point, UseTransforms);
166 DeprecatedPaintLayer::mapPointToPaintBackingCoordinates(paintInvalidationContainer, floatPoint);
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;
177 static void addQuadToPath(const FloatQuad& quad, Path& path)
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();
187 void LinkHighlightImpl::computeQuads(const Node& node, Vector<FloatQuad>& outQuads) const
189 if (!node.layoutObject())
190 return;
192 LayoutObject* layoutObject = node.layoutObject();
194 // For inline elements, absoluteQuads will return a line box based on the line-height
195 // and font metrics, which is technically incorrect as replaced elements like images
196 // should use their intristic height and expand the linebox as needed. To get an
197 // appropriately sized highlight we descend into the children and have them add their
198 // boxes.
199 if (layoutObject->isLayoutInline()) {
200 for (Node* child = LayoutTreeBuilderTraversal::firstChild(node); child; child = LayoutTreeBuilderTraversal::nextSibling(*child))
201 computeQuads(*child, outQuads);
202 } else {
203 // FIXME: this does not need to be absolute, just in the paint invalidation container's space.
204 layoutObject->absoluteQuads(outQuads);
208 bool LinkHighlightImpl::computeHighlightLayerPathAndPosition(const LayoutBoxModelObject* paintInvalidationContainer)
210 if (!m_node || !m_node->layoutObject() || !m_currentGraphicsLayer)
211 return false;
212 ASSERT(paintInvalidationContainer);
214 // FIXME: This is defensive code to avoid crashes such as those described in
215 // crbug.com/440887. This should be cleaned up once we fix the root cause of
216 // of the paint invalidation container not being composited.
217 if (!paintInvalidationContainer->layer()->compositedDeprecatedPaintLayerMapping() && !paintInvalidationContainer->layer()->groupedMapping())
218 return false;
220 // Get quads for node in absolute coordinates.
221 Vector<FloatQuad> quads;
222 computeQuads(*m_node, quads);
223 ASSERT(quads.size());
224 Path newPath;
226 for (size_t quadIndex = 0; quadIndex < quads.size(); ++quadIndex) {
227 FloatQuad absoluteQuad = quads[quadIndex];
229 // Transform node quads in target absolute coords to local coordinates in the compositor layer.
230 FloatQuad transformedQuad;
231 convertTargetSpaceQuadToCompositedLayer(absoluteQuad, m_node->layoutObject(), paintInvalidationContainer, transformedQuad);
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 && !m_owningWebViewImpl->settingsImpl()->mockGestureTapHighlightsEnabled()) {
239 FloatSize rectRoundingRadii(3, 3);
240 newPath.addRoundedRect(transformedQuad.boundingBox(), rectRoundingRadii);
241 } else {
242 addQuadToPath(transformedQuad, newPath);
246 FloatRect boundingRect = newPath.boundingRect();
247 newPath.translate(-toFloatSize(boundingRect.location()));
249 bool pathHasChanged = !(newPath == m_path);
250 if (pathHasChanged) {
251 m_path = newPath;
252 m_contentLayer->layer()->setBounds(enclosingIntRect(boundingRect).size());
255 m_contentLayer->layer()->setPosition(boundingRect.location());
257 return pathHasChanged;
260 void LinkHighlightImpl::paintContents(WebCanvas* canvas, const WebRect&, WebContentLayerClient::PaintingControlSetting paintingControl)
262 if (!m_node || !m_node->layoutObject())
263 return;
265 SkPaint paint;
266 paint.setStyle(SkPaint::kFill_Style);
267 paint.setFlags(SkPaint::kAntiAlias_Flag);
268 paint.setColor(m_node->layoutObject()->style()->tapHighlightColor().rgb());
269 canvas->drawPath(m_path.skPath(), paint);
272 void LinkHighlightImpl::paintContents(WebDisplayItemList* webDisplayItemList, const WebRect& webClipRect, WebContentLayerClient::PaintingControlSetting paintingControl)
274 if (!m_node || !m_node->layoutObject())
275 return;
277 SkPictureRecorder recorder;
278 SkCanvas* canvas = recorder.beginRecording(webClipRect.width, webClipRect.height);
279 canvas->translate(-webClipRect.x, -webClipRect.y);
280 paintContents(canvas, webClipRect, paintingControl);
281 RefPtr<const SkPicture> picture = adoptRef(recorder.endRecording());
282 webDisplayItemList->appendDrawingItem(picture.get());
285 void LinkHighlightImpl::startHighlightAnimationIfNeeded()
287 if (m_isAnimating)
288 return;
290 m_isAnimating = true;
291 const float startOpacity = 1;
292 // FIXME: Should duration be configurable?
293 const float fadeDuration = 0.1f;
294 const float minPreFadeDuration = 0.1f;
296 m_contentLayer->layer()->setOpacity(startOpacity);
298 WebCompositorSupport* compositorSupport = Platform::current()->compositorSupport();
300 OwnPtr<WebFloatAnimationCurve> curve = adoptPtr(compositorSupport->createFloatAnimationCurve());
302 curve->add(WebFloatKeyframe(0, startOpacity));
303 // Make sure we have displayed for at least minPreFadeDuration before starting to fade out.
304 float extraDurationRequired = std::max(0.f, minPreFadeDuration - static_cast<float>(monotonicallyIncreasingTime() - m_startTime));
305 if (extraDurationRequired)
306 curve->add(WebFloatKeyframe(extraDurationRequired, startOpacity));
307 // For layout tests we don't fade out.
308 curve->add(WebFloatKeyframe(fadeDuration + extraDurationRequired, layoutTestMode() ? startOpacity : 0));
310 OwnPtr<WebCompositorAnimation> animation = adoptPtr(compositorSupport->createAnimation(*curve, WebCompositorAnimation::TargetPropertyOpacity));
312 m_contentLayer->layer()->setDrawsContent(true);
313 if (RuntimeEnabledFeatures::compositorAnimationTimelinesEnabled())
314 m_compositorPlayer->addAnimation(animation.leakPtr());
315 else
316 m_contentLayer->layer()->addAnimation(animation.leakPtr());
318 invalidate();
319 m_owningWebViewImpl->scheduleAnimation();
322 void LinkHighlightImpl::clearGraphicsLayerLinkHighlightPointer()
324 if (m_currentGraphicsLayer) {
325 m_currentGraphicsLayer->removeLinkHighlight(this);
326 m_currentGraphicsLayer = 0;
330 void LinkHighlightImpl::notifyAnimationStarted(double, int)
334 void LinkHighlightImpl::notifyAnimationFinished(double, int)
336 // Since WebViewImpl may hang on to us for a while, make sure we
337 // release resources as soon as possible.
338 clearGraphicsLayerLinkHighlightPointer();
339 releaseResources();
342 void LinkHighlightImpl::updateGeometry()
344 // To avoid unnecessary updates (e.g. other entities have requested animations from our WebViewImpl),
345 // only proceed if we actually requested an update.
346 if (!m_geometryNeedsUpdate)
347 return;
349 m_geometryNeedsUpdate = false;
351 bool hasLayoutObject = m_node && m_node->layoutObject();
352 const LayoutBoxModelObject* paintInvalidationContainer = hasLayoutObject ? m_node->layoutObject()->containerForPaintInvalidation() : 0;
353 if (paintInvalidationContainer)
354 attachLinkHighlightToCompositingLayer(paintInvalidationContainer);
355 if (paintInvalidationContainer && computeHighlightLayerPathAndPosition(paintInvalidationContainer)) {
356 // We only need to invalidate the layer if the highlight size has changed, otherwise
357 // we can just re-position the layer without needing to repaint.
358 m_contentLayer->layer()->invalidate();
360 if (m_currentGraphicsLayer && m_currentGraphicsLayer->isTrackingPaintInvalidations())
361 m_currentGraphicsLayer->trackPaintInvalidationRect(FloatRect(layer()->position().x, layer()->position().y, layer()->bounds().width, layer()->bounds().height));
362 } else if (!hasLayoutObject) {
363 clearGraphicsLayerLinkHighlightPointer();
364 releaseResources();
368 void LinkHighlightImpl::clearCurrentGraphicsLayer()
370 m_currentGraphicsLayer = 0;
371 m_geometryNeedsUpdate = true;
374 void LinkHighlightImpl::invalidate()
376 // Make sure we update geometry on the next callback from WebViewImpl::layout().
377 m_geometryNeedsUpdate = true;
380 WebLayer* LinkHighlightImpl::layer()
382 return clipLayer();
385 WebCompositorAnimationPlayer* LinkHighlightImpl::compositorPlayer() const
387 return m_compositorPlayer.get();
390 } // namespace blink