a63174ed1a1c59c04d1d0f0896738a2a7ed8d924
[WebKit-https.git] / Source / WebKit / chromium / src / 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 "Color.h"
31 #include "Frame.h"
32 #include "FrameView.h"
33 #include "LayoutTypes.h"
34 #include "Node.h"
35 #include "NonCompositedContentHost.h"
36 #include "PlatformContextSkia.h"
37 #include "RenderLayer.h"
38 #include "RenderLayerBacking.h"
39 #include "RenderObject.h"
40 #include "RenderView.h"
41 #include "WebFrameImpl.h"
42 #include "WebKit.h"
43 #include "WebViewImpl.h"
44 #include <public/WebAnimationCurve.h>
45 #include <public/WebFloatAnimationCurve.h>
46 #include <public/WebFloatPoint.h>
47 #include <public/WebRect.h>
48 #include <public/WebSize.h>
49
50 using namespace WebCore;
51
52 namespace WebKit {
53
54 class WebViewImpl;
55
56 PassOwnPtr<LinkHighlight> LinkHighlight::create(Node* node, WebViewImpl* owningWebViewImpl)
57 {
58     return adoptPtr(new LinkHighlight(node, owningWebViewImpl));
59 }
60
61 LinkHighlight::LinkHighlight(Node* node, WebViewImpl* owningWebViewImpl)
62     : m_contentLayer(WebContentLayer::create(this))
63     , m_clipLayer(WebLayer::create())
64     , m_node(node)
65     , m_owningWebViewImpl(owningWebViewImpl)
66     , m_currentGraphicsLayer(0)
67     , m_geometryNeedsUpdate(false)
68 {
69     ASSERT(m_node);
70     ASSERT(owningWebViewImpl);
71
72     m_clipLayer.setAnchorPoint(WebFloatPoint());
73     m_clipLayer.addChild(m_contentLayer);
74     m_contentLayer.setDrawsContent(false);
75
76     // We don't want to show the highlight until startAnimation is called, even though the highlight
77     // layer may be added to the tree immediately.
78     m_contentLayer.setOpacity(0);
79     m_contentLayer.setAnimationDelegate(this);
80 }
81
82 LinkHighlight::~LinkHighlight()
83 {
84     clearGraphicsLayerLinkHighlightPointer();
85     releaseResources();
86 }
87
88 WebContentLayer* LinkHighlight::contentLayer()
89 {
90     return &m_contentLayer;
91 }
92
93 WebLayer* LinkHighlight::clipLayer()
94 {
95     return &m_clipLayer;
96 }
97
98 void LinkHighlight::releaseResources()
99 {
100     m_node.clear();
101     m_contentLayer.clearClient();
102     m_contentLayer.setAnimationDelegate(0);
103 }
104
105 RenderLayer* LinkHighlight::computeEnclosingCompositingLayer()
106 {
107     if (!m_node || !m_node->renderer())
108         return 0;
109
110     RenderLayer* renderLayer = m_node->renderer()->enclosingLayer();
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     while (renderLayer && !renderLayer->isComposited()) {
115         if (!renderLayer->parent()) {
116             // See if we've reached the root in an enclosed frame.
117             if (renderLayer->renderer()->frame()->ownerRenderer())
118                 renderLayer = renderLayer->renderer()->frame()->ownerRenderer()->enclosingLayer();
119             else
120                 renderLayer = 0;
121         } else
122             renderLayer = renderLayer->parent();
123     }
124
125     if (!renderLayer || !renderLayer->isComposited())
126         return 0;
127
128     m_graphicsLayerOffset = FloatPoint();
129     GraphicsLayerChromium* newGraphicsLayer = static_cast<GraphicsLayerChromium*>(renderLayer->backing()->graphicsLayer());
130     if (!newGraphicsLayer->drawsContent()) {
131         m_graphicsLayerOffset = newGraphicsLayer->position();
132         newGraphicsLayer = static_cast<GraphicsLayerChromium*>(m_owningWebViewImpl->nonCompositedContentHost()->topLevelRootLayer());
133     }
134
135     if (m_currentGraphicsLayer != newGraphicsLayer) {
136         if (m_currentGraphicsLayer)
137             clearGraphicsLayerLinkHighlightPointer();
138
139         m_currentGraphicsLayer = newGraphicsLayer;
140         m_currentGraphicsLayer->setLinkHighlight(this);
141     }
142
143     return renderLayer;
144 }
145
146 bool LinkHighlight::computeHighlightLayerPathAndPosition(RenderLayer* compositingLayer)
147 {
148     if (!m_node || !m_node->renderer())
149         return false;
150
151     bool pathHasChanged = false;
152     FloatRect boundingRect = m_node->getPixelSnappedRect();
153
154     // FIXME: If we ever use a more sophisticated highlight path, we'll need
155     // to devise a way of detecting when it changes.
156     if (boundingRect.size() != m_path.boundingRect().size()) {
157         FloatSize rectRoundingRadii(3, 3);
158         m_path.clear();
159         m_path.addRoundedRect(boundingRect, rectRoundingRadii);
160         // Always treat the path as being at the origin of this layer.
161         m_path.translate(FloatPoint() - boundingRect.location());
162         pathHasChanged = true;
163     }
164
165     FloatRect nodeBounds = boundingRect;
166
167     // This is a simplified, but basically correct, transformation of the target location, converted
168     // from its containing frame view to window coordinates and then back to the containing frame view
169     // of the composited layer.
170     // FIXME: We also need to transform the target's size in case of scaling. This can be done by also transforming
171     //        the full rects in the xToY calls, and transforming both the upper-left and lower right corners
172     //        to local coordinates at the end..
173     ASSERT(compositingLayer);
174     IntPoint targetWindow = m_node->renderer()->frame()->view()->contentsToWindow(enclosingIntRect(nodeBounds).location());
175     IntPoint targetCompositorAbsolute = compositingLayer->renderer()->frame()->view()->windowToContents(targetWindow);
176     FloatPoint targetCompositorLocal = compositingLayer->renderer()->absoluteToLocal(targetCompositorAbsolute, false, true);
177
178     m_contentLayer.setBounds(WebSize(enclosingIntRect(nodeBounds).size()));
179     m_contentLayer.setPosition(WebFloatPoint(targetCompositorLocal));
180
181     return pathHasChanged;
182 }
183
184 void LinkHighlight::paintContents(WebCanvas* canvas, const WebRect& webClipRect, WebFloatRect&)
185 {
186     if (!m_node || !m_node->renderer())
187         return;
188
189     PlatformContextSkia platformContext(canvas);
190     GraphicsContext gc(&platformContext);
191     IntRect clipRect(IntPoint(webClipRect.x, webClipRect.y), IntSize(webClipRect.width, webClipRect.height));
192     gc.clip(clipRect);
193     gc.setFillColor(m_node->renderer()->style()->tapHighlightColor(), ColorSpaceDeviceRGB);
194     gc.fillPath(m_path);
195 }
196
197 void LinkHighlight::startHighlightAnimation()
198 {
199     const float startOpacity = 1;
200     // FIXME: Should duration be configurable?
201     const float duration = 2;
202
203     m_contentLayer.setOpacity(startOpacity);
204
205     WebFloatAnimationCurve curve;
206     curve.add(WebFloatKeyframe(0, startOpacity));
207     curve.add(WebFloatKeyframe(duration / 2, startOpacity));
208     // For layout tests we don't fade out.
209     curve.add(WebFloatKeyframe(duration, WebKit::layoutTestMode() ? startOpacity : 0));
210
211     m_animation = adoptPtr(WebAnimation::create(curve, WebAnimation::TargetPropertyOpacity));
212     m_contentLayer.setDrawsContent(true);
213     m_contentLayer.addAnimation(m_animation.get());
214
215     invalidate();
216     m_owningWebViewImpl->scheduleAnimation();
217 }
218
219 void LinkHighlight::clearGraphicsLayerLinkHighlightPointer()
220 {
221     if (m_currentGraphicsLayer) {
222         m_currentGraphicsLayer->setLinkHighlight(0);
223         m_currentGraphicsLayer = 0;
224     }
225 }
226
227 void LinkHighlight::notifyAnimationStarted(double)
228 {
229 }
230
231 void LinkHighlight::notifyAnimationFinished(double)
232 {
233     // Since WebViewImpl may hang on to us for a while, make sure we
234     // release resources as soon as possible.
235     clearGraphicsLayerLinkHighlightPointer();
236     releaseResources();
237 }
238
239 void LinkHighlight::updateGeometry()
240 {
241     // To avoid unnecessary updates (e.g. other entities have requested animations from our WebViewImpl),
242     // only proceed if we actually requested an update.
243     if (!m_geometryNeedsUpdate)
244         return;
245
246     m_geometryNeedsUpdate = false;
247
248     RenderLayer* compositingLayer = computeEnclosingCompositingLayer();
249     if (compositingLayer && computeHighlightLayerPathAndPosition(compositingLayer)) {
250         // We only need to invalidate the layer if the highlight size has changed, otherwise
251         // we can just re-position the layer without needing to repaint.
252         m_contentLayer.invalidate();
253     }
254 }
255
256 void LinkHighlight::clearCurrentGraphicsLayer()
257 {
258     m_currentGraphicsLayer = 0;
259     m_geometryNeedsUpdate = true;
260 }
261
262 void LinkHighlight::invalidate()
263 {
264     // Make sure we update geometry on the next callback from WebViewImpl::layout().
265     m_geometryNeedsUpdate = true;
266 }
267
268 WebLayer* LinkHighlight::layer()
269 {
270     return clipLayer();
271 }
272
273 } // namespace WeKit