|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 /* class that manages rules for positioning floats */ |
|
7 |
|
8 #include "nsFloatManager.h" |
|
9 #include "nsIPresShell.h" |
|
10 #include "nsMemory.h" |
|
11 #include "nsHTMLReflowState.h" |
|
12 #include "nsBlockDebugFlags.h" |
|
13 #include "nsError.h" |
|
14 #include <algorithm> |
|
15 |
|
16 using namespace mozilla; |
|
17 |
|
18 int32_t nsFloatManager::sCachedFloatManagerCount = 0; |
|
19 void* nsFloatManager::sCachedFloatManagers[NS_FLOAT_MANAGER_CACHE_SIZE]; |
|
20 |
|
21 ///////////////////////////////////////////////////////////////////////////// |
|
22 |
|
23 // PresShell Arena allocate callback (for nsIntervalSet use below) |
|
24 static void* |
|
25 PSArenaAllocCB(size_t aSize, void* aClosure) |
|
26 { |
|
27 return static_cast<nsIPresShell*>(aClosure)->AllocateMisc(aSize); |
|
28 } |
|
29 |
|
30 // PresShell Arena free callback (for nsIntervalSet use below) |
|
31 static void |
|
32 PSArenaFreeCB(size_t aSize, void* aPtr, void* aClosure) |
|
33 { |
|
34 static_cast<nsIPresShell*>(aClosure)->FreeMisc(aSize, aPtr); |
|
35 } |
|
36 |
|
37 ///////////////////////////////////////////////////////////////////////////// |
|
38 // nsFloatManager |
|
39 |
|
40 nsFloatManager::nsFloatManager(nsIPresShell* aPresShell) |
|
41 : mX(0), mY(0), |
|
42 mFloatDamage(PSArenaAllocCB, PSArenaFreeCB, aPresShell), |
|
43 mPushedLeftFloatPastBreak(false), |
|
44 mPushedRightFloatPastBreak(false), |
|
45 mSplitLeftFloatAcrossBreak(false), |
|
46 mSplitRightFloatAcrossBreak(false) |
|
47 { |
|
48 MOZ_COUNT_CTOR(nsFloatManager); |
|
49 } |
|
50 |
|
51 nsFloatManager::~nsFloatManager() |
|
52 { |
|
53 MOZ_COUNT_DTOR(nsFloatManager); |
|
54 } |
|
55 |
|
56 // static |
|
57 void* nsFloatManager::operator new(size_t aSize) CPP_THROW_NEW |
|
58 { |
|
59 if (sCachedFloatManagerCount > 0) { |
|
60 // We have cached unused instances of this class, return a cached |
|
61 // instance in stead of always creating a new one. |
|
62 return sCachedFloatManagers[--sCachedFloatManagerCount]; |
|
63 } |
|
64 |
|
65 // The cache is empty, this means we haveto create a new instance using |
|
66 // the global |operator new|. |
|
67 return nsMemory::Alloc(aSize); |
|
68 } |
|
69 |
|
70 void |
|
71 nsFloatManager::operator delete(void* aPtr, size_t aSize) |
|
72 { |
|
73 if (!aPtr) |
|
74 return; |
|
75 // This float manager is no longer used, if there's still room in |
|
76 // the cache we'll cache this float manager, unless the layout |
|
77 // module was already shut down. |
|
78 |
|
79 if (sCachedFloatManagerCount < NS_FLOAT_MANAGER_CACHE_SIZE && |
|
80 sCachedFloatManagerCount >= 0) { |
|
81 // There's still space in the cache for more instances, put this |
|
82 // instance in the cache in stead of deleting it. |
|
83 |
|
84 sCachedFloatManagers[sCachedFloatManagerCount++] = aPtr; |
|
85 return; |
|
86 } |
|
87 |
|
88 // The cache is full, or the layout module has been shut down, |
|
89 // delete this float manager. |
|
90 nsMemory::Free(aPtr); |
|
91 } |
|
92 |
|
93 |
|
94 /* static */ |
|
95 void nsFloatManager::Shutdown() |
|
96 { |
|
97 // The layout module is being shut down, clean up the cache and |
|
98 // disable further caching. |
|
99 |
|
100 int32_t i; |
|
101 |
|
102 for (i = 0; i < sCachedFloatManagerCount; i++) { |
|
103 void* floatManager = sCachedFloatManagers[i]; |
|
104 if (floatManager) |
|
105 nsMemory::Free(floatManager); |
|
106 } |
|
107 |
|
108 // Disable further caching. |
|
109 sCachedFloatManagerCount = -1; |
|
110 } |
|
111 |
|
112 nsFlowAreaRect |
|
113 nsFloatManager::GetFlowArea(nscoord aYOffset, BandInfoType aInfoType, |
|
114 nscoord aHeight, nsRect aContentArea, |
|
115 SavedState* aState) const |
|
116 { |
|
117 NS_ASSERTION(aHeight >= 0, "unexpected max height"); |
|
118 NS_ASSERTION(aContentArea.width >= 0, "unexpected content area width"); |
|
119 |
|
120 nscoord top = aYOffset + mY; |
|
121 if (top < nscoord_MIN) { |
|
122 NS_WARNING("bad value"); |
|
123 top = nscoord_MIN; |
|
124 } |
|
125 |
|
126 // Determine the last float that we should consider. |
|
127 uint32_t floatCount; |
|
128 if (aState) { |
|
129 // Use the provided state. |
|
130 floatCount = aState->mFloatInfoCount; |
|
131 NS_ABORT_IF_FALSE(floatCount <= mFloats.Length(), "bad state"); |
|
132 } else { |
|
133 // Use our current state. |
|
134 floatCount = mFloats.Length(); |
|
135 } |
|
136 |
|
137 // If there are no floats at all, or we're below the last one, return |
|
138 // quickly. |
|
139 if (floatCount == 0 || |
|
140 (mFloats[floatCount-1].mLeftYMost <= top && |
|
141 mFloats[floatCount-1].mRightYMost <= top)) { |
|
142 return nsFlowAreaRect(aContentArea.x, aYOffset, aContentArea.width, |
|
143 aHeight, false); |
|
144 } |
|
145 |
|
146 nscoord bottom; |
|
147 if (aHeight == nscoord_MAX) { |
|
148 // This warning (and the two below) are possible to hit on pages |
|
149 // with really large objects. |
|
150 NS_WARN_IF_FALSE(aInfoType == BAND_FROM_POINT, |
|
151 "bad height"); |
|
152 bottom = nscoord_MAX; |
|
153 } else { |
|
154 bottom = top + aHeight; |
|
155 if (bottom < top || bottom > nscoord_MAX) { |
|
156 NS_WARNING("bad value"); |
|
157 bottom = nscoord_MAX; |
|
158 } |
|
159 } |
|
160 nscoord left = mX + aContentArea.x; |
|
161 nscoord right = mX + aContentArea.XMost(); |
|
162 if (right < left) { |
|
163 NS_WARNING("bad value"); |
|
164 right = left; |
|
165 } |
|
166 |
|
167 // Walk backwards through the floats until we either hit the front of |
|
168 // the list or we're above |top|. |
|
169 bool haveFloats = false; |
|
170 for (uint32_t i = floatCount; i > 0; --i) { |
|
171 const FloatInfo &fi = mFloats[i-1]; |
|
172 if (fi.mLeftYMost <= top && fi.mRightYMost <= top) { |
|
173 // There aren't any more floats that could intersect this band. |
|
174 break; |
|
175 } |
|
176 if (fi.mRect.IsEmpty()) { |
|
177 // For compatibility, ignore floats with empty rects, even though it |
|
178 // disagrees with the spec. (We might want to fix this in the |
|
179 // future, though.) |
|
180 continue; |
|
181 } |
|
182 nscoord floatTop = fi.mRect.y, floatBottom = fi.mRect.YMost(); |
|
183 if (top < floatTop && aInfoType == BAND_FROM_POINT) { |
|
184 // This float is below our band. Shrink our band's height if needed. |
|
185 if (floatTop < bottom) { |
|
186 bottom = floatTop; |
|
187 } |
|
188 } |
|
189 // If top == bottom (which happens only with WIDTH_WITHIN_HEIGHT), |
|
190 // we include floats that begin at our 0-height vertical area. We |
|
191 // need to to this to satisfy the invariant that a |
|
192 // WIDTH_WITHIN_HEIGHT call is at least as narrow on both sides as a |
|
193 // BAND_WITHIN_POINT call beginning at its top. |
|
194 else if (top < floatBottom && |
|
195 (floatTop < bottom || (floatTop == bottom && top == bottom))) { |
|
196 // This float is in our band. |
|
197 |
|
198 // Shrink our band's height if needed. |
|
199 if (floatBottom < bottom && aInfoType == BAND_FROM_POINT) { |
|
200 bottom = floatBottom; |
|
201 } |
|
202 |
|
203 // Shrink our band's width if needed. |
|
204 if (fi.mFrame->StyleDisplay()->mFloats == NS_STYLE_FLOAT_LEFT) { |
|
205 // A left float. |
|
206 nscoord rightEdge = fi.mRect.XMost(); |
|
207 if (rightEdge > left) { |
|
208 left = rightEdge; |
|
209 // Only set haveFloats to true if the float is inside our |
|
210 // containing block. This matches the spec for what some |
|
211 // callers want and disagrees for other callers, so we should |
|
212 // probably provide better information at some point. |
|
213 haveFloats = true; |
|
214 } |
|
215 } else { |
|
216 // A right float. |
|
217 nscoord leftEdge = fi.mRect.x; |
|
218 if (leftEdge < right) { |
|
219 right = leftEdge; |
|
220 // See above. |
|
221 haveFloats = true; |
|
222 } |
|
223 } |
|
224 } |
|
225 } |
|
226 |
|
227 nscoord height = (bottom == nscoord_MAX) ? nscoord_MAX : (bottom - top); |
|
228 return nsFlowAreaRect(left - mX, top - mY, right - left, height, haveFloats); |
|
229 } |
|
230 |
|
231 nsresult |
|
232 nsFloatManager::AddFloat(nsIFrame* aFloatFrame, const nsRect& aMarginRect) |
|
233 { |
|
234 NS_ASSERTION(aMarginRect.width >= 0, "negative width!"); |
|
235 NS_ASSERTION(aMarginRect.height >= 0, "negative height!"); |
|
236 |
|
237 FloatInfo info(aFloatFrame, aMarginRect + nsPoint(mX, mY)); |
|
238 |
|
239 // Set mLeftYMost and mRightYMost. |
|
240 if (HasAnyFloats()) { |
|
241 FloatInfo &tail = mFloats[mFloats.Length() - 1]; |
|
242 info.mLeftYMost = tail.mLeftYMost; |
|
243 info.mRightYMost = tail.mRightYMost; |
|
244 } else { |
|
245 info.mLeftYMost = nscoord_MIN; |
|
246 info.mRightYMost = nscoord_MIN; |
|
247 } |
|
248 uint8_t floatStyle = aFloatFrame->StyleDisplay()->mFloats; |
|
249 NS_ASSERTION(floatStyle == NS_STYLE_FLOAT_LEFT || |
|
250 floatStyle == NS_STYLE_FLOAT_RIGHT, "unexpected float"); |
|
251 nscoord& sideYMost = (floatStyle == NS_STYLE_FLOAT_LEFT) ? info.mLeftYMost |
|
252 : info.mRightYMost; |
|
253 nscoord thisYMost = info.mRect.YMost(); |
|
254 if (thisYMost > sideYMost) |
|
255 sideYMost = thisYMost; |
|
256 |
|
257 if (!mFloats.AppendElement(info)) |
|
258 return NS_ERROR_OUT_OF_MEMORY; |
|
259 |
|
260 return NS_OK; |
|
261 } |
|
262 |
|
263 nsRect |
|
264 nsFloatManager::CalculateRegionFor(nsIFrame* aFloat, |
|
265 const nsMargin& aMargin) |
|
266 { |
|
267 // We consider relatively positioned frames at their original position. |
|
268 nsRect region(aFloat->GetNormalPosition(), aFloat->GetSize()); |
|
269 |
|
270 // Float region includes its margin |
|
271 region.Inflate(aMargin); |
|
272 |
|
273 // Don't store rectangles with negative margin-box width or height in |
|
274 // the float manager; it can't deal with them. |
|
275 if (region.width < 0) { |
|
276 // Preserve the right margin-edge for left floats and the left |
|
277 // margin-edge for right floats |
|
278 const nsStyleDisplay* display = aFloat->StyleDisplay(); |
|
279 if (NS_STYLE_FLOAT_LEFT == display->mFloats) { |
|
280 region.x = region.XMost(); |
|
281 } |
|
282 region.width = 0; |
|
283 } |
|
284 if (region.height < 0) { |
|
285 region.height = 0; |
|
286 } |
|
287 return region; |
|
288 } |
|
289 |
|
290 NS_DECLARE_FRAME_PROPERTY(FloatRegionProperty, nsIFrame::DestroyMargin) |
|
291 |
|
292 nsRect |
|
293 nsFloatManager::GetRegionFor(nsIFrame* aFloat) |
|
294 { |
|
295 nsRect region = aFloat->GetRect(); |
|
296 void* storedRegion = aFloat->Properties().Get(FloatRegionProperty()); |
|
297 if (storedRegion) { |
|
298 nsMargin margin = *static_cast<nsMargin*>(storedRegion); |
|
299 region.Inflate(margin); |
|
300 } |
|
301 return region; |
|
302 } |
|
303 |
|
304 void |
|
305 nsFloatManager::StoreRegionFor(nsIFrame* aFloat, |
|
306 nsRect& aRegion) |
|
307 { |
|
308 nsRect rect = aFloat->GetRect(); |
|
309 FrameProperties props = aFloat->Properties(); |
|
310 if (aRegion.IsEqualEdges(rect)) { |
|
311 props.Delete(FloatRegionProperty()); |
|
312 } |
|
313 else { |
|
314 nsMargin* storedMargin = static_cast<nsMargin*> |
|
315 (props.Get(FloatRegionProperty())); |
|
316 if (!storedMargin) { |
|
317 storedMargin = new nsMargin(); |
|
318 props.Set(FloatRegionProperty(), storedMargin); |
|
319 } |
|
320 *storedMargin = aRegion - rect; |
|
321 } |
|
322 } |
|
323 |
|
324 nsresult |
|
325 nsFloatManager::RemoveTrailingRegions(nsIFrame* aFrameList) |
|
326 { |
|
327 if (!aFrameList) { |
|
328 return NS_OK; |
|
329 } |
|
330 // This could be a good bit simpler if we could guarantee that the |
|
331 // floats given were at the end of our list, so we could just search |
|
332 // for the head of aFrameList. (But we can't; |
|
333 // layout/reftests/bugs/421710-1.html crashes.) |
|
334 nsTHashtable<nsPtrHashKey<nsIFrame> > frameSet(1); |
|
335 |
|
336 for (nsIFrame* f = aFrameList; f; f = f->GetNextSibling()) { |
|
337 frameSet.PutEntry(f); |
|
338 } |
|
339 |
|
340 uint32_t newLength = mFloats.Length(); |
|
341 while (newLength > 0) { |
|
342 if (!frameSet.Contains(mFloats[newLength - 1].mFrame)) { |
|
343 break; |
|
344 } |
|
345 --newLength; |
|
346 } |
|
347 mFloats.TruncateLength(newLength); |
|
348 |
|
349 #ifdef DEBUG |
|
350 for (uint32_t i = 0; i < mFloats.Length(); ++i) { |
|
351 NS_ASSERTION(!frameSet.Contains(mFloats[i].mFrame), |
|
352 "Frame region deletion was requested but we couldn't delete it"); |
|
353 } |
|
354 #endif |
|
355 |
|
356 return NS_OK; |
|
357 } |
|
358 |
|
359 void |
|
360 nsFloatManager::PushState(SavedState* aState) |
|
361 { |
|
362 NS_PRECONDITION(aState, "Need a place to save state"); |
|
363 |
|
364 // This is a cheap push implementation, which |
|
365 // only saves the (x,y) and last frame in the mFrameInfoMap |
|
366 // which is enough info to get us back to where we should be |
|
367 // when pop is called. |
|
368 // |
|
369 // This push/pop mechanism is used to undo any |
|
370 // floats that were added during the unconstrained reflow |
|
371 // in nsBlockReflowContext::DoReflowBlock(). (See bug 96736) |
|
372 // |
|
373 // It should also be noted that the state for mFloatDamage is |
|
374 // intentionally not saved or restored in PushState() and PopState(), |
|
375 // since that could lead to bugs where damage is missed/dropped when |
|
376 // we move from position A to B (during the intermediate incremental |
|
377 // reflow mentioned above) and then from B to C during the subsequent |
|
378 // reflow. In the typical case A and C will be the same, but not always. |
|
379 // Allowing mFloatDamage to accumulate the damage incurred during both |
|
380 // reflows ensures that nothing gets missed. |
|
381 aState->mX = mX; |
|
382 aState->mY = mY; |
|
383 aState->mPushedLeftFloatPastBreak = mPushedLeftFloatPastBreak; |
|
384 aState->mPushedRightFloatPastBreak = mPushedRightFloatPastBreak; |
|
385 aState->mSplitLeftFloatAcrossBreak = mSplitLeftFloatAcrossBreak; |
|
386 aState->mSplitRightFloatAcrossBreak = mSplitRightFloatAcrossBreak; |
|
387 aState->mFloatInfoCount = mFloats.Length(); |
|
388 } |
|
389 |
|
390 void |
|
391 nsFloatManager::PopState(SavedState* aState) |
|
392 { |
|
393 NS_PRECONDITION(aState, "No state to restore?"); |
|
394 |
|
395 mX = aState->mX; |
|
396 mY = aState->mY; |
|
397 mPushedLeftFloatPastBreak = aState->mPushedLeftFloatPastBreak; |
|
398 mPushedRightFloatPastBreak = aState->mPushedRightFloatPastBreak; |
|
399 mSplitLeftFloatAcrossBreak = aState->mSplitLeftFloatAcrossBreak; |
|
400 mSplitRightFloatAcrossBreak = aState->mSplitRightFloatAcrossBreak; |
|
401 |
|
402 NS_ASSERTION(aState->mFloatInfoCount <= mFloats.Length(), |
|
403 "somebody misused PushState/PopState"); |
|
404 mFloats.TruncateLength(aState->mFloatInfoCount); |
|
405 } |
|
406 |
|
407 nscoord |
|
408 nsFloatManager::GetLowestFloatTop() const |
|
409 { |
|
410 if (mPushedLeftFloatPastBreak || mPushedRightFloatPastBreak) { |
|
411 return nscoord_MAX; |
|
412 } |
|
413 if (!HasAnyFloats()) { |
|
414 return nscoord_MIN; |
|
415 } |
|
416 return mFloats[mFloats.Length() - 1].mRect.y - mY; |
|
417 } |
|
418 |
|
419 #ifdef DEBUG_FRAME_DUMP |
|
420 void |
|
421 DebugListFloatManager(const nsFloatManager *aFloatManager) |
|
422 { |
|
423 aFloatManager->List(stdout); |
|
424 } |
|
425 |
|
426 nsresult |
|
427 nsFloatManager::List(FILE* out) const |
|
428 { |
|
429 if (!HasAnyFloats()) |
|
430 return NS_OK; |
|
431 |
|
432 for (uint32_t i = 0; i < mFloats.Length(); ++i) { |
|
433 const FloatInfo &fi = mFloats[i]; |
|
434 fprintf_stderr(out, "Float %u: frame=%p rect={%d,%d,%d,%d} ymost={l:%d, r:%d}\n", |
|
435 i, static_cast<void*>(fi.mFrame), |
|
436 fi.mRect.x, fi.mRect.y, fi.mRect.width, fi.mRect.height, |
|
437 fi.mLeftYMost, fi.mRightYMost); |
|
438 } |
|
439 return NS_OK; |
|
440 } |
|
441 #endif |
|
442 |
|
443 nscoord |
|
444 nsFloatManager::ClearFloats(nscoord aY, uint8_t aBreakType, |
|
445 uint32_t aFlags) const |
|
446 { |
|
447 if (!(aFlags & DONT_CLEAR_PUSHED_FLOATS) && ClearContinues(aBreakType)) { |
|
448 return nscoord_MAX; |
|
449 } |
|
450 if (!HasAnyFloats()) { |
|
451 return aY; |
|
452 } |
|
453 |
|
454 nscoord bottom = aY + mY; |
|
455 |
|
456 const FloatInfo &tail = mFloats[mFloats.Length() - 1]; |
|
457 switch (aBreakType) { |
|
458 case NS_STYLE_CLEAR_BOTH: |
|
459 bottom = std::max(bottom, tail.mLeftYMost); |
|
460 bottom = std::max(bottom, tail.mRightYMost); |
|
461 break; |
|
462 case NS_STYLE_CLEAR_LEFT: |
|
463 bottom = std::max(bottom, tail.mLeftYMost); |
|
464 break; |
|
465 case NS_STYLE_CLEAR_RIGHT: |
|
466 bottom = std::max(bottom, tail.mRightYMost); |
|
467 break; |
|
468 default: |
|
469 // Do nothing |
|
470 break; |
|
471 } |
|
472 |
|
473 bottom -= mY; |
|
474 |
|
475 return bottom; |
|
476 } |
|
477 |
|
478 bool |
|
479 nsFloatManager::ClearContinues(uint8_t aBreakType) const |
|
480 { |
|
481 return ((mPushedLeftFloatPastBreak || mSplitLeftFloatAcrossBreak) && |
|
482 (aBreakType == NS_STYLE_CLEAR_BOTH || |
|
483 aBreakType == NS_STYLE_CLEAR_LEFT)) || |
|
484 ((mPushedRightFloatPastBreak || mSplitRightFloatAcrossBreak) && |
|
485 (aBreakType == NS_STYLE_CLEAR_BOTH || |
|
486 aBreakType == NS_STYLE_CLEAR_RIGHT)); |
|
487 } |
|
488 |
|
489 ///////////////////////////////////////////////////////////////////////////// |
|
490 // FloatInfo |
|
491 |
|
492 nsFloatManager::FloatInfo::FloatInfo(nsIFrame* aFrame, const nsRect& aRect) |
|
493 : mFrame(aFrame), mRect(aRect) |
|
494 { |
|
495 MOZ_COUNT_CTOR(nsFloatManager::FloatInfo); |
|
496 } |
|
497 |
|
498 #ifdef NS_BUILD_REFCNT_LOGGING |
|
499 nsFloatManager::FloatInfo::FloatInfo(const FloatInfo& aOther) |
|
500 : mFrame(aOther.mFrame), |
|
501 mRect(aOther.mRect), |
|
502 mLeftYMost(aOther.mLeftYMost), |
|
503 mRightYMost(aOther.mRightYMost) |
|
504 { |
|
505 MOZ_COUNT_CTOR(nsFloatManager::FloatInfo); |
|
506 } |
|
507 |
|
508 nsFloatManager::FloatInfo::~FloatInfo() |
|
509 { |
|
510 MOZ_COUNT_DTOR(nsFloatManager::FloatInfo); |
|
511 } |
|
512 #endif |
|
513 |
|
514 //---------------------------------------------------------------------- |
|
515 |
|
516 nsAutoFloatManager::~nsAutoFloatManager() |
|
517 { |
|
518 // Restore the old float manager in the reflow state if necessary. |
|
519 if (mNew) { |
|
520 #ifdef NOISY_FLOATMANAGER |
|
521 printf("restoring old float manager %p\n", mOld); |
|
522 #endif |
|
523 |
|
524 mReflowState.mFloatManager = mOld; |
|
525 |
|
526 #ifdef NOISY_FLOATMANAGER |
|
527 if (mOld) { |
|
528 static_cast<nsFrame *>(mReflowState.frame)->ListTag(stdout); |
|
529 printf(": space-manager %p after reflow\n", mOld); |
|
530 mOld->List(stdout); |
|
531 } |
|
532 #endif |
|
533 |
|
534 delete mNew; |
|
535 } |
|
536 } |
|
537 |
|
538 nsresult |
|
539 nsAutoFloatManager::CreateFloatManager(nsPresContext *aPresContext) |
|
540 { |
|
541 // Create a new float manager and install it in the reflow |
|
542 // state. `Remember' the old float manager so we can restore it |
|
543 // later. |
|
544 mNew = new nsFloatManager(aPresContext->PresShell()); |
|
545 if (! mNew) |
|
546 return NS_ERROR_OUT_OF_MEMORY; |
|
547 |
|
548 #ifdef NOISY_FLOATMANAGER |
|
549 printf("constructed new float manager %p (replacing %p)\n", |
|
550 mNew, mReflowState.mFloatManager); |
|
551 #endif |
|
552 |
|
553 // Set the float manager in the existing reflow state |
|
554 mOld = mReflowState.mFloatManager; |
|
555 mReflowState.mFloatManager = mNew; |
|
556 return NS_OK; |
|
557 } |