1/*
2 * Copyright (C) 2014-2017 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 "PageOverlayController.h"
28
29#include "Chrome.h"
30#include "ChromeClient.h"
31#include "Frame.h"
32#include "FrameView.h"
33#include "GraphicsContext.h"
34#include "GraphicsLayer.h"
35#include "Page.h"
36#include "PageOverlay.h"
37#include "ScrollingCoordinator.h"
38#include "Settings.h"
39#include "TiledBacking.h"
40
41// FIXME: Someone needs to call didChangeSettings() if we want dynamic updates of layer border/repaint counter settings.
42
43namespace WebCore {
44
45PageOverlayController::PageOverlayController(Page& page)
46 : m_page(page)
47{
48}
49
50PageOverlayController::~PageOverlayController() = default;
51
52void PageOverlayController::createRootLayersIfNeeded()
53{
54 if (m_initialized)
55 return;
56
57 m_initialized = true;
58
59 ASSERT(!m_documentOverlayRootLayer);
60 ASSERT(!m_viewOverlayRootLayer);
61
62 m_documentOverlayRootLayer = GraphicsLayer::create(m_page.chrome().client().graphicsLayerFactory(), *this);
63 m_viewOverlayRootLayer = GraphicsLayer::create(m_page.chrome().client().graphicsLayerFactory(), *this);
64 m_documentOverlayRootLayer->setName("Document overlay Container");
65 m_viewOverlayRootLayer->setName("View overlay container");
66}
67
68void PageOverlayController::installedPageOverlaysChanged()
69{
70 if (hasViewOverlays())
71 attachViewOverlayLayers();
72 else
73 detachViewOverlayLayers();
74
75 if (auto* frameView = m_page.mainFrame().view())
76 frameView->setNeedsCompositingConfigurationUpdate();
77
78 updateForceSynchronousScrollLayerPositionUpdates();
79}
80
81bool PageOverlayController::hasDocumentOverlays() const
82{
83 for (const auto& overlay : m_pageOverlays) {
84 if (overlay->overlayType() == PageOverlay::OverlayType::Document)
85 return true;
86 }
87 return false;
88}
89
90bool PageOverlayController::hasViewOverlays() const
91{
92 for (const auto& overlay : m_pageOverlays) {
93 if (overlay->overlayType() == PageOverlay::OverlayType::View)
94 return true;
95 }
96 return false;
97}
98
99void PageOverlayController::attachViewOverlayLayers()
100{
101 if (hasViewOverlays())
102 m_page.chrome().client().attachViewOverlayGraphicsLayer(&layerWithViewOverlays());
103}
104
105void PageOverlayController::detachViewOverlayLayers()
106{
107 m_page.chrome().client().attachViewOverlayGraphicsLayer(nullptr);
108}
109
110GraphicsLayer* PageOverlayController::documentOverlayRootLayer() const
111{
112 return m_documentOverlayRootLayer.get();
113}
114
115GraphicsLayer* PageOverlayController::viewOverlayRootLayer() const
116{
117 return m_viewOverlayRootLayer.get();
118}
119
120static void updateOverlayGeometry(PageOverlay& overlay, GraphicsLayer& graphicsLayer)
121{
122 IntRect overlayFrame = overlay.frame();
123
124 if (overlayFrame.location() == graphicsLayer.position() && overlayFrame.size() == graphicsLayer.size())
125 return;
126
127 graphicsLayer.setPosition(overlayFrame.location());
128 graphicsLayer.setSize(overlayFrame.size());
129}
130
131GraphicsLayer& PageOverlayController::layerWithDocumentOverlays()
132{
133 createRootLayersIfNeeded();
134
135 bool inWindow = m_page.isInWindow();
136
137 for (auto& overlayAndLayer : m_overlayGraphicsLayers) {
138 PageOverlay& overlay = *overlayAndLayer.key;
139 if (overlay.overlayType() != PageOverlay::OverlayType::Document)
140 continue;
141
142 auto& layer = overlayAndLayer.value;
143 GraphicsLayer::traverse(layer.get(), [inWindow](GraphicsLayer& layer) {
144 layer.setIsInWindow(inWindow);
145 });
146 updateOverlayGeometry(overlay, layer.get());
147
148 if (!layer->parent())
149 m_documentOverlayRootLayer->addChild(layer.copyRef());
150 }
151
152 return *m_documentOverlayRootLayer;
153}
154
155GraphicsLayer& PageOverlayController::layerWithViewOverlays()
156{
157 createRootLayersIfNeeded();
158
159 bool inWindow = m_page.isInWindow();
160
161 for (auto& overlayAndLayer : m_overlayGraphicsLayers) {
162 PageOverlay& overlay = *overlayAndLayer.key;
163 if (overlay.overlayType() != PageOverlay::OverlayType::View)
164 continue;
165
166 auto& layer = overlayAndLayer.value;
167 GraphicsLayer::traverse(layer.get(), [inWindow](GraphicsLayer& layer) {
168 layer.setIsInWindow(inWindow);
169 });
170 updateOverlayGeometry(overlay, layer.get());
171
172 if (!layer->parent())
173 m_viewOverlayRootLayer->addChild(layer.copyRef());
174 }
175
176 return *m_viewOverlayRootLayer;
177}
178
179void PageOverlayController::installPageOverlay(PageOverlay& overlay, PageOverlay::FadeMode fadeMode)
180{
181 createRootLayersIfNeeded();
182
183 if (m_pageOverlays.contains(&overlay))
184 return;
185
186 m_pageOverlays.append(&overlay);
187
188 auto layer = GraphicsLayer::create(m_page.chrome().client().graphicsLayerFactory(), *this);
189 layer->setAnchorPoint({ });
190 layer->setBackgroundColor(overlay.backgroundColor());
191 layer->setName("Overlay content");
192
193 updateSettingsForLayer(layer.get());
194
195 switch (overlay.overlayType()) {
196 case PageOverlay::OverlayType::View:
197 m_viewOverlayRootLayer->addChild(layer.get());
198 break;
199 case PageOverlay::OverlayType::Document:
200 m_documentOverlayRootLayer->addChild(layer.get());
201 break;
202 }
203
204 auto& rawLayer = layer.get();
205 m_overlayGraphicsLayers.set(&overlay, WTFMove(layer));
206
207 overlay.setPage(&m_page);
208
209 if (FrameView* frameView = m_page.mainFrame().view())
210 frameView->enterCompositingMode();
211
212 updateOverlayGeometry(overlay, rawLayer);
213
214 if (fadeMode == PageOverlay::FadeMode::Fade)
215 overlay.startFadeInAnimation();
216
217 installedPageOverlaysChanged();
218}
219
220void PageOverlayController::uninstallPageOverlay(PageOverlay& overlay, PageOverlay::FadeMode fadeMode)
221{
222 if (fadeMode == PageOverlay::FadeMode::Fade) {
223 overlay.startFadeOutAnimation();
224 return;
225 }
226
227 overlay.setPage(nullptr);
228
229 if (auto optionalLayer = m_overlayGraphicsLayers.take(&overlay))
230 optionalLayer.value()->removeFromParent();
231
232 bool removed = m_pageOverlays.removeFirst(&overlay);
233 ASSERT_UNUSED(removed, removed);
234
235 installedPageOverlaysChanged();
236}
237
238void PageOverlayController::updateForceSynchronousScrollLayerPositionUpdates()
239{
240#if ENABLE(ASYNC_SCROLLING)
241 bool forceSynchronousScrollLayerPositionUpdates = false;
242
243 for (auto& overlay : m_pageOverlays) {
244 if (overlay->needsSynchronousScrolling())
245 forceSynchronousScrollLayerPositionUpdates = true;
246 }
247
248 if (ScrollingCoordinator* scrollingCoordinator = m_page.scrollingCoordinator())
249 scrollingCoordinator->setForceSynchronousScrollLayerPositionUpdates(forceSynchronousScrollLayerPositionUpdates);
250#endif
251}
252
253void PageOverlayController::setPageOverlayNeedsDisplay(PageOverlay& overlay, const IntRect& dirtyRect)
254{
255 ASSERT(m_pageOverlays.contains(&overlay));
256 auto* graphicsLayer = m_overlayGraphicsLayers.get(&overlay);
257
258 if (!graphicsLayer->drawsContent()) {
259 graphicsLayer->setDrawsContent(true);
260 updateOverlayGeometry(overlay, *graphicsLayer);
261 }
262
263 graphicsLayer->setNeedsDisplayInRect(dirtyRect);
264}
265
266void PageOverlayController::setPageOverlayOpacity(PageOverlay& overlay, float opacity)
267{
268 ASSERT(m_pageOverlays.contains(&overlay));
269 m_overlayGraphicsLayers.get(&overlay)->setOpacity(opacity);
270}
271
272void PageOverlayController::clearPageOverlay(PageOverlay& overlay)
273{
274 ASSERT(m_pageOverlays.contains(&overlay));
275 m_overlayGraphicsLayers.get(&overlay)->setDrawsContent(false);
276}
277
278GraphicsLayer& PageOverlayController::layerForOverlay(PageOverlay& overlay) const
279{
280 ASSERT(m_pageOverlays.contains(&overlay));
281 return *m_overlayGraphicsLayers.get(&overlay);
282}
283
284void PageOverlayController::didChangeViewSize()
285{
286 for (auto& overlayAndLayer : m_overlayGraphicsLayers) {
287 if (overlayAndLayer.key->overlayType() == PageOverlay::OverlayType::View)
288 updateOverlayGeometry(*overlayAndLayer.key, overlayAndLayer.value.get());
289 }
290}
291
292void PageOverlayController::didChangeDocumentSize()
293{
294 for (auto& overlayAndLayer : m_overlayGraphicsLayers) {
295 if (overlayAndLayer.key->overlayType() == PageOverlay::OverlayType::Document)
296 updateOverlayGeometry(*overlayAndLayer.key, overlayAndLayer.value.get());
297 }
298}
299
300void PageOverlayController::didChangeSettings()
301{
302 // FIXME: We should apply these settings to all overlay sublayers recursively.
303 for (auto& graphicsLayer : m_overlayGraphicsLayers.values())
304 updateSettingsForLayer(graphicsLayer.get());
305}
306
307void PageOverlayController::didChangeDeviceScaleFactor()
308{
309 if (!m_initialized)
310 return;
311
312 m_documentOverlayRootLayer->noteDeviceOrPageScaleFactorChangedIncludingDescendants();
313 m_viewOverlayRootLayer->noteDeviceOrPageScaleFactorChangedIncludingDescendants();
314
315 for (auto& graphicsLayer : m_overlayGraphicsLayers.values())
316 graphicsLayer->setNeedsDisplay();
317}
318
319void PageOverlayController::didChangeViewExposedRect()
320{
321 m_page.renderingUpdateScheduler().scheduleTimedRenderingUpdate();
322}
323
324void PageOverlayController::didScrollFrame(Frame& frame)
325{
326 for (auto& overlayAndLayer : m_overlayGraphicsLayers) {
327 if (overlayAndLayer.key->overlayType() == PageOverlay::OverlayType::View || !frame.isMainFrame())
328 overlayAndLayer.value->setNeedsDisplay();
329 overlayAndLayer.key->didScrollFrame(frame);
330 }
331}
332
333void PageOverlayController::updateSettingsForLayer(GraphicsLayer& layer)
334{
335 Settings& settings = m_page.settings();
336 layer.setAcceleratesDrawing(settings.acceleratedDrawingEnabled());
337 layer.setShowDebugBorder(settings.showDebugBorders());
338 layer.setShowRepaintCounter(settings.showRepaintCounter());
339}
340
341bool PageOverlayController::handleMouseEvent(const PlatformMouseEvent& mouseEvent)
342{
343 if (m_pageOverlays.isEmpty())
344 return false;
345
346 for (auto it = m_pageOverlays.rbegin(), end = m_pageOverlays.rend(); it != end; ++it) {
347 if ((*it)->mouseEvent(mouseEvent))
348 return true;
349 }
350
351 return false;
352}
353
354bool PageOverlayController::copyAccessibilityAttributeStringValueForPoint(String attribute, FloatPoint parameter, String& value)
355{
356 if (m_pageOverlays.isEmpty())
357 return false;
358
359 for (auto it = m_pageOverlays.rbegin(), end = m_pageOverlays.rend(); it != end; ++it) {
360 if ((*it)->copyAccessibilityAttributeStringValueForPoint(attribute, parameter, value))
361 return true;
362 }
363
364 return false;
365}
366
367bool PageOverlayController::copyAccessibilityAttributeBoolValueForPoint(String attribute, FloatPoint parameter, bool& value)
368{
369 if (m_pageOverlays.isEmpty())
370 return false;
371
372 for (auto it = m_pageOverlays.rbegin(), end = m_pageOverlays.rend(); it != end; ++it) {
373 if ((*it)->copyAccessibilityAttributeBoolValueForPoint(attribute, parameter, value))
374 return true;
375 }
376
377 return false;
378}
379
380Vector<String> PageOverlayController::copyAccessibilityAttributesNames(bool parameterizedNames)
381{
382 if (m_pageOverlays.isEmpty())
383 return { };
384
385 for (auto it = m_pageOverlays.rbegin(), end = m_pageOverlays.rend(); it != end; ++it) {
386 Vector<String> names = (*it)->copyAccessibilityAttributeNames(parameterizedNames);
387 if (!names.isEmpty())
388 return names;
389 }
390
391 return { };
392}
393
394void PageOverlayController::paintContents(const GraphicsLayer* graphicsLayer, GraphicsContext& graphicsContext, OptionSet<GraphicsLayerPaintingPhase>, const FloatRect& clipRect, GraphicsLayerPaintBehavior)
395{
396 for (auto& overlayAndGraphicsLayer : m_overlayGraphicsLayers) {
397 if (overlayAndGraphicsLayer.value.ptr() != graphicsLayer)
398 continue;
399
400 GraphicsContextStateSaver stateSaver(graphicsContext);
401 graphicsContext.clip(clipRect);
402 overlayAndGraphicsLayer.key->drawRect(graphicsContext, enclosingIntRect(clipRect));
403
404 return;
405 }
406}
407
408float PageOverlayController::deviceScaleFactor() const
409{
410 return m_page.deviceScaleFactor();
411}
412
413void PageOverlayController::notifyFlushRequired(const GraphicsLayer*)
414{
415 m_page.renderingUpdateScheduler().scheduleTimedRenderingUpdate();
416}
417
418void PageOverlayController::didChangeOverlayFrame(PageOverlay& overlay)
419{
420 ASSERT(m_pageOverlays.contains(&overlay));
421 if (auto* layer = m_overlayGraphicsLayers.get(&overlay))
422 updateOverlayGeometry(overlay, *layer);
423}
424
425void PageOverlayController::didChangeOverlayBackgroundColor(PageOverlay& overlay)
426{
427 ASSERT(m_pageOverlays.contains(&overlay));
428 if (auto* layer = m_overlayGraphicsLayers.get(&overlay))
429 layer->setBackgroundColor(overlay.backgroundColor());
430}
431
432bool PageOverlayController::shouldSkipLayerInDump(const GraphicsLayer*, LayerTreeAsTextBehavior behavior) const
433{
434 return !(behavior & LayerTreeAsTextIncludePageOverlayLayers);
435}
436
437void PageOverlayController::tiledBackingUsageChanged(const GraphicsLayer* graphicsLayer, bool usingTiledBacking)
438{
439 if (usingTiledBacking)
440 graphicsLayer->tiledBacking()->setIsInWindow(m_page.isInWindow());
441}
442
443} // namespace WebKit
444