Update To 11.40.268.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 "web/LinkHighlight.h"
29
30 #include "SkMatrix44.h"
31 #include "core/dom/Node.h"
32 #include "core/dom/NodeRenderingTraversal.h"
33 #include "core/frame/FrameView.h"
34 #include "core/frame/LocalFrame.h"
35 #include "core/rendering/RenderLayer.h"
36 #include "core/rendering/RenderLayerModelObject.h"
37 #include "core/rendering/RenderObject.h"
38 #include "core/rendering/RenderPart.h"
39 #include "core/rendering/RenderView.h"
40 #include "core/rendering/compositing/CompositedLayerMapping.h"
41 #include "core/rendering/style/ShadowData.h"
42 #include "platform/graphics/Color.h"
43 #include "public/platform/Platform.h"
44 #include "public/platform/WebCompositorAnimationCurve.h"
45 #include "public/platform/WebCompositorSupport.h"
46 #include "public/platform/WebFloatAnimationCurve.h"
47 #include "public/platform/WebFloatPoint.h"
48 #include "public/platform/WebRect.h"
49 #include "public/platform/WebSize.h"
50 #include "public/web/WebKit.h"
51 #include "web/WebLocalFrameImpl.h"
52 #include "web/WebSettingsImpl.h"
53 #include "web/WebViewImpl.h"
54 #include "wtf/CurrentTime.h"
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->setTransformOrigin(WebFloatPoint3D());
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 void LinkHighlight::attachLinkHighlightToCompositingLayer(const RenderLayerModelObject* paintInvalidationContainer)
109 {
110     // FIXME: there should always be a GraphicsLayer. See https://code.google.com/p/chromium/issues/detail?id=359877.
111     GraphicsLayer* newGraphicsLayer = paintInvalidationContainer->layer()->graphicsLayerBacking();
112     if (newGraphicsLayer && !newGraphicsLayer->drawsContent())
113         newGraphicsLayer = paintInvalidationContainer->layer()->graphicsLayerBackingForScrolling();
114     if (!newGraphicsLayer)
115         return;
116
117     m_clipLayer->setTransform(SkMatrix44(SkMatrix44::kIdentity_Constructor));
118
119     if (m_currentGraphicsLayer != newGraphicsLayer) {
120         if (m_currentGraphicsLayer)
121             clearGraphicsLayerLinkHighlightPointer();
122
123         m_currentGraphicsLayer = newGraphicsLayer;
124         m_currentGraphicsLayer->addLinkHighlight(this);
125     }
126 }
127
128 static void convertTargetSpaceQuadToCompositedLayer(const FloatQuad& targetSpaceQuad, RenderObject* targetRenderer, const RenderLayerModelObject* paintInvalidationContainer, FloatQuad& compositedSpaceQuad)
129 {
130     ASSERT(targetRenderer);
131     ASSERT(paintInvalidationContainer);
132     for (unsigned i = 0; i < 4; ++i) {
133         IntPoint point;
134         switch (i) {
135         case 0: point = roundedIntPoint(targetSpaceQuad.p1()); break;
136         case 1: point = roundedIntPoint(targetSpaceQuad.p2()); break;
137         case 2: point = roundedIntPoint(targetSpaceQuad.p3()); break;
138         case 3: point = roundedIntPoint(targetSpaceQuad.p4()); break;
139         }
140
141         // FIXME: this does not need to be absolute, just in the paint invalidation container's space.
142         point = targetRenderer->frame()->view()->contentsToWindow(point);
143         point = paintInvalidationContainer->frame()->view()->windowToContents(point);
144         FloatPoint floatPoint = paintInvalidationContainer->absoluteToLocal(point, UseTransforms);
145         RenderLayer::mapPointToPaintBackingCoordinates(paintInvalidationContainer, floatPoint);
146
147         switch (i) {
148         case 0: compositedSpaceQuad.setP1(floatPoint); break;
149         case 1: compositedSpaceQuad.setP2(floatPoint); break;
150         case 2: compositedSpaceQuad.setP3(floatPoint); break;
151         case 3: compositedSpaceQuad.setP4(floatPoint); break;
152         }
153     }
154 }
155
156 static void addQuadToPath(const FloatQuad& quad, Path& path)
157 {
158     // FIXME: Make this create rounded quad-paths, just like the axis-aligned case.
159     path.moveTo(quad.p1());
160     path.addLineTo(quad.p2());
161     path.addLineTo(quad.p3());
162     path.addLineTo(quad.p4());
163     path.closeSubpath();
164 }
165
166 void LinkHighlight::computeQuads(const Node& node, Vector<FloatQuad>& outQuads) const
167 {
168     if (!node.renderer())
169         return;
170
171     RenderObject* renderer = node.renderer();
172
173     // For inline elements, absoluteQuads will return a line box based on the line-height
174     // and font metrics, which is technically incorrect as replaced elements like images
175     // should use their intristic height and expand the linebox  as needed. To get an
176     // appropriately sized highlight we descend into the children and have them add their
177     // boxes.
178     if (renderer->isRenderInline()) {
179         for (Node* child = NodeRenderingTraversal::firstChild(&node); child; child = NodeRenderingTraversal::nextSibling(child))
180             computeQuads(*child, outQuads);
181     } else {
182         // FIXME: this does not need to be absolute, just in the paint invalidation container's space.
183         renderer->absoluteQuads(outQuads);
184     }
185 }
186
187 bool LinkHighlight::computeHighlightLayerPathAndPosition(const RenderLayerModelObject* paintInvalidationContainer)
188 {
189     if (!m_node || !m_node->renderer() || !m_currentGraphicsLayer)
190         return false;
191     ASSERT(paintInvalidationContainer);
192
193     // Get quads for node in absolute coordinates.
194     Vector<FloatQuad> quads;
195     computeQuads(*m_node, quads);
196     ASSERT(quads.size());
197     Path newPath;
198
199     FloatPoint positionAdjustForCompositedScrolling = IntPoint(m_currentGraphicsLayer->offsetFromRenderer());
200
201     for (size_t quadIndex = 0; quadIndex < quads.size(); ++quadIndex) {
202         FloatQuad absoluteQuad = quads[quadIndex];
203
204         // FIXME: this hack should not be necessary. It's a consequence of the fact that composited layers for scrolling are represented
205         // differently in Blink than other composited layers.
206         if (paintInvalidationContainer->layer()->needsCompositedScrolling() && m_node->renderer() != paintInvalidationContainer)
207             absoluteQuad.move(-positionAdjustForCompositedScrolling.x(), -positionAdjustForCompositedScrolling.y());
208
209         // Transform node quads in target absolute coords to local coordinates in the compositor layer.
210         FloatQuad transformedQuad;
211         convertTargetSpaceQuadToCompositedLayer(absoluteQuad, m_node->renderer(), paintInvalidationContainer, transformedQuad);
212
213         // FIXME: for now, we'll only use rounded paths if we have a single node quad. The reason for this is that
214         // we may sometimes get a chain of adjacent boxes (e.g. for text nodes) which end up looking like sausage
215         // links: these should ideally be merged into a single rect before creating the path, but that's
216         // another CL.
217         if (quads.size() == 1 && transformedQuad.isRectilinear()
218             && !m_owningWebViewImpl->settingsImpl()->mockGestureTapHighlightsEnabled()) {
219             FloatSize rectRoundingRadii(3, 3);
220             newPath.addRoundedRect(transformedQuad.boundingBox(), rectRoundingRadii);
221         } else
222             addQuadToPath(transformedQuad, newPath);
223     }
224
225     FloatRect boundingRect = newPath.boundingRect();
226     newPath.translate(-toFloatSize(boundingRect.location()));
227
228     bool pathHasChanged = !(newPath == m_path);
229     if (pathHasChanged) {
230         m_path = newPath;
231         m_contentLayer->layer()->setBounds(enclosingIntRect(boundingRect).size());
232     }
233
234     m_contentLayer->layer()->setPosition(boundingRect.location());
235
236     return pathHasChanged;
237 }
238
239 void LinkHighlight::paintContents(WebCanvas* canvas, const WebRect& webClipRect, bool, WebContentLayerClient::GraphicsContextStatus contextStatus)
240 {
241     if (!m_node || !m_node->renderer())
242         return;
243
244     GraphicsContext gc(canvas,
245         contextStatus == WebContentLayerClient::GraphicsContextEnabled ? GraphicsContext::NothingDisabled : GraphicsContext::FullyDisabled);
246     IntRect clipRect(IntPoint(webClipRect.x, webClipRect.y), IntSize(webClipRect.width, webClipRect.height));
247     gc.clip(clipRect);
248     gc.setFillColor(m_node->renderer()->style()->tapHighlightColor());
249     gc.fillPath(m_path);
250 }
251
252 void LinkHighlight::startHighlightAnimationIfNeeded()
253 {
254     if (m_isAnimating)
255         return;
256
257     m_isAnimating = true;
258     const float startOpacity = 1;
259     // FIXME: Should duration be configurable?
260     const float fadeDuration = 0.1f;
261     const float minPreFadeDuration = 0.1f;
262
263     m_contentLayer->layer()->setOpacity(startOpacity);
264
265     WebCompositorSupport* compositorSupport = Platform::current()->compositorSupport();
266
267     OwnPtr<WebFloatAnimationCurve> curve = adoptPtr(compositorSupport->createFloatAnimationCurve());
268
269     curve->add(WebFloatKeyframe(0, startOpacity));
270     // Make sure we have displayed for at least minPreFadeDuration before starting to fade out.
271     float extraDurationRequired = std::max(0.f, minPreFadeDuration - static_cast<float>(monotonicallyIncreasingTime() - m_startTime));
272     if (extraDurationRequired)
273         curve->add(WebFloatKeyframe(extraDurationRequired, startOpacity));
274     // For layout tests we don't fade out.
275     curve->add(WebFloatKeyframe(fadeDuration + extraDurationRequired, layoutTestMode() ? startOpacity : 0));
276
277     OwnPtr<WebCompositorAnimation> animation = adoptPtr(compositorSupport->createAnimation(*curve, WebCompositorAnimation::TargetPropertyOpacity));
278
279     m_contentLayer->layer()->setDrawsContent(true);
280     m_contentLayer->layer()->addAnimation(animation.leakPtr());
281
282     invalidate();
283     m_owningWebViewImpl->scheduleAnimation();
284 }
285
286 void LinkHighlight::clearGraphicsLayerLinkHighlightPointer()
287 {
288     if (m_currentGraphicsLayer) {
289         m_currentGraphicsLayer->removeLinkHighlight(this);
290         m_currentGraphicsLayer = 0;
291     }
292 }
293
294 void LinkHighlight::notifyAnimationStarted(double, int)
295 {
296 }
297
298 void LinkHighlight::notifyAnimationFinished(double, int)
299 {
300     // Since WebViewImpl may hang on to us for a while, make sure we
301     // release resources as soon as possible.
302     clearGraphicsLayerLinkHighlightPointer();
303     releaseResources();
304 }
305
306 void LinkHighlight::updateGeometry()
307 {
308     // To avoid unnecessary updates (e.g. other entities have requested animations from our WebViewImpl),
309     // only proceed if we actually requested an update.
310     if (!m_geometryNeedsUpdate)
311         return;
312
313     m_geometryNeedsUpdate = false;
314
315     bool hasRenderer = m_node && m_node->renderer();
316     const RenderLayerModelObject* paintInvalidationContainer = hasRenderer ? m_node->renderer()->containerForPaintInvalidation() : 0;
317     if (paintInvalidationContainer)
318         attachLinkHighlightToCompositingLayer(paintInvalidationContainer);
319     if (paintInvalidationContainer && computeHighlightLayerPathAndPosition(paintInvalidationContainer)) {
320         // We only need to invalidate the layer if the highlight size has changed, otherwise
321         // we can just re-position the layer without needing to repaint.
322         m_contentLayer->layer()->invalidate();
323
324         if (m_currentGraphicsLayer)
325             m_currentGraphicsLayer->addRepaintRect(FloatRect(layer()->position().x, layer()->position().y, layer()->bounds().width, layer()->bounds().height));
326     } else if (!hasRenderer) {
327         clearGraphicsLayerLinkHighlightPointer();
328         releaseResources();
329     }
330 }
331
332 void LinkHighlight::clearCurrentGraphicsLayer()
333 {
334     m_currentGraphicsLayer = 0;
335     m_geometryNeedsUpdate = true;
336 }
337
338 void LinkHighlight::invalidate()
339 {
340     // Make sure we update geometry on the next callback from WebViewImpl::layout().
341     m_geometryNeedsUpdate = true;
342 }
343
344 WebLayer* LinkHighlight::layer()
345 {
346     return clipLayer();
347 }
348
349 } // namespace blink