Crash when encountering <object style="resize:both;">
[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 "AnimationController.h"
28 #include "Frame.h"
29 #include "GraphicsContext.h"
30 #include "HitTestResult.h"
31 #include "RenderCounter.h"
32 #include "RenderLayer.h"
33 #include "RenderView.h"
34 #include "RenderWidgetProtector.h"
35
36 #if USE(ACCELERATED_COMPOSITING)
37 #include "RenderLayerBacking.h"
38 #endif
39
40 using namespace std;
41
42 namespace WebCore {
43
44 static HashMap<const Widget*, RenderWidget*>& widgetRendererMap()
45 {
46     static HashMap<const Widget*, RenderWidget*>* staticWidgetRendererMap = new HashMap<const Widget*, RenderWidget*>;
47     return *staticWidgetRendererMap;
48 }
49
50 unsigned WidgetHierarchyUpdatesSuspensionScope::s_widgetHierarchyUpdateSuspendCount = 0;
51
52 WidgetHierarchyUpdatesSuspensionScope::WidgetToParentMap& WidgetHierarchyUpdatesSuspensionScope::widgetNewParentMap()
53 {
54     DEFINE_STATIC_LOCAL(WidgetToParentMap, map, ());
55     return map;
56 }
57
58 void WidgetHierarchyUpdatesSuspensionScope::moveWidgets()
59 {
60     WidgetToParentMap map = widgetNewParentMap();
61     widgetNewParentMap().clear();
62     WidgetToParentMap::iterator end = map.end();
63     for (WidgetToParentMap::iterator it = map.begin(); it != end; ++it) {
64         Widget* child = it->key.get();
65         ScrollView* currentParent = child->parent();
66         FrameView* newParent = it->value;
67         if (newParent != currentParent) {
68             if (currentParent)
69                 currentParent->removeChild(child);
70             if (newParent)
71                 newParent->addChild(child);
72         }
73     }
74 }
75
76 static void moveWidgetToParentSoon(Widget* child, FrameView* parent)
77 {
78     if (!WidgetHierarchyUpdatesSuspensionScope::isSuspended()) {
79         if (parent)
80             parent->addChild(child);
81         else
82             child->removeFromParent();
83         return;
84     }
85     WidgetHierarchyUpdatesSuspensionScope::scheduleWidgetToMove(child, parent);
86 }
87
88 RenderWidget::RenderWidget(Element* element)
89     : RenderReplaced(element)
90     , m_widget(0)
91     , m_frameView(element->document()->view())
92     // Reference counting is used to prevent the widget from being
93     // destroyed while inside the Widget code, which might not be
94     // able to handle that.
95     , m_refCount(1)
96 {
97     view()->addWidget(this);
98 }
99
100 void RenderWidget::willBeDestroyed()
101 {
102     if (RenderView* v = view())
103         v->removeWidget(this);
104     
105     if (AXObjectCache::accessibilityEnabled()) {
106         document()->axObjectCache()->childrenChanged(this->parent());
107         document()->axObjectCache()->remove(this);
108     }
109
110     setWidget(0);
111
112     RenderReplaced::willBeDestroyed();
113 }
114
115 void RenderWidget::destroy()
116 {
117     willBeDestroyed();
118
119     // Grab the arena from node()->document()->renderArena() before clearing the node pointer.
120     // Clear the node before deref-ing, as this may be deleted when deref is called.
121     RenderArena* arena = renderArena();
122     clearNode();
123     deref(arena);
124 }
125
126 RenderWidget::~RenderWidget()
127 {
128     ASSERT(m_refCount <= 0);
129     clearWidget();
130 }
131
132 // Widgets are always placed on integer boundaries, so rounding the size is actually
133 // the desired behavior. This function is here because it's otherwise seldom what we
134 // want to do with a LayoutRect.
135 static inline IntRect roundedIntRect(const LayoutRect& rect)
136 {
137     return IntRect(roundedIntPoint(rect.location()), roundedIntSize(rect.size()));
138 }
139
140 bool RenderWidget::setWidgetGeometry(const LayoutRect& frame)
141 {
142     if (!node())
143         return false;
144
145     IntRect clipRect = roundedIntRect(enclosingLayer()->childrenClipRect());
146     bool clipChanged = m_clipRect != clipRect;
147     bool boundsChanged = m_widget->frameRect() != frame;
148
149     if (!boundsChanged && !clipChanged)
150         return false;
151
152     m_clipRect = clipRect;
153
154     RenderWidgetProtector protector(this);
155     RefPtr<Node> protectedNode(node());
156     m_widget->setFrameRect(roundedIntRect(frame));
157     
158 #if USE(ACCELERATED_COMPOSITING)
159     if (hasLayer() && layer()->isComposited())
160         layer()->backing()->updateAfterWidgetResize();
161 #endif
162     
163     return boundsChanged;
164 }
165
166 bool RenderWidget::updateWidgetGeometry()
167 {
168     LayoutRect contentBox = contentBoxRect();
169     if (!m_widget->transformsAffectFrameRect())
170         return setWidgetGeometry(absoluteContentBox());
171
172     LayoutRect absoluteContentBox(localToAbsoluteQuad(FloatQuad(contentBox)).boundingBox());
173     if (m_widget->isFrameView()) {
174         contentBox.setLocation(absoluteContentBox.location());
175         return setWidgetGeometry(contentBox);
176     }
177
178     return setWidgetGeometry(absoluteContentBox);
179 }
180
181 void RenderWidget::setWidget(PassRefPtr<Widget> widget)
182 {
183     if (widget == m_widget)
184         return;
185
186     if (m_widget) {
187         moveWidgetToParentSoon(m_widget.get(), 0);
188         widgetRendererMap().remove(m_widget.get());
189         clearWidget();
190     }
191     m_widget = widget;
192     if (m_widget) {
193         widgetRendererMap().add(m_widget.get(), this);
194         // If we've already received a layout, apply the calculated space to the
195         // widget immediately, but we have to have really been fully constructed (with a non-null
196         // style pointer).
197         if (style()) {
198             if (!needsLayout())
199                 updateWidgetGeometry();
200
201             if (style()->visibility() != VISIBLE)
202                 m_widget->hide();
203             else {
204                 m_widget->show();
205                 repaint();
206             }
207         }
208         moveWidgetToParentSoon(m_widget.get(), m_frameView);
209     }
210 }
211
212 void RenderWidget::layout()
213 {
214     StackStats::LayoutCheckPoint layoutCheckPoint;
215     ASSERT(needsLayout());
216
217     setNeedsLayout(false);
218 }
219
220 void RenderWidget::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
221 {
222     RenderReplaced::styleDidChange(diff, oldStyle);
223     if (m_widget) {
224         if (style()->visibility() != VISIBLE)
225             m_widget->hide();
226         else
227             m_widget->show();
228     }
229 }
230
231 void RenderWidget::notifyWidget(WidgetNotification notification)
232 {
233     if (m_widget)
234         m_widget->notifyWidget(notification);
235 }
236
237 void RenderWidget::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
238 {
239     if (!shouldPaint(paintInfo, paintOffset))
240         return;
241
242     LayoutPoint adjustedPaintOffset = paintOffset + location();
243
244     if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
245         paintBoxDecorations(paintInfo, adjustedPaintOffset);
246
247     if (paintInfo.phase == PaintPhaseMask) {
248         paintMask(paintInfo, adjustedPaintOffset);
249         return;
250     }
251
252     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && hasOutline())
253         paintOutline(paintInfo.context, LayoutRect(adjustedPaintOffset, size()));
254
255     if (!m_frameView || paintInfo.phase != PaintPhaseForeground)
256         return;
257
258 #if PLATFORM(MAC)
259     if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
260         paintCustomHighlight(paintOffset, style()->highlight(), true);
261 #endif
262
263     if (style()->hasBorderRadius()) {
264         LayoutRect borderRect = LayoutRect(adjustedPaintOffset, size());
265
266         if (borderRect.isEmpty())
267             return;
268
269         // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
270         paintInfo.context->save();
271         RoundedRect roundedInnerRect = style()->getRoundedInnerBorderFor(borderRect,
272             paddingTop() + borderTop(), paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), paddingRight() + borderRight(), true, true);
273         clipRoundedInnerRect(paintInfo.context, borderRect, roundedInnerRect);
274     }
275
276     if (m_widget) {
277         // Tell the widget to paint now.  This is the only time the widget is allowed
278         // to paint itself.  That way it will composite properly with z-indexed layers.
279         IntPoint widgetLocation = m_widget->frameRect().location();
280         IntPoint paintLocation(roundToInt(adjustedPaintOffset.x() + borderLeft() + paddingLeft()),
281             roundToInt(adjustedPaintOffset.y() + borderTop() + paddingTop()));
282         IntRect paintRect = paintInfo.rect;
283
284         IntSize widgetPaintOffset = paintLocation - widgetLocation;
285         // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer,
286         // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing.
287         if (!widgetPaintOffset.isZero()) {
288             paintInfo.context->translate(widgetPaintOffset);
289             paintRect.move(-widgetPaintOffset);
290         }
291         m_widget->paint(paintInfo.context, paintRect);
292
293         if (!widgetPaintOffset.isZero())
294             paintInfo.context->translate(-widgetPaintOffset);
295
296         if (m_widget->isFrameView()) {
297             FrameView* frameView = static_cast<FrameView*>(m_widget.get());
298             bool runOverlapTests = !frameView->useSlowRepaintsIfNotOverlapped() || frameView->hasCompositedContentIncludingDescendants();
299             if (paintInfo.overlapTestRequests && runOverlapTests) {
300                 ASSERT(!paintInfo.overlapTestRequests->contains(this));
301                 paintInfo.overlapTestRequests->set(this, m_widget->frameRect());
302             }
303          }
304     }
305
306     if (style()->hasBorderRadius())
307         paintInfo.context->restore();
308
309     // Paint a partially transparent wash over selected widgets.
310     if (isSelected() && !document()->printing()) {
311         // FIXME: selectionRect() is in absolute, not painting coordinates.
312         paintInfo.context->fillRect(pixelSnappedIntRect(selectionRect()), selectionBackgroundColor(), style()->colorSpace());
313     }
314
315     if (hasLayer() && layer()->canResize())
316         layer()->paintResizer(paintInfo.context, roundedIntPoint(adjustedPaintOffset), paintInfo.rect);
317 }
318
319 void RenderWidget::setOverlapTestResult(bool isOverlapped)
320 {
321     ASSERT(m_widget);
322     ASSERT(m_widget->isFrameView());
323     static_cast<FrameView*>(m_widget.get())->setIsOverlapped(isOverlapped);
324 }
325
326 void RenderWidget::deref(RenderArena *arena)
327 {
328     if (--m_refCount <= 0)
329         arenaDelete(arena, this);
330 }
331
332 void RenderWidget::updateWidgetPosition()
333 {
334     if (!m_widget || !node()) // Check the node in case destroy() has been called.
335         return;
336
337     bool boundsChanged = updateWidgetGeometry();
338     
339     // if the frame bounds got changed, or if view needs layout (possibly indicating
340     // content size is wrong) we have to do a layout to set the right widget size
341     if (m_widget && m_widget->isFrameView()) {
342         FrameView* frameView = static_cast<FrameView*>(m_widget.get());
343         // Check the frame's page to make sure that the frame isn't in the process of being destroyed.
344         if ((boundsChanged || frameView->needsLayout()) && frameView->frame()->page())
345             frameView->layout();
346     }
347 }
348
349 void RenderWidget::widgetPositionsUpdated()
350 {
351     if (!m_widget)
352         return;
353     m_widget->widgetPositionsUpdated();
354 }
355
356 IntRect RenderWidget::windowClipRect() const
357 {
358     if (!m_frameView)
359         return IntRect();
360
361     return intersection(m_frameView->contentsToWindow(m_clipRect), m_frameView->windowClipRect());
362 }
363
364 void RenderWidget::setSelectionState(SelectionState state)
365 {
366     // The selection state for our containing block hierarchy is updated by the base class call.
367     RenderReplaced::setSelectionState(state);
368
369     if (m_widget)
370         m_widget->setIsSelected(isSelected());
371 }
372
373 void RenderWidget::clearWidget()
374 {
375     m_widget = 0;
376 }
377
378 RenderWidget* RenderWidget::find(const Widget* widget)
379 {
380     return widgetRendererMap().get(widget);
381 }
382
383 bool RenderWidget::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
384 {
385     bool hadResult = result.innerNode();
386     bool inside = RenderReplaced::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, action);
387
388     // Check to see if we are really over the widget itself (and not just in the border/padding area).
389     if ((inside || result.isRectBasedTest()) && !hadResult && result.innerNode() == node())
390         result.setIsOverWidget(contentBoxRect().contains(result.localPoint()));
391     return inside;
392 }
393
394 CursorDirective RenderWidget::getCursor(const LayoutPoint& point, Cursor& cursor) const
395 {
396     if (widget() && widget()->isPluginViewBase()) {
397         // A plug-in is responsible for setting the cursor when the pointer is over it.
398         return DoNotSetCursor;
399     }
400     return RenderReplaced::getCursor(point, cursor);
401 }
402
403 } // namespace WebCore