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