• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "web/LinkHighlight.h"
29 
30 #include "SkMatrix44.h"
31 #include "core/dom/Node.h"
32 #include "core/frame/FrameView.h"
33 #include "core/frame/LocalFrame.h"
34 #include "core/rendering/RenderLayer.h"
35 #include "core/rendering/RenderLayerModelObject.h"
36 #include "core/rendering/RenderObject.h"
37 #include "core/rendering/RenderView.h"
38 #include "core/rendering/compositing/CompositedLayerMapping.h"
39 #include "core/rendering/style/ShadowData.h"
40 #include "platform/graphics/Color.h"
41 #include "public/platform/Platform.h"
42 #include "public/platform/WebAnimationCurve.h"
43 #include "public/platform/WebCompositorSupport.h"
44 #include "public/platform/WebFloatAnimationCurve.h"
45 #include "public/platform/WebFloatPoint.h"
46 #include "public/platform/WebRect.h"
47 #include "public/platform/WebSize.h"
48 #include "public/web/WebKit.h"
49 #include "web/WebLocalFrameImpl.h"
50 #include "web/WebViewImpl.h"
51 #include "wtf/CurrentTime.h"
52 
53 using namespace WebCore;
54 
55 namespace blink {
56 
57 class WebViewImpl;
58 
create(Node * node,WebViewImpl * owningWebViewImpl)59 PassOwnPtr<LinkHighlight> LinkHighlight::create(Node* node, WebViewImpl* owningWebViewImpl)
60 {
61     return adoptPtr(new LinkHighlight(node, owningWebViewImpl));
62 }
63 
LinkHighlight(Node * node,WebViewImpl * owningWebViewImpl)64 LinkHighlight::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->setTransformOrigin(WebFloatPoint3D());
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 
~LinkHighlight()86 LinkHighlight::~LinkHighlight()
87 {
88     clearGraphicsLayerLinkHighlightPointer();
89     releaseResources();
90 }
91 
contentLayer()92 WebContentLayer* LinkHighlight::contentLayer()
93 {
94     return m_contentLayer.get();
95 }
96 
clipLayer()97 WebLayer* LinkHighlight::clipLayer()
98 {
99     return m_clipLayer.get();
100 }
101 
releaseResources()102 void LinkHighlight::releaseResources()
103 {
104     m_node.clear();
105 }
106 
computeEnclosingCompositingLayer()107 RenderLayer* 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     RenderLayer* renderLayer;
116     do {
117         renderLayer = renderer->enclosingLayer()->enclosingCompositingLayerForRepaint();
118         if (!renderLayer) {
119             renderer = renderer->frame()->ownerRenderer();
120             if (!renderer)
121                 return 0;
122         }
123     } while (!renderLayer);
124 
125     CompositedLayerMappingPtr compositedLayerMapping = renderLayer->compositingState() == PaintsIntoGroupedBacking ? renderLayer->groupedMapping() : renderLayer->compositedLayerMapping();
126     GraphicsLayer* newGraphicsLayer = renderLayer->compositingState() == PaintsIntoGroupedBacking ? compositedLayerMapping->squashingLayer() : compositedLayerMapping->mainGraphicsLayer();
127 
128     m_clipLayer->setTransform(SkMatrix44(SkMatrix44::kIdentity_Constructor));
129 
130     if (!newGraphicsLayer->drawsContent()) {
131         if (renderLayer->scrollableArea() && renderLayer->scrollableArea()->usesCompositedScrolling()) {
132             ASSERT(renderLayer->hasCompositedLayerMapping() && renderLayer->compositedLayerMapping()->scrollingContentsLayer());
133             newGraphicsLayer = compositedLayerMapping->scrollingContentsLayer();
134         }
135     }
136 
137     if (m_currentGraphicsLayer != newGraphicsLayer) {
138         if (m_currentGraphicsLayer)
139             clearGraphicsLayerLinkHighlightPointer();
140 
141         m_currentGraphicsLayer = newGraphicsLayer;
142         m_currentGraphicsLayer->addLinkHighlight(this);
143     }
144 
145     return renderLayer;
146 }
147 
convertTargetSpaceQuadToCompositedLayer(const FloatQuad & targetSpaceQuad,RenderObject * targetRenderer,RenderObject * compositedRenderer,FloatQuad & compositedSpaceQuad)148 static void convertTargetSpaceQuadToCompositedLayer(const FloatQuad& targetSpaceQuad, RenderObject* targetRenderer, RenderObject* compositedRenderer, FloatQuad& compositedSpaceQuad)
149 {
150     ASSERT(targetRenderer);
151     ASSERT(compositedRenderer);
152 
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;
160         }
161 
162         point = targetRenderer->frame()->view()->contentsToWindow(point);
163         point = compositedRenderer->frame()->view()->windowToContents(point);
164         FloatPoint floatPoint = compositedRenderer->absoluteToLocal(point, UseTransforms);
165 
166         switch (i) {
167         case 0: compositedSpaceQuad.setP1(floatPoint); break;
168         case 1: compositedSpaceQuad.setP2(floatPoint); break;
169         case 2: compositedSpaceQuad.setP3(floatPoint); break;
170         case 3: compositedSpaceQuad.setP4(floatPoint); break;
171         }
172     }
173 }
174 
addQuadToPath(const FloatQuad & quad,Path & path)175 static void addQuadToPath(const FloatQuad& quad, Path& path)
176 {
177     // FIXME: Make this create rounded quad-paths, just like the axis-aligned case.
178     path.moveTo(quad.p1());
179     path.addLineTo(quad.p2());
180     path.addLineTo(quad.p3());
181     path.addLineTo(quad.p4());
182     path.closeSubpath();
183 }
184 
computeQuads(Node * node,Vector<FloatQuad> & outQuads) const185 void LinkHighlight::computeQuads(Node* node, Vector<FloatQuad>& outQuads) const
186 {
187     if (!node || !node->renderer())
188         return;
189 
190     RenderObject* renderer = node->renderer();
191 
192     // For inline elements, absoluteQuads will return a line box based on the line-height
193     // and font metrics, which is technically incorrect as replaced elements like images
194     // should use their intristic height and expand the linebox  as needed. To get an
195     // appropriately sized highlight we descend into the children and have them add their
196     // boxes.
197     if (renderer->isRenderInline()) {
198         for (Node* child = node->firstChild(); child; child = child->nextSibling())
199             computeQuads(child, outQuads);
200     } else {
201         renderer->absoluteQuads(outQuads);
202     }
203 
204 }
205 
computeHighlightLayerPathAndPosition(RenderLayer * compositingLayer)206 bool LinkHighlight::computeHighlightLayerPathAndPosition(RenderLayer* compositingLayer)
207 {
208     if (!m_node || !m_node->renderer() || !m_currentGraphicsLayer)
209         return false;
210 
211     ASSERT(compositingLayer);
212 
213     // Get quads for node in absolute coordinates.
214     Vector<FloatQuad> quads;
215     computeQuads(m_node.get(), quads);
216     ASSERT(quads.size());
217 
218     // Adjust for offset between target graphics layer and the node's renderer.
219     FloatPoint positionAdjust = IntPoint(m_currentGraphicsLayer->offsetFromRenderer());
220 
221     Path newPath;
222     for (size_t quadIndex = 0; quadIndex < quads.size(); ++quadIndex) {
223         FloatQuad absoluteQuad = quads[quadIndex];
224         absoluteQuad.move(-positionAdjust.x(), -positionAdjust.y());
225 
226         // Transform node quads in target absolute coords to local coordinates in the compositor layer.
227         FloatQuad transformedQuad;
228         convertTargetSpaceQuadToCompositedLayer(absoluteQuad, m_node->renderer(), compositingLayer->renderer(), transformedQuad);
229 
230         // FIXME: for now, we'll only use rounded paths if we have a single node quad. The reason for this is that
231         // we may sometimes get a chain of adjacent boxes (e.g. for text nodes) which end up looking like sausage
232         // links: these should ideally be merged into a single rect before creating the path, but that's
233         // another CL.
234         if (quads.size() == 1 && transformedQuad.isRectilinear()) {
235             FloatSize rectRoundingRadii(3, 3);
236             newPath.addRoundedRect(transformedQuad.boundingBox(), rectRoundingRadii);
237         } else
238             addQuadToPath(transformedQuad, newPath);
239     }
240 
241     FloatRect boundingRect = newPath.boundingRect();
242     newPath.translate(-toFloatSize(boundingRect.location()));
243 
244     bool pathHasChanged = !(newPath == m_path);
245     if (pathHasChanged) {
246         m_path = newPath;
247         m_contentLayer->layer()->setBounds(enclosingIntRect(boundingRect).size());
248     }
249 
250     m_contentLayer->layer()->setPosition(boundingRect.location());
251 
252     return pathHasChanged;
253 }
254 
paintContents(WebCanvas * canvas,const WebRect & webClipRect,bool,WebFloatRect &,WebContentLayerClient::GraphicsContextStatus contextStatus)255 void LinkHighlight::paintContents(WebCanvas* canvas, const WebRect& webClipRect, bool, WebFloatRect&,
256     WebContentLayerClient::GraphicsContextStatus contextStatus)
257 {
258     if (!m_node || !m_node->renderer())
259         return;
260 
261     GraphicsContext gc(canvas,
262         contextStatus == WebContentLayerClient::GraphicsContextEnabled ? GraphicsContext::NothingDisabled : GraphicsContext::FullyDisabled);
263     IntRect clipRect(IntPoint(webClipRect.x, webClipRect.y), IntSize(webClipRect.width, webClipRect.height));
264     gc.clip(clipRect);
265     gc.setFillColor(m_node->renderer()->style()->tapHighlightColor());
266     gc.fillPath(m_path);
267 }
268 
startHighlightAnimationIfNeeded()269 void LinkHighlight::startHighlightAnimationIfNeeded()
270 {
271     if (m_isAnimating)
272         return;
273 
274     m_isAnimating = true;
275     const float startOpacity = 1;
276     // FIXME: Should duration be configurable?
277     const float fadeDuration = 0.1f;
278     const float minPreFadeDuration = 0.1f;
279 
280     m_contentLayer->layer()->setOpacity(startOpacity);
281 
282     WebCompositorSupport* compositorSupport = Platform::current()->compositorSupport();
283 
284     OwnPtr<WebFloatAnimationCurve> curve = adoptPtr(compositorSupport->createFloatAnimationCurve());
285 
286     curve->add(WebFloatKeyframe(0, startOpacity));
287     // Make sure we have displayed for at least minPreFadeDuration before starting to fade out.
288     float extraDurationRequired = std::max(0.f, minPreFadeDuration - static_cast<float>(monotonicallyIncreasingTime() - m_startTime));
289     if (extraDurationRequired)
290         curve->add(WebFloatKeyframe(extraDurationRequired, startOpacity));
291     // For layout tests we don't fade out.
292     curve->add(WebFloatKeyframe(fadeDuration + extraDurationRequired, blink::layoutTestMode() ? startOpacity : 0));
293 
294     OwnPtr<WebAnimation> animation = adoptPtr(compositorSupport->createAnimation(*curve, WebAnimation::TargetPropertyOpacity));
295 
296     m_contentLayer->layer()->setDrawsContent(true);
297     m_contentLayer->layer()->addAnimation(animation.leakPtr());
298 
299     invalidate();
300     m_owningWebViewImpl->scheduleAnimation();
301 }
302 
clearGraphicsLayerLinkHighlightPointer()303 void LinkHighlight::clearGraphicsLayerLinkHighlightPointer()
304 {
305     if (m_currentGraphicsLayer) {
306         m_currentGraphicsLayer->removeLinkHighlight(this);
307         m_currentGraphicsLayer = 0;
308     }
309 }
310 
notifyAnimationStarted(double,blink::WebAnimation::TargetProperty)311 void LinkHighlight::notifyAnimationStarted(double, blink::WebAnimation::TargetProperty)
312 {
313 }
314 
notifyAnimationFinished(double,blink::WebAnimation::TargetProperty)315 void LinkHighlight::notifyAnimationFinished(double, blink::WebAnimation::TargetProperty)
316 {
317     // Since WebViewImpl may hang on to us for a while, make sure we
318     // release resources as soon as possible.
319     clearGraphicsLayerLinkHighlightPointer();
320     releaseResources();
321 }
322 
updateGeometry()323 void LinkHighlight::updateGeometry()
324 {
325     // To avoid unnecessary updates (e.g. other entities have requested animations from our WebViewImpl),
326     // only proceed if we actually requested an update.
327     if (!m_geometryNeedsUpdate)
328         return;
329 
330     m_geometryNeedsUpdate = false;
331 
332     RenderLayer* compositingLayer = computeEnclosingCompositingLayer();
333     if (compositingLayer && computeHighlightLayerPathAndPosition(compositingLayer)) {
334         // We only need to invalidate the layer if the highlight size has changed, otherwise
335         // we can just re-position the layer without needing to repaint.
336         m_contentLayer->layer()->invalidate();
337 
338         if (m_currentGraphicsLayer)
339             m_currentGraphicsLayer->addRepaintRect(FloatRect(layer()->position().x, layer()->position().y, layer()->bounds().width, layer()->bounds().height));
340     } else if (!m_node || !m_node->renderer()) {
341         clearGraphicsLayerLinkHighlightPointer();
342         releaseResources();
343     }
344 }
345 
clearCurrentGraphicsLayer()346 void LinkHighlight::clearCurrentGraphicsLayer()
347 {
348     m_currentGraphicsLayer = 0;
349     m_geometryNeedsUpdate = true;
350 }
351 
invalidate()352 void LinkHighlight::invalidate()
353 {
354     // Make sure we update geometry on the next callback from WebViewImpl::layout().
355     m_geometryNeedsUpdate = true;
356 }
357 
layer()358 WebLayer* LinkHighlight::layer()
359 {
360     return clipLayer();
361 }
362 
363 } // namespace WeKit
364