Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / web / LinkHighlight.cpp
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 #include "wtf/Vector.h"
53
54 using namespace WebCore;
55
56 namespace blink {
57
58 class WebViewImpl;
59
60 PassOwnPtr<LinkHighlight> LinkHighlight::create(Node* node, WebViewImpl* owningWebViewImpl)
61 {
62     return adoptPtr(new LinkHighlight(node, owningWebViewImpl));
63 }
64
65 LinkHighlight::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)
71     , m_startTime(monotonicallyIncreasingTime())
72 {
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
87 LinkHighlight::~LinkHighlight()
88 {
89     clearGraphicsLayerLinkHighlightPointer();
90     releaseResources();
91 }
92
93 WebContentLayer* LinkHighlight::contentLayer()
94 {
95     return m_contentLayer.get();
96 }
97
98 WebLayer* LinkHighlight::clipLayer()
99 {
100     return m_clipLayer.get();
101 }
102
103 void LinkHighlight::releaseResources()
104 {
105     m_node.clear();
106 }
107
108 RenderLayer* LinkHighlight::computeEnclosingCompositingLayer()
109 {
110     if (!m_node || !m_node->renderer())
111         return 0;
112
113     // Find the nearest enclosing composited layer and attach to it. We may need to cross frame boundaries
114     // to find a suitable layer.
115     RenderObject* renderer = m_node->renderer();
116     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();
126
127     if (!renderLayer || renderLayer->compositingState() == NotComposited)
128         return 0;
129
130     GraphicsLayer* newGraphicsLayer = renderLayer->compositedLayerMapping()->mainGraphicsLayer();
131     m_clipLayer->setTransform(SkMatrix44());
132
133     if (!newGraphicsLayer->drawsContent()) {
134         if (renderLayer->scrollableArea() && renderLayer->scrollableArea()->usesCompositedScrolling()) {
135             ASSERT(renderLayer->hasCompositedLayerMapping() && renderLayer->compositedLayerMapping()->scrollingContentsLayer());
136             newGraphicsLayer = renderLayer->compositedLayerMapping()->scrollingContentsLayer();
137         }
138     }
139
140     if (m_currentGraphicsLayer != newGraphicsLayer) {
141         if (m_currentGraphicsLayer)
142             clearGraphicsLayerLinkHighlightPointer();
143
144         m_currentGraphicsLayer = newGraphicsLayer;
145         m_currentGraphicsLayer->addLinkHighlight(this);
146     }
147
148     return renderLayer;
149 }
150
151 static 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);
167         FloatPoint floatPoint = compositedRenderer->absoluteToLocal(point, UseTransforms);
168
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
178 static 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
188 void 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
209 bool LinkHighlight::computeHighlightLayerPathAndPosition(RenderLayer* compositingLayer)
210 {
211     if (!m_node || !m_node->renderer() || !m_currentGraphicsLayer)
212         return false;
213
214     ASSERT(compositingLayer);
215
216     // Get quads for node in absolute coordinates.
217     Vector<FloatQuad> quads;
218     computeQuads(m_node.get(), quads);
219     ASSERT(quads.size());
220
221     // Adjust for offset between target graphics layer and the node's renderer.
222     FloatPoint positionAdjust = IntPoint(m_currentGraphicsLayer->offsetFromRenderer());
223
224     Path newPath;
225     for (size_t quadIndex = 0; quadIndex < quads.size(); ++quadIndex) {
226         FloatQuad absoluteQuad = quads[quadIndex];
227         absoluteQuad.move(-positionAdjust.x(), -positionAdjust.y());
228
229         // Transform node quads in target absolute coords to local coordinates in the compositor layer.
230         FloatQuad transformedQuad;
231         convertTargetSpaceQuadToCompositedLayer(absoluteQuad, m_node->renderer(), compositingLayer->renderer(), transformedQuad);
232
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();
245     newPath.translate(-toFloatSize(boundingRect.location()));
246
247     bool pathHasChanged = !(newPath == m_path);
248     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
258 void LinkHighlight::paintContents(WebCanvas* canvas, const WebRect& webClipRect, bool, WebFloatRect&)
259 {
260     if (!m_node || !m_node->renderer())
261         return;
262
263     GraphicsContext gc(canvas);
264     IntRect clipRect(IntPoint(webClipRect.x, webClipRect.y), IntSize(webClipRect.width, webClipRect.height));
265     gc.clip(clipRect);
266     gc.setFillColor(m_node->renderer()->style()->tapHighlightColor());
267     gc.fillPath(m_path);
268 }
269
270 void 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?
278     const float fadeDuration = 0.1f;
279     const float minPreFadeDuration = 0.1f;
280
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));
288     // 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));
292     // For layout tests we don't fade out.
293     curve->add(WebFloatKeyframe(fadeDuration + extraDurationRequired, blink::layoutTestMode() ? startOpacity : 0));
294
295     OwnPtr<WebAnimation> animation = adoptPtr(compositorSupport->createAnimation(*curve, WebAnimation::TargetPropertyOpacity));
296
297     m_contentLayer->layer()->setDrawsContent(true);
298     m_contentLayer->layer()->addAnimation(animation.leakPtr());
299
300     invalidate();
301     m_owningWebViewImpl->scheduleAnimation();
302 }
303
304 void LinkHighlight::clearGraphicsLayerLinkHighlightPointer()
305 {
306     if (m_currentGraphicsLayer) {
307         m_currentGraphicsLayer->removeLinkHighlight(this);
308         m_currentGraphicsLayer = 0;
309     }
310 }
311
312 void LinkHighlight::notifyAnimationStarted(double, double, blink::WebAnimation::TargetProperty)
313 {
314 }
315
316 void LinkHighlight::notifyAnimationFinished(double, double, blink::WebAnimation::TargetProperty)
317 {
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
324 void 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));
341     } else if (!m_node || !m_node->renderer()) {
342         clearGraphicsLayerLinkHighlightPointer();
343         releaseResources();
344     }
345 }
346
347 void LinkHighlight::clearCurrentGraphicsLayer()
348 {
349     m_currentGraphicsLayer = 0;
350     m_geometryNeedsUpdate = true;
351 }
352
353 void LinkHighlight::invalidate()
354 {
355     // Make sure we update geometry on the next callback from WebViewImpl::layout().
356     m_geometryNeedsUpdate = true;
357 }
358
359 WebLayer* LinkHighlight::layer()
360 {
361     return clipLayer();
362 }
363
364 } // namespace WeKit