Use enum classes and OptionSets for PaintPhase and PaintBehavior
[WebKit-https.git] / Source / WebCore / rendering / RenderWidget.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  * Copyright (C) 2000 Dirk Mueller (mueller@kde.org)
4  * Copyright (C) 2004, 2006, 2009, 2010 Apple Inc. All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22
23 #include "config.h"
24 #include "RenderWidget.h"
25
26 #include "AXObjectCache.h"
27 #include "FloatRoundedRect.h"
28 #include "Frame.h"
29 #include "HTMLFrameOwnerElement.h"
30 #include "HitTestResult.h"
31 #include "RenderLayer.h"
32 #include "RenderLayerBacking.h"
33 #include "RenderView.h"
34 #include "SecurityOrigin.h"
35 #include <wtf/IsoMallocInlines.h>
36 #include <wtf/StackStats.h>
37 #include <wtf/Ref.h>
38
39 namespace WebCore {
40
41 WTF_MAKE_ISO_ALLOCATED_IMPL(RenderWidget);
42
43 static HashMap<const Widget*, RenderWidget*>& widgetRendererMap()
44 {
45     static HashMap<const Widget*, RenderWidget*>* staticWidgetRendererMap = new HashMap<const Widget*, RenderWidget*>;
46     return *staticWidgetRendererMap;
47 }
48
49 unsigned WidgetHierarchyUpdatesSuspensionScope::s_widgetHierarchyUpdateSuspendCount = 0;
50
51 WidgetHierarchyUpdatesSuspensionScope::WidgetToParentMap& WidgetHierarchyUpdatesSuspensionScope::widgetNewParentMap()
52 {
53     static NeverDestroyed<WidgetToParentMap> map;
54     return map;
55 }
56
57 void WidgetHierarchyUpdatesSuspensionScope::moveWidgets()
58 {
59     auto map = WTFMove(widgetNewParentMap());
60     for (auto& entry : map) {
61         auto& child = *entry.key;
62         auto* currentParent = child.parent();
63         auto* newParent = entry.value;
64         if (newParent != currentParent) {
65             if (currentParent)
66                 currentParent->removeChild(child);
67             if (newParent)
68                 newParent->addChild(child);
69         }
70     }
71 }
72
73 static void moveWidgetToParentSoon(Widget& child, FrameView* parent)
74 {
75     if (!WidgetHierarchyUpdatesSuspensionScope::isSuspended()) {
76         if (parent)
77             parent->addChild(child);
78         else
79             child.removeFromParent();
80         return;
81     }
82     WidgetHierarchyUpdatesSuspensionScope::scheduleWidgetToMove(child, parent);
83 }
84
85 RenderWidget::RenderWidget(HTMLFrameOwnerElement& element, RenderStyle&& style)
86     : RenderReplaced(element, WTFMove(style))
87 {
88     setInline(false);
89 }
90
91 void RenderWidget::willBeDestroyed()
92 {
93 #if PLATFORM(IOS)
94     if (hasLayer())
95         layer()->willBeDestroyed();
96 #endif
97
98     if (AXObjectCache* cache = document().existingAXObjectCache()) {
99         cache->childrenChanged(this->parent());
100         cache->remove(this);
101     }
102
103     setWidget(nullptr);
104
105     RenderReplaced::willBeDestroyed();
106 }
107
108 RenderWidget::~RenderWidget()
109 {
110     ASSERT(!m_refCount);
111 }
112
113 // Widgets are always placed on integer boundaries, so rounding the size is actually
114 // the desired behavior. This function is here because it's otherwise seldom what we
115 // want to do with a LayoutRect.
116 static inline IntRect roundedIntRect(const LayoutRect& rect)
117 {
118     return IntRect(roundedIntPoint(rect.location()), roundedIntSize(rect.size()));
119 }
120
121 bool RenderWidget::setWidgetGeometry(const LayoutRect& frame)
122 {
123     IntRect clipRect = roundedIntRect(enclosingLayer()->childrenClipRect());
124     IntRect newFrameRect = roundedIntRect(frame);
125     IntRect oldFrameRect = m_widget->frameRect();
126     bool clipChanged = m_clipRect != clipRect;
127     bool boundsChanged = oldFrameRect != newFrameRect;
128
129     if (!boundsChanged && !clipChanged)
130         return false;
131
132     m_clipRect = clipRect;
133
134     auto weakThis = makeWeakPtr(*this);
135     // These calls *may* cause this renderer to disappear from underneath...
136     if (boundsChanged)
137         m_widget->setFrameRect(newFrameRect);
138     else if (clipChanged)
139         m_widget->clipRectChanged();
140     // ...so we follow up with a sanity check.
141     if (!weakThis)
142         return true;
143
144     if (boundsChanged && isComposited())
145         layer()->backing()->updateAfterWidgetResize();
146
147     return oldFrameRect.size() != newFrameRect.size();
148 }
149
150 bool RenderWidget::updateWidgetGeometry()
151 {
152     if (!m_widget->transformsAffectFrameRect())
153         return setWidgetGeometry(absoluteContentBox());
154
155     LayoutRect contentBox = contentBoxRect();
156     LayoutRect absoluteContentBox(localToAbsoluteQuad(FloatQuad(contentBox)).boundingBox());
157     if (m_widget->isFrameView()) {
158         contentBox.setLocation(absoluteContentBox.location());
159         return setWidgetGeometry(contentBox);
160     }
161
162     return setWidgetGeometry(absoluteContentBox);
163 }
164
165 void RenderWidget::setWidget(RefPtr<Widget>&& widget)
166 {
167     if (widget == m_widget)
168         return;
169
170     if (m_widget) {
171         moveWidgetToParentSoon(*m_widget, nullptr);
172         view().frameView().willRemoveWidgetFromRenderTree(*m_widget);
173         widgetRendererMap().remove(m_widget.get());
174         m_widget = nullptr;
175     }
176     m_widget = widget;
177     if (m_widget) {
178         widgetRendererMap().add(m_widget.get(), this);
179         view().frameView().didAddWidgetToRenderTree(*m_widget);
180         // If we've already received a layout, apply the calculated space to the
181         // widget immediately, but we have to have really been fully constructed.
182         if (hasInitializedStyle()) {
183             if (!needsLayout()) {
184                 auto weakThis = makeWeakPtr(*this);
185                 updateWidgetGeometry();
186                 if (!weakThis)
187                     return;
188             }
189
190             if (style().visibility() != Visibility::Visible)
191                 m_widget->hide();
192             else {
193                 m_widget->show();
194                 repaint();
195             }
196         }
197         moveWidgetToParentSoon(*m_widget, &view().frameView());
198     }
199 }
200
201 void RenderWidget::layout()
202 {
203     StackStats::LayoutCheckPoint layoutCheckPoint;
204     ASSERT(needsLayout());
205
206     clearNeedsLayout();
207 }
208
209 void RenderWidget::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
210 {
211     RenderReplaced::styleDidChange(diff, oldStyle);
212     if (m_widget) {
213         if (style().visibility() != Visibility::Visible)
214             m_widget->hide();
215         else
216             m_widget->show();
217     }
218 }
219
220 void RenderWidget::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
221 {
222     if (paintInfo.requireSecurityOriginAccessForWidgets) {
223         if (auto contentDocument = frameOwnerElement().contentDocument()) {
224             if (!document().securityOrigin().canAccess(contentDocument->securityOrigin()))
225                 return;
226         }
227     }
228
229     IntPoint contentPaintOffset = roundedIntPoint(paintOffset + location() + contentBoxRect().location());
230     // Tell the widget to paint now. This is the only time the widget is allowed
231     // to paint itself. That way it will composite properly with z-indexed layers.
232     LayoutRect paintRect = paintInfo.rect;
233
234     OptionSet<PaintBehavior> oldBehavior = PaintBehavior::Normal;
235     if (is<FrameView>(*m_widget) && (paintInfo.paintBehavior & PaintBehavior::TileFirstPaint)) {
236         FrameView& frameView = downcast<FrameView>(*m_widget);
237         oldBehavior = frameView.paintBehavior();
238         frameView.setPaintBehavior(oldBehavior | PaintBehavior::TileFirstPaint);
239     }
240
241     IntPoint widgetLocation = m_widget->frameRect().location();
242     IntSize widgetPaintOffset = contentPaintOffset - widgetLocation;
243     // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer,
244     // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing.
245     if (!widgetPaintOffset.isZero()) {
246         paintInfo.context().translate(widgetPaintOffset);
247         paintRect.move(-widgetPaintOffset);
248     }
249     // FIXME: Remove repaintrect enclosing/integral snapping when RenderWidget becomes device pixel snapped.
250     m_widget->paint(paintInfo.context(), snappedIntRect(paintRect), paintInfo.requireSecurityOriginAccessForWidgets ? Widget::SecurityOriginPaintPolicy::AccessibleOriginOnly : Widget::SecurityOriginPaintPolicy::AnyOrigin);
251
252     if (!widgetPaintOffset.isZero())
253         paintInfo.context().translate(-widgetPaintOffset);
254
255     if (is<FrameView>(*m_widget)) {
256         FrameView& frameView = downcast<FrameView>(*m_widget);
257         bool runOverlapTests = !frameView.useSlowRepaintsIfNotOverlapped();
258         if (paintInfo.overlapTestRequests && runOverlapTests) {
259             ASSERT(!paintInfo.overlapTestRequests->contains(this) || (paintInfo.overlapTestRequests->get(this) == m_widget->frameRect()));
260             paintInfo.overlapTestRequests->set(this, m_widget->frameRect());
261         }
262         if (paintInfo.paintBehavior & PaintBehavior::TileFirstPaint)
263             frameView.setPaintBehavior(oldBehavior);
264     }
265 }
266
267 void RenderWidget::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
268 {
269     if (!shouldPaint(paintInfo, paintOffset))
270         return;
271
272     LayoutPoint adjustedPaintOffset = paintOffset + location();
273
274     if (hasVisibleBoxDecorations() && (paintInfo.phase == PaintPhase::Foreground || paintInfo.phase == PaintPhase::Selection))
275         paintBoxDecorations(paintInfo, adjustedPaintOffset);
276
277     if (paintInfo.phase == PaintPhase::Mask) {
278         paintMask(paintInfo, adjustedPaintOffset);
279         return;
280     }
281
282     if ((paintInfo.phase == PaintPhase::Outline || paintInfo.phase == PaintPhase::SelfOutline) && hasOutline())
283         paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, size()));
284
285     if (paintInfo.phase != PaintPhase::Foreground)
286         return;
287
288     if (style().hasBorderRadius()) {
289         LayoutRect borderRect = LayoutRect(adjustedPaintOffset, size());
290
291         if (borderRect.isEmpty())
292             return;
293
294         // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
295         paintInfo.context().save();
296         FloatRoundedRect roundedInnerRect = FloatRoundedRect(style().getRoundedInnerBorderFor(borderRect,
297             paddingTop() + borderTop(), paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), paddingRight() + borderRight(), true, true));
298         clipRoundedInnerRect(paintInfo.context(), borderRect, roundedInnerRect);
299     }
300
301     if (m_widget)
302         paintContents(paintInfo, paintOffset);
303
304     if (style().hasBorderRadius())
305         paintInfo.context().restore();
306
307     // Paint a partially transparent wash over selected widgets.
308     if (isSelected() && !document().printing()) {
309         // FIXME: selectionRect() is in absolute, not painting coordinates.
310         paintInfo.context().fillRect(snappedIntRect(selectionRect()), selectionBackgroundColor());
311     }
312
313     if (hasLayer() && layer()->canResize())
314         layer()->paintResizer(paintInfo.context(), roundedIntPoint(adjustedPaintOffset), paintInfo.rect);
315 }
316
317 void RenderWidget::setOverlapTestResult(bool isOverlapped)
318 {
319     ASSERT(m_widget);
320     downcast<FrameView>(*m_widget).setIsOverlapped(isOverlapped);
321 }
322
323 RenderWidget::ChildWidgetState RenderWidget::updateWidgetPosition()
324 {
325     if (!m_widget)
326         return ChildWidgetState::Destroyed;
327
328     auto weakThis = makeWeakPtr(*this);
329     bool widgetSizeChanged = updateWidgetGeometry();
330     if (!weakThis || !m_widget)
331         return ChildWidgetState::Destroyed;
332
333     // if the frame size got changed, or if view needs layout (possibly indicating
334     // content size is wrong) we have to do a layout to set the right widget size.
335     if (is<FrameView>(*m_widget)) {
336         FrameView& frameView = downcast<FrameView>(*m_widget);
337         // Check the frame's page to make sure that the frame isn't in the process of being destroyed.
338         if ((widgetSizeChanged || frameView.needsLayout()) && frameView.frame().page() && frameView.frame().document())
339             frameView.layoutContext().layout();
340     }
341     return ChildWidgetState::Valid;
342 }
343
344 IntRect RenderWidget::windowClipRect() const
345 {
346     return intersection(view().frameView().contentsToWindow(m_clipRect), view().frameView().windowClipRect());
347 }
348
349 void RenderWidget::setSelectionState(SelectionState state)
350 {
351     // The selection state for our containing block hierarchy is updated by the base class call.
352     RenderReplaced::setSelectionState(state);
353
354     if (m_widget)
355         m_widget->setIsSelected(isSelected());
356 }
357
358 RenderWidget* RenderWidget::find(const Widget& widget)
359 {
360     return widgetRendererMap().get(&widget);
361 }
362
363 bool RenderWidget::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
364 {
365     if (request.allowsChildFrameContent() && is<FrameView>(widget()) && downcast<FrameView>(*widget()).renderView()) {
366         FrameView& childFrameView = downcast<FrameView>(*widget());
367         RenderView& childRoot = *childFrameView.renderView();
368
369         LayoutPoint adjustedLocation = accumulatedOffset + location();
370         LayoutPoint contentOffset = LayoutPoint(borderLeft() + paddingLeft(), borderTop() + paddingTop()) - toIntSize(childFrameView.scrollPosition());
371         HitTestLocation newHitTestLocation(locationInContainer, -adjustedLocation - contentOffset);
372         HitTestRequest newHitTestRequest(request.type() | HitTestRequest::ChildFrameHitTest);
373         HitTestResult childFrameResult(newHitTestLocation);
374
375         bool isInsideChildFrame = childRoot.hitTest(newHitTestRequest, newHitTestLocation, childFrameResult);
376
377         if (request.resultIsElementList())
378             result.append(childFrameResult, request);
379         else if (isInsideChildFrame)
380             result = childFrameResult;
381
382         if (isInsideChildFrame)
383             return true;
384     }
385
386     bool hadResult = result.innerNode();
387     bool inside = RenderReplaced::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, action);
388
389     // Check to see if we are really over the widget itself (and not just in the border/padding area).
390     if ((inside || result.isRectBasedTest()) && !hadResult && result.innerNode() == &frameOwnerElement())
391         result.setIsOverWidget(contentBoxRect().contains(result.localPoint()));
392     return inside;
393 }
394
395 bool RenderWidget::requiresLayer() const
396 {
397     return RenderReplaced::requiresLayer() || requiresAcceleratedCompositing();
398 }
399
400 bool RenderWidget::requiresAcceleratedCompositing() const
401 {
402     // If this is a renderer with a contentDocument and that document needs a layer, then we need a layer.
403     if (Document* contentDocument = frameOwnerElement().contentDocument()) {
404         if (RenderView* view = contentDocument->renderView())
405             return view->usesCompositing();
406     }
407
408     return false;
409 }
410
411 bool RenderWidget::needsPreferredWidthsRecalculation() const
412 {
413     if (RenderReplaced::needsPreferredWidthsRecalculation())
414         return true;
415     return embeddedContentBox();
416 }
417
418 RenderBox* RenderWidget::embeddedContentBox() const
419 {
420     if (!is<FrameView>(widget()))
421         return nullptr;
422     return downcast<FrameView>(*widget()).embeddedContentBox();
423 }
424
425 } // namespace WebCore