Line data Source code
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 :
10 : #include <algorithm>
11 : #include <initializer_list>
12 :
13 : #include "mozilla/ReflowInput.h"
14 : #include "mozilla/ShapeUtils.h"
15 : #include "nsBlockFrame.h"
16 : #include "nsError.h"
17 : #include "nsIPresShell.h"
18 : #include "nsMemory.h"
19 :
20 : using namespace mozilla;
21 :
22 : int32_t nsFloatManager::sCachedFloatManagerCount = 0;
23 : void* nsFloatManager::sCachedFloatManagers[NS_FLOAT_MANAGER_CACHE_SIZE];
24 :
25 : /////////////////////////////////////////////////////////////////////////////
26 : // nsFloatManager
27 :
28 152 : nsFloatManager::nsFloatManager(nsIPresShell* aPresShell,
29 152 : mozilla::WritingMode aWM)
30 : :
31 : #ifdef DEBUG
32 : mWritingMode(aWM),
33 : #endif
34 : mLineLeft(0), mBlockStart(0),
35 : mFloatDamage(aPresShell),
36 : mPushedLeftFloatPastBreak(false),
37 : mPushedRightFloatPastBreak(false),
38 : mSplitLeftFloatAcrossBreak(false),
39 152 : mSplitRightFloatAcrossBreak(false)
40 : {
41 152 : MOZ_COUNT_CTOR(nsFloatManager);
42 152 : }
43 :
44 304 : nsFloatManager::~nsFloatManager()
45 : {
46 152 : MOZ_COUNT_DTOR(nsFloatManager);
47 152 : }
48 :
49 : // static
50 152 : void* nsFloatManager::operator new(size_t aSize) CPP_THROW_NEW
51 : {
52 152 : if (sCachedFloatManagerCount > 0) {
53 : // We have cached unused instances of this class, return a cached
54 : // instance in stead of always creating a new one.
55 150 : return sCachedFloatManagers[--sCachedFloatManagerCount];
56 : }
57 :
58 : // The cache is empty, this means we have to create a new instance using
59 : // the global |operator new|.
60 2 : return moz_xmalloc(aSize);
61 : }
62 :
63 : void
64 152 : nsFloatManager::operator delete(void* aPtr, size_t aSize)
65 : {
66 152 : if (!aPtr)
67 0 : return;
68 : // This float manager is no longer used, if there's still room in
69 : // the cache we'll cache this float manager, unless the layout
70 : // module was already shut down.
71 :
72 304 : if (sCachedFloatManagerCount < NS_FLOAT_MANAGER_CACHE_SIZE &&
73 152 : sCachedFloatManagerCount >= 0) {
74 : // There's still space in the cache for more instances, put this
75 : // instance in the cache in stead of deleting it.
76 :
77 152 : sCachedFloatManagers[sCachedFloatManagerCount++] = aPtr;
78 152 : return;
79 : }
80 :
81 : // The cache is full, or the layout module has been shut down,
82 : // delete this float manager.
83 0 : free(aPtr);
84 : }
85 :
86 :
87 : /* static */
88 0 : void nsFloatManager::Shutdown()
89 : {
90 : // The layout module is being shut down, clean up the cache and
91 : // disable further caching.
92 :
93 : int32_t i;
94 :
95 0 : for (i = 0; i < sCachedFloatManagerCount; i++) {
96 0 : void* floatManager = sCachedFloatManagers[i];
97 0 : if (floatManager)
98 0 : free(floatManager);
99 : }
100 :
101 : // Disable further caching.
102 0 : sCachedFloatManagerCount = -1;
103 0 : }
104 :
105 : #define CHECK_BLOCK_AND_LINE_DIR(aWM) \
106 : NS_ASSERTION((aWM).GetBlockDir() == mWritingMode.GetBlockDir() && \
107 : (aWM).IsLineInverted() == mWritingMode.IsLineInverted(), \
108 : "incompatible writing modes")
109 :
110 : nsFlowAreaRect
111 235 : nsFloatManager::GetFlowArea(WritingMode aWM, nscoord aBCoord, nscoord aBSize,
112 : BandInfoType aBandInfoType, ShapeType aShapeType,
113 : LogicalRect aContentArea, SavedState* aState,
114 : const nsSize& aContainerSize) const
115 : {
116 235 : CHECK_BLOCK_AND_LINE_DIR(aWM);
117 235 : NS_ASSERTION(aBSize >= 0, "unexpected max block size");
118 235 : NS_ASSERTION(aContentArea.ISize(aWM) >= 0,
119 : "unexpected content area inline size");
120 :
121 235 : nscoord blockStart = aBCoord + mBlockStart;
122 235 : if (blockStart < nscoord_MIN) {
123 0 : NS_WARNING("bad value");
124 0 : blockStart = nscoord_MIN;
125 : }
126 :
127 : // Determine the last float that we should consider.
128 : uint32_t floatCount;
129 235 : if (aState) {
130 : // Use the provided state.
131 0 : floatCount = aState->mFloatInfoCount;
132 0 : MOZ_ASSERT(floatCount <= mFloats.Length(), "bad state");
133 : } else {
134 : // Use our current state.
135 235 : floatCount = mFloats.Length();
136 : }
137 :
138 : // If there are no floats at all, or we're below the last one, return
139 : // quickly.
140 470 : if (floatCount == 0 ||
141 0 : (mFloats[floatCount-1].mLeftBEnd <= blockStart &&
142 0 : mFloats[floatCount-1].mRightBEnd <= blockStart)) {
143 235 : return nsFlowAreaRect(aWM, aContentArea.IStart(aWM), aBCoord,
144 470 : aContentArea.ISize(aWM), aBSize, false);
145 : }
146 :
147 : nscoord blockEnd;
148 0 : if (aBSize == nscoord_MAX) {
149 : // This warning (and the two below) are possible to hit on pages
150 : // with really large objects.
151 0 : NS_WARNING_ASSERTION(aBandInfoType == BandInfoType::BandFromPoint, "bad height");
152 0 : blockEnd = nscoord_MAX;
153 : } else {
154 0 : blockEnd = blockStart + aBSize;
155 0 : if (blockEnd < blockStart || blockEnd > nscoord_MAX) {
156 0 : NS_WARNING("bad value");
157 0 : blockEnd = nscoord_MAX;
158 : }
159 : }
160 0 : nscoord lineLeft = mLineLeft + aContentArea.LineLeft(aWM, aContainerSize);
161 0 : nscoord lineRight = mLineLeft + aContentArea.LineRight(aWM, aContainerSize);
162 0 : if (lineRight < lineLeft) {
163 0 : NS_WARNING("bad value");
164 0 : lineRight = lineLeft;
165 : }
166 :
167 : // Walk backwards through the floats until we either hit the front of
168 : // the list or we're above |blockStart|.
169 0 : bool haveFloats = false;
170 0 : for (uint32_t i = floatCount; i > 0; --i) {
171 0 : const FloatInfo &fi = mFloats[i-1];
172 0 : if (fi.mLeftBEnd <= blockStart && fi.mRightBEnd <= blockStart) {
173 : // There aren't any more floats that could intersect this band.
174 0 : break;
175 : }
176 0 : if (fi.IsEmpty(aShapeType)) {
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 0 : continue;
181 : }
182 :
183 0 : nscoord floatBStart = fi.BStart(aShapeType);
184 0 : nscoord floatBEnd = fi.BEnd(aShapeType);
185 0 : if (blockStart < floatBStart && aBandInfoType == BandInfoType::BandFromPoint) {
186 : // This float is below our band. Shrink our band's height if needed.
187 0 : if (floatBStart < blockEnd) {
188 0 : blockEnd = floatBStart;
189 : }
190 : }
191 : // If blockStart == blockEnd (which happens only with WidthWithinHeight),
192 : // we include floats that begin at our 0-height vertical area. We
193 : // need to do this to satisfy the invariant that a
194 : // WidthWithinHeight call is at least as narrow on both sides as a
195 : // BandFromPoint call beginning at its blockStart.
196 0 : else if (blockStart < floatBEnd &&
197 0 : (floatBStart < blockEnd ||
198 0 : (floatBStart == blockEnd && blockStart == blockEnd))) {
199 : // This float is in our band.
200 :
201 : // Shrink our band's width if needed.
202 0 : StyleFloat floatStyle = fi.mFrame->StyleDisplay()->PhysicalFloats(aWM);
203 :
204 : // When aBandInfoType is BandFromPoint, we're only intended to
205 : // consider a point along the y axis rather than a band.
206 : const nscoord bandBlockEnd =
207 0 : aBandInfoType == BandInfoType::BandFromPoint ? blockStart : blockEnd;
208 0 : if (floatStyle == StyleFloat::Left) {
209 : // A left float
210 : nscoord lineRightEdge =
211 0 : fi.LineRight(aShapeType, blockStart, bandBlockEnd);
212 0 : if (lineRightEdge > lineLeft) {
213 0 : lineLeft = lineRightEdge;
214 : // Only set haveFloats to true if the float is inside our
215 : // containing block. This matches the spec for what some
216 : // callers want and disagrees for other callers, so we should
217 : // probably provide better information at some point.
218 0 : haveFloats = true;
219 : }
220 : } else {
221 : // A right float
222 : nscoord lineLeftEdge =
223 0 : fi.LineLeft(aShapeType, blockStart, bandBlockEnd);
224 0 : if (lineLeftEdge < lineRight) {
225 0 : lineRight = lineLeftEdge;
226 : // See above.
227 0 : haveFloats = true;
228 : }
229 : }
230 :
231 : // Shrink our band's height if needed.
232 0 : if (floatBEnd < blockEnd && aBandInfoType == BandInfoType::BandFromPoint) {
233 0 : blockEnd = floatBEnd;
234 : }
235 : }
236 : }
237 :
238 0 : nscoord blockSize = (blockEnd == nscoord_MAX) ?
239 0 : nscoord_MAX : (blockEnd - blockStart);
240 : // convert back from LineLeft/Right to IStart
241 0 : nscoord inlineStart = aWM.IsBidiLTR()
242 0 : ? lineLeft - mLineLeft
243 0 : : mLineLeft - lineRight +
244 0 : LogicalSize(aWM, aContainerSize).ISize(aWM);
245 :
246 0 : return nsFlowAreaRect(aWM, inlineStart, blockStart - mBlockStart,
247 0 : lineRight - lineLeft, blockSize, haveFloats);
248 : }
249 :
250 : void
251 0 : nsFloatManager::AddFloat(nsIFrame* aFloatFrame, const LogicalRect& aMarginRect,
252 : WritingMode aWM, const nsSize& aContainerSize)
253 : {
254 0 : CHECK_BLOCK_AND_LINE_DIR(aWM);
255 0 : NS_ASSERTION(aMarginRect.ISize(aWM) >= 0, "negative inline size!");
256 0 : NS_ASSERTION(aMarginRect.BSize(aWM) >= 0, "negative block size!");
257 :
258 : FloatInfo info(aFloatFrame, mLineLeft, mBlockStart, aMarginRect, aWM,
259 0 : aContainerSize);
260 :
261 : // Set mLeftBEnd and mRightBEnd.
262 0 : if (HasAnyFloats()) {
263 0 : FloatInfo &tail = mFloats[mFloats.Length() - 1];
264 0 : info.mLeftBEnd = tail.mLeftBEnd;
265 0 : info.mRightBEnd = tail.mRightBEnd;
266 : } else {
267 0 : info.mLeftBEnd = nscoord_MIN;
268 0 : info.mRightBEnd = nscoord_MIN;
269 : }
270 0 : StyleFloat floatStyle = aFloatFrame->StyleDisplay()->PhysicalFloats(aWM);
271 0 : MOZ_ASSERT(floatStyle == StyleFloat::Left || floatStyle == StyleFloat::Right,
272 : "Unexpected float style!");
273 : nscoord& sideBEnd =
274 0 : floatStyle == StyleFloat::Left ? info.mLeftBEnd : info.mRightBEnd;
275 0 : nscoord thisBEnd = info.BEnd();
276 0 : if (thisBEnd > sideBEnd)
277 0 : sideBEnd = thisBEnd;
278 :
279 0 : mFloats.AppendElement(Move(info));
280 0 : }
281 :
282 : // static
283 : LogicalRect
284 0 : nsFloatManager::CalculateRegionFor(WritingMode aWM,
285 : nsIFrame* aFloat,
286 : const LogicalMargin& aMargin,
287 : const nsSize& aContainerSize)
288 : {
289 : // We consider relatively positioned frames at their original position.
290 0 : LogicalRect region(aWM, nsRect(aFloat->GetNormalPosition(),
291 0 : aFloat->GetSize()),
292 0 : aContainerSize);
293 :
294 : // Float region includes its margin
295 0 : region.Inflate(aWM, aMargin);
296 :
297 : // Don't store rectangles with negative margin-box width or height in
298 : // the float manager; it can't deal with them.
299 0 : if (region.ISize(aWM) < 0) {
300 : // Preserve the right margin-edge for left floats and the left
301 : // margin-edge for right floats
302 0 : const nsStyleDisplay* display = aFloat->StyleDisplay();
303 0 : StyleFloat floatStyle = display->PhysicalFloats(aWM);
304 0 : if ((StyleFloat::Left == floatStyle) == aWM.IsBidiLTR()) {
305 0 : region.IStart(aWM) = region.IEnd(aWM);
306 : }
307 0 : region.ISize(aWM) = 0;
308 : }
309 0 : if (region.BSize(aWM) < 0) {
310 0 : region.BSize(aWM) = 0;
311 : }
312 0 : return region;
313 : }
314 :
315 0 : NS_DECLARE_FRAME_PROPERTY_DELETABLE(FloatRegionProperty, nsMargin)
316 :
317 : LogicalRect
318 0 : nsFloatManager::GetRegionFor(WritingMode aWM, nsIFrame* aFloat,
319 : const nsSize& aContainerSize)
320 : {
321 0 : LogicalRect region = aFloat->GetLogicalRect(aWM, aContainerSize);
322 0 : void* storedRegion = aFloat->GetProperty(FloatRegionProperty());
323 0 : if (storedRegion) {
324 0 : nsMargin margin = *static_cast<nsMargin*>(storedRegion);
325 0 : region.Inflate(aWM, LogicalMargin(aWM, margin));
326 : }
327 0 : return region;
328 : }
329 :
330 : void
331 0 : nsFloatManager::StoreRegionFor(WritingMode aWM, nsIFrame* aFloat,
332 : const LogicalRect& aRegion,
333 : const nsSize& aContainerSize)
334 : {
335 0 : nsRect region = aRegion.GetPhysicalRect(aWM, aContainerSize);
336 0 : nsRect rect = aFloat->GetRect();
337 0 : if (region.IsEqualEdges(rect)) {
338 0 : aFloat->DeleteProperty(FloatRegionProperty());
339 : }
340 : else {
341 0 : nsMargin* storedMargin = aFloat->GetProperty(FloatRegionProperty());
342 0 : if (!storedMargin) {
343 0 : storedMargin = new nsMargin();
344 0 : aFloat->SetProperty(FloatRegionProperty(), storedMargin);
345 : }
346 0 : *storedMargin = region - rect;
347 : }
348 0 : }
349 :
350 : nsresult
351 0 : nsFloatManager::RemoveTrailingRegions(nsIFrame* aFrameList)
352 : {
353 0 : if (!aFrameList) {
354 0 : return NS_OK;
355 : }
356 : // This could be a good bit simpler if we could guarantee that the
357 : // floats given were at the end of our list, so we could just search
358 : // for the head of aFrameList. (But we can't;
359 : // layout/reftests/bugs/421710-1.html crashes.)
360 0 : nsTHashtable<nsPtrHashKey<nsIFrame> > frameSet(1);
361 :
362 0 : for (nsIFrame* f = aFrameList; f; f = f->GetNextSibling()) {
363 0 : frameSet.PutEntry(f);
364 : }
365 :
366 0 : uint32_t newLength = mFloats.Length();
367 0 : while (newLength > 0) {
368 0 : if (!frameSet.Contains(mFloats[newLength - 1].mFrame)) {
369 0 : break;
370 : }
371 0 : --newLength;
372 : }
373 0 : mFloats.TruncateLength(newLength);
374 :
375 : #ifdef DEBUG
376 0 : for (uint32_t i = 0; i < mFloats.Length(); ++i) {
377 0 : NS_ASSERTION(!frameSet.Contains(mFloats[i].mFrame),
378 : "Frame region deletion was requested but we couldn't delete it");
379 : }
380 : #endif
381 :
382 0 : return NS_OK;
383 : }
384 :
385 : void
386 237 : nsFloatManager::PushState(SavedState* aState)
387 : {
388 237 : NS_PRECONDITION(aState, "Need a place to save state");
389 :
390 : // This is a cheap push implementation, which
391 : // only saves the (x,y) and last frame in the mFrameInfoMap
392 : // which is enough info to get us back to where we should be
393 : // when pop is called.
394 : //
395 : // This push/pop mechanism is used to undo any
396 : // floats that were added during the unconstrained reflow
397 : // in nsBlockReflowContext::DoReflowBlock(). (See bug 96736)
398 : //
399 : // It should also be noted that the state for mFloatDamage is
400 : // intentionally not saved or restored in PushState() and PopState(),
401 : // since that could lead to bugs where damage is missed/dropped when
402 : // we move from position A to B (during the intermediate incremental
403 : // reflow mentioned above) and then from B to C during the subsequent
404 : // reflow. In the typical case A and C will be the same, but not always.
405 : // Allowing mFloatDamage to accumulate the damage incurred during both
406 : // reflows ensures that nothing gets missed.
407 237 : aState->mLineLeft = mLineLeft;
408 237 : aState->mBlockStart = mBlockStart;
409 237 : aState->mPushedLeftFloatPastBreak = mPushedLeftFloatPastBreak;
410 237 : aState->mPushedRightFloatPastBreak = mPushedRightFloatPastBreak;
411 237 : aState->mSplitLeftFloatAcrossBreak = mSplitLeftFloatAcrossBreak;
412 237 : aState->mSplitRightFloatAcrossBreak = mSplitRightFloatAcrossBreak;
413 237 : aState->mFloatInfoCount = mFloats.Length();
414 237 : }
415 :
416 : void
417 0 : nsFloatManager::PopState(SavedState* aState)
418 : {
419 0 : NS_PRECONDITION(aState, "No state to restore?");
420 :
421 0 : mLineLeft = aState->mLineLeft;
422 0 : mBlockStart = aState->mBlockStart;
423 0 : mPushedLeftFloatPastBreak = aState->mPushedLeftFloatPastBreak;
424 0 : mPushedRightFloatPastBreak = aState->mPushedRightFloatPastBreak;
425 0 : mSplitLeftFloatAcrossBreak = aState->mSplitLeftFloatAcrossBreak;
426 0 : mSplitRightFloatAcrossBreak = aState->mSplitRightFloatAcrossBreak;
427 :
428 0 : NS_ASSERTION(aState->mFloatInfoCount <= mFloats.Length(),
429 : "somebody misused PushState/PopState");
430 0 : mFloats.TruncateLength(aState->mFloatInfoCount);
431 0 : }
432 :
433 : nscoord
434 0 : nsFloatManager::GetLowestFloatTop() const
435 : {
436 0 : if (mPushedLeftFloatPastBreak || mPushedRightFloatPastBreak) {
437 0 : return nscoord_MAX;
438 : }
439 0 : if (!HasAnyFloats()) {
440 0 : return nscoord_MIN;
441 : }
442 0 : return mFloats[mFloats.Length() -1].BStart() - mBlockStart;
443 : }
444 :
445 : #ifdef DEBUG_FRAME_DUMP
446 : void
447 0 : DebugListFloatManager(const nsFloatManager *aFloatManager)
448 : {
449 0 : aFloatManager->List(stdout);
450 0 : }
451 :
452 : nsresult
453 0 : nsFloatManager::List(FILE* out) const
454 : {
455 0 : if (!HasAnyFloats())
456 0 : return NS_OK;
457 :
458 0 : for (uint32_t i = 0; i < mFloats.Length(); ++i) {
459 0 : const FloatInfo &fi = mFloats[i];
460 0 : fprintf_stderr(out, "Float %u: frame=%p rect={%d,%d,%d,%d} BEnd={l:%d, r:%d}\n",
461 0 : i, static_cast<void*>(fi.mFrame),
462 : fi.LineLeft(), fi.BStart(), fi.ISize(), fi.BSize(),
463 0 : fi.mLeftBEnd, fi.mRightBEnd);
464 : }
465 0 : return NS_OK;
466 : }
467 : #endif
468 :
469 : nscoord
470 0 : nsFloatManager::ClearFloats(nscoord aBCoord, StyleClear aBreakType,
471 : uint32_t aFlags) const
472 : {
473 0 : if (!(aFlags & DONT_CLEAR_PUSHED_FLOATS) && ClearContinues(aBreakType)) {
474 0 : return nscoord_MAX;
475 : }
476 0 : if (!HasAnyFloats()) {
477 0 : return aBCoord;
478 : }
479 :
480 0 : nscoord blockEnd = aBCoord + mBlockStart;
481 :
482 0 : const FloatInfo &tail = mFloats[mFloats.Length() - 1];
483 0 : switch (aBreakType) {
484 : case StyleClear::Both:
485 0 : blockEnd = std::max(blockEnd, tail.mLeftBEnd);
486 0 : blockEnd = std::max(blockEnd, tail.mRightBEnd);
487 0 : break;
488 : case StyleClear::Left:
489 0 : blockEnd = std::max(blockEnd, tail.mLeftBEnd);
490 0 : break;
491 : case StyleClear::Right:
492 0 : blockEnd = std::max(blockEnd, tail.mRightBEnd);
493 0 : break;
494 : default:
495 : // Do nothing
496 0 : break;
497 : }
498 :
499 0 : blockEnd -= mBlockStart;
500 :
501 0 : return blockEnd;
502 : }
503 :
504 : bool
505 0 : nsFloatManager::ClearContinues(StyleClear aBreakType) const
506 : {
507 0 : return ((mPushedLeftFloatPastBreak || mSplitLeftFloatAcrossBreak) &&
508 0 : (aBreakType == StyleClear::Both ||
509 0 : aBreakType == StyleClear::Left)) ||
510 0 : ((mPushedRightFloatPastBreak || mSplitRightFloatAcrossBreak) &&
511 0 : (aBreakType == StyleClear::Both ||
512 0 : aBreakType == StyleClear::Right));
513 : }
514 :
515 : /////////////////////////////////////////////////////////////////////////////
516 : // RoundedBoxShapeInfo
517 :
518 : nscoord
519 0 : nsFloatManager::RoundedBoxShapeInfo::LineLeft(const nscoord aBStart,
520 : const nscoord aBEnd) const
521 : {
522 0 : if (!mRadii) {
523 0 : return mRect.x;
524 : }
525 :
526 : nscoord lineLeftDiff =
527 0 : ComputeEllipseLineInterceptDiff(
528 0 : mRect.y, mRect.YMost(),
529 0 : mRadii[eCornerTopLeftX], mRadii[eCornerTopLeftY],
530 0 : mRadii[eCornerBottomLeftX], mRadii[eCornerBottomLeftY],
531 0 : aBStart, aBEnd);
532 0 : return mRect.x + lineLeftDiff;
533 : }
534 :
535 : nscoord
536 0 : nsFloatManager::RoundedBoxShapeInfo::LineRight(const nscoord aBStart,
537 : const nscoord aBEnd) const
538 : {
539 0 : if (!mRadii) {
540 0 : return mRect.XMost();
541 : }
542 :
543 : nscoord lineRightDiff =
544 0 : ComputeEllipseLineInterceptDiff(
545 0 : mRect.y, mRect.YMost(),
546 0 : mRadii[eCornerTopRightX], mRadii[eCornerTopRightY],
547 0 : mRadii[eCornerBottomRightX], mRadii[eCornerBottomRightY],
548 0 : aBStart, aBEnd);
549 0 : return mRect.XMost() - lineRightDiff;
550 : }
551 :
552 : /////////////////////////////////////////////////////////////////////////////
553 : // EllipseShapeInfo
554 :
555 : nscoord
556 0 : nsFloatManager::EllipseShapeInfo::LineLeft(const nscoord aBStart,
557 : const nscoord aBEnd) const
558 : {
559 : nscoord lineLeftDiff =
560 0 : ComputeEllipseLineInterceptDiff(BStart(), BEnd(),
561 0 : mRadii.width, mRadii.height,
562 0 : mRadii.width, mRadii.height,
563 0 : aBStart, aBEnd);
564 0 : return mCenter.x - mRadii.width + lineLeftDiff;
565 : }
566 :
567 : nscoord
568 0 : nsFloatManager::EllipseShapeInfo::LineRight(const nscoord aBStart,
569 : const nscoord aBEnd) const
570 : {
571 : nscoord lineRightDiff =
572 0 : ComputeEllipseLineInterceptDiff(BStart(), BEnd(),
573 0 : mRadii.width, mRadii.height,
574 0 : mRadii.width, mRadii.height,
575 0 : aBStart, aBEnd);
576 0 : return mCenter.x + mRadii.width - lineRightDiff;
577 : }
578 :
579 : /////////////////////////////////////////////////////////////////////////////
580 : // PolygonShapeInfo
581 :
582 0 : nsFloatManager::PolygonShapeInfo::PolygonShapeInfo(nsTArray<nsPoint>&& aVertices)
583 0 : : mVertices(aVertices)
584 : {
585 : // Polygons with fewer than three vertices result in an empty area.
586 : // https://drafts.csswg.org/css-shapes/#funcdef-polygon
587 0 : if (mVertices.Length() < 3) {
588 0 : mEmpty = true;
589 0 : return;
590 : }
591 :
592 0 : auto Determinant = [] (const nsPoint& aP0, const nsPoint& aP1) {
593 : // Returns the determinant of the 2x2 matrix [aP0 aP1].
594 : // https://en.wikipedia.org/wiki/Determinant#2_.C3.97_2_matrices
595 0 : return aP0.x * aP1.y - aP0.y * aP1.x;
596 0 : };
597 :
598 : // See if we have any vertices that are non-collinear with the first two.
599 : // (If a polygon's vertices are all collinear, it encloses no area.)
600 0 : bool isEntirelyCollinear = true;
601 0 : const nsPoint& p0 = mVertices[0];
602 0 : const nsPoint& p1 = mVertices[1];
603 0 : for (size_t i = 2; i < mVertices.Length(); ++i) {
604 0 : const nsPoint& p2 = mVertices[i];
605 :
606 : // If the determinant of the matrix formed by two points is 0, that
607 : // means they're collinear with respect to the origin. Here, if it's
608 : // nonzero, then p1 and p2 are non-collinear with respect to p0, i.e.
609 : // the three points are non-collinear.
610 0 : if (Determinant(p2 - p0, p1 - p0) != 0) {
611 0 : isEntirelyCollinear = false;
612 0 : break;
613 : }
614 : }
615 :
616 0 : if (isEntirelyCollinear) {
617 0 : mEmpty = true;
618 0 : return;
619 : }
620 :
621 : // mBStart and mBEnd are the lower and the upper bounds of all the
622 : // vertex.y, respectively. The vertex.y is actually on the block-axis of
623 : // the float manager's writing mode.
624 0 : for (const nsPoint& vertex : mVertices) {
625 0 : mBStart = std::min(mBStart, vertex.y);
626 0 : mBEnd = std::max(mBEnd, vertex.y);
627 : }
628 : }
629 :
630 : nscoord
631 0 : nsFloatManager::PolygonShapeInfo::LineLeft(const nscoord aBStart,
632 : const nscoord aBEnd) const
633 : {
634 0 : MOZ_ASSERT(!mEmpty, "Shouldn't be called if the polygon encloses no area.");
635 :
636 : // We want the line-left-most inline-axis coordinate where the
637 : // (block-axis) aBStart/aBEnd band crosses a line segment of the polygon.
638 : // To get that, we start as line-right as possible (at nscoord_MAX). Then
639 : // we iterate each line segment to compute its intersection point with the
640 : // band (if any) and using std::min() successively to get the smallest
641 : // inline-coordinates among those intersection points.
642 : //
643 : // Note: std::min<nscoord> means the function std::min() with template
644 : // parameter nscoord, not the minimum value of nscoord.
645 0 : return ComputeLineIntercept(aBStart, aBEnd, std::min<nscoord>, nscoord_MAX);
646 : }
647 :
648 : nscoord
649 0 : nsFloatManager::PolygonShapeInfo::LineRight(const nscoord aBStart,
650 : const nscoord aBEnd) const
651 : {
652 0 : MOZ_ASSERT(!mEmpty, "Shouldn't be called if the polygon encloses no area.");
653 :
654 : // Similar to LineLeft(). Though here, we want the line-right-most
655 : // inline-axis coordinate, so we instead start at nscoord_MIN and use
656 : // std::max() to get the biggest inline-coordinate among those
657 : // intersection points.
658 0 : return ComputeLineIntercept(aBStart, aBEnd, std::max<nscoord>, nscoord_MIN);
659 : }
660 :
661 : nscoord
662 0 : nsFloatManager::PolygonShapeInfo::ComputeLineIntercept(
663 : const nscoord aBStart,
664 : const nscoord aBEnd,
665 : nscoord (*aCompareOp) (std::initializer_list<nscoord>),
666 : const nscoord aLineInterceptInitialValue) const
667 : {
668 0 : MOZ_ASSERT(aBStart <= aBEnd,
669 : "The band's block start is greater than its block end?");
670 :
671 0 : const size_t len = mVertices.Length();
672 0 : nscoord lineIntercept = aLineInterceptInitialValue;
673 :
674 : // Iterate each line segment {p0, p1}, {p1, p2}, ..., {pn, p0}.
675 0 : for (size_t i = 0; i < len; ++i) {
676 0 : const nsPoint* smallYVertex = &mVertices[i];
677 0 : const nsPoint* bigYVertex = &mVertices[(i + 1) % len];
678 :
679 : // Swap the two points to satisfy the requirement for calling
680 : // XInterceptAtY.
681 0 : if (smallYVertex->y > bigYVertex->y) {
682 0 : std::swap(smallYVertex, bigYVertex);
683 : }
684 :
685 0 : if (aBStart >= bigYVertex->y || aBEnd <= smallYVertex->y ||
686 0 : smallYVertex->y == bigYVertex->y) {
687 : // Skip computing the intercept if a) the band doesn't intersect the
688 : // line segment (even if it crosses one of two the vertices); or b)
689 : // the line segment is horizontal. It's OK because the two end points
690 : // forming this horizontal segment will still be considered if each of
691 : // them is forming another non-horizontal segment with other points.
692 0 : continue;
693 : }
694 :
695 : nscoord bStartLineIntercept =
696 0 : aBStart <= smallYVertex->y
697 0 : ? smallYVertex->x
698 0 : : XInterceptAtY(aBStart, *smallYVertex, *bigYVertex);
699 : nscoord bEndLineIntercept =
700 0 : aBEnd >= bigYVertex->y
701 0 : ? bigYVertex->x
702 0 : : XInterceptAtY(aBEnd, *smallYVertex, *bigYVertex);
703 :
704 : // If either new intercept is more extreme than lineIntercept (per
705 : // aCompareOp), then update lineIntercept to that value.
706 : lineIntercept =
707 0 : aCompareOp({lineIntercept, bStartLineIntercept, bEndLineIntercept});
708 : }
709 :
710 0 : return lineIntercept;
711 : }
712 :
713 : void
714 0 : nsFloatManager::PolygonShapeInfo::Translate(nscoord aLineLeft,
715 : nscoord aBlockStart)
716 : {
717 0 : for (nsPoint& vertex : mVertices) {
718 0 : vertex.MoveBy(aLineLeft, aBlockStart);
719 : }
720 0 : mBStart += aBlockStart;
721 0 : mBEnd += aBlockStart;
722 0 : }
723 :
724 : /* static */ nscoord
725 0 : nsFloatManager::PolygonShapeInfo::XInterceptAtY(const nscoord aY,
726 : const nsPoint& aP1,
727 : const nsPoint& aP2)
728 : {
729 : // Solve for x in the linear equation: x = x1 + (y-y1) * (x2-x1) / (y2-y1),
730 : // where aP1 = (x1, y1) and aP2 = (x2, y2).
731 :
732 0 : MOZ_ASSERT(aP1.y <= aY && aY <= aP2.y,
733 : "This function won't work if the horizontal line at aY and "
734 : "the line segment (aP1, aP2) do not intersect!");
735 :
736 0 : MOZ_ASSERT(aP1.y != aP2.y,
737 : "A horizontal line segment results in dividing by zero error!");
738 :
739 0 : return aP1.x + (aY - aP1.y) * (aP2.x - aP1.x) / (aP2.y - aP1.y);
740 : }
741 :
742 : /////////////////////////////////////////////////////////////////////////////
743 : // FloatInfo
744 :
745 0 : nsFloatManager::FloatInfo::FloatInfo(nsIFrame* aFrame,
746 : nscoord aLineLeft, nscoord aBlockStart,
747 : const LogicalRect& aMarginRect,
748 : WritingMode aWM,
749 0 : const nsSize& aContainerSize)
750 : : mFrame(aFrame)
751 0 : , mRect(ShapeInfo::ConvertToFloatLogical(aMarginRect, aWM, aContainerSize) +
752 0 : nsPoint(aLineLeft, aBlockStart))
753 : {
754 0 : MOZ_COUNT_CTOR(nsFloatManager::FloatInfo);
755 :
756 0 : const StyleShapeSource& shapeOutside = mFrame->StyleDisplay()->mShapeOutside;
757 :
758 0 : if (shapeOutside.GetType() == StyleShapeSourceType::None) {
759 0 : return;
760 : }
761 :
762 0 : if (shapeOutside.GetType() == StyleShapeSourceType::URL) {
763 : // Bug 1265343: Implement 'shape-image-threshold'. Early return
764 : // here because shape-outside with url() value doesn't have a
765 : // reference box, and GetReferenceBox() asserts that.
766 0 : return;
767 : }
768 :
769 : // Initialize <shape-box>'s reference rect.
770 : LogicalRect shapeBoxRect =
771 0 : ShapeInfo::ComputeShapeBoxRect(shapeOutside, mFrame, aMarginRect, aWM);
772 :
773 0 : if (shapeOutside.GetType() == StyleShapeSourceType::Box) {
774 0 : mShapeInfo = ShapeInfo::CreateShapeBox(mFrame, shapeBoxRect, aWM,
775 0 : aContainerSize);
776 0 : } else if (shapeOutside.GetType() == StyleShapeSourceType::Shape) {
777 0 : StyleBasicShape* const basicShape = shapeOutside.GetBasicShape();
778 :
779 0 : switch (basicShape->GetShapeType()) {
780 : case StyleBasicShapeType::Polygon:
781 : mShapeInfo =
782 0 : ShapeInfo::CreatePolygon(basicShape, shapeBoxRect, aWM,
783 0 : aContainerSize);
784 0 : break;
785 : case StyleBasicShapeType::Circle:
786 : case StyleBasicShapeType::Ellipse:
787 : mShapeInfo =
788 0 : ShapeInfo::CreateCircleOrEllipse(basicShape, shapeBoxRect, aWM,
789 0 : aContainerSize);
790 0 : break;
791 : case StyleBasicShapeType::Inset:
792 : mShapeInfo =
793 0 : ShapeInfo::CreateInset(basicShape, shapeBoxRect, aWM, aContainerSize);
794 0 : break;
795 : }
796 : } else {
797 0 : MOZ_ASSERT_UNREACHABLE("Unknown StyleShapeSourceType!");
798 : }
799 :
800 0 : MOZ_ASSERT(mShapeInfo,
801 : "All shape-outside values except none should have mShapeInfo!");
802 :
803 : // Translate the shape to the same origin as nsFloatManager.
804 0 : mShapeInfo->Translate(aLineLeft, aBlockStart);
805 : }
806 :
807 : #ifdef NS_BUILD_REFCNT_LOGGING
808 0 : nsFloatManager::FloatInfo::FloatInfo(FloatInfo&& aOther)
809 0 : : mFrame(Move(aOther.mFrame))
810 0 : , mLeftBEnd(Move(aOther.mLeftBEnd))
811 0 : , mRightBEnd(Move(aOther.mRightBEnd))
812 0 : , mRect(Move(aOther.mRect))
813 0 : , mShapeInfo(Move(aOther.mShapeInfo))
814 : {
815 0 : MOZ_COUNT_CTOR(nsFloatManager::FloatInfo);
816 0 : }
817 :
818 0 : nsFloatManager::FloatInfo::~FloatInfo()
819 : {
820 0 : MOZ_COUNT_DTOR(nsFloatManager::FloatInfo);
821 0 : }
822 : #endif
823 :
824 : nscoord
825 0 : nsFloatManager::FloatInfo::LineLeft(ShapeType aShapeType,
826 : const nscoord aBStart,
827 : const nscoord aBEnd) const
828 : {
829 0 : if (aShapeType == ShapeType::Margin) {
830 0 : return LineLeft();
831 : }
832 :
833 0 : MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
834 0 : if (!mShapeInfo) {
835 0 : return LineLeft();
836 : }
837 : // Clip the flow area to the margin-box because
838 : // https://drafts.csswg.org/css-shapes-1/#relation-to-box-model-and-float-behavior
839 : // says "When a shape is used to define a float area, the shape is clipped
840 : // to the float’s margin box."
841 0 : return std::max(LineLeft(), mShapeInfo->LineLeft(aBStart, aBEnd));
842 : }
843 :
844 : nscoord
845 0 : nsFloatManager::FloatInfo::LineRight(ShapeType aShapeType,
846 : const nscoord aBStart,
847 : const nscoord aBEnd) const
848 : {
849 0 : if (aShapeType == ShapeType::Margin) {
850 0 : return LineRight();
851 : }
852 :
853 0 : MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
854 0 : if (!mShapeInfo) {
855 0 : return LineRight();
856 : }
857 : // Clip the flow area to the margin-box. See LineLeft().
858 0 : return std::min(LineRight(), mShapeInfo->LineRight(aBStart, aBEnd));
859 : }
860 :
861 : nscoord
862 0 : nsFloatManager::FloatInfo::BStart(ShapeType aShapeType) const
863 : {
864 0 : if (aShapeType == ShapeType::Margin) {
865 0 : return BStart();
866 : }
867 :
868 0 : MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
869 0 : if (!mShapeInfo) {
870 0 : return BStart();
871 : }
872 : // Clip the flow area to the margin-box. See LineLeft().
873 0 : return std::max(BStart(), mShapeInfo->BStart());
874 : }
875 :
876 : nscoord
877 0 : nsFloatManager::FloatInfo::BEnd(ShapeType aShapeType) const
878 : {
879 0 : if (aShapeType == ShapeType::Margin) {
880 0 : return BEnd();
881 : }
882 :
883 0 : MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
884 0 : if (!mShapeInfo) {
885 0 : return BEnd();
886 : }
887 : // Clip the flow area to the margin-box. See LineLeft().
888 0 : return std::min(BEnd(), mShapeInfo->BEnd());
889 : }
890 :
891 : bool
892 0 : nsFloatManager::FloatInfo::IsEmpty(ShapeType aShapeType) const
893 : {
894 0 : if (aShapeType == ShapeType::Margin) {
895 0 : return IsEmpty();
896 : }
897 :
898 0 : MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
899 0 : if (!mShapeInfo) {
900 0 : return IsEmpty();
901 : }
902 0 : return mShapeInfo->IsEmpty();
903 : }
904 :
905 : /////////////////////////////////////////////////////////////////////////////
906 : // ShapeInfo
907 :
908 : /* static */ LogicalRect
909 0 : nsFloatManager::ShapeInfo::ComputeShapeBoxRect(
910 : const StyleShapeSource& aShapeOutside,
911 : nsIFrame* const aFrame,
912 : const mozilla::LogicalRect& aMarginRect,
913 : mozilla::WritingMode aWM)
914 : {
915 0 : LogicalRect rect = aMarginRect;
916 :
917 0 : switch (aShapeOutside.GetReferenceBox()) {
918 : case StyleGeometryBox::ContentBox:
919 0 : rect.Deflate(aWM, aFrame->GetLogicalUsedPadding(aWM));
920 : MOZ_FALLTHROUGH;
921 : case StyleGeometryBox::PaddingBox:
922 0 : rect.Deflate(aWM, aFrame->GetLogicalUsedBorder(aWM));
923 : MOZ_FALLTHROUGH;
924 : case StyleGeometryBox::BorderBox:
925 0 : rect.Deflate(aWM, aFrame->GetLogicalUsedMargin(aWM));
926 0 : break;
927 : case StyleGeometryBox::MarginBox:
928 : // Do nothing. rect is already a margin rect.
929 0 : break;
930 : case StyleGeometryBox::NoBox:
931 : default:
932 0 : MOZ_ASSERT(aShapeOutside.GetType() != StyleShapeSourceType::Box,
933 : "Box source type must have <shape-box> specified!");
934 0 : break;
935 : }
936 :
937 0 : return rect;
938 : }
939 :
940 : /* static */ UniquePtr<nsFloatManager::ShapeInfo>
941 0 : nsFloatManager::ShapeInfo::CreateShapeBox(
942 : nsIFrame* const aFrame,
943 : const LogicalRect& aShapeBoxRect,
944 : WritingMode aWM,
945 : const nsSize& aContainerSize)
946 : {
947 : nsRect logicalShapeBoxRect
948 0 : = ConvertToFloatLogical(aShapeBoxRect, aWM, aContainerSize);
949 :
950 : nscoord physicalRadii[8];
951 0 : bool hasRadii = aFrame->GetShapeBoxBorderRadii(physicalRadii);
952 0 : if (!hasRadii) {
953 0 : return MakeUnique<RoundedBoxShapeInfo>(logicalShapeBoxRect,
954 0 : UniquePtr<nscoord[]>());
955 : }
956 :
957 0 : return MakeUnique<RoundedBoxShapeInfo>(logicalShapeBoxRect,
958 0 : ConvertToFloatLogical(physicalRadii,
959 0 : aWM));
960 : }
961 :
962 : /* static */ UniquePtr<nsFloatManager::ShapeInfo>
963 0 : nsFloatManager::ShapeInfo::CreateInset(
964 : const StyleBasicShape* aBasicShape,
965 : const LogicalRect& aShapeBoxRect,
966 : WritingMode aWM,
967 : const nsSize& aContainerSize)
968 : {
969 : // Use physical coordinates to compute inset() because the top, right,
970 : // bottom and left offsets are physical.
971 : // https://drafts.csswg.org/css-shapes-1/#funcdef-inset
972 : nsRect physicalShapeBoxRect =
973 0 : aShapeBoxRect.GetPhysicalRect(aWM, aContainerSize);
974 : nsRect insetRect =
975 0 : ShapeUtils::ComputeInsetRect(aBasicShape, physicalShapeBoxRect);
976 :
977 : nsRect logicalInsetRect =
978 0 : ConvertToFloatLogical(LogicalRect(aWM, insetRect, aContainerSize),
979 0 : aWM, aContainerSize);
980 : nscoord physicalRadii[8];
981 : bool hasRadii =
982 : ShapeUtils::ComputeInsetRadii(aBasicShape, insetRect, physicalShapeBoxRect,
983 0 : physicalRadii);
984 0 : if (!hasRadii) {
985 0 : return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
986 0 : UniquePtr<nscoord[]>());
987 : }
988 :
989 0 : return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
990 0 : ConvertToFloatLogical(physicalRadii,
991 0 : aWM));
992 : }
993 :
994 : /* static */ UniquePtr<nsFloatManager::ShapeInfo>
995 0 : nsFloatManager::ShapeInfo::CreateCircleOrEllipse(
996 : const StyleBasicShape* aBasicShape,
997 : const LogicalRect& aShapeBoxRect,
998 : WritingMode aWM,
999 : const nsSize& aContainerSize)
1000 : {
1001 : // Use physical coordinates to compute the center of circle() or ellipse()
1002 : // since the <position> keywords such as 'left', 'top', etc. are physical.
1003 : // https://drafts.csswg.org/css-shapes-1/#funcdef-ellipse
1004 : nsRect physicalShapeBoxRect =
1005 0 : aShapeBoxRect.GetPhysicalRect(aWM, aContainerSize);
1006 : nsPoint physicalCenter =
1007 0 : ShapeUtils::ComputeCircleOrEllipseCenter(aBasicShape, physicalShapeBoxRect);
1008 : nsPoint logicalCenter =
1009 0 : ConvertToFloatLogical(physicalCenter, aWM, aContainerSize);
1010 :
1011 : // Compute the circle or ellipse radii.
1012 0 : nsSize radii;
1013 0 : StyleBasicShapeType type = aBasicShape->GetShapeType();
1014 0 : if (type == StyleBasicShapeType::Circle) {
1015 : nscoord radius = ShapeUtils::ComputeCircleRadius(aBasicShape, physicalCenter,
1016 0 : physicalShapeBoxRect);
1017 0 : radii = nsSize(radius, radius);
1018 : } else {
1019 0 : MOZ_ASSERT(type == StyleBasicShapeType::Ellipse);
1020 : nsSize physicalRadii =
1021 : ShapeUtils::ComputeEllipseRadii(aBasicShape, physicalCenter,
1022 0 : physicalShapeBoxRect);
1023 0 : LogicalSize logicalRadii(aWM, physicalRadii);
1024 0 : radii = nsSize(logicalRadii.ISize(aWM), logicalRadii.BSize(aWM));
1025 : }
1026 :
1027 0 : return MakeUnique<EllipseShapeInfo>(logicalCenter, radii);
1028 : }
1029 :
1030 : /* static */ UniquePtr<nsFloatManager::ShapeInfo>
1031 0 : nsFloatManager::ShapeInfo::CreatePolygon(
1032 : const StyleBasicShape* aBasicShape,
1033 : const LogicalRect& aShapeBoxRect,
1034 : WritingMode aWM,
1035 : const nsSize& aContainerSize)
1036 : {
1037 : // Use physical coordinates to compute each (xi, yi) vertex because CSS
1038 : // represents them using physical coordinates.
1039 : // https://drafts.csswg.org/css-shapes-1/#funcdef-polygon
1040 : nsRect physicalShapeBoxRect =
1041 0 : aShapeBoxRect.GetPhysicalRect(aWM, aContainerSize);
1042 :
1043 : // Get physical vertices.
1044 : nsTArray<nsPoint> vertices =
1045 0 : ShapeUtils::ComputePolygonVertices(aBasicShape, physicalShapeBoxRect);
1046 :
1047 : // Convert all the physical vertices to logical.
1048 0 : for (nsPoint& vertex : vertices) {
1049 0 : vertex = ConvertToFloatLogical(vertex, aWM, aContainerSize);
1050 : }
1051 :
1052 0 : return MakeUnique<PolygonShapeInfo>(Move(vertices));
1053 : }
1054 :
1055 : /* static */ nscoord
1056 0 : nsFloatManager::ShapeInfo::ComputeEllipseLineInterceptDiff(
1057 : const nscoord aShapeBoxBStart, const nscoord aShapeBoxBEnd,
1058 : const nscoord aBStartCornerRadiusL, const nscoord aBStartCornerRadiusB,
1059 : const nscoord aBEndCornerRadiusL, const nscoord aBEndCornerRadiusB,
1060 : const nscoord aBandBStart, const nscoord aBandBEnd)
1061 : {
1062 : // An example for the band intersecting with the top right corner of an
1063 : // ellipse with writing-mode horizontal-tb.
1064 : //
1065 : // lineIntercept lineDiff
1066 : // | |
1067 : // +---------------------------------|-------|-+---- aShapeBoxBStart
1068 : // | ##########^ | | |
1069 : // | ##############|#### | | |
1070 : // +---------#################|######|-------|-+---- aBandBStart
1071 : // | ###################|######|## | |
1072 : // | aBStartCornerRadiusB |######|### | |
1073 : // | ######################|######|##### | |
1074 : // +---#######################|<-----------><->^---- aBandBEnd
1075 : // | ########################|############## |
1076 : // | ########################|############## |---- b
1077 : // | #########################|############### |
1078 : // | ######################## v<-------------->v
1079 : // |###################### aBStartCornerRadiusL|
1080 : // |###########################################|
1081 : // |###########################################|
1082 : // |###########################################|
1083 : // |###########################################|
1084 : // | ######################################### |
1085 : // | ######################################### |
1086 : // | ####################################### |
1087 : // | ####################################### |
1088 : // | ##################################### |
1089 : // | ################################### |
1090 : // | ############################### |
1091 : // | ############################# |
1092 : // | ######################### |
1093 : // | ################### |
1094 : // | ########### |
1095 : // +-------------------------------------------+----- aShapeBoxBEnd
1096 :
1097 0 : NS_ASSERTION(aShapeBoxBStart <= aShapeBoxBEnd, "Bad shape box coordinates!");
1098 0 : NS_ASSERTION(aBandBStart <= aBandBEnd, "Bad band coordinates!");
1099 :
1100 0 : nscoord lineDiff = 0;
1101 :
1102 : // If the band intersects both the block-start and block-end corners, we
1103 : // don't need to enter either branch because the correct lineDiff is 0.
1104 0 : if (aBStartCornerRadiusB > 0 &&
1105 0 : aBandBEnd >= aShapeBoxBStart &&
1106 0 : aBandBEnd <= aShapeBoxBStart + aBStartCornerRadiusB) {
1107 : // The band intersects only the block-start corner.
1108 0 : nscoord b = aBStartCornerRadiusB - (aBandBEnd - aShapeBoxBStart);
1109 : nscoord lineIntercept =
1110 0 : XInterceptAtY(b, aBStartCornerRadiusL, aBStartCornerRadiusB);
1111 0 : lineDiff = aBStartCornerRadiusL - lineIntercept;
1112 0 : } else if (aBEndCornerRadiusB > 0 &&
1113 0 : aBandBStart >= aShapeBoxBEnd - aBEndCornerRadiusB &&
1114 : aBandBStart <= aShapeBoxBEnd) {
1115 : // The band intersects only the block-end corner.
1116 0 : nscoord b = aBEndCornerRadiusB - (aShapeBoxBEnd - aBandBStart);
1117 : nscoord lineIntercept =
1118 0 : XInterceptAtY(b, aBEndCornerRadiusL, aBEndCornerRadiusB);
1119 0 : lineDiff = aBEndCornerRadiusL - lineIntercept;
1120 : }
1121 :
1122 0 : return lineDiff;
1123 : }
1124 :
1125 : /* static */ nscoord
1126 0 : nsFloatManager::ShapeInfo::XInterceptAtY(const nscoord aY,
1127 : const nscoord aRadiusX,
1128 : const nscoord aRadiusY)
1129 : {
1130 : // Solve for x in the ellipse equation (x/radiusX)^2 + (y/radiusY)^2 = 1.
1131 0 : MOZ_ASSERT(aRadiusY > 0);
1132 0 : return aRadiusX * std::sqrt(1 - (aY * aY) / double(aRadiusY * aRadiusY));
1133 : }
1134 :
1135 : /* static */ nsPoint
1136 0 : nsFloatManager::ShapeInfo::ConvertToFloatLogical(
1137 : const nsPoint& aPoint,
1138 : WritingMode aWM,
1139 : const nsSize& aContainerSize)
1140 : {
1141 0 : LogicalPoint logicalPoint(aWM, aPoint, aContainerSize);
1142 : return nsPoint(logicalPoint.LineRelative(aWM, aContainerSize),
1143 0 : logicalPoint.B(aWM));
1144 : }
1145 :
1146 : /* static */ UniquePtr<nscoord[]>
1147 0 : nsFloatManager::ShapeInfo::ConvertToFloatLogical(const nscoord aRadii[8],
1148 : WritingMode aWM)
1149 : {
1150 0 : UniquePtr<nscoord[]> logicalRadii(new nscoord[8]);
1151 :
1152 : // Get the physical side for line-left and line-right since border radii
1153 : // are on the physical axis.
1154 : Side lineLeftSide =
1155 0 : aWM.PhysicalSide(aWM.LogicalSideForLineRelativeDir(eLineRelativeDirLeft));
1156 0 : logicalRadii[eCornerTopLeftX] =
1157 0 : aRadii[SideToHalfCorner(lineLeftSide, true, false)];
1158 0 : logicalRadii[eCornerTopLeftY] =
1159 0 : aRadii[SideToHalfCorner(lineLeftSide, true, true)];
1160 0 : logicalRadii[eCornerBottomLeftX] =
1161 0 : aRadii[SideToHalfCorner(lineLeftSide, false, false)];
1162 0 : logicalRadii[eCornerBottomLeftY] =
1163 0 : aRadii[SideToHalfCorner(lineLeftSide, false, true)];
1164 :
1165 : Side lineRightSide =
1166 0 : aWM.PhysicalSide(aWM.LogicalSideForLineRelativeDir(eLineRelativeDirRight));
1167 0 : logicalRadii[eCornerTopRightX] =
1168 0 : aRadii[SideToHalfCorner(lineRightSide, false, false)];
1169 0 : logicalRadii[eCornerTopRightY] =
1170 0 : aRadii[SideToHalfCorner(lineRightSide, false, true)];
1171 0 : logicalRadii[eCornerBottomRightX] =
1172 0 : aRadii[SideToHalfCorner(lineRightSide, true, false)];
1173 0 : logicalRadii[eCornerBottomRightY] =
1174 0 : aRadii[SideToHalfCorner(lineRightSide, true, true)];
1175 :
1176 0 : if (aWM.IsLineInverted()) {
1177 : // When IsLineInverted() is true, i.e. aWM is vertical-lr,
1178 : // line-over/line-under are inverted from block-start/block-end. So the
1179 : // relationship reverses between which corner comes first going
1180 : // clockwise, and which corner is block-start versus block-end. We need
1181 : // to swap the values stored in top and bottom corners.
1182 0 : std::swap(logicalRadii[eCornerTopLeftX], logicalRadii[eCornerBottomLeftX]);
1183 0 : std::swap(logicalRadii[eCornerTopLeftY], logicalRadii[eCornerBottomLeftY]);
1184 0 : std::swap(logicalRadii[eCornerTopRightX], logicalRadii[eCornerBottomRightX]);
1185 0 : std::swap(logicalRadii[eCornerTopRightY], logicalRadii[eCornerBottomRightY]);
1186 : }
1187 :
1188 0 : return logicalRadii;
1189 : }
1190 :
1191 : //----------------------------------------------------------------------
1192 :
1193 324 : nsAutoFloatManager::~nsAutoFloatManager()
1194 : {
1195 : // Restore the old float manager in the reflow input if necessary.
1196 162 : if (mNew) {
1197 : #ifdef DEBUG
1198 152 : if (nsBlockFrame::gNoisyFloatManager) {
1199 0 : printf("restoring old float manager %p\n", mOld);
1200 : }
1201 : #endif
1202 :
1203 152 : mReflowInput.mFloatManager = mOld;
1204 :
1205 : #ifdef DEBUG
1206 152 : if (nsBlockFrame::gNoisyFloatManager) {
1207 0 : if (mOld) {
1208 0 : mReflowInput.mFrame->ListTag(stdout);
1209 0 : printf(": float manager %p after reflow\n", mOld);
1210 0 : mOld->List(stdout);
1211 : }
1212 : }
1213 : #endif
1214 : }
1215 162 : }
1216 :
1217 : void
1218 152 : nsAutoFloatManager::CreateFloatManager(nsPresContext *aPresContext)
1219 : {
1220 152 : MOZ_ASSERT(!mNew, "Redundant call to CreateFloatManager!");
1221 :
1222 : // Create a new float manager and install it in the reflow
1223 : // input. `Remember' the old float manager so we can restore it
1224 : // later.
1225 304 : mNew = MakeUnique<nsFloatManager>(aPresContext->PresShell(),
1226 456 : mReflowInput.GetWritingMode());
1227 :
1228 : #ifdef DEBUG
1229 152 : if (nsBlockFrame::gNoisyFloatManager) {
1230 0 : printf("constructed new float manager %p (replacing %p)\n",
1231 0 : mNew.get(), mReflowInput.mFloatManager);
1232 : }
1233 : #endif
1234 :
1235 : // Set the float manager in the existing reflow input.
1236 152 : mOld = mReflowInput.mFloatManager;
1237 152 : mReflowInput.mFloatManager = mNew.get();
1238 152 : }
|