1/*
2 * Copyright (C) 2010 Apple 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 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "PageOverlay.h"
28
29#include "Frame.h"
30#include "FrameView.h"
31#include "GraphicsContext.h"
32#include "Page.h"
33#include "PageOverlayController.h"
34#include "PlatformMouseEvent.h"
35#include "ScrollbarTheme.h"
36
37namespace WebCore {
38
39static const Seconds fadeAnimationDuration { 200_ms };
40static const double fadeAnimationFrameRate = 30;
41
42static PageOverlay::PageOverlayID generatePageOverlayID()
43{
44 static PageOverlay::PageOverlayID pageOverlayID;
45 return ++pageOverlayID;
46}
47
48Ref<PageOverlay> PageOverlay::create(Client& client, OverlayType overlayType)
49{
50 return adoptRef(*new PageOverlay(client, overlayType));
51}
52
53PageOverlay::PageOverlay(Client& client, OverlayType overlayType)
54 : m_client(client)
55 , m_fadeAnimationTimer(*this, &PageOverlay::fadeAnimationTimerFired)
56 , m_fadeAnimationDuration(fadeAnimationDuration)
57 , m_needsSynchronousScrolling(overlayType == OverlayType::View)
58 , m_overlayType(overlayType)
59 , m_pageOverlayID(generatePageOverlayID())
60{
61}
62
63PageOverlay::~PageOverlay() = default;
64
65PageOverlayController* PageOverlay::controller() const
66{
67 if (!m_page)
68 return nullptr;
69 return &m_page->pageOverlayController();
70}
71
72IntRect PageOverlay::bounds() const
73{
74 if (!m_overrideFrame.isEmpty())
75 return { { }, m_overrideFrame.size() };
76
77 FrameView* frameView = m_page->mainFrame().view();
78
79 if (!frameView)
80 return IntRect();
81
82 switch (m_overlayType) {
83 case OverlayType::View: {
84 int width = frameView->width();
85 int height = frameView->height();
86
87 if (!ScrollbarTheme::theme().usesOverlayScrollbars()) {
88 if (frameView->verticalScrollbar())
89 width -= frameView->verticalScrollbar()->width();
90 if (frameView->horizontalScrollbar())
91 height -= frameView->horizontalScrollbar()->height();
92 }
93 return IntRect(0, 0, width, height);
94 }
95 case OverlayType::Document:
96 return IntRect(IntPoint(), frameView->contentsSize());
97 }
98
99 ASSERT_NOT_REACHED();
100 return IntRect(IntPoint(), frameView->contentsSize());
101}
102
103IntRect PageOverlay::frame() const
104{
105 if (!m_overrideFrame.isEmpty())
106 return m_overrideFrame;
107
108 return bounds();
109}
110
111void PageOverlay::setFrame(IntRect frame)
112{
113 if (m_overrideFrame == frame)
114 return;
115
116 m_overrideFrame = frame;
117
118 if (auto pageOverlayController = controller())
119 pageOverlayController->didChangeOverlayFrame(*this);
120}
121
122IntSize PageOverlay::viewToOverlayOffset() const
123{
124 switch (m_overlayType) {
125 case OverlayType::View:
126 return IntSize();
127
128 case OverlayType::Document: {
129 FrameView* frameView = m_page->mainFrame().view();
130 return frameView ? toIntSize(frameView->viewToContents(IntPoint())) : IntSize();
131 }
132 }
133 return IntSize();
134}
135
136void PageOverlay::setBackgroundColor(const Color& backgroundColor)
137{
138 if (m_backgroundColor == backgroundColor)
139 return;
140
141 m_backgroundColor = backgroundColor;
142
143 if (auto pageOverlayController = controller())
144 pageOverlayController->didChangeOverlayBackgroundColor(*this);
145}
146
147void PageOverlay::setPage(Page* page)
148{
149 m_client.willMoveToPage(*this, page);
150 m_page = page;
151 m_client.didMoveToPage(*this, page);
152
153 m_fadeAnimationTimer.stop();
154}
155
156void PageOverlay::setNeedsDisplay(const IntRect& dirtyRect)
157{
158 if (auto pageOverlayController = controller()) {
159 if (m_fadeAnimationType != FadeAnimationType::NoAnimation)
160 pageOverlayController->setPageOverlayOpacity(*this, m_fractionFadedIn);
161 pageOverlayController->setPageOverlayNeedsDisplay(*this, dirtyRect);
162 }
163}
164
165void PageOverlay::setNeedsDisplay()
166{
167 setNeedsDisplay(bounds());
168}
169
170void PageOverlay::drawRect(GraphicsContext& graphicsContext, const IntRect& dirtyRect)
171{
172 // If the dirty rect is outside the bounds, ignore it.
173 IntRect paintRect = intersection(dirtyRect, bounds());
174 if (paintRect.isEmpty())
175 return;
176
177 GraphicsContextStateSaver stateSaver(graphicsContext);
178
179 if (m_overlayType == PageOverlay::OverlayType::Document) {
180 if (FrameView* frameView = m_page->mainFrame().view()) {
181 auto offset = frameView->scrollOrigin();
182 graphicsContext.translate(toFloatSize(offset));
183 paintRect.moveBy(-offset);
184 }
185 }
186
187 m_client.drawRect(*this, graphicsContext, paintRect);
188}
189
190bool PageOverlay::mouseEvent(const PlatformMouseEvent& mouseEvent)
191{
192 IntPoint mousePositionInOverlayCoordinates(mouseEvent.position());
193
194 if (m_overlayType == PageOverlay::OverlayType::Document)
195 mousePositionInOverlayCoordinates = m_page->mainFrame().view()->windowToContents(mousePositionInOverlayCoordinates);
196 mousePositionInOverlayCoordinates.moveBy(-frame().location());
197
198 // Ignore events outside the bounds.
199 if (m_shouldIgnoreMouseEventsOutsideBounds && !bounds().contains(mousePositionInOverlayCoordinates))
200 return false;
201
202 return m_client.mouseEvent(*this, mouseEvent);
203}
204
205void PageOverlay::didScrollFrame(Frame& frame)
206{
207 m_client.didScrollFrame(*this, frame);
208}
209
210bool PageOverlay::copyAccessibilityAttributeStringValueForPoint(String attribute, FloatPoint parameter, String& value)
211{
212 return m_client.copyAccessibilityAttributeStringValueForPoint(*this, attribute, parameter, value);
213}
214
215bool PageOverlay::copyAccessibilityAttributeBoolValueForPoint(String attribute, FloatPoint parameter, bool& value)
216{
217 return m_client.copyAccessibilityAttributeBoolValueForPoint(*this, attribute, parameter, value);
218}
219
220Vector<String> PageOverlay::copyAccessibilityAttributeNames(bool parameterizedNames)
221{
222 return m_client.copyAccessibilityAttributeNames(*this, parameterizedNames);
223}
224
225void PageOverlay::startFadeInAnimation()
226{
227 if (m_fadeAnimationType == FadeInAnimation && m_fadeAnimationTimer.isActive())
228 return;
229
230 m_fractionFadedIn = 0;
231 m_fadeAnimationType = FadeInAnimation;
232
233 startFadeAnimation();
234}
235
236void PageOverlay::startFadeOutAnimation()
237{
238 if (m_fadeAnimationType == FadeOutAnimation && m_fadeAnimationTimer.isActive())
239 return;
240
241 m_fractionFadedIn = 1;
242 m_fadeAnimationType = FadeOutAnimation;
243
244 startFadeAnimation();
245}
246
247void PageOverlay::stopFadeOutAnimation()
248{
249 m_fractionFadedIn = 1.0;
250 m_fadeAnimationTimer.stop();
251}
252
253void PageOverlay::startFadeAnimation()
254{
255 m_fadeAnimationStartTime = WallTime::now();
256 m_fadeAnimationTimer.startRepeating(1_s / fadeAnimationFrameRate);
257}
258
259void PageOverlay::fadeAnimationTimerFired()
260{
261 float animationProgress = (WallTime::now() - m_fadeAnimationStartTime) / m_fadeAnimationDuration;
262
263 if (animationProgress >= 1.0)
264 animationProgress = 1.0;
265
266 double sine = sin(piOverTwoFloat * animationProgress);
267 float fadeAnimationValue = sine * sine;
268
269 m_fractionFadedIn = (m_fadeAnimationType == FadeInAnimation) ? fadeAnimationValue : 1 - fadeAnimationValue;
270 controller()->setPageOverlayOpacity(*this, m_fractionFadedIn);
271
272 if (animationProgress == 1.0) {
273 m_fadeAnimationTimer.stop();
274
275 bool wasFadingOut = m_fadeAnimationType == FadeOutAnimation;
276 m_fadeAnimationType = NoAnimation;
277
278 // If this was a fade out, uninstall the page overlay.
279 if (wasFadingOut)
280 controller()->uninstallPageOverlay(*this, PageOverlay::FadeMode::DoNotFade);
281 }
282}
283
284void PageOverlay::clear()
285{
286 if (auto pageOverlayController = controller())
287 pageOverlayController->clearPageOverlay(*this);
288}
289
290GraphicsLayer& PageOverlay::layer()
291{
292 return controller()->layerForOverlay(*this);
293}
294
295} // namespace WebKit
296