b2621ce5374af4da4d83decaf340f564fb447818
[WebKit-https.git] / Source / WebCore / page / DebugPageOverlays.cpp
1 /*
2  * Copyright (C) 2014 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 "DebugPageOverlays.h"
28
29 #include "ElementIterator.h"
30 #include "FrameView.h"
31 #include "GraphicsContext.h"
32 #include "MainFrame.h"
33 #include "Page.h"
34 #include "PageOverlay.h"
35 #include "PageOverlayController.h"
36 #include "Region.h"
37 #include "ScrollingCoordinator.h"
38 #include "Settings.h"
39 #include <wtf/RefPtr.h>
40
41 namespace WebCore {
42
43 DebugPageOverlays* DebugPageOverlays::sharedDebugOverlays;
44
45 class RegionOverlay : public RefCounted<RegionOverlay>, public PageOverlay::Client {
46 public:
47     static PassRefPtr<RegionOverlay> create(MainFrame&, DebugPageOverlays::RegionType);
48     virtual ~RegionOverlay();
49
50     void recomputeRegion();
51     PageOverlay& overlay() { return *m_overlay; }
52
53 protected:
54     RegionOverlay(MainFrame&, Color);
55
56 private:
57     virtual void pageOverlayDestroyed(PageOverlay&) final;
58     virtual void willMoveToPage(PageOverlay&, Page*) final;
59     virtual void didMoveToPage(PageOverlay&, Page*) final;
60     virtual void drawRect(PageOverlay&, GraphicsContext&, const IntRect& dirtyRect) final;
61     virtual bool mouseEvent(PageOverlay&, const PlatformMouseEvent&) final;
62     virtual void didScrollFrame(PageOverlay&, Frame&) final;
63
64 protected:
65     // Returns true if the region changed.
66     virtual bool updateRegion() = 0;
67     
68     MainFrame& m_frame;
69     RefPtr<PageOverlay> m_overlay;
70     std::unique_ptr<Region> m_region;
71     Color m_color;
72 };
73
74
75 class MouseWheelRegionOverlay final : public RegionOverlay {
76 public:
77     static PassRefPtr<MouseWheelRegionOverlay> create(MainFrame& frame)
78     {
79         return adoptRef(new MouseWheelRegionOverlay(frame));
80     }
81
82 private:
83     explicit MouseWheelRegionOverlay(MainFrame& frame)
84         : RegionOverlay(frame, Color(0.5f, 0.0f, 0.0f, 0.4f))
85     {
86     }
87
88     virtual bool updateRegion() override;
89 };
90
91 bool MouseWheelRegionOverlay::updateRegion()
92 {
93     std::unique_ptr<Region> region = std::make_unique<Region>(m_frame.document()->absoluteRegionForEventTargets(m_frame.document()->wheelEventTargets()));
94
95     bool regionChanged = !m_region || !(*m_region == *region);
96     m_region = WTF::move(region);
97     return regionChanged;
98 }
99
100 class NonFastScrollableRegionOverlay final : public RegionOverlay {
101 public:
102     static PassRefPtr<NonFastScrollableRegionOverlay> create(MainFrame& frame)
103     {
104         return adoptRef(new NonFastScrollableRegionOverlay(frame));
105     }
106
107 private:
108     explicit NonFastScrollableRegionOverlay(MainFrame& frame)
109         : RegionOverlay(frame, Color(1.0f, 0.5f, 0.0f, 0.4f))
110     {
111     }
112
113     virtual bool updateRegion() override;
114 };
115
116 bool NonFastScrollableRegionOverlay::updateRegion()
117 {
118     std::unique_ptr<Region> region = std::make_unique<Region>();
119
120     if (Page* page = m_frame.page()) {
121         if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator())
122             *region = scrollingCoordinator->computeNonFastScrollableRegion(m_frame, IntPoint());
123     }
124
125     // computeNonFastScrollableRegion() applies topContentInset.
126     region->translate(IntSize(0, -m_frame.view()->topContentInset()));
127
128     bool regionChanged = !m_region || !(*m_region == *region);
129     m_region = WTF::move(region);
130     return regionChanged;
131 }
132
133 PassRefPtr<RegionOverlay> RegionOverlay::create(MainFrame& frame, DebugPageOverlays::RegionType regionType)
134 {
135     switch (regionType) {
136     case DebugPageOverlays::RegionType::WheelEventHandlers:
137         return MouseWheelRegionOverlay::create(frame);
138
139     case DebugPageOverlays::RegionType::NonFastScrollableRegion:
140         return NonFastScrollableRegionOverlay::create(frame);
141     }
142     return nullptr;
143 }
144
145 RegionOverlay::RegionOverlay(MainFrame& frame, Color regionColor)
146     : m_frame(frame)
147     , m_overlay(PageOverlay::create(*this, PageOverlay::OverlayType::Document))
148     , m_color(regionColor)
149 {
150 }
151
152 RegionOverlay::~RegionOverlay()
153 {
154     if (m_overlay)
155         m_frame.pageOverlayController().uninstallPageOverlay(m_overlay.get(), PageOverlay::FadeMode::DoNotFade);
156 }
157
158 void RegionOverlay::pageOverlayDestroyed(PageOverlay&)
159 {
160 }
161
162 void RegionOverlay::willMoveToPage(PageOverlay&, Page* page)
163 {
164     if (!page)
165         m_overlay = nullptr;
166 }
167
168 void RegionOverlay::didMoveToPage(PageOverlay&, Page* page)
169 {
170     if (page)
171         recomputeRegion();
172 }
173
174 void RegionOverlay::drawRect(PageOverlay&, GraphicsContext& context, const IntRect& dirtyRect)
175 {
176     context.clearRect(dirtyRect);
177
178     if (!m_region)
179         return;
180     
181     GraphicsContextStateSaver saver(context);
182     context.setFillColor(m_color, ColorSpaceSRGB);
183     for (auto rect : m_region->rects()) {
184     
185         if (rect.intersects(dirtyRect))
186             context.fillRect(rect);
187     }
188 }
189
190 bool RegionOverlay::mouseEvent(PageOverlay&, const PlatformMouseEvent&)
191 {
192     return false;
193 }
194
195 void RegionOverlay::didScrollFrame(PageOverlay&, Frame&)
196 {
197 }
198
199 void RegionOverlay::recomputeRegion()
200 {
201     if (updateRegion())
202         m_overlay->setNeedsDisplay();
203 }
204
205 DebugPageOverlays& DebugPageOverlays::singleton()
206 {
207     if (!sharedDebugOverlays)
208         sharedDebugOverlays = new DebugPageOverlays;
209
210     return *sharedDebugOverlays;
211 }
212
213 static inline size_t indexOf(DebugPageOverlays::RegionType regionType)
214 {
215     return static_cast<size_t>(regionType);
216 }
217
218 RegionOverlay& DebugPageOverlays::ensureRegionOverlayForFrame(MainFrame& frame, RegionType regionType)
219 {
220     auto it = m_frameRegionOverlays.find(&frame);
221     if (it != m_frameRegionOverlays.end()) {
222         auto& visualizers = it->value;
223         
224         if (!visualizers[indexOf(regionType)])
225             visualizers[indexOf(regionType)] = RegionOverlay::create(frame, regionType);
226
227         return *visualizers[indexOf(regionType)];
228     }
229
230     Vector<RefPtr<RegionOverlay>> visualizers(NumberOfRegionTypes);
231     
232     RefPtr<RegionOverlay> visualizer = RegionOverlay::create(frame, regionType);
233     visualizers[indexOf(regionType)] = visualizer;
234
235     m_frameRegionOverlays.add(&frame, WTF::move(visualizers));
236     return *visualizer;
237 }
238
239 void DebugPageOverlays::showRegionOverlay(MainFrame& frame, RegionType regionType)
240 {
241     RegionOverlay& visualizer = ensureRegionOverlayForFrame(frame, regionType);
242     frame.pageOverlayController().installPageOverlay(&visualizer.overlay(), PageOverlay::FadeMode::DoNotFade);
243 }
244
245 void DebugPageOverlays::hideRegionOverlay(MainFrame& frame, RegionType regionType)
246 {
247     auto it = m_frameRegionOverlays.find(&frame);
248     if (it != m_frameRegionOverlays.end()) {
249         auto& visualizers = it->value;
250         if (RegionOverlay* visualizer = visualizers[indexOf(regionType)].get()) {
251             frame.pageOverlayController().uninstallPageOverlay(&visualizer->overlay(), PageOverlay::FadeMode::DoNotFade);
252             visualizers[indexOf(regionType)] = nullptr;
253         }
254     }
255 }
256
257 void DebugPageOverlays::regionChanged(Frame& frame, RegionType regionType)
258 {
259     if (RegionOverlay* visualizer = regionOverlayForFrame(frame.mainFrame(), regionType))
260         visualizer->recomputeRegion();
261 }
262
263 RegionOverlay* DebugPageOverlays::regionOverlayForFrame(MainFrame& frame, RegionType regionType) const
264 {
265     auto it = m_frameRegionOverlays.find(&frame);
266     if (it != m_frameRegionOverlays.end())
267         return it->value.at(indexOf(regionType)).get();
268
269     return nullptr;
270 }
271
272 void DebugPageOverlays::updateOverlayRegionVisibility(MainFrame& frame, DebugOverlayRegions visibleRegions)
273 {
274     if (visibleRegions & NonFastScrollableRegion)
275         showRegionOverlay(frame, RegionType::NonFastScrollableRegion);
276     else
277         hideRegionOverlay(frame, RegionType::NonFastScrollableRegion);
278
279     if (visibleRegions & WheelEventHandlerRegion)
280         showRegionOverlay(frame, RegionType::WheelEventHandlers);
281     else
282         hideRegionOverlay(frame, RegionType::WheelEventHandlers);
283 }
284
285 void DebugPageOverlays::settingsChanged(MainFrame& frame)
286 {
287     DebugOverlayRegions activeOverlayRegions = frame.settings().visibleDebugOverlayRegions();
288     if (!activeOverlayRegions && !hasOverlays(frame))
289         return;
290
291     DebugPageOverlays::singleton().updateOverlayRegionVisibility(frame, activeOverlayRegions);
292 }
293
294 }