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