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