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 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 "AnimationController.h"
27 #include "AXObjectCache.h"
28 #include "GraphicsContext.h"
29 #include "HitTestResult.h"
30 #include "RenderView.h"
31
32 using namespace std;
33
34 namespace WebCore {
35
widgetRendererMap()36 static HashMap<const Widget*, RenderWidget*>& widgetRendererMap()
37 {
38 static HashMap<const Widget*, RenderWidget*>* staticWidgetRendererMap = new HashMap<const Widget*, RenderWidget*>;
39 return *staticWidgetRendererMap;
40 }
41
RenderWidget(Node * node)42 RenderWidget::RenderWidget(Node* node)
43 : RenderReplaced(node)
44 , m_widget(0)
45 , m_frameView(node->document()->view())
46 , m_refCount(0)
47 {
48 view()->addWidget(this);
49
50 // Reference counting is used to prevent the widget from being
51 // destroyed while inside the Widget code, which might not be
52 // able to handle that.
53 ref();
54 }
55
destroy()56 void RenderWidget::destroy()
57 {
58 // We can't call the base class's destroy because we don't
59 // want to unconditionally delete ourselves (we're ref-counted).
60 // So the code below includes copied and pasted contents of
61 // both RenderBox::destroy() and RenderObject::destroy().
62 // Fix originally made for <rdar://problem/4228818>.
63 animation()->cancelAnimations(this);
64
65 if (RenderView* v = view())
66 v->removeWidget(this);
67
68 if (AXObjectCache::accessibilityEnabled()) {
69 document()->axObjectCache()->childrenChanged(this->parent());
70 document()->axObjectCache()->remove(this);
71 }
72 remove();
73
74 if (m_widget) {
75 if (m_frameView)
76 m_frameView->removeChild(m_widget.get());
77 widgetRendererMap().remove(m_widget.get());
78 }
79
80 // removes from override size map
81 if (hasOverrideSize())
82 setOverrideSize(-1);
83
84 if (style() && (style()->height().isPercent() || style()->minHeight().isPercent() || style()->maxHeight().isPercent()))
85 RenderBlock::removePercentHeightDescendant(this);
86
87 if (hasLayer()) {
88 layer()->clearClipRects();
89 setHasLayer(false);
90 destroyLayer();
91 }
92
93 // Grab the arena from node()->document()->renderArena() before clearing the node pointer.
94 // Clear the node before deref-ing, as this may be deleted when deref is called.
95 RenderArena* arena = renderArena();
96 setNode(0);
97 deref(arena);
98 }
99
~RenderWidget()100 RenderWidget::~RenderWidget()
101 {
102 ASSERT(m_refCount <= 0);
103 clearWidget();
104 }
105
setWidgetGeometry(const IntRect & frame)106 void RenderWidget::setWidgetGeometry(const IntRect& frame)
107 {
108 if (node() && m_widget->frameRect() != frame) {
109 RenderArena* arena = ref();
110 RefPtr<Node> protectedElement(node());
111 m_widget->setFrameRect(frame);
112 deref(arena);
113 }
114 }
115
setWidget(PassRefPtr<Widget> widget)116 void RenderWidget::setWidget(PassRefPtr<Widget> widget)
117 {
118 if (widget != m_widget) {
119 if (m_widget) {
120 m_widget->removeFromParent();
121 widgetRendererMap().remove(m_widget.get());
122 clearWidget();
123 }
124 m_widget = widget;
125 if (m_widget) {
126 widgetRendererMap().add(m_widget.get(), this);
127 // if we've already received a layout, apply the calculated space to the
128 // widget immediately, but we have to have really been full constructed (with a non-null
129 // style pointer).
130 if (style()) {
131 if (!needsLayout())
132 setWidgetGeometry(absoluteContentBox());
133 if (style()->visibility() != VISIBLE)
134 m_widget->hide();
135 else
136 m_widget->show();
137 }
138 m_frameView->addChild(m_widget.get());
139 }
140 }
141 }
142
layout()143 void RenderWidget::layout()
144 {
145 ASSERT(needsLayout());
146
147 setNeedsLayout(false);
148 }
149
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)150 void RenderWidget::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
151 {
152 RenderReplaced::styleDidChange(diff, oldStyle);
153 if (m_widget) {
154 if (style()->visibility() != VISIBLE)
155 m_widget->hide();
156 else
157 m_widget->show();
158 }
159 }
160
paint(PaintInfo & paintInfo,int tx,int ty)161 void RenderWidget::paint(PaintInfo& paintInfo, int tx, int ty)
162 {
163 if (!shouldPaint(paintInfo, tx, ty))
164 return;
165
166 tx += x();
167 ty += y();
168
169 if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
170 paintBoxDecorations(paintInfo, tx, ty);
171
172 if (paintInfo.phase == PaintPhaseMask) {
173 paintMask(paintInfo, tx, ty);
174 return;
175 }
176
177 if (!m_frameView || paintInfo.phase != PaintPhaseForeground || style()->visibility() != VISIBLE)
178 return;
179
180 #if PLATFORM(MAC)
181 if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
182 paintCustomHighlight(tx - x(), ty - y(), style()->highlight(), true);
183 #endif
184
185 if (style()->hasBorderRadius()) {
186 // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
187 paintInfo.context->save();
188
189 IntSize topLeft, topRight, bottomLeft, bottomRight;
190 IntRect borderRect = IntRect(tx, ty, width(), height());
191 style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight);
192
193 paintInfo.context->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight);
194 }
195
196 if (m_widget) {
197 // Move the widget if necessary. We normally move and resize widgets during layout, but sometimes
198 // widgets can move without layout occurring (most notably when you scroll a document that
199 // contains fixed positioned elements).
200 m_widget->move(tx + borderLeft() + paddingLeft(), ty + borderTop() + paddingTop());
201
202 // Tell the widget to paint now. This is the only time the widget is allowed
203 // to paint itself. That way it will composite properly with z-indexed layers.
204 m_widget->paint(paintInfo.context, paintInfo.rect);
205
206 if (m_widget->isFrameView() && paintInfo.overlapTestRequests && !static_cast<FrameView*>(m_widget.get())->useSlowRepaints()) {
207 ASSERT(!paintInfo.overlapTestRequests->contains(this));
208 paintInfo.overlapTestRequests->set(this, m_widget->frameRect());
209 }
210 }
211
212 if (style()->hasBorderRadius())
213 paintInfo.context->restore();
214
215 // Paint a partially transparent wash over selected widgets.
216 if (isSelected() && !document()->printing()) {
217 // FIXME: selectionRect() is in absolute, not painting coordinates.
218 paintInfo.context->fillRect(selectionRect(), selectionBackgroundColor());
219 }
220 }
221
setOverlapTestResult(bool isOverlapped)222 void RenderWidget::setOverlapTestResult(bool isOverlapped)
223 {
224 ASSERT(m_widget);
225 ASSERT(m_widget->isFrameView());
226 static_cast<FrameView*>(m_widget.get())->setIsOverlapped(isOverlapped);
227 }
228
deref(RenderArena * arena)229 void RenderWidget::deref(RenderArena *arena)
230 {
231 if (--m_refCount <= 0)
232 arenaDelete(arena, this);
233 }
234
updateWidgetPosition()235 void RenderWidget::updateWidgetPosition()
236 {
237 if (!m_widget)
238 return;
239
240 // FIXME: This doesn't work correctly with transforms.
241 FloatPoint absPos = localToAbsolute();
242 absPos.move(borderLeft() + paddingLeft(), borderTop() + paddingTop());
243
244 int w = width() - borderLeft() - borderRight() - paddingLeft() - paddingRight();
245 int h = height() - borderTop() - borderBottom() - paddingTop() - paddingBottom();
246
247 IntRect newBounds(absPos.x(), absPos.y(), w, h);
248 IntRect oldBounds(m_widget->frameRect());
249 bool boundsChanged = newBounds != oldBounds;
250 if (boundsChanged) {
251 RenderArena* arena = ref();
252 node()->ref();
253 m_widget->setFrameRect(newBounds);
254 node()->deref();
255 deref(arena);
256 }
257
258 #ifndef FLATTEN_IFRAME
259 // if the frame bounds got changed, or if view needs layout (possibly indicating
260 // content size is wrong) we have to do a layout to set the right widget size
261 if (m_widget->isFrameView()) {
262 FrameView* frameView = static_cast<FrameView*>(m_widget.get());
263 if (boundsChanged || frameView->needsLayout())
264 frameView->layout();
265 }
266 #endif
267 }
268
setSelectionState(SelectionState state)269 void RenderWidget::setSelectionState(SelectionState state)
270 {
271 if (selectionState() != state) {
272 RenderReplaced::setSelectionState(state);
273 if (m_widget)
274 m_widget->setIsSelected(isSelected());
275 }
276 }
277
clearWidget()278 void RenderWidget::clearWidget()
279 {
280 m_widget = 0;
281 }
282
find(const Widget * widget)283 RenderWidget* RenderWidget::find(const Widget* widget)
284 {
285 return widgetRendererMap().get(widget);
286 }
287
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,int x,int y,int tx,int ty,HitTestAction action)288 bool RenderWidget::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction action)
289 {
290 bool hadResult = result.innerNode();
291 bool inside = RenderReplaced::nodeAtPoint(request, result, x, y, tx, ty, action);
292
293 // Check to see if we are really over the widget itself (and not just in the border/padding area).
294 if (inside && !hadResult && result.innerNode() == node())
295 result.setIsOverWidget(contentBoxRect().contains(result.localPoint()));
296 return inside;
297 }
298
299 } // namespace WebCore
300