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 * Copyright (C) 2013 Google Inc. All rights reserved.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
25 #include "core/rendering/RenderWidget.h"
27 #include "core/accessibility/AXObjectCache.h"
28 #include "core/frame/Frame.h"
29 #include "core/rendering/CompositedLayerMapping.h"
30 #include "core/rendering/GraphicsContextAnnotator.h"
31 #include "core/rendering/HitTestResult.h"
32 #include "core/rendering/LayoutRectRecorder.h"
33 #include "core/rendering/RenderLayer.h"
34 #include "core/rendering/RenderView.h"
35 #include "wtf/HashMap.h"
39 typedef HashMap<RefPtr<Widget>, FrameView*> WidgetToParentMap;
40 static WidgetToParentMap& widgetNewParentMap()
42 DEFINE_STATIC_LOCAL(WidgetToParentMap, map, ());
46 static unsigned s_updateSuspendCount = 0;
48 RenderWidget::UpdateSuspendScope::UpdateSuspendScope()
50 ++s_updateSuspendCount;
53 RenderWidget::UpdateSuspendScope::~UpdateSuspendScope()
55 ASSERT(s_updateSuspendCount > 0);
56 if (s_updateSuspendCount == 1) {
57 WidgetToParentMap map;
58 widgetNewParentMap().swap(map);
59 WidgetToParentMap::iterator end = map.end();
60 for (WidgetToParentMap::iterator it = map.begin(); it != end; ++it) {
61 Widget* child = it->key.get();
62 ScrollView* currentParent = toScrollView(child->parent());
63 FrameView* newParent = it->value;
64 if (newParent != currentParent) {
66 currentParent->removeChild(child);
68 newParent->addChild(child);
72 --s_updateSuspendCount;
75 static void moveWidgetToParentSoon(Widget* child, FrameView* parent)
77 if (!s_updateSuspendCount) {
79 parent->addChild(child);
81 toScrollView(child->parent())->removeChild(child);
84 widgetNewParentMap().set(child, parent);
87 RenderWidget::RenderWidget(Element* element)
88 : RenderReplaced(element)
90 // Reference counting is used to prevent the widget from being
91 // destroyed while inside the Widget code, which might not be
92 // able to handle that.
96 frameView()->addWidget(this);
99 void RenderWidget::willBeDestroyed()
101 frameView()->removeWidget(this);
103 if (AXObjectCache* cache = document().existingAXObjectCache()) {
104 cache->childrenChanged(this->parent());
110 RenderReplaced::willBeDestroyed();
113 void RenderWidget::destroy()
120 RenderWidget::~RenderWidget()
122 ASSERT(m_refCount <= 0);
126 // Widgets are always placed on integer boundaries, so rounding the size is actually
127 // the desired behavior. This function is here because it's otherwise seldom what we
128 // want to do with a LayoutRect.
129 static inline IntRect roundedIntRect(const LayoutRect& rect)
131 return IntRect(roundedIntPoint(rect.location()), roundedIntSize(rect.size()));
134 bool RenderWidget::setWidgetGeometry(const LayoutRect& frame)
139 IntRect clipRect = roundedIntRect(enclosingLayer()->clipper().childrenClipRect());
140 IntRect newFrame = roundedIntRect(frame);
141 bool clipChanged = m_clipRect != clipRect;
142 bool frameRectChanged = m_widget->frameRect() != newFrame;
144 if (!frameRectChanged && !clipChanged)
147 m_clipRect = clipRect;
149 RefPtr<RenderWidget> protector(this);
150 RefPtr<Node> protectedNode(node());
151 m_widget->setFrameRect(newFrame);
153 if (clipChanged && !frameRectChanged)
154 m_widget->clipRectChanged();
156 if (hasLayer() && layer()->compositingState() == PaintsIntoOwnBacking)
157 layer()->compositedLayerMapping()->updateAfterWidgetResize();
159 bool boundsChanged = m_widget->frameRect().size() != newFrame.size();
160 return boundsChanged;
163 bool RenderWidget::updateWidgetGeometry()
165 LayoutRect contentBox = contentBoxRect();
166 LayoutRect absoluteContentBox(localToAbsoluteQuad(FloatQuad(contentBox)).boundingBox());
167 if (m_widget->isFrameView()) {
168 contentBox.setLocation(absoluteContentBox.location());
169 return setWidgetGeometry(contentBox);
172 return setWidgetGeometry(absoluteContentBox);
175 void RenderWidget::setWidget(PassRefPtr<Widget> widget)
177 if (widget == m_widget)
181 moveWidgetToParentSoon(m_widget.get(), 0);
186 // If we've already received a layout, apply the calculated space to the
187 // widget immediately, but we have to have really been fully constructed (with a non-null
191 updateWidgetGeometry();
193 if (style()->visibility() != VISIBLE)
200 moveWidgetToParentSoon(m_widget.get(), frameView());
203 if (AXObjectCache* cache = document().existingAXObjectCache())
204 cache->childrenChanged(this);
207 void RenderWidget::layout()
209 ASSERT(needsLayout());
211 LayoutRectRecorder recorder(*this);
215 void RenderWidget::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
217 RenderReplaced::styleDidChange(diff, oldStyle);
219 if (style()->visibility() != VISIBLE)
226 void RenderWidget::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
228 LayoutPoint adjustedPaintOffset = paintOffset + 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 IntPoint widgetLocation = m_widget->frameRect().location();
233 IntPoint paintLocation(roundToInt(adjustedPaintOffset.x() + borderLeft() + paddingLeft()),
234 roundToInt(adjustedPaintOffset.y() + borderTop() + paddingTop()));
235 IntRect paintRect = paintInfo.rect;
237 IntSize widgetPaintOffset = paintLocation - widgetLocation;
238 // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer,
239 // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing.
240 if (!widgetPaintOffset.isZero()) {
241 paintInfo.context->translate(widgetPaintOffset);
242 paintRect.move(-widgetPaintOffset);
244 m_widget->paint(paintInfo.context, paintRect);
246 if (!widgetPaintOffset.isZero())
247 paintInfo.context->translate(-widgetPaintOffset);
249 if (m_widget->isFrameView()) {
250 FrameView* frameView = toFrameView(m_widget.get());
251 bool runOverlapTests = !frameView->useSlowRepaintsIfNotOverlapped() || frameView->hasCompositedContent();
252 if (paintInfo.overlapTestRequests && runOverlapTests) {
253 ASSERT(!paintInfo.overlapTestRequests->contains(this));
254 paintInfo.overlapTestRequests->set(this, m_widget->frameRect());
259 void RenderWidget::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
261 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
263 if (!shouldPaint(paintInfo, paintOffset))
266 LayoutPoint adjustedPaintOffset = paintOffset + location();
268 if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
269 paintBoxDecorations(paintInfo, adjustedPaintOffset);
271 if (paintInfo.phase == PaintPhaseMask) {
272 paintMask(paintInfo, adjustedPaintOffset);
276 if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && hasOutline())
277 paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, size()));
279 if (paintInfo.phase != PaintPhaseForeground)
282 if (style()->hasBorderRadius()) {
283 LayoutRect borderRect = LayoutRect(adjustedPaintOffset, size());
285 if (borderRect.isEmpty())
288 // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
289 paintInfo.context->save();
290 RoundedRect roundedInnerRect = style()->getRoundedInnerBorderFor(borderRect,
291 paddingTop() + borderTop(), paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), paddingRight() + borderRight(), true, true);
292 clipRoundedInnerRect(paintInfo.context, borderRect, roundedInnerRect);
296 paintContents(paintInfo, paintOffset);
298 if (style()->hasBorderRadius())
299 paintInfo.context->restore();
301 // Paint a partially transparent wash over selected widgets.
302 if (isSelected() && !document().printing()) {
303 // FIXME: selectionRect() is in absolute, not painting coordinates.
304 paintInfo.context->fillRect(pixelSnappedIntRect(selectionRect()), selectionBackgroundColor());
308 layer()->scrollableArea()->paintResizer(paintInfo.context, roundedIntPoint(adjustedPaintOffset), paintInfo.rect);
311 void RenderWidget::setIsOverlapped(bool isOverlapped)
314 ASSERT(m_widget->isFrameView());
315 toFrameView(m_widget.get())->setIsOverlapped(isOverlapped);
318 void RenderWidget::deref()
320 if (--m_refCount <= 0)
324 void RenderWidget::updateWidgetPosition()
326 if (!m_widget || !node()) // Check the node in case destroy() has been called.
329 bool boundsChanged = updateWidgetGeometry();
331 // if the frame bounds got changed, or if view needs layout (possibly indicating
332 // content size is wrong) we have to do a layout to set the right widget size
333 if (m_widget && m_widget->isFrameView()) {
334 FrameView* frameView = toFrameView(m_widget.get());
335 // Check the frame's page to make sure that the frame isn't in the process of being destroyed.
336 if ((boundsChanged || frameView->needsLayout()) && frameView->frame().page())
341 void RenderWidget::widgetPositionsUpdated()
345 m_widget->widgetPositionsUpdated();
348 void RenderWidget::clearWidget()
353 bool RenderWidget::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
355 bool hadResult = result.innerNode();
356 bool inside = RenderReplaced::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, action);
358 // Check to see if we are really over the widget itself (and not just in the border/padding area).
359 if ((inside || result.isRectBasedTest()) && !hadResult && result.innerNode() == node())
360 result.setIsOverWidget(contentBoxRect().contains(result.localPoint()));
364 CursorDirective RenderWidget::getCursor(const LayoutPoint& point, Cursor& cursor) const
366 if (widget() && widget()->isPluginView()) {
367 // A plug-in is responsible for setting the cursor when the pointer is over it.
368 return DoNotSetCursor;
370 return RenderReplaced::getCursor(point, cursor);
373 } // namespace WebCore