Add WTF::move()
[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 <wtf/StackStats.h>
35 #include <wtf/Ref.h>
36
37 namespace WebCore {
38
39 static HashMap<const Widget*, RenderWidget*>& widgetRendererMap()
40 {
41     static HashMap<const Widget*, RenderWidget*>* staticWidgetRendererMap = new HashMap<const Widget*, RenderWidget*>;
42     return *staticWidgetRendererMap;
43 }
44
45 unsigned WidgetHierarchyUpdatesSuspensionScope::s_widgetHierarchyUpdateSuspendCount = 0;
46
47 WidgetHierarchyUpdatesSuspensionScope::WidgetToParentMap& WidgetHierarchyUpdatesSuspensionScope::widgetNewParentMap()
48 {
49     DEPRECATED_DEFINE_STATIC_LOCAL(WidgetToParentMap, map, ());
50     return map;
51 }
52
53 void WidgetHierarchyUpdatesSuspensionScope::moveWidgets()
54 {
55     WidgetToParentMap map;
56     widgetNewParentMap().swap(map);
57     WidgetToParentMap::iterator end = map.end();
58     for (WidgetToParentMap::iterator it = map.begin(); it != end; ++it) {
59         Widget* child = it->key.get();
60         ScrollView* currentParent = child->parent();
61         FrameView* newParent = it->value;
62         if (newParent != currentParent) {
63             if (currentParent)
64                 currentParent->removeChild(child);
65             if (newParent)
66                 newParent->addChild(child);
67         }
68     }
69 }
70
71 static void moveWidgetToParentSoon(Widget* child, FrameView* parent)
72 {
73     if (!WidgetHierarchyUpdatesSuspensionScope::isSuspended()) {
74         if (parent)
75             parent->addChild(child);
76         else
77             child->removeFromParent();
78         return;
79     }
80     WidgetHierarchyUpdatesSuspensionScope::scheduleWidgetToMove(child, parent);
81 }
82
83 RenderWidget::RenderWidget(HTMLFrameOwnerElement& element, PassRef<RenderStyle> style)
84     : RenderReplaced(element, WTF::move(style))
85     , m_weakPtrFactory(this)
86 {
87     setInline(false);
88 }
89
90 void RenderWidget::willBeDestroyed()
91 {
92 #if PLATFORM(IOS)
93     if (hasLayer())
94         layer()->willBeDestroyed();
95 #endif
96
97     if (AXObjectCache* cache = document().existingAXObjectCache()) {
98         cache->childrenChanged(this->parent());
99         cache->remove(this);
100     }
101
102     setWidget(0);
103
104     RenderReplaced::willBeDestroyed();
105 }
106
107 RenderWidget::~RenderWidget()
108 {
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 && hasLayer() && layer()->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(PassRefPtr<Widget> widget)
164 {
165     if (widget == m_widget)
166         return;
167
168     if (m_widget) {
169         moveWidgetToParentSoon(m_widget.get(), 0);
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.get(), &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     LayoutPoint adjustedPaintOffset = paintOffset + location();
221
222     // Tell the widget to paint now. This is the only time the widget is allowed
223     // to paint itself. That way it will composite properly with z-indexed layers.
224     IntPoint widgetLocation = m_widget->frameRect().location();
225     IntPoint paintLocation(roundToInt(adjustedPaintOffset.x() + borderLeft() + paddingLeft()),
226         roundToInt(adjustedPaintOffset.y() + borderTop() + paddingTop()));
227     LayoutRect paintRect = paintInfo.rect;
228
229     LayoutSize widgetPaintOffset = paintLocation - widgetLocation;
230     // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer,
231     // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing.
232     if (!widgetPaintOffset.isZero()) {
233         paintInfo.context->translate(widgetPaintOffset);
234         paintRect.move(-widgetPaintOffset);
235     }
236     m_widget->paint(paintInfo.context, pixelSnappedIntRect(paintRect));
237
238     if (!widgetPaintOffset.isZero())
239         paintInfo.context->translate(-widgetPaintOffset);
240
241     if (m_widget->isFrameView()) {
242         FrameView* frameView = toFrameView(m_widget.get());
243         bool runOverlapTests = !frameView->useSlowRepaintsIfNotOverlapped() || frameView->hasCompositedContentIncludingDescendants();
244         if (paintInfo.overlapTestRequests && runOverlapTests) {
245             ASSERT(!paintInfo.overlapTestRequests->contains(this));
246             paintInfo.overlapTestRequests->set(this, m_widget->frameRect());
247         }
248     }
249 }
250
251 void RenderWidget::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
252 {
253     if (!shouldPaint(paintInfo, paintOffset))
254         return;
255
256     LayoutPoint adjustedPaintOffset = paintOffset + location();
257
258     if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
259         paintBoxDecorations(paintInfo, adjustedPaintOffset);
260
261     if (paintInfo.phase == PaintPhaseMask) {
262         paintMask(paintInfo, adjustedPaintOffset);
263         return;
264     }
265
266     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && hasOutline())
267         paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, size()));
268
269     if (paintInfo.phase != PaintPhaseForeground)
270         return;
271
272     if (style().hasBorderRadius()) {
273         LayoutRect borderRect = LayoutRect(adjustedPaintOffset, size());
274
275         if (borderRect.isEmpty())
276             return;
277
278         // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
279         paintInfo.context->save();
280         FloatRoundedRect roundedInnerRect = FloatRoundedRect(style().getRoundedInnerBorderFor(borderRect,
281             paddingTop() + borderTop(), paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), paddingRight() + borderRight(), true, true));
282         clipRoundedInnerRect(paintInfo.context, borderRect, roundedInnerRect);
283     }
284
285     if (m_widget)
286         paintContents(paintInfo, paintOffset);
287
288     if (style().hasBorderRadius())
289         paintInfo.context->restore();
290
291     // Paint a partially transparent wash over selected widgets.
292     if (isSelected() && !document().printing()) {
293         // FIXME: selectionRect() is in absolute, not painting coordinates.
294         paintInfo.context->fillRect(pixelSnappedIntRect(selectionRect()), selectionBackgroundColor(), style().colorSpace());
295     }
296
297     if (hasLayer() && layer()->canResize())
298         layer()->paintResizer(paintInfo.context, roundedIntPoint(adjustedPaintOffset), paintInfo.rect);
299 }
300
301 void RenderWidget::setOverlapTestResult(bool isOverlapped)
302 {
303     ASSERT(m_widget);
304     ASSERT(m_widget->isFrameView());
305     toFrameView(m_widget.get())->setIsOverlapped(isOverlapped);
306 }
307
308 void RenderWidget::updateWidgetPosition()
309 {
310     if (!m_widget)
311         return;
312
313     WeakPtr<RenderWidget> weakThis = createWeakPtr();
314     bool widgetSizeChanged = updateWidgetGeometry();
315     if (!weakThis)
316         return;
317
318     // if the frame size got changed, or if view needs layout (possibly indicating
319     // content size is wrong) we have to do a layout to set the right widget size.
320     if (m_widget->isFrameView()) {
321         FrameView* frameView = toFrameView(m_widget.get());
322         // Check the frame's page to make sure that the frame isn't in the process of being destroyed.
323         if ((widgetSizeChanged || frameView->needsLayout()) && frameView->frame().page())
324             frameView->layout();
325     }
326 }
327
328 IntRect RenderWidget::windowClipRect() const
329 {
330     return intersection(view().frameView().contentsToWindow(m_clipRect), view().frameView().windowClipRect());
331 }
332
333 void RenderWidget::setSelectionState(SelectionState state)
334 {
335     // The selection state for our containing block hierarchy is updated by the base class call.
336     RenderReplaced::setSelectionState(state);
337
338     if (m_widget)
339         m_widget->setIsSelected(isSelected());
340 }
341
342 RenderWidget* RenderWidget::find(const Widget* widget)
343 {
344     return widgetRendererMap().get(widget);
345 }
346
347 bool RenderWidget::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
348 {
349     if (request.allowsChildFrameContent() && widget() && widget()->isFrameView() && toFrameView(widget())->renderView()) {
350         FrameView* childFrameView = toFrameView(widget());
351         RenderView* childRoot = childFrameView->renderView();
352
353         LayoutPoint adjustedLocation = accumulatedOffset + location();
354         LayoutPoint contentOffset = LayoutPoint(borderLeft() + paddingLeft(), borderTop() + paddingTop()) - childFrameView->scrollOffset();
355         HitTestLocation newHitTestLocation(locationInContainer, -adjustedLocation - contentOffset);
356         HitTestRequest newHitTestRequest(request.type() | HitTestRequest::ChildFrameHitTest);
357         HitTestResult childFrameResult(newHitTestLocation);
358
359         bool isInsideChildFrame = childRoot->hitTest(newHitTestRequest, newHitTestLocation, childFrameResult);
360
361         if (newHitTestLocation.isRectBasedTest())
362             result.append(childFrameResult);
363         else if (isInsideChildFrame)
364             result = childFrameResult;
365
366         if (isInsideChildFrame)
367             return true;
368     }
369
370     bool hadResult = result.innerNode();
371     bool inside = RenderReplaced::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, action);
372
373     // Check to see if we are really over the widget itself (and not just in the border/padding area).
374     if ((inside || result.isRectBasedTest()) && !hadResult && result.innerNode() == &frameOwnerElement())
375         result.setIsOverWidget(contentBoxRect().contains(result.localPoint()));
376     return inside;
377 }
378
379 bool RenderWidget::requiresLayer() const
380 {
381     return RenderReplaced::requiresLayer() || requiresAcceleratedCompositing();
382 }
383
384 bool RenderWidget::requiresAcceleratedCompositing() const
385 {
386     // If this is a renderer with a contentDocument and that document needs a layer, then we need a layer.
387     if (Document* contentDocument = frameOwnerElement().contentDocument()) {
388         if (RenderView* view = contentDocument->renderView())
389             return view->usesCompositing();
390     }
391
392     return false;
393 }
394
395 bool RenderWidget::needsPreferredWidthsRecalculation() const
396 {
397     if (RenderReplaced::needsPreferredWidthsRecalculation())
398         return true;
399     return embeddedContentBox();
400 }
401
402 RenderBox* RenderWidget::embeddedContentBox() const
403 {
404     if (!widget() || !widget()->isFrameView())
405         return 0;
406     return toFrameView(widget())->embeddedContentBox();
407 }
408
409 } // namespace WebCore