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