1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2007 David Smith (catfish.man@gmail.com)
5 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
6 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 #include "config.h"
25 #include "core/rendering/FloatingObjects.h"
26
27 #include "core/rendering/RenderBlockFlow.h"
28 #include "core/rendering/RenderBox.h"
29 #include "core/rendering/RenderView.h"
30
31 using namespace std;
32 using namespace WTF;
33
34 namespace WebCore {
35
36 struct SameSizeAsFloatingObject {
37 void* pointers[2];
38 LayoutRect rect;
39 int paginationStrut;
40 uint32_t bitfields : 8;
41 };
42
43 COMPILE_ASSERT(sizeof(FloatingObject) == sizeof(SameSizeAsFloatingObject), FloatingObject_should_stay_small);
44
FloatingObject(RenderBox * renderer)45 FloatingObject::FloatingObject(RenderBox* renderer)
46 : m_renderer(renderer)
47 , m_originatingLine(0)
48 , m_paginationStrut(0)
49 , m_shouldPaint(true)
50 , m_isDescendant(false)
51 , m_isPlaced(false)
52 #ifndef NDEBUG
53 , m_isInPlacedTree(false)
54 #endif
55 {
56 EFloat type = renderer->style()->floating();
57 ASSERT(type != NoFloat);
58 if (type == LeftFloat)
59 m_type = FloatLeft;
60 else if (type == RightFloat)
61 m_type = FloatRight;
62 }
63
FloatingObject(RenderBox * renderer,Type type,const LayoutRect & frameRect,bool shouldPaint,bool isDescendant)64 FloatingObject::FloatingObject(RenderBox* renderer, Type type, const LayoutRect& frameRect, bool shouldPaint, bool isDescendant)
65 : m_renderer(renderer)
66 , m_originatingLine(0)
67 , m_frameRect(frameRect)
68 , m_paginationStrut(0)
69 , m_type(type)
70 , m_shouldPaint(shouldPaint)
71 , m_isDescendant(isDescendant)
72 , m_isPlaced(true)
73 #ifndef NDEBUG
74 , m_isInPlacedTree(false)
75 #endif
76 {
77 }
78
create(RenderBox * renderer)79 PassOwnPtr<FloatingObject> FloatingObject::create(RenderBox* renderer)
80 {
81 OwnPtr<FloatingObject> newObj = adoptPtr(new FloatingObject(renderer));
82 newObj->setShouldPaint(!renderer->hasSelfPaintingLayer()); // If a layer exists, the float will paint itself. Otherwise someone else will.
83 newObj->setIsDescendant(true);
84
85 return newObj.release();
86 }
87
copyToNewContainer(LayoutSize offset,bool shouldPaint,bool isDescendant) const88 PassOwnPtr<FloatingObject> FloatingObject::copyToNewContainer(LayoutSize offset, bool shouldPaint, bool isDescendant) const
89 {
90 return adoptPtr(new FloatingObject(renderer(), type(), LayoutRect(frameRect().location() - offset, frameRect().size()), shouldPaint, isDescendant));
91 }
92
unsafeClone() const93 PassOwnPtr<FloatingObject> FloatingObject::unsafeClone() const
94 {
95 OwnPtr<FloatingObject> cloneObject = adoptPtr(new FloatingObject(renderer(), type(), m_frameRect, m_shouldPaint, m_isDescendant));
96 cloneObject->m_originatingLine = m_originatingLine;
97 cloneObject->m_paginationStrut = m_paginationStrut;
98 cloneObject->m_isPlaced = m_isPlaced;
99 return cloneObject.release();
100 }
101
102 template <FloatingObject::Type FloatTypeValue>
103 class ComputeFloatOffsetAdapter {
104 public:
105 typedef FloatingObjectInterval IntervalType;
106
ComputeFloatOffsetAdapter(const RenderBlockFlow * renderer,int lineTop,int lineBottom,LayoutUnit offset)107 ComputeFloatOffsetAdapter(const RenderBlockFlow* renderer, int lineTop, int lineBottom, LayoutUnit offset)
108 : m_renderer(renderer)
109 , m_lineTop(lineTop)
110 , m_lineBottom(lineBottom)
111 , m_offset(offset)
112 , m_outermostFloat(0)
113 {
114 }
115
lowValue() const116 int lowValue() const { return m_lineTop; }
highValue() const117 int highValue() const { return m_lineBottom; }
118 void collectIfNeeded(const IntervalType&);
119
offset() const120 LayoutUnit offset() const { return m_offset; }
121 LayoutUnit shapeOffset() const;
122 LayoutUnit heightRemaining() const;
123
124 private:
125 bool updateOffsetIfNeeded(const FloatingObject*);
126
127 const RenderBlockFlow* m_renderer;
128 int m_lineTop;
129 int m_lineBottom;
130 LayoutUnit m_offset;
131 const FloatingObject* m_outermostFloat;
132 };
133
134
~FloatingObjects()135 FloatingObjects::~FloatingObjects()
136 {
137 // FIXME: m_set should use OwnPtr instead.
138 deleteAllValues(m_set);
139 }
clearLineBoxTreePointers()140 void FloatingObjects::clearLineBoxTreePointers()
141 {
142 // Clear references to originating lines, since the lines are being deleted
143 FloatingObjectSetIterator end = m_set.end();
144 for (FloatingObjectSetIterator it = m_set.begin(); it != end; ++it) {
145 ASSERT(!((*it)->originatingLine()) || (*it)->originatingLine()->renderer() == m_renderer);
146 (*it)->setOriginatingLine(0);
147 }
148 }
149
150 template<>
updateOffsetIfNeeded(const FloatingObject * floatingObject)151 inline bool ComputeFloatOffsetAdapter<FloatingObject::FloatLeft>::updateOffsetIfNeeded(const FloatingObject* floatingObject)
152 {
153 LayoutUnit logicalRight = m_renderer->logicalRightForFloat(floatingObject);
154 if (logicalRight > m_offset) {
155 m_offset = logicalRight;
156 return true;
157 }
158 return false;
159 }
160
FloatingObjects(const RenderBlockFlow * renderer,bool horizontalWritingMode)161 FloatingObjects::FloatingObjects(const RenderBlockFlow* renderer, bool horizontalWritingMode)
162 : m_placedFloatsTree(UninitializedTree)
163 , m_leftObjectsCount(0)
164 , m_rightObjectsCount(0)
165 , m_horizontalWritingMode(horizontalWritingMode)
166 , m_renderer(renderer)
167 , m_cachedHorizontalWritingMode(false)
168 {
169 }
170
clear()171 void FloatingObjects::clear()
172 {
173 deleteAllValues(m_set);
174 m_set.clear();
175 m_placedFloatsTree.clear();
176 m_leftObjectsCount = 0;
177 m_rightObjectsCount = 0;
178 markLowestFloatLogicalBottomCacheAsDirty();
179 }
180
lowestFloatLogicalBottom(FloatingObject::Type floatType)181 LayoutUnit FloatingObjects::lowestFloatLogicalBottom(FloatingObject::Type floatType)
182 {
183 bool isInHorizontalWritingMode = m_horizontalWritingMode;
184 if (floatType != FloatingObject::FloatLeftRight) {
185 if (hasLowestFloatLogicalBottomCached(isInHorizontalWritingMode, floatType))
186 return getCachedlowestFloatLogicalBottom(floatType);
187 } else {
188 if (hasLowestFloatLogicalBottomCached(isInHorizontalWritingMode, FloatingObject::FloatLeft) && hasLowestFloatLogicalBottomCached(isInHorizontalWritingMode, FloatingObject::FloatRight)) {
189 return max(getCachedlowestFloatLogicalBottom(FloatingObject::FloatLeft),
190 getCachedlowestFloatLogicalBottom(FloatingObject::FloatRight));
191 }
192 }
193
194 LayoutUnit lowestFloatBottom = 0;
195 const FloatingObjectSet& floatingObjectSet = set();
196 FloatingObjectSetIterator end = floatingObjectSet.end();
197 if (floatType == FloatingObject::FloatLeftRight) {
198 LayoutUnit lowestFloatBottomLeft = 0;
199 LayoutUnit lowestFloatBottomRight = 0;
200 for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) {
201 FloatingObject* floatingObject = *it;
202 if (floatingObject->isPlaced()) {
203 FloatingObject::Type curType = floatingObject->type();
204 LayoutUnit curFloatLogicalBottom = m_renderer->logicalBottomForFloat(floatingObject);
205 if (curType & FloatingObject::FloatLeft)
206 lowestFloatBottomLeft = max(lowestFloatBottomLeft, curFloatLogicalBottom);
207 if (curType & FloatingObject::FloatRight)
208 lowestFloatBottomRight = max(lowestFloatBottomRight, curFloatLogicalBottom);
209 }
210 }
211 lowestFloatBottom = max(lowestFloatBottomLeft, lowestFloatBottomRight);
212 setCachedLowestFloatLogicalBottom(isInHorizontalWritingMode, FloatingObject::FloatLeft, lowestFloatBottomLeft);
213 setCachedLowestFloatLogicalBottom(isInHorizontalWritingMode, FloatingObject::FloatRight, lowestFloatBottomRight);
214 } else {
215 for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) {
216 FloatingObject* floatingObject = *it;
217 if (floatingObject->isPlaced() && floatingObject->type() == floatType)
218 lowestFloatBottom = max(lowestFloatBottom, m_renderer->logicalBottomForFloat(floatingObject));
219 }
220 setCachedLowestFloatLogicalBottom(isInHorizontalWritingMode, floatType, lowestFloatBottom);
221 }
222
223 return lowestFloatBottom;
224 }
225
hasLowestFloatLogicalBottomCached(bool isHorizontal,FloatingObject::Type type) const226 bool FloatingObjects::hasLowestFloatLogicalBottomCached(bool isHorizontal, FloatingObject::Type type) const
227 {
228 int floatIndex = static_cast<int>(type) - 1;
229 ASSERT(floatIndex < static_cast<int>(sizeof(m_lowestFloatBottomCache) / sizeof(FloatBottomCachedValue)));
230 ASSERT(floatIndex >= 0);
231 return (m_cachedHorizontalWritingMode == isHorizontal && !m_lowestFloatBottomCache[floatIndex].dirty);
232 }
233
getCachedlowestFloatLogicalBottom(FloatingObject::Type type) const234 LayoutUnit FloatingObjects::getCachedlowestFloatLogicalBottom(FloatingObject::Type type) const
235 {
236 int floatIndex = static_cast<int>(type) - 1;
237 ASSERT(floatIndex < static_cast<int>(sizeof(m_lowestFloatBottomCache) / sizeof(FloatBottomCachedValue)));
238 ASSERT(floatIndex >= 0);
239 return m_lowestFloatBottomCache[floatIndex].value;
240 }
241
setCachedLowestFloatLogicalBottom(bool isHorizontal,FloatingObject::Type type,LayoutUnit value)242 void FloatingObjects::setCachedLowestFloatLogicalBottom(bool isHorizontal, FloatingObject::Type type, LayoutUnit value)
243 {
244 int floatIndex = static_cast<int>(type) - 1;
245 ASSERT(floatIndex < static_cast<int>(sizeof(m_lowestFloatBottomCache) / sizeof(FloatBottomCachedValue)));
246 ASSERT(floatIndex >= 0);
247 m_cachedHorizontalWritingMode = isHorizontal;
248 m_lowestFloatBottomCache[floatIndex].value = value;
249 m_lowestFloatBottomCache[floatIndex].dirty = false;
250 }
251
markLowestFloatLogicalBottomCacheAsDirty()252 void FloatingObjects::markLowestFloatLogicalBottomCacheAsDirty()
253 {
254 for (size_t i = 0; i < sizeof(m_lowestFloatBottomCache) / sizeof(FloatBottomCachedValue); ++i)
255 m_lowestFloatBottomCache[i].dirty = true;
256 }
257
moveAllToFloatInfoMap(RendererToFloatInfoMap & map)258 void FloatingObjects::moveAllToFloatInfoMap(RendererToFloatInfoMap& map)
259 {
260 FloatingObjectSetIterator end = m_set.end();
261 for (FloatingObjectSetIterator it = m_set.begin(); it != end; ++it)
262 map.add((*it)->renderer(), *it);
263
264 // clear set before clearing this because we don't want to delete all of
265 // the objects we have just transferred.
266 m_set.clear();
267 clear();
268 }
269
increaseObjectsCount(FloatingObject::Type type)270 inline void FloatingObjects::increaseObjectsCount(FloatingObject::Type type)
271 {
272 if (type == FloatingObject::FloatLeft)
273 m_leftObjectsCount++;
274 else
275 m_rightObjectsCount++;
276 }
277
decreaseObjectsCount(FloatingObject::Type type)278 inline void FloatingObjects::decreaseObjectsCount(FloatingObject::Type type)
279 {
280 if (type == FloatingObject::FloatLeft)
281 m_leftObjectsCount--;
282 else
283 m_rightObjectsCount--;
284 }
285
intervalForFloatingObject(FloatingObject * floatingObject)286 inline FloatingObjectInterval FloatingObjects::intervalForFloatingObject(FloatingObject* floatingObject)
287 {
288 if (m_horizontalWritingMode)
289 return FloatingObjectInterval(floatingObject->frameRect().pixelSnappedY(), floatingObject->frameRect().pixelSnappedMaxY(), floatingObject);
290 return FloatingObjectInterval(floatingObject->frameRect().pixelSnappedX(), floatingObject->frameRect().pixelSnappedMaxX(), floatingObject);
291 }
292
addPlacedObject(FloatingObject * floatingObject)293 void FloatingObjects::addPlacedObject(FloatingObject* floatingObject)
294 {
295 ASSERT(!floatingObject->isInPlacedTree());
296
297 floatingObject->setIsPlaced(true);
298 if (m_placedFloatsTree.isInitialized())
299 m_placedFloatsTree.add(intervalForFloatingObject(floatingObject));
300
301 #ifndef NDEBUG
302 floatingObject->setIsInPlacedTree(true);
303 #endif
304 markLowestFloatLogicalBottomCacheAsDirty();
305 }
306
removePlacedObject(FloatingObject * floatingObject)307 void FloatingObjects::removePlacedObject(FloatingObject* floatingObject)
308 {
309 ASSERT(floatingObject->isPlaced() && floatingObject->isInPlacedTree());
310
311 if (m_placedFloatsTree.isInitialized()) {
312 bool removed = m_placedFloatsTree.remove(intervalForFloatingObject(floatingObject));
313 ASSERT_UNUSED(removed, removed);
314 }
315
316 floatingObject->setIsPlaced(false);
317 #ifndef NDEBUG
318 floatingObject->setIsInPlacedTree(false);
319 #endif
320 markLowestFloatLogicalBottomCacheAsDirty();
321 }
322
add(PassOwnPtr<FloatingObject> floatingObject)323 FloatingObject* FloatingObjects::add(PassOwnPtr<FloatingObject> floatingObject)
324 {
325 FloatingObject* newObject = floatingObject.leakPtr();
326 increaseObjectsCount(newObject->type());
327 m_set.add(newObject);
328 if (newObject->isPlaced())
329 addPlacedObject(newObject);
330 markLowestFloatLogicalBottomCacheAsDirty();
331 return newObject;
332 }
333
remove(FloatingObject * floatingObject)334 void FloatingObjects::remove(FloatingObject* floatingObject)
335 {
336 decreaseObjectsCount(floatingObject->type());
337 m_set.remove(floatingObject);
338 ASSERT(floatingObject->isPlaced() || !floatingObject->isInPlacedTree());
339 if (floatingObject->isPlaced())
340 removePlacedObject(floatingObject);
341 markLowestFloatLogicalBottomCacheAsDirty();
342 ASSERT(!floatingObject->originatingLine());
343 delete floatingObject;
344 }
345
computePlacedFloatsTree()346 void FloatingObjects::computePlacedFloatsTree()
347 {
348 ASSERT(!m_placedFloatsTree.isInitialized());
349 if (m_set.isEmpty())
350 return;
351 m_placedFloatsTree.initIfNeeded(m_renderer->view()->intervalArena());
352 FloatingObjectSetIterator it = m_set.begin();
353 FloatingObjectSetIterator end = m_set.end();
354 for (; it != end; ++it) {
355 FloatingObject* floatingObject = *it;
356 if (floatingObject->isPlaced())
357 m_placedFloatsTree.add(intervalForFloatingObject(floatingObject));
358 }
359 }
360
shapeInfoForFloat(const FloatingObject * floatingObject,const RenderBlockFlow * containingBlock,LayoutUnit lineTop,LayoutUnit lineBottom)361 static inline ShapeOutsideInfo* shapeInfoForFloat(const FloatingObject* floatingObject, const RenderBlockFlow* containingBlock, LayoutUnit lineTop, LayoutUnit lineBottom)
362 {
363 if (floatingObject) {
364 if (ShapeOutsideInfo* shapeOutside = floatingObject->renderer()->shapeOutsideInfo()) {
365 shapeOutside->updateDeltasForContainingBlockLine(containingBlock, floatingObject, lineTop, lineBottom - lineTop);
366 return shapeOutside;
367 }
368 }
369
370 return 0;
371 }
372
373 template<>
shapeOffset() const374 inline LayoutUnit ComputeFloatOffsetAdapter<FloatingObject::FloatLeft>::shapeOffset() const
375 {
376 if (ShapeOutsideInfo* shapeOutside = shapeInfoForFloat(m_outermostFloat, m_renderer, m_lineTop, m_lineBottom))
377 return m_offset + shapeOutside->rightMarginBoxDelta();
378
379 return m_offset;
380 }
381
382 template<>
shapeOffset() const383 inline LayoutUnit ComputeFloatOffsetAdapter<FloatingObject::FloatRight>::shapeOffset() const
384 {
385 if (ShapeOutsideInfo* shapeOutside = shapeInfoForFloat(m_outermostFloat, m_renderer, m_lineTop, m_lineBottom))
386 return m_offset + shapeOutside->leftMarginBoxDelta();
387
388 return m_offset;
389 }
390
logicalLeftOffsetForPositioningFloat(LayoutUnit fixedOffset,LayoutUnit logicalTop,LayoutUnit * heightRemaining)391 LayoutUnit FloatingObjects::logicalLeftOffsetForPositioningFloat(LayoutUnit fixedOffset, LayoutUnit logicalTop, LayoutUnit *heightRemaining)
392 {
393 int logicalTopAsInt = roundToInt(logicalTop);
394 ComputeFloatOffsetAdapter<FloatingObject::FloatLeft> adapter(m_renderer, logicalTopAsInt, logicalTopAsInt, fixedOffset);
395 placedFloatsTree().allOverlapsWithAdapter(adapter);
396
397 if (heightRemaining)
398 *heightRemaining = adapter.heightRemaining();
399
400 return adapter.offset();
401 }
402
logicalRightOffsetForPositioningFloat(LayoutUnit fixedOffset,LayoutUnit logicalTop,LayoutUnit * heightRemaining)403 LayoutUnit FloatingObjects::logicalRightOffsetForPositioningFloat(LayoutUnit fixedOffset, LayoutUnit logicalTop, LayoutUnit *heightRemaining)
404 {
405 int logicalTopAsInt = roundToInt(logicalTop);
406 ComputeFloatOffsetAdapter<FloatingObject::FloatRight> adapter(m_renderer, logicalTopAsInt, logicalTopAsInt, fixedOffset);
407 placedFloatsTree().allOverlapsWithAdapter(adapter);
408
409 if (heightRemaining)
410 *heightRemaining = adapter.heightRemaining();
411
412 return min(fixedOffset, adapter.offset());
413 }
414
logicalLeftOffset(LayoutUnit fixedOffset,LayoutUnit logicalTop,LayoutUnit logicalHeight)415 LayoutUnit FloatingObjects::logicalLeftOffset(LayoutUnit fixedOffset, LayoutUnit logicalTop, LayoutUnit logicalHeight)
416 {
417 ComputeFloatOffsetAdapter<FloatingObject::FloatLeft> adapter(m_renderer, roundToInt(logicalTop), roundToInt(logicalTop + logicalHeight), fixedOffset);
418 placedFloatsTree().allOverlapsWithAdapter(adapter);
419
420 return adapter.shapeOffset();
421 }
422
logicalRightOffset(LayoutUnit fixedOffset,LayoutUnit logicalTop,LayoutUnit logicalHeight)423 LayoutUnit FloatingObjects::logicalRightOffset(LayoutUnit fixedOffset, LayoutUnit logicalTop, LayoutUnit logicalHeight)
424 {
425 ComputeFloatOffsetAdapter<FloatingObject::FloatRight> adapter(m_renderer, roundToInt(logicalTop), roundToInt(logicalTop + logicalHeight), fixedOffset);
426 placedFloatsTree().allOverlapsWithAdapter(adapter);
427
428 return min(fixedOffset, adapter.shapeOffset());
429 }
430
FloatBottomCachedValue()431 FloatingObjects::FloatBottomCachedValue::FloatBottomCachedValue()
432 : value(0)
433 , dirty(true)
434 {
435 }
436
rangesIntersect(int floatTop,int floatBottom,int objectTop,int objectBottom)437 inline static bool rangesIntersect(int floatTop, int floatBottom, int objectTop, int objectBottom)
438 {
439 if (objectTop >= floatBottom || objectBottom < floatTop)
440 return false;
441
442 // The top of the object overlaps the float
443 if (objectTop >= floatTop)
444 return true;
445
446 // The object encloses the float
447 if (objectTop < floatTop && objectBottom > floatBottom)
448 return true;
449
450 // The bottom of the object overlaps the float
451 if (objectBottom > objectTop && objectBottom > floatTop && objectBottom <= floatBottom)
452 return true;
453
454 return false;
455 }
456
457 template<>
updateOffsetIfNeeded(const FloatingObject * floatingObject)458 inline bool ComputeFloatOffsetAdapter<FloatingObject::FloatRight>::updateOffsetIfNeeded(const FloatingObject* floatingObject)
459 {
460 LayoutUnit logicalLeft = m_renderer->logicalLeftForFloat(floatingObject);
461 if (logicalLeft < m_offset) {
462 m_offset = logicalLeft;
463 return true;
464 }
465 return false;
466 }
467
468 template <FloatingObject::Type FloatTypeValue>
collectIfNeeded(const IntervalType & interval)469 inline void ComputeFloatOffsetAdapter<FloatTypeValue>::collectIfNeeded(const IntervalType& interval)
470 {
471 const FloatingObject* floatingObject = interval.data();
472 if (floatingObject->type() != FloatTypeValue || !rangesIntersect(interval.low(), interval.high(), m_lineTop, m_lineBottom))
473 return;
474
475 // Make sure the float hasn't changed since it was added to the placed floats tree.
476 ASSERT(floatingObject->isPlaced());
477 ASSERT(interval.low() == m_renderer->pixelSnappedLogicalTopForFloat(floatingObject));
478 ASSERT(interval.high() == m_renderer->pixelSnappedLogicalBottomForFloat(floatingObject));
479
480 bool floatIsNewExtreme = updateOffsetIfNeeded(floatingObject);
481 if (floatIsNewExtreme)
482 m_outermostFloat = floatingObject;
483 }
484
485 template <FloatingObject::Type FloatTypeValue>
heightRemaining() const486 LayoutUnit ComputeFloatOffsetAdapter<FloatTypeValue>::heightRemaining() const
487 {
488 return m_outermostFloat ? m_renderer->logicalBottomForFloat(m_outermostFloat) - m_lineTop : LayoutUnit(1);
489 }
490
491 #ifndef NDEBUG
492 // These helpers are only used by the PODIntervalTree for debugging purposes.
string(const int value)493 String ValueToString<int>::string(const int value)
494 {
495 return String::number(value);
496 }
497
string(const FloatingObject * floatingObject)498 String ValueToString<FloatingObject*>::string(const FloatingObject* floatingObject)
499 {
500 return String::format("%p (%dx%d %dx%d)", floatingObject, floatingObject->frameRect().pixelSnappedX(), floatingObject->frameRect().pixelSnappedY(), floatingObject->frameRect().pixelSnappedMaxX(), floatingObject->frameRect().pixelSnappedMaxY());
501 }
502 #endif
503
504
505 } // namespace WebCore
506