From d40acb44125124bf56a37d9b2002f784c69bfb7d Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Fri, 9 Mar 2018 05:27:15 +0100 Subject: [PATCH 01/25] Bug 1440753: Replace pixman regions with our own region code. r=mattwoodrow MozReview-Commit-ID: KPsTAw3Uwa2 --- gfx/layers/client/ContentClient.cpp | 2 + gfx/src/nsRect.h | 4 + gfx/src/nsRegion.cpp | 1038 ++++++------ gfx/src/nsRegion.h | 2102 +++++++++++++++++++++++-- gfx/tests/gtest/TestRegion.cpp | 720 +++++++++ layout/painting/FrameLayerBuilder.cpp | 2 +- layout/svg/nsFilterInstance.cpp | 3 +- 7 files changed, 3121 insertions(+), 750 deletions(-) diff --git a/gfx/layers/client/ContentClient.cpp b/gfx/layers/client/ContentClient.cpp index f02f0e210451..fbfbdcdd068e 100644 --- a/gfx/layers/client/ContentClient.cpp +++ b/gfx/layers/client/ContentClient.cpp @@ -231,6 +231,8 @@ ContentClient::BeginPaint(PaintedLayer* aLayer, } } + MOZ_ASSERT(dest.mBufferRect.Contains(result.mRegionToDraw.GetBounds())); + NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || dest.mBufferRect == dest.mNeededRegion.GetBounds(), "If we're resampling, we need to validate the entire buffer"); diff --git a/gfx/src/nsRect.h b/gfx/src/nsRect.h index 5cba0753cd3f..ccec32aed7ef 100644 --- a/gfx/src/nsRect.h +++ b/gfx/src/nsRect.h @@ -112,6 +112,10 @@ struct nsRect : { return SaturatingUnion(aRect); } + MOZ_MUST_USE nsRect UnsafeUnion(const nsRect& aRect) const + { + return Super::Union(aRect); + } void UnionRect(const nsRect& aRect1, const nsRect& aRect2) { *this = aRect1.Union(aRect2); diff --git a/gfx/src/nsRegion.cpp b/gfx/src/nsRegion.cpp index 35d4ef0b4695..686427c4aaae 100644 --- a/gfx/src/nsRegion.cpp +++ b/gfx/src/nsRegion.cpp @@ -10,6 +10,73 @@ #include "gfxUtils.h" #include "mozilla/ToString.h" +using namespace std; + +void +nsRegion::AssertStateInternal() const +{ + bool failed = false; + // Verify consistent state inside the region. + int32_t lastY = INT32_MIN; + int32_t lowestX = INT32_MAX; + int32_t highestX = INT32_MIN; + for (auto iter = mBands.begin(); iter != mBands.end(); iter++) { + const Band& band = *iter; + if (band.bottom <= band.top) { + failed = true; + break; + } + if (band.top < lastY) { + failed = true; + break; + } + lastY = band.bottom; + + lowestX = std::min(lowestX, band.mStrips.begin()->left); + highestX = std::max(highestX, band.mStrips.LastElement().right); + + int32_t lastX = INT32_MIN; + if (iter != mBands.begin()) { + auto prev = iter; + prev--; + + if (prev->bottom == iter->top) { + if (band.EqualStrips(*prev)) { + failed = true; + break; + } + } + } + for (const Strip& strip : band.mStrips) { + if (strip.right <= strip.left) { + failed = true; + break; + } + if (strip.left <= lastX) { + failed = true; + break; + } + lastX = strip.right; + } + if (failed) { + break; + } + } + + if (!(mBounds == CalculateBounds())) { + failed = true; + } + + if (failed) { +#ifdef DEBUG_REGIONS + if (mCurrentOpGenerator) { + mCurrentOpGenerator->OutputOp(); + } +#endif + MOZ_ASSERT(false); + } +} + bool nsRegion::Contains(const nsRegion& aRgn) const { // XXX this could be made faster by iterating over @@ -24,76 +91,92 @@ bool nsRegion::Contains(const nsRegion& aRgn) const bool nsRegion::Intersects(const nsRect& aRect) const { - // XXX this could be made faster by using pixman_region32_contains_rect - for (auto iter = RectIter(); !iter.Done(); iter.Next()) { - if (iter.Get().Intersects(aRect)) { - return true; - } + if (mBands.IsEmpty()) { + return mBounds.Intersects(aRect); } + + if (!mBounds.Intersects(aRect)) { + return false; + } + + Strip rectStrip(aRect.X(), aRect.XMost()); + + auto iter = mBands.begin(); + while (iter != mBands.end()) { + if (iter->top >= aRect.YMost()) { + return false; + } + + if (iter->bottom <= aRect.Y()) { + // This band is entirely before aRect, move on. + iter++; + continue; + } + + if (!iter->Intersects(rectStrip)) { + // This band does not intersect aRect horizontally. Move on. + iter++; + continue; + } + + // This band intersects with aRect. + return true; + } + return false; } void nsRegion::Inflate(const nsMargin& aMargin) { - int n; - pixman_box32_t *boxes = pixman_region32_rectangles(&mImpl, &n); - for (int i=0; i= 1, "Invalid max rect count"); - if (GetNumRects() <= aMaxRects) + if (GetNumRects() <= aMaxRects) { return; - - pixman_box32_t *boxes; - int n; - boxes = pixman_region32_rectangles(&mImpl, &n); + } // Try combining rects in horizontal bands into a single rect - int dest = 0; - for (int src = 1; src < n; src++) - { - // The goal here is to try to keep groups of rectangles that are vertically - // discontiguous as separate rectangles in the final region. This is - // simple and fast to implement and page contents tend to vary more - // vertically than horizontally (which is why our rectangles are stored - // sorted by y-coordinate, too). - // - // Note: if boxes share y1 because of the canonical representation they - // will share y2 - while ((src < (n)) && boxes[dest].y1 == boxes[src].y1) { - // merge box[i] and box[i+1] - boxes[dest].x2 = boxes[src].x2; - src++; - } - if (src < n) { - dest++; - boxes[dest] = boxes[src]; + // The goal here is to try to keep groups of rectangles that are vertically + // discontiguous as separate rectangles in the final region. This is + // simple and fast to implement and page contents tend to vary more + // vertically than horizontally (which is why our rectangles are stored + // sorted by y-coordinate, too). + // + // Note: if boxes share y1 because of the canonical representation they + // will share y2 + + size_t idx = 0; + + while (idx < mBands.Length()) { + size_t oldIdx = idx; + mBands[idx].mStrips.begin()->right = mBands[idx].mStrips.LastElement().right; + mBands[idx].mStrips.TruncateLength(1); + idx++; + + // Merge any bands with the same bounds. + while (idx < mBands.Length() && + mBands[idx].mStrips.begin()->left == mBands[oldIdx].mStrips.begin()->left && + mBands[idx].mStrips.LastElement().right == mBands[oldIdx].mStrips.begin()->right) { + mBands[oldIdx].bottom = mBands[idx].bottom; + mBands.RemoveElementAt(idx); } } - uint32_t reducedCount = dest+1; - // pixman has a special representation for - // regions of 1 rectangle. So just use the - // bounds in that case - if (reducedCount > 1 && reducedCount <= aMaxRects) { - // reach into pixman and lower the number - // of rects stored in data. - mImpl.data->numRects = reducedCount; - } else { + AssertState(); + + // mBands.size() is now equal to our rect count. + if (mBands.Length() > aMaxRects) { *this = GetBounds(); } } @@ -102,443 +185,271 @@ void nsRegion::SimplifyOutward (uint32_t aMaxRects) // by iterating over both rows simultaneously and adding up // the additional increase in area caused by extending each // of the rectangles to the combined height of both rows -static uint32_t ComputeMergedAreaIncrease(pixman_box32_t *topRects, - pixman_box32_t *topRectsEnd, - pixman_box32_t *bottomRects, - pixman_box32_t *bottomRectsEnd) +uint32_t +nsRegion::ComputeMergedAreaIncrease(const Band& aTopBand, + const Band& aBottomBand) { uint32_t totalArea = 0; - struct pt { - int32_t x, y; - }; + uint32_t topHeight = aBottomBand.top - aTopBand.top; + uint32_t bottomHeight = aBottomBand.bottom - aTopBand.bottom; + uint32_t currentStripBottom = 0; - pt *i = (pt*)topRects; - pt *end_i = (pt*)topRectsEnd; - pt *j = (pt*)bottomRects; - pt *end_j = (pt*)bottomRectsEnd; - bool top = false; - bool bottom = false; - - int cur_x = i->x; - bool top_next = top; - bool bottom_next = bottom; - //XXX: we could probably simplify this condition and perhaps move it into the loop below - if (j->x < cur_x) { - cur_x = j->x; - j++; - bottom_next = !bottom; - } else if (j->x == cur_x) { - i++; - top_next = !top; - bottom_next = !bottom; - j++; - } else { - top_next = !top; - i++; - } - - int topRectsHeight = topRects->y2 - topRects->y1; - int bottomRectsHeight = bottomRects->y2 - bottomRects->y1; - int inbetweenHeight = bottomRects->y1 - topRects->y2; - int width = cur_x; - // top and bottom are the in-status to the left of cur_x - do { - if (top && !bottom) { - totalArea += (inbetweenHeight+bottomRectsHeight)*width; - } else if (bottom && !top) { - totalArea += (inbetweenHeight+topRectsHeight)*width; - } else if (bottom && top) { - totalArea += (inbetweenHeight)*width; + // This could be done with slightly better worse case performance by merging these two + // for-loops, but this makes the code a lot easier to understand. + for (auto& strip : aTopBand.mStrips) { + if (currentStripBottom == aBottomBand.mStrips.Length() || strip.right < aBottomBand.mStrips[currentStripBottom].left) { + totalArea += bottomHeight * strip.Size(); + continue; } - top = top_next; - bottom = bottom_next; - // find the next edge - if (i->x < j->x) { - top_next = !top; - width = i->x - cur_x; - cur_x = i->x; - i++; - } else if (j->x < i->x) { - bottom_next = !bottom; - width = j->x - cur_x; - cur_x = j->x; - j++; - } else { // i->x == j->x - top_next = !top; - bottom_next = !bottom; - width = i->x - cur_x; - cur_x = i->x; - i++; - j++; + + int32_t currentX = strip.left; + while (currentStripBottom != aBottomBand.mStrips.Length() && aBottomBand.mStrips[currentStripBottom].left < strip.right) { + if (currentX >= strip.right) { + break; + } + if (currentX < aBottomBand.mStrips[currentStripBottom].left) { + // Add the part that's not intersecting. + totalArea += (aBottomBand.mStrips[currentStripBottom].left - currentX) * bottomHeight; + } + + currentX = std::max(aBottomBand.mStrips[currentStripBottom].right, currentX); + currentStripBottom++; } - } while (i < end_i && j < end_j); - // handle any remaining rects - while (i < end_i) { - width = i->x - cur_x; - cur_x = i->x; - i++; - if (top) - totalArea += (inbetweenHeight+bottomRectsHeight)*width; - top = !top; + // Add remainder of this strip. + if (currentX < strip.right) { + totalArea += (strip.right - currentX) * bottomHeight; + } + if (currentStripBottom) { + currentStripBottom--; + } } + uint32_t currentStripTop = 0; + for (auto& strip : aBottomBand.mStrips) { + if (currentStripTop == aTopBand.mStrips.Length() || strip.right < aTopBand.mStrips[currentStripTop].left) { + totalArea += topHeight * strip.Size(); + continue; + } - while (j < end_j) { - width = j->x - cur_x; - cur_x = j->x; - j++; - if (bottom) - totalArea += (inbetweenHeight+topRectsHeight)*width; - bottom = !bottom; + int32_t currentX = strip.left; + while (currentStripTop != aTopBand.mStrips.Length() && aTopBand.mStrips[currentStripTop].left < strip.right) { + if (currentX >= strip.right) { + break; + } + if (currentX < aTopBand.mStrips[currentStripTop].left) { + // Add the part that's not intersecting. + totalArea += (aTopBand.mStrips[currentStripTop].left - currentX) * topHeight; + } + + currentX = std::max(aTopBand.mStrips[currentStripTop].right, currentX); + currentStripTop++; + } + + // Add remainder of this strip. + if (currentX < strip.right) { + totalArea += (strip.right - currentX) * topHeight; + } + if (currentStripTop) { + currentStripTop--; + } } return totalArea; } -static pixman_box32_t * -CopyRow(pixman_box32_t *dest_it, pixman_box32_t *src_start, pixman_box32_t *src_end) -{ - // XXX: std::copy - pixman_box32_t *src_it = src_start; - while (src_it < src_end) { - *dest_it++ = *src_it++; - } - return dest_it; -} - - -#define WRITE_RECT(x1, x2, y1, y2) \ - do { \ - tmpRect->x1 = x1; \ - tmpRect->x2 = x2; \ - tmpRect->y1 = y1; \ - tmpRect->y2 = y2; \ - tmpRect++; \ - } while (0) - -/* If 'r' overlaps the current rect, then expand the current rect to include - * it. Otherwise write the current rect out to tmpRect, and set r as the - * updated current rect. */ -#define MERGE_RECT(r) \ - do { \ - if (r->x1 <= x2) { \ - if (x2 < r->x2) \ - x2 = r->x2; \ - } else { \ - WRITE_RECT(x1, x2, y1, y2); \ - x1 = r->x1; \ - x2 = r->x2; \ - } \ - r++; \ - } while (0) - - -/* Can we merge two sets of rects without extra space? - * Yes, but not easily. We can even do it stably - * but we don't need that property. - * - * This is written in the style of pixman_region_union_o */ -static pixman_box32_t * -MergeRects(pixman_box32_t *r1, - pixman_box32_t *r1_end, - pixman_box32_t *r2, - pixman_box32_t *r2_end, - pixman_box32_t *tmpRect) -{ - /* This routine works by maintaining the current - * rectangle in x1,x2,y1,y2 and either merging - * in the left most rectangle if it overlaps or - * outputing the current rectangle and setting - * it to the the left most one */ - const int y1 = r1->y1; - const int y2 = r2->y2; - int x1; - int x2; - - /* Find the left-most edge */ - if (r1->x1 < r2->x1) { - x1 = r1->x1; - x2 = r1->x2; - r1++; - } else { - x1 = r2->x1; - x2 = r2->x2; - r2++; - } - - while (r1 != r1_end && r2 != r2_end) { - /* Find and merge the left-most rectangle */ - if (r1->x1 < r2->x1) - MERGE_RECT (r1); - else - MERGE_RECT (r2); - } - - /* Finish up any left overs */ - if (r1 != r1_end) { - do { - MERGE_RECT (r1); - } while (r1 != r1_end); - } else if (r2 != r2_end) { - do { - MERGE_RECT(r2); - } while (r2 != r2_end); - } - - /* Finish up the last rectangle */ - WRITE_RECT(x1, x2, y1, y2); - - return tmpRect; -} - void nsRegion::SimplifyOutwardByArea(uint32_t aThreshold) { - - pixman_box32_t *boxes; - int n; - boxes = pixman_region32_rectangles(&mImpl, &n); - - // if we have no rectangles then we're done - if (!n) + if (mBands.Length() < 2) { + // We have only one or no row and we're done. return; - - pixman_box32_t *end = boxes + n; - pixman_box32_t *topRectsEnd = boxes+1; - pixman_box32_t *topRects = boxes; - - // we need some temporary storage for merging both rows of rectangles - AutoTArray tmpStorage; - tmpStorage.SetCapacity(n); - pixman_box32_t *tmpRect = tmpStorage.Elements(); - - pixman_box32_t *destRect = boxes; - pixman_box32_t *rect = tmpRect; - // find the end of the first span of rectangles - while (topRectsEnd < end && topRectsEnd->y1 == topRects->y1) { - topRectsEnd++; } - // if we only have one row we are done - if (topRectsEnd == end) - return; - - pixman_box32_t *bottomRects = topRectsEnd; - pixman_box32_t *bottomRectsEnd = bottomRects+1; + uint32_t currentBand = 0; do { - // find the end of the bottom span of rectangles - while (bottomRectsEnd < end && bottomRectsEnd->y1 == bottomRects->y1) { - bottomRectsEnd++; - } - uint32_t totalArea = ComputeMergedAreaIncrease(topRects, topRectsEnd, - bottomRects, bottomRectsEnd); + Band& band = mBands[currentBand]; + + uint32_t totalArea = ComputeMergedAreaIncrease(band, mBands[currentBand + 1]); if (totalArea <= aThreshold) { - // merge the rects into tmpRect - rect = MergeRects(topRects, topRectsEnd, bottomRects, bottomRectsEnd, tmpRect); - - // set topRects to where the newly merged rects will be so that we use them - // as our next set of topRects - topRects = destRect; - // copy the merged rects back into the destination - topRectsEnd = CopyRow(destRect, tmpRect, rect); - } else { - // copy the unmerged rects - destRect = CopyRow(destRect, topRects, topRectsEnd); - - topRects = bottomRects; - topRectsEnd = bottomRectsEnd; - if (bottomRectsEnd == end) { - // copy the last row when we are done - topRectsEnd = CopyRow(destRect, topRects, topRectsEnd); + for (Strip& strip : mBands[currentBand + 1].mStrips) { + // This could use an optimized function to merge two bands. + band.InsertStrip(strip); } + band.bottom = mBands[currentBand + 1].bottom; + mBands.RemoveElementAt(currentBand + 1); + } else { + currentBand++; } - bottomRects = bottomRectsEnd; - } while (bottomRectsEnd != end); + } while (currentBand + 1 < mBands.Length()); - - uint32_t reducedCount = topRectsEnd - pixman_region32_rectangles(&this->mImpl, &n); - // pixman has a special representation for - // regions of 1 rectangle. So just use the - // bounds in that case - if (reducedCount > 1) { - // reach into pixman and lower the number - // of rects stored in data. - this->mImpl.data->numRects = reducedCount; - } else { - *this = GetBounds(); - } + EnsureSimplified(); + AssertState(); } typedef void (*visit_fn)(void *closure, VisitSide side, int x1, int y1, int x2, int y2); -static bool VisitNextEdgeBetweenRect(visit_fn visit, void *closure, VisitSide side, - pixman_box32_t *&r1, pixman_box32_t *&r2, const int y, int &x1) -{ - // check for overlap - if (r1->x2 >= r2->x1) { - MOZ_ASSERT(r2->x1 >= x1); - visit(closure, side, x1, y, r2->x1, y); - - // find the rect that ends first or always drop the one that comes first? - if (r1->x2 < r2->x2) { - x1 = r1->x2; - r1++; - } else { - x1 = r2->x2; - r2++; - } - return true; - } else { - MOZ_ASSERT(r1->x2 < r2->x2); - // we handle the corners by just extending the top and bottom edges - visit(closure, side, x1, y, r1->x2+1, y); - r1++; - // we assign x1 because we can assume that x1 <= r2->x1 - 1 - // However the caller may know better and if so, may update - // x1 to r1->x1 - x1 = r2->x1 - 1; - return false; - } -} - -//XXX: if we need to this can compute the end of the row -static void -VisitSides(visit_fn visit, void *closure, pixman_box32_t *r, pixman_box32_t *r_end) -{ - // XXX: we can drop LEFT/RIGHT and just use the orientation - // of the line if it makes sense - while (r != r_end) { - visit(closure, VisitSide::LEFT, r->x1, r->y1, r->x1, r->y2); - visit(closure, VisitSide::RIGHT, r->x2, r->y1, r->x2, r->y2); - r++; - } -} - -static void -VisitAbove(visit_fn visit, void *closure, pixman_box32_t *r, pixman_box32_t *r_end) -{ - while (r != r_end) { - visit(closure, VisitSide::TOP, r->x1-1, r->y1, r->x2+1, r->y1); - r++; - } -} - -static void -VisitBelow(visit_fn visit, void *closure, pixman_box32_t *r, pixman_box32_t *r_end) -{ - while (r != r_end) { - visit(closure, VisitSide::BOTTOM, r->x1-1, r->y2, r->x2+1, r->y2); - r++; - } -} - -static pixman_box32_t * -VisitInbetween(visit_fn visit, void *closure, pixman_box32_t *r1, - pixman_box32_t *r1_end, - pixman_box32_t *r2, - pixman_box32_t *r2_end) -{ - const int y = r1->y2; - int x1; - - bool overlap = false; - while (r1 != r1_end && r2 != r2_end) { - if (!overlap) { - /* Find the left-most edge */ - if (r1->x1 < r2->x1) { - x1 = r1->x1 - 1; - } else { - x1 = r2->x1 - 1; - } - } - - MOZ_ASSERT((x1 >= (r1->x1 - 1)) || (x1 >= (r2->x1 - 1))); - if (r1->x1 < r2->x1) { - overlap = VisitNextEdgeBetweenRect(visit, closure, VisitSide::BOTTOM, r1, r2, y, x1); - } else { - overlap = VisitNextEdgeBetweenRect(visit, closure, VisitSide::TOP, r2, r1, y, x1); - } - } - - /* Finish up which ever row has remaining rects*/ - if (r1 != r1_end) { - // top row - do { - visit(closure, VisitSide::BOTTOM, x1, y, r1->x2 + 1, y); - r1++; - if (r1 == r1_end) - break; - x1 = r1->x1 - 1; - } while (true); - } else if (r2 != r2_end) { - // bottom row - do { - visit(closure, VisitSide::TOP, x1, y, r2->x2 + 1, y); - r2++; - if (r2 == r2_end) - break; - x1 = r2->x1 - 1; - } while (true); - } - - return 0; -} - void nsRegion::VisitEdges (visit_fn visit, void *closure) { - pixman_box32_t *boxes; - int n; - boxes = pixman_region32_rectangles(&mImpl, &n); - - // if we have no rectangles then we're done - if (!n) + if (mBands.IsEmpty()) { + visit(closure, VisitSide::LEFT, mBounds.X(), mBounds.Y(), mBounds.X(), mBounds.YMost()); + visit(closure, VisitSide::RIGHT, mBounds.XMost(), mBounds.Y(), mBounds.XMost(), mBounds.YMost()); + visit(closure, VisitSide::TOP, mBounds.X() - 1, mBounds.Y(), mBounds.XMost() + 1, mBounds.Y()); + visit(closure, VisitSide::BOTTOM, mBounds.X() - 1, mBounds.YMost(), mBounds.XMost() + 1, mBounds.YMost()); return; - - pixman_box32_t *end = boxes + n; - pixman_box32_t *topRectsEnd = boxes + 1; - pixman_box32_t *topRects = boxes; - - // find the end of the first span of rectangles - while (topRectsEnd < end && topRectsEnd->y1 == topRects->y1) { - topRectsEnd++; } - // In order to properly handle convex corners we always visit the sides first - // that way when we visit the corners we can pad using the value from the sides - VisitSides(visit, closure, topRects, topRectsEnd); + auto band = std::begin(mBands); + auto bandFinal = std::end(mBands); + bandFinal--; + for (const Strip& strip : band->mStrips) { + visit(closure, VisitSide::LEFT, strip.left, band->top, strip.left, band->bottom); + visit(closure, VisitSide::RIGHT, strip.right, band->top, strip.right, band->bottom); + visit(closure, VisitSide::TOP, strip.left - 1, band->top, strip.right + 1, band->top); + } - VisitAbove(visit, closure, topRects, topRectsEnd); - - pixman_box32_t *bottomRects = topRects; - pixman_box32_t *bottomRectsEnd = topRectsEnd; - if (topRectsEnd != end) { + if (band != bandFinal) { do { - // find the next row of rects - bottomRects = topRectsEnd; - bottomRectsEnd = topRectsEnd + 1; - while (bottomRectsEnd < end && bottomRectsEnd->y1 == bottomRects->y1) { - bottomRectsEnd++; + Band& topBand = *band; + band++; + + for (const Strip& strip : band->mStrips) { + visit(closure, VisitSide::LEFT, strip.left, band->top, strip.left, band->bottom); + visit(closure, VisitSide::RIGHT, strip.right, band->top, strip.right, band->bottom); } - VisitSides(visit, closure, bottomRects, bottomRectsEnd); + if (band->top == topBand.bottom) { + // Two bands touching each other vertically. + Band& bottomBand = *band; + auto topStrip = std::begin(topBand.mStrips); + auto bottomStrip = std::begin(bottomBand.mStrips); - if (topRects->y2 == bottomRects->y1) { - VisitInbetween(visit, closure, topRects, topRectsEnd, - bottomRects, bottomRectsEnd); + int y = topBand.bottom; + + // State from this point on along the vertical edge: + // 0 - Empty + // 1 - Touched by top rect + // 2 - Touched by bottom rect + // 3 - Touched on both sides + int state; + const int TouchedByNothing = 0; + const int TouchedByTop = 1; + const int TouchedByBottom = 2; + // We always start with nothing. + int oldState = TouchedByNothing; + // Last state change, adjusted by -1 if the last state change was + // a change away from 0. + int lastX = std::min(topStrip->left, bottomStrip->left) - 1; + + // Current edge being considered for top and bottom, 0 - left, 1 - right. + bool topEdgeIsLeft = true; + bool bottomEdgeIsLeft = true; + while (topStrip != std::end(topBand.mStrips) && bottomStrip != std::end(bottomBand.mStrips)) { + int topPos; + int bottomPos; + if (topEdgeIsLeft) { + topPos = topStrip->left; + } else { + topPos = topStrip->right; + } + if (bottomEdgeIsLeft) { + bottomPos = bottomStrip->left; + } else { + bottomPos = bottomStrip->right; + } + + int currentX = std::min(topPos, bottomPos); + if (topPos < bottomPos) { + if (topEdgeIsLeft) { + state = oldState | TouchedByTop; + } else { + state = oldState ^ TouchedByTop; + topStrip++; + } + topEdgeIsLeft = !topEdgeIsLeft; + } else if (bottomPos < topPos) { + if (bottomEdgeIsLeft) { + state = oldState | TouchedByBottom; + } else { + state = oldState ^ TouchedByBottom; + bottomStrip++; + } + bottomEdgeIsLeft = !bottomEdgeIsLeft; + } else { + // bottomPos == topPos + state = TouchedByNothing; + if (bottomEdgeIsLeft) { + state = TouchedByBottom; + } else { + bottomStrip++; + } + if (topEdgeIsLeft) { + state |= TouchedByTop; + } else { + topStrip++; + } + topEdgeIsLeft = !topEdgeIsLeft; + bottomEdgeIsLeft = !bottomEdgeIsLeft; + } + + MOZ_ASSERT(state != oldState); + if (oldState == TouchedByNothing) { + // We had nothing before, make sure the left edge will be padded. + lastX = currentX - 1; + } else if (oldState == TouchedByTop) { + if (state == TouchedByNothing) { + visit(closure, VisitSide::BOTTOM, lastX, y, currentX + 1, y); + } else { + visit(closure, VisitSide::BOTTOM, lastX, y, currentX, y); + lastX = currentX; + } + } else if (oldState == TouchedByBottom) { + if (state == TouchedByNothing) { + visit(closure, VisitSide::TOP, lastX, y, currentX + 1, y); + } else { + visit(closure, VisitSide::TOP, lastX, y, currentX, y); + lastX = currentX; + } + } else { + lastX = currentX; + } + oldState = state; + } + + MOZ_ASSERT(!state || (topEdgeIsLeft || bottomEdgeIsLeft)); + if (topStrip != std::end(topBand.mStrips)) { + if (!topEdgeIsLeft) { + visit(closure, VisitSide::BOTTOM, lastX, y, topStrip->right + 1, y); + topStrip++; + } + while (topStrip != std::end(topBand.mStrips)) { + visit(closure, VisitSide::BOTTOM, topStrip->left - 1, y, topStrip->right + 1, y); + topStrip++; + } + } else if (bottomStrip != std::end(bottomBand.mStrips)) { + if (!bottomEdgeIsLeft) { + visit(closure, VisitSide::TOP, lastX, y, bottomStrip->right + 1, y); + bottomStrip++; + } + while (bottomStrip != std::end(bottomBand.mStrips)) { + visit(closure, VisitSide::TOP, bottomStrip->left - 1, y, bottomStrip->right + 1, y); + bottomStrip++; + } + } } else { - VisitBelow(visit, closure, topRects, topRectsEnd); - VisitAbove(visit, closure, bottomRects, bottomRectsEnd); + for (const Strip& strip : topBand.mStrips) { + visit(closure, VisitSide::BOTTOM, strip.left - 1, topBand.bottom, strip.right + 1, topBand.bottom); + } + for (const Strip& strip : band->mStrips) { + visit(closure, VisitSide::TOP, strip.left - 1, band->top, strip.right + 1, band->top); + } } - - topRects = bottomRects; - topRectsEnd = bottomRectsEnd; - } while (bottomRectsEnd != end); + } while (band != bandFinal); } - // the bottom of the region doesn't touch anything else so we - // can always visit it at the end - VisitBelow(visit, closure, bottomRects, bottomRectsEnd); + for (const Strip& strip : band->mStrips) { + visit(closure, VisitSide::BOTTOM, strip.left - 1, band->bottom, strip.right + 1, band->bottom); + } } @@ -554,11 +465,18 @@ void nsRegion::SimplifyInward (uint32_t aMaxRects) uint64_t nsRegion::Area () const { - uint64_t area = 0; - for (auto iter = RectIter(); !iter.Done(); iter.Next()) { - const nsRect& rect = iter.Get(); - area += uint64_t(rect.Width()) * rect.Height(); + if (mBands.IsEmpty()) { + return mBounds.Area(); } + + uint64_t area = 0; + for (const Band& band : mBands) { + uint32_t height = band.bottom - band.top; + for (const Strip& strip : band.mStrips) { + area += (strip.right - strip.left) * height; + } + } + return area; } @@ -569,39 +487,27 @@ nsRegion& nsRegion::ScaleRoundOut (float aXScale, float aYScale) return *this; } - int n; - pixman_box32_t *boxes = pixman_region32_rectangles(&mImpl, &n); - for (int i=0; i= first.XMost()) { - // The top of rect contains the bottom of the first rect - firstDeviceRect.SetBottomEdge(deviceRect.Y()); - } else if (rect.X() >= first.X() && rect.XMost() <= first.XMost()) { - // The bottom of the first contains the top of rect - deviceRect.SetTopEdge(firstDeviceRect.YMost()); - } - } + mozilla::gfx::IntRect firstDeviceRect = + first.ScaleToInsidePixels(aScaleX, aScaleY, aAppUnitsPerPixel); + + for (iter.Next(); !iter.Done(); iter.Next()) { + nsRect rect = iter.Get(); + mozilla::gfx::IntRect deviceRect = + rect.ScaleToInsidePixels(aScaleX, aScaleY, aAppUnitsPerPixel); + + if (rect.Y() <= first.YMost()) { + if (rect.XMost() == first.X() && rect.YMost() <= first.YMost()) { + // rect is touching on the left edge of the first rect and contained within + // the length of its left edge + deviceRect.SetRightEdge(firstDeviceRect.X()); + } else if (rect.X() == first.XMost() && rect.YMost() <= first.YMost()) { + // rect is touching on the right edge of the first rect and contained within + // the length of its right edge + deviceRect.SetLeftEdge(firstDeviceRect.XMost()); + } else if (rect.Y() == first.YMost()) { + // The bottom of the first rect is on the same line as the top of rect, but + // they aren't necessarily contained. + if (rect.X() <= first.X() && rect.XMost() >= first.XMost()) { + // The top of rect contains the bottom of the first rect + firstDeviceRect.SetBottomEdge(deviceRect.Y()); + } else if (rect.X() >= first.X() && rect.XMost() <= first.XMost()) { + // The bottom of the first contains the top of rect + deviceRect.SetTopEdge(firstDeviceRect.YMost()); + } } - - boxes[i] = RectToBox(deviceRect); } - boxes[0] = RectToBox(firstDeviceRect); - - pixman_region32_fini(&intRegion.mImpl.mImpl); - // This will union all of the rectangles and runs in about O(n lg(n)) - pixman_region32_init_rects(&intRegion.mImpl.mImpl, boxes, n); + intRegion.OrWith(deviceRect); } - return intRegion; + intRegion.OrWith(firstDeviceRect); + return intRegion; } // A cell's "value" is a pair consisting of @@ -1129,19 +987,35 @@ nsRect nsRegion::GetLargestRectangle (const nsRect& aContainingRect) const { std::ostream& operator<<(std::ostream& stream, const nsRegion& m) { stream << "["; - int n; - pixman_box32_t *boxes = pixman_region32_rectangles(const_cast(&m.mImpl), &n); - for (int i=0; i +#endif + /* For information on the internal representation look at pixman-region.c * * This replaces an older homebrew implementation of nsRegion. The @@ -47,34 +55,481 @@ enum class VisitSide { RIGHT }; +namespace regiondetails { +struct Band; +} + +template<> +struct nsTArray_CopyChooser +{ + typedef nsTArray_CopyWithConstructors Type; +}; + +namespace regiondetails { + +template +class UncheckedArray : public T +{ +public: + using T::Elements; + using T::Length; + + E & operator[](size_t aIndex) { return Elements()[aIndex]; } + const E& operator[](size_t aIndex) const { return Elements()[aIndex]; } + E& LastElement() { return Elements()[Length() - 1]; } + const E& LastElement() const { return Elements()[Length() - 1]; } + + using iterator = E* ; + using const_iterator = const E*; + + iterator begin() { return iterator(Elements()); } + const_iterator begin() const { return const_iterator(Elements()); } + const_iterator cbegin() const { return begin(); } + iterator end() { return iterator(Elements() + Length()); } + const_iterator end() const { return const_iterator(Elements() + Length()); } + const_iterator cend() const { return end(); } +}; + +struct Strip +{ + // Default constructor should never be called, but is required for + // vector::resize to compile. + Strip() { MOZ_CRASH(); } + Strip(int32_t aLeft, int32_t aRight) : left(aLeft), right(aRight) {} + + bool operator != (const Strip& aOther) const + { + return left != aOther.left || right != aOther.right; + } + + uint32_t Size() const + { + return right - left; + } + + int32_t left; + int32_t right; +}; + +struct Band +{ + using Strip = regiondetails::Strip; +#ifndef DEBUG + using StripArray = regiondetails::UncheckedArray, Strip>; +#else + using StripArray = AutoTArray; +#endif + + MOZ_IMPLICIT Band(const nsRect& aRect) + : top(aRect.Y()), bottom(aRect.YMost()) + { + mStrips.AppendElement(Strip{ aRect.X(), aRect.XMost() }); + } + + Band(const Band& aOther) + : top(aOther.top), bottom(aOther.bottom) + , mStrips(aOther.mStrips) + {} + Band(const Band&& aOther) + : top(aOther.top), bottom(aOther.bottom) + , mStrips(std::move(aOther.mStrips)) + {} + + void InsertStrip(const Strip& aStrip) + { + for (size_t i = 0; i < mStrips.Length(); i++) { + Strip& strip = mStrips[i]; + if (strip.left > aStrip.right) { + // Current strip is beyond aStrip, insert aStrip before. + mStrips.InsertElementAt(i, aStrip); + return; + } + + if (strip.right < aStrip.left) { + // Current strip is before aStrip, try the next. + continue; + } + + // Current strip intersects with aStrip, extend to the lext. + strip.left = std::min(strip.left, aStrip.left); + + if (strip.right >= aStrip.right) { + // Current strip extends beyond aStrip, done. + return; + } + + size_t next = i; + next++; + // Consume any subsequent strips intersecting with aStrip. + while (next < mStrips.Length() && mStrips[next].left <= aStrip.right) { + strip.right = mStrips[next].right; + + mStrips.RemoveElementAt(next); + } + + // Extend the strip in case the aStrip goes on beyond it. + strip.right = std::max(strip.right, aStrip.right); + return; + } + mStrips.AppendElement(aStrip); + } + + void SubStrip(const Strip& aStrip) + { + for (size_t i = 0; i < mStrips.Length(); i++) { + Strip& strip = mStrips[i]; + if (strip.left > aStrip.right) { + // Strip is entirely to the right of aStrip. Done. + return; + } + + if (strip.right < aStrip.left) { + // Strip is entirely to the left of aStrip. Move on. + continue; + } + + if (strip.left < aStrip.left) { + if (strip.right <= aStrip.right) { + strip.right = aStrip.left; + // This strip lies to the left of the start of aStrip. + continue; + } + + // aStrip is completely contained by this strip. + Strip newStrip(aStrip.right, strip.right); + strip.right = aStrip.left; + if (i < mStrips.Length()) { + i++; + mStrips.InsertElementAt(i, newStrip); + } else { + mStrips.AppendElement(newStrip); + } + return; + } + + // This strip lies to the right of the start of aStrip. + if (strip.right <= aStrip.right) { + // aStrip completely contains this strip. + mStrips.RemoveElementAt(i); + // Make sure we evaluate the strip now at i. This loop will increment. + i--; + continue; + } + strip.left = aStrip.right; + return; + } + } + + bool Intersects(const Strip& aStrip) const + { + for (const Strip& strip : mStrips) { + if (strip.left >= aStrip.right) { + return false; + } + + if (strip.right <= aStrip.left) { + continue; + } + + return true; + } + return false; + } + + bool IntersectStripBounds(Strip& aStrip) const + { + bool intersected = false; + + int32_t rightMost; + for (const Strip& strip : mStrips) { + if (strip.left > aStrip.right) { + break; + } + + if (strip.right <= aStrip.left) { + continue; + } + + if (!intersected) { + // First intersection, this is where the left side begins. + aStrip.left = std::max(aStrip.left, strip.left); + } + + intersected = true; + // Expand to the right for each intersecting strip found. + rightMost = std::min(strip.right, aStrip.right); + } + + if (intersected) { + aStrip.right = rightMost; + } + else { + aStrip.right = aStrip.left = 0; + } + return intersected; + } + + bool ContainsStrip(const Strip& aStrip) const + { + for (const Strip& strip : mStrips) { + if (strip.left > aStrip.left) { + return false; + } + + if (strip.right >= aStrip.right) { + return true; + } + } + return false; + } + + bool EqualStrips(const Band& aBand) const + { + if (mStrips.Length() != aBand.mStrips.Length()) { + return false; + } + + for (auto iter1 = mStrips.begin(), iter2 = aBand.mStrips.begin(); + iter1 != mStrips.end(); iter1++, iter2++) + { + if (*iter1 != *iter2) { + return false; + } + } + + return true; + } + + void IntersectStrip(const Strip& aStrip) + { + size_t i = 0; + + while (i < mStrips.Length()) { + Strip& strip = mStrips[i]; + if (strip.right <= aStrip.left) { + mStrips.RemoveElementAt(i); + continue; + } + + if (strip.left >= aStrip.right) { + mStrips.TruncateLength(i); + return; + } + + strip.left = std::max(aStrip.left, strip.left); + strip.right = std::min(aStrip.right, strip.right); + i++; + } + } + + void IntersectStrips(const Band& aOther) + { + auto iter = mStrips.begin(); + auto iterOther = aOther.mStrips.begin(); + + StripArray newStrips; + + // This function finds the intersection between two sets of strips. + while (true) { + while (true) { + while (iter != mStrips.end() && iter->right <= iterOther->left) { + // Increment our current strip until it ends beyond aOther's current strip. + iter++; + } + + if (iter == mStrips.end()) { + // End of our strips. Done. + break; + } + + while (iterOther != aOther.mStrips.end() && iterOther->right <= iter->left) { + // Increment aOther's current strip until it lies beyond our current strip. + iterOther++; + } + + if (iterOther == aOther.mStrips.end()) { + // End of aOther's strips. Done. + break; + } + + if (iterOther->left < iter->right) { + // Intersection! + break; + } + } + + if (iter == mStrips.end() || iterOther == aOther.mStrips.end()) { + break; + } + + newStrips.AppendElement(Strip(std::max(iter->left, iterOther->left), std::min(iterOther->right, iter->right))); + + if (iterOther->right < iter->right) { + iterOther++; + if (iterOther == aOther.mStrips.end()) { + break; + } + } else { + iter++; + } + } + + mStrips = newStrips; + } + + bool Intersects(const Band& aOther) const + { + auto iter = mStrips.begin(); + auto iterOther = aOther.mStrips.begin(); + + // This function finds the intersection between two sets of strips. + while (true) { + while (true) { + while (iter != mStrips.end() && iter->right <= iterOther->left) { + // Increment our current strip until it ends beyond aOther's current strip. + iter++; + } + + if (iter == mStrips.end()) { + // End of our strips. Done. + break; + } + + while (iterOther != aOther.mStrips.end() && iterOther->right <= iter->left) { + // Increment aOther's current strip until it lies beyond our current strip. + iterOther++; + } + + if (iterOther == aOther.mStrips.end()) { + // End of aOther's strips. Done. + break; + } + + if (iterOther->left < iter->right) { + // Intersection! + break; + } + } + + if (iter == mStrips.end()|| iterOther == aOther.mStrips.end()) { + break; + } + + return true; + } + return false; + } + + void SubStrips(const Band& aOther) + { + size_t idx = 0; + auto iterOther = aOther.mStrips.begin(); + + // This function finds the intersection between two sets of strips. + while (true) { + while (true) { + while (idx < mStrips.Length() && mStrips[idx].right <= iterOther->left) { + // Increment our current strip until it ends beyond aOther's current strip. + idx++; + } + + if (idx == mStrips.Length()) { + // End of our strips. Done. + break; + } + + while (iterOther != aOther.mStrips.end() && iterOther->right <= mStrips[idx].left) { + // Increment aOther's current strip until it lies beyond our current strip. + iterOther++; + } + + if (iterOther == aOther.mStrips.end()) { + // End of aOther's strips. Done. + break; + } + + if (iterOther->left < mStrips[idx].right) { + // Intersection! + break; + } + } + + if (idx == mStrips.Length() || iterOther == aOther.mStrips.end()) { + break; + } + + if (mStrips[idx].left < iterOther->left) { + size_t oldIdx = idx; + // Our strip starts beyond other's + if (mStrips[idx].right > iterOther->right) { + // Our strip ends beyond other's as well. + Strip newStrip(mStrips[idx]); + newStrip.left = iterOther->right; + mStrips.InsertElementAt(idx + 1, newStrip); + idx++; + } + mStrips[oldIdx].right = iterOther->left; + // Either idx was just incremented, or the current index no longer intersects with iterOther. + continue; + } else if (mStrips[idx].right > iterOther->right) { + mStrips[idx].left = iterOther->right; + // Current strip no longer intersects, continue. + iterOther++; + if (iterOther == aOther.mStrips.end()) { + break; + } + continue; + } + + // Our current strip is completely contained by the other strip. + mStrips.RemoveElementAt(idx); + } + } + + int32_t top; + int32_t bottom; + StripArray mStrips; +}; +} + class nsRegion { public: + using Band = regiondetails::Band; + using Strip = regiondetails::Strip; +#ifndef DEBUG + using BandArray = regiondetails::UncheckedArray, Band>; + using StripArray = regiondetails::UncheckedArray, Strip>; +#else + using BandArray = nsTArray; + using StripArray = AutoTArray; +#endif + typedef nsRect RectType; typedef nsPoint PointType; typedef nsMargin MarginType; - nsRegion () { pixman_region32_init(&mImpl); } - MOZ_IMPLICIT nsRegion (const nsRect& aRect) { pixman_region32_init_rect(&mImpl, - aRect.X(), - aRect.Y(), - aRect.Width(), - aRect.Height()); } - explicit nsRegion (mozilla::gfx::ArrayView aRects) + nsRegion() { } + MOZ_IMPLICIT nsRegion(const nsRect& aRect) { + mBounds = aRect; + } + explicit nsRegion(mozilla::gfx::ArrayView aRects) { - pixman_region32_init_rects(&mImpl, aRects.Data(), aRects.Length()); + for (uint32_t i = 0; i < aRects.Length(); i++) { + AddRect(BoxToRect(aRects[i])); + } } - nsRegion (const nsRegion& aRegion) { pixman_region32_init(&mImpl); pixman_region32_copy(&mImpl,aRegion.Impl()); } - nsRegion (nsRegion&& aRegion) { mImpl = aRegion.mImpl; pixman_region32_init(&aRegion.mImpl); } - nsRegion& operator = (nsRegion&& aRegion) { - pixman_region32_fini(&mImpl); - mImpl = aRegion.mImpl; - pixman_region32_init(&aRegion.mImpl); - return *this; + + nsRegion(const nsRegion& aRegion) { Copy(aRegion); } + nsRegion(nsRegion&& aRegion) { mBands.SwapElements(aRegion.mBands); mBounds = aRegion.mBounds; aRegion.SetEmpty(); } + nsRegion& operator =(nsRegion&& aRegion) { + mBands.SwapElements(aRegion.mBands); + mBounds = aRegion.mBounds; + aRegion.SetEmpty(); + return *this; } - ~nsRegion () { pixman_region32_fini(&mImpl); } - nsRegion& operator = (const nsRect& aRect) { Copy (aRect); return *this; } - nsRegion& operator = (const nsRegion& aRegion) { Copy (aRegion); return *this; } + nsRegion& operator =(const nsRect& aRect) { Copy(aRect); return *this; } + nsRegion& operator =(const nsRegion& aRegion) { Copy(aRegion); return *this; } bool operator==(const nsRegion& aRgn) const { return IsEqual(aRgn); @@ -85,25 +540,328 @@ public: } friend std::ostream& operator<<(std::ostream& stream, const nsRegion& m); + void OutputToStream(std::string aObjName, std::ostream& stream) const; - void Swap(nsRegion* aOther) + static + nsresult InitStatic() { - pixman_region32_t tmp = mImpl; - mImpl = aOther->mImpl; - aOther->mImpl = tmp; + return NS_OK; } - void AndWith(const nsRegion& aOther) + static + void ShutdownStatic() {} + +private: +#ifdef DEBUG_REGIONS + class OperationStringGenerator { - And(*this, aOther); + public: + virtual ~OperationStringGenerator() {} + + virtual void OutputOp() = 0; + }; +#endif +public: + + void AssertStateInternal() const; + void AssertState() const { +#ifdef DEBUG_REGIONS + AssertStateInternal(); +#endif } - void AndWith(const nsRect& aOther) + +private: + void And(BandArray& aOut, const BandArray& aIn1, const BandArray& aIn2) { - And(*this, aOther); + size_t idx = 0; + size_t idxOther = 0; + + // This algorithm essentially forms a new list of bands, by iterating over + // both regions' lists of band simultaneously, and building a new band + // wherever the two regions intersect. + while (true) { + while (true) { + while (idx != aIn1.Length() && aIn1[idx].bottom <= aIn2[idxOther].top) { + // Increment our current band until it ends beyond aOther's current band. + idx++; + } + + if (idx == aIn1.Length()) { + // This region is out of bands, the other region's future bands are ignored. + break; + } + + while (idxOther != aIn2.Length() && aIn2[idxOther].bottom <= aIn1[idx].top) { + // Increment aOther's current band until it ends beyond our current band. + idxOther++; + } + + if (idxOther == aIn2.Length()) { + // The other region's bands are all processed, all our future bands are ignored. + break; + } + + if (aIn2[idxOther].top < aIn1[idx].bottom) { + // We know the other band's bottom lies beyond our band's top because + // otherwise we would've incremented above. Intersecting bands found. + break; + } + } + + if (idx == aIn1.Length() || idxOther == aIn2.Length()) { + // The above loop executed a break because we're done. + break; + } + + Band newBand(aIn1[idx]); + // The new band is the intersection of the two current bands from both regions. + newBand.top = std::max(aIn1[idx].top, aIn2[idxOther].top); + newBand.bottom = std::min(aIn1[idx].bottom, aIn2[idxOther].bottom); + newBand.IntersectStrips(aIn2[idxOther]); + + if (newBand.mStrips.Length()) { + // The intersecting area of the bands had overlapping strips, if it is + // identical to the band above it merge, otherwise append. + if (aOut.Length() && aOut.LastElement().bottom == newBand.top && + aOut.LastElement().EqualStrips(newBand)) { + aOut.LastElement().bottom = newBand.bottom; + } else { + aOut.AppendElement(std::move(newBand)); + } + } + + if (aIn2[idxOther].bottom < aIn1[idx].bottom) { + idxOther++; + if (idxOther == aIn2.Length()) { + // Since we will access idxOther the next iteration, check if we're not done. + break; + } + } else { + // No need to check here since we do at the beginning of the next iteration. + idx++; + } + } } - nsRegion& And(const nsRegion& aRgn1, const nsRegion& aRgn2) + +public: + nsRegion& AndWith(const nsRegion& aRegion) { - pixman_region32_intersect(&mImpl, aRgn1.Impl(), aRgn2.Impl()); +#ifdef DEBUG_REGIONS + class OperationStringGeneratorAndWith : public OperationStringGenerator + { + public: + OperationStringGeneratorAndWith(nsRegion& aRegion, const nsRegion& aOtherRegion) + : mRegion(&aRegion), mRegionCopy(aRegion), mOtherRegion(aOtherRegion) + { + aRegion.mCurrentOpGenerator = this; + } + virtual ~OperationStringGeneratorAndWith() + { + mRegion->mCurrentOpGenerator = nullptr; + } + + virtual void OutputOp() override + { + std::stringstream stream; + mRegionCopy.OutputToStream("r1", stream); + mOtherRegion.OutputToStream("r2", stream); + stream << "r1.AndWith(r2);\n"; + gfxCriticalError() << stream.str(); + } + private: + nsRegion * mRegion; + nsRegion mRegionCopy; + nsRegion mOtherRegion; + }; + + OperationStringGeneratorAndWith opGenerator(*this, aRegion); +#endif + if (mBounds.IsEmpty()) { + // Region is empty, stays empty. + return *this; + } + + if (aRegion.IsEmpty()) { + SetEmpty(); + return *this; + } + + if (aRegion.mBands.IsEmpty()) { + // Other region is a rect. + return AndWith(aRegion.mBounds); + } + + if (mBands.IsEmpty()) { + mBands.AppendElement(mBounds); + } + + BandArray newBands; + + And(newBands, mBands, aRegion.mBands); + + mBands = std::move(newBands); + if (!mBands.Length()) { + mBounds = nsRect(); + } else { + mBounds = CalculateBounds(); + } + + EnsureSimplified(); + AssertState(); + return *this; + } + + nsRegion& AndWith(const nsRect& aRect) + { +#ifdef DEBUG_REGIONS + class OperationStringGeneratorAndWith : public OperationStringGenerator + { + public: + OperationStringGeneratorAndWith(nsRegion& aRegion, const nsRect& aRect) + : mRegion(&aRegion), mRegionCopy(aRegion), mRect(aRect) + { + aRegion.mCurrentOpGenerator = this; + } + virtual ~OperationStringGeneratorAndWith() + { + mRegion->mCurrentOpGenerator = nullptr; + } + + virtual void OutputOp() override + { + std::stringstream stream; + mRegionCopy.OutputToStream("r", stream); + stream << "r.AndWith(nsRect(" << mRect.X() << ", " << mRect.Y() << ", " << mRect.Width() << ", " << mRect.Height() << "));\n"; + gfxCriticalError() << stream.str(); + } + private: + nsRegion * mRegion; + nsRegion mRegionCopy; + nsRect mRect; + }; + + OperationStringGeneratorAndWith opGenerator(*this, aRect); +#endif + if (aRect.IsEmpty()) { + SetEmpty(); + return *this; + } + + if (mBands.IsEmpty()) { + mBounds = mBounds.Intersect(aRect); + return *this; + } + + size_t idx = 0; + + size_t removeStart = 0; + + // This removes all bands that do not intersect with aRect, and intersects + // the remaining ones with aRect. + + // Start by figuring out how much to remove from the start. + while (idx != mBands.Length() && mBands[idx].bottom <= aRect.Y()) { + idx++; + } + + // We'll remove these later to avoid needless copying in the array. + removeStart = idx; + + while (idx != mBands.Length()) { + if (mBands[idx].top >= aRect.YMost()) { + mBands.TruncateLength(idx); + break; + } + + mBands[idx].top = std::max(mBands[idx].top, aRect.Y()); + mBands[idx].bottom = std::min(mBands[idx].bottom, aRect.YMost()); + + mBands[idx].IntersectStrip(Strip(aRect.X(), aRect.XMost())); + + if (!mBands[idx].mStrips.Length()) { + mBands.RemoveElementAt(idx); + } else { + if (idx > removeStart) { + CompressBefore(idx); + } + idx++; + } + } + + if (removeStart) { + mBands.RemoveElementsAt(0, removeStart); + } + + if (mBands.Length()) { + mBounds = CalculateBounds(); + } else { + mBounds.SetEmpty(); + } + EnsureSimplified(); + AssertState(); + return *this; + } + nsRegion& And(const nsRegion& aRgn1, const nsRegion& aRgn2) + { + if (&aRgn1 == this) { + return AndWith(aRgn2); + } +#ifdef DEBUG_REGIONS + class OperationStringGeneratorAnd : public OperationStringGenerator + { + public: + OperationStringGeneratorAnd(nsRegion& aRegion, const nsRegion& aRegion1, const nsRegion& aRegion2) + : mRegion(&aRegion), mRegion1(aRegion1), mRegion2(aRegion2) + { + aRegion.mCurrentOpGenerator = this; + } + virtual ~OperationStringGeneratorAnd() + { + mRegion->mCurrentOpGenerator = nullptr; + } + + virtual void OutputOp() override + { + std::stringstream stream; + mRegion1.OutputToStream("r1", stream); + mRegion2.OutputToStream("r2", stream); + stream << "nsRegion r3;\nr3.And(r1, r2);\n"; + gfxCriticalError() << stream.str(); + } + private: + nsRegion * mRegion; + nsRegion mRegion1; + nsRegion mRegion2; + }; + + OperationStringGeneratorAnd opGenerator(*this, aRgn1, aRgn2); +#endif + mBands.Clear(); + + if (aRgn1.IsEmpty() || aRgn2.IsEmpty()) { + mBounds.SetEmpty(); + return *this; + } + + if (aRgn1.mBands.IsEmpty() && aRgn2.mBands.IsEmpty()) { + mBounds = aRgn1.mBounds.Intersect(aRgn2.mBounds); + return *this; + } else if (aRgn1.mBands.IsEmpty()) { + return And(aRgn2, aRgn1.mBounds); + } else if (aRgn2.mBands.IsEmpty()) { + return And(aRgn1, aRgn2.mBounds); + } + + And(mBands, aRgn1.mBands, aRgn2.mBands); + + if (!mBands.Length()) { + mBounds = nsRect(); + } else { + mBounds = CalculateBounds(); + } + + EnsureSimplified(); + AssertState(); return *this; } nsRegion& And(const nsRect& aRect, const nsRegion& aRegion) @@ -112,33 +870,130 @@ public: } nsRegion& And(const nsRegion& aRegion, const nsRect& aRect) { - pixman_region32_intersect_rect(&mImpl, aRegion.Impl(), aRect.X(), aRect.Y(), aRect.Width(), aRect.Height()); + if (&aRegion == this) { + return AndWith(aRect); + } +#ifdef DEBUG_REGIONS + class OperationStringGeneratorAnd : public OperationStringGenerator + { + public: + OperationStringGeneratorAnd(nsRegion& aThisRegion, const nsRegion& aRegion, const nsRect& aRect) + : mThisRegion(&aThisRegion), mRegion(aRegion), mRect(aRect) + { + aThisRegion.mCurrentOpGenerator = this; + } + virtual ~OperationStringGeneratorAnd() + { + mThisRegion->mCurrentOpGenerator = nullptr; + } + + virtual void OutputOp() override + { + std::stringstream stream; + mRegion.OutputToStream("r", stream); + stream << "nsRegion r2;\nr.And(r2, nsRect(" << mRect.X() << ", " << mRect.Y() << ", " << mRect.Width() << ", " << mRect.Height() << "));\n"; + gfxCriticalError() << stream.str(); + } + private: + nsRegion* mThisRegion; + nsRegion mRegion; + nsRect mRect; + }; + + OperationStringGeneratorAnd opGenerator(*this, aRegion, aRect); +#endif + mBands.Clear(); + + if (aRect.IsEmpty()) { + mBounds.SetEmpty(); + return *this; + } + + if (aRegion.mBands.IsEmpty()) { + mBounds = aRegion.mBounds.Intersect(aRect); + return *this; + } + + size_t idx = 0; + const BandArray& bands = aRegion.mBands; + + mBands.SetCapacity(bands.Length() + 3); + while (idx != bands.Length()) { + // Ignore anything before. + if (bands[idx].bottom <= aRect.Y()) { + idx++; + continue; + } + // We're done once we've reached the bottom. + if (bands[idx].top >= aRect.YMost()) { + break; + } + + // Now deal with bands actually intersecting the rectangle. + Band newBand(bands[idx]); + newBand.top = std::max(bands[idx].top, aRect.Y()); + newBand.bottom = std::min(bands[idx].bottom, aRect.YMost()); + + newBand.IntersectStrip(Strip(aRect.X(), aRect.XMost())); + + if (newBand.mStrips.Length()) { + if (!mBands.IsEmpty() && + newBand.top == mBands.LastElement().bottom && + newBand.EqualStrips(mBands.LastElement())) { + mBands.LastElement().bottom = newBand.bottom; + } else { + mBands.AppendElement(std::move(newBand)); + } + } + idx++; + } + + if (mBands.Length()) { + mBounds = CalculateBounds(); + } else { + mBounds.SetEmpty(); + } + + EnsureSimplified(); + AssertState(); return *this; } nsRegion& And(const nsRect& aRect1, const nsRect& aRect2) { - nsRect TmpRect; + nsRect tmpRect; - TmpRect.IntersectRect(aRect1, aRect2); - return Copy(TmpRect); + tmpRect.IntersectRect(aRect1, aRect2); + return Copy(tmpRect); } nsRegion& OrWith(const nsRegion& aOther) { - return Or(*this, aOther); + for (RectIterator idx(aOther); !idx.Done(); idx.Next()) { + AddRect(idx.Get()); + } + return *this; } nsRegion& OrWith(const nsRect& aOther) { - return Or(*this, aOther); + AddRect(aOther); + return *this; } nsRegion& Or(const nsRegion& aRgn1, const nsRegion& aRgn2) { - pixman_region32_union(&mImpl, aRgn1.Impl(), aRgn2.Impl()); + if (&aRgn1 != this) { + *this = aRgn1; + } + for (RectIterator idx(aRgn2); !idx.Done(); idx.Next()) { + AddRect(idx.Get()); + } return *this; } nsRegion& Or(const nsRegion& aRegion, const nsRect& aRect) { - pixman_region32_union_rect(&mImpl, aRegion.Impl(), aRect.X(), aRect.Y(), aRect.Width(), aRect.Height()); + if (&aRegion != this) { + *this = aRegion; + } + AddRect(aRect); return *this; } nsRegion& Or(const nsRect& aRect, const nsRegion& aRegion) @@ -147,8 +1002,8 @@ public: } nsRegion& Or(const nsRect& aRect1, const nsRect& aRect2) { - Copy (aRect1); - return Or (*this, aRect2); + Copy(aRect1); + return Or(*this, aRect2); } nsRegion& XorWith(const nsRegion& aOther) @@ -159,7 +1014,7 @@ public: { return Xor(*this, aOther); } - nsRegion& Xor(const nsRegion& aRgn1, const nsRegion& aRgn2) + nsRegion& Xor(const nsRegion& aRgn1, const nsRegion& aRgn2) { // this could be implemented better if pixman had direct // support for xoring regions. @@ -182,60 +1037,750 @@ public: return Xor(nsRegion(aRect1), nsRegion(aRect2)); } - nsRegion ToAppUnits (nscoord aAppUnitsPerPixel) const; + nsRegion ToAppUnits(nscoord aAppUnitsPerPixel) const; + nsRegion& SubWith(const nsRegion& aOther) + { +#ifdef DEBUG_REGIONS + class OperationStringGeneratorSubWith : public OperationStringGenerator + { + public: + OperationStringGeneratorSubWith(nsRegion& aRegion, const nsRegion& aOtherRegion) + : mRegion(&aRegion), mRegionCopy(aRegion), mOtherRegion(aOtherRegion) + { + aRegion.mCurrentOpGenerator = this; + } + virtual ~OperationStringGeneratorSubWith() + { + mRegion->mCurrentOpGenerator = nullptr; + } + + virtual void OutputOp() override + { + std::stringstream stream; + mRegionCopy.OutputToStream("r1", stream); + mOtherRegion.OutputToStream("r2", stream); + stream << "r1.SubWith(r2);\n"; + gfxCriticalError() << stream.str(); + } + private: + nsRegion * mRegion; + nsRegion mRegionCopy; + nsRegion mOtherRegion; + }; + + OperationStringGeneratorSubWith opGenerator(*this, aOther); +#endif + + if (mBounds.IsEmpty()) { + return *this; + } + + if (aOther.mBands.IsEmpty()) { + return SubWith(aOther.mBounds); + } + + if (mBands.IsEmpty()) { + mBands.AppendElement(Band(mBounds)); + } + + size_t idx = 0; + size_t idxOther = 0; + while (idx < mBands.Length()) { + while (true) { + while (idx != mBands.Length() && mBands[idx].bottom <= aOther.mBands[idxOther].top) { + // Increment our current band until it ends beyond aOther's current band. + idx++; + } + + if (idx == mBands.Length()) { + // This region is out of bands, the other region's future bands are ignored. + break; + } + + while (idxOther != aOther.mBands.Length() && aOther.mBands[idxOther].bottom <= mBands[idx].top) { + // Increment aOther's current band until it ends beyond our current band. + idxOther++; + } + + if (idxOther == aOther.mBands.Length()) { + // The other region's bands are all processed, all our future bands are ignored. + break; + } + + if (aOther.mBands[idxOther].top < mBands[idx].bottom) { + // We know the other band's bottom lies beyond our band's top because + // otherwise we would've incremented above. Intersecting bands found. + break; + } + } + + if (idx == mBands.Length() || idxOther == aOther.mBands.Length()) { + // The above loop executed a break because we're done. + break; + } + + const Band& bandOther = aOther.mBands[idxOther]; + + if (!mBands[idx].Intersects(bandOther)) { + if (mBands[idx].bottom < bandOther.bottom) { + idx++; + } else { + idxOther++; + if (idxOther == aOther.mBands.Length()) { + break; + } + } + continue; + } + + // These bands actually intersect. + if (mBands[idx].top < bandOther.top) { + mBands.InsertElementAt(idx + 1, Band(mBands[idx])); + mBands[idx].bottom = bandOther.top; + idx++; + mBands[idx].top = bandOther.top; + } + + // mBands[idx].top >= bandOther.top; + if (mBands[idx].bottom <= bandOther.bottom) { + mBands[idx].SubStrips(bandOther); + if (mBands[idx].mStrips.IsEmpty()) { + mBands.RemoveElementAt(idx); + } else { + CompressBefore(idx); + idx++; + // The band before us just changed, it may be identical now. + CompressBefore(idx); + } + continue; + } + + // mBands[idx].bottom > bandOther.bottom + Band newBand = mBands[idx]; + newBand.SubStrips(bandOther); + + if (!newBand.mStrips.IsEmpty()) { + mBands.InsertElementAt(idx, newBand); + mBands[idx].bottom = bandOther.bottom; + CompressBefore(idx); + idx++; + } + + mBands[idx].top = bandOther.bottom; + + idxOther++; + if (idxOther == aOther.mBands.Length()) { + break; + } + } + + if (mBands.IsEmpty()) { + mBounds.SetEmpty(); + } else { + mBounds = CalculateBounds(); + } + + AssertState(); + EnsureSimplified(); + return *this; + } nsRegion& SubOut(const nsRegion& aOther) { - return Sub(*this, aOther); + return SubWith(aOther); } nsRegion& SubOut(const nsRect& aOther) { - return Sub(*this, aOther); + return SubWith(aOther); } + +private: + void AppendOrExtend(const Band& aNewBand) + { + if (aNewBand.mStrips.IsEmpty()) { + return; + } + if (mBands.IsEmpty()) { + mBands.AppendElement(aNewBand); + return; + } + + if (mBands.LastElement().bottom == aNewBand.top && mBands.LastElement().EqualStrips(aNewBand)) { + mBands.LastElement().bottom = aNewBand.bottom; + } else { + mBands.AppendElement(aNewBand); + } + } + void AppendOrExtend(const Band&& aNewBand) + { + if (aNewBand.mStrips.IsEmpty()) { + return; + } + if (mBands.IsEmpty()) { + mBands.AppendElement(std::move(aNewBand)); + return; + } + + if (mBands.LastElement().bottom == aNewBand.top && mBands.LastElement().EqualStrips(aNewBand)) { + mBands.LastElement().bottom = aNewBand.bottom; + } else { + mBands.AppendElement(std::move(aNewBand)); + } + } +public: nsRegion& Sub(const nsRegion& aRgn1, const nsRegion& aRgn2) { - pixman_region32_subtract(&mImpl, aRgn1.Impl(), aRgn2.Impl()); + if (&aRgn1 == this) { + return SubWith(aRgn2); + } +#ifdef DEBUG_REGIONS + class OperationStringGeneratorSub : public OperationStringGenerator + { + public: + OperationStringGeneratorSub(nsRegion& aRegion, const nsRegion& aRgn1, const nsRegion& aRgn2) + : mRegion(&aRegion), mRegion1(aRgn1), mRegion2(aRgn2) + { + aRegion.mCurrentOpGenerator = this; + } + virtual ~OperationStringGeneratorSub() + { + mRegion->mCurrentOpGenerator = nullptr; + } + + virtual void OutputOp() override + { + std::stringstream stream; + mRegion1.OutputToStream("r1", stream); + mRegion2.OutputToStream("r2", stream); + stream << "nsRegion r3;\nr3.Sub(r1, r2);\n"; + gfxCriticalError() << stream.str(); + } + private: + nsRegion* mRegion; + nsRegion mRegion1; + nsRegion mRegion2; + }; + + OperationStringGeneratorSub opGenerator(*this, aRgn1, aRgn2); +#endif + + mBands.Clear(); + + if (aRgn1.mBounds.IsEmpty()) { + mBounds.SetEmpty(); + return *this; + } + + if (aRgn2.mBounds.IsEmpty()) { + Copy(aRgn1); + return *this; + } + + if (aRgn1.mBands.IsEmpty() && aRgn2.mBands.IsEmpty()) { + return Sub(aRgn1.mBounds, aRgn2.mBounds); + } else if (aRgn1.mBands.IsEmpty()) { + return Sub(aRgn1.mBounds, aRgn2); + } else if (aRgn2.mBands.IsEmpty()) { + return Sub(aRgn1, aRgn2.mBounds); + } + + const BandArray& bands1 = aRgn1.mBands; + const BandArray& bands2 = aRgn2.mBands; + + size_t idx = 0; + size_t idxOther = 0; + + // We iterate the source region's bands, subtracting the other regions bands from them as we + // move them into ours. + while (idx < bands1.Length()) { + while (idxOther < bands2.Length() && bands2[idxOther].bottom <= bands1[idx].top) { + // These other bands are irrelevant as they don't intersect with the + // band we're currently processing. + idxOther++; + } + if (idxOther == bands2.Length()) { + break; + } + + const Band& other = bands2[idxOther]; + + // bands2[idxOther].bottom >= bands1[idx].top + Band origBand(bands1[idx]); + if (other.top >= origBand.bottom) { + // No intersecting bands, append and continue. + AppendOrExtend(origBand); + idx++; + continue; + } + + // Push a band for an uncovered region above our band. + if (origBand.top < other.top) { + Band newBand(origBand); + newBand.bottom = other.top; + AppendOrExtend(std::move(newBand)); + } + + int32_t lastBottom = std::max(other.top, origBand.top); + while (idxOther < bands2.Length() && bands2[idxOther].top < origBand.bottom) { + const Band& other = bands2[idxOther]; + Band newBand(origBand); + newBand.top = std::max(origBand.top, other.top); + newBand.bottom = std::min(origBand.bottom, other.bottom); + + // If there was a gap, we need to add the original band there. + if (newBand.top > lastBottom) { + Band betweenBand(newBand); + betweenBand.top = lastBottom; + betweenBand.bottom = newBand.top; + AppendOrExtend(std::move(betweenBand)); + } + + lastBottom = newBand.bottom; + newBand.SubStrips(other); + AppendOrExtend(std::move(newBand)); + idxOther++; + } + // Decrement other here so we are back at the last band in region 2 + // that intersected. + idxOther--; + + if (bands2[idxOther].bottom < origBand.bottom) { + // The last band in region2 that intersected ended before this one, + // we can copy the rest. + Band newBand(origBand); + newBand.top = bands2[idxOther].bottom; + AppendOrExtend(std::move(newBand)); + idxOther++; + } + idx++; + } + + // Copy any remaining bands, the first one may have to be extended to fit + // the last one added before. The rest can be unconditionally appended. + if (idx < bands1.Length()) { + AppendOrExtend(bands1[idx]); + idx++; + } + + while (idx < bands1.Length()) { + mBands.AppendElement(bands1[idx]); + idx++; + } + + if (mBands.IsEmpty()) { + mBounds.SetEmpty(); + } + else { + mBounds = CalculateBounds(); + } + + AssertState(); + EnsureSimplified(); + return *this; + } + +private: + // Internal helper for executing subtraction. + void RunSubtraction(const nsRect& aRect) + { + Strip rectStrip(aRect.X(), aRect.XMost()); + + size_t idx = 0; + + while (idx < mBands.Length()) { + if (mBands[idx].top >= aRect.YMost()) { + return; + } + + if (mBands[idx].bottom <= aRect.Y()) { + // This band is entirely before aRect, move on. + idx++; + continue; + } + + if (!mBands[idx].Intersects(Strip(aRect.X(), aRect.XMost()))) { + // This band does not intersect aRect horizontally. Move on. + idx++; + continue; + } + + // This band intersects with aRect. + + if (mBands[idx].top < aRect.Y()) { + // This band starts above the start of aRect, split the band into two + // along the intersection, and continue to the next iteration to process + // the one that now intersects exactly. + auto above = mBands.InsertElementAt(idx, Band(mBands[idx])); + above->bottom = aRect.Y(); + idx++; + mBands[idx].top = aRect.Y(); + // Continue to run the loop for the next band. + continue; + } + + if (mBands[idx].bottom <= aRect.YMost()) { + // This band ends before the end of aRect. + mBands[idx].SubStrip(rectStrip); + if (mBands[idx].mStrips.Length()) { + CompressAdjacentBands(idx); + } else { + mBands.RemoveElementAt(idx); + } + continue; + } + + // This band extends beyond aRect. + Band newBand = mBands[idx]; + newBand.SubStrip(rectStrip); + newBand.bottom = aRect.YMost(); + mBands[idx].top = aRect.YMost(); + + if (newBand.mStrips.Length()) { + if (idx && mBands[idx - 1].bottom == newBand.top && newBand.EqualStrips(mBands[idx - 1])) { + mBands[idx - 1].bottom = aRect.YMost(); + } else { + mBands.InsertElementAt(idx, std::move(newBand)); + } + } + + return; + } + } + +public: + nsRegion& SubWith(const nsRect& aRect) { + if (!mBounds.Intersects(aRect)) { + return *this; + } + + if (aRect.Contains(mBounds)) { + SetEmpty(); + return *this; + } + +#ifdef DEBUG_REGIONS + class OperationStringGeneratorSubWith : public OperationStringGenerator + { + public: + OperationStringGeneratorSubWith(nsRegion& aRegion, const nsRect& aRect) + : mRegion(&aRegion), mRegionCopy(aRegion), mRect(aRect) + { + aRegion.mCurrentOpGenerator = this; + } + virtual ~OperationStringGeneratorSubWith() + { + mRegion->mCurrentOpGenerator = nullptr; + } + + virtual void OutputOp() override + { + std::stringstream stream; + mRegionCopy.OutputToStream("r", stream); + stream << "r.SubWith(nsRect(" << mRect.X() << ", " << mRect.Y() << ", " << mRect.Width() << ", " << mRect.Height() << "));\n"; + gfxCriticalError() << stream.str(); + } + private: + nsRegion * mRegion; + nsRegion mRegionCopy; + nsRect mRect; + }; + + OperationStringGeneratorSubWith opGenerator(*this, aRect); +#endif + + if (mBands.IsEmpty()) { + mBands.AppendElement(Band(mBounds)); + } + + RunSubtraction(aRect); + + if (aRect.x <= mBounds.x || aRect.y <= mBounds.y || + aRect.XMost() >= mBounds.XMost() || aRect.YMost() >= mBounds.YMost()) { + mBounds = CalculateBounds(); + } + EnsureSimplified(); + AssertState(); return *this; } nsRegion& Sub(const nsRegion& aRegion, const nsRect& aRect) { - return Sub(aRegion, nsRegion(aRect)); + if (aRect.Contains(aRegion.mBounds)) { + SetEmpty(); + return *this; + } + if (&aRegion == this) { + return SubWith(aRect); + } +#ifdef DEBUG_REGIONS + class OperationStringGeneratorSub : public OperationStringGenerator + { + public: + OperationStringGeneratorSub(nsRegion& aRegion, const nsRegion& aRegionOther, const nsRect& aRect) + : mRegion(&aRegion), mRegionOther(aRegionOther), mRect(aRect) + { + aRegion.mCurrentOpGenerator = this; + } + virtual ~OperationStringGeneratorSub() + { + mRegion->mCurrentOpGenerator = nullptr; + } + + virtual void OutputOp() override + { + std::stringstream stream; + mRegionOther.OutputToStream("r1", stream); + stream << "nsRegion r2;\nr2.Sub(r1, nsRect(" << mRect.X() << ", " << mRect.Y() << ", " << mRect.Width() << ", " << mRect.Height() << "));\n"; + gfxCriticalError() << stream.str(); + } + private: + nsRegion * mRegion; + nsRegion mRegionOther; + nsRect mRect; + }; + + OperationStringGeneratorSub opGenerator(*this, aRegion, aRect); +#endif + + mBands.Clear(); + + if (aRegion.mBounds.IsEmpty()) { + mBounds.SetEmpty(); + return *this; + } + + if (aRect.IsEmpty()) { + Copy(aRegion); + return *this; + } + + if (aRegion.mBands.IsEmpty()) { + Copy(aRegion.mBounds); + return SubWith(aRect); + } + + const BandArray& bands = aRegion.mBands; + + size_t idx = 0; + + Strip strip(aRect.X(), aRect.XMost()); + + mBands.SetCapacity(bands.Length() + 3); + + // Process all bands that lie before aRect. + while (idx < bands.Length() && bands[idx].bottom <= aRect.Y()) { + mBands.AppendElement(bands[idx]); + idx++; + } + + // This band's bottom lies beyond aRect. + if (idx < bands.Length() && bands[idx].top < aRect.Y()) { + Band newBand(bands[idx]); + if (bands[idx].Intersects(strip)) { + newBand.bottom = aRect.Y(); + } else { + idx++; + } + mBands.AppendElement(std::move(newBand)); + } + + // This tracks whether the band when we -exit- the next loop intersected the rectangle. + bool didIntersect = false; + + while (idx < bands.Length() && bands[idx].top < aRect.YMost()) { + // Process all bands intersecting with aRect. + if (!bands[idx].Intersects(strip)) { + AppendOrExtend(bands[idx]); + idx++; + didIntersect = false; + continue; + } + + didIntersect = true; + Band newBand(bands[idx]); + newBand.top = std::max(newBand.top, aRect.Y()); + newBand.bottom = std::min(newBand.bottom, aRect.YMost()); + newBand.SubStrip(strip); + AppendOrExtend(std::move(newBand)); + idx++; + } + + if (didIntersect) { + if (aRect.YMost() < bands[idx - 1].bottom) { + // If this band does not intersect the loop above has already added the + // whole unmodified band. + Band newBand(bands[idx - 1]); + newBand.top = aRect.YMost(); + AppendOrExtend(std::move(newBand)); + } + } + + // Now process all bands beyond aRect. + if (idx < bands.Length()) { + AppendOrExtend(bands[idx]); + idx++; + } + + mBands.AppendElements(bands.Elements() + idx, bands.Length() - idx); + + if (mBands.IsEmpty()) { + mBounds.SetEmpty(); + } else { + mBounds = CalculateBounds(); + } + + AssertState(); + EnsureSimplified(); + return *this; } nsRegion& Sub(const nsRect& aRect, const nsRegion& aRegion) { - return Sub(nsRegion(aRect), aRegion); + Copy(aRect); + return SubWith(aRegion); } nsRegion& Sub(const nsRect& aRect1, const nsRect& aRect2) { Copy(aRect1); - return Sub(*this, aRect2); + return SubWith(aRect2); } /** - * Returns true iff the given point is inside the region. A region + * Returns true if the given point is inside the region. A region * created from a rect (x=0, y=0, w=100, h=100) will NOT contain * the point x=100, y=100. */ - bool Contains (int aX, int aY) const + bool Contains(int aX, int aY) const { - return pixman_region32_contains_point(Impl(), aX, aY, nullptr); - } - bool Contains (const nsRect& aRect) const - { - pixman_box32_t box = RectToBox(aRect); - return pixman_region32_contains_rectangle(Impl(), &box) == PIXMAN_REGION_IN; - } - bool Contains (const nsRegion& aRgn) const; - bool Intersects (const nsRect& aRect) const; + if (mBands.IsEmpty()) { + return mBounds.Contains(aX, aY); + } - void MoveBy (int32_t aXOffset, int32_t aYOffset) - { - MoveBy (nsPoint (aXOffset, aYOffset)); + auto iter = mBands.begin(); + + while (iter != mBands.end()) { + if (iter->bottom <= aY) { + iter++; + continue; + } + + if (iter->top > aY) { + return false; + } + + if (iter->ContainsStrip(Strip(aX, aX + 1))) { + return true; + } + return false; + } + return false; } - void MoveBy (nsPoint aPt) { pixman_region32_translate(&mImpl, aPt.x, aPt.y); } - void SetEmpty () + bool Contains(const nsRect& aRect) const { - pixman_region32_clear(&mImpl); + if (aRect.IsEmpty()) { + return false; + } + + if (mBands.IsEmpty()) { + return mBounds.Contains(aRect); + } + + auto iter = mBands.begin(); + + while (iter != mBands.end()) { + if (iter->bottom <= aRect.Y()) { + iter++; + continue; + } + + if (iter->top > aRect.Y()) { + return false; + } + + // Now inside the rectangle. + if (!iter->ContainsStrip(Strip(aRect.X(), aRect.XMost()))) { + return false; + } + + if (iter->bottom >= aRect.YMost()) { + return true; + } + + int32_t lastY = iter->bottom; + iter++; + while (iter != mBands.end()) { + // Bands do not connect. + if (iter->top != lastY) { + return false; + } + + if (!iter->ContainsStrip(Strip(aRect.X(), aRect.XMost()))) { + return false; + } + + if (iter->bottom >= aRect.YMost()) { + return true; + } + + lastY = iter->bottom; + iter++; + } + } + return false; + } + + bool Contains(const nsRegion& aRgn) const; + bool Intersects(const nsRect& aRect) const; + + void MoveBy(int32_t aXOffset, int32_t aYOffset) + { + MoveBy(nsPoint(aXOffset, aYOffset)); + } + void MoveBy(nsPoint aPt) + { +#ifdef DEBUG_REGIONS + class OperationStringGeneratorMoveBy : public OperationStringGenerator + { + public: + OperationStringGeneratorMoveBy(nsRegion& aRegion, const nsPoint& aPoint) + : mRegion(&aRegion), mRegionCopy(aRegion), mPoint(aPoint) + { + aRegion.mCurrentOpGenerator = this; + } + virtual ~OperationStringGeneratorMoveBy() + { + mRegion->mCurrentOpGenerator = nullptr; + } + + virtual void OutputOp() override + { + std::stringstream stream; + mRegionCopy.OutputToStream("r", stream); + stream << "r.MoveBy(nsPoint(" << mPoint.x << ", " << mPoint.y << "));\n"; + gfxCriticalError() << stream.str(); + } + private: + nsRegion * mRegion; + nsRegion mRegionCopy; + nsPoint mPoint; + }; + + OperationStringGeneratorMoveBy opGenerator(*this, aPt); +#endif + + mBounds.MoveBy(aPt); + for (Band& band : mBands) { + band.top += aPt.Y(); + band.bottom += aPt.Y(); + for (Strip& strip : band.mStrips) { + strip.left += aPt.X(); + strip.right += aPt.X(); + } + } + AssertState(); + } + void SetEmpty() + { + mBands.Clear(); + mBounds.SetEmpty(); } nsRegion MovedBy(int32_t aXOffset, int32_t aYOffset) const @@ -265,21 +1810,45 @@ public: return copy; } - bool IsEmpty () const { return !pixman_region32_not_empty(Impl()); } - bool IsComplex () const { return GetNumRects() > 1; } - bool IsEqual (const nsRegion& aRegion) const + bool IsEmpty() const { return mBounds.IsEmpty(); } + bool IsComplex() const { return GetNumRects() > 1; } + bool IsEqual(const nsRegion& aRegion) const { - return pixman_region32_equal(Impl(), aRegion.Impl()); + if (mBands.IsEmpty() && aRegion.mBands.IsEmpty()) { + return mBounds.IsEqualInterior(aRegion.mBounds); + } + + if (mBands.Length() != aRegion.mBands.Length()) { + return false; + } + + for (auto iter1 = mBands.begin(), iter2 = aRegion.mBands.begin(); + iter1 != mBands.end(); iter1++, iter2++) + { + if (!iter1->EqualStrips(*iter2)) { + return false; + } + } + + return true; } - uint32_t GetNumRects () const + + uint32_t GetNumRects() const { - // Work around pixman bug. Sometimes pixman creates regions with 1 rect - // that's empty. - uint32_t result = pixman_region32_n_rects(Impl()); - return (result == 1 && GetBounds().IsEmpty()) ? 0 : result; + if (mBands.IsEmpty()) { + return mBounds.IsEmpty() ? 0 : 1; + } + + uint32_t rects = 0; + + for (const Band& band : mBands) { + rects += band.mStrips.Length(); + } + + return rects; } - const nsRect GetBounds () const { return BoxToRect(mImpl.extents); } - uint64_t Area () const; + const nsRect GetBounds() const { return mBounds; } + uint64_t Area() const; /** * Return this region scaled to a different appunits per pixel (APP) ratio. @@ -289,17 +1858,17 @@ public: * @note this can turn an empty region into a non-empty region */ MOZ_MUST_USE nsRegion - ScaleToOtherAppUnitsRoundOut (int32_t aFromAPP, int32_t aToAPP) const; + ScaleToOtherAppUnitsRoundOut(int32_t aFromAPP, int32_t aToAPP) const; MOZ_MUST_USE nsRegion - ScaleToOtherAppUnitsRoundIn (int32_t aFromAPP, int32_t aToAPP) const; + ScaleToOtherAppUnitsRoundIn(int32_t aFromAPP, int32_t aToAPP) const; nsRegion& ScaleRoundOut(float aXScale, float aYScale); nsRegion& ScaleInverseRoundOut(float aXScale, float aYScale); - nsRegion& Transform (const mozilla::gfx::Matrix4x4 &aTransform); - nsIntRegion ScaleToOutsidePixels (float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const; - nsIntRegion ScaleToInsidePixels (float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const; - nsIntRegion ScaleToNearestPixels (float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const; - nsIntRegion ToOutsidePixels (nscoord aAppUnitsPerPixel) const; - nsIntRegion ToNearestPixels (nscoord aAppUnitsPerPixel) const; + nsRegion& Transform(const mozilla::gfx::Matrix4x4 &aTransform); + nsIntRegion ScaleToOutsidePixels(float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const; + nsIntRegion ScaleToInsidePixels(float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const; + nsIntRegion ScaleToNearestPixels(float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const; + nsIntRegion ToOutsidePixels(nscoord aAppUnitsPerPixel) const; + nsIntRegion ToNearestPixels(nscoord aAppUnitsPerPixel) const; /** * Gets the largest rectangle contained in the region. @@ -307,7 +1876,7 @@ public: * maximizes the area intersecting with aContainingRect (and break ties by * then choosing the largest rectangle overall) */ - nsRect GetLargestRectangle (const nsRect& aContainingRect = nsRect()) const; + nsRect GetLargestRectangle(const nsRect& aContainingRect = nsRect()) const; /** * Make sure the region has at most aMaxRects by adding area to it @@ -315,7 +1884,7 @@ public: * original region. The simplified region's bounding box will be * the same as for the current region. */ - void SimplifyOutward (uint32_t aMaxRects); + void SimplifyOutward(uint32_t aMaxRects); /** * Simplify the region by adding at most aThreshold area between spans of * rects. The simplified region will be a superset of the original region. @@ -328,7 +1897,7 @@ public: * it if necessary. The simplified region will be a subset of the * original region. */ - void SimplifyInward (uint32_t aMaxRects); + void SimplifyInward(uint32_t aMaxRects); /** * VisitEdges is a weird kind of function that we use for padding @@ -342,49 +1911,11 @@ public: * and specifies which kind of edge is being visited. x1, y1, x2, y2 * are the coordinates of the line. (x1 == x2) || (y1 == y2) */ - typedef void (*visitFn)(void *closure, VisitSide side, int x1, int y1, int x2, int y2); + typedef void(*visitFn)(void *closure, VisitSide side, int x1, int y1, int x2, int y2); void VisitEdges(visitFn, void *closure); nsCString ToString() const; - class RectIterator - { - int mCurrent; // Index of the current entry - int mLimit; // Index one past the final entry. - mutable nsRect mTmp; // The most recently gotten rectangle. - pixman_box32_t *mBoxes; - - public: - explicit RectIterator(const nsRegion& aRegion) - { - mCurrent = 0; - mBoxes = pixman_region32_rectangles(aRegion.Impl(), &mLimit); - // Work around pixman bug. Sometimes pixman creates regions with 1 rect - // that's empty. - if (mLimit == 1 && nsRegion::BoxToRect(mBoxes[0]).IsEmpty()) { - mLimit = 0; - } - } - - bool Done() const { return mCurrent == mLimit; } - - const nsRect& Get() const - { - MOZ_ASSERT(!Done()); - mTmp = nsRegion::BoxToRect(mBoxes[mCurrent]); - NS_ASSERTION(!mTmp.IsEmpty(), "Shouldn't return empty rect"); - return mTmp; - } - - void Next() - { - MOZ_ASSERT(!Done()); - mCurrent++; - } - }; - - RectIterator RectIter() const { return RectIterator(*this); } - static inline pixman_box32_t RectToBox(const nsRect &aRect) { pixman_box32_t box = { aRect.X(), aRect.Y(), aRect.XMost(), aRect.YMost() }; @@ -396,56 +1927,300 @@ public: pixman_box32_t box = { aRect.X(), aRect.Y(), aRect.XMost(), aRect.YMost() }; return box; } +private: + nsIntRegion ToPixels(nscoord aAppUnitsPerPixel, bool aOutsidePixels) const; + + nsRegion& Copy(const nsRegion& aRegion) + { + mBounds = aRegion.mBounds; + mBands = aRegion.mBands; + return *this; + } + + nsRegion& Copy(const nsRect& aRect) + { + mBands.Clear(); + mBounds = aRect; + return *this; + } + + void EnsureSimplified() { + if (mBands.Length() == 1 && mBands.begin()->mStrips.Length() == 1) { + mBands.Clear(); + } + } static inline nsRect BoxToRect(const pixman_box32_t &aBox) { return nsRect(aBox.x1, aBox.y1, - aBox.x2 - aBox.x1, - aBox.y2 - aBox.y1); + aBox.x2 - aBox.x1, + aBox.y2 - aBox.y1); } -private: - pixman_region32_t mImpl; - -#ifndef MOZ_TREE_PIXMAN - // For compatibility with pixman versions older than 0.25.2. - static inline void - pixman_region32_clear(pixman_region32_t *region) + void AddRect(const nsRect& aRect) { - pixman_region32_fini(region); - pixman_region32_init(region); +#ifdef DEBUG_REGIONS + class OperationStringGeneratorAddRect : public OperationStringGenerator + { + public: + OperationStringGeneratorAddRect(nsRegion& aRegion, const nsRect& aRect) + : mRegion(&aRegion), mRegionCopy(aRegion), mRect(aRect) + { + aRegion.mCurrentOpGenerator = this; + } + virtual ~OperationStringGeneratorAddRect() + { + mRegion->mCurrentOpGenerator = nullptr; + } + + virtual void OutputOp() override + { + std::stringstream stream; + mRegionCopy.OutputToStream("r", stream); + stream << "r.OrWith(nsRect(" << mRect.X() << ", " << mRect.Y() << ", " << mRect.Width() << ", " << mRect.Height() << "));\n"; + gfxCriticalError() << stream.str(); + } + private: + nsRegion* mRegion; + nsRegion mRegionCopy; + nsRect mRect; + }; + + OperationStringGeneratorAddRect opGenerator(*this, aRect); +#endif + if (aRect.IsEmpty()) { + return; + } + + if (aRect.Overflows()) { + // We don't accept rects which overflow. + gfxWarning() << "Passing overflowing rect to AddRect."; + return; + } + + if (mBands.IsEmpty()) { + if (mBounds.IsEmpty()) { + mBounds = aRect; + return; + } else if (mBounds.Contains(aRect)) { + return; + } + + mBands.AppendElement(Band(mBounds)); + } + + mBounds = aRect.UnsafeUnion(mBounds); + + size_t idx = 0; + + Strip strip(aRect.X(), aRect.XMost()); + Band remaining(aRect); + + while (idx != mBands.Length()) { + if (mBands[idx].bottom <= remaining.top) { + // This band lies wholly above aRect. + idx++; + continue; + } + + if (remaining.top >= remaining.bottom) { + AssertState(); + EnsureSimplified(); + return; + } + + if (mBands[idx].top >= remaining.bottom) { + // This band lies wholly below aRect. + break; + } + + if (mBands[idx].EqualStrips(remaining)) { + mBands[idx].top = std::min(mBands[idx].top, remaining.top); + // Nothing to do for this band. Just expand. + remaining.top = mBands[idx].bottom; + CompressBefore(idx); + idx++; + continue; + } + + if (mBands[idx].top > remaining.top) { + auto before = mBands.InsertElementAt(idx, remaining); + before->bottom = mBands[idx + 1].top; + remaining.top = before->bottom; + CompressBefore(idx); + idx++; + CompressBefore(idx); + continue; + } + + if (mBands[idx].ContainsStrip(strip)) { + remaining.top = mBands[idx].bottom; + idx++; + continue; + } + + // mBands[idx].top <= remaining.top. + + if (mBands[idx].top < remaining.top) { + auto before = mBands.InsertElementAt(idx, Band(mBands[idx])); + before->bottom = remaining.top; + idx++; + mBands[idx].top = remaining.top; + continue; + } + + // mBands[idx].top == remaining.top + if (mBands[idx].bottom > remaining.bottom) { + auto below = mBands.InsertElementAt(idx + 1, Band(mBands[idx])); + below->top = remaining.bottom; + mBands[idx].bottom = remaining.bottom; + } + + mBands[idx].InsertStrip(strip); + CompressBefore(idx); + remaining.top = mBands[idx].bottom; + idx++; + CompressBefore(idx); + } + + if (remaining.top < remaining.bottom) { + // We didn't find any bands that overlapped aRect. + if (idx) { + if (mBands[idx - 1].bottom == remaining.top && mBands[idx - 1].EqualStrips(remaining)) { + mBands[idx - 1].bottom = remaining.bottom; + CompressBefore(idx); + AssertState(); + EnsureSimplified(); + return; + } + } + mBands.InsertElementAt(idx, remaining); + idx++; + CompressBefore(idx); + } else { + CompressBefore(idx); + } + + AssertState(); + EnsureSimplified(); } + + // Most callers could probably do this on the fly, if this ever shows up + // in profiles we could optimize this. + nsRect CalculateBounds() const + { + if (mBands.IsEmpty()) { + return mBounds; + } + + int32_t top = mBands.begin()->top; + int32_t bottom = mBands.LastElement().bottom; + + int32_t leftMost = mBands.begin()->mStrips.begin()->left; + int32_t rightMost = mBands.begin()->mStrips.LastElement().right; + for (const Band& band : mBands) { + leftMost = std::min(leftMost, band.mStrips.begin()->left); + rightMost = std::max(rightMost, band.mStrips.LastElement().right); + } + + return nsRect(leftMost, top, rightMost - leftMost, bottom - top); + } + + static uint32_t ComputeMergedAreaIncrease(const Band& aTopBand, + const Band& aBottomBand); + + // Returns true if idx is now referring to the 'next' band + bool CompressAdjacentBands(size_t& aIdx) + { + if ((aIdx + 1) < mBands.Length()) { + if (mBands[aIdx + 1].top == mBands[aIdx].bottom && mBands[aIdx + 1].EqualStrips(mBands[aIdx])) { + mBands[aIdx].bottom = mBands[aIdx + 1].bottom; + mBands.RemoveElementAt(aIdx + 1); + } + } + if (aIdx) { + if (mBands[aIdx - 1].bottom == mBands[aIdx].top && mBands[aIdx].EqualStrips(mBands[aIdx - 1])) { + mBands[aIdx - 1].bottom = mBands[aIdx].bottom; + mBands.RemoveElementAt(aIdx); + return true; + } + } + return false; + } + + void CompressBefore(size_t& aIdx) + { + if (aIdx && aIdx < mBands.Length()) { + if (mBands[aIdx - 1].bottom == mBands[aIdx].top && mBands[aIdx - 1].EqualStrips(mBands[aIdx])) { + mBands[aIdx].top = mBands[aIdx - 1].top; + mBands.RemoveElementAt(aIdx - 1); + aIdx--; + } + } + } + + + BandArray mBands; + // Considering we only ever OR with nsRects, the bounds should fit in an nsRect as well. + nsRect mBounds; +#ifdef DEBUG_REGIONS + friend class OperationStringGenerator; + OperationStringGenerator* mCurrentOpGenerator; #endif - nsIntRegion ToPixels(nscoord aAppUnitsPerPixel, bool aOutsidePixels) const; - - nsRegion& Copy (const nsRegion& aRegion) +public: + class RectIterator { - pixman_region32_copy(&mImpl, aRegion.Impl()); - return *this; - } + const nsRegion& mRegion; + typename BandArray::const_iterator mCurrentBand; + typename StripArray::const_iterator mCurrentStrip; - nsRegion& Copy (const nsRect& aRect) - { - // pixman needs to distinguish between an empty region and a region - // with one rect so that it can return a different number of rectangles. - // Empty rect: data = empty_box - // 1 rect: data = null - // >1 rect: data = rects - if (aRect.IsEmpty()) { - pixman_region32_clear(&mImpl); - } else { - pixman_box32_t box = RectToBox(aRect); - pixman_region32_reset(&mImpl, &box); + public: + explicit RectIterator(const nsRegion& aRegion) + : mRegion(aRegion) + , mCurrentBand(aRegion.mBands.begin()) + { + mIsDone = mRegion.mBounds.IsEmpty(); + if (mCurrentBand != aRegion.mBands.end()) { + mCurrentStrip = mCurrentBand->mStrips.begin(); + } } - return *this; - } - pixman_region32_t* Impl() const - { - return const_cast(&mImpl); - } + bool Done() const { return mIsDone; } + + const nsRect Get() const + { + if (mRegion.mBands.IsEmpty()) { + return mRegion.mBounds; + } + return nsRect(mCurrentStrip->left, mCurrentBand->top, + mCurrentStrip->right - mCurrentStrip->left, + mCurrentBand->bottom - mCurrentBand->top); + } + + void Next() + { + if (mRegion.mBands.IsEmpty()) { + mIsDone = true; + return; + } + + mCurrentStrip++; + if (mCurrentStrip == mCurrentBand->mStrips.end()) { + mCurrentBand++; + if (mCurrentBand != mRegion.mBands.end()) { + mCurrentStrip = mCurrentBand->mStrips.begin(); + } else { + mIsDone = true; + } + } + } + + bool mIsDone; + }; + + RectIterator RectIter() const { return RectIterator(*this); } }; namespace mozilla { @@ -491,11 +2266,6 @@ public: return stream << m.mImpl; } - void Swap(Derived* aOther) - { - mImpl.Swap(&aOther->mImpl); - } - void AndWith(const Derived& aOther) { And(This(), aOther); @@ -639,7 +2409,7 @@ public: } void MoveBy (Point aPt) { - mImpl.MoveBy (aPt.x, aPt.y); + mImpl.MoveBy (aPt.X(), aPt.Y()); } Derived MovedBy(int32_t aXOffset, int32_t aYOffset) const { @@ -687,7 +2457,7 @@ public: nsRegion ToAppUnits (nscoord aAppUnitsPerPixel) const { nsRegion result; - for (auto iter = RectIter(); !iter.Done(); iter.Next()) { + for (auto iter = RectIterator(*this); !iter.Done(); iter.Next()) { nsRect appRect = ::ToAppUnits(iter.Get(), aAppUnitsPerPixel); result.Or(result, appRect); } diff --git a/gfx/tests/gtest/TestRegion.cpp b/gfx/tests/gtest/TestRegion.cpp index 405e16d0aa5d..e0b492cb5c58 100644 --- a/gfx/tests/gtest/TestRegion.cpp +++ b/gfx/tests/gtest/TestRegion.cpp @@ -16,6 +16,8 @@ using namespace std; using namespace mozilla::gfx; +//#define REGION_RANDOM_STRESS_TESTS + class TestLargestRegion { public: static void TestSingleRect(nsRect r) { @@ -182,6 +184,724 @@ TEST(Gfx, RegionScaleToInside) { } +TEST(Gfx, RegionOrWith) { + { + nsRegion r(nsRect(79, 31, 75, 12)); + r.OrWith(nsRect(22, 43, 132, 5)); + r.OrWith(nsRect(22, 48, 125, 3)); + r.OrWith(nsRect(22, 51, 96, 20)); + r.OrWith(nsRect(34, 71, 1, 14)); + r.OrWith(nsRect(26, 85, 53, 1)); + r.OrWith(nsRect(26, 86, 53, 4)); + r.OrWith(nsRect(96, 86, 30, 4)); + r.OrWith(nsRect(34, 90, 1, 2)); + r.OrWith(nsRect(96, 90, 30, 2)); + r.OrWith(nsRect(34, 92, 1, 3)); + r.OrWith(nsRect(49, 92, 34, 3)); + r.OrWith(nsRect(96, 92, 30, 3)); + r.OrWith(nsRect(34, 95, 1, 17)); + r.OrWith(nsRect(49, 95, 77, 17)); + r.OrWith(nsRect(34, 112, 1, 12)); + r.OrWith(nsRect(75, 112, 51, 12)); + r.OrWith(nsRect(34, 124, 1, 10)); + r.OrWith(nsRect(75, 124, 44, 10)); + r.OrWith(nsRect(34, 134, 1, 19)); + r.OrWith(nsRect(22, 17, 96, 27)); + } + { + nsRegion r(nsRect(0, 8, 257, 32)); + r.OrWith(nsRect(3702, 8, 138, 32)); + r.OrWith(nsRect(0, 40, 225, 1)); + r.OrWith(nsRect(3702, 40, 138, 1)); + r.OrWith(nsRect(0, 41, 101, 40)); + r.OrWith(nsRect(69, 41, 32, 40)); + } + { + nsRegion r(nsRect(79, 56, 8, 32)); + r.OrWith(nsRect(5, 94, 23, 81)); + r.OrWith(nsRect(56, 29, 91, 81)); + } + { + nsRegion r(nsRect(0, 82, 3840, 2046)); + r.OrWith(nsRect(0, 0, 3840, 82)); + } + { + nsRegion r(nsRect(2, 5, 600, 28)); + r.OrWith(nsRect(2, 82, 600, 19)); + r.OrWith(nsRect(2, 33, 600, 49)); + } + { + nsRegion r(nsRect(3823, 0, 17, 17)); + r.OrWith(nsRect(3823, 2029, 17, 17)); + r.OrWith(nsRect(3823, 0, 17, 2046)); + } + { + nsRegion r(nsRect(1036, 4, 32, 21)); + r.OrWith(nsRect(1070, 4, 66, 21)); + r.OrWith(nsRect(40, 5, 0, 33)); + } + { + nsRegion r(nsRect(0, 0, 1024, 1152)); + r.OrWith(nsRect(-335802, -1073741824, 1318851, 1860043520)); + } + { + nsRegion r(nsRect(0, 0, 800, 1000)); + r.OrWith(nsRect(0, 0, 536870912, 1073741824)); + } + { + nsRegion r(nsRect(53, 2, 52, 3)); + r.OrWith(nsRect(45, 5, 60, 16)); + r.OrWith(nsRect(16, 21, 8, 1)); + r.OrWith(nsRect(45, 21, 12, 1)); + r.OrWith(nsRect(16, 22, 8, 5)); + r.OrWith(nsRect(33, 22, 52, 5)); + r.OrWith(nsRect(16, 27, 8, 7)); + r.OrWith(nsRect(33, 27, 66, 7)); + r.OrWith(nsRect(0, 34, 99, 1)); + r.OrWith(nsRect(0, 35, 159, 27)); + r.OrWith(nsRect(0, 62, 122, 3)); + r.OrWith(nsRect(0, 65, 85, 11)); + r.OrWith(nsRect(91, 65, 97, 11)); + r.OrWith(nsRect(11, 76, 74, 2)); + r.OrWith(nsRect(91, 76, 97, 2)); + r.OrWith(nsRect(11, 78, 74, 12)); + r.OrWith(nsRect(11, 90, 13, 3)); + r.OrWith(nsRect(33, 90, 108, 3)); + r.OrWith(nsRect(16, 93, 8, 22)); + r.OrWith(nsRect(33, 93, 108, 22)); + r.OrWith(nsRect(16, 115, 8, 1)); + r.OrWith(nsRect(58, 115, 83, 1)); + r.OrWith(nsRect(58, 116, 83, 25)); + r.OrWith(nsRect(59, 37, 88, 92)); + } +#ifdef REGION_RANDOM_STRESS_TESTS + const uint32_t TestIterations = 100000; + const uint32_t RectsPerTest = 100; + + nsRect rects[RectsPerTest]; + + for (uint32_t i = 0; i < TestIterations; i++) { + nsRegion r; + for (uint32_t n = 0; n < RectsPerTest; n++) { + rects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); + } + r.SetEmpty(); + for (uint32_t n = 0; n < RectsPerTest; n++) { + r.OrWith(rects[n]); + } + for (uint32_t n = 0; n < RectsPerTest; n++) { + EXPECT_TRUE(r.Contains(rects[n])); + } + } +#endif +} + +TEST(Gfx, RegionSubWith) { + { + nsRegion r1(nsRect(0, 0, 100, 50)); + r1.OrWith(nsRect(50, 50, 50, 50)); + nsRegion r2(nsRect(0, 0, 100, 50)); + r2.OrWith(nsRect(50, 50, 50, 50)); + r1.SubWith(r2); + EXPECT_FALSE(r1.Contains(1, 1)); + } + { + nsRegion r1(nsRect(0, 0, 800, 1000)); + nsRegion r2(nsRect(8, 108, 22, 20)); + r2.OrWith(nsRect(91, 138, 17, 18)); + r1.SubWith(r2); + EXPECT_TRUE(r1.Contains(400, 130)); + } + { + nsRegion r1(nsRect(392, 2, 28, 7)); + r1.OrWith(nsRect(115, 9, 305, 16)); + r1.OrWith(nsRect(392, 25, 28, 5)); + r1.OrWith(nsRect(0, 32, 1280, 41)); + nsRegion r2(nsRect(0, 0, 1280, 9)); + r2.OrWith(nsRect(0, 9, 115, 16)); + r2.OrWith(nsRect(331, 9, 949, 16)); + r2.OrWith(nsRect(0, 25, 1280, 7)); + r2.OrWith(nsRect(331, 32, 124, 1)); + r1.SubWith(r2); + EXPECT_FALSE(r1.Contains(350, 15)); + } + { + nsRegion r1(nsRect(552, 0, 2, 2)); + r1.OrWith(nsRect(362, 2, 222, 28)); + r1.OrWith(nsRect(552, 30, 2, 2)); + r1.OrWith(nsRect(0, 32, 1280, 41)); + nsRegion r2(nsRect(512, 0, 146, 9)); + r2.OrWith(nsRect(340, 9, 318, 16)); + r2.OrWith(nsRect(512, 25, 146, 8)); + r1.SubWith(r2); + EXPECT_FALSE(r1.Contains(350, 15)); + } + { + nsRegion r(nsRect(0, 0, 229380, 6780)); + r.OrWith(nsRect(76800, 6780, 76800, 4440)); + r.OrWith(nsRect(76800, 11220, 44082, 1800)); + r.OrWith(nsRect(122682, 11220, 30918, 1800)); + r.OrWith(nsRect(76800, 13020, 76800, 2340)); + r.OrWith(nsRect(85020, 15360, 59340, 75520)); + r.OrWith(nsRect(85020, 90880, 38622, 11332)); + r.OrWith(nsRect(143789, 90880, 571, 11332)); + r.OrWith(nsRect(85020, 102212, 59340, 960)); + r.OrWith(nsRect(85020, 103172, 38622, 1560)); + r.OrWith(nsRect(143789, 103172, 571, 1560)); + r.OrWith(nsRect(85020, 104732, 59340, 12292)); + r.OrWith(nsRect(85020, 117024, 38622, 1560)); + r.OrWith(nsRect(143789, 117024, 571, 1560)); + r.OrWith(nsRect(85020, 118584, 59340, 11976)); + r.SubWith(nsRect(123642, 89320, 20147, 1560)); + } + { + nsRegion r(nsRect(0, 0, 9480, 12900)); + r.OrWith(nsRect(0, 12900, 8460, 1020)); + r.SubWith(nsRect(8460, 0, 1020, 12900)); + } + { + nsRegion r1(nsRect(99, 1, 51, 2)); + r1.OrWith(nsRect(85, 3, 65, 1)); + r1.OrWith(nsRect(10, 4, 66, 5)); + r1.OrWith(nsRect(85, 4, 37, 5)); + r1.OrWith(nsRect(10, 9, 112, 3)); + r1.OrWith(nsRect(1, 12, 121, 1)); + r1.OrWith(nsRect(1, 13, 139, 3)); + r1.OrWith(nsRect(0, 16, 140, 3)); + r1.OrWith(nsRect(0, 19, 146, 3)); + r1.OrWith(nsRect(0, 22, 149, 2)); + r1.OrWith(nsRect(0, 24, 154, 2)); + r1.OrWith(nsRect(0, 26, 160, 23)); + r1.OrWith(nsRect(0, 49, 162, 31)); + r1.OrWith(nsRect(0, 80, 171, 19)); + r1.OrWith(nsRect(0, 99, 173, 11)); + r1.OrWith(nsRect(2, 110, 171, 6)); + r1.OrWith(nsRect(6, 116, 165, 5)); + r1.OrWith(nsRect(8, 121, 163, 1)); + r1.OrWith(nsRect(13, 122, 158, 11)); + r1.OrWith(nsRect(14, 133, 157, 23)); + r1.OrWith(nsRect(29, 156, 142, 10)); + r1.OrWith(nsRect(37, 166, 134, 6)); + r1.OrWith(nsRect(55, 172, 4, 4)); + r1.OrWith(nsRect(83, 172, 88, 4)); + r1.OrWith(nsRect(55, 176, 4, 2)); + r1.OrWith(nsRect(89, 176, 6, 2)); + r1.OrWith(nsRect(89, 178, 6, 4)); + nsRegion r2(nsRect(63, 11, 39, 11)); + r2.OrWith(nsRect(63, 22, 99, 16)); + r2.OrWith(nsRect(37, 38, 125, 61)); + r2.OrWith(nsRect(45, 99, 117, 8)); + r2.OrWith(nsRect(47, 107, 115, 7)); + r2.OrWith(nsRect(47, 114, 66, 1)); + r2.OrWith(nsRect(49, 115, 64, 2)); + r2.OrWith(nsRect(49, 117, 54, 30)); + r1.SubWith(r2); + } + { + nsRegion r1(nsRect(95, 2, 47, 1)); + r1.OrWith(nsRect(62, 3, 80, 2)); + r1.OrWith(nsRect(1, 5, 18, 3)); + r1.OrWith(nsRect(48, 5, 94, 3)); + r1.OrWith(nsRect(1, 8, 18, 3)); + r1.OrWith(nsRect(23, 8, 119, 3)); + r1.OrWith(nsRect(1, 11, 172, 9)); + r1.OrWith(nsRect(1, 20, 18, 8)); + r1.OrWith(nsRect(20, 20, 153, 8)); + r1.OrWith(nsRect(1, 28, 172, 13)); + r1.OrWith(nsRect(1, 41, 164, 1)); + r1.OrWith(nsRect(1, 42, 168, 1)); + r1.OrWith(nsRect(0, 43, 169, 15)); + r1.OrWith(nsRect(1, 58, 168, 26)); + r1.OrWith(nsRect(1, 84, 162, 2)); + r1.OrWith(nsRect(1, 86, 165, 23)); + r1.OrWith(nsRect(1, 109, 162, 23)); + r1.OrWith(nsRect(1, 132, 152, 4)); + r1.OrWith(nsRect(1, 136, 150, 12)); + r1.OrWith(nsRect(12, 148, 139, 4)); + r1.OrWith(nsRect(12, 152, 113, 2)); + r1.OrWith(nsRect(14, 154, 31, 3)); + r1.OrWith(nsRect(82, 154, 43, 3)); + r1.OrWith(nsRect(17, 157, 13, 19)); + r1.OrWith(nsRect(82, 157, 43, 19)); + r1.OrWith(nsRect(17, 176, 13, 16)); + nsRegion r2(nsRect(97, 9, 6, 10)); + r2.OrWith(nsRect(71, 19, 32, 2)); + r2.OrWith(nsRect(20, 21, 83, 2)); + r2.OrWith(nsRect(2, 23, 101, 9)); + r2.OrWith(nsRect(2, 32, 98, 1)); + r2.OrWith(nsRect(2, 33, 104, 5)); + r2.OrWith(nsRect(2, 38, 118, 2)); + r2.OrWith(nsRect(15, 40, 9, 11)); + r2.OrWith(nsRect(36, 40, 84, 11)); + r2.OrWith(nsRect(4, 51, 116, 33)); + r2.OrWith(nsRect(4, 84, 159, 8)); + r2.OrWith(nsRect(4, 92, 116, 13)); + r2.OrWith(nsRect(15, 105, 9, 7)); + r2.OrWith(nsRect(36, 105, 84, 7)); + r2.OrWith(nsRect(36, 112, 84, 22)); + r2.OrWith(nsRect(71, 134, 39, 46)); + r1.SubWith(r2); + } +#ifdef REGION_RANDOM_STRESS_TESTS + const uint32_t TestIterations = 100000; + const uint32_t RectsPerTest = 100; + const uint32_t SubRectsPerTest = 10; + + nsRect rects[RectsPerTest]; + nsRect subRects[SubRectsPerTest]; + + for (uint32_t i = 0; i < TestIterations; i++) { + nsRegion r; + for (uint32_t n = 0; n < RectsPerTest; n++) { + rects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); + } + r.SetEmpty(); + for (uint32_t n = 0; n < RectsPerTest; n++) { + r.OrWith(rects[n]); + } + for (uint32_t n = 0; n < RectsPerTest; n++) { + EXPECT_TRUE(r.Contains(rects[n])); + } + for (uint32_t n = 0; n < SubRectsPerTest; n++) { + subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); + } + for (uint32_t n = 0; n < SubRectsPerTest; n++) { + r.SubWith(subRects[n]); + } + for (uint32_t n = 0; n < SubRectsPerTest; n++) { + EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].y)); + EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].YMost() - 1)); + EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1)); + EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].Y())); + } + } + for (uint32_t i = 0; i < TestIterations; i++) { + nsRegion r; + for (uint32_t n = 0; n < RectsPerTest; n++) { + rects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); + } + r.SetEmpty(); + for (uint32_t n = 0; n < RectsPerTest; n++) { + r.OrWith(rects[n]); + } + for (uint32_t n = 0; n < RectsPerTest; n++) { + EXPECT_TRUE(r.Contains(rects[n])); + } + for (uint32_t n = 0; n < SubRectsPerTest; n++) { + subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); + } + nsRegion r2; + for (uint32_t n = 0; n < SubRectsPerTest; n++) { + r2.OrWith(subRects[n]); + } + r.SubWith(r2); + + for (uint32_t n = 0; n < SubRectsPerTest; n++) { + EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].y)); + EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].YMost() - 1)); + EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1)); + EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].Y())); + } + } + for (uint32_t i = 0; i < TestIterations; i++) { + nsRegion r(nsRect(-1, -1, 202, 202)); + for (uint32_t n = 0; n < SubRectsPerTest; n++) { + subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); + r.SubWith(subRects[n]); + } + for (uint32_t n = 0; n < SubRectsPerTest; n++) { + EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].y)); + EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].YMost() - 1)); + EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1)); + EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].Y())); + } + EXPECT_TRUE(r.Contains(-1, -1)); + EXPECT_TRUE(r.Contains(-1, 200)); + EXPECT_TRUE(r.Contains(200, -1)); + EXPECT_TRUE(r.Contains(200, 200)); + } + for (uint32_t i = 0; i < TestIterations; i++) { + nsRegion r(nsRect(-1, -1, 202, 202)); + nsRegion r2; + for (uint32_t n = 0; n < SubRectsPerTest; n++) { + subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); + r2.OrWith(subRects[n]); + } + r.SubWith(r2); + for (uint32_t n = 0; n < SubRectsPerTest; n++) { + EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].y)); + EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].YMost() - 1)); + EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1)); + EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].Y())); + } + EXPECT_TRUE(r.Contains(-1, -1)); + EXPECT_TRUE(r.Contains(-1, 200)); + EXPECT_TRUE(r.Contains(200, -1)); + EXPECT_TRUE(r.Contains(200, 200)); + } +#endif +} +TEST(Gfx, RegionSub) { + { + nsRegion r1(nsRect(0, 0, 100, 50)); + r1.OrWith(nsRect(50, 50, 50, 50)); + nsRegion r2(nsRect(0, 0, 100, 50)); + r2.OrWith(nsRect(50, 50, 50, 50)); + nsRegion r3; + r3.Sub(r1, r2); + EXPECT_FALSE(r3.Contains(1, 1)); + } + { + nsRegion r1(nsRect(0, 0, 800, 1000)); + nsRegion r2(nsRect(8, 108, 22, 20)); + r2.OrWith(nsRect(91, 138, 17, 18)); + nsRegion r3; + r3.Sub(r1, r2); + EXPECT_TRUE(r3.Contains(400, 130)); + } + { + nsRegion r1(nsRect(392, 2, 28, 7)); + r1.OrWith(nsRect(115, 9, 305, 16)); + r1.OrWith(nsRect(392, 25, 28, 5)); + r1.OrWith(nsRect(0, 32, 1280, 41)); + nsRegion r2(nsRect(0, 0, 1280, 9)); + r2.OrWith(nsRect(0, 9, 115, 16)); + r2.OrWith(nsRect(331, 9, 949, 16)); + r2.OrWith(nsRect(0, 25, 1280, 7)); + r2.OrWith(nsRect(331, 32, 124, 1)); + nsRegion r3; + r3.Sub(r1, r2); + EXPECT_FALSE(r3.Contains(350, 15)); + } + { + nsRegion r1(nsRect(552, 0, 2, 2)); + r1.OrWith(nsRect(362, 2, 222, 28)); + r1.OrWith(nsRect(552, 30, 2, 2)); + r1.OrWith(nsRect(0, 32, 1280, 41)); + nsRegion r2(nsRect(512, 0, 146, 9)); + r2.OrWith(nsRect(340, 9, 318, 16)); + r2.OrWith(nsRect(512, 25, 146, 8)); + nsRegion r3; + r3.Sub(r1, r2); + EXPECT_FALSE(r3.Contains(350, 15)); + } + { + nsRegion r1(nsRect(0, 0, 1265, 1024)); + nsRegion r2(nsRect(1265, 0, 15, 685)); + r2.OrWith(nsRect(0, 714, 1280, 221)); + nsRegion r3; + r3.Sub(r1, r2); + } + { + nsRegion r1(nsRect(6, 0, 64, 1)); + r1.OrWith(nsRect(6, 1, 67, 1)); + r1.OrWith(nsRect(6, 2, 67, 2)); + r1.OrWith(nsRect(79, 2, 67, 2)); + r1.OrWith(nsRect(6, 4, 67, 1)); + r1.OrWith(nsRect(79, 4, 98, 1)); + r1.OrWith(nsRect(6, 5, 171, 18)); + r1.OrWith(nsRect(1, 23, 176, 3)); + r1.OrWith(nsRect(1, 26, 178, 5)); + r1.OrWith(nsRect(1, 31, 176, 9)); + r1.OrWith(nsRect(0, 40, 177, 57)); + r1.OrWith(nsRect(0, 97, 176, 33)); + r1.OrWith(nsRect(0, 130, 12, 17)); + r1.OrWith(nsRect(15, 130, 161, 17)); + r1.OrWith(nsRect(0, 147, 12, 5)); + r1.OrWith(nsRect(15, 147, 111, 5)); + r1.OrWith(nsRect(0, 152, 12, 7)); + r1.OrWith(nsRect(17, 152, 109, 7)); + r1.OrWith(nsRect(0, 159, 12, 2)); + r1.OrWith(nsRect(17, 159, 98, 2)); + r1.OrWith(nsRect(17, 161, 98, 9)); + r1.OrWith(nsRect(27, 170, 63, 21)); + nsRegion r2(nsRect(9, 9, 37, 17)); + r2.OrWith(nsRect(92, 9, 26, 17)); + r2.OrWith(nsRect(9, 26, 37, 9)); + r2.OrWith(nsRect(84, 26, 65, 9)); + r2.OrWith(nsRect(9, 35, 37, 2)); + r2.OrWith(nsRect(51, 35, 98, 2)); + r2.OrWith(nsRect(51, 37, 98, 11)); + r2.OrWith(nsRect(51, 48, 78, 4)); + r2.OrWith(nsRect(87, 52, 42, 7)); + r2.OrWith(nsRect(19, 59, 12, 5)); + r2.OrWith(nsRect(87, 59, 42, 5)); + r2.OrWith(nsRect(19, 64, 12, 9)); + r2.OrWith(nsRect(32, 64, 97, 9)); + r2.OrWith(nsRect(19, 73, 12, 2)); + r2.OrWith(nsRect(32, 73, 104, 2)); + r2.OrWith(nsRect(19, 75, 117, 5)); + r2.OrWith(nsRect(18, 80, 118, 5)); + r2.OrWith(nsRect(18, 85, 111, 38)); + r2.OrWith(nsRect(87, 123, 42, 11)); + nsRegion r3; + r3.Sub(r1, r2); + } + { + nsRegion r1(nsRect(27, 0, 39, 1)); + r1.OrWith(nsRect(86, 0, 22, 1)); + r1.OrWith(nsRect(27, 1, 43, 1)); + r1.OrWith(nsRect(86, 1, 22, 1)); + r1.OrWith(nsRect(27, 2, 43, 1)); + r1.OrWith(nsRect(86, 2, 75, 1)); + r1.OrWith(nsRect(12, 3, 58, 1)); + r1.OrWith(nsRect(86, 3, 75, 1)); + r1.OrWith(nsRect(12, 4, 149, 5)); + r1.OrWith(nsRect(0, 9, 161, 9)); + r1.OrWith(nsRect(0, 18, 167, 17)); + r1.OrWith(nsRect(0, 35, 171, 5)); + r1.OrWith(nsRect(0, 40, 189, 28)); + r1.OrWith(nsRect(0, 68, 171, 16)); + r1.OrWith(nsRect(4, 84, 167, 5)); + r1.OrWith(nsRect(4, 89, 177, 9)); + r1.OrWith(nsRect(1, 98, 180, 59)); + r1.OrWith(nsRect(4, 157, 177, 1)); + r1.OrWith(nsRect(4, 158, 139, 15)); + r1.OrWith(nsRect(17, 173, 126, 2)); + r1.OrWith(nsRect(20, 175, 123, 2)); + r1.OrWith(nsRect(20, 177, 118, 6)); + r1.OrWith(nsRect(20, 183, 84, 2)); + nsRegion r2(nsRect(64, 2, 30, 6)); + r2.OrWith(nsRect(26, 11, 41, 17)); + r2.OrWith(nsRect(19, 28, 48, 23)); + r2.OrWith(nsRect(19, 51, 76, 8)); + r2.OrWith(nsRect(4, 59, 91, 31)); + r2.OrWith(nsRect(19, 90, 76, 29)); + r2.OrWith(nsRect(33, 119, 62, 25)); + r2.OrWith(nsRect(33, 144, 4, 21)); + nsRegion r3; + r3.Sub(r1, r2); + } +#ifdef REGION_RANDOM_STRESS_TESTS + const uint32_t TestIterations = 100000; + const uint32_t RectsPerTest = 100; + const uint32_t SubRectsPerTest = 10; + + nsRect rects[RectsPerTest]; + nsRect subRects[SubRectsPerTest]; + + for (uint32_t i = 0; i < TestIterations; i++) { + nsRegion r; + for (uint32_t n = 0; n < RectsPerTest; n++) { + rects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); + } + r.SetEmpty(); + for (uint32_t n = 0; n < RectsPerTest; n++) { + r.OrWith(rects[n]); + } + for (uint32_t n = 0; n < RectsPerTest; n++) { + EXPECT_TRUE(r.Contains(rects[n])); + } + for (uint32_t n = 0; n < SubRectsPerTest; n++) { + subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); + } + nsRegion r2; + for (uint32_t n = 0; n < SubRectsPerTest; n++) { + r2.OrWith(subRects[n]); + } + nsRegion r3; + r3.Sub(r, r2); + + for (uint32_t n = 0; n < SubRectsPerTest; n++) { + EXPECT_FALSE(r3.Contains(subRects[n].x, subRects[n].y)); + EXPECT_FALSE(r3.Contains(subRects[n].x, subRects[n].YMost() - 1)); + EXPECT_FALSE(r3.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1)); + EXPECT_FALSE(r3.Contains(subRects[n].XMost() - 1, subRects[n].Y())); + } + } + for (uint32_t i = 0; i < TestIterations; i++) { + nsRegion r(nsRect(-1, -1, 202, 202)); + nsRegion r2; + for (uint32_t n = 0; n < SubRectsPerTest; n++) { + subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); + r2.OrWith(subRects[n]); + } + nsRegion r3; + r3.Sub(r, r2); + for (uint32_t n = 0; n < SubRectsPerTest; n++) { + EXPECT_FALSE(r3.Contains(subRects[n].x, subRects[n].y)); + EXPECT_FALSE(r3.Contains(subRects[n].x, subRects[n].YMost() - 1)); + EXPECT_FALSE(r3.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1)); + EXPECT_FALSE(r3.Contains(subRects[n].XMost() - 1, subRects[n].Y())); + } + EXPECT_TRUE(r3.Contains(-1, -1)); + EXPECT_TRUE(r3.Contains(-1, 200)); + EXPECT_TRUE(r3.Contains(200, -1)); + EXPECT_TRUE(r3.Contains(200, 200)); + } +#endif +} + +TEST(Gfx, RegionAndWith) { + { + nsRegion r(nsRect(20, 0, 20, 20)); + r.OrWith(nsRect(0, 20, 40, 20)); + r.AndWith(nsRect(0, 0, 5, 5)); + EXPECT_FALSE(r.Contains(1, 1)); + } + { + nsRegion r1(nsRect(512, 1792, 256, 256)); + nsRegion r2(nsRect(17, 1860, 239, 35)); + r2.OrWith(nsRect(17, 1895, 239, 7)); + r2.OrWith(nsRect(768, 1895, 154, 7)); + r2.OrWith(nsRect(17, 1902, 905, 483)); + r1.AndWith(r2); + } +#ifdef REGION_RANDOM_STRESS_TESTS + const uint32_t TestIterations = 100000; + const uint32_t RectsPerTest = 50; + const uint32_t pointsTested = 100; + + { + nsRect rectsSet1[RectsPerTest]; + nsRect rectsSet2[RectsPerTest]; + + for (uint32_t i = 0; i < TestIterations; i++) { + nsRegion r1; + nsRegion r2; + for (uint32_t n = 0; n < RectsPerTest; n++) { + rectsSet1[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); + rectsSet2[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); + r1.OrWith(rectsSet1[n]); + r2.OrWith(rectsSet1[n]); + } + + nsRegion r3 = r1; + r3.AndWith(r2); + + for (uint32_t n = 0; n < pointsTested; n++) { + nsPoint p(rand() % 200, rand() % 200); + if (r1.Contains(p.x, p.y) && r2.Contains(p.x, p.y)) { + EXPECT_TRUE(r3.Contains(p.x, p.y)); + } else { + EXPECT_FALSE(r3.Contains(p.x, p.y)); + } + } + } + } + + { + nsRect rectsSet[RectsPerTest]; + nsRect testRect; + for (uint32_t i = 0; i < TestIterations; i++) { + nsRegion r; + for (uint32_t n = 0; n < RectsPerTest; n++) { + rectsSet[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); + } + for (uint32_t n = 0; n < RectsPerTest; n++) { + r.OrWith(rectsSet[n]); + } + testRect.SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); + + nsRegion r2 = r; + r2.AndWith(testRect); + + for (uint32_t n = 0; n < pointsTested; n++) { + nsPoint p(rand() % 200, rand() % 200); + if (r.Contains(p.x, p.y) && testRect.Contains(p.x, p.y)) { + EXPECT_TRUE(r2.Contains(p.x, p.y)); + } else { + EXPECT_FALSE(r2.Contains(p.x, p.y)); + } + } + } + } +#endif +} + +TEST(Gfx, RegionAnd) { + { + nsRegion r(nsRect(20, 0, 20, 20)); + r.OrWith(nsRect(0, 20, 40, 20)); + nsRegion r2; + r2.And(r, nsRect(0, 0, 5, 5)); + EXPECT_FALSE(r.Contains(1, 1)); + } + { + nsRegion r(nsRect(51, 2, 57, 5)); + r.OrWith(nsRect(36, 7, 72, 4)); + r.OrWith(nsRect(36, 11, 25, 1)); + r.OrWith(nsRect(69, 12, 6, 4)); + r.OrWith(nsRect(37, 16, 54, 2)); + r.OrWith(nsRect(37, 18, 82, 2)); + r.OrWith(nsRect(10, 20, 109, 3)); + r.OrWith(nsRect(1, 23, 136, 21)); + r.OrWith(nsRect(1, 44, 148, 2)); + r.OrWith(nsRect(1, 46, 176, 31)); + r.OrWith(nsRect(6, 77, 171, 1)); + r.OrWith(nsRect(5, 78, 172, 30)); + r.OrWith(nsRect(5, 108, 165, 45)); + r.OrWith(nsRect(5, 153, 61, 5)); + r.OrWith(nsRect(72, 153, 98, 5)); + r.OrWith(nsRect(38, 158, 25, 4)); + r.OrWith(nsRect(72, 158, 98, 4)); + r.OrWith(nsRect(58, 162, 5, 8)); + r.OrWith(nsRect(72, 162, 98, 8)); + r.OrWith(nsRect(72, 170, 98, 5)); + nsRegion r2; + r.And(r2, nsRect(18, 78, 53, 45)); + } +#ifdef REGION_RANDOM_STRESS_TESTS + const uint32_t TestIterations = 100000; + const uint32_t RectsPerTest = 50; + const uint32_t pointsTested = 100; + + { + nsRect rectsSet1[RectsPerTest]; + nsRect rectsSet2[RectsPerTest]; + + for (uint32_t i = 0; i < TestIterations; i++) { + nsRegion r1; + nsRegion r2; + for (uint32_t n = 0; n < RectsPerTest; n++) { + rectsSet1[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); + rectsSet2[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); + r1.OrWith(rectsSet1[n]); + r2.OrWith(rectsSet1[n]); + } + + nsRegion r3; + r3.And(r1, r2); + + for (uint32_t n = 0; n < pointsTested; n++) { + nsPoint p(rand() % 200, rand() % 200); + if (r1.Contains(p.x, p.y) && r2.Contains(p.x, p.y)) { + EXPECT_TRUE(r3.Contains(p.x, p.y)); + } else { + EXPECT_FALSE(r3.Contains(p.x, p.y)); + } + } + } + } + + { + nsRect rectsSet[RectsPerTest]; + nsRect testRect; + for (uint32_t i = 0; i < TestIterations; i++) { + nsRegion r; + for (uint32_t n = 0; n < RectsPerTest; n++) { + rectsSet[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); + } + for (uint32_t n = 0; n < RectsPerTest; n++) { + r.OrWith(rectsSet[n]); + } + testRect.SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); + + nsRegion r2; + r2.And(r, testRect); + + for (uint32_t n = 0; n < pointsTested; n++) { + nsPoint p(rand() % 200, rand() % 200); + if (r.Contains(p.x, p.y) && testRect.Contains(p.x, p.y)) { + EXPECT_TRUE(r2.Contains(p.x, p.y)); + } else { + EXPECT_FALSE(r2.Contains(p.x, p.y)); + } + } + } + } +#endif +} TEST(Gfx, RegionSimplify) { { // ensure simplify works on a single rect diff --git a/layout/painting/FrameLayerBuilder.cpp b/layout/painting/FrameLayerBuilder.cpp index cd64c556ece2..a263ee5713b1 100644 --- a/layout/painting/FrameLayerBuilder.cpp +++ b/layout/painting/FrameLayerBuilder.cpp @@ -5934,7 +5934,7 @@ FrameLayerBuilder::RecomputeVisibilityForItems(nsTArray& aI newVisible.Sub(visible, removed); // Don't let the visible region get too complex. if (newVisible.GetNumRects() <= 15) { - visible = newVisible; + visible = Move(newVisible); } } } diff --git a/layout/svg/nsFilterInstance.cpp b/layout/svg/nsFilterInstance.cpp index faeb079a0e71..51202c689be4 100644 --- a/layout/svg/nsFilterInstance.cpp +++ b/layout/svg/nsFilterInstance.cpp @@ -608,7 +608,8 @@ nsFilterInstance::FrameSpaceToFilterSpace(const nsRegion* aRegion) const nsIntRegion result; for (auto iter = aRegion->RectIter(); !iter.Done(); iter.Next()) { // FrameSpaceToFilterSpace rounds out, so this works. - result.Or(result, FrameSpaceToFilterSpace(&iter.Get())); + nsRect rect = iter.Get(); + result.Or(result, FrameSpaceToFilterSpace(&rect)); } return result; } From fa9dc40ccb01354b944decc7d7a36db8473924b8 Mon Sep 17 00:00:00 2001 From: UK992 Date: Wed, 25 Oct 2017 22:50:01 +0200 Subject: [PATCH 02/25] Bug 1373921 - Remove duplicated css code from about:support/profiles/url-classifier. r=dao --- toolkit/content/aboutProfiles.xhtml | 5 +- toolkit/content/aboutSupport.xhtml | 6 +-- toolkit/content/aboutUrlClassifier.css | 45 +--------------- toolkit/content/aboutUrlClassifier.xhtml | 3 +- toolkit/themes/shared/aboutProfiles.css | 45 +--------------- toolkit/themes/shared/aboutSupport.css | 53 +------------------ .../themes/shared/in-content/common.inc.css | 1 + .../shared/in-content/info-pages.inc.css | 48 ++++++++++++++++- 8 files changed, 59 insertions(+), 147 deletions(-) diff --git a/toolkit/content/aboutProfiles.xhtml b/toolkit/content/aboutProfiles.xhtml index 52a3b32f7fa5..e960e76000c0 100644 --- a/toolkit/content/aboutProfiles.xhtml +++ b/toolkit/content/aboutProfiles.xhtml @@ -15,12 +15,11 @@ &aboutProfiles.title; - - +

&aboutUrlClassifier.title;

&aboutUrlClassifier.providerTitle;

diff --git a/toolkit/themes/shared/aboutProfiles.css b/toolkit/themes/shared/aboutProfiles.css index 0640b71f9d8d..1cc5a047993d 100644 --- a/toolkit/themes/shared/aboutProfiles.css +++ b/toolkit/themes/shared/aboutProfiles.css @@ -2,14 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -html { - --aboutProfiles-table-background: #ebebeb; - background-color: var(--in-content-page-background); -} - -body { - margin: 40px 48px; -} + @import url("chrome://global/skin/in-content/info-pages.css"); .page-subtitle { margin-bottom: 3em; @@ -18,49 +11,15 @@ body { button { margin-inline-start: 0; margin-inline-end: 8px; -} - -table { - background-color: var(--aboutProfiles-table-background); - color: var(--in-content-text-color); - font: message-box; - text-align: start; - width: 100%; - border: 1px solid var(--in-content-border-color); - border-spacing: 0px; -} - -th, td { - border: 1px solid var(--in-content-border-color); - padding: 4px; - text-align: start; -} - -th { - background-color: var(--in-content-table-header-background); - color: var(--in-content-selected-text); -} - -th.column { - white-space: nowrap; - width: 0px; -} - -td { - border-color: var(--in-content-table-border-dark-color); - unicode-bidi: plaintext; /* Make sure file paths will be LTR */ + padding: 3px; } #action-box { - background-color: var(--aboutProfiles-table-background); - border: 1px solid var(--in-content-border-color); - color: var(--in-content-text-color); float: right; margin-top: 2em; margin-bottom: 20px; margin-inline-start: 20px; margin-inline-end: 0; - padding: 16px; width: 30%; } diff --git a/toolkit/themes/shared/aboutSupport.css b/toolkit/themes/shared/aboutSupport.css index c9092ac8adf6..366481476214 100644 --- a/toolkit/themes/shared/aboutSupport.css +++ b/toolkit/themes/shared/aboutSupport.css @@ -2,14 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -html { - --aboutSupport-table-background: #ebebeb; - background-color: var(--in-content-page-background); -} - -body { - margin: 40px 48px; -} +@import url("chrome://global/skin/in-content/info-pages.css"); .page-subtitle { margin-bottom: 3em; @@ -26,31 +19,7 @@ body { button { margin-inline-start: 0; margin-inline-end: 8px; -} - -table { - background-color: var(--aboutSupport-table-background); - color: var(--in-content-text-color); - font: message-box; - text-align: start; - width: 100%; - border: 1px solid var(--in-content-border-color); - border-spacing: 0px; -} - -th, td { - border: 1px solid var(--in-content-border-color); - padding: 4px; -} - -thead th { - text-align: center; -} - -th { - text-align: start; - background-color: var(--in-content-table-header-background); - color: var(--in-content-selected-text); + padding: 3px; } th.title-column { @@ -59,16 +28,6 @@ th.title-column { font-size: medium; } -th.column { - white-space: nowrap; - width: 0px; -} - -td { - text-align: start; - border-color: var(--in-content-table-border-dark-color); -} - td.integer { text-align: end; font-family: monospace; @@ -92,15 +51,11 @@ td.integer { } #action-box { - background-color: var(--aboutSupport-table-background); - border: 1px solid var(--in-content-border-color); - color: var(--in-content-text-color); float: right; margin-top: 2em; margin-bottom: 20px; margin-inline-start: 20px; margin-inline-end: 0; - padding: 16px; width: 30%; } @@ -127,10 +82,6 @@ td.integer { overflow: auto; } -.block { - display: block; -} - .hidden { display: none; } diff --git a/toolkit/themes/shared/in-content/common.inc.css b/toolkit/themes/shared/in-content/common.inc.css index d1d640ee0c5b..619f8fc9c7dc 100644 --- a/toolkit/themes/shared/in-content/common.inc.css +++ b/toolkit/themes/shared/in-content/common.inc.css @@ -38,6 +38,7 @@ --in-content-primary-button-background: #0a84ff; --in-content-primary-button-background-hover: #0060df; --in-content-primary-button-background-active: #003EAA; + --in-content-table-background: #ebebeb; --in-content-table-border-dark-color: #d1d1d1; --in-content-table-header-background: #0a84ff; } diff --git a/toolkit/themes/shared/in-content/info-pages.inc.css b/toolkit/themes/shared/in-content/info-pages.inc.css index 884569ddcfec..d261777ab108 100644 --- a/toolkit/themes/shared/in-content/info-pages.inc.css +++ b/toolkit/themes/shared/in-content/info-pages.inc.css @@ -20,6 +20,10 @@ body { justify-content: center; } +body.wide-container { + display: block; +} + .container { min-width: var(--in-content-container-min-width); max-width: var(--in-content-container-max-width); @@ -122,9 +126,51 @@ tree { width: 100%; } -/* Illustrated Info Pages */ +/* Tables */ +table { + background-color: var(--in-content-table-background); + color: var(--in-content-text-color); + font: message-box; + text-align: start; + width: 100%; + border: 1px solid var(--in-content-border-color); + border-spacing: 0px; +} +th, td { + border: 1px solid var(--in-content-border-color); + padding: 4px; + text-align: start; +} + +thead th { + text-align: center; +} + +th { + background-color: var(--in-content-table-header-background); + color: var(--in-content-selected-text); +} + +th.column { + white-space: nowrap; + width: 0px; +} + +td { + border-color: var(--in-content-table-border-dark-color); + unicode-bidi: plaintext; /* Make sure file paths will be LTR */ +} + +/* Illustrated Info Pages */ .illustrated .title { margin-inline-start: 0; padding-inline-start: 0; } + +.notice-box { + background-color: var(--in-content-table-background); + border: 1px solid var(--in-content-border-color); + color: var(--in-content-text-color); + padding: 16px; +} From 6e60c098ef096d0ad86bc753bd91e1c3c8a6ed6c Mon Sep 17 00:00:00 2001 From: Michael Kelly Date: Mon, 2 Apr 2018 13:22:45 -0700 Subject: [PATCH 03/25] Bug 1449367: Use 100% sample rate for devtools error reports. r=jryans Differential Revision: https://phabricator.services.mozilla.com/D835 MozReview-Commit-ID: IgtFosj2mii --HG-- extra : rebase_source : c1090924be94df402c4761d5cf5bbf3aa59c24e3 extra : amend_source : 94236a6405c1b716107c1e91748c106f4d0a3a6b --- browser/modules/BrowserErrorReporter.jsm | 15 ++++++++++- .../browser/browser_BrowserErrorReporter.js | 26 ++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/browser/modules/BrowserErrorReporter.jsm b/browser/modules/BrowserErrorReporter.jsm index a9519ade5a68..adbbc618aa0f 100644 --- a/browser/modules/BrowserErrorReporter.jsm +++ b/browser/modules/BrowserErrorReporter.jsm @@ -59,6 +59,13 @@ const TELEMETRY_REPORTED_PATTERNS = new Set([ /^chrome:\/\/(?:global|browser|devtools)/, ]); +// Mapping of regexes to sample rates; if the regex matches the module an error +// is thrown from, the matching sample rate is used instead of the default. +// In case of a conflict, the first matching rate by insertion order is used. +const MODULE_SAMPLE_RATES = new Map([ + [/^(?:chrome|resource):\/\/devtools/, 1], +]); + /** * Collects nsIScriptError messages logged to the browser console and reports * them to a remotely-hosted error collection service. @@ -212,7 +219,13 @@ class BrowserErrorReporter { } // Sample the amount of errors we send out - const sampleRate = Number.parseFloat(this.sampleRatePref); + let sampleRate = Number.parseFloat(this.sampleRatePref); + for (const [regex, rate] of MODULE_SAMPLE_RATES) { + if (message.sourceName.match(regex)) { + sampleRate = rate; + break; + } + } if (!Number.isFinite(sampleRate) || (Math.random() >= sampleRate)) { return; } diff --git a/browser/modules/test/browser/browser_BrowserErrorReporter.js b/browser/modules/test/browser/browser_BrowserErrorReporter.js index 114e93265823..4c2278e3c362 100644 --- a/browser/modules/test/browser/browser_BrowserErrorReporter.js +++ b/browser/modules/test/browser/browser_BrowserErrorReporter.js @@ -27,7 +27,7 @@ function createScriptError(options = {}) { const scriptError = Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError); scriptError.init( options.message || "", - options.sourceName || null, + "sourceName" in options ? options.sourceName : null, options.sourceLine || null, options.lineNumber || null, options.columnNumber || null, @@ -210,6 +210,12 @@ add_task(async function testSampling() { "A 1.0 sample rate will cause the reporter to always collect errors.", ); + await reporter.observe(createScriptError({message: "undefined", sourceName: undefined})); + ok( + fetchPassedError(fetchSpy, "undefined"), + "A missing sourceName doesn't break reporting.", + ); + await SpecialPowers.pushPrefEnv({set: [ [PREF_SAMPLE_RATE, "0.0"], ]}); @@ -219,6 +225,24 @@ add_task(async function testSampling() { "A 0.0 sample rate will cause the reporter to never collect errors.", ); + await reporter.observe(createScriptError({ + message: "chromedevtools", + sourceName: "chrome://devtools/Foo.jsm", + })); + ok( + fetchPassedError(fetchSpy, "chromedevtools"), + "chrome://devtools/ paths are sampled at 100% even if the default rate is 0.0.", + ); + + await reporter.observe(createScriptError({ + message: "resourcedevtools", + sourceName: "resource://devtools/Foo.jsm", + })); + ok( + fetchPassedError(fetchSpy, "resourcedevtools"), + "resource://devtools/ paths are sampled at 100% even if the default rate is 0.0.", + ); + await SpecialPowers.pushPrefEnv({set: [ [PREF_SAMPLE_RATE, ")fasdf"], ]}); From 8efec59d87622b0da7902c1dba4d7dec574d0130 Mon Sep 17 00:00:00 2001 From: "Ting-Yu Lin ext:(%2C%20Brad%20Werth%20%3Cbwerth%40mozilla.com%3E)" Date: Thu, 25 Jan 2018 14:55:18 +0800 Subject: [PATCH 04/25] Bug 1404222 Part 1: Implement shape-outside: . r=dbaron,dholbert When creating ImageShapeInfo, it's likely that the image is still decoding. Part 2 will add mechanism to trigger reflow after the image is ready. --- layout/generic/nsFloatManager.cpp | 361 +++++++++++++++++++++++++++- layout/generic/nsFloatManager.h | 1 + layout/painting/nsImageRenderer.cpp | 66 ++++- layout/painting/nsImageRenderer.h | 8 + 4 files changed, 428 insertions(+), 8 deletions(-) diff --git a/layout/generic/nsFloatManager.cpp b/layout/generic/nsFloatManager.cpp index 1c0fd78038ff..90e142647673 100644 --- a/layout/generic/nsFloatManager.cpp +++ b/layout/generic/nsFloatManager.cpp @@ -11,10 +11,12 @@ #include #include +#include "gfxContext.h" #include "mozilla/ReflowInput.h" #include "mozilla/ShapeUtils.h" #include "nsBlockFrame.h" #include "nsError.h" +#include "nsImageRenderer.h" #include "nsIPresShell.h" #include "nsMemory.h" @@ -175,9 +177,8 @@ nsFloatManager::GetFlowArea(WritingMode aWM, nscoord aBCoord, nscoord aBSize, break; } if (fi.IsEmpty(aShapeType)) { - // For compatibility, ignore floats with empty rects, even though it - // disagrees with the spec. (We might want to fix this in the - // future, though.) + // Ignore empty float areas. + // https://drafts.csswg.org/css-shapes/#relation-to-box-model-and-float-behavior continue; } @@ -580,6 +581,13 @@ public: WritingMode aWM, const nsSize& aContainerSize); + static UniquePtr CreateImageShape( + const UniquePtr& aShapeImage, + float aShapeImageThreshold, + nsIFrame* const aFrame, + WritingMode aWM, + const nsSize& aContainerSize); + protected: // Compute the minimum line-axis difference between the bounding shape // box and its rounded corner within the given band (block-axis region). @@ -615,6 +623,7 @@ protected: // RoundedBoxShapeInfo // // Implements shape-outside: and shape-outside: inset(). +// class nsFloatManager::RoundedBoxShapeInfo final : public nsFloatManager::ShapeInfo { public: @@ -952,6 +961,246 @@ nsFloatManager::PolygonShapeInfo::XInterceptAtY(const nscoord aY, return aP1.x + (aY - aP1.y) * (aP2.x - aP1.x) / (aP2.y - aP1.y); } +///////////////////////////////////////////////////////////////////////////// +// ImageShapeInfo +// +// Implements shape-outside: +// +class nsFloatManager::ImageShapeInfo final : public nsFloatManager::ShapeInfo +{ +public: + ImageShapeInfo(uint8_t* aAlphaPixels, + int32_t aStride, + const LayoutDeviceIntSize& aImageSize, + int32_t aAppUnitsPerDevPixel, + float aShapeImageThreshold, + const nsRect& aContentRect, + WritingMode aWM, + const nsSize& aContainerSize); + + nscoord LineLeft(const nscoord aBStart, + const nscoord aBEnd) const override; + nscoord LineRight(const nscoord aBStart, + const nscoord aBEnd) const override; + nscoord BStart() const override { return mBStart; } + nscoord BEnd() const override { return mBEnd; } + bool IsEmpty() const override { return mIntervals.IsEmpty(); } + + void Translate(nscoord aLineLeft, nscoord aBlockStart) override; + +private: + size_t MinIntervalIndexContainingY(const nscoord aTargetY) const; + nscoord LineEdge(const nscoord aBStart, + const nscoord aBEnd, + bool aLeft) const; + + // An interval is slice of the float area defined by this ImageShapeInfo. + // Each interval is a rectangle that is one pixel deep in the block + // axis. The values are stored as block edges in the y coordinates, + // and inline edges as the x coordinates. + + // The intervals are stored in ascending order on y. + nsTArray mIntervals; + + nscoord mBStart = nscoord_MAX; + nscoord mBEnd = nscoord_MIN; +}; + +nsFloatManager::ImageShapeInfo::ImageShapeInfo( + uint8_t* aAlphaPixels, + int32_t aStride, + const LayoutDeviceIntSize& aImageSize, + int32_t aAppUnitsPerDevPixel, + float aShapeImageThreshold, + const nsRect& aContentRect, + WritingMode aWM, + const nsSize& aContainerSize) +{ + MOZ_ASSERT(aShapeImageThreshold >=0.0 && aShapeImageThreshold <=1.0, + "The computed value of shape-image-threshold is wrong!"); + + const uint8_t threshold = NSToIntFloor(aShapeImageThreshold * 255); + const int32_t w = aImageSize.width; + const int32_t h = aImageSize.height; + + // Scan the pixels in a double loop. For horizontal writing modes, we do + // this row by row, from top to bottom. For vertical writing modes, we do + // column by column, from left to right. We define the two loops + // generically, then figure out the rows and cols within the i loop. + const int32_t bSize = aWM.IsVertical() ? w : h; + const int32_t iSize = aWM.IsVertical() ? h : w; + for (int32_t b = 0; b < bSize; ++b) { + // iMin and iMax store the start and end of the float area for the row + // or column represented by this iteration of the b loop. + int32_t iMin = -1; + int32_t iMax = -1; + + for (int32_t i = 0; i < iSize; ++i) { + const int32_t col = aWM.IsVertical() ? b : i; + const int32_t row = aWM.IsVertical() ? i : b; + + // Determine if the alpha pixel at this row and column has a value + // greater than the threshold. If it does, update our iMin and iMax values + // to track the edges of the float area for this row or column. + // https://drafts.csswg.org/css-shapes-1/#valdef-shape-image-threshold-number + const uint8_t alpha = aAlphaPixels[col + row * aStride]; + if (alpha > threshold) { + if (iMin == -1) { + iMin = i; + } + MOZ_ASSERT(iMax < i); + iMax = i; + } + } + + // At the end of a row or column; did we find something? + if (iMin != -1) { + // Store an interval as an nsRect with our inline axis values stored in x + // and our block axis values stored in y. The position is dependent on + // the writing mode, but the size is the same for all writing modes. + + // Size is the difference in inline axis edges stored as x, and one + // block axis pixel stored as y. For the inline axis, we add 1 to iMax + // because we want to capture the far edge of the last pixel. + nsSize size(((iMax + 1) - iMin) * aAppUnitsPerDevPixel, + aAppUnitsPerDevPixel); + + // Since we started our scanning of the image pixels from the top left, + // the interval position starts from the origin of the content rect, + // converted to logical coordinates. + nsPoint origin = ConvertToFloatLogical(aContentRect.TopLeft(), aWM, + aContainerSize); + + // Depending on the writing mode, we now move the origin. + if (aWM.IsVerticalRL()) { + // vertical-rl or sideways-rl. + // These writing modes proceed from the top right, and each interval + // moves in a positive inline direction and negative block direction. + // That means that the intervals will be reversed after all have been + // constructed. We add 1 to b to capture the end of the block axis pixel. + origin.MoveBy(iMin * aAppUnitsPerDevPixel, (b + 1) * -aAppUnitsPerDevPixel); + } else if (aWM.IsVerticalLR() && aWM.IsSideways()) { + // sideways-lr. + // These writing modes proceed from the bottom left, and each interval + // moves in a negative inline direction and a positive block direction. + // We add 1 to iMax to capture the end of the inline axis pixel. + origin.MoveBy((iMax + 1) * -aAppUnitsPerDevPixel, b * aAppUnitsPerDevPixel); + } else { + // horizontal-tb or vertical-lr. + // These writing modes proceed from the top left and each interval + // moves in a positive step in both inline and block directions. + origin.MoveBy(iMin * aAppUnitsPerDevPixel, b * aAppUnitsPerDevPixel); + } + + mIntervals.AppendElement(nsRect(origin, size)); + } + } + + if (aWM.IsVerticalRL()) { + // vertical-rl or sideways-rl. + // Because we scan the columns from left to right, we need to reverse + // the array so that it's sorted (in ascending order) on the block + // direction. + mIntervals.Reverse(); + } + + if (!mIntervals.IsEmpty()) { + mBStart = mIntervals[0].Y(); + mBEnd = mIntervals.LastElement().YMost(); + } +} + +size_t +nsFloatManager::ImageShapeInfo::MinIntervalIndexContainingY( + const nscoord aTargetY) const +{ + // Perform a binary search to find the minimum index of an interval + // that contains aTargetY. If no such interval exists, return a value + // equal to the number of intervals. + size_t startIdx = 0; + size_t endIdx = mIntervals.Length(); + while (startIdx < endIdx) { + size_t midIdx = startIdx + (endIdx - startIdx) / 2; + if (mIntervals[midIdx].ContainsY(aTargetY)) { + return midIdx; + } + nscoord midY = mIntervals[midIdx].Y(); + if (midY < aTargetY) { + startIdx = midIdx + 1; + } else { + endIdx = midIdx; + } + } + + return endIdx; +} + +nscoord +nsFloatManager::ImageShapeInfo::LineEdge(const nscoord aBStart, + const nscoord aBEnd, + bool aLeft) const +{ + MOZ_ASSERT(aBStart <= aBEnd, + "The band's block start is greater than its block end?"); + + // Find all the intervals whose rects overlap the aBStart to + // aBEnd range, and find the most constraining inline edge + // depending on the value of aLeft. + + // Since the intervals are stored in block-axis order, we need + // to find the first interval that overlaps aBStart and check + // succeeding intervals until we get past aBEnd. + + nscoord lineEdge = aLeft ? nscoord_MAX : nscoord_MIN; + + size_t intervalCount = mIntervals.Length(); + for (size_t i = MinIntervalIndexContainingY(aBStart); + i < intervalCount; ++i) { + // We can always get the bCoord from the intervals' mLineLeft, + // since the y() coordinate is duplicated in both points in the + // interval. + auto& interval = mIntervals[i]; + nscoord bCoord = interval.Y(); + if (bCoord > aBEnd) { + break; + } + // Get the edge from the interval point indicated by aLeft. + if (aLeft) { + lineEdge = std::min(lineEdge, interval.X()); + } else { + lineEdge = std::max(lineEdge, interval.XMost()); + } + } + + return lineEdge; +} + +nscoord +nsFloatManager::ImageShapeInfo::LineLeft(const nscoord aBStart, + const nscoord aBEnd) const +{ + return LineEdge(aBStart, aBEnd, true); +} + +nscoord +nsFloatManager::ImageShapeInfo::LineRight(const nscoord aBStart, + const nscoord aBEnd) const +{ + return LineEdge(aBStart, aBEnd, false); +} + +void +nsFloatManager::ImageShapeInfo::Translate(nscoord aLineLeft, + nscoord aBlockStart) +{ + for (nsRect& interval : mIntervals) { + interval.MoveBy(aLineLeft, aBlockStart); + } + + mBStart += aBlockStart; + mBEnd += aBlockStart; +} + ///////////////////////////////////////////////////////////////////////////// // FloatInfo @@ -966,6 +1215,16 @@ nsFloatManager::FloatInfo::FloatInfo(nsIFrame* aFrame, { MOZ_COUNT_CTOR(nsFloatManager::FloatInfo); + if (IsEmpty()) { + // Per spec, a float area defined by a shape is clipped to the float’s + // margin box. Therefore, no need to create a shape info if the float's + // margin box is empty, since a float area can only be smaller than the + // margin box. + + // https://drafts.csswg.org/css-shapes/#relation-to-box-model-and-float-behavior + return; + } + const StyleShapeSource& shapeOutside = mFrame->StyleDisplay()->mShapeOutside; switch (shapeOutside.GetType()) { @@ -977,10 +1236,20 @@ nsFloatManager::FloatInfo::FloatInfo(nsIFrame* aFrame, MOZ_ASSERT_UNREACHABLE("shape-outside doesn't have URL source type!"); return; - case StyleShapeSourceType::Image: - // Bug 1265343: Implement 'shape-image-threshold' - // Bug 1404222: Support shape-outside: - return; + case StyleShapeSourceType::Image: { + float shapeImageThreshold = mFrame->StyleDisplay()->mShapeImageThreshold; + mShapeInfo = ShapeInfo::CreateImageShape(shapeOutside.GetShapeImage(), + shapeImageThreshold, + mFrame, + aWM, + aContainerSize); + if (!mShapeInfo) { + // Image is not ready, or fails to load, etc. + return; + } + + break; + } case StyleShapeSourceType::Box: { // Initialize 's reference rect. @@ -1277,6 +1546,84 @@ nsFloatManager::ShapeInfo::CreatePolygon( return MakeUnique(Move(vertices)); } +/* static */ UniquePtr +nsFloatManager::ShapeInfo::CreateImageShape( + const UniquePtr& aShapeImage, + float aShapeImageThreshold, + nsIFrame* const aFrame, + WritingMode aWM, + const nsSize& aContainerSize) +{ + MOZ_ASSERT(aShapeImage == + aFrame->StyleDisplay()->mShapeOutside.GetShapeImage(), + "aFrame should be the frame that we got aShapeImage from"); + + nsImageRenderer imageRenderer(aFrame, aShapeImage.get(), + nsImageRenderer::FLAG_SYNC_DECODE_IMAGES); + + if (!imageRenderer.PrepareImage()) { + // The image is not ready yet. + return nullptr; + } + + nsRect contentRect = aFrame->GetContentRect(); + + // Create a draw target and draw shape image on it. + nsDeviceContext* dc = aFrame->PresContext()->DeviceContext(); + int32_t appUnitsPerDevPixel = dc->AppUnitsPerDevPixel(); + LayoutDeviceIntSize contentSizeInDevPixels = + LayoutDeviceIntSize::FromAppUnitsRounded(contentRect.Size(), + appUnitsPerDevPixel); + + // Use empty CSSSizeOrRatio to force set the preferred size as the frame's + // content box size. + imageRenderer.SetPreferredSize(CSSSizeOrRatio(), contentRect.Size()); + + RefPtr drawTarget = + gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget( + contentSizeInDevPixels.ToUnknownSize(), + gfx::SurfaceFormat::A8); + if (!drawTarget) { + return nullptr; + } + + RefPtr context = gfxContext::CreateOrNull(drawTarget); + MOZ_ASSERT(context); // already checked the target above + + ImgDrawResult result = + imageRenderer.DrawShapeImage(aFrame->PresContext(), *context); + + if (result != ImgDrawResult::SUCCESS) { + return nullptr; + } + + // Retrieve the pixel image buffer to create the image shape info. + RefPtr sourceSurface = drawTarget->Snapshot(); + RefPtr dataSourceSurface = sourceSurface->GetDataSurface(); + DataSourceSurface::ScopedMap map(dataSourceSurface, DataSourceSurface::READ); + + if (!map.IsMapped()) { + return nullptr; + } + + MOZ_ASSERT(sourceSurface->GetSize() == contentSizeInDevPixels.ToUnknownSize(), + "Who changes the size?"); + + uint8_t* alphaPixels = map.GetData(); + int32_t stride = map.GetStride(); + + // NOTE: ImageShapeInfo constructor does not keep a persistent copy of + // alphaPixels; it's only used during the constructor to compute pixel ranges. + return MakeUnique(alphaPixels, + stride, + contentSizeInDevPixels, + appUnitsPerDevPixel, + aShapeImageThreshold, + contentRect, + aWM, + aContainerSize); +} + /* static */ nscoord nsFloatManager::ShapeInfo::ComputeEllipseLineInterceptDiff( const nscoord aShapeBoxBStart, const nscoord aShapeBoxBEnd, diff --git a/layout/generic/nsFloatManager.h b/layout/generic/nsFloatManager.h index f45bc0286fd8..7cfedfe86c68 100644 --- a/layout/generic/nsFloatManager.h +++ b/layout/generic/nsFloatManager.h @@ -344,6 +344,7 @@ private: class RoundedBoxShapeInfo; class EllipseShapeInfo; class PolygonShapeInfo; + class ImageShapeInfo; struct FloatInfo { nsIFrame *const mFrame; diff --git a/layout/painting/nsImageRenderer.cpp b/layout/painting/nsImageRenderer.cpp index 3813aa627a59..90045ed28f31 100644 --- a/layout/painting/nsImageRenderer.cpp +++ b/layout/painting/nsImageRenderer.cpp @@ -4,7 +4,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -/* utility functions for drawing borders and backgrounds */ +/* utility code for drawing images as CSS borders, backgrounds, and shapes. */ #include "nsImageRenderer.h" @@ -13,11 +13,13 @@ #include "gfxContext.h" #include "gfxDrawable.h" #include "ImageOps.h" +#include "ImageRegion.h" #include "mozilla/layers/StackingContextHelper.h" #include "mozilla/layers/WebRenderLayerManager.h" #include "nsContentUtils.h" #include "nsCSSRendering.h" #include "nsCSSRenderingGradients.h" +#include "nsDeviceContext.h" #include "nsIFrame.h" #include "nsStyleStructInlines.h" #include "nsSVGDisplayableFrame.h" @@ -946,10 +948,72 @@ nsImageRenderer::DrawBorderImageComponent(nsPresContext* aPresContext, nsRect destTile = RequiresScaling(fillRect, aHFill, aVFill, aUnitSize) ? ComputeTile(fillRect, aHFill, aVFill, aUnitSize, repeatSize) : fillRect; + return Draw(aPresContext, aRenderingContext, aDirtyRect, destTile, fillRect, destTile.TopLeft(), repeatSize, aSrc); } +ImgDrawResult +nsImageRenderer::DrawShapeImage(nsPresContext* aPresContext, + gfxContext& aRenderingContext) +{ + if (!IsReady()) { + NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me"); + return ImgDrawResult::NOT_READY; + } + + if (mSize.width <= 0 || mSize.height <= 0) { + return ImgDrawResult::SUCCESS; + } + + ImgDrawResult result = ImgDrawResult::SUCCESS; + + switch (mType) { + case eStyleImageType_Image: { + uint32_t drawFlags = ConvertImageRendererToDrawFlags(mFlags) | + imgIContainer::FRAME_FIRST; + nsRect dest(nsPoint(0, 0), mSize); + // We have a tricky situation in our choice of SamplingFilter. Shape images + // define a float area based on the alpha values in the rendered pixels. + // When multiple device pixels are used for one css pixel, the sampling + // can change crisp edges into aliased edges. For visual pixels, that's + // usually the right choice. For defining a float area, it can cause problems. + // If a style is using a shape-image-threshold value that is less than the + // alpha of the edge pixels, any filtering may smear the alpha into adjacent + // pixels and expand the float area in a confusing way. Since the alpha + // threshold can be set precisely in CSS, and since a web author may be + // counting on that threshold to define a precise float area from an image, + // it is least confusing to have the rendered pixels have unfiltered alpha. + // We use SamplingFilter::POINT to ensure that each rendered pixel has an + // alpha that precisely matches the alpha of the closest pixel in the image. + nsLayoutUtils::DrawSingleImage(aRenderingContext, aPresContext, + mImageContainer, SamplingFilter::POINT, + dest, dest, Nothing(), + drawFlags, + nullptr, nullptr); + break; + } + + case eStyleImageType_Gradient: { + nsCSSGradientRenderer renderer = + nsCSSGradientRenderer::Create(aPresContext, mGradientData, mSize); + nsRect dest(nsPoint(0, 0), mSize); + + renderer.Paint(aRenderingContext, dest, dest, mSize, + CSSIntRect::FromAppUnitsRounded(dest), + dest, 1.0); + break; + } + + default: + // Unsupported image type. + result = ImgDrawResult::BAD_IMAGE; + break; + } + + return result; +} + bool nsImageRenderer::IsRasterImage() { diff --git a/layout/painting/nsImageRenderer.h b/layout/painting/nsImageRenderer.h index 91e9c93d5ead..e73ff231977e 100644 --- a/layout/painting/nsImageRenderer.h +++ b/layout/painting/nsImageRenderer.h @@ -254,6 +254,14 @@ public: const mozilla::Maybe& aSVGViewportSize, const bool aHasIntrinsicRatio); + /** + * Draw the image to aRenderingContext which can be used to define the + * float area in the presence of "shape-outside: ". + */ + ImgDrawResult + DrawShapeImage(nsPresContext* aPresContext, + gfxContext& aRenderingContext); + bool IsRasterImage(); bool IsAnimatedImage(); From 311eb01351cc58bef33559e21f8688cdbee9e2f4 Mon Sep 17 00:00:00 2001 From: Brad Werth Date: Fri, 16 Mar 2018 11:01:57 -0700 Subject: [PATCH 05/25] Bug 1404222 Part 2: Extend ImageLoader to associate flags with each request-frame relationship. r=dbaron --- layout/style/ImageLoader.cpp | 16 ++++++++++------ layout/style/ImageLoader.h | 30 +++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/layout/style/ImageLoader.cpp b/layout/style/ImageLoader.cpp index b91f03893cf3..f6e0066cfb42 100644 --- a/layout/style/ImageLoader.cpp +++ b/layout/style/ImageLoader.cpp @@ -77,9 +77,10 @@ ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest, }); // Add these to the sets, but only if they're not already there. - uint32_t i = frameSet->IndexOfFirstElementGt(aFrame); - if (i == 0 || aFrame != frameSet->ElementAt(i-1)) { - frameSet->InsertElementAt(i, aFrame); + FrameWithFlags fwf(aFrame); + uint32_t i = frameSet->IndexOfFirstElementGt(fwf, FrameOnlyComparator()); + if (i == 0 || aFrame != frameSet->ElementAt(i-1).mFrame) { + frameSet->InsertElementAt(i, fwf); } i = requestSet->IndexOfFirstElementGt(aRequest); if (i == 0 || aRequest != requestSet->ElementAt(i-1)) { @@ -139,7 +140,8 @@ ImageLoader::RemoveRequestToFrameMapping(imgIRequest* aRequest, if (auto entry = mRequestToFrameMap.Lookup(aRequest)) { FrameSet* frameSet = entry.Data(); MOZ_ASSERT(frameSet, "This should never be null"); - frameSet->RemoveElementSorted(aFrame); + frameSet->RemoveElementSorted(FrameWithFlags(aFrame), + FrameOnlyComparator()); if (frameSet->IsEmpty()) { nsPresContext* presContext = GetPresContext(); if (presContext) { @@ -362,7 +364,8 @@ ImageLoader::DoRedraw(FrameSet* aFrameSet, bool aForcePaint) NS_ASSERTION(aFrameSet, "Must have a frame set"); NS_ASSERTION(mDocument, "Should have returned earlier!"); - for (nsIFrame* frame : *aFrameSet) { + for (FrameWithFlags& fwf : *aFrameSet) { + nsIFrame* frame = fwf.mFrame; if (frame->StyleVisibility()->IsVisible()) { if (frame->IsFrameOfType(nsIFrame::eTablePart)) { // Tables don't necessarily build border/background display items @@ -454,7 +457,8 @@ ImageLoader::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage) return NS_OK; } - for (nsIFrame* frame : *frameSet) { + for (FrameWithFlags& fwf : *frameSet) { + nsIFrame* frame = fwf.mFrame; if (frame->StyleVisibility()->IsVisible()) { frame->MarkNeedsDisplayItemRebuild(); } diff --git a/layout/style/ImageLoader.h b/layout/style/ImageLoader.h index 583001dbea1c..763cafd6164b 100644 --- a/layout/style/ImageLoader.h +++ b/layout/style/ImageLoader.h @@ -80,7 +80,35 @@ private: // the frame goes away). Thus we maintain hashtables going both ways. These // should always be in sync. - typedef nsTArray FrameSet; + // We also associate flags alongside frames in the request-to-frames hashmap. + // These are used for special handling of events for requests. + typedef uint32_t FrameFlags; + + struct FrameWithFlags { + FrameWithFlags(nsIFrame* aFrame) + : mFrame(aFrame), + mFlags(0) + { + MOZ_ASSERT(mFrame); + } + nsIFrame* const mFrame; + FrameFlags mFlags; + }; + + // A helper class to compare FrameWithFlags by comparing mFrame and + // ignoring mFlags. + class FrameOnlyComparator { + public: + bool Equals(const FrameWithFlags& aElem1, + const FrameWithFlags& aElem2) const + { return aElem1.mFrame == aElem2.mFrame; } + + bool LessThan(const FrameWithFlags& aElem1, + const FrameWithFlags& aElem2) const + { return aElem1.mFrame < aElem2.mFrame; } + }; + + typedef nsTArray FrameSet; typedef nsTArray > RequestSet; typedef nsTHashtable > ImageHashSet; typedef nsClassHashtable Date: Thu, 25 Jan 2018 14:56:43 +0800 Subject: [PATCH 06/25] Bug 1404222 Part 3: Block onload when shape-outside images are requested for a frame, and keep it blocked until the frame is removed or reflow is complete. r=dbaron,dholbert When we finish decoding an image frame, we need to trigger reflow for the frame containing a float with shape-outside: , and delay the firing of the document's onload event until that reflow is complete. --- layout/generic/nsFrame.cpp | 30 ++++- layout/generic/nsIFrame.h | 3 +- layout/painting/nsCSSRendering.cpp | 2 +- layout/painting/nsCSSRenderingBorders.cpp | 2 +- layout/style/ImageLoader.cpp | 157 +++++++++++++++++++++- layout/style/ImageLoader.h | 42 +++++- layout/style/nsStyleStruct.cpp | 12 ++ layout/style/nsStyleStruct.h | 5 + layout/svg/SVGObserverUtils.cpp | 2 +- 9 files changed, 235 insertions(+), 20 deletions(-) diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index d5c1fe301fcc..9993872546de 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -106,7 +106,6 @@ #include "mozilla/LookAndFeel.h" #include "mozilla/MouseEvents.h" #include "mozilla/ServoStyleSet.h" -#include "mozilla/css/ImageLoader.h" #include "mozilla/gfx/Tools.h" #include "nsPrintfCString.h" #include "ActiveLayerTracker.h" @@ -114,6 +113,8 @@ #include "nsITheme.h" #include "nsThemeConstants.h" +#include "ImageLoader.h" + using namespace mozilla; using namespace mozilla::css; using namespace mozilla::dom; @@ -907,7 +908,7 @@ AddAndRemoveImageAssociations(nsFrame* aFrame, CompareLayers(aNewLayers, aOldLayers, [&imageLoader, aFrame](imgRequestProxy* aReq) - { imageLoader->AssociateRequestToFrame(aReq, aFrame); } + { imageLoader->AssociateRequestToFrame(aReq, aFrame, 0); } ); } @@ -1175,7 +1176,24 @@ nsFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) imageLoader->DisassociateRequestFromFrame(oldBorderImage, this); } if (newBorderImage) { - imageLoader->AssociateRequestToFrame(newBorderImage, this); + imageLoader->AssociateRequestToFrame(newBorderImage, this, 0); + } + } + + imgIRequest* oldShapeImage = + aOldComputedStyle + ? aOldComputedStyle->StyleDisplay()->mShapeOutside.GetShapeImageData() + : nullptr; + imgIRequest* newShapeImage = + StyleDisplay()->mShapeOutside.GetShapeImageData(); + + if (oldShapeImage != newShapeImage) { + if (oldShapeImage && HasImageRequest()) { + imageLoader->DisassociateRequestFromFrame(oldShapeImage, this); + } + if (newShapeImage) { + imageLoader->AssociateRequestToFrame(newShapeImage, this, + ImageLoader::REQUEST_REQUIRES_REFLOW); } } @@ -5220,7 +5238,8 @@ nsIFrame::ContentOffsets nsFrame::CalcContentOffsetsFromFramePoint(const nsPoint } void -nsIFrame::AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext) +nsIFrame::AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext, + uint32_t aImageLoaderFlags) { if (aImage.GetType() != eStyleImageType_Image) { return; @@ -5230,12 +5249,11 @@ nsIFrame::AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext if (!req) { return; } - mozilla::css::ImageLoader* loader = aPresContext->Document()->StyleImageLoader(); // If this fails there's not much we can do ... - loader->AssociateRequestToFrame(req, this); + loader->AssociateRequestToFrame(req, this, aImageLoaderFlags); } nsresult diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 269b573d1172..d0dea10b2484 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -1955,7 +1955,8 @@ public: * Ensure that aImage gets notifed when the underlying image request loads * or animates. */ - void AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext); + void AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext, + uint32_t aImageLoaderFlags); /** * This structure holds information about a cursor. mContainer represents a diff --git a/layout/painting/nsCSSRendering.cpp b/layout/painting/nsCSSRendering.cpp index 2b951131c75c..c2d86ceb2236 100644 --- a/layout/painting/nsCSSRendering.cpp +++ b/layout/painting/nsCSSRendering.cpp @@ -2745,7 +2745,7 @@ nsCSSRendering::PaintStyleImageLayerWithSC(const PaintBGParams& aParams, NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT_WITH_RANGE(i, layers, startLayer, count) { aParams.frame->AssociateImage(layers.mLayers[i].mImage, - &aParams.presCtx); + &aParams.presCtx, 0); } } diff --git a/layout/painting/nsCSSRenderingBorders.cpp b/layout/painting/nsCSSRenderingBorders.cpp index 3f9bc8dc498b..aa998884f672 100644 --- a/layout/painting/nsCSSRenderingBorders.cpp +++ b/layout/painting/nsCSSRenderingBorders.cpp @@ -3486,7 +3486,7 @@ nsCSSBorderImageRenderer::CreateBorderImageRenderer(nsPresContext* aPresContext, // XXX We shouldn't really... since if anybody is passing in a // different style, they'll potentially have the wrong size for the // border too. - aForFrame->AssociateImage(aStyleBorder.mBorderImageSource, aPresContext); + aForFrame->AssociateImage(aStyleBorder.mBorderImageSource, aPresContext, 0); nsCSSBorderImageRenderer renderer(aForFrame, aBorderArea, aStyleBorder, aSkipSides, imgRenderer); diff --git a/layout/style/ImageLoader.cpp b/layout/style/ImageLoader.cpp index f6e0066cfb42..1693e8fd2cb2 100644 --- a/layout/style/ImageLoader.cpp +++ b/layout/style/ImageLoader.cpp @@ -14,6 +14,7 @@ #include "nsLayoutUtils.h" #include "nsError.h" #include "nsDisplayList.h" +#include "nsIFrameInlines.h" #include "FrameLayerBuilder.h" #include "SVGObserverUtils.h" #include "imgIContainer.h" @@ -47,7 +48,8 @@ ImageLoader::DropDocumentReference() void ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest, - nsIFrame* aFrame) + nsIFrame* aFrame, + FrameFlags aFlags) { nsCOMPtr observer; aRequest->GetNotificationObserver(getter_AddRefs(observer)); @@ -76,16 +78,63 @@ ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest, return new RequestSet(); }); - // Add these to the sets, but only if they're not already there. + // Add frame to the frameSet, and handle any special processing the + // frame might require. FrameWithFlags fwf(aFrame); + FrameWithFlags* fwfToModify(&fwf); + + // See if the frameSet already has this frame. uint32_t i = frameSet->IndexOfFirstElementGt(fwf, FrameOnlyComparator()); + if (i > 0 && aFrame == frameSet->ElementAt(i-1).mFrame) { + // We're already tracking this frame, so prepare to modify the + // existing FrameWithFlags object. + fwfToModify = &frameSet->ElementAt(i-1); + } + + // Check if the frame requires special processing. + if (aFlags & REQUEST_REQUIRES_REFLOW) { + fwfToModify->mFlags |= REQUEST_REQUIRES_REFLOW; + + // If we weren't already blocking onload, do that now. + if ((fwfToModify->mFlags & REQUEST_HAS_BLOCKED_ONLOAD) == 0) { + fwfToModify->mFlags |= REQUEST_HAS_BLOCKED_ONLOAD; + + // Block document onload until we either remove the frame in + // RemoveRequestToFrameMapping or complete a reflow. + mDocument->BlockOnload(); + + // We need to stay blocked until we get a reflow. If the first frame + // is not yet decoded, we'll trigger that reflow from onFrameComplete. + // But if the first frame is already decoded, we need to trigger that + // reflow now, because we'll never get a call to onFrameComplete. + uint32_t status = 0; + if(NS_SUCCEEDED(aRequest->GetImageStatus(&status)) && + status & imgIRequest::STATUS_FRAME_COMPLETE) { + RequestReflowOnFrame(aFrame, aRequest); + } + } + } + + // Do some sanity checking to ensure that we only add to one mapping + // iff we also add to the other mapping. + DebugOnly didAddToFrameSet(false); + DebugOnly didAddToRequestSet(false); + + // If we weren't already tracking this frame, add it to the frameSet. if (i == 0 || aFrame != frameSet->ElementAt(i-1).mFrame) { frameSet->InsertElementAt(i, fwf); + didAddToFrameSet = true; } + + // Add request to the request set if it wasn't already there. i = requestSet->IndexOfFirstElementGt(aRequest); if (i == 0 || aRequest != requestSet->ElementAt(i-1)) { requestSet->InsertElementAt(i, aRequest); + didAddToRequestSet = true; } + + MOZ_ASSERT(didAddToFrameSet == didAddToRequestSet, + "We should only add to one map iff we also add to the other map."); } void @@ -140,8 +189,21 @@ ImageLoader::RemoveRequestToFrameMapping(imgIRequest* aRequest, if (auto entry = mRequestToFrameMap.Lookup(aRequest)) { FrameSet* frameSet = entry.Data(); MOZ_ASSERT(frameSet, "This should never be null"); - frameSet->RemoveElementSorted(FrameWithFlags(aFrame), - FrameOnlyComparator()); + + // Before we remove aFrame from the frameSet, unblock onload if needed. + uint32_t i = frameSet->IndexOfFirstElementGt(FrameWithFlags(aFrame), + FrameOnlyComparator()); + + if (i > 0 && aFrame == frameSet->ElementAt(i-1).mFrame) { + FrameWithFlags& fwf = frameSet->ElementAt(i-1); + if (fwf.mFlags & REQUEST_HAS_BLOCKED_ONLOAD) { + mDocument->UnblockOnload(false); + // We're about to remove fwf from the frameSet, so we don't bother + // updating the flag. + } + frameSet->RemoveElementAt(i-1); + } + if (frameSet->IsEmpty()) { nsPresContext* presContext = GetPresContext(); if (presContext) { @@ -390,6 +452,59 @@ ImageLoader::DoRedraw(FrameSet* aFrameSet, bool aForcePaint) } } +void +ImageLoader::UnblockOnloadIfNeeded(nsIFrame* aFrame, imgIRequest* aRequest) +{ + MOZ_ASSERT(aFrame); + MOZ_ASSERT(aRequest); + + FrameSet* frameSet = mRequestToFrameMap.Get(aRequest); + if (!frameSet) { + return; + } + + size_t i = frameSet->BinaryIndexOf(FrameWithFlags(aFrame), + FrameOnlyComparator()); + if (i != FrameSet::NoIndex) { + FrameWithFlags& fwf = frameSet->ElementAt(i); + if (fwf.mFlags & REQUEST_HAS_BLOCKED_ONLOAD) { + mDocument->UnblockOnload(false); + fwf.mFlags &= ~REQUEST_HAS_BLOCKED_ONLOAD; + } + } +} + +void +ImageLoader::RequestReflowIfNeeded(FrameSet* aFrameSet, imgIRequest* aRequest) +{ + MOZ_ASSERT(aFrameSet); + + for (FrameWithFlags& fwf : *aFrameSet) { + nsIFrame* frame = fwf.mFrame; + if (fwf.mFlags & REQUEST_REQUIRES_REFLOW) { + // Tell the container of the float to reflow because the + // shape-outside: has finished decoding its first frame. + RequestReflowOnFrame(frame, aRequest); + } + } +} + +void +ImageLoader::RequestReflowOnFrame(nsIFrame* aFrame, imgIRequest* aRequest) +{ + // Actually request the reflow. + nsIFrame* floatContainer = aFrame->GetInFlowParent(); + floatContainer->PresShell()->FrameNeedsReflow( + floatContainer, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); + + // We'll respond to the reflow events by unblocking onload, regardless + // of whether the reflow was completed or cancelled. The callback will + // also delete itself when it is called. + ImageReflowCallback* unblocker = new ImageReflowCallback(this, aFrame, + aRequest); + floatContainer->PresShell()->PostReflowCallback(unblocker); +} + NS_IMPL_ADDREF(ImageLoader) NS_IMPL_RELEASE(ImageLoader) @@ -503,6 +618,9 @@ ImageLoader::OnFrameComplete(imgIRequest* aRequest) return NS_OK; } + // We may need reflow (for example if the image is from shape-outside). + RequestReflowIfNeeded(frameSet, aRequest); + // Since we just finished decoding a frame, we always want to paint, in case // we're now able to paint an image that we couldn't paint before (and hence // that we don't have retained data for). @@ -545,5 +663,36 @@ ImageLoader::FlushUseCounters() } } +bool +ImageLoader::ImageReflowCallback::ReflowFinished() +{ + // Check that the frame is still valid. If it isn't, then onload was + // unblocked when the frame was removed from the FrameSet in + // RemoveRequestToFrameMapping. + if (mFrame.IsAlive()) { + mLoader->UnblockOnloadIfNeeded(mFrame, mRequest); + } + + // Get rid of this callback object. + delete this; + + // We don't need to trigger layout. + return false; +} + +void +ImageLoader::ImageReflowCallback::ReflowCallbackCanceled() +{ + // Check that the frame is still valid. If it isn't, then onload was + // unblocked when the frame was removed from the FrameSet in + // RemoveRequestToFrameMapping. + if (mFrame.IsAlive()) { + mLoader->UnblockOnloadIfNeeded(mFrame, mRequest); + } + + // Get rid of this callback object. + delete this; +} + } // namespace css } // namespace mozilla diff --git a/layout/style/ImageLoader.h b/layout/style/ImageLoader.h index 763cafd6164b..b0a04ecdbbbf 100644 --- a/layout/style/ImageLoader.h +++ b/layout/style/ImageLoader.h @@ -10,9 +10,11 @@ #ifndef mozilla_css_ImageLoader_h___ #define mozilla_css_ImageLoader_h___ -#include "CORSMode.h" +#include "mozilla/CORSMode.h" #include "nsClassHashtable.h" #include "nsHashKeys.h" +#include "nsIFrame.h" +#include "nsIReflowCallback.h" #include "nsTArray.h" #include "imgIRequest.h" #include "imgINotificationObserver.h" @@ -33,6 +35,14 @@ struct ImageValue; class ImageLoader final : public imgINotificationObserver { public: + // We also associate flags alongside frames in the request-to-frames hashmap. + // These are used for special handling of events for requests. + typedef uint32_t FrameFlags; + enum { + REQUEST_REQUIRES_REFLOW = 1u << 0, + REQUEST_HAS_BLOCKED_ONLOAD = 1u << 1 + }; + typedef mozilla::css::ImageValue Image; explicit ImageLoader(nsIDocument* aDocument) @@ -51,7 +61,8 @@ public: void DeregisterCSSImage(Image* aImage); void AssociateRequestToFrame(imgIRequest* aRequest, - nsIFrame* aFrame); + nsIFrame* aFrame, + FrameFlags aFlags); void DisassociateRequestFromFrame(imgIRequest* aRequest, nsIFrame* aFrame); @@ -73,6 +84,26 @@ public: void FlushUseCounters(); private: + // This callback is used to unblock document onload after a reflow + // triggered from an image load. + struct ImageReflowCallback final : public nsIReflowCallback + { + RefPtr mLoader; + WeakFrame mFrame; + nsCOMPtr const mRequest; + + ImageReflowCallback(ImageLoader* aLoader, + nsIFrame* aFrame, + imgIRequest* aRequest) + : mLoader(aLoader) + , mFrame(aFrame) + , mRequest(aRequest) + {} + + bool ReflowFinished() override; + void ReflowCallbackCanceled() override; + }; + ~ImageLoader() {} // We need to be able to look up the frames associated with a request (for @@ -80,10 +111,6 @@ private: // the frame goes away). Thus we maintain hashtables going both ways. These // should always be in sync. - // We also associate flags alongside frames in the request-to-frames hashmap. - // These are used for special handling of events for requests. - typedef uint32_t FrameFlags; - struct FrameWithFlags { FrameWithFlags(nsIFrame* aFrame) : mFrame(aFrame), @@ -122,6 +149,9 @@ private: nsPresContext* GetPresContext(); void DoRedraw(FrameSet* aFrameSet, bool aForcePaint); + void UnblockOnloadIfNeeded(nsIFrame* aFrame, imgIRequest* aRequest); + void RequestReflowIfNeeded(FrameSet* aFrameSet, imgIRequest* aRequest); + void RequestReflowOnFrame(nsIFrame* aFrame, imgIRequest* aRequest); nsresult OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage); nsresult OnFrameComplete(imgIRequest* aRequest); diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 57344660aefb..781faab76a62 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -1069,6 +1069,18 @@ StyleShapeSource::SetShapeImage(UniquePtr aShapeImage) mType = StyleShapeSourceType::Image; } +imgIRequest* +StyleShapeSource::GetShapeImageData() const +{ + if (mType != StyleShapeSourceType::Image) { + return nullptr; + } + if (mShapeImage->GetType() != eStyleImageType_Image) { + return nullptr; + } + return mShapeImage->GetImageData(); +} + void StyleShapeSource::SetBasicShape(UniquePtr aBasicShape, StyleGeometryBox aReferenceBox) diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 736bcbf0dbe4..e6fbe701736c 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -2145,6 +2145,11 @@ struct StyleShapeSource final return mShapeImage; } + // Iff we have "shape-outside:" with an image URI (not a gradient), + // this method returns the corresponding imgIRequest*. Else, returns + // null. + imgIRequest* GetShapeImageData() const; + void SetShapeImage(UniquePtr aShapeImage); const UniquePtr& GetBasicShape() const diff --git a/layout/svg/SVGObserverUtils.cpp b/layout/svg/SVGObserverUtils.cpp index 73fe058a67b1..121602578e74 100644 --- a/layout/svg/SVGObserverUtils.cpp +++ b/layout/svg/SVGObserverUtils.cpp @@ -423,7 +423,7 @@ nsSVGMaskProperty::ResolveImage(uint32_t aIndex) mozilla::css::ImageLoader* imageLoader = mFrame->PresContext()->Document()->StyleImageLoader(); if (imgRequestProxy* req = image.GetImageData()) { - imageLoader->AssociateRequestToFrame(req, mFrame); + imageLoader->AssociateRequestToFrame(req, mFrame, 0); } } } From 5ac6fffbd27a970618805c640ee5bf9fd600a5cf Mon Sep 17 00:00:00 2001 From: Ting-Yu Lin Date: Thu, 25 Jan 2018 14:58:07 +0800 Subject: [PATCH 07/25] Bug 1404222 Part 4: Replace hash '#' with %23 in SVG data URIs, to fix XML parse errors. r=dholbert The hash symbol '#' is reserved as fragment identifier per bug 1430526 comment 1, so fix the test cases. MozReview-Commit-ID: 5o0qLg81iVi --- .../css-shapes/shape-outside/shape-image/shape-image-002.html | 4 ++-- .../css-shapes/shape-outside/shape-image/shape-image-005.html | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-002.html b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-002.html index ee2dd75db6a4..01d916d7cb85 100644 --- a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-002.html +++ b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-002.html @@ -22,7 +22,7 @@ } #image { float: left; - shape-outside: url('data:image/svg+xml;utf8,'); + shape-outside: url('data:image/svg+xml;utf8,'); } @@ -31,7 +31,7 @@ The test passes if you see a solid green square. There should be no red.

- + X X
diff --git a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-005.html b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-005.html index 2c642c02d0b4..93f39787ad1a 100644 --- a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-005.html +++ b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-005.html @@ -22,7 +22,7 @@ } #image { float: left; - shape-outside: url('data:image/svg+xml;utf8,'); + shape-outside: url('data:image/svg+xml;utf8,'); shape-image-threshold: 0.8; } @@ -32,7 +32,7 @@ The test passes if you see a solid green square. There should be no red.

- + X X
From b47d83e6bd8c45fcd27e57a3f243633fa82396a5 Mon Sep 17 00:00:00 2001 From: Ting-Yu Lin Date: Thu, 25 Jan 2018 15:00:10 +0800 Subject: [PATCH 08/25] Bug 1404222 Part 5: Add web-platform-tests for linear-gradient with writing-modes. r=dholbert There's no shape-outside: test with writing-modes on web-platform-tests, so I added some. MozReview-Commit-ID: FekYjzweKRG --- testing/web-platform/meta/MANIFEST.json | 72 +++++++++++++++++-- .../shape-outside-linear-gradient-005.html | 58 +++++++++++++++ .../shape-outside-linear-gradient-006.html | 58 +++++++++++++++ .../shape-outside-linear-gradient-007.html | 58 +++++++++++++++ .../shape-outside-linear-gradient-008.html | 58 +++++++++++++++ 5 files changed, 300 insertions(+), 4 deletions(-) create mode 100644 testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-005.html create mode 100644 testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-006.html create mode 100644 testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-007.html create mode 100644 testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-008.html diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index 0194714840e5..47967463bba1 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -125727,6 +125727,54 @@ {} ] ], + "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-005.html": [ + [ + "/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-005.html", + [ + [ + "/css/css-shapes/shape-outside/shape-image/gradients/reference/shape-outside-linear-gradient-001-ref.html", + "==" + ] + ], + {} + ] + ], + "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-006.html": [ + [ + "/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-006.html", + [ + [ + "/css/css-shapes/shape-outside/shape-image/gradients/reference/shape-outside-linear-gradient-001-ref.html", + "==" + ] + ], + {} + ] + ], + "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-007.html": [ + [ + "/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-007.html", + [ + [ + "/css/css-shapes/shape-outside/shape-image/gradients/reference/shape-outside-linear-gradient-001-ref.html", + "==" + ] + ], + {} + ] + ], + "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-008.html": [ + [ + "/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-008.html", + [ + [ + "/css/css-shapes/shape-outside/shape-image/gradients/reference/shape-outside-linear-gradient-001-ref.html", + "==" + ] + ], + {} + ] + ], "css/css-shapes/shape-outside/shape-image/shape-image-000.html": [ [ "/css/css-shapes/shape-outside/shape-image/shape-image-000.html", @@ -506862,6 +506910,22 @@ "1e5377c120916557dc1525b38c9cf7eb86ae0151", "reftest" ], + "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-005.html": [ + "dfbf4ad61f18ad05e243e58a2458b5b776ecf3fb", + "reftest" + ], + "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-006.html": [ + "da35426f9ffc53101a053a61512c6928d140adff", + "reftest" + ], + "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-007.html": [ + "03752707f61fbf16290b9733c280b79e00440b96", + "reftest" + ], + "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-008.html": [ + "2ce6445aed0987c0ab7b83f614e9ceb8186fff7b", + "reftest" + ], "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-radial-gradient-001.html": [ "b13df2df3be12ac74a7933794d91558c416b412c", "testharness" @@ -506875,7 +506939,7 @@ "testharness" ], "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-radial-gradient-004.html": [ - "67c3b8ea983da6b7d9b6b7e252b6415ebc7d1f9d", + "fc47f277737dce4f7c6b08d0181355a810107c7b", "testharness" ], "css/css-shapes/shape-outside/shape-image/reference/shape-image-000-ref.html": [ @@ -506947,11 +507011,11 @@ "reftest" ], "css/css-shapes/shape-outside/shape-image/shape-image-001.html": [ - "d97482f9149c68fff971e383ffb72f1371e898ef", + "f69cd2483ed81b496fb5d1c0024b6a16df7375b1", "reftest" ], "css/css-shapes/shape-outside/shape-image/shape-image-002.html": [ - "1f3ace2827629f37439108e43f6528d8d728e5a2", + "299635765004bd1cf9cff8a0888678a2331d7b19", "reftest" ], "css/css-shapes/shape-outside/shape-image/shape-image-003.html": [ @@ -506963,7 +507027,7 @@ "reftest" ], "css/css-shapes/shape-outside/shape-image/shape-image-005.html": [ - "274932a778268e3fda90b289e7f5ca22ee0f9adc", + "38883b770a73cc4eac3170a356b272edb6a5c483", "reftest" ], "css/css-shapes/shape-outside/shape-image/shape-image-006.html": [ diff --git a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-005.html b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-005.html new file mode 100644 index 000000000000..a6228790df7e --- /dev/null +++ b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-005.html @@ -0,0 +1,58 @@ + + + + CSS Test: Test float with linear gradient under writing-mode: vertical-rl + + + + + + + + + + +

+ The test passes if you see a green square. There should be no red. +

+
+
+ x x x x +
+
+
+ x x x x +
+ + diff --git a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-006.html b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-006.html new file mode 100644 index 000000000000..18253edc778d --- /dev/null +++ b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-006.html @@ -0,0 +1,58 @@ + + + + CSS Test: Test float with linear gradient under writing-mode: vertical-lr + + + + + + + + + + +

+ The test passes if you see a green square. There should be no red. +

+
+
+ x x x x +
+
+
+ x x x x +
+ + diff --git a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-007.html b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-007.html new file mode 100644 index 000000000000..9087fb0f093e --- /dev/null +++ b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-007.html @@ -0,0 +1,58 @@ + + + + CSS Test: Test float with linear gradient under writing-mode: sideways-rl + + + + + + + + + + +

+ The test passes if you see a green square. There should be no red. +

+
+
+ x x x x +
+
+
+ x x x x +
+ + diff --git a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-008.html b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-008.html new file mode 100644 index 000000000000..05132b45c29b --- /dev/null +++ b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-008.html @@ -0,0 +1,58 @@ + + + + CSS Test: Test float with linear gradient under writing-mode: sideways-lr + + + + + + + + + + +

+ The test passes if you see a green square. There should be no red. +

+
+
+ x x x x +
+
+
+ x x x x +
+ + From 441df3bbaa1fc3a3ba852242327a80ecfcd3a83e Mon Sep 17 00:00:00 2001 From: Ting-Yu Lin Date: Thu, 25 Jan 2018 15:01:08 +0800 Subject: [PATCH 09/25] Bug 1404222 Part 6: Add a crashtest. r=dholbert The browser should not crash if there's no width and height set on the div with linear-gradient. MozReview-Commit-ID: 2DbfpV7JT2m --- layout/generic/crashtests/1404222-empty-shape.html | 2 ++ layout/generic/crashtests/crashtests.list | 1 + 2 files changed, 3 insertions(+) create mode 100644 layout/generic/crashtests/1404222-empty-shape.html diff --git a/layout/generic/crashtests/1404222-empty-shape.html b/layout/generic/crashtests/1404222-empty-shape.html new file mode 100644 index 000000000000..0f4a62c2cc2e --- /dev/null +++ b/layout/generic/crashtests/1404222-empty-shape.html @@ -0,0 +1,2 @@ + +
diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index 176b33c64425..6acc2bd43974 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -674,6 +674,7 @@ load 1381134-2.html load 1401420-1.html load 1401709.html load 1401807.html +load 1404222-empty-shape.html load 1405443.html load 1415185.html load 1416544.html From a4dd74b531edffa3ba13669f29383a928cec3a25 Mon Sep 17 00:00:00 2001 From: Brad Werth Date: Wed, 7 Feb 2018 14:59:43 -0800 Subject: [PATCH 10/25] Bug 1404222 Part 7: Turn off a 'todo' in a mochitest. r=dholbert MozReview-Commit-ID: 18bkqA6rxoZ --- layout/style/test/file_shape_outside_CORS.html | 1 - 1 file changed, 1 deletion(-) diff --git a/layout/style/test/file_shape_outside_CORS.html b/layout/style/test/file_shape_outside_CORS.html index de558c47f8a0..ec061115802e 100644 --- a/layout/style/test/file_shape_outside_CORS.html +++ b/layout/style/test/file_shape_outside_CORS.html @@ -33,7 +33,6 @@ function sendResults() { window.parent.postMessage({ "result": (divAllowSib.getBoundingClientRect().left == divAllow.getBoundingClientRect().left), "message": "Test 1: Sibling is at same left offset as div (shape-outside was allowed).", - "todo": true, }, DOMAIN); From 67f0edceff1e458066f9a58c3c877425859f71de Mon Sep 17 00:00:00 2001 From: Brad Werth Date: Fri, 9 Feb 2018 16:24:03 -0800 Subject: [PATCH 11/25] Bug 1404222 Part 8: Fix wpt reftest shape-image-001.html to correct a too-wide container. r=dholbert MozReview-Commit-ID: 3fwtUNCqWLX The test currently stretches a 100 x 100 image to 150 pixels wide, which makes the shaded region of the png stretch to 75 pixels. None of the math in the rest of the test accounts for this stretching, and the test fails on all browsers. It seems clear that the intention was to use an unstretched, 100 pixel wide image, which makes the test pass. --- .../css-shapes/shape-outside/shape-image/shape-image-001.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-001.html b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-001.html index 8273030bc0af..ac2d92fba434 100644 --- a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-001.html +++ b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-001.html @@ -22,7 +22,7 @@ } #image { float: left; - width: 150px; + width: 100px; height: 100px; shape-outside: url("support/left-half-rectangle-70.png"); } From 776e43d2298460079ce7c2403969d9ebd8baaf97 Mon Sep 17 00:00:00 2001 From: Brad Werth Date: Fri, 23 Mar 2018 07:23:48 -0700 Subject: [PATCH 12/25] Bug 1404222 Part 9: Fix wpt reftest shape-image-025.html to make every frame of the animated GIF use a green box in the lower-left quadrant. r=dholbert This test is designed to check that the float area is calculated from the first frame of an animated GIF. The reference rendering is a solid green square with the lower-left of the square supplied by the pixels in the GIF. Frame 2 of the GIF has white pixels in that quadrant. If layout is completed and onload is fired while frame 2 is showing, the test will fail even though the float area has been correctly calculated. This change puts green in the lower-left quadrant of all frames of the GIF so that the test is only testing the float area and not also testing that onload fires while a certain frame is showing. Note that this change doesn't impact the thoroughness of how we're testing shape-outside because it changes only the color of the pixels and not their opacity, and the float area is calculated from the alpha of the pixels. MozReview-Commit-ID: LTGlrgbxRyT --- .../shape-image/support/animated.gif | Bin 1816 -> 867 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/support/animated.gif b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/support/animated.gif index 083c9a0a23df0815fe593540ea487ef36119ef4f..9e424df7cdfb6b228b18297334fba7cabdb270cd 100644 GIT binary patch delta 302 zcmbQi_n6Jt-P6s&GBJfAg<&BB!~g&PfeauC1d2adI0YFP7<7ObD44>)!1S-Df92`9 z43h&{ni;t!Uu7v_lIWc5&8p4#Z?Y9zNIm11U4Q$|zy4mn^FRNIW9A+a8R8u?EYeTh zs93=v`NHC)MCMEPnU)nNe=hqu^Sb6Pwx}p`tE{XptyNK{ZoTBmeLMax-+NZ~`Cd*r^>glVdF$HwmhU_F z?wK4{)P9Zs9{ySN{qhU;_rHHHVsFa$l1C-tL&E+Rc5bGC7XnI$8wI8Po`g?)x{H4vn0Q5Ga2m>>+x9`lEY|VBT0GmR7*Z=?k delta 1275 zcmaFNHiNI;-P6s&GBJfAg<&ES!~g&PfeaAIz@Ye_JHj_W!6mUI5lGwER}>^BXQ!4Z zB&DWj=GiK}-@RW+Av48RDcsc8z_-9TH6zobswg$M$}c3jDm&RSMakYy!KT8hBDWwn zwIorYA~z?m*s8)-DKRBKDb)(d1_|reD=C20lw{i~If7&qK$be^7o{qg>KW@M8yFfY zn3?GrnwXlI8R{q)85kPr8vv1+uAzyQshO3LxdId@0j(=aNwW%aaf4b01^l#~=$ z>Fbx5m+O@q>*W`v>l<2HT7t|lGSV$dsZY}_uFNY*tkBIXR)!hjl3JWxlvz-cnV+Ws zGBPnKzqG_w3FuUaCvpqm=H?aafjy*`oS&;-kyxN_sAr&$q|nzFztZ~LT)6V$lAu(C zi?Nzq98y`3svneEoL^d$oC;K~46>`h%DE^tu_V7JBtJg~7LEZK`6c7$ckpLiMKLc8oDjz4}1c=oi%kqZcW6ZM9qnzcEzM1h4fS=kHPuy>73F26 zCB;RB1^IcoIoVm68R==MDalER3Gs2UG0{A;Cd`0selzKHgrQ9`0_gF3wJl z4)%7Dwl>yQmKNq_rY6Qlh6eh2x;olgni}eAsw&D#iVE^_vNF8 zH7z|OGb=l1uH1R@=F6X)Qd(ACQCU@8vsUdob?enHE@^FR@96C6?%At%pT7P2H@A$A mjZaKYP0yS)d)7pP-NY(_>ZUtWfNpxrzj$&2qwnOKOkDsYSkp`Z From 99b5917ab8c50dbe90f882b7d1426b7737c13765 Mon Sep 17 00:00:00 2001 From: Brad Werth Date: Thu, 8 Feb 2018 12:43:48 -0800 Subject: [PATCH 13/25] Bug 1404222 Part 10: Mark as PASS all shape-outside image web-platform-tests that don't rely on shape-margin. r=dholbert MozReview-Commit-ID: FNgeeBpFtMs shape-margin support is being added in https://bugzilla.mozilla.org/show_bug.cgi?id=1265342. --- .../gradients/shape-outside-linear-gradient-001.html.ini | 2 -- .../gradients/shape-outside-linear-gradient-002.html.ini | 2 -- .../gradients/shape-outside-linear-gradient-003.html.ini | 2 -- .../gradients/shape-outside-linear-gradient-004.html.ini | 2 -- .../gradients/shape-outside-radial-gradient-001.html.ini | 4 ---- .../shape-outside/shape-image/shape-image-000.html.ini | 2 -- .../shape-outside/shape-image/shape-image-001.html.ini | 2 -- .../shape-outside/shape-image/shape-image-002.html.ini | 2 -- .../shape-outside/shape-image/shape-image-003.html.ini | 2 -- .../shape-outside/shape-image/shape-image-004.html.ini | 2 -- .../shape-outside/shape-image/shape-image-005.html.ini | 2 -- .../shape-outside/shape-image/shape-image-012.html.ini | 2 -- .../shape-outside/shape-image/shape-image-013.html.ini | 2 -- .../shape-outside/shape-image/shape-image-014.html.ini | 2 -- .../shape-outside/shape-image/shape-image-015.html.ini | 2 -- .../shape-outside/shape-image/shape-image-016.html.ini | 2 -- .../shape-outside/shape-image/shape-image-017.html.ini | 2 -- .../shape-outside/shape-image/shape-image-025.html.ini | 2 -- .../css/css-shapes/spec-examples/shape-outside-010.html.ini | 4 ---- .../css/css-shapes/spec-examples/shape-outside-011.html.ini | 4 ---- .../css/css-shapes/spec-examples/shape-outside-012.html.ini | 4 ---- 21 files changed, 50 deletions(-) delete mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-001.html.ini delete mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-002.html.ini delete mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-003.html.ini delete mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-004.html.ini delete mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-radial-gradient-001.html.ini delete mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-000.html.ini delete mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-001.html.ini delete mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-002.html.ini delete mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-003.html.ini delete mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-004.html.ini delete mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-005.html.ini delete mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-012.html.ini delete mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-013.html.ini delete mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-014.html.ini delete mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-015.html.ini delete mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-016.html.ini delete mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-017.html.ini delete mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-025.html.ini delete mode 100644 testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-010.html.ini delete mode 100644 testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-011.html.ini delete mode 100644 testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-012.html.ini diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-001.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-001.html.ini deleted file mode 100644 index 89b86425076c..000000000000 --- a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-001.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[shape-outside-linear-gradient-001.html] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-002.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-002.html.ini deleted file mode 100644 index 6d559345a736..000000000000 --- a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-002.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[shape-outside-linear-gradient-002.html] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-003.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-003.html.ini deleted file mode 100644 index b6bee5d07cbc..000000000000 --- a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-003.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[shape-outside-linear-gradient-003.html] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-004.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-004.html.ini deleted file mode 100644 index 3bc010629c8b..000000000000 --- a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-004.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[shape-outside-linear-gradient-004.html] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-radial-gradient-001.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-radial-gradient-001.html.ini deleted file mode 100644 index 690b6ba5f5d8..000000000000 --- a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-radial-gradient-001.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[shape-outside-radial-gradient-001.html] - [CSS Test: Left float with radial gradient] - expected: FAIL - diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-000.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-000.html.ini deleted file mode 100644 index f73a31be3c6c..000000000000 --- a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-000.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[shape-image-000.html] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-001.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-001.html.ini deleted file mode 100644 index df0a0aada282..000000000000 --- a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-001.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[shape-image-001.html] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-002.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-002.html.ini deleted file mode 100644 index 01d250923337..000000000000 --- a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-002.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[shape-image-002.html] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-003.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-003.html.ini deleted file mode 100644 index 0a4d46925531..000000000000 --- a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-003.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[shape-image-003.html] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-004.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-004.html.ini deleted file mode 100644 index 461a202b5972..000000000000 --- a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-004.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[shape-image-004.html] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-005.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-005.html.ini deleted file mode 100644 index 72c200237f49..000000000000 --- a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-005.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[shape-image-005.html] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-012.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-012.html.ini deleted file mode 100644 index e5e0d1099cff..000000000000 --- a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-012.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[shape-image-012.html] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-013.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-013.html.ini deleted file mode 100644 index c73bf089c413..000000000000 --- a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-013.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[shape-image-013.html] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-014.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-014.html.ini deleted file mode 100644 index a95f6092dd03..000000000000 --- a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-014.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[shape-image-014.html] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-015.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-015.html.ini deleted file mode 100644 index e04bb5be7f4a..000000000000 --- a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-015.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[shape-image-015.html] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-016.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-016.html.ini deleted file mode 100644 index db84eb8e3adc..000000000000 --- a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-016.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[shape-image-016.html] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-017.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-017.html.ini deleted file mode 100644 index 921acf8b0d3a..000000000000 --- a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-017.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[shape-image-017.html] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-025.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-025.html.ini deleted file mode 100644 index 1cea8dab999b..000000000000 --- a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-025.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[shape-image-025.html] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-010.html.ini b/testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-010.html.ini deleted file mode 100644 index cafe9b6219c7..000000000000 --- a/testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-010.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[shape-outside-010.html] - [CSS Test: Shape from image - url(), alpha channel, opacity 0] - expected: FAIL - diff --git a/testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-011.html.ini b/testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-011.html.ini deleted file mode 100644 index 78c48b2decb0..000000000000 --- a/testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-011.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[shape-outside-011.html] - [CSS Test: Shape from image - url(), alpha channel, opacity > 0] - expected: FAIL - diff --git a/testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-012.html.ini b/testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-012.html.ini deleted file mode 100644 index aea7237462ab..000000000000 --- a/testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-012.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[shape-outside-012.html] - [CSS Test: Shape from image - shape-image-threshold - 0.9] - expected: FAIL - From 2f5d7919b09160c2bcde0ea9ba734efa8a9ccdb3 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Mon, 2 Apr 2018 14:08:33 -0700 Subject: [PATCH 14/25] Bug 1404222 followup: add 'explicit' keyword to fix static analysis build error. (no review, trivial bustagefix) --- layout/style/ImageLoader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layout/style/ImageLoader.h b/layout/style/ImageLoader.h index b0a04ecdbbbf..0fa13da6536a 100644 --- a/layout/style/ImageLoader.h +++ b/layout/style/ImageLoader.h @@ -112,7 +112,7 @@ private: // should always be in sync. struct FrameWithFlags { - FrameWithFlags(nsIFrame* aFrame) + explicit FrameWithFlags(nsIFrame* aFrame) : mFrame(aFrame), mFlags(0) { From 152a7fc82e38a048e6494972b4996892bec8fcbe Mon Sep 17 00:00:00 2001 From: David Major Date: Mon, 2 Apr 2018 17:20:35 -0400 Subject: [PATCH 15/25] Backed out bug 1422368 fixes because they weren't sufficient. --- gfx/ycbcr/README | 2 -- gfx/ycbcr/clang-cl-workaround.patch | 28 ---------------------------- gfx/ycbcr/update.sh | 1 - gfx/ycbcr/yuv_row_win.cpp | 8 -------- 4 files changed, 39 deletions(-) delete mode 100644 gfx/ycbcr/clang-cl-workaround.patch diff --git a/gfx/ycbcr/README b/gfx/ycbcr/README index 0baa56d14ee2..a951bc83a84d 100644 --- a/gfx/ycbcr/README +++ b/gfx/ycbcr/README @@ -27,5 +27,3 @@ win64.patch: SSE2 optimization for Microsoft Visual C++ x64 version TypeFromSize.patch: Bug 656185 - Add a method to detect YUVType from plane sizes. QuellGccWarnings.patch: Bug 711895 - Avoid some GCC compilation warnings. - -clang-cl-workaround.patch: Bug 1422368 - Work around a clang-cl unresolved symbol bug. diff --git a/gfx/ycbcr/clang-cl-workaround.patch b/gfx/ycbcr/clang-cl-workaround.patch deleted file mode 100644 index 1072ad4007ab..000000000000 --- a/gfx/ycbcr/clang-cl-workaround.patch +++ /dev/null @@ -1,28 +0,0 @@ -diff --git a/gfx/ycbcr/yuv_row_win.cpp b/gfx/ycbcr/yuv_row_win.cpp ---- a/gfx/ycbcr/yuv_row_win.cpp -+++ b/gfx/ycbcr/yuv_row_win.cpp -@@ -7,19 +7,21 @@ - - #define kCoefficientsRgbU kCoefficientsRgbY + 2048 - #define kCoefficientsRgbV kCoefficientsRgbY + 4096 - - extern "C" { - - #if defined(MOZILLA_MAY_SUPPORT_SSE) && defined(_M_IX86) - #if defined(__clang__) --// clang-cl may erroneously discard the symbol `kCoefficientsRgbY` --// https://bugs.llvm.org/show_bug.cgi?id=35290 --volatile auto keep_kCoefficientsRgbY_alive = &kCoefficientsRgbY; -+// clang-cl has a bug where it doesn't mangle names in inline asm -+// so let's do the mangling in the preprocessor (ugh) -+// (but we still need to declare a dummy extern for the parser) -+extern void* _kCoefficientsRgbY; -+#define kCoefficientsRgbY _kCoefficientsRgbY - #endif - - __declspec(naked) - void FastConvertYUVToRGB32Row_SSE(const uint8* y_buf, - const uint8* u_buf, - const uint8* v_buf, - uint8* rgb_buf, - int width) { diff --git a/gfx/ycbcr/update.sh b/gfx/ycbcr/update.sh index 6607e1eb294b..3a38fe81a3d5 100644 --- a/gfx/ycbcr/update.sh +++ b/gfx/ycbcr/update.sh @@ -10,4 +10,3 @@ patch -p3 Date: Mon, 2 Apr 2018 17:22:06 -0400 Subject: [PATCH 16/25] Backed out changeset 737b602f4c6f864b34080d0fe57c2ff75de3b909 because I shouldn't back out the original fix until the better one lands. --- gfx/ycbcr/README | 2 ++ gfx/ycbcr/clang-cl-workaround.patch | 28 ++++++++++++++++++++++++++++ gfx/ycbcr/update.sh | 1 + gfx/ycbcr/yuv_row_win.cpp | 8 ++++++++ 4 files changed, 39 insertions(+) create mode 100644 gfx/ycbcr/clang-cl-workaround.patch diff --git a/gfx/ycbcr/README b/gfx/ycbcr/README index a951bc83a84d..0baa56d14ee2 100644 --- a/gfx/ycbcr/README +++ b/gfx/ycbcr/README @@ -27,3 +27,5 @@ win64.patch: SSE2 optimization for Microsoft Visual C++ x64 version TypeFromSize.patch: Bug 656185 - Add a method to detect YUVType from plane sizes. QuellGccWarnings.patch: Bug 711895 - Avoid some GCC compilation warnings. + +clang-cl-workaround.patch: Bug 1422368 - Work around a clang-cl unresolved symbol bug. diff --git a/gfx/ycbcr/clang-cl-workaround.patch b/gfx/ycbcr/clang-cl-workaround.patch new file mode 100644 index 000000000000..1072ad4007ab --- /dev/null +++ b/gfx/ycbcr/clang-cl-workaround.patch @@ -0,0 +1,28 @@ +diff --git a/gfx/ycbcr/yuv_row_win.cpp b/gfx/ycbcr/yuv_row_win.cpp +--- a/gfx/ycbcr/yuv_row_win.cpp ++++ b/gfx/ycbcr/yuv_row_win.cpp +@@ -7,19 +7,21 @@ + + #define kCoefficientsRgbU kCoefficientsRgbY + 2048 + #define kCoefficientsRgbV kCoefficientsRgbY + 4096 + + extern "C" { + + #if defined(MOZILLA_MAY_SUPPORT_SSE) && defined(_M_IX86) + #if defined(__clang__) +-// clang-cl may erroneously discard the symbol `kCoefficientsRgbY` +-// https://bugs.llvm.org/show_bug.cgi?id=35290 +-volatile auto keep_kCoefficientsRgbY_alive = &kCoefficientsRgbY; ++// clang-cl has a bug where it doesn't mangle names in inline asm ++// so let's do the mangling in the preprocessor (ugh) ++// (but we still need to declare a dummy extern for the parser) ++extern void* _kCoefficientsRgbY; ++#define kCoefficientsRgbY _kCoefficientsRgbY + #endif + + __declspec(naked) + void FastConvertYUVToRGB32Row_SSE(const uint8* y_buf, + const uint8* u_buf, + const uint8* v_buf, + uint8* rgb_buf, + int width) { diff --git a/gfx/ycbcr/update.sh b/gfx/ycbcr/update.sh index 3a38fe81a3d5..6607e1eb294b 100644 --- a/gfx/ycbcr/update.sh +++ b/gfx/ycbcr/update.sh @@ -10,3 +10,4 @@ patch -p3 Date: Mon, 2 Apr 2018 17:57:27 -0400 Subject: [PATCH 17/25] Backed out 11 changesets (bug 1404222) for static analysis failures on a CLOSED TREE. Backed out changeset a6a99136300c (bug 1404222) Backed out changeset 7183b8104399 (bug 1404222) Backed out changeset a1e4294c1c59 (bug 1404222) Backed out changeset b79d6e8318db (bug 1404222) Backed out changeset 0450620fdabd (bug 1404222) Backed out changeset 026c74a92d04 (bug 1404222) Backed out changeset 50ac4167f702 (bug 1404222) Backed out changeset 59038f2db68a (bug 1404222) Backed out changeset f6b9096da915 (bug 1404222) Backed out changeset 4e0baffdd79b (bug 1404222) Backed out changeset 57eeb849ab88 (bug 1404222) --- .../crashtests/1404222-empty-shape.html | 2 - layout/generic/crashtests/crashtests.list | 1 - layout/generic/nsFloatManager.cpp | 361 +----------------- layout/generic/nsFloatManager.h | 1 - layout/generic/nsFrame.cpp | 30 +- layout/generic/nsIFrame.h | 3 +- layout/painting/nsCSSRendering.cpp | 2 +- layout/painting/nsCSSRenderingBorders.cpp | 2 +- layout/painting/nsImageRenderer.cpp | 66 +--- layout/painting/nsImageRenderer.h | 8 - layout/style/ImageLoader.cpp | 169 +------- layout/style/ImageLoader.h | 64 +--- layout/style/nsStyleStruct.cpp | 12 - layout/style/nsStyleStruct.h | 5 - .../style/test/file_shape_outside_CORS.html | 1 + layout/svg/SVGObserverUtils.cpp | 2 +- testing/web-platform/meta/MANIFEST.json | 72 +--- ...shape-outside-linear-gradient-001.html.ini | 2 + ...shape-outside-linear-gradient-002.html.ini | 2 + ...shape-outside-linear-gradient-003.html.ini | 2 + ...shape-outside-linear-gradient-004.html.ini | 2 + ...shape-outside-radial-gradient-001.html.ini | 4 + .../shape-image/shape-image-000.html.ini | 2 + .../shape-image/shape-image-001.html.ini | 2 + .../shape-image/shape-image-002.html.ini | 2 + .../shape-image/shape-image-003.html.ini | 2 + .../shape-image/shape-image-004.html.ini | 2 + .../shape-image/shape-image-005.html.ini | 2 + .../shape-image/shape-image-012.html.ini | 2 + .../shape-image/shape-image-013.html.ini | 2 + .../shape-image/shape-image-014.html.ini | 2 + .../shape-image/shape-image-015.html.ini | 2 + .../shape-image/shape-image-016.html.ini | 2 + .../shape-image/shape-image-017.html.ini | 2 + .../shape-image/shape-image-025.html.ini | 2 + .../spec-examples/shape-outside-010.html.ini | 4 + .../spec-examples/shape-outside-011.html.ini | 4 + .../spec-examples/shape-outside-012.html.ini | 4 + .../shape-outside-linear-gradient-005.html | 58 --- .../shape-outside-linear-gradient-006.html | 58 --- .../shape-outside-linear-gradient-007.html | 58 --- .../shape-outside-linear-gradient-008.html | 58 --- .../shape-image/shape-image-001.html | 2 +- .../shape-image/shape-image-002.html | 4 +- .../shape-image/shape-image-005.html | 4 +- .../shape-image/support/animated.gif | Bin 867 -> 1816 bytes 46 files changed, 89 insertions(+), 1004 deletions(-) delete mode 100644 layout/generic/crashtests/1404222-empty-shape.html create mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-001.html.ini create mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-002.html.ini create mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-003.html.ini create mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-004.html.ini create mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-radial-gradient-001.html.ini create mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-000.html.ini create mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-001.html.ini create mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-002.html.ini create mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-003.html.ini create mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-004.html.ini create mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-005.html.ini create mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-012.html.ini create mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-013.html.ini create mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-014.html.ini create mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-015.html.ini create mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-016.html.ini create mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-017.html.ini create mode 100644 testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-025.html.ini create mode 100644 testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-010.html.ini create mode 100644 testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-011.html.ini create mode 100644 testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-012.html.ini delete mode 100644 testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-005.html delete mode 100644 testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-006.html delete mode 100644 testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-007.html delete mode 100644 testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-008.html diff --git a/layout/generic/crashtests/1404222-empty-shape.html b/layout/generic/crashtests/1404222-empty-shape.html deleted file mode 100644 index 0f4a62c2cc2e..000000000000 --- a/layout/generic/crashtests/1404222-empty-shape.html +++ /dev/null @@ -1,2 +0,0 @@ - -
diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index 6acc2bd43974..176b33c64425 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -674,7 +674,6 @@ load 1381134-2.html load 1401420-1.html load 1401709.html load 1401807.html -load 1404222-empty-shape.html load 1405443.html load 1415185.html load 1416544.html diff --git a/layout/generic/nsFloatManager.cpp b/layout/generic/nsFloatManager.cpp index 90e142647673..1c0fd78038ff 100644 --- a/layout/generic/nsFloatManager.cpp +++ b/layout/generic/nsFloatManager.cpp @@ -11,12 +11,10 @@ #include #include -#include "gfxContext.h" #include "mozilla/ReflowInput.h" #include "mozilla/ShapeUtils.h" #include "nsBlockFrame.h" #include "nsError.h" -#include "nsImageRenderer.h" #include "nsIPresShell.h" #include "nsMemory.h" @@ -177,8 +175,9 @@ nsFloatManager::GetFlowArea(WritingMode aWM, nscoord aBCoord, nscoord aBSize, break; } if (fi.IsEmpty(aShapeType)) { - // Ignore empty float areas. - // https://drafts.csswg.org/css-shapes/#relation-to-box-model-and-float-behavior + // For compatibility, ignore floats with empty rects, even though it + // disagrees with the spec. (We might want to fix this in the + // future, though.) continue; } @@ -581,13 +580,6 @@ public: WritingMode aWM, const nsSize& aContainerSize); - static UniquePtr CreateImageShape( - const UniquePtr& aShapeImage, - float aShapeImageThreshold, - nsIFrame* const aFrame, - WritingMode aWM, - const nsSize& aContainerSize); - protected: // Compute the minimum line-axis difference between the bounding shape // box and its rounded corner within the given band (block-axis region). @@ -623,7 +615,6 @@ protected: // RoundedBoxShapeInfo // // Implements shape-outside: and shape-outside: inset(). -// class nsFloatManager::RoundedBoxShapeInfo final : public nsFloatManager::ShapeInfo { public: @@ -961,246 +952,6 @@ nsFloatManager::PolygonShapeInfo::XInterceptAtY(const nscoord aY, return aP1.x + (aY - aP1.y) * (aP2.x - aP1.x) / (aP2.y - aP1.y); } -///////////////////////////////////////////////////////////////////////////// -// ImageShapeInfo -// -// Implements shape-outside: -// -class nsFloatManager::ImageShapeInfo final : public nsFloatManager::ShapeInfo -{ -public: - ImageShapeInfo(uint8_t* aAlphaPixels, - int32_t aStride, - const LayoutDeviceIntSize& aImageSize, - int32_t aAppUnitsPerDevPixel, - float aShapeImageThreshold, - const nsRect& aContentRect, - WritingMode aWM, - const nsSize& aContainerSize); - - nscoord LineLeft(const nscoord aBStart, - const nscoord aBEnd) const override; - nscoord LineRight(const nscoord aBStart, - const nscoord aBEnd) const override; - nscoord BStart() const override { return mBStart; } - nscoord BEnd() const override { return mBEnd; } - bool IsEmpty() const override { return mIntervals.IsEmpty(); } - - void Translate(nscoord aLineLeft, nscoord aBlockStart) override; - -private: - size_t MinIntervalIndexContainingY(const nscoord aTargetY) const; - nscoord LineEdge(const nscoord aBStart, - const nscoord aBEnd, - bool aLeft) const; - - // An interval is slice of the float area defined by this ImageShapeInfo. - // Each interval is a rectangle that is one pixel deep in the block - // axis. The values are stored as block edges in the y coordinates, - // and inline edges as the x coordinates. - - // The intervals are stored in ascending order on y. - nsTArray mIntervals; - - nscoord mBStart = nscoord_MAX; - nscoord mBEnd = nscoord_MIN; -}; - -nsFloatManager::ImageShapeInfo::ImageShapeInfo( - uint8_t* aAlphaPixels, - int32_t aStride, - const LayoutDeviceIntSize& aImageSize, - int32_t aAppUnitsPerDevPixel, - float aShapeImageThreshold, - const nsRect& aContentRect, - WritingMode aWM, - const nsSize& aContainerSize) -{ - MOZ_ASSERT(aShapeImageThreshold >=0.0 && aShapeImageThreshold <=1.0, - "The computed value of shape-image-threshold is wrong!"); - - const uint8_t threshold = NSToIntFloor(aShapeImageThreshold * 255); - const int32_t w = aImageSize.width; - const int32_t h = aImageSize.height; - - // Scan the pixels in a double loop. For horizontal writing modes, we do - // this row by row, from top to bottom. For vertical writing modes, we do - // column by column, from left to right. We define the two loops - // generically, then figure out the rows and cols within the i loop. - const int32_t bSize = aWM.IsVertical() ? w : h; - const int32_t iSize = aWM.IsVertical() ? h : w; - for (int32_t b = 0; b < bSize; ++b) { - // iMin and iMax store the start and end of the float area for the row - // or column represented by this iteration of the b loop. - int32_t iMin = -1; - int32_t iMax = -1; - - for (int32_t i = 0; i < iSize; ++i) { - const int32_t col = aWM.IsVertical() ? b : i; - const int32_t row = aWM.IsVertical() ? i : b; - - // Determine if the alpha pixel at this row and column has a value - // greater than the threshold. If it does, update our iMin and iMax values - // to track the edges of the float area for this row or column. - // https://drafts.csswg.org/css-shapes-1/#valdef-shape-image-threshold-number - const uint8_t alpha = aAlphaPixels[col + row * aStride]; - if (alpha > threshold) { - if (iMin == -1) { - iMin = i; - } - MOZ_ASSERT(iMax < i); - iMax = i; - } - } - - // At the end of a row or column; did we find something? - if (iMin != -1) { - // Store an interval as an nsRect with our inline axis values stored in x - // and our block axis values stored in y. The position is dependent on - // the writing mode, but the size is the same for all writing modes. - - // Size is the difference in inline axis edges stored as x, and one - // block axis pixel stored as y. For the inline axis, we add 1 to iMax - // because we want to capture the far edge of the last pixel. - nsSize size(((iMax + 1) - iMin) * aAppUnitsPerDevPixel, - aAppUnitsPerDevPixel); - - // Since we started our scanning of the image pixels from the top left, - // the interval position starts from the origin of the content rect, - // converted to logical coordinates. - nsPoint origin = ConvertToFloatLogical(aContentRect.TopLeft(), aWM, - aContainerSize); - - // Depending on the writing mode, we now move the origin. - if (aWM.IsVerticalRL()) { - // vertical-rl or sideways-rl. - // These writing modes proceed from the top right, and each interval - // moves in a positive inline direction and negative block direction. - // That means that the intervals will be reversed after all have been - // constructed. We add 1 to b to capture the end of the block axis pixel. - origin.MoveBy(iMin * aAppUnitsPerDevPixel, (b + 1) * -aAppUnitsPerDevPixel); - } else if (aWM.IsVerticalLR() && aWM.IsSideways()) { - // sideways-lr. - // These writing modes proceed from the bottom left, and each interval - // moves in a negative inline direction and a positive block direction. - // We add 1 to iMax to capture the end of the inline axis pixel. - origin.MoveBy((iMax + 1) * -aAppUnitsPerDevPixel, b * aAppUnitsPerDevPixel); - } else { - // horizontal-tb or vertical-lr. - // These writing modes proceed from the top left and each interval - // moves in a positive step in both inline and block directions. - origin.MoveBy(iMin * aAppUnitsPerDevPixel, b * aAppUnitsPerDevPixel); - } - - mIntervals.AppendElement(nsRect(origin, size)); - } - } - - if (aWM.IsVerticalRL()) { - // vertical-rl or sideways-rl. - // Because we scan the columns from left to right, we need to reverse - // the array so that it's sorted (in ascending order) on the block - // direction. - mIntervals.Reverse(); - } - - if (!mIntervals.IsEmpty()) { - mBStart = mIntervals[0].Y(); - mBEnd = mIntervals.LastElement().YMost(); - } -} - -size_t -nsFloatManager::ImageShapeInfo::MinIntervalIndexContainingY( - const nscoord aTargetY) const -{ - // Perform a binary search to find the minimum index of an interval - // that contains aTargetY. If no such interval exists, return a value - // equal to the number of intervals. - size_t startIdx = 0; - size_t endIdx = mIntervals.Length(); - while (startIdx < endIdx) { - size_t midIdx = startIdx + (endIdx - startIdx) / 2; - if (mIntervals[midIdx].ContainsY(aTargetY)) { - return midIdx; - } - nscoord midY = mIntervals[midIdx].Y(); - if (midY < aTargetY) { - startIdx = midIdx + 1; - } else { - endIdx = midIdx; - } - } - - return endIdx; -} - -nscoord -nsFloatManager::ImageShapeInfo::LineEdge(const nscoord aBStart, - const nscoord aBEnd, - bool aLeft) const -{ - MOZ_ASSERT(aBStart <= aBEnd, - "The band's block start is greater than its block end?"); - - // Find all the intervals whose rects overlap the aBStart to - // aBEnd range, and find the most constraining inline edge - // depending on the value of aLeft. - - // Since the intervals are stored in block-axis order, we need - // to find the first interval that overlaps aBStart and check - // succeeding intervals until we get past aBEnd. - - nscoord lineEdge = aLeft ? nscoord_MAX : nscoord_MIN; - - size_t intervalCount = mIntervals.Length(); - for (size_t i = MinIntervalIndexContainingY(aBStart); - i < intervalCount; ++i) { - // We can always get the bCoord from the intervals' mLineLeft, - // since the y() coordinate is duplicated in both points in the - // interval. - auto& interval = mIntervals[i]; - nscoord bCoord = interval.Y(); - if (bCoord > aBEnd) { - break; - } - // Get the edge from the interval point indicated by aLeft. - if (aLeft) { - lineEdge = std::min(lineEdge, interval.X()); - } else { - lineEdge = std::max(lineEdge, interval.XMost()); - } - } - - return lineEdge; -} - -nscoord -nsFloatManager::ImageShapeInfo::LineLeft(const nscoord aBStart, - const nscoord aBEnd) const -{ - return LineEdge(aBStart, aBEnd, true); -} - -nscoord -nsFloatManager::ImageShapeInfo::LineRight(const nscoord aBStart, - const nscoord aBEnd) const -{ - return LineEdge(aBStart, aBEnd, false); -} - -void -nsFloatManager::ImageShapeInfo::Translate(nscoord aLineLeft, - nscoord aBlockStart) -{ - for (nsRect& interval : mIntervals) { - interval.MoveBy(aLineLeft, aBlockStart); - } - - mBStart += aBlockStart; - mBEnd += aBlockStart; -} - ///////////////////////////////////////////////////////////////////////////// // FloatInfo @@ -1215,16 +966,6 @@ nsFloatManager::FloatInfo::FloatInfo(nsIFrame* aFrame, { MOZ_COUNT_CTOR(nsFloatManager::FloatInfo); - if (IsEmpty()) { - // Per spec, a float area defined by a shape is clipped to the float’s - // margin box. Therefore, no need to create a shape info if the float's - // margin box is empty, since a float area can only be smaller than the - // margin box. - - // https://drafts.csswg.org/css-shapes/#relation-to-box-model-and-float-behavior - return; - } - const StyleShapeSource& shapeOutside = mFrame->StyleDisplay()->mShapeOutside; switch (shapeOutside.GetType()) { @@ -1236,20 +977,10 @@ nsFloatManager::FloatInfo::FloatInfo(nsIFrame* aFrame, MOZ_ASSERT_UNREACHABLE("shape-outside doesn't have URL source type!"); return; - case StyleShapeSourceType::Image: { - float shapeImageThreshold = mFrame->StyleDisplay()->mShapeImageThreshold; - mShapeInfo = ShapeInfo::CreateImageShape(shapeOutside.GetShapeImage(), - shapeImageThreshold, - mFrame, - aWM, - aContainerSize); - if (!mShapeInfo) { - // Image is not ready, or fails to load, etc. - return; - } - - break; - } + case StyleShapeSourceType::Image: + // Bug 1265343: Implement 'shape-image-threshold' + // Bug 1404222: Support shape-outside: + return; case StyleShapeSourceType::Box: { // Initialize 's reference rect. @@ -1546,84 +1277,6 @@ nsFloatManager::ShapeInfo::CreatePolygon( return MakeUnique(Move(vertices)); } -/* static */ UniquePtr -nsFloatManager::ShapeInfo::CreateImageShape( - const UniquePtr& aShapeImage, - float aShapeImageThreshold, - nsIFrame* const aFrame, - WritingMode aWM, - const nsSize& aContainerSize) -{ - MOZ_ASSERT(aShapeImage == - aFrame->StyleDisplay()->mShapeOutside.GetShapeImage(), - "aFrame should be the frame that we got aShapeImage from"); - - nsImageRenderer imageRenderer(aFrame, aShapeImage.get(), - nsImageRenderer::FLAG_SYNC_DECODE_IMAGES); - - if (!imageRenderer.PrepareImage()) { - // The image is not ready yet. - return nullptr; - } - - nsRect contentRect = aFrame->GetContentRect(); - - // Create a draw target and draw shape image on it. - nsDeviceContext* dc = aFrame->PresContext()->DeviceContext(); - int32_t appUnitsPerDevPixel = dc->AppUnitsPerDevPixel(); - LayoutDeviceIntSize contentSizeInDevPixels = - LayoutDeviceIntSize::FromAppUnitsRounded(contentRect.Size(), - appUnitsPerDevPixel); - - // Use empty CSSSizeOrRatio to force set the preferred size as the frame's - // content box size. - imageRenderer.SetPreferredSize(CSSSizeOrRatio(), contentRect.Size()); - - RefPtr drawTarget = - gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget( - contentSizeInDevPixels.ToUnknownSize(), - gfx::SurfaceFormat::A8); - if (!drawTarget) { - return nullptr; - } - - RefPtr context = gfxContext::CreateOrNull(drawTarget); - MOZ_ASSERT(context); // already checked the target above - - ImgDrawResult result = - imageRenderer.DrawShapeImage(aFrame->PresContext(), *context); - - if (result != ImgDrawResult::SUCCESS) { - return nullptr; - } - - // Retrieve the pixel image buffer to create the image shape info. - RefPtr sourceSurface = drawTarget->Snapshot(); - RefPtr dataSourceSurface = sourceSurface->GetDataSurface(); - DataSourceSurface::ScopedMap map(dataSourceSurface, DataSourceSurface::READ); - - if (!map.IsMapped()) { - return nullptr; - } - - MOZ_ASSERT(sourceSurface->GetSize() == contentSizeInDevPixels.ToUnknownSize(), - "Who changes the size?"); - - uint8_t* alphaPixels = map.GetData(); - int32_t stride = map.GetStride(); - - // NOTE: ImageShapeInfo constructor does not keep a persistent copy of - // alphaPixels; it's only used during the constructor to compute pixel ranges. - return MakeUnique(alphaPixels, - stride, - contentSizeInDevPixels, - appUnitsPerDevPixel, - aShapeImageThreshold, - contentRect, - aWM, - aContainerSize); -} - /* static */ nscoord nsFloatManager::ShapeInfo::ComputeEllipseLineInterceptDiff( const nscoord aShapeBoxBStart, const nscoord aShapeBoxBEnd, diff --git a/layout/generic/nsFloatManager.h b/layout/generic/nsFloatManager.h index 7cfedfe86c68..f45bc0286fd8 100644 --- a/layout/generic/nsFloatManager.h +++ b/layout/generic/nsFloatManager.h @@ -344,7 +344,6 @@ private: class RoundedBoxShapeInfo; class EllipseShapeInfo; class PolygonShapeInfo; - class ImageShapeInfo; struct FloatInfo { nsIFrame *const mFrame; diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 9993872546de..d5c1fe301fcc 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -106,6 +106,7 @@ #include "mozilla/LookAndFeel.h" #include "mozilla/MouseEvents.h" #include "mozilla/ServoStyleSet.h" +#include "mozilla/css/ImageLoader.h" #include "mozilla/gfx/Tools.h" #include "nsPrintfCString.h" #include "ActiveLayerTracker.h" @@ -113,8 +114,6 @@ #include "nsITheme.h" #include "nsThemeConstants.h" -#include "ImageLoader.h" - using namespace mozilla; using namespace mozilla::css; using namespace mozilla::dom; @@ -908,7 +907,7 @@ AddAndRemoveImageAssociations(nsFrame* aFrame, CompareLayers(aNewLayers, aOldLayers, [&imageLoader, aFrame](imgRequestProxy* aReq) - { imageLoader->AssociateRequestToFrame(aReq, aFrame, 0); } + { imageLoader->AssociateRequestToFrame(aReq, aFrame); } ); } @@ -1176,24 +1175,7 @@ nsFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) imageLoader->DisassociateRequestFromFrame(oldBorderImage, this); } if (newBorderImage) { - imageLoader->AssociateRequestToFrame(newBorderImage, this, 0); - } - } - - imgIRequest* oldShapeImage = - aOldComputedStyle - ? aOldComputedStyle->StyleDisplay()->mShapeOutside.GetShapeImageData() - : nullptr; - imgIRequest* newShapeImage = - StyleDisplay()->mShapeOutside.GetShapeImageData(); - - if (oldShapeImage != newShapeImage) { - if (oldShapeImage && HasImageRequest()) { - imageLoader->DisassociateRequestFromFrame(oldShapeImage, this); - } - if (newShapeImage) { - imageLoader->AssociateRequestToFrame(newShapeImage, this, - ImageLoader::REQUEST_REQUIRES_REFLOW); + imageLoader->AssociateRequestToFrame(newBorderImage, this); } } @@ -5238,8 +5220,7 @@ nsIFrame::ContentOffsets nsFrame::CalcContentOffsetsFromFramePoint(const nsPoint } void -nsIFrame::AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext, - uint32_t aImageLoaderFlags) +nsIFrame::AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext) { if (aImage.GetType() != eStyleImageType_Image) { return; @@ -5249,11 +5230,12 @@ nsIFrame::AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext if (!req) { return; } + mozilla::css::ImageLoader* loader = aPresContext->Document()->StyleImageLoader(); // If this fails there's not much we can do ... - loader->AssociateRequestToFrame(req, this, aImageLoaderFlags); + loader->AssociateRequestToFrame(req, this); } nsresult diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index d0dea10b2484..269b573d1172 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -1955,8 +1955,7 @@ public: * Ensure that aImage gets notifed when the underlying image request loads * or animates. */ - void AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext, - uint32_t aImageLoaderFlags); + void AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext); /** * This structure holds information about a cursor. mContainer represents a diff --git a/layout/painting/nsCSSRendering.cpp b/layout/painting/nsCSSRendering.cpp index c2d86ceb2236..2b951131c75c 100644 --- a/layout/painting/nsCSSRendering.cpp +++ b/layout/painting/nsCSSRendering.cpp @@ -2745,7 +2745,7 @@ nsCSSRendering::PaintStyleImageLayerWithSC(const PaintBGParams& aParams, NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT_WITH_RANGE(i, layers, startLayer, count) { aParams.frame->AssociateImage(layers.mLayers[i].mImage, - &aParams.presCtx, 0); + &aParams.presCtx); } } diff --git a/layout/painting/nsCSSRenderingBorders.cpp b/layout/painting/nsCSSRenderingBorders.cpp index aa998884f672..3f9bc8dc498b 100644 --- a/layout/painting/nsCSSRenderingBorders.cpp +++ b/layout/painting/nsCSSRenderingBorders.cpp @@ -3486,7 +3486,7 @@ nsCSSBorderImageRenderer::CreateBorderImageRenderer(nsPresContext* aPresContext, // XXX We shouldn't really... since if anybody is passing in a // different style, they'll potentially have the wrong size for the // border too. - aForFrame->AssociateImage(aStyleBorder.mBorderImageSource, aPresContext, 0); + aForFrame->AssociateImage(aStyleBorder.mBorderImageSource, aPresContext); nsCSSBorderImageRenderer renderer(aForFrame, aBorderArea, aStyleBorder, aSkipSides, imgRenderer); diff --git a/layout/painting/nsImageRenderer.cpp b/layout/painting/nsImageRenderer.cpp index 90045ed28f31..3813aa627a59 100644 --- a/layout/painting/nsImageRenderer.cpp +++ b/layout/painting/nsImageRenderer.cpp @@ -4,7 +4,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -/* utility code for drawing images as CSS borders, backgrounds, and shapes. */ +/* utility functions for drawing borders and backgrounds */ #include "nsImageRenderer.h" @@ -13,13 +13,11 @@ #include "gfxContext.h" #include "gfxDrawable.h" #include "ImageOps.h" -#include "ImageRegion.h" #include "mozilla/layers/StackingContextHelper.h" #include "mozilla/layers/WebRenderLayerManager.h" #include "nsContentUtils.h" #include "nsCSSRendering.h" #include "nsCSSRenderingGradients.h" -#include "nsDeviceContext.h" #include "nsIFrame.h" #include "nsStyleStructInlines.h" #include "nsSVGDisplayableFrame.h" @@ -948,72 +946,10 @@ nsImageRenderer::DrawBorderImageComponent(nsPresContext* aPresContext, nsRect destTile = RequiresScaling(fillRect, aHFill, aVFill, aUnitSize) ? ComputeTile(fillRect, aHFill, aVFill, aUnitSize, repeatSize) : fillRect; - return Draw(aPresContext, aRenderingContext, aDirtyRect, destTile, fillRect, destTile.TopLeft(), repeatSize, aSrc); } -ImgDrawResult -nsImageRenderer::DrawShapeImage(nsPresContext* aPresContext, - gfxContext& aRenderingContext) -{ - if (!IsReady()) { - NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me"); - return ImgDrawResult::NOT_READY; - } - - if (mSize.width <= 0 || mSize.height <= 0) { - return ImgDrawResult::SUCCESS; - } - - ImgDrawResult result = ImgDrawResult::SUCCESS; - - switch (mType) { - case eStyleImageType_Image: { - uint32_t drawFlags = ConvertImageRendererToDrawFlags(mFlags) | - imgIContainer::FRAME_FIRST; - nsRect dest(nsPoint(0, 0), mSize); - // We have a tricky situation in our choice of SamplingFilter. Shape images - // define a float area based on the alpha values in the rendered pixels. - // When multiple device pixels are used for one css pixel, the sampling - // can change crisp edges into aliased edges. For visual pixels, that's - // usually the right choice. For defining a float area, it can cause problems. - // If a style is using a shape-image-threshold value that is less than the - // alpha of the edge pixels, any filtering may smear the alpha into adjacent - // pixels and expand the float area in a confusing way. Since the alpha - // threshold can be set precisely in CSS, and since a web author may be - // counting on that threshold to define a precise float area from an image, - // it is least confusing to have the rendered pixels have unfiltered alpha. - // We use SamplingFilter::POINT to ensure that each rendered pixel has an - // alpha that precisely matches the alpha of the closest pixel in the image. - nsLayoutUtils::DrawSingleImage(aRenderingContext, aPresContext, - mImageContainer, SamplingFilter::POINT, - dest, dest, Nothing(), - drawFlags, - nullptr, nullptr); - break; - } - - case eStyleImageType_Gradient: { - nsCSSGradientRenderer renderer = - nsCSSGradientRenderer::Create(aPresContext, mGradientData, mSize); - nsRect dest(nsPoint(0, 0), mSize); - - renderer.Paint(aRenderingContext, dest, dest, mSize, - CSSIntRect::FromAppUnitsRounded(dest), - dest, 1.0); - break; - } - - default: - // Unsupported image type. - result = ImgDrawResult::BAD_IMAGE; - break; - } - - return result; -} - bool nsImageRenderer::IsRasterImage() { diff --git a/layout/painting/nsImageRenderer.h b/layout/painting/nsImageRenderer.h index e73ff231977e..91e9c93d5ead 100644 --- a/layout/painting/nsImageRenderer.h +++ b/layout/painting/nsImageRenderer.h @@ -254,14 +254,6 @@ public: const mozilla::Maybe& aSVGViewportSize, const bool aHasIntrinsicRatio); - /** - * Draw the image to aRenderingContext which can be used to define the - * float area in the presence of "shape-outside: ". - */ - ImgDrawResult - DrawShapeImage(nsPresContext* aPresContext, - gfxContext& aRenderingContext); - bool IsRasterImage(); bool IsAnimatedImage(); diff --git a/layout/style/ImageLoader.cpp b/layout/style/ImageLoader.cpp index 1693e8fd2cb2..b91f03893cf3 100644 --- a/layout/style/ImageLoader.cpp +++ b/layout/style/ImageLoader.cpp @@ -14,7 +14,6 @@ #include "nsLayoutUtils.h" #include "nsError.h" #include "nsDisplayList.h" -#include "nsIFrameInlines.h" #include "FrameLayerBuilder.h" #include "SVGObserverUtils.h" #include "imgIContainer.h" @@ -48,8 +47,7 @@ ImageLoader::DropDocumentReference() void ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest, - nsIFrame* aFrame, - FrameFlags aFlags) + nsIFrame* aFrame) { nsCOMPtr observer; aRequest->GetNotificationObserver(getter_AddRefs(observer)); @@ -78,63 +76,15 @@ ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest, return new RequestSet(); }); - // Add frame to the frameSet, and handle any special processing the - // frame might require. - FrameWithFlags fwf(aFrame); - FrameWithFlags* fwfToModify(&fwf); - - // See if the frameSet already has this frame. - uint32_t i = frameSet->IndexOfFirstElementGt(fwf, FrameOnlyComparator()); - if (i > 0 && aFrame == frameSet->ElementAt(i-1).mFrame) { - // We're already tracking this frame, so prepare to modify the - // existing FrameWithFlags object. - fwfToModify = &frameSet->ElementAt(i-1); + // Add these to the sets, but only if they're not already there. + uint32_t i = frameSet->IndexOfFirstElementGt(aFrame); + if (i == 0 || aFrame != frameSet->ElementAt(i-1)) { + frameSet->InsertElementAt(i, aFrame); } - - // Check if the frame requires special processing. - if (aFlags & REQUEST_REQUIRES_REFLOW) { - fwfToModify->mFlags |= REQUEST_REQUIRES_REFLOW; - - // If we weren't already blocking onload, do that now. - if ((fwfToModify->mFlags & REQUEST_HAS_BLOCKED_ONLOAD) == 0) { - fwfToModify->mFlags |= REQUEST_HAS_BLOCKED_ONLOAD; - - // Block document onload until we either remove the frame in - // RemoveRequestToFrameMapping or complete a reflow. - mDocument->BlockOnload(); - - // We need to stay blocked until we get a reflow. If the first frame - // is not yet decoded, we'll trigger that reflow from onFrameComplete. - // But if the first frame is already decoded, we need to trigger that - // reflow now, because we'll never get a call to onFrameComplete. - uint32_t status = 0; - if(NS_SUCCEEDED(aRequest->GetImageStatus(&status)) && - status & imgIRequest::STATUS_FRAME_COMPLETE) { - RequestReflowOnFrame(aFrame, aRequest); - } - } - } - - // Do some sanity checking to ensure that we only add to one mapping - // iff we also add to the other mapping. - DebugOnly didAddToFrameSet(false); - DebugOnly didAddToRequestSet(false); - - // If we weren't already tracking this frame, add it to the frameSet. - if (i == 0 || aFrame != frameSet->ElementAt(i-1).mFrame) { - frameSet->InsertElementAt(i, fwf); - didAddToFrameSet = true; - } - - // Add request to the request set if it wasn't already there. i = requestSet->IndexOfFirstElementGt(aRequest); if (i == 0 || aRequest != requestSet->ElementAt(i-1)) { requestSet->InsertElementAt(i, aRequest); - didAddToRequestSet = true; } - - MOZ_ASSERT(didAddToFrameSet == didAddToRequestSet, - "We should only add to one map iff we also add to the other map."); } void @@ -189,21 +139,7 @@ ImageLoader::RemoveRequestToFrameMapping(imgIRequest* aRequest, if (auto entry = mRequestToFrameMap.Lookup(aRequest)) { FrameSet* frameSet = entry.Data(); MOZ_ASSERT(frameSet, "This should never be null"); - - // Before we remove aFrame from the frameSet, unblock onload if needed. - uint32_t i = frameSet->IndexOfFirstElementGt(FrameWithFlags(aFrame), - FrameOnlyComparator()); - - if (i > 0 && aFrame == frameSet->ElementAt(i-1).mFrame) { - FrameWithFlags& fwf = frameSet->ElementAt(i-1); - if (fwf.mFlags & REQUEST_HAS_BLOCKED_ONLOAD) { - mDocument->UnblockOnload(false); - // We're about to remove fwf from the frameSet, so we don't bother - // updating the flag. - } - frameSet->RemoveElementAt(i-1); - } - + frameSet->RemoveElementSorted(aFrame); if (frameSet->IsEmpty()) { nsPresContext* presContext = GetPresContext(); if (presContext) { @@ -426,8 +362,7 @@ ImageLoader::DoRedraw(FrameSet* aFrameSet, bool aForcePaint) NS_ASSERTION(aFrameSet, "Must have a frame set"); NS_ASSERTION(mDocument, "Should have returned earlier!"); - for (FrameWithFlags& fwf : *aFrameSet) { - nsIFrame* frame = fwf.mFrame; + for (nsIFrame* frame : *aFrameSet) { if (frame->StyleVisibility()->IsVisible()) { if (frame->IsFrameOfType(nsIFrame::eTablePart)) { // Tables don't necessarily build border/background display items @@ -452,59 +387,6 @@ ImageLoader::DoRedraw(FrameSet* aFrameSet, bool aForcePaint) } } -void -ImageLoader::UnblockOnloadIfNeeded(nsIFrame* aFrame, imgIRequest* aRequest) -{ - MOZ_ASSERT(aFrame); - MOZ_ASSERT(aRequest); - - FrameSet* frameSet = mRequestToFrameMap.Get(aRequest); - if (!frameSet) { - return; - } - - size_t i = frameSet->BinaryIndexOf(FrameWithFlags(aFrame), - FrameOnlyComparator()); - if (i != FrameSet::NoIndex) { - FrameWithFlags& fwf = frameSet->ElementAt(i); - if (fwf.mFlags & REQUEST_HAS_BLOCKED_ONLOAD) { - mDocument->UnblockOnload(false); - fwf.mFlags &= ~REQUEST_HAS_BLOCKED_ONLOAD; - } - } -} - -void -ImageLoader::RequestReflowIfNeeded(FrameSet* aFrameSet, imgIRequest* aRequest) -{ - MOZ_ASSERT(aFrameSet); - - for (FrameWithFlags& fwf : *aFrameSet) { - nsIFrame* frame = fwf.mFrame; - if (fwf.mFlags & REQUEST_REQUIRES_REFLOW) { - // Tell the container of the float to reflow because the - // shape-outside: has finished decoding its first frame. - RequestReflowOnFrame(frame, aRequest); - } - } -} - -void -ImageLoader::RequestReflowOnFrame(nsIFrame* aFrame, imgIRequest* aRequest) -{ - // Actually request the reflow. - nsIFrame* floatContainer = aFrame->GetInFlowParent(); - floatContainer->PresShell()->FrameNeedsReflow( - floatContainer, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); - - // We'll respond to the reflow events by unblocking onload, regardless - // of whether the reflow was completed or cancelled. The callback will - // also delete itself when it is called. - ImageReflowCallback* unblocker = new ImageReflowCallback(this, aFrame, - aRequest); - floatContainer->PresShell()->PostReflowCallback(unblocker); -} - NS_IMPL_ADDREF(ImageLoader) NS_IMPL_RELEASE(ImageLoader) @@ -572,8 +454,7 @@ ImageLoader::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage) return NS_OK; } - for (FrameWithFlags& fwf : *frameSet) { - nsIFrame* frame = fwf.mFrame; + for (nsIFrame* frame : *frameSet) { if (frame->StyleVisibility()->IsVisible()) { frame->MarkNeedsDisplayItemRebuild(); } @@ -618,9 +499,6 @@ ImageLoader::OnFrameComplete(imgIRequest* aRequest) return NS_OK; } - // We may need reflow (for example if the image is from shape-outside). - RequestReflowIfNeeded(frameSet, aRequest); - // Since we just finished decoding a frame, we always want to paint, in case // we're now able to paint an image that we couldn't paint before (and hence // that we don't have retained data for). @@ -663,36 +541,5 @@ ImageLoader::FlushUseCounters() } } -bool -ImageLoader::ImageReflowCallback::ReflowFinished() -{ - // Check that the frame is still valid. If it isn't, then onload was - // unblocked when the frame was removed from the FrameSet in - // RemoveRequestToFrameMapping. - if (mFrame.IsAlive()) { - mLoader->UnblockOnloadIfNeeded(mFrame, mRequest); - } - - // Get rid of this callback object. - delete this; - - // We don't need to trigger layout. - return false; -} - -void -ImageLoader::ImageReflowCallback::ReflowCallbackCanceled() -{ - // Check that the frame is still valid. If it isn't, then onload was - // unblocked when the frame was removed from the FrameSet in - // RemoveRequestToFrameMapping. - if (mFrame.IsAlive()) { - mLoader->UnblockOnloadIfNeeded(mFrame, mRequest); - } - - // Get rid of this callback object. - delete this; -} - } // namespace css } // namespace mozilla diff --git a/layout/style/ImageLoader.h b/layout/style/ImageLoader.h index 0fa13da6536a..583001dbea1c 100644 --- a/layout/style/ImageLoader.h +++ b/layout/style/ImageLoader.h @@ -10,11 +10,9 @@ #ifndef mozilla_css_ImageLoader_h___ #define mozilla_css_ImageLoader_h___ -#include "mozilla/CORSMode.h" +#include "CORSMode.h" #include "nsClassHashtable.h" #include "nsHashKeys.h" -#include "nsIFrame.h" -#include "nsIReflowCallback.h" #include "nsTArray.h" #include "imgIRequest.h" #include "imgINotificationObserver.h" @@ -35,14 +33,6 @@ struct ImageValue; class ImageLoader final : public imgINotificationObserver { public: - // We also associate flags alongside frames in the request-to-frames hashmap. - // These are used for special handling of events for requests. - typedef uint32_t FrameFlags; - enum { - REQUEST_REQUIRES_REFLOW = 1u << 0, - REQUEST_HAS_BLOCKED_ONLOAD = 1u << 1 - }; - typedef mozilla::css::ImageValue Image; explicit ImageLoader(nsIDocument* aDocument) @@ -61,8 +51,7 @@ public: void DeregisterCSSImage(Image* aImage); void AssociateRequestToFrame(imgIRequest* aRequest, - nsIFrame* aFrame, - FrameFlags aFlags); + nsIFrame* aFrame); void DisassociateRequestFromFrame(imgIRequest* aRequest, nsIFrame* aFrame); @@ -84,26 +73,6 @@ public: void FlushUseCounters(); private: - // This callback is used to unblock document onload after a reflow - // triggered from an image load. - struct ImageReflowCallback final : public nsIReflowCallback - { - RefPtr mLoader; - WeakFrame mFrame; - nsCOMPtr const mRequest; - - ImageReflowCallback(ImageLoader* aLoader, - nsIFrame* aFrame, - imgIRequest* aRequest) - : mLoader(aLoader) - , mFrame(aFrame) - , mRequest(aRequest) - {} - - bool ReflowFinished() override; - void ReflowCallbackCanceled() override; - }; - ~ImageLoader() {} // We need to be able to look up the frames associated with a request (for @@ -111,31 +80,7 @@ private: // the frame goes away). Thus we maintain hashtables going both ways. These // should always be in sync. - struct FrameWithFlags { - explicit FrameWithFlags(nsIFrame* aFrame) - : mFrame(aFrame), - mFlags(0) - { - MOZ_ASSERT(mFrame); - } - nsIFrame* const mFrame; - FrameFlags mFlags; - }; - - // A helper class to compare FrameWithFlags by comparing mFrame and - // ignoring mFlags. - class FrameOnlyComparator { - public: - bool Equals(const FrameWithFlags& aElem1, - const FrameWithFlags& aElem2) const - { return aElem1.mFrame == aElem2.mFrame; } - - bool LessThan(const FrameWithFlags& aElem1, - const FrameWithFlags& aElem2) const - { return aElem1.mFrame < aElem2.mFrame; } - }; - - typedef nsTArray FrameSet; + typedef nsTArray FrameSet; typedef nsTArray > RequestSet; typedef nsTHashtable > ImageHashSet; typedef nsClassHashtable aShapeImage) mType = StyleShapeSourceType::Image; } -imgIRequest* -StyleShapeSource::GetShapeImageData() const -{ - if (mType != StyleShapeSourceType::Image) { - return nullptr; - } - if (mShapeImage->GetType() != eStyleImageType_Image) { - return nullptr; - } - return mShapeImage->GetImageData(); -} - void StyleShapeSource::SetBasicShape(UniquePtr aBasicShape, StyleGeometryBox aReferenceBox) diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index e6fbe701736c..736bcbf0dbe4 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -2145,11 +2145,6 @@ struct StyleShapeSource final return mShapeImage; } - // Iff we have "shape-outside:" with an image URI (not a gradient), - // this method returns the corresponding imgIRequest*. Else, returns - // null. - imgIRequest* GetShapeImageData() const; - void SetShapeImage(UniquePtr aShapeImage); const UniquePtr& GetBasicShape() const diff --git a/layout/style/test/file_shape_outside_CORS.html b/layout/style/test/file_shape_outside_CORS.html index ec061115802e..de558c47f8a0 100644 --- a/layout/style/test/file_shape_outside_CORS.html +++ b/layout/style/test/file_shape_outside_CORS.html @@ -33,6 +33,7 @@ function sendResults() { window.parent.postMessage({ "result": (divAllowSib.getBoundingClientRect().left == divAllow.getBoundingClientRect().left), "message": "Test 1: Sibling is at same left offset as div (shape-outside was allowed).", + "todo": true, }, DOMAIN); diff --git a/layout/svg/SVGObserverUtils.cpp b/layout/svg/SVGObserverUtils.cpp index 121602578e74..73fe058a67b1 100644 --- a/layout/svg/SVGObserverUtils.cpp +++ b/layout/svg/SVGObserverUtils.cpp @@ -423,7 +423,7 @@ nsSVGMaskProperty::ResolveImage(uint32_t aIndex) mozilla::css::ImageLoader* imageLoader = mFrame->PresContext()->Document()->StyleImageLoader(); if (imgRequestProxy* req = image.GetImageData()) { - imageLoader->AssociateRequestToFrame(req, mFrame, 0); + imageLoader->AssociateRequestToFrame(req, mFrame); } } } diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index 47967463bba1..0194714840e5 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -125727,54 +125727,6 @@ {} ] ], - "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-005.html": [ - [ - "/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-005.html", - [ - [ - "/css/css-shapes/shape-outside/shape-image/gradients/reference/shape-outside-linear-gradient-001-ref.html", - "==" - ] - ], - {} - ] - ], - "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-006.html": [ - [ - "/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-006.html", - [ - [ - "/css/css-shapes/shape-outside/shape-image/gradients/reference/shape-outside-linear-gradient-001-ref.html", - "==" - ] - ], - {} - ] - ], - "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-007.html": [ - [ - "/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-007.html", - [ - [ - "/css/css-shapes/shape-outside/shape-image/gradients/reference/shape-outside-linear-gradient-001-ref.html", - "==" - ] - ], - {} - ] - ], - "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-008.html": [ - [ - "/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-008.html", - [ - [ - "/css/css-shapes/shape-outside/shape-image/gradients/reference/shape-outside-linear-gradient-001-ref.html", - "==" - ] - ], - {} - ] - ], "css/css-shapes/shape-outside/shape-image/shape-image-000.html": [ [ "/css/css-shapes/shape-outside/shape-image/shape-image-000.html", @@ -506910,22 +506862,6 @@ "1e5377c120916557dc1525b38c9cf7eb86ae0151", "reftest" ], - "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-005.html": [ - "dfbf4ad61f18ad05e243e58a2458b5b776ecf3fb", - "reftest" - ], - "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-006.html": [ - "da35426f9ffc53101a053a61512c6928d140adff", - "reftest" - ], - "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-007.html": [ - "03752707f61fbf16290b9733c280b79e00440b96", - "reftest" - ], - "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-008.html": [ - "2ce6445aed0987c0ab7b83f614e9ceb8186fff7b", - "reftest" - ], "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-radial-gradient-001.html": [ "b13df2df3be12ac74a7933794d91558c416b412c", "testharness" @@ -506939,7 +506875,7 @@ "testharness" ], "css/css-shapes/shape-outside/shape-image/gradients/shape-outside-radial-gradient-004.html": [ - "fc47f277737dce4f7c6b08d0181355a810107c7b", + "67c3b8ea983da6b7d9b6b7e252b6415ebc7d1f9d", "testharness" ], "css/css-shapes/shape-outside/shape-image/reference/shape-image-000-ref.html": [ @@ -507011,11 +506947,11 @@ "reftest" ], "css/css-shapes/shape-outside/shape-image/shape-image-001.html": [ - "f69cd2483ed81b496fb5d1c0024b6a16df7375b1", + "d97482f9149c68fff971e383ffb72f1371e898ef", "reftest" ], "css/css-shapes/shape-outside/shape-image/shape-image-002.html": [ - "299635765004bd1cf9cff8a0888678a2331d7b19", + "1f3ace2827629f37439108e43f6528d8d728e5a2", "reftest" ], "css/css-shapes/shape-outside/shape-image/shape-image-003.html": [ @@ -507027,7 +506963,7 @@ "reftest" ], "css/css-shapes/shape-outside/shape-image/shape-image-005.html": [ - "38883b770a73cc4eac3170a356b272edb6a5c483", + "274932a778268e3fda90b289e7f5ca22ee0f9adc", "reftest" ], "css/css-shapes/shape-outside/shape-image/shape-image-006.html": [ diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-001.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-001.html.ini new file mode 100644 index 000000000000..89b86425076c --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-001.html.ini @@ -0,0 +1,2 @@ +[shape-outside-linear-gradient-001.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-002.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-002.html.ini new file mode 100644 index 000000000000..6d559345a736 --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-002.html.ini @@ -0,0 +1,2 @@ +[shape-outside-linear-gradient-002.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-003.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-003.html.ini new file mode 100644 index 000000000000..b6bee5d07cbc --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-003.html.ini @@ -0,0 +1,2 @@ +[shape-outside-linear-gradient-003.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-004.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-004.html.ini new file mode 100644 index 000000000000..3bc010629c8b --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-004.html.ini @@ -0,0 +1,2 @@ +[shape-outside-linear-gradient-004.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-radial-gradient-001.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-radial-gradient-001.html.ini new file mode 100644 index 000000000000..690b6ba5f5d8 --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-radial-gradient-001.html.ini @@ -0,0 +1,4 @@ +[shape-outside-radial-gradient-001.html] + [CSS Test: Left float with radial gradient] + expected: FAIL + diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-000.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-000.html.ini new file mode 100644 index 000000000000..f73a31be3c6c --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-000.html.ini @@ -0,0 +1,2 @@ +[shape-image-000.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-001.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-001.html.ini new file mode 100644 index 000000000000..df0a0aada282 --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-001.html.ini @@ -0,0 +1,2 @@ +[shape-image-001.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-002.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-002.html.ini new file mode 100644 index 000000000000..01d250923337 --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-002.html.ini @@ -0,0 +1,2 @@ +[shape-image-002.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-003.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-003.html.ini new file mode 100644 index 000000000000..0a4d46925531 --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-003.html.ini @@ -0,0 +1,2 @@ +[shape-image-003.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-004.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-004.html.ini new file mode 100644 index 000000000000..461a202b5972 --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-004.html.ini @@ -0,0 +1,2 @@ +[shape-image-004.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-005.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-005.html.ini new file mode 100644 index 000000000000..72c200237f49 --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-005.html.ini @@ -0,0 +1,2 @@ +[shape-image-005.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-012.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-012.html.ini new file mode 100644 index 000000000000..e5e0d1099cff --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-012.html.ini @@ -0,0 +1,2 @@ +[shape-image-012.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-013.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-013.html.ini new file mode 100644 index 000000000000..c73bf089c413 --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-013.html.ini @@ -0,0 +1,2 @@ +[shape-image-013.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-014.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-014.html.ini new file mode 100644 index 000000000000..a95f6092dd03 --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-014.html.ini @@ -0,0 +1,2 @@ +[shape-image-014.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-015.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-015.html.ini new file mode 100644 index 000000000000..e04bb5be7f4a --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-015.html.ini @@ -0,0 +1,2 @@ +[shape-image-015.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-016.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-016.html.ini new file mode 100644 index 000000000000..db84eb8e3adc --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-016.html.ini @@ -0,0 +1,2 @@ +[shape-image-016.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-017.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-017.html.ini new file mode 100644 index 000000000000..921acf8b0d3a --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-017.html.ini @@ -0,0 +1,2 @@ +[shape-image-017.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-025.html.ini b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-025.html.ini new file mode 100644 index 000000000000..1cea8dab999b --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-025.html.ini @@ -0,0 +1,2 @@ +[shape-image-025.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-010.html.ini b/testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-010.html.ini new file mode 100644 index 000000000000..cafe9b6219c7 --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-010.html.ini @@ -0,0 +1,4 @@ +[shape-outside-010.html] + [CSS Test: Shape from image - url(), alpha channel, opacity 0] + expected: FAIL + diff --git a/testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-011.html.ini b/testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-011.html.ini new file mode 100644 index 000000000000..78c48b2decb0 --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-011.html.ini @@ -0,0 +1,4 @@ +[shape-outside-011.html] + [CSS Test: Shape from image - url(), alpha channel, opacity > 0] + expected: FAIL + diff --git a/testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-012.html.ini b/testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-012.html.ini new file mode 100644 index 000000000000..aea7237462ab --- /dev/null +++ b/testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-012.html.ini @@ -0,0 +1,4 @@ +[shape-outside-012.html] + [CSS Test: Shape from image - shape-image-threshold - 0.9] + expected: FAIL + diff --git a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-005.html b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-005.html deleted file mode 100644 index a6228790df7e..000000000000 --- a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-005.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - CSS Test: Test float with linear gradient under writing-mode: vertical-rl - - - - - - - - - - -

- The test passes if you see a green square. There should be no red. -

-
-
- x x x x -
-
-
- x x x x -
- - diff --git a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-006.html b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-006.html deleted file mode 100644 index 18253edc778d..000000000000 --- a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-006.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - CSS Test: Test float with linear gradient under writing-mode: vertical-lr - - - - - - - - - - -

- The test passes if you see a green square. There should be no red. -

-
-
- x x x x -
-
-
- x x x x -
- - diff --git a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-007.html b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-007.html deleted file mode 100644 index 9087fb0f093e..000000000000 --- a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-007.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - CSS Test: Test float with linear gradient under writing-mode: sideways-rl - - - - - - - - - - -

- The test passes if you see a green square. There should be no red. -

-
-
- x x x x -
-
-
- x x x x -
- - diff --git a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-008.html b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-008.html deleted file mode 100644 index 05132b45c29b..000000000000 --- a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-008.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - CSS Test: Test float with linear gradient under writing-mode: sideways-lr - - - - - - - - - - -

- The test passes if you see a green square. There should be no red. -

-
-
- x x x x -
-
-
- x x x x -
- - diff --git a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-001.html b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-001.html index ac2d92fba434..8273030bc0af 100644 --- a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-001.html +++ b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-001.html @@ -22,7 +22,7 @@ } #image { float: left; - width: 100px; + width: 150px; height: 100px; shape-outside: url("support/left-half-rectangle-70.png"); } diff --git a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-002.html b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-002.html index 01d916d7cb85..ee2dd75db6a4 100644 --- a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-002.html +++ b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-002.html @@ -22,7 +22,7 @@ } #image { float: left; - shape-outside: url('data:image/svg+xml;utf8,'); + shape-outside: url('data:image/svg+xml;utf8,'); } @@ -31,7 +31,7 @@ The test passes if you see a solid green square. There should be no red.

- + X X
diff --git a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-005.html b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-005.html index 93f39787ad1a..2c642c02d0b4 100644 --- a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-005.html +++ b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/shape-image-005.html @@ -22,7 +22,7 @@ } #image { float: left; - shape-outside: url('data:image/svg+xml;utf8,'); + shape-outside: url('data:image/svg+xml;utf8,'); shape-image-threshold: 0.8; } @@ -32,7 +32,7 @@ The test passes if you see a solid green square. There should be no red.

- + X X
diff --git a/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/support/animated.gif b/testing/web-platform/tests/css/css-shapes/shape-outside/shape-image/support/animated.gif index 9e424df7cdfb6b228b18297334fba7cabdb270cd..083c9a0a23df0815fe593540ea487ef36119ef4f 100644 GIT binary patch delta 1275 zcmaFNHiNI;-P6s&GBJfAg<&ES!~g&PfeaAIz@Ye_JHj_W!6mUI5lGwER}>^BXQ!4Z zB&DWj=GiK}-@RW+Av48RDcsc8z_-9TH6zobswg$M$}c3jDm&RSMakYy!KT8hBDWwn zwIorYA~z?m*s8)-DKRBKDb)(d1_|reD=C20lw{i~If7&qK$be^7o{qg>KW@M8yFfY zn3?GrnwXlI8R{q)85kPr8vv1+uAzyQshO3LxdId@0j(=aNwW%aaf4b01^l#~=$ z>Fbx5m+O@q>*W`v>l<2HT7t|lGSV$dsZY}_uFNY*tkBIXR)!hjl3JWxlvz-cnV+Ws zGBPnKzqG_w3FuUaCvpqm=H?aafjy*`oS&;-kyxN_sAr&$q|nzFztZ~LT)6V$lAu(C zi?Nzq98y`3svneEoL^d$oC;K~46>`h%DE^tu_V7JBtJg~7LEZK`6c7$ckpLiMKLc8oDjz4}1c=oi%kqZcW6ZM9qnzcEzM1h4fS=kHPuy>73F26 zCB;RB1^IcoIoVm68R==MDalER3Gs2UG0{A;Cd`0selzKHgrQ9`0_gF3wJl z4)%7Dwl>yQmKNq_rY6Qlh6eh2x;olgni}eAsw&D#iVE^_vNF8 zH7z|OGb=l1uH1R@=F6X)Qd(ACQCU@8vsUdob?enHE@^FR@96C6?%At%pT7P2H@A$A mjZaKYP0yS)d)7pP-NY(_>ZUtWfNpxrzj$&2qwnOKOkDsYSkp`Z delta 302 zcmbQi_n6Jt-P6s&GBJfAg<&BB!~g&PfeauC1d2adI0YFP7<7ObD44>)!1S-Df92`9 z43h&{ni;t!Uu7v_lIWc5&8p4#Z?Y9zNIm11U4Q$|zy4mn^FRNIW9A+a8R8u?EYeTh zs93=v`NHC)MCMEPnU)nNe=hqu^Sb6Pwx}p`tE{XptyNK{ZoTBmeLMax-+NZ~`Cd*r^>glVdF$HwmhU_F z?wK4{)P9Zs9{ySN{qhU;_rHHHVsFa$l1C-tL&E+Rc5bGC7XnI$8wI8Po`g?)x{H4vn0Q5Ga2m>>+x9`lEY|VBT0GmR7*Z=?k From d9d59209a6b7c6d1262dba302ac4ae613724a995 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Mon, 2 Apr 2018 17:59:30 -0400 Subject: [PATCH 18/25] Backed out changeset d85b5825a721 (bug 1440753) for frequent OSX iframe-scrolling-attr-2.html failures on a CLOSED TREE. --- gfx/layers/client/ContentClient.cpp | 2 - gfx/src/nsRect.h | 4 - gfx/src/nsRegion.cpp | 1038 ++++++------ gfx/src/nsRegion.h | 2092 ++----------------------- gfx/tests/gtest/TestRegion.cpp | 720 --------- layout/painting/FrameLayerBuilder.cpp | 2 +- layout/svg/nsFilterInstance.cpp | 3 +- 7 files changed, 745 insertions(+), 3116 deletions(-) diff --git a/gfx/layers/client/ContentClient.cpp b/gfx/layers/client/ContentClient.cpp index fbfbdcdd068e..f02f0e210451 100644 --- a/gfx/layers/client/ContentClient.cpp +++ b/gfx/layers/client/ContentClient.cpp @@ -231,8 +231,6 @@ ContentClient::BeginPaint(PaintedLayer* aLayer, } } - MOZ_ASSERT(dest.mBufferRect.Contains(result.mRegionToDraw.GetBounds())); - NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || dest.mBufferRect == dest.mNeededRegion.GetBounds(), "If we're resampling, we need to validate the entire buffer"); diff --git a/gfx/src/nsRect.h b/gfx/src/nsRect.h index ccec32aed7ef..5cba0753cd3f 100644 --- a/gfx/src/nsRect.h +++ b/gfx/src/nsRect.h @@ -112,10 +112,6 @@ struct nsRect : { return SaturatingUnion(aRect); } - MOZ_MUST_USE nsRect UnsafeUnion(const nsRect& aRect) const - { - return Super::Union(aRect); - } void UnionRect(const nsRect& aRect1, const nsRect& aRect2) { *this = aRect1.Union(aRect2); diff --git a/gfx/src/nsRegion.cpp b/gfx/src/nsRegion.cpp index 686427c4aaae..35d4ef0b4695 100644 --- a/gfx/src/nsRegion.cpp +++ b/gfx/src/nsRegion.cpp @@ -10,73 +10,6 @@ #include "gfxUtils.h" #include "mozilla/ToString.h" -using namespace std; - -void -nsRegion::AssertStateInternal() const -{ - bool failed = false; - // Verify consistent state inside the region. - int32_t lastY = INT32_MIN; - int32_t lowestX = INT32_MAX; - int32_t highestX = INT32_MIN; - for (auto iter = mBands.begin(); iter != mBands.end(); iter++) { - const Band& band = *iter; - if (band.bottom <= band.top) { - failed = true; - break; - } - if (band.top < lastY) { - failed = true; - break; - } - lastY = band.bottom; - - lowestX = std::min(lowestX, band.mStrips.begin()->left); - highestX = std::max(highestX, band.mStrips.LastElement().right); - - int32_t lastX = INT32_MIN; - if (iter != mBands.begin()) { - auto prev = iter; - prev--; - - if (prev->bottom == iter->top) { - if (band.EqualStrips(*prev)) { - failed = true; - break; - } - } - } - for (const Strip& strip : band.mStrips) { - if (strip.right <= strip.left) { - failed = true; - break; - } - if (strip.left <= lastX) { - failed = true; - break; - } - lastX = strip.right; - } - if (failed) { - break; - } - } - - if (!(mBounds == CalculateBounds())) { - failed = true; - } - - if (failed) { -#ifdef DEBUG_REGIONS - if (mCurrentOpGenerator) { - mCurrentOpGenerator->OutputOp(); - } -#endif - MOZ_ASSERT(false); - } -} - bool nsRegion::Contains(const nsRegion& aRgn) const { // XXX this could be made faster by iterating over @@ -91,92 +24,76 @@ bool nsRegion::Contains(const nsRegion& aRgn) const bool nsRegion::Intersects(const nsRect& aRect) const { - if (mBands.IsEmpty()) { - return mBounds.Intersects(aRect); - } - - if (!mBounds.Intersects(aRect)) { - return false; - } - - Strip rectStrip(aRect.X(), aRect.XMost()); - - auto iter = mBands.begin(); - while (iter != mBands.end()) { - if (iter->top >= aRect.YMost()) { - return false; + // XXX this could be made faster by using pixman_region32_contains_rect + for (auto iter = RectIter(); !iter.Done(); iter.Next()) { + if (iter.Get().Intersects(aRect)) { + return true; } - - if (iter->bottom <= aRect.Y()) { - // This band is entirely before aRect, move on. - iter++; - continue; - } - - if (!iter->Intersects(rectStrip)) { - // This band does not intersect aRect horizontally. Move on. - iter++; - continue; - } - - // This band intersects with aRect. - return true; } - return false; } void nsRegion::Inflate(const nsMargin& aMargin) { - nsRegion newRegion; - for (RectIterator iter = RectIterator(*this); !iter.Done(); iter.Next()) { - nsRect rect = iter.Get(); + int n; + pixman_box32_t *boxes = pixman_region32_rectangles(&mImpl, &n); + for (int i=0; i= 1, "Invalid max rect count"); - if (GetNumRects() <= aMaxRects) { + if (GetNumRects() <= aMaxRects) return; - } + + pixman_box32_t *boxes; + int n; + boxes = pixman_region32_rectangles(&mImpl, &n); // Try combining rects in horizontal bands into a single rect - // The goal here is to try to keep groups of rectangles that are vertically - // discontiguous as separate rectangles in the final region. This is - // simple and fast to implement and page contents tend to vary more - // vertically than horizontally (which is why our rectangles are stored - // sorted by y-coordinate, too). - // - // Note: if boxes share y1 because of the canonical representation they - // will share y2 - - size_t idx = 0; - - while (idx < mBands.Length()) { - size_t oldIdx = idx; - mBands[idx].mStrips.begin()->right = mBands[idx].mStrips.LastElement().right; - mBands[idx].mStrips.TruncateLength(1); - idx++; - - // Merge any bands with the same bounds. - while (idx < mBands.Length() && - mBands[idx].mStrips.begin()->left == mBands[oldIdx].mStrips.begin()->left && - mBands[idx].mStrips.LastElement().right == mBands[oldIdx].mStrips.begin()->right) { - mBands[oldIdx].bottom = mBands[idx].bottom; - mBands.RemoveElementAt(idx); + int dest = 0; + for (int src = 1; src < n; src++) + { + // The goal here is to try to keep groups of rectangles that are vertically + // discontiguous as separate rectangles in the final region. This is + // simple and fast to implement and page contents tend to vary more + // vertically than horizontally (which is why our rectangles are stored + // sorted by y-coordinate, too). + // + // Note: if boxes share y1 because of the canonical representation they + // will share y2 + while ((src < (n)) && boxes[dest].y1 == boxes[src].y1) { + // merge box[i] and box[i+1] + boxes[dest].x2 = boxes[src].x2; + src++; + } + if (src < n) { + dest++; + boxes[dest] = boxes[src]; } } - AssertState(); - - // mBands.size() is now equal to our rect count. - if (mBands.Length() > aMaxRects) { + uint32_t reducedCount = dest+1; + // pixman has a special representation for + // regions of 1 rectangle. So just use the + // bounds in that case + if (reducedCount > 1 && reducedCount <= aMaxRects) { + // reach into pixman and lower the number + // of rects stored in data. + mImpl.data->numRects = reducedCount; + } else { *this = GetBounds(); } } @@ -185,271 +102,443 @@ void nsRegion::SimplifyOutward (uint32_t aMaxRects) // by iterating over both rows simultaneously and adding up // the additional increase in area caused by extending each // of the rectangles to the combined height of both rows -uint32_t -nsRegion::ComputeMergedAreaIncrease(const Band& aTopBand, - const Band& aBottomBand) +static uint32_t ComputeMergedAreaIncrease(pixman_box32_t *topRects, + pixman_box32_t *topRectsEnd, + pixman_box32_t *bottomRects, + pixman_box32_t *bottomRectsEnd) { uint32_t totalArea = 0; + struct pt { + int32_t x, y; + }; - uint32_t topHeight = aBottomBand.top - aTopBand.top; - uint32_t bottomHeight = aBottomBand.bottom - aTopBand.bottom; - uint32_t currentStripBottom = 0; - // This could be done with slightly better worse case performance by merging these two - // for-loops, but this makes the code a lot easier to understand. - for (auto& strip : aTopBand.mStrips) { - if (currentStripBottom == aBottomBand.mStrips.Length() || strip.right < aBottomBand.mStrips[currentStripBottom].left) { - totalArea += bottomHeight * strip.Size(); - continue; - } + pt *i = (pt*)topRects; + pt *end_i = (pt*)topRectsEnd; + pt *j = (pt*)bottomRects; + pt *end_j = (pt*)bottomRectsEnd; + bool top = false; + bool bottom = false; - int32_t currentX = strip.left; - while (currentStripBottom != aBottomBand.mStrips.Length() && aBottomBand.mStrips[currentStripBottom].left < strip.right) { - if (currentX >= strip.right) { - break; - } - if (currentX < aBottomBand.mStrips[currentStripBottom].left) { - // Add the part that's not intersecting. - totalArea += (aBottomBand.mStrips[currentStripBottom].left - currentX) * bottomHeight; - } - - currentX = std::max(aBottomBand.mStrips[currentStripBottom].right, currentX); - currentStripBottom++; - } - - // Add remainder of this strip. - if (currentX < strip.right) { - totalArea += (strip.right - currentX) * bottomHeight; - } - if (currentStripBottom) { - currentStripBottom--; - } + int cur_x = i->x; + bool top_next = top; + bool bottom_next = bottom; + //XXX: we could probably simplify this condition and perhaps move it into the loop below + if (j->x < cur_x) { + cur_x = j->x; + j++; + bottom_next = !bottom; + } else if (j->x == cur_x) { + i++; + top_next = !top; + bottom_next = !bottom; + j++; + } else { + top_next = !top; + i++; } - uint32_t currentStripTop = 0; - for (auto& strip : aBottomBand.mStrips) { - if (currentStripTop == aTopBand.mStrips.Length() || strip.right < aTopBand.mStrips[currentStripTop].left) { - totalArea += topHeight * strip.Size(); - continue; - } - int32_t currentX = strip.left; - while (currentStripTop != aTopBand.mStrips.Length() && aTopBand.mStrips[currentStripTop].left < strip.right) { - if (currentX >= strip.right) { - break; - } - if (currentX < aTopBand.mStrips[currentStripTop].left) { - // Add the part that's not intersecting. - totalArea += (aTopBand.mStrips[currentStripTop].left - currentX) * topHeight; - } + int topRectsHeight = topRects->y2 - topRects->y1; + int bottomRectsHeight = bottomRects->y2 - bottomRects->y1; + int inbetweenHeight = bottomRects->y1 - topRects->y2; + int width = cur_x; + // top and bottom are the in-status to the left of cur_x + do { + if (top && !bottom) { + totalArea += (inbetweenHeight+bottomRectsHeight)*width; + } else if (bottom && !top) { + totalArea += (inbetweenHeight+topRectsHeight)*width; + } else if (bottom && top) { + totalArea += (inbetweenHeight)*width; + } + top = top_next; + bottom = bottom_next; + // find the next edge + if (i->x < j->x) { + top_next = !top; + width = i->x - cur_x; + cur_x = i->x; + i++; + } else if (j->x < i->x) { + bottom_next = !bottom; + width = j->x - cur_x; + cur_x = j->x; + j++; + } else { // i->x == j->x + top_next = !top; + bottom_next = !bottom; + width = i->x - cur_x; + cur_x = i->x; + i++; + j++; + } + } while (i < end_i && j < end_j); - currentX = std::max(aTopBand.mStrips[currentStripTop].right, currentX); - currentStripTop++; - } + // handle any remaining rects + while (i < end_i) { + width = i->x - cur_x; + cur_x = i->x; + i++; + if (top) + totalArea += (inbetweenHeight+bottomRectsHeight)*width; + top = !top; + } - // Add remainder of this strip. - if (currentX < strip.right) { - totalArea += (strip.right - currentX) * topHeight; - } - if (currentStripTop) { - currentStripTop--; - } + while (j < end_j) { + width = j->x - cur_x; + cur_x = j->x; + j++; + if (bottom) + totalArea += (inbetweenHeight+topRectsHeight)*width; + bottom = !bottom; } return totalArea; } +static pixman_box32_t * +CopyRow(pixman_box32_t *dest_it, pixman_box32_t *src_start, pixman_box32_t *src_end) +{ + // XXX: std::copy + pixman_box32_t *src_it = src_start; + while (src_it < src_end) { + *dest_it++ = *src_it++; + } + return dest_it; +} + + +#define WRITE_RECT(x1, x2, y1, y2) \ + do { \ + tmpRect->x1 = x1; \ + tmpRect->x2 = x2; \ + tmpRect->y1 = y1; \ + tmpRect->y2 = y2; \ + tmpRect++; \ + } while (0) + +/* If 'r' overlaps the current rect, then expand the current rect to include + * it. Otherwise write the current rect out to tmpRect, and set r as the + * updated current rect. */ +#define MERGE_RECT(r) \ + do { \ + if (r->x1 <= x2) { \ + if (x2 < r->x2) \ + x2 = r->x2; \ + } else { \ + WRITE_RECT(x1, x2, y1, y2); \ + x1 = r->x1; \ + x2 = r->x2; \ + } \ + r++; \ + } while (0) + + +/* Can we merge two sets of rects without extra space? + * Yes, but not easily. We can even do it stably + * but we don't need that property. + * + * This is written in the style of pixman_region_union_o */ +static pixman_box32_t * +MergeRects(pixman_box32_t *r1, + pixman_box32_t *r1_end, + pixman_box32_t *r2, + pixman_box32_t *r2_end, + pixman_box32_t *tmpRect) +{ + /* This routine works by maintaining the current + * rectangle in x1,x2,y1,y2 and either merging + * in the left most rectangle if it overlaps or + * outputing the current rectangle and setting + * it to the the left most one */ + const int y1 = r1->y1; + const int y2 = r2->y2; + int x1; + int x2; + + /* Find the left-most edge */ + if (r1->x1 < r2->x1) { + x1 = r1->x1; + x2 = r1->x2; + r1++; + } else { + x1 = r2->x1; + x2 = r2->x2; + r2++; + } + + while (r1 != r1_end && r2 != r2_end) { + /* Find and merge the left-most rectangle */ + if (r1->x1 < r2->x1) + MERGE_RECT (r1); + else + MERGE_RECT (r2); + } + + /* Finish up any left overs */ + if (r1 != r1_end) { + do { + MERGE_RECT (r1); + } while (r1 != r1_end); + } else if (r2 != r2_end) { + do { + MERGE_RECT(r2); + } while (r2 != r2_end); + } + + /* Finish up the last rectangle */ + WRITE_RECT(x1, x2, y1, y2); + + return tmpRect; +} + void nsRegion::SimplifyOutwardByArea(uint32_t aThreshold) { - if (mBands.Length() < 2) { - // We have only one or no row and we're done. + + pixman_box32_t *boxes; + int n; + boxes = pixman_region32_rectangles(&mImpl, &n); + + // if we have no rectangles then we're done + if (!n) return; + + pixman_box32_t *end = boxes + n; + pixman_box32_t *topRectsEnd = boxes+1; + pixman_box32_t *topRects = boxes; + + // we need some temporary storage for merging both rows of rectangles + AutoTArray tmpStorage; + tmpStorage.SetCapacity(n); + pixman_box32_t *tmpRect = tmpStorage.Elements(); + + pixman_box32_t *destRect = boxes; + pixman_box32_t *rect = tmpRect; + // find the end of the first span of rectangles + while (topRectsEnd < end && topRectsEnd->y1 == topRects->y1) { + topRectsEnd++; } - uint32_t currentBand = 0; - do { - Band& band = mBands[currentBand]; + // if we only have one row we are done + if (topRectsEnd == end) + return; - uint32_t totalArea = ComputeMergedAreaIncrease(band, mBands[currentBand + 1]); + pixman_box32_t *bottomRects = topRectsEnd; + pixman_box32_t *bottomRectsEnd = bottomRects+1; + do { + // find the end of the bottom span of rectangles + while (bottomRectsEnd < end && bottomRectsEnd->y1 == bottomRects->y1) { + bottomRectsEnd++; + } + uint32_t totalArea = ComputeMergedAreaIncrease(topRects, topRectsEnd, + bottomRects, bottomRectsEnd); if (totalArea <= aThreshold) { - for (Strip& strip : mBands[currentBand + 1].mStrips) { - // This could use an optimized function to merge two bands. - band.InsertStrip(strip); - } - band.bottom = mBands[currentBand + 1].bottom; - mBands.RemoveElementAt(currentBand + 1); - } else { - currentBand++; - } - } while (currentBand + 1 < mBands.Length()); + // merge the rects into tmpRect + rect = MergeRects(topRects, topRectsEnd, bottomRects, bottomRectsEnd, tmpRect); - EnsureSimplified(); - AssertState(); + // set topRects to where the newly merged rects will be so that we use them + // as our next set of topRects + topRects = destRect; + // copy the merged rects back into the destination + topRectsEnd = CopyRow(destRect, tmpRect, rect); + } else { + // copy the unmerged rects + destRect = CopyRow(destRect, topRects, topRectsEnd); + + topRects = bottomRects; + topRectsEnd = bottomRectsEnd; + if (bottomRectsEnd == end) { + // copy the last row when we are done + topRectsEnd = CopyRow(destRect, topRects, topRectsEnd); + } + } + bottomRects = bottomRectsEnd; + } while (bottomRectsEnd != end); + + + uint32_t reducedCount = topRectsEnd - pixman_region32_rectangles(&this->mImpl, &n); + // pixman has a special representation for + // regions of 1 rectangle. So just use the + // bounds in that case + if (reducedCount > 1) { + // reach into pixman and lower the number + // of rects stored in data. + this->mImpl.data->numRects = reducedCount; + } else { + *this = GetBounds(); + } } typedef void (*visit_fn)(void *closure, VisitSide side, int x1, int y1, int x2, int y2); +static bool VisitNextEdgeBetweenRect(visit_fn visit, void *closure, VisitSide side, + pixman_box32_t *&r1, pixman_box32_t *&r2, const int y, int &x1) +{ + // check for overlap + if (r1->x2 >= r2->x1) { + MOZ_ASSERT(r2->x1 >= x1); + visit(closure, side, x1, y, r2->x1, y); + + // find the rect that ends first or always drop the one that comes first? + if (r1->x2 < r2->x2) { + x1 = r1->x2; + r1++; + } else { + x1 = r2->x2; + r2++; + } + return true; + } else { + MOZ_ASSERT(r1->x2 < r2->x2); + // we handle the corners by just extending the top and bottom edges + visit(closure, side, x1, y, r1->x2+1, y); + r1++; + // we assign x1 because we can assume that x1 <= r2->x1 - 1 + // However the caller may know better and if so, may update + // x1 to r1->x1 + x1 = r2->x1 - 1; + return false; + } +} + +//XXX: if we need to this can compute the end of the row +static void +VisitSides(visit_fn visit, void *closure, pixman_box32_t *r, pixman_box32_t *r_end) +{ + // XXX: we can drop LEFT/RIGHT and just use the orientation + // of the line if it makes sense + while (r != r_end) { + visit(closure, VisitSide::LEFT, r->x1, r->y1, r->x1, r->y2); + visit(closure, VisitSide::RIGHT, r->x2, r->y1, r->x2, r->y2); + r++; + } +} + +static void +VisitAbove(visit_fn visit, void *closure, pixman_box32_t *r, pixman_box32_t *r_end) +{ + while (r != r_end) { + visit(closure, VisitSide::TOP, r->x1-1, r->y1, r->x2+1, r->y1); + r++; + } +} + +static void +VisitBelow(visit_fn visit, void *closure, pixman_box32_t *r, pixman_box32_t *r_end) +{ + while (r != r_end) { + visit(closure, VisitSide::BOTTOM, r->x1-1, r->y2, r->x2+1, r->y2); + r++; + } +} + +static pixman_box32_t * +VisitInbetween(visit_fn visit, void *closure, pixman_box32_t *r1, + pixman_box32_t *r1_end, + pixman_box32_t *r2, + pixman_box32_t *r2_end) +{ + const int y = r1->y2; + int x1; + + bool overlap = false; + while (r1 != r1_end && r2 != r2_end) { + if (!overlap) { + /* Find the left-most edge */ + if (r1->x1 < r2->x1) { + x1 = r1->x1 - 1; + } else { + x1 = r2->x1 - 1; + } + } + + MOZ_ASSERT((x1 >= (r1->x1 - 1)) || (x1 >= (r2->x1 - 1))); + if (r1->x1 < r2->x1) { + overlap = VisitNextEdgeBetweenRect(visit, closure, VisitSide::BOTTOM, r1, r2, y, x1); + } else { + overlap = VisitNextEdgeBetweenRect(visit, closure, VisitSide::TOP, r2, r1, y, x1); + } + } + + /* Finish up which ever row has remaining rects*/ + if (r1 != r1_end) { + // top row + do { + visit(closure, VisitSide::BOTTOM, x1, y, r1->x2 + 1, y); + r1++; + if (r1 == r1_end) + break; + x1 = r1->x1 - 1; + } while (true); + } else if (r2 != r2_end) { + // bottom row + do { + visit(closure, VisitSide::TOP, x1, y, r2->x2 + 1, y); + r2++; + if (r2 == r2_end) + break; + x1 = r2->x1 - 1; + } while (true); + } + + return 0; +} + void nsRegion::VisitEdges (visit_fn visit, void *closure) { - if (mBands.IsEmpty()) { - visit(closure, VisitSide::LEFT, mBounds.X(), mBounds.Y(), mBounds.X(), mBounds.YMost()); - visit(closure, VisitSide::RIGHT, mBounds.XMost(), mBounds.Y(), mBounds.XMost(), mBounds.YMost()); - visit(closure, VisitSide::TOP, mBounds.X() - 1, mBounds.Y(), mBounds.XMost() + 1, mBounds.Y()); - visit(closure, VisitSide::BOTTOM, mBounds.X() - 1, mBounds.YMost(), mBounds.XMost() + 1, mBounds.YMost()); + pixman_box32_t *boxes; + int n; + boxes = pixman_region32_rectangles(&mImpl, &n); + + // if we have no rectangles then we're done + if (!n) return; + + pixman_box32_t *end = boxes + n; + pixman_box32_t *topRectsEnd = boxes + 1; + pixman_box32_t *topRects = boxes; + + // find the end of the first span of rectangles + while (topRectsEnd < end && topRectsEnd->y1 == topRects->y1) { + topRectsEnd++; } - auto band = std::begin(mBands); - auto bandFinal = std::end(mBands); - bandFinal--; - for (const Strip& strip : band->mStrips) { - visit(closure, VisitSide::LEFT, strip.left, band->top, strip.left, band->bottom); - visit(closure, VisitSide::RIGHT, strip.right, band->top, strip.right, band->bottom); - visit(closure, VisitSide::TOP, strip.left - 1, band->top, strip.right + 1, band->top); - } + // In order to properly handle convex corners we always visit the sides first + // that way when we visit the corners we can pad using the value from the sides + VisitSides(visit, closure, topRects, topRectsEnd); - if (band != bandFinal) { + VisitAbove(visit, closure, topRects, topRectsEnd); + + pixman_box32_t *bottomRects = topRects; + pixman_box32_t *bottomRectsEnd = topRectsEnd; + if (topRectsEnd != end) { do { - Band& topBand = *band; - band++; - - for (const Strip& strip : band->mStrips) { - visit(closure, VisitSide::LEFT, strip.left, band->top, strip.left, band->bottom); - visit(closure, VisitSide::RIGHT, strip.right, band->top, strip.right, band->bottom); + // find the next row of rects + bottomRects = topRectsEnd; + bottomRectsEnd = topRectsEnd + 1; + while (bottomRectsEnd < end && bottomRectsEnd->y1 == bottomRects->y1) { + bottomRectsEnd++; } - if (band->top == topBand.bottom) { - // Two bands touching each other vertically. - Band& bottomBand = *band; - auto topStrip = std::begin(topBand.mStrips); - auto bottomStrip = std::begin(bottomBand.mStrips); + VisitSides(visit, closure, bottomRects, bottomRectsEnd); - int y = topBand.bottom; - - // State from this point on along the vertical edge: - // 0 - Empty - // 1 - Touched by top rect - // 2 - Touched by bottom rect - // 3 - Touched on both sides - int state; - const int TouchedByNothing = 0; - const int TouchedByTop = 1; - const int TouchedByBottom = 2; - // We always start with nothing. - int oldState = TouchedByNothing; - // Last state change, adjusted by -1 if the last state change was - // a change away from 0. - int lastX = std::min(topStrip->left, bottomStrip->left) - 1; - - // Current edge being considered for top and bottom, 0 - left, 1 - right. - bool topEdgeIsLeft = true; - bool bottomEdgeIsLeft = true; - while (topStrip != std::end(topBand.mStrips) && bottomStrip != std::end(bottomBand.mStrips)) { - int topPos; - int bottomPos; - if (topEdgeIsLeft) { - topPos = topStrip->left; - } else { - topPos = topStrip->right; - } - if (bottomEdgeIsLeft) { - bottomPos = bottomStrip->left; - } else { - bottomPos = bottomStrip->right; - } - - int currentX = std::min(topPos, bottomPos); - if (topPos < bottomPos) { - if (topEdgeIsLeft) { - state = oldState | TouchedByTop; - } else { - state = oldState ^ TouchedByTop; - topStrip++; - } - topEdgeIsLeft = !topEdgeIsLeft; - } else if (bottomPos < topPos) { - if (bottomEdgeIsLeft) { - state = oldState | TouchedByBottom; - } else { - state = oldState ^ TouchedByBottom; - bottomStrip++; - } - bottomEdgeIsLeft = !bottomEdgeIsLeft; - } else { - // bottomPos == topPos - state = TouchedByNothing; - if (bottomEdgeIsLeft) { - state = TouchedByBottom; - } else { - bottomStrip++; - } - if (topEdgeIsLeft) { - state |= TouchedByTop; - } else { - topStrip++; - } - topEdgeIsLeft = !topEdgeIsLeft; - bottomEdgeIsLeft = !bottomEdgeIsLeft; - } - - MOZ_ASSERT(state != oldState); - if (oldState == TouchedByNothing) { - // We had nothing before, make sure the left edge will be padded. - lastX = currentX - 1; - } else if (oldState == TouchedByTop) { - if (state == TouchedByNothing) { - visit(closure, VisitSide::BOTTOM, lastX, y, currentX + 1, y); - } else { - visit(closure, VisitSide::BOTTOM, lastX, y, currentX, y); - lastX = currentX; - } - } else if (oldState == TouchedByBottom) { - if (state == TouchedByNothing) { - visit(closure, VisitSide::TOP, lastX, y, currentX + 1, y); - } else { - visit(closure, VisitSide::TOP, lastX, y, currentX, y); - lastX = currentX; - } - } else { - lastX = currentX; - } - oldState = state; - } - - MOZ_ASSERT(!state || (topEdgeIsLeft || bottomEdgeIsLeft)); - if (topStrip != std::end(topBand.mStrips)) { - if (!topEdgeIsLeft) { - visit(closure, VisitSide::BOTTOM, lastX, y, topStrip->right + 1, y); - topStrip++; - } - while (topStrip != std::end(topBand.mStrips)) { - visit(closure, VisitSide::BOTTOM, topStrip->left - 1, y, topStrip->right + 1, y); - topStrip++; - } - } else if (bottomStrip != std::end(bottomBand.mStrips)) { - if (!bottomEdgeIsLeft) { - visit(closure, VisitSide::TOP, lastX, y, bottomStrip->right + 1, y); - bottomStrip++; - } - while (bottomStrip != std::end(bottomBand.mStrips)) { - visit(closure, VisitSide::TOP, bottomStrip->left - 1, y, bottomStrip->right + 1, y); - bottomStrip++; - } - } + if (topRects->y2 == bottomRects->y1) { + VisitInbetween(visit, closure, topRects, topRectsEnd, + bottomRects, bottomRectsEnd); } else { - for (const Strip& strip : topBand.mStrips) { - visit(closure, VisitSide::BOTTOM, strip.left - 1, topBand.bottom, strip.right + 1, topBand.bottom); - } - for (const Strip& strip : band->mStrips) { - visit(closure, VisitSide::TOP, strip.left - 1, band->top, strip.right + 1, band->top); - } + VisitBelow(visit, closure, topRects, topRectsEnd); + VisitAbove(visit, closure, bottomRects, bottomRectsEnd); } - } while (band != bandFinal); + + topRects = bottomRects; + topRectsEnd = bottomRectsEnd; + } while (bottomRectsEnd != end); } - for (const Strip& strip : band->mStrips) { - visit(closure, VisitSide::BOTTOM, strip.left - 1, band->bottom, strip.right + 1, band->bottom); - } + // the bottom of the region doesn't touch anything else so we + // can always visit it at the end + VisitBelow(visit, closure, bottomRects, bottomRectsEnd); } @@ -465,18 +554,11 @@ void nsRegion::SimplifyInward (uint32_t aMaxRects) uint64_t nsRegion::Area () const { - if (mBands.IsEmpty()) { - return mBounds.Area(); - } - uint64_t area = 0; - for (const Band& band : mBands) { - uint32_t height = band.bottom - band.top; - for (const Strip& strip : band.mStrips) { - area += (strip.right - strip.left) * height; - } + for (auto iter = RectIter(); !iter.Done(); iter.Next()) { + const nsRect& rect = iter.Get(); + area += uint64_t(rect.Width()) * rect.Height(); } - return area; } @@ -487,27 +569,39 @@ nsRegion& nsRegion::ScaleRoundOut (float aXScale, float aYScale) return *this; } - nsRegion newRegion; - for (RectIterator iter = RectIterator(*this); !iter.Done(); iter.Next()) { - nsRect rect = iter.Get(); + int n; + pixman_box32_t *boxes = pixman_region32_rectangles(&mImpl, &n); + for (int i=0; i= first.XMost()) { - // The top of rect contains the bottom of the first rect - firstDeviceRect.SetBottomEdge(deviceRect.Y()); - } else if (rect.X() >= first.X() && rect.XMost() <= first.XMost()) { - // The bottom of the first contains the top of rect - deviceRect.SetTopEdge(firstDeviceRect.YMost()); - } + if (rect.Y() <= first.YMost()) { + if (rect.XMost() == first.X() && rect.YMost() <= first.YMost()) { + // rect is touching on the left edge of the first rect and contained within + // the length of its left edge + deviceRect.SetRightEdge(firstDeviceRect.X()); + } else if (rect.X() == first.XMost() && rect.YMost() <= first.YMost()) { + // rect is touching on the right edge of the first rect and contained within + // the length of its right edge + deviceRect.SetLeftEdge(firstDeviceRect.XMost()); + } else if (rect.Y() == first.YMost()) { + // The bottom of the first rect is on the same line as the top of rect, but + // they aren't necessarily contained. + if (rect.X() <= first.X() && rect.XMost() >= first.XMost()) { + // The top of rect contains the bottom of the first rect + firstDeviceRect.SetBottomEdge(deviceRect.Y()); + } else if (rect.X() >= first.X() && rect.XMost() <= first.XMost()) { + // The bottom of the first contains the top of rect + deviceRect.SetTopEdge(firstDeviceRect.YMost()); + } + } } + + boxes[i] = RectToBox(deviceRect); } - intRegion.OrWith(deviceRect); - } + boxes[0] = RectToBox(firstDeviceRect); - intRegion.OrWith(firstDeviceRect); + pixman_region32_fini(&intRegion.mImpl.mImpl); + // This will union all of the rectangles and runs in about O(n lg(n)) + pixman_region32_init_rects(&intRegion.mImpl.mImpl, boxes, n); + } return intRegion; + } // A cell's "value" is a pair consisting of @@ -987,35 +1129,19 @@ nsRect nsRegion::GetLargestRectangle (const nsRect& aContainingRect) const { std::ostream& operator<<(std::ostream& stream, const nsRegion& m) { stream << "["; - bool first = true; - for (auto iter = m.RectIter(); !iter.Done(); iter.Next()) { - if (!first) { + int n; + pixman_box32_t *boxes = pixman_region32_rectangles(const_cast(&m.mImpl), &n); + for (int i=0; i -#endif - /* For information on the internal representation look at pixman-region.c * * This replaces an older homebrew implementation of nsRegion. The @@ -55,481 +47,34 @@ enum class VisitSide { RIGHT }; -namespace regiondetails { -struct Band; -} - -template<> -struct nsTArray_CopyChooser -{ - typedef nsTArray_CopyWithConstructors Type; -}; - -namespace regiondetails { - -template -class UncheckedArray : public T -{ -public: - using T::Elements; - using T::Length; - - E & operator[](size_t aIndex) { return Elements()[aIndex]; } - const E& operator[](size_t aIndex) const { return Elements()[aIndex]; } - E& LastElement() { return Elements()[Length() - 1]; } - const E& LastElement() const { return Elements()[Length() - 1]; } - - using iterator = E* ; - using const_iterator = const E*; - - iterator begin() { return iterator(Elements()); } - const_iterator begin() const { return const_iterator(Elements()); } - const_iterator cbegin() const { return begin(); } - iterator end() { return iterator(Elements() + Length()); } - const_iterator end() const { return const_iterator(Elements() + Length()); } - const_iterator cend() const { return end(); } -}; - -struct Strip -{ - // Default constructor should never be called, but is required for - // vector::resize to compile. - Strip() { MOZ_CRASH(); } - Strip(int32_t aLeft, int32_t aRight) : left(aLeft), right(aRight) {} - - bool operator != (const Strip& aOther) const - { - return left != aOther.left || right != aOther.right; - } - - uint32_t Size() const - { - return right - left; - } - - int32_t left; - int32_t right; -}; - -struct Band -{ - using Strip = regiondetails::Strip; -#ifndef DEBUG - using StripArray = regiondetails::UncheckedArray, Strip>; -#else - using StripArray = AutoTArray; -#endif - - MOZ_IMPLICIT Band(const nsRect& aRect) - : top(aRect.Y()), bottom(aRect.YMost()) - { - mStrips.AppendElement(Strip{ aRect.X(), aRect.XMost() }); - } - - Band(const Band& aOther) - : top(aOther.top), bottom(aOther.bottom) - , mStrips(aOther.mStrips) - {} - Band(const Band&& aOther) - : top(aOther.top), bottom(aOther.bottom) - , mStrips(std::move(aOther.mStrips)) - {} - - void InsertStrip(const Strip& aStrip) - { - for (size_t i = 0; i < mStrips.Length(); i++) { - Strip& strip = mStrips[i]; - if (strip.left > aStrip.right) { - // Current strip is beyond aStrip, insert aStrip before. - mStrips.InsertElementAt(i, aStrip); - return; - } - - if (strip.right < aStrip.left) { - // Current strip is before aStrip, try the next. - continue; - } - - // Current strip intersects with aStrip, extend to the lext. - strip.left = std::min(strip.left, aStrip.left); - - if (strip.right >= aStrip.right) { - // Current strip extends beyond aStrip, done. - return; - } - - size_t next = i; - next++; - // Consume any subsequent strips intersecting with aStrip. - while (next < mStrips.Length() && mStrips[next].left <= aStrip.right) { - strip.right = mStrips[next].right; - - mStrips.RemoveElementAt(next); - } - - // Extend the strip in case the aStrip goes on beyond it. - strip.right = std::max(strip.right, aStrip.right); - return; - } - mStrips.AppendElement(aStrip); - } - - void SubStrip(const Strip& aStrip) - { - for (size_t i = 0; i < mStrips.Length(); i++) { - Strip& strip = mStrips[i]; - if (strip.left > aStrip.right) { - // Strip is entirely to the right of aStrip. Done. - return; - } - - if (strip.right < aStrip.left) { - // Strip is entirely to the left of aStrip. Move on. - continue; - } - - if (strip.left < aStrip.left) { - if (strip.right <= aStrip.right) { - strip.right = aStrip.left; - // This strip lies to the left of the start of aStrip. - continue; - } - - // aStrip is completely contained by this strip. - Strip newStrip(aStrip.right, strip.right); - strip.right = aStrip.left; - if (i < mStrips.Length()) { - i++; - mStrips.InsertElementAt(i, newStrip); - } else { - mStrips.AppendElement(newStrip); - } - return; - } - - // This strip lies to the right of the start of aStrip. - if (strip.right <= aStrip.right) { - // aStrip completely contains this strip. - mStrips.RemoveElementAt(i); - // Make sure we evaluate the strip now at i. This loop will increment. - i--; - continue; - } - strip.left = aStrip.right; - return; - } - } - - bool Intersects(const Strip& aStrip) const - { - for (const Strip& strip : mStrips) { - if (strip.left >= aStrip.right) { - return false; - } - - if (strip.right <= aStrip.left) { - continue; - } - - return true; - } - return false; - } - - bool IntersectStripBounds(Strip& aStrip) const - { - bool intersected = false; - - int32_t rightMost; - for (const Strip& strip : mStrips) { - if (strip.left > aStrip.right) { - break; - } - - if (strip.right <= aStrip.left) { - continue; - } - - if (!intersected) { - // First intersection, this is where the left side begins. - aStrip.left = std::max(aStrip.left, strip.left); - } - - intersected = true; - // Expand to the right for each intersecting strip found. - rightMost = std::min(strip.right, aStrip.right); - } - - if (intersected) { - aStrip.right = rightMost; - } - else { - aStrip.right = aStrip.left = 0; - } - return intersected; - } - - bool ContainsStrip(const Strip& aStrip) const - { - for (const Strip& strip : mStrips) { - if (strip.left > aStrip.left) { - return false; - } - - if (strip.right >= aStrip.right) { - return true; - } - } - return false; - } - - bool EqualStrips(const Band& aBand) const - { - if (mStrips.Length() != aBand.mStrips.Length()) { - return false; - } - - for (auto iter1 = mStrips.begin(), iter2 = aBand.mStrips.begin(); - iter1 != mStrips.end(); iter1++, iter2++) - { - if (*iter1 != *iter2) { - return false; - } - } - - return true; - } - - void IntersectStrip(const Strip& aStrip) - { - size_t i = 0; - - while (i < mStrips.Length()) { - Strip& strip = mStrips[i]; - if (strip.right <= aStrip.left) { - mStrips.RemoveElementAt(i); - continue; - } - - if (strip.left >= aStrip.right) { - mStrips.TruncateLength(i); - return; - } - - strip.left = std::max(aStrip.left, strip.left); - strip.right = std::min(aStrip.right, strip.right); - i++; - } - } - - void IntersectStrips(const Band& aOther) - { - auto iter = mStrips.begin(); - auto iterOther = aOther.mStrips.begin(); - - StripArray newStrips; - - // This function finds the intersection between two sets of strips. - while (true) { - while (true) { - while (iter != mStrips.end() && iter->right <= iterOther->left) { - // Increment our current strip until it ends beyond aOther's current strip. - iter++; - } - - if (iter == mStrips.end()) { - // End of our strips. Done. - break; - } - - while (iterOther != aOther.mStrips.end() && iterOther->right <= iter->left) { - // Increment aOther's current strip until it lies beyond our current strip. - iterOther++; - } - - if (iterOther == aOther.mStrips.end()) { - // End of aOther's strips. Done. - break; - } - - if (iterOther->left < iter->right) { - // Intersection! - break; - } - } - - if (iter == mStrips.end() || iterOther == aOther.mStrips.end()) { - break; - } - - newStrips.AppendElement(Strip(std::max(iter->left, iterOther->left), std::min(iterOther->right, iter->right))); - - if (iterOther->right < iter->right) { - iterOther++; - if (iterOther == aOther.mStrips.end()) { - break; - } - } else { - iter++; - } - } - - mStrips = newStrips; - } - - bool Intersects(const Band& aOther) const - { - auto iter = mStrips.begin(); - auto iterOther = aOther.mStrips.begin(); - - // This function finds the intersection between two sets of strips. - while (true) { - while (true) { - while (iter != mStrips.end() && iter->right <= iterOther->left) { - // Increment our current strip until it ends beyond aOther's current strip. - iter++; - } - - if (iter == mStrips.end()) { - // End of our strips. Done. - break; - } - - while (iterOther != aOther.mStrips.end() && iterOther->right <= iter->left) { - // Increment aOther's current strip until it lies beyond our current strip. - iterOther++; - } - - if (iterOther == aOther.mStrips.end()) { - // End of aOther's strips. Done. - break; - } - - if (iterOther->left < iter->right) { - // Intersection! - break; - } - } - - if (iter == mStrips.end()|| iterOther == aOther.mStrips.end()) { - break; - } - - return true; - } - return false; - } - - void SubStrips(const Band& aOther) - { - size_t idx = 0; - auto iterOther = aOther.mStrips.begin(); - - // This function finds the intersection between two sets of strips. - while (true) { - while (true) { - while (idx < mStrips.Length() && mStrips[idx].right <= iterOther->left) { - // Increment our current strip until it ends beyond aOther's current strip. - idx++; - } - - if (idx == mStrips.Length()) { - // End of our strips. Done. - break; - } - - while (iterOther != aOther.mStrips.end() && iterOther->right <= mStrips[idx].left) { - // Increment aOther's current strip until it lies beyond our current strip. - iterOther++; - } - - if (iterOther == aOther.mStrips.end()) { - // End of aOther's strips. Done. - break; - } - - if (iterOther->left < mStrips[idx].right) { - // Intersection! - break; - } - } - - if (idx == mStrips.Length() || iterOther == aOther.mStrips.end()) { - break; - } - - if (mStrips[idx].left < iterOther->left) { - size_t oldIdx = idx; - // Our strip starts beyond other's - if (mStrips[idx].right > iterOther->right) { - // Our strip ends beyond other's as well. - Strip newStrip(mStrips[idx]); - newStrip.left = iterOther->right; - mStrips.InsertElementAt(idx + 1, newStrip); - idx++; - } - mStrips[oldIdx].right = iterOther->left; - // Either idx was just incremented, or the current index no longer intersects with iterOther. - continue; - } else if (mStrips[idx].right > iterOther->right) { - mStrips[idx].left = iterOther->right; - // Current strip no longer intersects, continue. - iterOther++; - if (iterOther == aOther.mStrips.end()) { - break; - } - continue; - } - - // Our current strip is completely contained by the other strip. - mStrips.RemoveElementAt(idx); - } - } - - int32_t top; - int32_t bottom; - StripArray mStrips; -}; -} - class nsRegion { public: - using Band = regiondetails::Band; - using Strip = regiondetails::Strip; -#ifndef DEBUG - using BandArray = regiondetails::UncheckedArray, Band>; - using StripArray = regiondetails::UncheckedArray, Strip>; -#else - using BandArray = nsTArray; - using StripArray = AutoTArray; -#endif - typedef nsRect RectType; typedef nsPoint PointType; typedef nsMargin MarginType; - nsRegion() { } - MOZ_IMPLICIT nsRegion(const nsRect& aRect) { - mBounds = aRect; - } - explicit nsRegion(mozilla::gfx::ArrayView aRects) + nsRegion () { pixman_region32_init(&mImpl); } + MOZ_IMPLICIT nsRegion (const nsRect& aRect) { pixman_region32_init_rect(&mImpl, + aRect.X(), + aRect.Y(), + aRect.Width(), + aRect.Height()); } + explicit nsRegion (mozilla::gfx::ArrayView aRects) { - for (uint32_t i = 0; i < aRects.Length(); i++) { - AddRect(BoxToRect(aRects[i])); - } + pixman_region32_init_rects(&mImpl, aRects.Data(), aRects.Length()); } - - nsRegion(const nsRegion& aRegion) { Copy(aRegion); } - nsRegion(nsRegion&& aRegion) { mBands.SwapElements(aRegion.mBands); mBounds = aRegion.mBounds; aRegion.SetEmpty(); } - nsRegion& operator =(nsRegion&& aRegion) { - mBands.SwapElements(aRegion.mBands); - mBounds = aRegion.mBounds; - aRegion.SetEmpty(); - return *this; + nsRegion (const nsRegion& aRegion) { pixman_region32_init(&mImpl); pixman_region32_copy(&mImpl,aRegion.Impl()); } + nsRegion (nsRegion&& aRegion) { mImpl = aRegion.mImpl; pixman_region32_init(&aRegion.mImpl); } + nsRegion& operator = (nsRegion&& aRegion) { + pixman_region32_fini(&mImpl); + mImpl = aRegion.mImpl; + pixman_region32_init(&aRegion.mImpl); + return *this; } - nsRegion& operator =(const nsRect& aRect) { Copy(aRect); return *this; } - nsRegion& operator =(const nsRegion& aRegion) { Copy(aRegion); return *this; } + ~nsRegion () { pixman_region32_fini(&mImpl); } + nsRegion& operator = (const nsRect& aRect) { Copy (aRect); return *this; } + nsRegion& operator = (const nsRegion& aRegion) { Copy (aRegion); return *this; } bool operator==(const nsRegion& aRgn) const { return IsEqual(aRgn); @@ -540,328 +85,25 @@ public: } friend std::ostream& operator<<(std::ostream& stream, const nsRegion& m); - void OutputToStream(std::string aObjName, std::ostream& stream) const; - static - nsresult InitStatic() + void Swap(nsRegion* aOther) { - return NS_OK; + pixman_region32_t tmp = mImpl; + mImpl = aOther->mImpl; + aOther->mImpl = tmp; } - static - void ShutdownStatic() {} - -private: -#ifdef DEBUG_REGIONS - class OperationStringGenerator + void AndWith(const nsRegion& aOther) { - public: - virtual ~OperationStringGenerator() {} - - virtual void OutputOp() = 0; - }; -#endif -public: - - void AssertStateInternal() const; - void AssertState() const { -#ifdef DEBUG_REGIONS - AssertStateInternal(); -#endif + And(*this, aOther); } - -private: - void And(BandArray& aOut, const BandArray& aIn1, const BandArray& aIn2) + void AndWith(const nsRect& aOther) { - size_t idx = 0; - size_t idxOther = 0; - - // This algorithm essentially forms a new list of bands, by iterating over - // both regions' lists of band simultaneously, and building a new band - // wherever the two regions intersect. - while (true) { - while (true) { - while (idx != aIn1.Length() && aIn1[idx].bottom <= aIn2[idxOther].top) { - // Increment our current band until it ends beyond aOther's current band. - idx++; - } - - if (idx == aIn1.Length()) { - // This region is out of bands, the other region's future bands are ignored. - break; - } - - while (idxOther != aIn2.Length() && aIn2[idxOther].bottom <= aIn1[idx].top) { - // Increment aOther's current band until it ends beyond our current band. - idxOther++; - } - - if (idxOther == aIn2.Length()) { - // The other region's bands are all processed, all our future bands are ignored. - break; - } - - if (aIn2[idxOther].top < aIn1[idx].bottom) { - // We know the other band's bottom lies beyond our band's top because - // otherwise we would've incremented above. Intersecting bands found. - break; - } - } - - if (idx == aIn1.Length() || idxOther == aIn2.Length()) { - // The above loop executed a break because we're done. - break; - } - - Band newBand(aIn1[idx]); - // The new band is the intersection of the two current bands from both regions. - newBand.top = std::max(aIn1[idx].top, aIn2[idxOther].top); - newBand.bottom = std::min(aIn1[idx].bottom, aIn2[idxOther].bottom); - newBand.IntersectStrips(aIn2[idxOther]); - - if (newBand.mStrips.Length()) { - // The intersecting area of the bands had overlapping strips, if it is - // identical to the band above it merge, otherwise append. - if (aOut.Length() && aOut.LastElement().bottom == newBand.top && - aOut.LastElement().EqualStrips(newBand)) { - aOut.LastElement().bottom = newBand.bottom; - } else { - aOut.AppendElement(std::move(newBand)); - } - } - - if (aIn2[idxOther].bottom < aIn1[idx].bottom) { - idxOther++; - if (idxOther == aIn2.Length()) { - // Since we will access idxOther the next iteration, check if we're not done. - break; - } - } else { - // No need to check here since we do at the beginning of the next iteration. - idx++; - } - } + And(*this, aOther); } - -public: - nsRegion& AndWith(const nsRegion& aRegion) + nsRegion& And(const nsRegion& aRgn1, const nsRegion& aRgn2) { -#ifdef DEBUG_REGIONS - class OperationStringGeneratorAndWith : public OperationStringGenerator - { - public: - OperationStringGeneratorAndWith(nsRegion& aRegion, const nsRegion& aOtherRegion) - : mRegion(&aRegion), mRegionCopy(aRegion), mOtherRegion(aOtherRegion) - { - aRegion.mCurrentOpGenerator = this; - } - virtual ~OperationStringGeneratorAndWith() - { - mRegion->mCurrentOpGenerator = nullptr; - } - - virtual void OutputOp() override - { - std::stringstream stream; - mRegionCopy.OutputToStream("r1", stream); - mOtherRegion.OutputToStream("r2", stream); - stream << "r1.AndWith(r2);\n"; - gfxCriticalError() << stream.str(); - } - private: - nsRegion * mRegion; - nsRegion mRegionCopy; - nsRegion mOtherRegion; - }; - - OperationStringGeneratorAndWith opGenerator(*this, aRegion); -#endif - if (mBounds.IsEmpty()) { - // Region is empty, stays empty. - return *this; - } - - if (aRegion.IsEmpty()) { - SetEmpty(); - return *this; - } - - if (aRegion.mBands.IsEmpty()) { - // Other region is a rect. - return AndWith(aRegion.mBounds); - } - - if (mBands.IsEmpty()) { - mBands.AppendElement(mBounds); - } - - BandArray newBands; - - And(newBands, mBands, aRegion.mBands); - - mBands = std::move(newBands); - if (!mBands.Length()) { - mBounds = nsRect(); - } else { - mBounds = CalculateBounds(); - } - - EnsureSimplified(); - AssertState(); - return *this; - } - - nsRegion& AndWith(const nsRect& aRect) - { -#ifdef DEBUG_REGIONS - class OperationStringGeneratorAndWith : public OperationStringGenerator - { - public: - OperationStringGeneratorAndWith(nsRegion& aRegion, const nsRect& aRect) - : mRegion(&aRegion), mRegionCopy(aRegion), mRect(aRect) - { - aRegion.mCurrentOpGenerator = this; - } - virtual ~OperationStringGeneratorAndWith() - { - mRegion->mCurrentOpGenerator = nullptr; - } - - virtual void OutputOp() override - { - std::stringstream stream; - mRegionCopy.OutputToStream("r", stream); - stream << "r.AndWith(nsRect(" << mRect.X() << ", " << mRect.Y() << ", " << mRect.Width() << ", " << mRect.Height() << "));\n"; - gfxCriticalError() << stream.str(); - } - private: - nsRegion * mRegion; - nsRegion mRegionCopy; - nsRect mRect; - }; - - OperationStringGeneratorAndWith opGenerator(*this, aRect); -#endif - if (aRect.IsEmpty()) { - SetEmpty(); - return *this; - } - - if (mBands.IsEmpty()) { - mBounds = mBounds.Intersect(aRect); - return *this; - } - - size_t idx = 0; - - size_t removeStart = 0; - - // This removes all bands that do not intersect with aRect, and intersects - // the remaining ones with aRect. - - // Start by figuring out how much to remove from the start. - while (idx != mBands.Length() && mBands[idx].bottom <= aRect.Y()) { - idx++; - } - - // We'll remove these later to avoid needless copying in the array. - removeStart = idx; - - while (idx != mBands.Length()) { - if (mBands[idx].top >= aRect.YMost()) { - mBands.TruncateLength(idx); - break; - } - - mBands[idx].top = std::max(mBands[idx].top, aRect.Y()); - mBands[idx].bottom = std::min(mBands[idx].bottom, aRect.YMost()); - - mBands[idx].IntersectStrip(Strip(aRect.X(), aRect.XMost())); - - if (!mBands[idx].mStrips.Length()) { - mBands.RemoveElementAt(idx); - } else { - if (idx > removeStart) { - CompressBefore(idx); - } - idx++; - } - } - - if (removeStart) { - mBands.RemoveElementsAt(0, removeStart); - } - - if (mBands.Length()) { - mBounds = CalculateBounds(); - } else { - mBounds.SetEmpty(); - } - EnsureSimplified(); - AssertState(); - return *this; - } - nsRegion& And(const nsRegion& aRgn1, const nsRegion& aRgn2) - { - if (&aRgn1 == this) { - return AndWith(aRgn2); - } -#ifdef DEBUG_REGIONS - class OperationStringGeneratorAnd : public OperationStringGenerator - { - public: - OperationStringGeneratorAnd(nsRegion& aRegion, const nsRegion& aRegion1, const nsRegion& aRegion2) - : mRegion(&aRegion), mRegion1(aRegion1), mRegion2(aRegion2) - { - aRegion.mCurrentOpGenerator = this; - } - virtual ~OperationStringGeneratorAnd() - { - mRegion->mCurrentOpGenerator = nullptr; - } - - virtual void OutputOp() override - { - std::stringstream stream; - mRegion1.OutputToStream("r1", stream); - mRegion2.OutputToStream("r2", stream); - stream << "nsRegion r3;\nr3.And(r1, r2);\n"; - gfxCriticalError() << stream.str(); - } - private: - nsRegion * mRegion; - nsRegion mRegion1; - nsRegion mRegion2; - }; - - OperationStringGeneratorAnd opGenerator(*this, aRgn1, aRgn2); -#endif - mBands.Clear(); - - if (aRgn1.IsEmpty() || aRgn2.IsEmpty()) { - mBounds.SetEmpty(); - return *this; - } - - if (aRgn1.mBands.IsEmpty() && aRgn2.mBands.IsEmpty()) { - mBounds = aRgn1.mBounds.Intersect(aRgn2.mBounds); - return *this; - } else if (aRgn1.mBands.IsEmpty()) { - return And(aRgn2, aRgn1.mBounds); - } else if (aRgn2.mBands.IsEmpty()) { - return And(aRgn1, aRgn2.mBounds); - } - - And(mBands, aRgn1.mBands, aRgn2.mBands); - - if (!mBands.Length()) { - mBounds = nsRect(); - } else { - mBounds = CalculateBounds(); - } - - EnsureSimplified(); - AssertState(); + pixman_region32_intersect(&mImpl, aRgn1.Impl(), aRgn2.Impl()); return *this; } nsRegion& And(const nsRect& aRect, const nsRegion& aRegion) @@ -870,130 +112,33 @@ public: } nsRegion& And(const nsRegion& aRegion, const nsRect& aRect) { - if (&aRegion == this) { - return AndWith(aRect); - } -#ifdef DEBUG_REGIONS - class OperationStringGeneratorAnd : public OperationStringGenerator - { - public: - OperationStringGeneratorAnd(nsRegion& aThisRegion, const nsRegion& aRegion, const nsRect& aRect) - : mThisRegion(&aThisRegion), mRegion(aRegion), mRect(aRect) - { - aThisRegion.mCurrentOpGenerator = this; - } - virtual ~OperationStringGeneratorAnd() - { - mThisRegion->mCurrentOpGenerator = nullptr; - } - - virtual void OutputOp() override - { - std::stringstream stream; - mRegion.OutputToStream("r", stream); - stream << "nsRegion r2;\nr.And(r2, nsRect(" << mRect.X() << ", " << mRect.Y() << ", " << mRect.Width() << ", " << mRect.Height() << "));\n"; - gfxCriticalError() << stream.str(); - } - private: - nsRegion* mThisRegion; - nsRegion mRegion; - nsRect mRect; - }; - - OperationStringGeneratorAnd opGenerator(*this, aRegion, aRect); -#endif - mBands.Clear(); - - if (aRect.IsEmpty()) { - mBounds.SetEmpty(); - return *this; - } - - if (aRegion.mBands.IsEmpty()) { - mBounds = aRegion.mBounds.Intersect(aRect); - return *this; - } - - size_t idx = 0; - const BandArray& bands = aRegion.mBands; - - mBands.SetCapacity(bands.Length() + 3); - while (idx != bands.Length()) { - // Ignore anything before. - if (bands[idx].bottom <= aRect.Y()) { - idx++; - continue; - } - // We're done once we've reached the bottom. - if (bands[idx].top >= aRect.YMost()) { - break; - } - - // Now deal with bands actually intersecting the rectangle. - Band newBand(bands[idx]); - newBand.top = std::max(bands[idx].top, aRect.Y()); - newBand.bottom = std::min(bands[idx].bottom, aRect.YMost()); - - newBand.IntersectStrip(Strip(aRect.X(), aRect.XMost())); - - if (newBand.mStrips.Length()) { - if (!mBands.IsEmpty() && - newBand.top == mBands.LastElement().bottom && - newBand.EqualStrips(mBands.LastElement())) { - mBands.LastElement().bottom = newBand.bottom; - } else { - mBands.AppendElement(std::move(newBand)); - } - } - idx++; - } - - if (mBands.Length()) { - mBounds = CalculateBounds(); - } else { - mBounds.SetEmpty(); - } - - EnsureSimplified(); - AssertState(); + pixman_region32_intersect_rect(&mImpl, aRegion.Impl(), aRect.X(), aRect.Y(), aRect.Width(), aRect.Height()); return *this; } nsRegion& And(const nsRect& aRect1, const nsRect& aRect2) { - nsRect tmpRect; + nsRect TmpRect; - tmpRect.IntersectRect(aRect1, aRect2); - return Copy(tmpRect); + TmpRect.IntersectRect(aRect1, aRect2); + return Copy(TmpRect); } nsRegion& OrWith(const nsRegion& aOther) { - for (RectIterator idx(aOther); !idx.Done(); idx.Next()) { - AddRect(idx.Get()); - } - return *this; + return Or(*this, aOther); } nsRegion& OrWith(const nsRect& aOther) { - AddRect(aOther); - return *this; + return Or(*this, aOther); } nsRegion& Or(const nsRegion& aRgn1, const nsRegion& aRgn2) { - if (&aRgn1 != this) { - *this = aRgn1; - } - for (RectIterator idx(aRgn2); !idx.Done(); idx.Next()) { - AddRect(idx.Get()); - } + pixman_region32_union(&mImpl, aRgn1.Impl(), aRgn2.Impl()); return *this; } nsRegion& Or(const nsRegion& aRegion, const nsRect& aRect) { - if (&aRegion != this) { - *this = aRegion; - } - AddRect(aRect); + pixman_region32_union_rect(&mImpl, aRegion.Impl(), aRect.X(), aRect.Y(), aRect.Width(), aRect.Height()); return *this; } nsRegion& Or(const nsRect& aRect, const nsRegion& aRegion) @@ -1002,8 +147,8 @@ public: } nsRegion& Or(const nsRect& aRect1, const nsRect& aRect2) { - Copy(aRect1); - return Or(*this, aRect2); + Copy (aRect1); + return Or (*this, aRect2); } nsRegion& XorWith(const nsRegion& aOther) @@ -1014,7 +159,7 @@ public: { return Xor(*this, aOther); } - nsRegion& Xor(const nsRegion& aRgn1, const nsRegion& aRgn2) + nsRegion& Xor(const nsRegion& aRgn1, const nsRegion& aRgn2) { // this could be implemented better if pixman had direct // support for xoring regions. @@ -1037,750 +182,60 @@ public: return Xor(nsRegion(aRect1), nsRegion(aRect2)); } - nsRegion ToAppUnits(nscoord aAppUnitsPerPixel) const; + nsRegion ToAppUnits (nscoord aAppUnitsPerPixel) const; - nsRegion& SubWith(const nsRegion& aOther) - { -#ifdef DEBUG_REGIONS - class OperationStringGeneratorSubWith : public OperationStringGenerator - { - public: - OperationStringGeneratorSubWith(nsRegion& aRegion, const nsRegion& aOtherRegion) - : mRegion(&aRegion), mRegionCopy(aRegion), mOtherRegion(aOtherRegion) - { - aRegion.mCurrentOpGenerator = this; - } - virtual ~OperationStringGeneratorSubWith() - { - mRegion->mCurrentOpGenerator = nullptr; - } - - virtual void OutputOp() override - { - std::stringstream stream; - mRegionCopy.OutputToStream("r1", stream); - mOtherRegion.OutputToStream("r2", stream); - stream << "r1.SubWith(r2);\n"; - gfxCriticalError() << stream.str(); - } - private: - nsRegion * mRegion; - nsRegion mRegionCopy; - nsRegion mOtherRegion; - }; - - OperationStringGeneratorSubWith opGenerator(*this, aOther); -#endif - - if (mBounds.IsEmpty()) { - return *this; - } - - if (aOther.mBands.IsEmpty()) { - return SubWith(aOther.mBounds); - } - - if (mBands.IsEmpty()) { - mBands.AppendElement(Band(mBounds)); - } - - size_t idx = 0; - size_t idxOther = 0; - while (idx < mBands.Length()) { - while (true) { - while (idx != mBands.Length() && mBands[idx].bottom <= aOther.mBands[idxOther].top) { - // Increment our current band until it ends beyond aOther's current band. - idx++; - } - - if (idx == mBands.Length()) { - // This region is out of bands, the other region's future bands are ignored. - break; - } - - while (idxOther != aOther.mBands.Length() && aOther.mBands[idxOther].bottom <= mBands[idx].top) { - // Increment aOther's current band until it ends beyond our current band. - idxOther++; - } - - if (idxOther == aOther.mBands.Length()) { - // The other region's bands are all processed, all our future bands are ignored. - break; - } - - if (aOther.mBands[idxOther].top < mBands[idx].bottom) { - // We know the other band's bottom lies beyond our band's top because - // otherwise we would've incremented above. Intersecting bands found. - break; - } - } - - if (idx == mBands.Length() || idxOther == aOther.mBands.Length()) { - // The above loop executed a break because we're done. - break; - } - - const Band& bandOther = aOther.mBands[idxOther]; - - if (!mBands[idx].Intersects(bandOther)) { - if (mBands[idx].bottom < bandOther.bottom) { - idx++; - } else { - idxOther++; - if (idxOther == aOther.mBands.Length()) { - break; - } - } - continue; - } - - // These bands actually intersect. - if (mBands[idx].top < bandOther.top) { - mBands.InsertElementAt(idx + 1, Band(mBands[idx])); - mBands[idx].bottom = bandOther.top; - idx++; - mBands[idx].top = bandOther.top; - } - - // mBands[idx].top >= bandOther.top; - if (mBands[idx].bottom <= bandOther.bottom) { - mBands[idx].SubStrips(bandOther); - if (mBands[idx].mStrips.IsEmpty()) { - mBands.RemoveElementAt(idx); - } else { - CompressBefore(idx); - idx++; - // The band before us just changed, it may be identical now. - CompressBefore(idx); - } - continue; - } - - // mBands[idx].bottom > bandOther.bottom - Band newBand = mBands[idx]; - newBand.SubStrips(bandOther); - - if (!newBand.mStrips.IsEmpty()) { - mBands.InsertElementAt(idx, newBand); - mBands[idx].bottom = bandOther.bottom; - CompressBefore(idx); - idx++; - } - - mBands[idx].top = bandOther.bottom; - - idxOther++; - if (idxOther == aOther.mBands.Length()) { - break; - } - } - - if (mBands.IsEmpty()) { - mBounds.SetEmpty(); - } else { - mBounds = CalculateBounds(); - } - - AssertState(); - EnsureSimplified(); - return *this; - } nsRegion& SubOut(const nsRegion& aOther) { - return SubWith(aOther); + return Sub(*this, aOther); } nsRegion& SubOut(const nsRect& aOther) { - return SubWith(aOther); + return Sub(*this, aOther); } - -private: - void AppendOrExtend(const Band& aNewBand) - { - if (aNewBand.mStrips.IsEmpty()) { - return; - } - if (mBands.IsEmpty()) { - mBands.AppendElement(aNewBand); - return; - } - - if (mBands.LastElement().bottom == aNewBand.top && mBands.LastElement().EqualStrips(aNewBand)) { - mBands.LastElement().bottom = aNewBand.bottom; - } else { - mBands.AppendElement(aNewBand); - } - } - void AppendOrExtend(const Band&& aNewBand) - { - if (aNewBand.mStrips.IsEmpty()) { - return; - } - if (mBands.IsEmpty()) { - mBands.AppendElement(std::move(aNewBand)); - return; - } - - if (mBands.LastElement().bottom == aNewBand.top && mBands.LastElement().EqualStrips(aNewBand)) { - mBands.LastElement().bottom = aNewBand.bottom; - } else { - mBands.AppendElement(std::move(aNewBand)); - } - } -public: nsRegion& Sub(const nsRegion& aRgn1, const nsRegion& aRgn2) { - if (&aRgn1 == this) { - return SubWith(aRgn2); - } -#ifdef DEBUG_REGIONS - class OperationStringGeneratorSub : public OperationStringGenerator - { - public: - OperationStringGeneratorSub(nsRegion& aRegion, const nsRegion& aRgn1, const nsRegion& aRgn2) - : mRegion(&aRegion), mRegion1(aRgn1), mRegion2(aRgn2) - { - aRegion.mCurrentOpGenerator = this; - } - virtual ~OperationStringGeneratorSub() - { - mRegion->mCurrentOpGenerator = nullptr; - } - - virtual void OutputOp() override - { - std::stringstream stream; - mRegion1.OutputToStream("r1", stream); - mRegion2.OutputToStream("r2", stream); - stream << "nsRegion r3;\nr3.Sub(r1, r2);\n"; - gfxCriticalError() << stream.str(); - } - private: - nsRegion* mRegion; - nsRegion mRegion1; - nsRegion mRegion2; - }; - - OperationStringGeneratorSub opGenerator(*this, aRgn1, aRgn2); -#endif - - mBands.Clear(); - - if (aRgn1.mBounds.IsEmpty()) { - mBounds.SetEmpty(); - return *this; - } - - if (aRgn2.mBounds.IsEmpty()) { - Copy(aRgn1); - return *this; - } - - if (aRgn1.mBands.IsEmpty() && aRgn2.mBands.IsEmpty()) { - return Sub(aRgn1.mBounds, aRgn2.mBounds); - } else if (aRgn1.mBands.IsEmpty()) { - return Sub(aRgn1.mBounds, aRgn2); - } else if (aRgn2.mBands.IsEmpty()) { - return Sub(aRgn1, aRgn2.mBounds); - } - - const BandArray& bands1 = aRgn1.mBands; - const BandArray& bands2 = aRgn2.mBands; - - size_t idx = 0; - size_t idxOther = 0; - - // We iterate the source region's bands, subtracting the other regions bands from them as we - // move them into ours. - while (idx < bands1.Length()) { - while (idxOther < bands2.Length() && bands2[idxOther].bottom <= bands1[idx].top) { - // These other bands are irrelevant as they don't intersect with the - // band we're currently processing. - idxOther++; - } - if (idxOther == bands2.Length()) { - break; - } - - const Band& other = bands2[idxOther]; - - // bands2[idxOther].bottom >= bands1[idx].top - Band origBand(bands1[idx]); - if (other.top >= origBand.bottom) { - // No intersecting bands, append and continue. - AppendOrExtend(origBand); - idx++; - continue; - } - - // Push a band for an uncovered region above our band. - if (origBand.top < other.top) { - Band newBand(origBand); - newBand.bottom = other.top; - AppendOrExtend(std::move(newBand)); - } - - int32_t lastBottom = std::max(other.top, origBand.top); - while (idxOther < bands2.Length() && bands2[idxOther].top < origBand.bottom) { - const Band& other = bands2[idxOther]; - Band newBand(origBand); - newBand.top = std::max(origBand.top, other.top); - newBand.bottom = std::min(origBand.bottom, other.bottom); - - // If there was a gap, we need to add the original band there. - if (newBand.top > lastBottom) { - Band betweenBand(newBand); - betweenBand.top = lastBottom; - betweenBand.bottom = newBand.top; - AppendOrExtend(std::move(betweenBand)); - } - - lastBottom = newBand.bottom; - newBand.SubStrips(other); - AppendOrExtend(std::move(newBand)); - idxOther++; - } - // Decrement other here so we are back at the last band in region 2 - // that intersected. - idxOther--; - - if (bands2[idxOther].bottom < origBand.bottom) { - // The last band in region2 that intersected ended before this one, - // we can copy the rest. - Band newBand(origBand); - newBand.top = bands2[idxOther].bottom; - AppendOrExtend(std::move(newBand)); - idxOther++; - } - idx++; - } - - // Copy any remaining bands, the first one may have to be extended to fit - // the last one added before. The rest can be unconditionally appended. - if (idx < bands1.Length()) { - AppendOrExtend(bands1[idx]); - idx++; - } - - while (idx < bands1.Length()) { - mBands.AppendElement(bands1[idx]); - idx++; - } - - if (mBands.IsEmpty()) { - mBounds.SetEmpty(); - } - else { - mBounds = CalculateBounds(); - } - - AssertState(); - EnsureSimplified(); - return *this; - } - -private: - // Internal helper for executing subtraction. - void RunSubtraction(const nsRect& aRect) - { - Strip rectStrip(aRect.X(), aRect.XMost()); - - size_t idx = 0; - - while (idx < mBands.Length()) { - if (mBands[idx].top >= aRect.YMost()) { - return; - } - - if (mBands[idx].bottom <= aRect.Y()) { - // This band is entirely before aRect, move on. - idx++; - continue; - } - - if (!mBands[idx].Intersects(Strip(aRect.X(), aRect.XMost()))) { - // This band does not intersect aRect horizontally. Move on. - idx++; - continue; - } - - // This band intersects with aRect. - - if (mBands[idx].top < aRect.Y()) { - // This band starts above the start of aRect, split the band into two - // along the intersection, and continue to the next iteration to process - // the one that now intersects exactly. - auto above = mBands.InsertElementAt(idx, Band(mBands[idx])); - above->bottom = aRect.Y(); - idx++; - mBands[idx].top = aRect.Y(); - // Continue to run the loop for the next band. - continue; - } - - if (mBands[idx].bottom <= aRect.YMost()) { - // This band ends before the end of aRect. - mBands[idx].SubStrip(rectStrip); - if (mBands[idx].mStrips.Length()) { - CompressAdjacentBands(idx); - } else { - mBands.RemoveElementAt(idx); - } - continue; - } - - // This band extends beyond aRect. - Band newBand = mBands[idx]; - newBand.SubStrip(rectStrip); - newBand.bottom = aRect.YMost(); - mBands[idx].top = aRect.YMost(); - - if (newBand.mStrips.Length()) { - if (idx && mBands[idx - 1].bottom == newBand.top && newBand.EqualStrips(mBands[idx - 1])) { - mBands[idx - 1].bottom = aRect.YMost(); - } else { - mBands.InsertElementAt(idx, std::move(newBand)); - } - } - - return; - } - } - -public: - nsRegion& SubWith(const nsRect& aRect) { - if (!mBounds.Intersects(aRect)) { - return *this; - } - - if (aRect.Contains(mBounds)) { - SetEmpty(); - return *this; - } - -#ifdef DEBUG_REGIONS - class OperationStringGeneratorSubWith : public OperationStringGenerator - { - public: - OperationStringGeneratorSubWith(nsRegion& aRegion, const nsRect& aRect) - : mRegion(&aRegion), mRegionCopy(aRegion), mRect(aRect) - { - aRegion.mCurrentOpGenerator = this; - } - virtual ~OperationStringGeneratorSubWith() - { - mRegion->mCurrentOpGenerator = nullptr; - } - - virtual void OutputOp() override - { - std::stringstream stream; - mRegionCopy.OutputToStream("r", stream); - stream << "r.SubWith(nsRect(" << mRect.X() << ", " << mRect.Y() << ", " << mRect.Width() << ", " << mRect.Height() << "));\n"; - gfxCriticalError() << stream.str(); - } - private: - nsRegion * mRegion; - nsRegion mRegionCopy; - nsRect mRect; - }; - - OperationStringGeneratorSubWith opGenerator(*this, aRect); -#endif - - if (mBands.IsEmpty()) { - mBands.AppendElement(Band(mBounds)); - } - - RunSubtraction(aRect); - - if (aRect.x <= mBounds.x || aRect.y <= mBounds.y || - aRect.XMost() >= mBounds.XMost() || aRect.YMost() >= mBounds.YMost()) { - mBounds = CalculateBounds(); - } - EnsureSimplified(); - AssertState(); + pixman_region32_subtract(&mImpl, aRgn1.Impl(), aRgn2.Impl()); return *this; } nsRegion& Sub(const nsRegion& aRegion, const nsRect& aRect) { - if (aRect.Contains(aRegion.mBounds)) { - SetEmpty(); - return *this; - } - if (&aRegion == this) { - return SubWith(aRect); - } -#ifdef DEBUG_REGIONS - class OperationStringGeneratorSub : public OperationStringGenerator - { - public: - OperationStringGeneratorSub(nsRegion& aRegion, const nsRegion& aRegionOther, const nsRect& aRect) - : mRegion(&aRegion), mRegionOther(aRegionOther), mRect(aRect) - { - aRegion.mCurrentOpGenerator = this; - } - virtual ~OperationStringGeneratorSub() - { - mRegion->mCurrentOpGenerator = nullptr; - } - - virtual void OutputOp() override - { - std::stringstream stream; - mRegionOther.OutputToStream("r1", stream); - stream << "nsRegion r2;\nr2.Sub(r1, nsRect(" << mRect.X() << ", " << mRect.Y() << ", " << mRect.Width() << ", " << mRect.Height() << "));\n"; - gfxCriticalError() << stream.str(); - } - private: - nsRegion * mRegion; - nsRegion mRegionOther; - nsRect mRect; - }; - - OperationStringGeneratorSub opGenerator(*this, aRegion, aRect); -#endif - - mBands.Clear(); - - if (aRegion.mBounds.IsEmpty()) { - mBounds.SetEmpty(); - return *this; - } - - if (aRect.IsEmpty()) { - Copy(aRegion); - return *this; - } - - if (aRegion.mBands.IsEmpty()) { - Copy(aRegion.mBounds); - return SubWith(aRect); - } - - const BandArray& bands = aRegion.mBands; - - size_t idx = 0; - - Strip strip(aRect.X(), aRect.XMost()); - - mBands.SetCapacity(bands.Length() + 3); - - // Process all bands that lie before aRect. - while (idx < bands.Length() && bands[idx].bottom <= aRect.Y()) { - mBands.AppendElement(bands[idx]); - idx++; - } - - // This band's bottom lies beyond aRect. - if (idx < bands.Length() && bands[idx].top < aRect.Y()) { - Band newBand(bands[idx]); - if (bands[idx].Intersects(strip)) { - newBand.bottom = aRect.Y(); - } else { - idx++; - } - mBands.AppendElement(std::move(newBand)); - } - - // This tracks whether the band when we -exit- the next loop intersected the rectangle. - bool didIntersect = false; - - while (idx < bands.Length() && bands[idx].top < aRect.YMost()) { - // Process all bands intersecting with aRect. - if (!bands[idx].Intersects(strip)) { - AppendOrExtend(bands[idx]); - idx++; - didIntersect = false; - continue; - } - - didIntersect = true; - Band newBand(bands[idx]); - newBand.top = std::max(newBand.top, aRect.Y()); - newBand.bottom = std::min(newBand.bottom, aRect.YMost()); - newBand.SubStrip(strip); - AppendOrExtend(std::move(newBand)); - idx++; - } - - if (didIntersect) { - if (aRect.YMost() < bands[idx - 1].bottom) { - // If this band does not intersect the loop above has already added the - // whole unmodified band. - Band newBand(bands[idx - 1]); - newBand.top = aRect.YMost(); - AppendOrExtend(std::move(newBand)); - } - } - - // Now process all bands beyond aRect. - if (idx < bands.Length()) { - AppendOrExtend(bands[idx]); - idx++; - } - - mBands.AppendElements(bands.Elements() + idx, bands.Length() - idx); - - if (mBands.IsEmpty()) { - mBounds.SetEmpty(); - } else { - mBounds = CalculateBounds(); - } - - AssertState(); - EnsureSimplified(); - return *this; + return Sub(aRegion, nsRegion(aRect)); } nsRegion& Sub(const nsRect& aRect, const nsRegion& aRegion) { - Copy(aRect); - return SubWith(aRegion); + return Sub(nsRegion(aRect), aRegion); } nsRegion& Sub(const nsRect& aRect1, const nsRect& aRect2) { Copy(aRect1); - return SubWith(aRect2); + return Sub(*this, aRect2); } /** - * Returns true if the given point is inside the region. A region + * Returns true iff the given point is inside the region. A region * created from a rect (x=0, y=0, w=100, h=100) will NOT contain * the point x=100, y=100. */ - bool Contains(int aX, int aY) const + bool Contains (int aX, int aY) const { - if (mBands.IsEmpty()) { - return mBounds.Contains(aX, aY); - } - - auto iter = mBands.begin(); - - while (iter != mBands.end()) { - if (iter->bottom <= aY) { - iter++; - continue; - } - - if (iter->top > aY) { - return false; - } - - if (iter->ContainsStrip(Strip(aX, aX + 1))) { - return true; - } - return false; - } - return false; + return pixman_region32_contains_point(Impl(), aX, aY, nullptr); } - bool Contains(const nsRect& aRect) const + bool Contains (const nsRect& aRect) const { - if (aRect.IsEmpty()) { - return false; - } - - if (mBands.IsEmpty()) { - return mBounds.Contains(aRect); - } - - auto iter = mBands.begin(); - - while (iter != mBands.end()) { - if (iter->bottom <= aRect.Y()) { - iter++; - continue; - } - - if (iter->top > aRect.Y()) { - return false; - } - - // Now inside the rectangle. - if (!iter->ContainsStrip(Strip(aRect.X(), aRect.XMost()))) { - return false; - } - - if (iter->bottom >= aRect.YMost()) { - return true; - } - - int32_t lastY = iter->bottom; - iter++; - while (iter != mBands.end()) { - // Bands do not connect. - if (iter->top != lastY) { - return false; - } - - if (!iter->ContainsStrip(Strip(aRect.X(), aRect.XMost()))) { - return false; - } - - if (iter->bottom >= aRect.YMost()) { - return true; - } - - lastY = iter->bottom; - iter++; - } - } - return false; + pixman_box32_t box = RectToBox(aRect); + return pixman_region32_contains_rectangle(Impl(), &box) == PIXMAN_REGION_IN; } + bool Contains (const nsRegion& aRgn) const; + bool Intersects (const nsRect& aRect) const; - bool Contains(const nsRegion& aRgn) const; - bool Intersects(const nsRect& aRect) const; - - void MoveBy(int32_t aXOffset, int32_t aYOffset) + void MoveBy (int32_t aXOffset, int32_t aYOffset) { - MoveBy(nsPoint(aXOffset, aYOffset)); + MoveBy (nsPoint (aXOffset, aYOffset)); } - void MoveBy(nsPoint aPt) + void MoveBy (nsPoint aPt) { pixman_region32_translate(&mImpl, aPt.x, aPt.y); } + void SetEmpty () { -#ifdef DEBUG_REGIONS - class OperationStringGeneratorMoveBy : public OperationStringGenerator - { - public: - OperationStringGeneratorMoveBy(nsRegion& aRegion, const nsPoint& aPoint) - : mRegion(&aRegion), mRegionCopy(aRegion), mPoint(aPoint) - { - aRegion.mCurrentOpGenerator = this; - } - virtual ~OperationStringGeneratorMoveBy() - { - mRegion->mCurrentOpGenerator = nullptr; - } - - virtual void OutputOp() override - { - std::stringstream stream; - mRegionCopy.OutputToStream("r", stream); - stream << "r.MoveBy(nsPoint(" << mPoint.x << ", " << mPoint.y << "));\n"; - gfxCriticalError() << stream.str(); - } - private: - nsRegion * mRegion; - nsRegion mRegionCopy; - nsPoint mPoint; - }; - - OperationStringGeneratorMoveBy opGenerator(*this, aPt); -#endif - - mBounds.MoveBy(aPt); - for (Band& band : mBands) { - band.top += aPt.Y(); - band.bottom += aPt.Y(); - for (Strip& strip : band.mStrips) { - strip.left += aPt.X(); - strip.right += aPt.X(); - } - } - AssertState(); - } - void SetEmpty() - { - mBands.Clear(); - mBounds.SetEmpty(); + pixman_region32_clear(&mImpl); } nsRegion MovedBy(int32_t aXOffset, int32_t aYOffset) const @@ -1810,45 +265,21 @@ public: return copy; } - bool IsEmpty() const { return mBounds.IsEmpty(); } - bool IsComplex() const { return GetNumRects() > 1; } - bool IsEqual(const nsRegion& aRegion) const + bool IsEmpty () const { return !pixman_region32_not_empty(Impl()); } + bool IsComplex () const { return GetNumRects() > 1; } + bool IsEqual (const nsRegion& aRegion) const { - if (mBands.IsEmpty() && aRegion.mBands.IsEmpty()) { - return mBounds.IsEqualInterior(aRegion.mBounds); - } - - if (mBands.Length() != aRegion.mBands.Length()) { - return false; - } - - for (auto iter1 = mBands.begin(), iter2 = aRegion.mBands.begin(); - iter1 != mBands.end(); iter1++, iter2++) - { - if (!iter1->EqualStrips(*iter2)) { - return false; - } - } - - return true; + return pixman_region32_equal(Impl(), aRegion.Impl()); } - - uint32_t GetNumRects() const + uint32_t GetNumRects () const { - if (mBands.IsEmpty()) { - return mBounds.IsEmpty() ? 0 : 1; - } - - uint32_t rects = 0; - - for (const Band& band : mBands) { - rects += band.mStrips.Length(); - } - - return rects; + // Work around pixman bug. Sometimes pixman creates regions with 1 rect + // that's empty. + uint32_t result = pixman_region32_n_rects(Impl()); + return (result == 1 && GetBounds().IsEmpty()) ? 0 : result; } - const nsRect GetBounds() const { return mBounds; } - uint64_t Area() const; + const nsRect GetBounds () const { return BoxToRect(mImpl.extents); } + uint64_t Area () const; /** * Return this region scaled to a different appunits per pixel (APP) ratio. @@ -1858,17 +289,17 @@ public: * @note this can turn an empty region into a non-empty region */ MOZ_MUST_USE nsRegion - ScaleToOtherAppUnitsRoundOut(int32_t aFromAPP, int32_t aToAPP) const; + ScaleToOtherAppUnitsRoundOut (int32_t aFromAPP, int32_t aToAPP) const; MOZ_MUST_USE nsRegion - ScaleToOtherAppUnitsRoundIn(int32_t aFromAPP, int32_t aToAPP) const; + ScaleToOtherAppUnitsRoundIn (int32_t aFromAPP, int32_t aToAPP) const; nsRegion& ScaleRoundOut(float aXScale, float aYScale); nsRegion& ScaleInverseRoundOut(float aXScale, float aYScale); - nsRegion& Transform(const mozilla::gfx::Matrix4x4 &aTransform); - nsIntRegion ScaleToOutsidePixels(float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const; - nsIntRegion ScaleToInsidePixels(float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const; - nsIntRegion ScaleToNearestPixels(float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const; - nsIntRegion ToOutsidePixels(nscoord aAppUnitsPerPixel) const; - nsIntRegion ToNearestPixels(nscoord aAppUnitsPerPixel) const; + nsRegion& Transform (const mozilla::gfx::Matrix4x4 &aTransform); + nsIntRegion ScaleToOutsidePixels (float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const; + nsIntRegion ScaleToInsidePixels (float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const; + nsIntRegion ScaleToNearestPixels (float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const; + nsIntRegion ToOutsidePixels (nscoord aAppUnitsPerPixel) const; + nsIntRegion ToNearestPixels (nscoord aAppUnitsPerPixel) const; /** * Gets the largest rectangle contained in the region. @@ -1876,7 +307,7 @@ public: * maximizes the area intersecting with aContainingRect (and break ties by * then choosing the largest rectangle overall) */ - nsRect GetLargestRectangle(const nsRect& aContainingRect = nsRect()) const; + nsRect GetLargestRectangle (const nsRect& aContainingRect = nsRect()) const; /** * Make sure the region has at most aMaxRects by adding area to it @@ -1884,7 +315,7 @@ public: * original region. The simplified region's bounding box will be * the same as for the current region. */ - void SimplifyOutward(uint32_t aMaxRects); + void SimplifyOutward (uint32_t aMaxRects); /** * Simplify the region by adding at most aThreshold area between spans of * rects. The simplified region will be a superset of the original region. @@ -1897,7 +328,7 @@ public: * it if necessary. The simplified region will be a subset of the * original region. */ - void SimplifyInward(uint32_t aMaxRects); + void SimplifyInward (uint32_t aMaxRects); /** * VisitEdges is a weird kind of function that we use for padding @@ -1911,11 +342,49 @@ public: * and specifies which kind of edge is being visited. x1, y1, x2, y2 * are the coordinates of the line. (x1 == x2) || (y1 == y2) */ - typedef void(*visitFn)(void *closure, VisitSide side, int x1, int y1, int x2, int y2); + typedef void (*visitFn)(void *closure, VisitSide side, int x1, int y1, int x2, int y2); void VisitEdges(visitFn, void *closure); nsCString ToString() const; + class RectIterator + { + int mCurrent; // Index of the current entry + int mLimit; // Index one past the final entry. + mutable nsRect mTmp; // The most recently gotten rectangle. + pixman_box32_t *mBoxes; + + public: + explicit RectIterator(const nsRegion& aRegion) + { + mCurrent = 0; + mBoxes = pixman_region32_rectangles(aRegion.Impl(), &mLimit); + // Work around pixman bug. Sometimes pixman creates regions with 1 rect + // that's empty. + if (mLimit == 1 && nsRegion::BoxToRect(mBoxes[0]).IsEmpty()) { + mLimit = 0; + } + } + + bool Done() const { return mCurrent == mLimit; } + + const nsRect& Get() const + { + MOZ_ASSERT(!Done()); + mTmp = nsRegion::BoxToRect(mBoxes[mCurrent]); + NS_ASSERTION(!mTmp.IsEmpty(), "Shouldn't return empty rect"); + return mTmp; + } + + void Next() + { + MOZ_ASSERT(!Done()); + mCurrent++; + } + }; + + RectIterator RectIter() const { return RectIterator(*this); } + static inline pixman_box32_t RectToBox(const nsRect &aRect) { pixman_box32_t box = { aRect.X(), aRect.Y(), aRect.XMost(), aRect.YMost() }; @@ -1927,300 +396,56 @@ public: pixman_box32_t box = { aRect.X(), aRect.Y(), aRect.XMost(), aRect.YMost() }; return box; } -private: - nsIntRegion ToPixels(nscoord aAppUnitsPerPixel, bool aOutsidePixels) const; - - nsRegion& Copy(const nsRegion& aRegion) - { - mBounds = aRegion.mBounds; - mBands = aRegion.mBands; - return *this; - } - - nsRegion& Copy(const nsRect& aRect) - { - mBands.Clear(); - mBounds = aRect; - return *this; - } - - void EnsureSimplified() { - if (mBands.Length() == 1 && mBands.begin()->mStrips.Length() == 1) { - mBands.Clear(); - } - } static inline nsRect BoxToRect(const pixman_box32_t &aBox) { return nsRect(aBox.x1, aBox.y1, - aBox.x2 - aBox.x1, - aBox.y2 - aBox.y1); + aBox.x2 - aBox.x1, + aBox.y2 - aBox.y1); } - void AddRect(const nsRect& aRect) +private: + pixman_region32_t mImpl; + +#ifndef MOZ_TREE_PIXMAN + // For compatibility with pixman versions older than 0.25.2. + static inline void + pixman_region32_clear(pixman_region32_t *region) { -#ifdef DEBUG_REGIONS - class OperationStringGeneratorAddRect : public OperationStringGenerator - { - public: - OperationStringGeneratorAddRect(nsRegion& aRegion, const nsRect& aRect) - : mRegion(&aRegion), mRegionCopy(aRegion), mRect(aRect) - { - aRegion.mCurrentOpGenerator = this; - } - virtual ~OperationStringGeneratorAddRect() - { - mRegion->mCurrentOpGenerator = nullptr; - } - - virtual void OutputOp() override - { - std::stringstream stream; - mRegionCopy.OutputToStream("r", stream); - stream << "r.OrWith(nsRect(" << mRect.X() << ", " << mRect.Y() << ", " << mRect.Width() << ", " << mRect.Height() << "));\n"; - gfxCriticalError() << stream.str(); - } - private: - nsRegion* mRegion; - nsRegion mRegionCopy; - nsRect mRect; - }; - - OperationStringGeneratorAddRect opGenerator(*this, aRect); + pixman_region32_fini(region); + pixman_region32_init(region); + } #endif + + nsIntRegion ToPixels(nscoord aAppUnitsPerPixel, bool aOutsidePixels) const; + + nsRegion& Copy (const nsRegion& aRegion) + { + pixman_region32_copy(&mImpl, aRegion.Impl()); + return *this; + } + + nsRegion& Copy (const nsRect& aRect) + { + // pixman needs to distinguish between an empty region and a region + // with one rect so that it can return a different number of rectangles. + // Empty rect: data = empty_box + // 1 rect: data = null + // >1 rect: data = rects if (aRect.IsEmpty()) { - return; - } - - if (aRect.Overflows()) { - // We don't accept rects which overflow. - gfxWarning() << "Passing overflowing rect to AddRect."; - return; - } - - if (mBands.IsEmpty()) { - if (mBounds.IsEmpty()) { - mBounds = aRect; - return; - } else if (mBounds.Contains(aRect)) { - return; - } - - mBands.AppendElement(Band(mBounds)); - } - - mBounds = aRect.UnsafeUnion(mBounds); - - size_t idx = 0; - - Strip strip(aRect.X(), aRect.XMost()); - Band remaining(aRect); - - while (idx != mBands.Length()) { - if (mBands[idx].bottom <= remaining.top) { - // This band lies wholly above aRect. - idx++; - continue; - } - - if (remaining.top >= remaining.bottom) { - AssertState(); - EnsureSimplified(); - return; - } - - if (mBands[idx].top >= remaining.bottom) { - // This band lies wholly below aRect. - break; - } - - if (mBands[idx].EqualStrips(remaining)) { - mBands[idx].top = std::min(mBands[idx].top, remaining.top); - // Nothing to do for this band. Just expand. - remaining.top = mBands[idx].bottom; - CompressBefore(idx); - idx++; - continue; - } - - if (mBands[idx].top > remaining.top) { - auto before = mBands.InsertElementAt(idx, remaining); - before->bottom = mBands[idx + 1].top; - remaining.top = before->bottom; - CompressBefore(idx); - idx++; - CompressBefore(idx); - continue; - } - - if (mBands[idx].ContainsStrip(strip)) { - remaining.top = mBands[idx].bottom; - idx++; - continue; - } - - // mBands[idx].top <= remaining.top. - - if (mBands[idx].top < remaining.top) { - auto before = mBands.InsertElementAt(idx, Band(mBands[idx])); - before->bottom = remaining.top; - idx++; - mBands[idx].top = remaining.top; - continue; - } - - // mBands[idx].top == remaining.top - if (mBands[idx].bottom > remaining.bottom) { - auto below = mBands.InsertElementAt(idx + 1, Band(mBands[idx])); - below->top = remaining.bottom; - mBands[idx].bottom = remaining.bottom; - } - - mBands[idx].InsertStrip(strip); - CompressBefore(idx); - remaining.top = mBands[idx].bottom; - idx++; - CompressBefore(idx); - } - - if (remaining.top < remaining.bottom) { - // We didn't find any bands that overlapped aRect. - if (idx) { - if (mBands[idx - 1].bottom == remaining.top && mBands[idx - 1].EqualStrips(remaining)) { - mBands[idx - 1].bottom = remaining.bottom; - CompressBefore(idx); - AssertState(); - EnsureSimplified(); - return; - } - } - mBands.InsertElementAt(idx, remaining); - idx++; - CompressBefore(idx); + pixman_region32_clear(&mImpl); } else { - CompressBefore(idx); + pixman_box32_t box = RectToBox(aRect); + pixman_region32_reset(&mImpl, &box); } - - AssertState(); - EnsureSimplified(); + return *this; } - // Most callers could probably do this on the fly, if this ever shows up - // in profiles we could optimize this. - nsRect CalculateBounds() const + pixman_region32_t* Impl() const { - if (mBands.IsEmpty()) { - return mBounds; - } - - int32_t top = mBands.begin()->top; - int32_t bottom = mBands.LastElement().bottom; - - int32_t leftMost = mBands.begin()->mStrips.begin()->left; - int32_t rightMost = mBands.begin()->mStrips.LastElement().right; - for (const Band& band : mBands) { - leftMost = std::min(leftMost, band.mStrips.begin()->left); - rightMost = std::max(rightMost, band.mStrips.LastElement().right); - } - - return nsRect(leftMost, top, rightMost - leftMost, bottom - top); + return const_cast(&mImpl); } - - static uint32_t ComputeMergedAreaIncrease(const Band& aTopBand, - const Band& aBottomBand); - - // Returns true if idx is now referring to the 'next' band - bool CompressAdjacentBands(size_t& aIdx) - { - if ((aIdx + 1) < mBands.Length()) { - if (mBands[aIdx + 1].top == mBands[aIdx].bottom && mBands[aIdx + 1].EqualStrips(mBands[aIdx])) { - mBands[aIdx].bottom = mBands[aIdx + 1].bottom; - mBands.RemoveElementAt(aIdx + 1); - } - } - if (aIdx) { - if (mBands[aIdx - 1].bottom == mBands[aIdx].top && mBands[aIdx].EqualStrips(mBands[aIdx - 1])) { - mBands[aIdx - 1].bottom = mBands[aIdx].bottom; - mBands.RemoveElementAt(aIdx); - return true; - } - } - return false; - } - - void CompressBefore(size_t& aIdx) - { - if (aIdx && aIdx < mBands.Length()) { - if (mBands[aIdx - 1].bottom == mBands[aIdx].top && mBands[aIdx - 1].EqualStrips(mBands[aIdx])) { - mBands[aIdx].top = mBands[aIdx - 1].top; - mBands.RemoveElementAt(aIdx - 1); - aIdx--; - } - } - } - - - BandArray mBands; - // Considering we only ever OR with nsRects, the bounds should fit in an nsRect as well. - nsRect mBounds; -#ifdef DEBUG_REGIONS - friend class OperationStringGenerator; - OperationStringGenerator* mCurrentOpGenerator; -#endif - -public: - class RectIterator - { - const nsRegion& mRegion; - typename BandArray::const_iterator mCurrentBand; - typename StripArray::const_iterator mCurrentStrip; - - public: - explicit RectIterator(const nsRegion& aRegion) - : mRegion(aRegion) - , mCurrentBand(aRegion.mBands.begin()) - { - mIsDone = mRegion.mBounds.IsEmpty(); - if (mCurrentBand != aRegion.mBands.end()) { - mCurrentStrip = mCurrentBand->mStrips.begin(); - } - } - - bool Done() const { return mIsDone; } - - const nsRect Get() const - { - if (mRegion.mBands.IsEmpty()) { - return mRegion.mBounds; - } - return nsRect(mCurrentStrip->left, mCurrentBand->top, - mCurrentStrip->right - mCurrentStrip->left, - mCurrentBand->bottom - mCurrentBand->top); - } - - void Next() - { - if (mRegion.mBands.IsEmpty()) { - mIsDone = true; - return; - } - - mCurrentStrip++; - if (mCurrentStrip == mCurrentBand->mStrips.end()) { - mCurrentBand++; - if (mCurrentBand != mRegion.mBands.end()) { - mCurrentStrip = mCurrentBand->mStrips.begin(); - } else { - mIsDone = true; - } - } - } - - bool mIsDone; - }; - - RectIterator RectIter() const { return RectIterator(*this); } }; namespace mozilla { @@ -2266,6 +491,11 @@ public: return stream << m.mImpl; } + void Swap(Derived* aOther) + { + mImpl.Swap(&aOther->mImpl); + } + void AndWith(const Derived& aOther) { And(This(), aOther); @@ -2409,7 +639,7 @@ public: } void MoveBy (Point aPt) { - mImpl.MoveBy (aPt.X(), aPt.Y()); + mImpl.MoveBy (aPt.x, aPt.y); } Derived MovedBy(int32_t aXOffset, int32_t aYOffset) const { @@ -2457,7 +687,7 @@ public: nsRegion ToAppUnits (nscoord aAppUnitsPerPixel) const { nsRegion result; - for (auto iter = RectIterator(*this); !iter.Done(); iter.Next()) { + for (auto iter = RectIter(); !iter.Done(); iter.Next()) { nsRect appRect = ::ToAppUnits(iter.Get(), aAppUnitsPerPixel); result.Or(result, appRect); } diff --git a/gfx/tests/gtest/TestRegion.cpp b/gfx/tests/gtest/TestRegion.cpp index e0b492cb5c58..405e16d0aa5d 100644 --- a/gfx/tests/gtest/TestRegion.cpp +++ b/gfx/tests/gtest/TestRegion.cpp @@ -16,8 +16,6 @@ using namespace std; using namespace mozilla::gfx; -//#define REGION_RANDOM_STRESS_TESTS - class TestLargestRegion { public: static void TestSingleRect(nsRect r) { @@ -184,724 +182,6 @@ TEST(Gfx, RegionScaleToInside) { } -TEST(Gfx, RegionOrWith) { - { - nsRegion r(nsRect(79, 31, 75, 12)); - r.OrWith(nsRect(22, 43, 132, 5)); - r.OrWith(nsRect(22, 48, 125, 3)); - r.OrWith(nsRect(22, 51, 96, 20)); - r.OrWith(nsRect(34, 71, 1, 14)); - r.OrWith(nsRect(26, 85, 53, 1)); - r.OrWith(nsRect(26, 86, 53, 4)); - r.OrWith(nsRect(96, 86, 30, 4)); - r.OrWith(nsRect(34, 90, 1, 2)); - r.OrWith(nsRect(96, 90, 30, 2)); - r.OrWith(nsRect(34, 92, 1, 3)); - r.OrWith(nsRect(49, 92, 34, 3)); - r.OrWith(nsRect(96, 92, 30, 3)); - r.OrWith(nsRect(34, 95, 1, 17)); - r.OrWith(nsRect(49, 95, 77, 17)); - r.OrWith(nsRect(34, 112, 1, 12)); - r.OrWith(nsRect(75, 112, 51, 12)); - r.OrWith(nsRect(34, 124, 1, 10)); - r.OrWith(nsRect(75, 124, 44, 10)); - r.OrWith(nsRect(34, 134, 1, 19)); - r.OrWith(nsRect(22, 17, 96, 27)); - } - { - nsRegion r(nsRect(0, 8, 257, 32)); - r.OrWith(nsRect(3702, 8, 138, 32)); - r.OrWith(nsRect(0, 40, 225, 1)); - r.OrWith(nsRect(3702, 40, 138, 1)); - r.OrWith(nsRect(0, 41, 101, 40)); - r.OrWith(nsRect(69, 41, 32, 40)); - } - { - nsRegion r(nsRect(79, 56, 8, 32)); - r.OrWith(nsRect(5, 94, 23, 81)); - r.OrWith(nsRect(56, 29, 91, 81)); - } - { - nsRegion r(nsRect(0, 82, 3840, 2046)); - r.OrWith(nsRect(0, 0, 3840, 82)); - } - { - nsRegion r(nsRect(2, 5, 600, 28)); - r.OrWith(nsRect(2, 82, 600, 19)); - r.OrWith(nsRect(2, 33, 600, 49)); - } - { - nsRegion r(nsRect(3823, 0, 17, 17)); - r.OrWith(nsRect(3823, 2029, 17, 17)); - r.OrWith(nsRect(3823, 0, 17, 2046)); - } - { - nsRegion r(nsRect(1036, 4, 32, 21)); - r.OrWith(nsRect(1070, 4, 66, 21)); - r.OrWith(nsRect(40, 5, 0, 33)); - } - { - nsRegion r(nsRect(0, 0, 1024, 1152)); - r.OrWith(nsRect(-335802, -1073741824, 1318851, 1860043520)); - } - { - nsRegion r(nsRect(0, 0, 800, 1000)); - r.OrWith(nsRect(0, 0, 536870912, 1073741824)); - } - { - nsRegion r(nsRect(53, 2, 52, 3)); - r.OrWith(nsRect(45, 5, 60, 16)); - r.OrWith(nsRect(16, 21, 8, 1)); - r.OrWith(nsRect(45, 21, 12, 1)); - r.OrWith(nsRect(16, 22, 8, 5)); - r.OrWith(nsRect(33, 22, 52, 5)); - r.OrWith(nsRect(16, 27, 8, 7)); - r.OrWith(nsRect(33, 27, 66, 7)); - r.OrWith(nsRect(0, 34, 99, 1)); - r.OrWith(nsRect(0, 35, 159, 27)); - r.OrWith(nsRect(0, 62, 122, 3)); - r.OrWith(nsRect(0, 65, 85, 11)); - r.OrWith(nsRect(91, 65, 97, 11)); - r.OrWith(nsRect(11, 76, 74, 2)); - r.OrWith(nsRect(91, 76, 97, 2)); - r.OrWith(nsRect(11, 78, 74, 12)); - r.OrWith(nsRect(11, 90, 13, 3)); - r.OrWith(nsRect(33, 90, 108, 3)); - r.OrWith(nsRect(16, 93, 8, 22)); - r.OrWith(nsRect(33, 93, 108, 22)); - r.OrWith(nsRect(16, 115, 8, 1)); - r.OrWith(nsRect(58, 115, 83, 1)); - r.OrWith(nsRect(58, 116, 83, 25)); - r.OrWith(nsRect(59, 37, 88, 92)); - } -#ifdef REGION_RANDOM_STRESS_TESTS - const uint32_t TestIterations = 100000; - const uint32_t RectsPerTest = 100; - - nsRect rects[RectsPerTest]; - - for (uint32_t i = 0; i < TestIterations; i++) { - nsRegion r; - for (uint32_t n = 0; n < RectsPerTest; n++) { - rects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); - } - r.SetEmpty(); - for (uint32_t n = 0; n < RectsPerTest; n++) { - r.OrWith(rects[n]); - } - for (uint32_t n = 0; n < RectsPerTest; n++) { - EXPECT_TRUE(r.Contains(rects[n])); - } - } -#endif -} - -TEST(Gfx, RegionSubWith) { - { - nsRegion r1(nsRect(0, 0, 100, 50)); - r1.OrWith(nsRect(50, 50, 50, 50)); - nsRegion r2(nsRect(0, 0, 100, 50)); - r2.OrWith(nsRect(50, 50, 50, 50)); - r1.SubWith(r2); - EXPECT_FALSE(r1.Contains(1, 1)); - } - { - nsRegion r1(nsRect(0, 0, 800, 1000)); - nsRegion r2(nsRect(8, 108, 22, 20)); - r2.OrWith(nsRect(91, 138, 17, 18)); - r1.SubWith(r2); - EXPECT_TRUE(r1.Contains(400, 130)); - } - { - nsRegion r1(nsRect(392, 2, 28, 7)); - r1.OrWith(nsRect(115, 9, 305, 16)); - r1.OrWith(nsRect(392, 25, 28, 5)); - r1.OrWith(nsRect(0, 32, 1280, 41)); - nsRegion r2(nsRect(0, 0, 1280, 9)); - r2.OrWith(nsRect(0, 9, 115, 16)); - r2.OrWith(nsRect(331, 9, 949, 16)); - r2.OrWith(nsRect(0, 25, 1280, 7)); - r2.OrWith(nsRect(331, 32, 124, 1)); - r1.SubWith(r2); - EXPECT_FALSE(r1.Contains(350, 15)); - } - { - nsRegion r1(nsRect(552, 0, 2, 2)); - r1.OrWith(nsRect(362, 2, 222, 28)); - r1.OrWith(nsRect(552, 30, 2, 2)); - r1.OrWith(nsRect(0, 32, 1280, 41)); - nsRegion r2(nsRect(512, 0, 146, 9)); - r2.OrWith(nsRect(340, 9, 318, 16)); - r2.OrWith(nsRect(512, 25, 146, 8)); - r1.SubWith(r2); - EXPECT_FALSE(r1.Contains(350, 15)); - } - { - nsRegion r(nsRect(0, 0, 229380, 6780)); - r.OrWith(nsRect(76800, 6780, 76800, 4440)); - r.OrWith(nsRect(76800, 11220, 44082, 1800)); - r.OrWith(nsRect(122682, 11220, 30918, 1800)); - r.OrWith(nsRect(76800, 13020, 76800, 2340)); - r.OrWith(nsRect(85020, 15360, 59340, 75520)); - r.OrWith(nsRect(85020, 90880, 38622, 11332)); - r.OrWith(nsRect(143789, 90880, 571, 11332)); - r.OrWith(nsRect(85020, 102212, 59340, 960)); - r.OrWith(nsRect(85020, 103172, 38622, 1560)); - r.OrWith(nsRect(143789, 103172, 571, 1560)); - r.OrWith(nsRect(85020, 104732, 59340, 12292)); - r.OrWith(nsRect(85020, 117024, 38622, 1560)); - r.OrWith(nsRect(143789, 117024, 571, 1560)); - r.OrWith(nsRect(85020, 118584, 59340, 11976)); - r.SubWith(nsRect(123642, 89320, 20147, 1560)); - } - { - nsRegion r(nsRect(0, 0, 9480, 12900)); - r.OrWith(nsRect(0, 12900, 8460, 1020)); - r.SubWith(nsRect(8460, 0, 1020, 12900)); - } - { - nsRegion r1(nsRect(99, 1, 51, 2)); - r1.OrWith(nsRect(85, 3, 65, 1)); - r1.OrWith(nsRect(10, 4, 66, 5)); - r1.OrWith(nsRect(85, 4, 37, 5)); - r1.OrWith(nsRect(10, 9, 112, 3)); - r1.OrWith(nsRect(1, 12, 121, 1)); - r1.OrWith(nsRect(1, 13, 139, 3)); - r1.OrWith(nsRect(0, 16, 140, 3)); - r1.OrWith(nsRect(0, 19, 146, 3)); - r1.OrWith(nsRect(0, 22, 149, 2)); - r1.OrWith(nsRect(0, 24, 154, 2)); - r1.OrWith(nsRect(0, 26, 160, 23)); - r1.OrWith(nsRect(0, 49, 162, 31)); - r1.OrWith(nsRect(0, 80, 171, 19)); - r1.OrWith(nsRect(0, 99, 173, 11)); - r1.OrWith(nsRect(2, 110, 171, 6)); - r1.OrWith(nsRect(6, 116, 165, 5)); - r1.OrWith(nsRect(8, 121, 163, 1)); - r1.OrWith(nsRect(13, 122, 158, 11)); - r1.OrWith(nsRect(14, 133, 157, 23)); - r1.OrWith(nsRect(29, 156, 142, 10)); - r1.OrWith(nsRect(37, 166, 134, 6)); - r1.OrWith(nsRect(55, 172, 4, 4)); - r1.OrWith(nsRect(83, 172, 88, 4)); - r1.OrWith(nsRect(55, 176, 4, 2)); - r1.OrWith(nsRect(89, 176, 6, 2)); - r1.OrWith(nsRect(89, 178, 6, 4)); - nsRegion r2(nsRect(63, 11, 39, 11)); - r2.OrWith(nsRect(63, 22, 99, 16)); - r2.OrWith(nsRect(37, 38, 125, 61)); - r2.OrWith(nsRect(45, 99, 117, 8)); - r2.OrWith(nsRect(47, 107, 115, 7)); - r2.OrWith(nsRect(47, 114, 66, 1)); - r2.OrWith(nsRect(49, 115, 64, 2)); - r2.OrWith(nsRect(49, 117, 54, 30)); - r1.SubWith(r2); - } - { - nsRegion r1(nsRect(95, 2, 47, 1)); - r1.OrWith(nsRect(62, 3, 80, 2)); - r1.OrWith(nsRect(1, 5, 18, 3)); - r1.OrWith(nsRect(48, 5, 94, 3)); - r1.OrWith(nsRect(1, 8, 18, 3)); - r1.OrWith(nsRect(23, 8, 119, 3)); - r1.OrWith(nsRect(1, 11, 172, 9)); - r1.OrWith(nsRect(1, 20, 18, 8)); - r1.OrWith(nsRect(20, 20, 153, 8)); - r1.OrWith(nsRect(1, 28, 172, 13)); - r1.OrWith(nsRect(1, 41, 164, 1)); - r1.OrWith(nsRect(1, 42, 168, 1)); - r1.OrWith(nsRect(0, 43, 169, 15)); - r1.OrWith(nsRect(1, 58, 168, 26)); - r1.OrWith(nsRect(1, 84, 162, 2)); - r1.OrWith(nsRect(1, 86, 165, 23)); - r1.OrWith(nsRect(1, 109, 162, 23)); - r1.OrWith(nsRect(1, 132, 152, 4)); - r1.OrWith(nsRect(1, 136, 150, 12)); - r1.OrWith(nsRect(12, 148, 139, 4)); - r1.OrWith(nsRect(12, 152, 113, 2)); - r1.OrWith(nsRect(14, 154, 31, 3)); - r1.OrWith(nsRect(82, 154, 43, 3)); - r1.OrWith(nsRect(17, 157, 13, 19)); - r1.OrWith(nsRect(82, 157, 43, 19)); - r1.OrWith(nsRect(17, 176, 13, 16)); - nsRegion r2(nsRect(97, 9, 6, 10)); - r2.OrWith(nsRect(71, 19, 32, 2)); - r2.OrWith(nsRect(20, 21, 83, 2)); - r2.OrWith(nsRect(2, 23, 101, 9)); - r2.OrWith(nsRect(2, 32, 98, 1)); - r2.OrWith(nsRect(2, 33, 104, 5)); - r2.OrWith(nsRect(2, 38, 118, 2)); - r2.OrWith(nsRect(15, 40, 9, 11)); - r2.OrWith(nsRect(36, 40, 84, 11)); - r2.OrWith(nsRect(4, 51, 116, 33)); - r2.OrWith(nsRect(4, 84, 159, 8)); - r2.OrWith(nsRect(4, 92, 116, 13)); - r2.OrWith(nsRect(15, 105, 9, 7)); - r2.OrWith(nsRect(36, 105, 84, 7)); - r2.OrWith(nsRect(36, 112, 84, 22)); - r2.OrWith(nsRect(71, 134, 39, 46)); - r1.SubWith(r2); - } -#ifdef REGION_RANDOM_STRESS_TESTS - const uint32_t TestIterations = 100000; - const uint32_t RectsPerTest = 100; - const uint32_t SubRectsPerTest = 10; - - nsRect rects[RectsPerTest]; - nsRect subRects[SubRectsPerTest]; - - for (uint32_t i = 0; i < TestIterations; i++) { - nsRegion r; - for (uint32_t n = 0; n < RectsPerTest; n++) { - rects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); - } - r.SetEmpty(); - for (uint32_t n = 0; n < RectsPerTest; n++) { - r.OrWith(rects[n]); - } - for (uint32_t n = 0; n < RectsPerTest; n++) { - EXPECT_TRUE(r.Contains(rects[n])); - } - for (uint32_t n = 0; n < SubRectsPerTest; n++) { - subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); - } - for (uint32_t n = 0; n < SubRectsPerTest; n++) { - r.SubWith(subRects[n]); - } - for (uint32_t n = 0; n < SubRectsPerTest; n++) { - EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].y)); - EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].YMost() - 1)); - EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1)); - EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].Y())); - } - } - for (uint32_t i = 0; i < TestIterations; i++) { - nsRegion r; - for (uint32_t n = 0; n < RectsPerTest; n++) { - rects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); - } - r.SetEmpty(); - for (uint32_t n = 0; n < RectsPerTest; n++) { - r.OrWith(rects[n]); - } - for (uint32_t n = 0; n < RectsPerTest; n++) { - EXPECT_TRUE(r.Contains(rects[n])); - } - for (uint32_t n = 0; n < SubRectsPerTest; n++) { - subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); - } - nsRegion r2; - for (uint32_t n = 0; n < SubRectsPerTest; n++) { - r2.OrWith(subRects[n]); - } - r.SubWith(r2); - - for (uint32_t n = 0; n < SubRectsPerTest; n++) { - EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].y)); - EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].YMost() - 1)); - EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1)); - EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].Y())); - } - } - for (uint32_t i = 0; i < TestIterations; i++) { - nsRegion r(nsRect(-1, -1, 202, 202)); - for (uint32_t n = 0; n < SubRectsPerTest; n++) { - subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); - r.SubWith(subRects[n]); - } - for (uint32_t n = 0; n < SubRectsPerTest; n++) { - EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].y)); - EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].YMost() - 1)); - EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1)); - EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].Y())); - } - EXPECT_TRUE(r.Contains(-1, -1)); - EXPECT_TRUE(r.Contains(-1, 200)); - EXPECT_TRUE(r.Contains(200, -1)); - EXPECT_TRUE(r.Contains(200, 200)); - } - for (uint32_t i = 0; i < TestIterations; i++) { - nsRegion r(nsRect(-1, -1, 202, 202)); - nsRegion r2; - for (uint32_t n = 0; n < SubRectsPerTest; n++) { - subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); - r2.OrWith(subRects[n]); - } - r.SubWith(r2); - for (uint32_t n = 0; n < SubRectsPerTest; n++) { - EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].y)); - EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].YMost() - 1)); - EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1)); - EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].Y())); - } - EXPECT_TRUE(r.Contains(-1, -1)); - EXPECT_TRUE(r.Contains(-1, 200)); - EXPECT_TRUE(r.Contains(200, -1)); - EXPECT_TRUE(r.Contains(200, 200)); - } -#endif -} -TEST(Gfx, RegionSub) { - { - nsRegion r1(nsRect(0, 0, 100, 50)); - r1.OrWith(nsRect(50, 50, 50, 50)); - nsRegion r2(nsRect(0, 0, 100, 50)); - r2.OrWith(nsRect(50, 50, 50, 50)); - nsRegion r3; - r3.Sub(r1, r2); - EXPECT_FALSE(r3.Contains(1, 1)); - } - { - nsRegion r1(nsRect(0, 0, 800, 1000)); - nsRegion r2(nsRect(8, 108, 22, 20)); - r2.OrWith(nsRect(91, 138, 17, 18)); - nsRegion r3; - r3.Sub(r1, r2); - EXPECT_TRUE(r3.Contains(400, 130)); - } - { - nsRegion r1(nsRect(392, 2, 28, 7)); - r1.OrWith(nsRect(115, 9, 305, 16)); - r1.OrWith(nsRect(392, 25, 28, 5)); - r1.OrWith(nsRect(0, 32, 1280, 41)); - nsRegion r2(nsRect(0, 0, 1280, 9)); - r2.OrWith(nsRect(0, 9, 115, 16)); - r2.OrWith(nsRect(331, 9, 949, 16)); - r2.OrWith(nsRect(0, 25, 1280, 7)); - r2.OrWith(nsRect(331, 32, 124, 1)); - nsRegion r3; - r3.Sub(r1, r2); - EXPECT_FALSE(r3.Contains(350, 15)); - } - { - nsRegion r1(nsRect(552, 0, 2, 2)); - r1.OrWith(nsRect(362, 2, 222, 28)); - r1.OrWith(nsRect(552, 30, 2, 2)); - r1.OrWith(nsRect(0, 32, 1280, 41)); - nsRegion r2(nsRect(512, 0, 146, 9)); - r2.OrWith(nsRect(340, 9, 318, 16)); - r2.OrWith(nsRect(512, 25, 146, 8)); - nsRegion r3; - r3.Sub(r1, r2); - EXPECT_FALSE(r3.Contains(350, 15)); - } - { - nsRegion r1(nsRect(0, 0, 1265, 1024)); - nsRegion r2(nsRect(1265, 0, 15, 685)); - r2.OrWith(nsRect(0, 714, 1280, 221)); - nsRegion r3; - r3.Sub(r1, r2); - } - { - nsRegion r1(nsRect(6, 0, 64, 1)); - r1.OrWith(nsRect(6, 1, 67, 1)); - r1.OrWith(nsRect(6, 2, 67, 2)); - r1.OrWith(nsRect(79, 2, 67, 2)); - r1.OrWith(nsRect(6, 4, 67, 1)); - r1.OrWith(nsRect(79, 4, 98, 1)); - r1.OrWith(nsRect(6, 5, 171, 18)); - r1.OrWith(nsRect(1, 23, 176, 3)); - r1.OrWith(nsRect(1, 26, 178, 5)); - r1.OrWith(nsRect(1, 31, 176, 9)); - r1.OrWith(nsRect(0, 40, 177, 57)); - r1.OrWith(nsRect(0, 97, 176, 33)); - r1.OrWith(nsRect(0, 130, 12, 17)); - r1.OrWith(nsRect(15, 130, 161, 17)); - r1.OrWith(nsRect(0, 147, 12, 5)); - r1.OrWith(nsRect(15, 147, 111, 5)); - r1.OrWith(nsRect(0, 152, 12, 7)); - r1.OrWith(nsRect(17, 152, 109, 7)); - r1.OrWith(nsRect(0, 159, 12, 2)); - r1.OrWith(nsRect(17, 159, 98, 2)); - r1.OrWith(nsRect(17, 161, 98, 9)); - r1.OrWith(nsRect(27, 170, 63, 21)); - nsRegion r2(nsRect(9, 9, 37, 17)); - r2.OrWith(nsRect(92, 9, 26, 17)); - r2.OrWith(nsRect(9, 26, 37, 9)); - r2.OrWith(nsRect(84, 26, 65, 9)); - r2.OrWith(nsRect(9, 35, 37, 2)); - r2.OrWith(nsRect(51, 35, 98, 2)); - r2.OrWith(nsRect(51, 37, 98, 11)); - r2.OrWith(nsRect(51, 48, 78, 4)); - r2.OrWith(nsRect(87, 52, 42, 7)); - r2.OrWith(nsRect(19, 59, 12, 5)); - r2.OrWith(nsRect(87, 59, 42, 5)); - r2.OrWith(nsRect(19, 64, 12, 9)); - r2.OrWith(nsRect(32, 64, 97, 9)); - r2.OrWith(nsRect(19, 73, 12, 2)); - r2.OrWith(nsRect(32, 73, 104, 2)); - r2.OrWith(nsRect(19, 75, 117, 5)); - r2.OrWith(nsRect(18, 80, 118, 5)); - r2.OrWith(nsRect(18, 85, 111, 38)); - r2.OrWith(nsRect(87, 123, 42, 11)); - nsRegion r3; - r3.Sub(r1, r2); - } - { - nsRegion r1(nsRect(27, 0, 39, 1)); - r1.OrWith(nsRect(86, 0, 22, 1)); - r1.OrWith(nsRect(27, 1, 43, 1)); - r1.OrWith(nsRect(86, 1, 22, 1)); - r1.OrWith(nsRect(27, 2, 43, 1)); - r1.OrWith(nsRect(86, 2, 75, 1)); - r1.OrWith(nsRect(12, 3, 58, 1)); - r1.OrWith(nsRect(86, 3, 75, 1)); - r1.OrWith(nsRect(12, 4, 149, 5)); - r1.OrWith(nsRect(0, 9, 161, 9)); - r1.OrWith(nsRect(0, 18, 167, 17)); - r1.OrWith(nsRect(0, 35, 171, 5)); - r1.OrWith(nsRect(0, 40, 189, 28)); - r1.OrWith(nsRect(0, 68, 171, 16)); - r1.OrWith(nsRect(4, 84, 167, 5)); - r1.OrWith(nsRect(4, 89, 177, 9)); - r1.OrWith(nsRect(1, 98, 180, 59)); - r1.OrWith(nsRect(4, 157, 177, 1)); - r1.OrWith(nsRect(4, 158, 139, 15)); - r1.OrWith(nsRect(17, 173, 126, 2)); - r1.OrWith(nsRect(20, 175, 123, 2)); - r1.OrWith(nsRect(20, 177, 118, 6)); - r1.OrWith(nsRect(20, 183, 84, 2)); - nsRegion r2(nsRect(64, 2, 30, 6)); - r2.OrWith(nsRect(26, 11, 41, 17)); - r2.OrWith(nsRect(19, 28, 48, 23)); - r2.OrWith(nsRect(19, 51, 76, 8)); - r2.OrWith(nsRect(4, 59, 91, 31)); - r2.OrWith(nsRect(19, 90, 76, 29)); - r2.OrWith(nsRect(33, 119, 62, 25)); - r2.OrWith(nsRect(33, 144, 4, 21)); - nsRegion r3; - r3.Sub(r1, r2); - } -#ifdef REGION_RANDOM_STRESS_TESTS - const uint32_t TestIterations = 100000; - const uint32_t RectsPerTest = 100; - const uint32_t SubRectsPerTest = 10; - - nsRect rects[RectsPerTest]; - nsRect subRects[SubRectsPerTest]; - - for (uint32_t i = 0; i < TestIterations; i++) { - nsRegion r; - for (uint32_t n = 0; n < RectsPerTest; n++) { - rects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); - } - r.SetEmpty(); - for (uint32_t n = 0; n < RectsPerTest; n++) { - r.OrWith(rects[n]); - } - for (uint32_t n = 0; n < RectsPerTest; n++) { - EXPECT_TRUE(r.Contains(rects[n])); - } - for (uint32_t n = 0; n < SubRectsPerTest; n++) { - subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); - } - nsRegion r2; - for (uint32_t n = 0; n < SubRectsPerTest; n++) { - r2.OrWith(subRects[n]); - } - nsRegion r3; - r3.Sub(r, r2); - - for (uint32_t n = 0; n < SubRectsPerTest; n++) { - EXPECT_FALSE(r3.Contains(subRects[n].x, subRects[n].y)); - EXPECT_FALSE(r3.Contains(subRects[n].x, subRects[n].YMost() - 1)); - EXPECT_FALSE(r3.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1)); - EXPECT_FALSE(r3.Contains(subRects[n].XMost() - 1, subRects[n].Y())); - } - } - for (uint32_t i = 0; i < TestIterations; i++) { - nsRegion r(nsRect(-1, -1, 202, 202)); - nsRegion r2; - for (uint32_t n = 0; n < SubRectsPerTest; n++) { - subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); - r2.OrWith(subRects[n]); - } - nsRegion r3; - r3.Sub(r, r2); - for (uint32_t n = 0; n < SubRectsPerTest; n++) { - EXPECT_FALSE(r3.Contains(subRects[n].x, subRects[n].y)); - EXPECT_FALSE(r3.Contains(subRects[n].x, subRects[n].YMost() - 1)); - EXPECT_FALSE(r3.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1)); - EXPECT_FALSE(r3.Contains(subRects[n].XMost() - 1, subRects[n].Y())); - } - EXPECT_TRUE(r3.Contains(-1, -1)); - EXPECT_TRUE(r3.Contains(-1, 200)); - EXPECT_TRUE(r3.Contains(200, -1)); - EXPECT_TRUE(r3.Contains(200, 200)); - } -#endif -} - -TEST(Gfx, RegionAndWith) { - { - nsRegion r(nsRect(20, 0, 20, 20)); - r.OrWith(nsRect(0, 20, 40, 20)); - r.AndWith(nsRect(0, 0, 5, 5)); - EXPECT_FALSE(r.Contains(1, 1)); - } - { - nsRegion r1(nsRect(512, 1792, 256, 256)); - nsRegion r2(nsRect(17, 1860, 239, 35)); - r2.OrWith(nsRect(17, 1895, 239, 7)); - r2.OrWith(nsRect(768, 1895, 154, 7)); - r2.OrWith(nsRect(17, 1902, 905, 483)); - r1.AndWith(r2); - } -#ifdef REGION_RANDOM_STRESS_TESTS - const uint32_t TestIterations = 100000; - const uint32_t RectsPerTest = 50; - const uint32_t pointsTested = 100; - - { - nsRect rectsSet1[RectsPerTest]; - nsRect rectsSet2[RectsPerTest]; - - for (uint32_t i = 0; i < TestIterations; i++) { - nsRegion r1; - nsRegion r2; - for (uint32_t n = 0; n < RectsPerTest; n++) { - rectsSet1[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); - rectsSet2[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); - r1.OrWith(rectsSet1[n]); - r2.OrWith(rectsSet1[n]); - } - - nsRegion r3 = r1; - r3.AndWith(r2); - - for (uint32_t n = 0; n < pointsTested; n++) { - nsPoint p(rand() % 200, rand() % 200); - if (r1.Contains(p.x, p.y) && r2.Contains(p.x, p.y)) { - EXPECT_TRUE(r3.Contains(p.x, p.y)); - } else { - EXPECT_FALSE(r3.Contains(p.x, p.y)); - } - } - } - } - - { - nsRect rectsSet[RectsPerTest]; - nsRect testRect; - for (uint32_t i = 0; i < TestIterations; i++) { - nsRegion r; - for (uint32_t n = 0; n < RectsPerTest; n++) { - rectsSet[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); - } - for (uint32_t n = 0; n < RectsPerTest; n++) { - r.OrWith(rectsSet[n]); - } - testRect.SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); - - nsRegion r2 = r; - r2.AndWith(testRect); - - for (uint32_t n = 0; n < pointsTested; n++) { - nsPoint p(rand() % 200, rand() % 200); - if (r.Contains(p.x, p.y) && testRect.Contains(p.x, p.y)) { - EXPECT_TRUE(r2.Contains(p.x, p.y)); - } else { - EXPECT_FALSE(r2.Contains(p.x, p.y)); - } - } - } - } -#endif -} - -TEST(Gfx, RegionAnd) { - { - nsRegion r(nsRect(20, 0, 20, 20)); - r.OrWith(nsRect(0, 20, 40, 20)); - nsRegion r2; - r2.And(r, nsRect(0, 0, 5, 5)); - EXPECT_FALSE(r.Contains(1, 1)); - } - { - nsRegion r(nsRect(51, 2, 57, 5)); - r.OrWith(nsRect(36, 7, 72, 4)); - r.OrWith(nsRect(36, 11, 25, 1)); - r.OrWith(nsRect(69, 12, 6, 4)); - r.OrWith(nsRect(37, 16, 54, 2)); - r.OrWith(nsRect(37, 18, 82, 2)); - r.OrWith(nsRect(10, 20, 109, 3)); - r.OrWith(nsRect(1, 23, 136, 21)); - r.OrWith(nsRect(1, 44, 148, 2)); - r.OrWith(nsRect(1, 46, 176, 31)); - r.OrWith(nsRect(6, 77, 171, 1)); - r.OrWith(nsRect(5, 78, 172, 30)); - r.OrWith(nsRect(5, 108, 165, 45)); - r.OrWith(nsRect(5, 153, 61, 5)); - r.OrWith(nsRect(72, 153, 98, 5)); - r.OrWith(nsRect(38, 158, 25, 4)); - r.OrWith(nsRect(72, 158, 98, 4)); - r.OrWith(nsRect(58, 162, 5, 8)); - r.OrWith(nsRect(72, 162, 98, 8)); - r.OrWith(nsRect(72, 170, 98, 5)); - nsRegion r2; - r.And(r2, nsRect(18, 78, 53, 45)); - } -#ifdef REGION_RANDOM_STRESS_TESTS - const uint32_t TestIterations = 100000; - const uint32_t RectsPerTest = 50; - const uint32_t pointsTested = 100; - - { - nsRect rectsSet1[RectsPerTest]; - nsRect rectsSet2[RectsPerTest]; - - for (uint32_t i = 0; i < TestIterations; i++) { - nsRegion r1; - nsRegion r2; - for (uint32_t n = 0; n < RectsPerTest; n++) { - rectsSet1[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); - rectsSet2[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); - r1.OrWith(rectsSet1[n]); - r2.OrWith(rectsSet1[n]); - } - - nsRegion r3; - r3.And(r1, r2); - - for (uint32_t n = 0; n < pointsTested; n++) { - nsPoint p(rand() % 200, rand() % 200); - if (r1.Contains(p.x, p.y) && r2.Contains(p.x, p.y)) { - EXPECT_TRUE(r3.Contains(p.x, p.y)); - } else { - EXPECT_FALSE(r3.Contains(p.x, p.y)); - } - } - } - } - - { - nsRect rectsSet[RectsPerTest]; - nsRect testRect; - for (uint32_t i = 0; i < TestIterations; i++) { - nsRegion r; - for (uint32_t n = 0; n < RectsPerTest; n++) { - rectsSet[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); - } - for (uint32_t n = 0; n < RectsPerTest; n++) { - r.OrWith(rectsSet[n]); - } - testRect.SetRect(rand() % 100, rand() % 100, rand() % 99 + 1, rand() % 99 + 1); - - nsRegion r2; - r2.And(r, testRect); - - for (uint32_t n = 0; n < pointsTested; n++) { - nsPoint p(rand() % 200, rand() % 200); - if (r.Contains(p.x, p.y) && testRect.Contains(p.x, p.y)) { - EXPECT_TRUE(r2.Contains(p.x, p.y)); - } else { - EXPECT_FALSE(r2.Contains(p.x, p.y)); - } - } - } - } -#endif -} TEST(Gfx, RegionSimplify) { { // ensure simplify works on a single rect diff --git a/layout/painting/FrameLayerBuilder.cpp b/layout/painting/FrameLayerBuilder.cpp index a263ee5713b1..cd64c556ece2 100644 --- a/layout/painting/FrameLayerBuilder.cpp +++ b/layout/painting/FrameLayerBuilder.cpp @@ -5934,7 +5934,7 @@ FrameLayerBuilder::RecomputeVisibilityForItems(nsTArray& aI newVisible.Sub(visible, removed); // Don't let the visible region get too complex. if (newVisible.GetNumRects() <= 15) { - visible = Move(newVisible); + visible = newVisible; } } } diff --git a/layout/svg/nsFilterInstance.cpp b/layout/svg/nsFilterInstance.cpp index 51202c689be4..faeb079a0e71 100644 --- a/layout/svg/nsFilterInstance.cpp +++ b/layout/svg/nsFilterInstance.cpp @@ -608,8 +608,7 @@ nsFilterInstance::FrameSpaceToFilterSpace(const nsRegion* aRegion) const nsIntRegion result; for (auto iter = aRegion->RectIter(); !iter.Done(); iter.Next()) { // FrameSpaceToFilterSpace rounds out, so this works. - nsRect rect = iter.Get(); - result.Or(result, FrameSpaceToFilterSpace(&rect)); + result.Or(result, FrameSpaceToFilterSpace(&iter.Get())); } return result; } From afd77f7fd57e00135bfab6a9479f9ecdeed5f22d Mon Sep 17 00:00:00 2001 From: Jeff Gilbert Date: Thu, 22 Mar 2018 15:48:52 -0700 Subject: [PATCH 19/25] Bug 1444274 - Require GCC 6.1+. - r=glandium MozReview-Commit-ID: 7alNSIhhbLI --- build/moz.configure/toolchain.configure | 25 +- .../configure/test_toolchain_configure.py | 496 +++++++++--------- taskcluster/ci/build/linux.yml | 4 +- 3 files changed, 271 insertions(+), 254 deletions(-) diff --git a/build/moz.configure/toolchain.configure b/build/moz.configure/toolchain.configure index fa066d5a9e35..61b0b612c87e 100755 --- a/build/moz.configure/toolchain.configure +++ b/build/moz.configure/toolchain.configure @@ -491,7 +491,6 @@ def check_compiler(compiler, language, target): # Note: MSVC, while supporting C++14, still reports 199711L for __cplusplus. # Note: this is a strict version check because we used to always add # -std=gnu++14. - draft_cxx14_version = 201300 cxx14_version = 201402 if info.language == 'C++': if info.type == 'clang' and info.language_version != cxx14_version: @@ -500,12 +499,6 @@ def check_compiler(compiler, language, target): # with appropriate checks. elif info.type == 'clang-cl' and info.language_version != cxx14_version: append_flag('-std=c++14') - # GCC 4.9 indicates that it implements draft C++14 features - # instead of the full language. - elif info.type == 'gcc' and \ - info.language_version not in (draft_cxx14_version, - cxx14_version): - append_flag('-std=gnu++14') # We force clang-cl to emulate Visual C++ 2017 version 15.6.0 msvc_version = '19.13.26128' @@ -904,14 +897,14 @@ def compiler(language, host_or_target, c_compiler=None, other_compiler=None, # Check the compiler version here instead of in `compiler_version` so # that the `checking` message doesn't pretend the compiler can be used # to then bail out one line later. - if info.type == 'gcc' and info.version < '4.9.0': - raise FatalCheckError( - 'Only GCC 4.9 or newer is supported (found version %s).' - % info.version) - - if info.type == 'gcc' and host_or_target.os == 'Android': - raise FatalCheckError('GCC is not supported on Android.\n' - 'Please use clang from the Android NDK instead.') + if info.type == 'gcc': + if host_or_target.os == 'Android': + raise FatalCheckError('GCC is not supported on Android.\n' + 'Please use clang from the Android NDK instead.') + if info.version < '6.1.0': + raise FatalCheckError( + 'Only GCC 6.1 or newer is supported (found version %s).' + % info.version) # If you want to bump the version check here search for # cxx_alignof above, and see the associated comment. @@ -1139,7 +1132,7 @@ def color_cflags(info): # value changes to e.g. "=always", exact string match may fail and # multiple color flags could be added. So examine downstream consumers # before adding flags to return values. - if info.type == 'gcc' and info.version >= '4.9.0': + if info.type == 'gcc': return '-fdiagnostics-color' elif info.type == 'clang': return '-fcolor-diagnostics' diff --git a/python/mozbuild/mozbuild/test/configure/test_toolchain_configure.py b/python/mozbuild/mozbuild/test/configure/test_toolchain_configure.py index 2a0b6922d7c8..47508a652c1a 100755 --- a/python/mozbuild/mozbuild/test/configure/test_toolchain_configure.py +++ b/python/mozbuild/mozbuild/test/configure/test_toolchain_configure.py @@ -84,20 +84,22 @@ def GCC(version): def GXX(version): return GCC_BASE(version) + DEFAULT_CXX_97 + SUPPORTS_GNUXX11 -SUPPORTS_DRAFT_CXX14_VERSION = { - '-std=gnu++14': DRAFT_CXX_14, -} SUPPORTS_DRAFT_CXX14_VERSION = { '-std=gnu++14': DRAFT_CXX_14, } -GCC_4_7 = GCC('4.7.3') -GXX_4_7 = GXX('4.7.3') GCC_4_9 = GCC('4.9.3') GXX_4_9 = GXX('4.9.3') + SUPPORTS_DRAFT_CXX14_VERSION GCC_5 = GCC('5.2.1') + DEFAULT_C11 GXX_5 = GXX('5.2.1') + SUPPORTS_GNUXX14 +GCC_6 = GCC('6.4.0') + DEFAULT_C11 +GXX_6 = GXX('6.4.0') + DEFAULT_CXX_14 +GCC_7 = GCC('7.3.0') + DEFAULT_C11 +GXX_7 = GXX('7.3.0') + DEFAULT_CXX_14 + +DEFAULT_GCC = GCC_6 +DEFAULT_GXX = GXX_6 GCC_PLATFORM_LITTLE_ENDIAN = { '__BYTE_ORDER__': 1234, @@ -192,6 +194,8 @@ CLANGXX_3_6 = CLANGXX('3.6.2') + { '__has_feature(cxx_alignof)': '1', }, } +DEFAULT_CLANG = CLANG_3_6 +DEFAULT_CLANGXX = CLANGXX_3_6 def CLANG_PLATFORM(gcc_platform): @@ -413,52 +417,67 @@ class BaseToolchainTest(BaseConfigureTest): '%s=%s' % (k, library_name_info[k])) +def old_gcc_message(old_ver): + return 'Only GCC 6.1 or newer is supported (found version {}).'.format(old_ver) + + class LinuxToolchainTest(BaseToolchainTest): PATHS = { - '/usr/bin/gcc': GCC_4_9 + GCC_PLATFORM_X86_64_LINUX, - '/usr/bin/g++': GXX_4_9 + GCC_PLATFORM_X86_64_LINUX, - '/usr/bin/gcc-4.7': GCC_4_7 + GCC_PLATFORM_X86_64_LINUX, - '/usr/bin/g++-4.7': GXX_4_7 + GCC_PLATFORM_X86_64_LINUX, + '/usr/bin/gcc': DEFAULT_GCC + GCC_PLATFORM_X86_64_LINUX, + '/usr/bin/g++': DEFAULT_GXX + GCC_PLATFORM_X86_64_LINUX, + '/usr/bin/gcc-4.9': GCC_4_9 + GCC_PLATFORM_X86_64_LINUX, + '/usr/bin/g++-4.9': GXX_4_9 + GCC_PLATFORM_X86_64_LINUX, '/usr/bin/gcc-5': GCC_5 + GCC_PLATFORM_X86_64_LINUX, '/usr/bin/g++-5': GXX_5 + GCC_PLATFORM_X86_64_LINUX, - '/usr/bin/clang': CLANG_3_6 + CLANG_PLATFORM_X86_64_LINUX, - '/usr/bin/clang++': CLANGXX_3_6 + CLANG_PLATFORM_X86_64_LINUX, + '/usr/bin/gcc-6': GCC_6 + GCC_PLATFORM_X86_64_LINUX, + '/usr/bin/g++-6': GXX_6 + GCC_PLATFORM_X86_64_LINUX, + '/usr/bin/gcc-7': GCC_7 + GCC_PLATFORM_X86_64_LINUX, + '/usr/bin/g++-7': GXX_7 + GCC_PLATFORM_X86_64_LINUX, + '/usr/bin/clang': DEFAULT_CLANG + CLANG_PLATFORM_X86_64_LINUX, + '/usr/bin/clang++': DEFAULT_CLANGXX + CLANG_PLATFORM_X86_64_LINUX, '/usr/bin/clang-3.6': CLANG_3_6 + CLANG_PLATFORM_X86_64_LINUX, '/usr/bin/clang++-3.6': CLANGXX_3_6 + CLANG_PLATFORM_X86_64_LINUX, '/usr/bin/clang-3.3': CLANG_3_3 + CLANG_PLATFORM_X86_64_LINUX, '/usr/bin/clang++-3.3': CLANGXX_3_3 + CLANG_PLATFORM_X86_64_LINUX, } - GCC_4_7_RESULT = ('Only GCC 4.9 or newer is supported ' - '(found version 4.7.3).') + + GCC_4_7_RESULT = old_gcc_message('4.7.3') GXX_4_7_RESULT = GCC_4_7_RESULT - GCC_4_9_RESULT = CompilerResult( + GCC_4_9_RESULT = old_gcc_message('4.9.3') + GXX_4_9_RESULT = GCC_4_9_RESULT + GCC_5_RESULT = old_gcc_message('5.2.1') + GXX_5_RESULT = GCC_5_RESULT + GCC_6_RESULT = CompilerResult( flags=['-std=gnu99'], - version='4.9.3', + version='6.4.0', type='gcc', - compiler='/usr/bin/gcc', + compiler='/usr/bin/gcc-6', language='C', ) - GXX_4_9_RESULT = CompilerResult( - flags=['-std=gnu++14'], - version='4.9.3', + GXX_6_RESULT = CompilerResult( + flags=[], + version='6.4.0', type='gcc', - compiler='/usr/bin/g++', + compiler='/usr/bin/g++-6', language='C++', ) - GCC_5_RESULT = CompilerResult( + GCC_7_RESULT = CompilerResult( flags=['-std=gnu99'], - version='5.2.1', + version='7.3.0', type='gcc', - compiler='/usr/bin/gcc-5', + compiler='/usr/bin/gcc-7', language='C', ) - GXX_5_RESULT = CompilerResult( - flags=['-std=gnu++14'], - version='5.2.1', + GXX_7_RESULT = CompilerResult( + flags=[], + version='7.3.0', type='gcc', - compiler='/usr/bin/g++-5', + compiler='/usr/bin/g++-7', language='C++', ) + DEFAULT_GCC_RESULT = GCC_6_RESULT + {'compiler': '/usr/bin/gcc'} + DEFAULT_GXX_RESULT = GXX_6_RESULT + {'compiler': '/usr/bin/g++'} + CLANG_3_3_RESULT = CompilerResult( flags=[], version='3.3.0', @@ -471,84 +490,86 @@ class LinuxToolchainTest(BaseToolchainTest): flags=['-std=gnu99'], version='3.6.2', type='clang', - compiler='/usr/bin/clang', + compiler='/usr/bin/clang-3.6', language='C', ) CLANGXX_3_6_RESULT = CompilerResult( flags=['-std=gnu++14'], version='3.6.2', type='clang', - compiler='/usr/bin/clang++', + compiler='/usr/bin/clang++-3.6', language='C++', ) + DEFAULT_CLANG_RESULT = CLANG_3_6_RESULT + {'compiler': '/usr/bin/clang'} + DEFAULT_CLANGXX_RESULT = CLANGXX_3_6_RESULT + {'compiler': '/usr/bin/clang++'} def test_gcc(self): # We'll try gcc and clang, and find gcc first. self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.GCC_4_9_RESULT, - 'cxx_compiler': self.GXX_4_9_RESULT, + 'c_compiler': self.DEFAULT_GCC_RESULT, + 'cxx_compiler': self.DEFAULT_GXX_RESULT, }) def test_unsupported_gcc(self): self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.GCC_4_7_RESULT, + 'c_compiler': self.GCC_4_9_RESULT, }, environ={ - 'CC': 'gcc-4.7', - 'CXX': 'g++-4.7', + 'CC': 'gcc-4.9', + 'CXX': 'g++-4.9', }) # Maybe this should be reporting the mismatched version instead. self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.GCC_4_9_RESULT, - 'cxx_compiler': self.GXX_4_7_RESULT, + 'c_compiler': self.DEFAULT_GCC_RESULT, + 'cxx_compiler': self.GXX_4_9_RESULT, }, environ={ - 'CXX': 'g++-4.7', + 'CXX': 'g++-4.9', }) def test_overridden_gcc(self): self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.GCC_5_RESULT, - 'cxx_compiler': self.GXX_5_RESULT, + 'c_compiler': self.GCC_7_RESULT, + 'cxx_compiler': self.GXX_7_RESULT, }, environ={ - 'CC': 'gcc-5', - 'CXX': 'g++-5', + 'CC': 'gcc-7', + 'CXX': 'g++-7', }) def test_guess_cxx(self): # When CXX is not set, we guess it from CC. self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.GCC_5_RESULT, - 'cxx_compiler': self.GXX_5_RESULT, + 'c_compiler': self.GCC_7_RESULT, + 'cxx_compiler': self.GXX_7_RESULT, }, environ={ - 'CC': 'gcc-5', + 'CC': 'gcc-7', }) def test_mismatched_gcc(self): self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.GCC_4_9_RESULT, + 'c_compiler': self.DEFAULT_GCC_RESULT, 'cxx_compiler': ( - 'The target C compiler is version 4.9.3, while the target ' - 'C++ compiler is version 5.2.1. Need to use the same compiler ' + 'The target C compiler is version 6.4.0, while the target ' + 'C++ compiler is version 7.3.0. Need to use the same compiler ' 'version.'), }, environ={ - 'CXX': 'g++-5', + 'CXX': 'g++-7', }) self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.GCC_4_9_RESULT, - 'cxx_compiler': self.GXX_4_9_RESULT, - 'host_c_compiler': self.GCC_4_9_RESULT, + 'c_compiler': self.DEFAULT_GCC_RESULT, + 'cxx_compiler': self.DEFAULT_GXX_RESULT, + 'host_c_compiler': self.DEFAULT_GCC_RESULT, 'host_cxx_compiler': ( - 'The host C compiler is version 4.9.3, while the host ' - 'C++ compiler is version 5.2.1. Need to use the same compiler ' + 'The host C compiler is version 6.4.0, while the host ' + 'C++ compiler is version 7.3.0. Need to use the same compiler ' 'version.'), }, environ={ - 'HOST_CXX': 'g++-5', + 'HOST_CXX': 'g++-7', }) def test_mismatched_compiler(self): self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.GCC_4_9_RESULT, + 'c_compiler': self.DEFAULT_GCC_RESULT, 'cxx_compiler': ( 'The target C compiler is gcc, while the target C++ compiler ' 'is clang. Need to use the same compiler suite.'), @@ -557,9 +578,9 @@ class LinuxToolchainTest(BaseToolchainTest): }) self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.GCC_4_9_RESULT, - 'cxx_compiler': self.GXX_4_9_RESULT, - 'host_c_compiler': self.GCC_4_9_RESULT, + 'c_compiler': self.DEFAULT_GCC_RESULT, + 'cxx_compiler': self.DEFAULT_GXX_RESULT, + 'host_c_compiler': self.DEFAULT_GCC_RESULT, 'host_cxx_compiler': ( 'The host C compiler is gcc, while the host C++ compiler ' 'is clang. Need to use the same compiler suite.'), @@ -575,7 +596,7 @@ class LinuxToolchainTest(BaseToolchainTest): }) self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.GCC_4_9_RESULT, + 'c_compiler': self.DEFAULT_GCC_RESULT, 'cxx_compiler': '`%s` is not a C++ compiler.' % mozpath.abspath('/usr/bin/gcc'), }, environ={ @@ -590,19 +611,15 @@ class LinuxToolchainTest(BaseToolchainTest): if os.path.basename(k) not in ('gcc', 'g++') } self.do_toolchain_test(paths, { - 'c_compiler': self.CLANG_3_6_RESULT, - 'cxx_compiler': self.CLANGXX_3_6_RESULT, + 'c_compiler': self.DEFAULT_CLANG_RESULT, + 'cxx_compiler': self.DEFAULT_CLANGXX_RESULT, }) def test_guess_cxx_clang(self): # When CXX is not set, we guess it from CC. self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.CLANG_3_6_RESULT + { - 'compiler': '/usr/bin/clang-3.6', - }, - 'cxx_compiler': self.CLANGXX_3_6_RESULT + { - 'compiler': '/usr/bin/clang++-3.6', - }, + 'c_compiler': self.CLANG_3_6_RESULT, + 'cxx_compiler': self.CLANGXX_3_6_RESULT, }, environ={ 'CC': 'clang-3.6', }) @@ -635,11 +652,11 @@ class LinuxToolchainTest(BaseToolchainTest): '/opt/clang/bin/clang++': paths['/usr/bin/clang++'], }) result = { - 'c_compiler': self.CLANG_3_6_RESULT + { + 'c_compiler': self.DEFAULT_CLANG_RESULT + { 'compiler': '/opt/clang/bin/clang', }, - 'cxx_compiler': self.CLANGXX_3_6_RESULT + { - 'compiler': '/opt/clang/bin/clang++' + 'cxx_compiler': self.DEFAULT_CLANGXX_RESULT + { + 'compiler': '/opt/clang/bin/clang++', }, } self.do_toolchain_test(paths, result, environ={ @@ -658,10 +675,10 @@ class LinuxToolchainTest(BaseToolchainTest): '/usr/bin/afl-clang-fast++': paths['/usr/bin/clang++'], }) self.do_toolchain_test(paths, { - 'c_compiler': self.CLANG_3_6_RESULT + { + 'c_compiler': self.DEFAULT_CLANG_RESULT + { 'compiler': '/usr/bin/afl-clang-fast', }, - 'cxx_compiler': self.CLANGXX_3_6_RESULT + { + 'cxx_compiler': self.DEFAULT_CLANGXX_RESULT + { 'compiler': '/usr/bin/afl-clang-fast++', }, }, environ={ @@ -671,20 +688,20 @@ class LinuxToolchainTest(BaseToolchainTest): def test_mixed_compilers(self): self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.CLANG_3_6_RESULT, - 'cxx_compiler': self.CLANGXX_3_6_RESULT, - 'host_c_compiler': self.GCC_4_9_RESULT, - 'host_cxx_compiler': self.GXX_4_9_RESULT, + 'c_compiler': self.DEFAULT_CLANG_RESULT, + 'cxx_compiler': self.DEFAULT_CLANGXX_RESULT, + 'host_c_compiler': self.DEFAULT_GCC_RESULT, + 'host_cxx_compiler': self.DEFAULT_GXX_RESULT, }, environ={ 'CC': 'clang', 'HOST_CC': 'gcc', }) self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.CLANG_3_6_RESULT, - 'cxx_compiler': self.CLANGXX_3_6_RESULT, - 'host_c_compiler': self.GCC_4_9_RESULT, - 'host_cxx_compiler': self.GXX_4_9_RESULT, + 'c_compiler': self.DEFAULT_CLANG_RESULT, + 'cxx_compiler': self.DEFAULT_CLANGXX_RESULT, + 'host_c_compiler': self.DEFAULT_GCC_RESULT, + 'host_cxx_compiler': self.DEFAULT_GXX_RESULT, }, environ={ 'CC': 'clang', 'CXX': 'clang++', @@ -695,33 +712,33 @@ class LinuxToolchainTest(BaseToolchainTest): class LinuxSimpleCrossToolchainTest(BaseToolchainTest): TARGET = 'i686-pc-linux-gnu' PATHS = LinuxToolchainTest.PATHS - GCC_4_9_RESULT = LinuxToolchainTest.GCC_4_9_RESULT - GXX_4_9_RESULT = LinuxToolchainTest.GXX_4_9_RESULT - CLANG_3_6_RESULT = LinuxToolchainTest.CLANG_3_6_RESULT - CLANGXX_3_6_RESULT = LinuxToolchainTest.CLANGXX_3_6_RESULT + DEFAULT_GCC_RESULT = LinuxToolchainTest.DEFAULT_GCC_RESULT + DEFAULT_GXX_RESULT = LinuxToolchainTest.DEFAULT_GXX_RESULT + DEFAULT_CLANG_RESULT = LinuxToolchainTest.DEFAULT_CLANG_RESULT + DEFAULT_CLANGXX_RESULT = LinuxToolchainTest.DEFAULT_CLANGXX_RESULT def test_cross_gcc(self): self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.GCC_4_9_RESULT + { + 'c_compiler': self.DEFAULT_GCC_RESULT + { 'flags': ['-m32'] }, - 'cxx_compiler': self.GXX_4_9_RESULT + { + 'cxx_compiler': self.DEFAULT_GXX_RESULT + { 'flags': ['-m32'] }, - 'host_c_compiler': self.GCC_4_9_RESULT, - 'host_cxx_compiler': self.GXX_4_9_RESULT, + 'host_c_compiler': self.DEFAULT_GCC_RESULT, + 'host_cxx_compiler': self.DEFAULT_GXX_RESULT, }) def test_cross_clang(self): self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.CLANG_3_6_RESULT + { + 'c_compiler': self.DEFAULT_CLANG_RESULT + { 'flags': ['--target=i686-linux-gnu'], }, - 'cxx_compiler': self.CLANGXX_3_6_RESULT + { + 'cxx_compiler': self.DEFAULT_CLANGXX_RESULT + { 'flags': ['--target=i686-linux-gnu'], }, - 'host_c_compiler': self.CLANG_3_6_RESULT, - 'host_cxx_compiler': self.CLANGXX_3_6_RESULT, + 'host_c_compiler': self.DEFAULT_CLANG_RESULT, + 'host_cxx_compiler': self.DEFAULT_CLANGXX_RESULT, }, environ={ 'CC': 'clang', }) @@ -731,38 +748,38 @@ class LinuxX86_64CrossToolchainTest(BaseToolchainTest): HOST = 'i686-pc-linux-gnu' TARGET = 'x86_64-pc-linux-gnu' PATHS = { - '/usr/bin/gcc': GCC_4_9 + GCC_PLATFORM_X86_LINUX, - '/usr/bin/g++': GXX_4_9 + GCC_PLATFORM_X86_LINUX, - '/usr/bin/clang': CLANG_3_6 + CLANG_PLATFORM_X86_LINUX, - '/usr/bin/clang++': CLANGXX_3_6 + CLANG_PLATFORM_X86_LINUX, + '/usr/bin/gcc': DEFAULT_GCC + GCC_PLATFORM_X86_LINUX, + '/usr/bin/g++': DEFAULT_GXX + GCC_PLATFORM_X86_LINUX, + '/usr/bin/clang': DEFAULT_CLANG + CLANG_PLATFORM_X86_LINUX, + '/usr/bin/clang++': DEFAULT_CLANGXX + CLANG_PLATFORM_X86_LINUX, } - GCC_4_9_RESULT = LinuxToolchainTest.GCC_4_9_RESULT - GXX_4_9_RESULT = LinuxToolchainTest.GXX_4_9_RESULT - CLANG_3_6_RESULT = LinuxToolchainTest.CLANG_3_6_RESULT - CLANGXX_3_6_RESULT = LinuxToolchainTest.CLANGXX_3_6_RESULT + DEFAULT_GCC_RESULT = LinuxToolchainTest.DEFAULT_GCC_RESULT + DEFAULT_GXX_RESULT = LinuxToolchainTest.DEFAULT_GXX_RESULT + DEFAULT_CLANG_RESULT = LinuxToolchainTest.DEFAULT_CLANG_RESULT + DEFAULT_CLANGXX_RESULT = LinuxToolchainTest.DEFAULT_CLANGXX_RESULT def test_cross_gcc(self): self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.GCC_4_9_RESULT + { + 'c_compiler': self.DEFAULT_GCC_RESULT + { 'flags': ['-m64'] }, - 'cxx_compiler': self.GXX_4_9_RESULT + { + 'cxx_compiler': self.DEFAULT_GXX_RESULT + { 'flags': ['-m64'] }, - 'host_c_compiler': self.GCC_4_9_RESULT, - 'host_cxx_compiler': self.GXX_4_9_RESULT, + 'host_c_compiler': self.DEFAULT_GCC_RESULT, + 'host_cxx_compiler': self.DEFAULT_GXX_RESULT, }) def test_cross_clang(self): self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.CLANG_3_6_RESULT + { + 'c_compiler': self.DEFAULT_CLANG_RESULT + { 'flags': ['--target=x86_64-linux-gnu'], }, - 'cxx_compiler': self.CLANGXX_3_6_RESULT + { + 'cxx_compiler': self.DEFAULT_CLANGXX_RESULT + { 'flags': ['--target=x86_64-linux-gnu'], }, - 'host_c_compiler': self.CLANG_3_6_RESULT, - 'host_cxx_compiler': self.CLANGXX_3_6_RESULT, + 'host_c_compiler': self.DEFAULT_CLANG_RESULT, + 'host_cxx_compiler': self.DEFAULT_CLANGXX_RESULT, }, environ={ 'CC': 'clang', }) @@ -771,14 +788,12 @@ class LinuxX86_64CrossToolchainTest(BaseToolchainTest): class OSXToolchainTest(BaseToolchainTest): HOST = 'x86_64-apple-darwin11.2.0' PATHS = { - '/usr/bin/gcc': GCC_4_9 + GCC_PLATFORM_X86_64_OSX, - '/usr/bin/g++': GXX_4_9 + GCC_PLATFORM_X86_64_OSX, - '/usr/bin/gcc-4.7': GCC_4_7 + GCC_PLATFORM_X86_64_OSX, - '/usr/bin/g++-4.7': GXX_4_7 + GCC_PLATFORM_X86_64_OSX, '/usr/bin/gcc-5': GCC_5 + GCC_PLATFORM_X86_64_OSX, '/usr/bin/g++-5': GXX_5 + GCC_PLATFORM_X86_64_OSX, - '/usr/bin/clang': CLANG_3_6 + CLANG_PLATFORM_X86_64_OSX, - '/usr/bin/clang++': CLANGXX_3_6 + CLANG_PLATFORM_X86_64_OSX, + '/usr/bin/gcc-7': GCC_7 + GCC_PLATFORM_X86_64_OSX, + '/usr/bin/g++-7': GXX_7 + GCC_PLATFORM_X86_64_OSX, + '/usr/bin/clang': DEFAULT_CLANG + CLANG_PLATFORM_X86_64_OSX, + '/usr/bin/clang++': DEFAULT_CLANGXX + CLANG_PLATFORM_X86_64_OSX, '/usr/bin/clang-3.6': CLANG_3_6 + CLANG_PLATFORM_X86_64_OSX, '/usr/bin/clang++-3.6': CLANGXX_3_6 + CLANG_PLATFORM_X86_64_OSX, '/usr/bin/clang-3.3': CLANG_3_3 + CLANG_PLATFORM_X86_64_OSX, @@ -786,17 +801,18 @@ class OSXToolchainTest(BaseToolchainTest): } CLANG_3_3_RESULT = LinuxToolchainTest.CLANG_3_3_RESULT CLANGXX_3_3_RESULT = LinuxToolchainTest.CLANGXX_3_3_RESULT - CLANG_3_6_RESULT = LinuxToolchainTest.CLANG_3_6_RESULT - CLANGXX_3_6_RESULT = LinuxToolchainTest.CLANGXX_3_6_RESULT - GCC_4_7_RESULT = LinuxToolchainTest.GCC_4_7_RESULT + DEFAULT_CLANG_RESULT = LinuxToolchainTest.DEFAULT_CLANG_RESULT + DEFAULT_CLANGXX_RESULT = LinuxToolchainTest.DEFAULT_CLANGXX_RESULT GCC_5_RESULT = LinuxToolchainTest.GCC_5_RESULT GXX_5_RESULT = LinuxToolchainTest.GXX_5_RESULT + GCC_7_RESULT = LinuxToolchainTest.GCC_7_RESULT + GXX_7_RESULT = LinuxToolchainTest.GXX_7_RESULT def test_clang(self): # We only try clang because gcc is known not to work. self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.CLANG_3_6_RESULT, - 'cxx_compiler': self.CLANGXX_3_6_RESULT, + 'c_compiler': self.DEFAULT_CLANG_RESULT, + 'cxx_compiler': self.DEFAULT_CLANGXX_RESULT, }) def test_not_gcc(self): @@ -822,19 +838,19 @@ class OSXToolchainTest(BaseToolchainTest): def test_forced_gcc(self): # GCC can still be forced if the user really wants it. self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.GCC_5_RESULT, - 'cxx_compiler': self.GXX_5_RESULT, + 'c_compiler': self.GCC_7_RESULT, + 'cxx_compiler': self.GXX_7_RESULT, }, environ={ - 'CC': 'gcc-5', - 'CXX': 'g++-5', + 'CC': 'gcc-7', + 'CXX': 'g++-7', }) def test_forced_unsupported_gcc(self): self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.GCC_4_7_RESULT, + 'c_compiler': self.GCC_5_RESULT, }, environ={ - 'CC': 'gcc-4.7', - 'CXX': 'g++-4.7', + 'CC': 'gcc-5', + 'CXX': 'g++-5', }) @@ -853,14 +869,16 @@ class WindowsToolchainTest(BaseToolchainTest): '/opt/VS_2017u4/bin/cl': VS_2017u4 + VS_PLATFORM_X86, '/usr/bin/cl': VS_2017u6 + VS_PLATFORM_X86, '/usr/bin/clang-cl': CLANG_CL_3_9 + CLANG_CL_PLATFORM_X86, - '/usr/bin/gcc': GCC_4_9 + GCC_PLATFORM_X86_WIN, - '/usr/bin/g++': GXX_4_9 + GCC_PLATFORM_X86_WIN, - '/usr/bin/gcc-4.7': GCC_4_7 + GCC_PLATFORM_X86_WIN, - '/usr/bin/g++-4.7': GXX_4_7 + GCC_PLATFORM_X86_WIN, + '/usr/bin/gcc': DEFAULT_GCC + GCC_PLATFORM_X86_WIN, + '/usr/bin/g++': DEFAULT_GXX + GCC_PLATFORM_X86_WIN, + '/usr/bin/gcc-4.9': GCC_4_9 + GCC_PLATFORM_X86_WIN, + '/usr/bin/g++-4.9': GXX_4_9 + GCC_PLATFORM_X86_WIN, '/usr/bin/gcc-5': GCC_5 + GCC_PLATFORM_X86_WIN, '/usr/bin/g++-5': GXX_5 + GCC_PLATFORM_X86_WIN, - '/usr/bin/clang': CLANG_3_6 + CLANG_PLATFORM_X86_WIN, - '/usr/bin/clang++': CLANGXX_3_6 + CLANG_PLATFORM_X86_WIN, + '/usr/bin/gcc-6': GCC_6 + GCC_PLATFORM_X86_WIN, + '/usr/bin/g++-6': GXX_6 + GCC_PLATFORM_X86_WIN, + '/usr/bin/clang': DEFAULT_CLANG + CLANG_PLATFORM_X86_WIN, + '/usr/bin/clang++': DEFAULT_CLANGXX + CLANG_PLATFORM_X86_WIN, '/usr/bin/clang-3.6': CLANG_3_6 + CLANG_PLATFORM_X86_WIN, '/usr/bin/clang++-3.6': CLANGXX_3_6 + CLANG_PLATFORM_X86_WIN, '/usr/bin/clang-3.3': CLANG_3_3 + CLANG_PLATFORM_X86_WIN, @@ -927,25 +945,16 @@ class WindowsToolchainTest(BaseToolchainTest): ) CLANG_3_3_RESULT = LinuxToolchainTest.CLANG_3_3_RESULT CLANGXX_3_3_RESULT = LinuxToolchainTest.CLANGXX_3_3_RESULT - CLANG_3_6_RESULT = LinuxToolchainTest.CLANG_3_6_RESULT - CLANGXX_3_6_RESULT = LinuxToolchainTest.CLANGXX_3_6_RESULT - GCC_4_7_RESULT = LinuxToolchainTest.GCC_4_7_RESULT + DEFAULT_CLANG_RESULT = LinuxToolchainTest.DEFAULT_CLANG_RESULT + DEFAULT_CLANGXX_RESULT = LinuxToolchainTest.DEFAULT_CLANGXX_RESULT GCC_4_9_RESULT = LinuxToolchainTest.GCC_4_9_RESULT - GXX_4_9_RESULT = CompilerResult( - flags=['-std=gnu++14'], - version='4.9.3', - type='gcc', - compiler='/usr/bin/g++', - language='C++', - ) + GXX_4_9_RESULT = LinuxToolchainTest.GXX_4_9_RESULT GCC_5_RESULT = LinuxToolchainTest.GCC_5_RESULT - GXX_5_RESULT = CompilerResult( - flags=['-std=gnu++14'], - version='5.2.1', - type='gcc', - compiler='/usr/bin/g++-5', - language='C++', - ) + GXX_5_RESULT = LinuxToolchainTest.GXX_5_RESULT + GCC_6_RESULT = LinuxToolchainTest.GCC_6_RESULT + GXX_6_RESULT = LinuxToolchainTest.GXX_6_RESULT + DEFAULT_GCC_RESULT = LinuxToolchainTest.DEFAULT_GCC_RESULT + DEFAULT_GXX_RESULT = LinuxToolchainTest.DEFAULT_GXX_RESULT # VS2017u6 or greater is required. def test_msvc(self): @@ -1015,16 +1024,16 @@ class WindowsToolchainTest(BaseToolchainTest): if os.path.basename(k) not in ('cl', 'clang-cl') } self.do_toolchain_test(paths, { - 'c_compiler': self.GCC_4_9_RESULT, - 'cxx_compiler': self.GXX_4_9_RESULT, + 'c_compiler': self.DEFAULT_GCC_RESULT, + 'cxx_compiler': self.DEFAULT_GXX_RESULT, }) def test_overridden_unsupported_gcc(self): self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.GCC_4_7_RESULT, + 'c_compiler': self.GCC_5_RESULT, }, environ={ - 'CC': 'gcc-4.7', - 'CXX': 'g++-4.7', + 'CC': 'gcc-5', + 'CXX': 'g++-5', }) def test_clang(self): @@ -1034,8 +1043,8 @@ class WindowsToolchainTest(BaseToolchainTest): if os.path.basename(k) not in ('cl', 'clang-cl', 'gcc') } self.do_toolchain_test(paths, { - 'c_compiler': self.CLANG_3_6_RESULT, - 'cxx_compiler': self.CLANGXX_3_6_RESULT, + 'c_compiler': self.DEFAULT_CLANG_RESULT, + 'cxx_compiler': self.DEFAULT_CLANGXX_RESULT, }) def test_overridden_unsupported_clang(self): @@ -1073,14 +1082,18 @@ class Windows64ToolchainTest(WindowsToolchainTest): '/opt/VS_2017u4/bin/cl': VS_2017u4 + VS_PLATFORM_X86_64, '/usr/bin/cl': VS_2017u6 + VS_PLATFORM_X86_64, '/usr/bin/clang-cl': CLANG_CL_3_9 + CLANG_CL_PLATFORM_X86_64, - '/usr/bin/gcc': GCC_4_9 + GCC_PLATFORM_X86_64_WIN, - '/usr/bin/g++': GXX_4_9 + GCC_PLATFORM_X86_64_WIN, - '/usr/bin/gcc-4.7': GCC_4_7 + GCC_PLATFORM_X86_64_WIN, - '/usr/bin/g++-4.7': GXX_4_7 + GCC_PLATFORM_X86_64_WIN, + '/usr/bin/gcc': DEFAULT_GCC + GCC_PLATFORM_X86_64_WIN, + '/usr/bin/g++': DEFAULT_GXX + GCC_PLATFORM_X86_64_WIN, + '/usr/bin/gcc-4.9': GCC_4_9 + GCC_PLATFORM_X86_64_WIN, + '/usr/bin/g++-4.9': GXX_4_9 + GCC_PLATFORM_X86_64_WIN, '/usr/bin/gcc-5': GCC_5 + GCC_PLATFORM_X86_64_WIN, '/usr/bin/g++-5': GXX_5 + GCC_PLATFORM_X86_64_WIN, - '/usr/bin/clang': CLANG_3_6 + CLANG_PLATFORM_X86_64_WIN, - '/usr/bin/clang++': CLANGXX_3_6 + CLANG_PLATFORM_X86_64_WIN, + '/usr/bin/gcc-6': GCC_6 + GCC_PLATFORM_X86_64_WIN, + '/usr/bin/g++-6': GXX_6 + GCC_PLATFORM_X86_64_WIN, + '/usr/bin/gcc-7': GCC_7 + GCC_PLATFORM_X86_64_WIN, + '/usr/bin/g++-7': GXX_7 + GCC_PLATFORM_X86_64_WIN, + '/usr/bin/clang': DEFAULT_CLANG + CLANG_PLATFORM_X86_64_WIN, + '/usr/bin/clang++': DEFAULT_CLANGXX + CLANG_PLATFORM_X86_64_WIN, '/usr/bin/clang-3.6': CLANG_3_6 + CLANG_PLATFORM_X86_64_WIN, '/usr/bin/clang++-3.6': CLANGXX_3_6 + CLANG_PLATFORM_X86_64_WIN, '/usr/bin/clang-3.3': CLANG_3_3 + CLANG_PLATFORM_X86_64_WIN, @@ -1100,25 +1113,36 @@ class Windows64ToolchainTest(WindowsToolchainTest): class LinuxCrossCompileToolchainTest(BaseToolchainTest): TARGET = 'arm-unknown-linux-gnu' PATHS = { - '/usr/bin/arm-linux-gnu-gcc': GCC_4_9 + GCC_PLATFORM_ARM_LINUX, - '/usr/bin/arm-linux-gnu-g++': GXX_4_9 + GCC_PLATFORM_ARM_LINUX, - '/usr/bin/arm-linux-gnu-gcc-4.7': GCC_4_7 + GCC_PLATFORM_ARM_LINUX, - '/usr/bin/arm-linux-gnu-g++-4.7': GXX_4_7 + GCC_PLATFORM_ARM_LINUX, + '/usr/bin/arm-linux-gnu-gcc-4.9': GCC_4_9 + GCC_PLATFORM_ARM_LINUX, + '/usr/bin/arm-linux-gnu-g++-4.9': GXX_4_9 + GCC_PLATFORM_ARM_LINUX, '/usr/bin/arm-linux-gnu-gcc-5': GCC_5 + GCC_PLATFORM_ARM_LINUX, '/usr/bin/arm-linux-gnu-g++-5': GXX_5 + GCC_PLATFORM_ARM_LINUX, + '/usr/bin/arm-linux-gnu-gcc': DEFAULT_GCC + GCC_PLATFORM_ARM_LINUX, + '/usr/bin/arm-linux-gnu-g++': DEFAULT_GXX + GCC_PLATFORM_ARM_LINUX, + '/usr/bin/arm-linux-gnu-gcc-7': GCC_7 + GCC_PLATFORM_ARM_LINUX, + '/usr/bin/arm-linux-gnu-g++-7': GXX_7 + GCC_PLATFORM_ARM_LINUX, } PATHS.update(LinuxToolchainTest.PATHS) - ARM_GCC_4_7_RESULT = LinuxToolchainTest.GXX_4_7_RESULT - ARM_GCC_5_RESULT = LinuxToolchainTest.GCC_5_RESULT + { - 'compiler': '/usr/bin/arm-linux-gnu-gcc-5', + ARM_GCC_4_9_RESULT = LinuxToolchainTest.GCC_4_9_RESULT + ARM_GXX_4_9_RESULT = LinuxToolchainTest.GXX_4_9_RESULT + ARM_GCC_5_RESULT = LinuxToolchainTest.GCC_5_RESULT + ARM_GXX_5_RESULT = LinuxToolchainTest.GXX_5_RESULT + ARM_DEFAULT_GCC_RESULT = LinuxToolchainTest.DEFAULT_GCC_RESULT + { + 'compiler': '/usr/bin/arm-linux-gnu-gcc', } - ARM_GXX_5_RESULT = LinuxToolchainTest.GXX_5_RESULT + { - 'compiler': '/usr/bin/arm-linux-gnu-g++-5', + ARM_DEFAULT_GXX_RESULT = LinuxToolchainTest.DEFAULT_GXX_RESULT + { + 'compiler': '/usr/bin/arm-linux-gnu-g++', } - CLANG_3_6_RESULT = LinuxToolchainTest.CLANG_3_6_RESULT - CLANGXX_3_6_RESULT = LinuxToolchainTest.CLANGXX_3_6_RESULT - GCC_4_9_RESULT = LinuxToolchainTest.GCC_4_9_RESULT - GXX_4_9_RESULT = LinuxToolchainTest.GXX_4_9_RESULT + ARM_GCC_7_RESULT = LinuxToolchainTest.GCC_7_RESULT + { + 'compiler': '/usr/bin/arm-linux-gnu-gcc-7', + } + ARM_GXX_7_RESULT = LinuxToolchainTest.GXX_7_RESULT + { + 'compiler': '/usr/bin/arm-linux-gnu-g++-7', + } + DEFAULT_CLANG_RESULT = LinuxToolchainTest.DEFAULT_CLANG_RESULT + DEFAULT_CLANGXX_RESULT = LinuxToolchainTest.DEFAULT_CLANGXX_RESULT + DEFAULT_GCC_RESULT = LinuxToolchainTest.DEFAULT_GCC_RESULT + DEFAULT_GXX_RESULT = LinuxToolchainTest.DEFAULT_GXX_RESULT little_endian = FakeCompiler(GCC_PLATFORM_LINUX, GCC_PLATFORM_LITTLE_ENDIAN) @@ -1204,17 +1228,17 @@ class LinuxCrossCompileToolchainTest(BaseToolchainTest): self.HOST = host self.TARGET = target paths = { - '/usr/bin/gcc': GCC_4_9 + self.PLATFORMS[host], - '/usr/bin/g++': GXX_4_9 + self.PLATFORMS[host], + '/usr/bin/gcc': DEFAULT_GCC + self.PLATFORMS[host], + '/usr/bin/g++': DEFAULT_GXX + self.PLATFORMS[host], } cross_flags = { 'flags': ['-m64' if '64' in target else '-m32'] } self.do_toolchain_test(paths, { - 'c_compiler': self.GCC_4_9_RESULT + cross_flags, - 'cxx_compiler': self.GXX_4_9_RESULT + cross_flags, - 'host_c_compiler': self.GCC_4_9_RESULT, - 'host_cxx_compiler': self.GXX_4_9_RESULT, + 'c_compiler': self.DEFAULT_GCC_RESULT + cross_flags, + 'cxx_compiler': self.DEFAULT_GXX_RESULT + cross_flags, + 'host_c_compiler': self.DEFAULT_GCC_RESULT, + 'host_cxx_compiler': self.DEFAULT_GXX_RESULT, }) self.HOST = LinuxCrossCompileToolchainTest.HOST self.TARGET = LinuxCrossCompileToolchainTest.TARGET @@ -1244,8 +1268,8 @@ class LinuxCrossCompileToolchainTest(BaseToolchainTest): cpu, manufacturer, os = target.split('-', 2) toolchain_prefix = '/usr/bin/%s-%s' % (cpu, os) paths = { - '/usr/bin/gcc': GCC_4_9 + self.PLATFORMS[host], - '/usr/bin/g++': GXX_4_9 + self.PLATFORMS[host], + '/usr/bin/gcc': DEFAULT_GCC + self.PLATFORMS[host], + '/usr/bin/g++': DEFAULT_GXX + self.PLATFORMS[host], } self.do_toolchain_test(paths, { 'c_compiler': ('Target C compiler target CPU (%s) ' @@ -1254,18 +1278,18 @@ class LinuxCrossCompileToolchainTest(BaseToolchainTest): }) paths.update({ - '%s-gcc' % toolchain_prefix: GCC_4_9 + self.PLATFORMS[target], - '%s-g++' % toolchain_prefix: GXX_4_9 + self.PLATFORMS[target], + '%s-gcc' % toolchain_prefix: DEFAULT_GCC + self.PLATFORMS[target], + '%s-g++' % toolchain_prefix: DEFAULT_GXX + self.PLATFORMS[target], }) self.do_toolchain_test(paths, { - 'c_compiler': self.GCC_4_9_RESULT + { + 'c_compiler': self.DEFAULT_GCC_RESULT + { 'compiler': '%s-gcc' % toolchain_prefix, }, - 'cxx_compiler': self.GXX_4_9_RESULT + { + 'cxx_compiler': self.DEFAULT_GXX_RESULT + { 'compiler': '%s-g++' % toolchain_prefix, }, - 'host_c_compiler': self.GCC_4_9_RESULT, - 'host_cxx_compiler': self.GXX_4_9_RESULT, + 'host_c_compiler': self.DEFAULT_GCC_RESULT, + 'host_cxx_compiler': self.DEFAULT_GXX_RESULT, }) self.HOST = LinuxCrossCompileToolchainTest.HOST self.TARGET = LinuxCrossCompileToolchainTest.TARGET @@ -1279,8 +1303,8 @@ class LinuxCrossCompileToolchainTest(BaseToolchainTest): self.TARGET = 'mipsel-unknown-linux-gnu' paths = { - '/usr/bin/gcc': GCC_4_9 + self.PLATFORMS['mips-unknown-linux-gnu'], - '/usr/bin/g++': GXX_4_9 + self.PLATFORMS['mips-unknown-linux-gnu'], + '/usr/bin/gcc': DEFAULT_GCC + self.PLATFORMS['mips-unknown-linux-gnu'], + '/usr/bin/g++': DEFAULT_GXX + self.PLATFORMS['mips-unknown-linux-gnu'], } self.do_toolchain_test(paths, { 'c_compiler': ('Target C compiler target endianness (big) ' @@ -1290,67 +1314,67 @@ class LinuxCrossCompileToolchainTest(BaseToolchainTest): def test_overridden_cross_gcc(self): self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.ARM_GCC_5_RESULT, - 'cxx_compiler': self.ARM_GXX_5_RESULT, - 'host_c_compiler': self.GCC_4_9_RESULT, - 'host_cxx_compiler': self.GXX_4_9_RESULT, + 'c_compiler': self.ARM_GCC_7_RESULT, + 'cxx_compiler': self.ARM_GXX_7_RESULT, + 'host_c_compiler': self.DEFAULT_GCC_RESULT, + 'host_cxx_compiler': self.DEFAULT_GXX_RESULT, }, environ={ - 'CC': 'arm-linux-gnu-gcc-5', - 'CXX': 'arm-linux-gnu-g++-5', + 'CC': 'arm-linux-gnu-gcc-7', + 'CXX': 'arm-linux-gnu-g++-7', }) def test_overridden_unsupported_cross_gcc(self): self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.ARM_GCC_4_7_RESULT, + 'c_compiler': self.ARM_GCC_4_9_RESULT, }, environ={ - 'CC': 'arm-linux-gnu-gcc-4.7', - 'CXX': 'arm-linux-gnu-g++-4.7', + 'CC': 'arm-linux-gnu-gcc-4.9', + 'CXX': 'arm-linux-gnu-g++-4.9', }) def test_guess_cross_cxx(self): # When CXX is not set, we guess it from CC. self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.ARM_GCC_5_RESULT, - 'cxx_compiler': self.ARM_GXX_5_RESULT, - 'host_c_compiler': self.GCC_4_9_RESULT, - 'host_cxx_compiler': self.GXX_4_9_RESULT, + 'c_compiler': self.ARM_GCC_7_RESULT, + 'cxx_compiler': self.ARM_GXX_7_RESULT, + 'host_c_compiler': self.DEFAULT_GCC_RESULT, + 'host_cxx_compiler': self.DEFAULT_GXX_RESULT, }, environ={ - 'CC': 'arm-linux-gnu-gcc-5', + 'CC': 'arm-linux-gnu-gcc-7', }) self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.ARM_GCC_5_RESULT, - 'cxx_compiler': self.ARM_GXX_5_RESULT, - 'host_c_compiler': self.CLANG_3_6_RESULT, - 'host_cxx_compiler': self.CLANGXX_3_6_RESULT, + 'c_compiler': self.ARM_DEFAULT_GCC_RESULT, + 'cxx_compiler': self.ARM_DEFAULT_GXX_RESULT, + 'host_c_compiler': self.DEFAULT_CLANG_RESULT, + 'host_cxx_compiler': self.DEFAULT_CLANGXX_RESULT, }, environ={ - 'CC': 'arm-linux-gnu-gcc-5', + 'CC': 'arm-linux-gnu-gcc', 'HOST_CC': 'clang', }) self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.ARM_GCC_5_RESULT, - 'cxx_compiler': self.ARM_GXX_5_RESULT, - 'host_c_compiler': self.CLANG_3_6_RESULT, - 'host_cxx_compiler': self.CLANGXX_3_6_RESULT, + 'c_compiler': self.ARM_DEFAULT_GCC_RESULT, + 'cxx_compiler': self.ARM_DEFAULT_GXX_RESULT, + 'host_c_compiler': self.DEFAULT_CLANG_RESULT, + 'host_cxx_compiler': self.DEFAULT_CLANGXX_RESULT, }, environ={ - 'CC': 'arm-linux-gnu-gcc-5', - 'CXX': 'arm-linux-gnu-g++-5', + 'CC': 'arm-linux-gnu-gcc', + 'CXX': 'arm-linux-gnu-g++', 'HOST_CC': 'clang', }) def test_cross_clang(self): - cross_clang_result = self.CLANG_3_6_RESULT + { + cross_clang_result = self.DEFAULT_CLANG_RESULT + { 'flags': ['--target=arm-linux-gnu'], } - cross_clangxx_result = self.CLANGXX_3_6_RESULT + { + cross_clangxx_result = self.DEFAULT_CLANGXX_RESULT + { 'flags': ['--target=arm-linux-gnu'], } self.do_toolchain_test(self.PATHS, { 'c_compiler': cross_clang_result, 'cxx_compiler': cross_clangxx_result, - 'host_c_compiler': self.CLANG_3_6_RESULT, - 'host_cxx_compiler': self.CLANGXX_3_6_RESULT, + 'host_c_compiler': self.DEFAULT_CLANG_RESULT, + 'host_cxx_compiler': self.DEFAULT_CLANGXX_RESULT, }, environ={ 'CC': 'clang', 'HOST_CC': 'clang', @@ -1359,8 +1383,8 @@ class LinuxCrossCompileToolchainTest(BaseToolchainTest): self.do_toolchain_test(self.PATHS, { 'c_compiler': cross_clang_result, 'cxx_compiler': cross_clangxx_result, - 'host_c_compiler': self.CLANG_3_6_RESULT, - 'host_cxx_compiler': self.CLANGXX_3_6_RESULT, + 'host_c_compiler': self.DEFAULT_CLANG_RESULT, + 'host_cxx_compiler': self.DEFAULT_CLANGXX_RESULT, }, environ={ 'CC': 'clang', }) @@ -1371,10 +1395,10 @@ class LinuxCrossCompileToolchainTest(BaseToolchainTest): '/usr/bin/afl-clang-fast': paths['/usr/bin/clang'], '/usr/bin/afl-clang-fast++': paths['/usr/bin/clang++'], }) - afl_clang_result = self.CLANG_3_6_RESULT + { + afl_clang_result = self.DEFAULT_CLANG_RESULT + { 'compiler': '/usr/bin/afl-clang-fast', } - afl_clangxx_result = self.CLANGXX_3_6_RESULT + { + afl_clangxx_result = self.DEFAULT_CLANGXX_RESULT + { 'compiler': '/usr/bin/afl-clang-fast++', } self.do_toolchain_test(paths, { @@ -1395,19 +1419,19 @@ class LinuxCrossCompileToolchainTest(BaseToolchainTest): class OSXCrossToolchainTest(BaseToolchainTest): TARGET = 'i686-apple-darwin11.2.0' PATHS = LinuxToolchainTest.PATHS - CLANG_3_6_RESULT = LinuxToolchainTest.CLANG_3_6_RESULT - CLANGXX_3_6_RESULT = LinuxToolchainTest.CLANGXX_3_6_RESULT + DEFAULT_CLANG_RESULT = LinuxToolchainTest.DEFAULT_CLANG_RESULT + DEFAULT_CLANGXX_RESULT = LinuxToolchainTest.DEFAULT_CLANGXX_RESULT def test_osx_cross(self): self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.CLANG_3_6_RESULT + { + 'c_compiler': self.DEFAULT_CLANG_RESULT + { 'flags': ['--target=i686-darwin11.2.0'], }, - 'cxx_compiler': self.CLANGXX_3_6_RESULT + { + 'cxx_compiler': self.DEFAULT_CLANGXX_RESULT + { 'flags': ['--target=i686-darwin11.2.0'], }, - 'host_c_compiler': self.CLANG_3_6_RESULT, - 'host_cxx_compiler': self.CLANGXX_3_6_RESULT, + 'host_c_compiler': self.DEFAULT_CLANG_RESULT, + 'host_cxx_compiler': self.DEFAULT_CLANGXX_RESULT, }, environ={ 'CC': 'clang', }) @@ -1425,16 +1449,16 @@ class OpenBSDToolchainTest(BaseToolchainTest): HOST = 'x86_64-unknown-openbsd6.1' TARGET = 'x86_64-unknown-openbsd6.1' PATHS = { - '/usr/bin/gcc': GCC_4_9 + GCC_PLATFORM_X86_64 + GCC_PLATFORM_OPENBSD, - '/usr/bin/g++': GXX_4_9 + GCC_PLATFORM_X86_64 + GCC_PLATFORM_OPENBSD, + '/usr/bin/gcc': DEFAULT_GCC + GCC_PLATFORM_X86_64 + GCC_PLATFORM_OPENBSD, + '/usr/bin/g++': DEFAULT_GXX + GCC_PLATFORM_X86_64 + GCC_PLATFORM_OPENBSD, } - GCC_4_9_RESULT = LinuxToolchainTest.GCC_4_9_RESULT - GXX_4_9_RESULT = LinuxToolchainTest.GXX_4_9_RESULT + DEFAULT_GCC_RESULT = LinuxToolchainTest.DEFAULT_GCC_RESULT + DEFAULT_GXX_RESULT = LinuxToolchainTest.DEFAULT_GXX_RESULT def test_gcc(self): self.do_toolchain_test(self.PATHS, { - 'c_compiler': self.GCC_4_9_RESULT, - 'cxx_compiler': self.GXX_4_9_RESULT, + 'c_compiler': self.DEFAULT_GCC_RESULT, + 'cxx_compiler': self.DEFAULT_GXX_RESULT, }) diff --git a/taskcluster/ci/build/linux.yml b/taskcluster/ci/build/linux.yml index f5205cc3084d..a844386c5c08 100644 --- a/taskcluster/ci/build/linux.yml +++ b/taskcluster/ci/build/linux.yml @@ -205,7 +205,7 @@ linux64-base-toolchains/opt: need-xvfb: true toolchains: - linux64-clang-3.9 - - linux64-gcc-4.9 + - linux64-gcc-6 - linux64-rust-1.24 - linux64-sccache @@ -235,7 +235,7 @@ linux64-base-toolchains/debug: need-xvfb: true toolchains: - linux64-clang-3.9 - - linux64-gcc-4.9 + - linux64-gcc-6 - linux64-rust-1.24 - linux64-sccache From d7f7c75314cd8292936f324f4a1beac722ff8055 Mon Sep 17 00:00:00 2001 From: Daisuke Akatsuka Date: Tue, 3 Apr 2018 08:35:46 +0900 Subject: [PATCH 20/25] Bug 1449625 - Part 1: Select the computed view instead of animation inspector in browser_markup_events_click_to_close.js test. r=gl --- .../browser_markup_events_click_to_close.js | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/devtools/client/inspector/markup/test/browser_markup_events_click_to_close.js b/devtools/client/inspector/markup/test/browser_markup_events_click_to_close.js index 8d596cf1fe2d..42043084fa23 100644 --- a/devtools/client/inspector/markup/test/browser_markup_events_click_to_close.js +++ b/devtools/client/inspector/markup/test/browser_markup_events_click_to_close.js @@ -53,20 +53,15 @@ add_task(async function() { await onShown; info("event tooltip for the second div is shown"); - info("Click on the animation inspector tab"); + info("Click on the computed view tab"); let onHighlighterHidden = toolbox.once("node-unhighlight"); - let onTabInspectorSelected = inspector.sidebar.once("newanimationinspector-selected"); - let onInspectorUpdated = inspector.once("inspector-updated"); - let animationInspectorTab = - inspector.panelDoc.querySelector("#newanimationinspector-tab"); - EventUtils.synthesizeMouseAtCenter(animationInspectorTab, {}, + let onTabComputedViewSelected = inspector.sidebar.once("computedview-selected"); + let computedViewTab = inspector.panelDoc.querySelector("#computedview-tab"); + EventUtils.synthesizeMouseAtCenter(computedViewTab, {}, inspector.panelDoc.defaultView); - await onTabInspectorSelected; - info("animation inspector was selected"); - - await onInspectorUpdated; - info("animation inspector was updated"); + await onTabComputedViewSelected; + info("computed view was selected"); await onHighlighterHidden; info("box model highlighter hidden after moving the mouse out of the markup view"); From b4fc01f3cb070383a4b831bea8084ddffd39f536 Mon Sep 17 00:00:00 2001 From: Daisuke Akatsuka Date: Tue, 3 Apr 2018 08:36:00 +0900 Subject: [PATCH 21/25] Bug 1449625 - Part 2: Turn on new animation inspector for beta tests. r=gl --- devtools/client/inspector/animation/test/head.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devtools/client/inspector/animation/test/head.js b/devtools/client/inspector/animation/test/head.js index 3d01dfa43905..3679bea9874d 100644 --- a/devtools/client/inspector/animation/test/head.js +++ b/devtools/client/inspector/animation/test/head.js @@ -15,10 +15,14 @@ const TAB_NAME = "newanimationinspector"; const ANIMATION_L10N = new LocalizationHelper("devtools/client/locales/animationinspector.properties"); +// Enable new animation inspector. +Services.prefs.setBoolPref("devtools.new-animationinspector.enabled", true); + // Auto clean-up when a test ends. // Clean-up all prefs that might have been changed during a test run // (safer here because if the test fails, then the pref is never reverted) registerCleanupFunction(() => { + Services.prefs.clearUserPref("devtools.new-animationinspector.enabled"); Services.prefs.clearUserPref("devtools.toolsidebar-width.inspector"); }); From fb33aab18336d5e3ec30bd6aea502fafe9dd41d2 Mon Sep 17 00:00:00 2001 From: Daisuke Akatsuka Date: Tue, 3 Apr 2018 09:12:53 +0900 Subject: [PATCH 22/25] Bug 1449497: Use Component instead of PureComponent. r=gl --- .../inspector/animation/components/AnimatedPropertyList.js | 4 ++-- .../client/inspector/animation/components/AnimationItem.js | 4 ++-- .../client/inspector/animation/components/AnimationTarget.js | 4 ++-- .../animation/components/AnimationTimelineTickList.js | 5 ++--- devtools/client/inspector/animation/components/App.js | 4 ++-- .../inspector/animation/components/NoAnimationPanel.js | 4 ++-- .../inspector/animation/components/graph/SummaryGraphPath.js | 4 ++-- 7 files changed, 14 insertions(+), 15 deletions(-) diff --git a/devtools/client/inspector/animation/components/AnimatedPropertyList.js b/devtools/client/inspector/animation/components/AnimatedPropertyList.js index 43858435f6bb..88203fc280b4 100644 --- a/devtools/client/inspector/animation/components/AnimatedPropertyList.js +++ b/devtools/client/inspector/animation/components/AnimatedPropertyList.js @@ -4,13 +4,13 @@ "use strict"; -const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react"); +const { Component, createFactory } = require("devtools/client/shared/vendor/react"); const dom = require("devtools/client/shared/vendor/react-dom-factories"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); const AnimatedPropertyItem = createFactory(require("./AnimatedPropertyItem")); -class AnimatedPropertyList extends PureComponent { +class AnimatedPropertyList extends Component { static get propTypes() { return { animation: PropTypes.object.isRequired, diff --git a/devtools/client/inspector/animation/components/AnimationItem.js b/devtools/client/inspector/animation/components/AnimationItem.js index f4ed048b7aa6..813cbec26797 100644 --- a/devtools/client/inspector/animation/components/AnimationItem.js +++ b/devtools/client/inspector/animation/components/AnimationItem.js @@ -5,14 +5,14 @@ "use strict"; const { connect } = require("devtools/client/shared/vendor/react-redux"); -const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react"); +const { Component, createFactory } = require("devtools/client/shared/vendor/react"); const dom = require("devtools/client/shared/vendor/react-dom-factories"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); const AnimationTarget = createFactory(require("./AnimationTarget")); const SummaryGraph = createFactory(require("./graph/SummaryGraph")); -class AnimationItem extends PureComponent { +class AnimationItem extends Component { static get propTypes() { return { animation: PropTypes.object.isRequired, diff --git a/devtools/client/inspector/animation/components/AnimationTarget.js b/devtools/client/inspector/animation/components/AnimationTarget.js index 4d1f3fbd2c61..f64a50174692 100644 --- a/devtools/client/inspector/animation/components/AnimationTarget.js +++ b/devtools/client/inspector/animation/components/AnimationTarget.js @@ -4,7 +4,7 @@ "use strict"; -const { PureComponent } = require("devtools/client/shared/vendor/react"); +const { Component } = require("devtools/client/shared/vendor/react"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); const dom = require("devtools/client/shared/vendor/react-dom-factories"); const { translateNodeFrontToGrip } = require("devtools/client/inspector/shared/utils"); @@ -13,7 +13,7 @@ const { REPS, MODE } = require("devtools/client/shared/components/reps/reps"); const { Rep } = REPS; const ElementNode = REPS.ElementNode; -class AnimationTarget extends PureComponent { +class AnimationTarget extends Component { static get propTypes() { return { animation: PropTypes.object.isRequired, diff --git a/devtools/client/inspector/animation/components/AnimationTimelineTickList.js b/devtools/client/inspector/animation/components/AnimationTimelineTickList.js index 196620f3a692..a073942f4737 100644 --- a/devtools/client/inspector/animation/components/AnimationTimelineTickList.js +++ b/devtools/client/inspector/animation/components/AnimationTimelineTickList.js @@ -4,8 +4,7 @@ "use strict"; -const { createFactory, PureComponent } = - require("devtools/client/shared/vendor/react"); +const { Component, createFactory } = require("devtools/client/shared/vendor/react"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); const dom = require("devtools/client/shared/vendor/react-dom-factories"); const { connect } = require("devtools/client/shared/vendor/react-redux"); @@ -18,7 +17,7 @@ const { findOptimalTimeInterval } = require("../utils/utils"); // The minimum spacing between 2 time graduation headers in the timeline (px). const TIME_GRADUATION_MIN_SPACING = 40; -class AnimationTimelineTickList extends PureComponent { +class AnimationTimelineTickList extends Component { static get propTypes() { return { sidebarWidth: PropTypes.number.isRequired, diff --git a/devtools/client/inspector/animation/components/App.js b/devtools/client/inspector/animation/components/App.js index 455b32f026c5..479f71701d79 100644 --- a/devtools/client/inspector/animation/components/App.js +++ b/devtools/client/inspector/animation/components/App.js @@ -4,7 +4,7 @@ "use strict"; -const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react"); +const { Component, createFactory } = require("devtools/client/shared/vendor/react"); const dom = require("devtools/client/shared/vendor/react-dom-factories"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); const { connect } = require("devtools/client/shared/vendor/react-redux"); @@ -15,7 +15,7 @@ const AnimationToolbar = createFactory(require("./AnimationToolbar")); const NoAnimationPanel = createFactory(require("./NoAnimationPanel")); const SplitBox = createFactory(require("devtools/client/shared/components/splitter/SplitBox")); -class App extends PureComponent { +class App extends Component { static get propTypes() { return { addAnimationsCurrentTimeListener: PropTypes.func.isRequired, diff --git a/devtools/client/inspector/animation/components/NoAnimationPanel.js b/devtools/client/inspector/animation/components/NoAnimationPanel.js index 3ed21d19d024..a2351f865701 100644 --- a/devtools/client/inspector/animation/components/NoAnimationPanel.js +++ b/devtools/client/inspector/animation/components/NoAnimationPanel.js @@ -4,7 +4,7 @@ "use strict"; -const { PureComponent } = require("devtools/client/shared/vendor/react"); +const { Component } = require("devtools/client/shared/vendor/react"); const dom = require("devtools/client/shared/vendor/react-dom-factories"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); const { connect } = require("devtools/client/shared/vendor/react-redux"); @@ -13,7 +13,7 @@ const { LocalizationHelper } = require("devtools/shared/l10n"); const L10N = new LocalizationHelper("devtools/client/locales/animationinspector.properties"); -class NoAnimationPanel extends PureComponent { +class NoAnimationPanel extends Component { static get propTypes() { return { elementPickerEnabled: PropTypes.bool.isRequired, diff --git a/devtools/client/inspector/animation/components/graph/SummaryGraphPath.js b/devtools/client/inspector/animation/components/graph/SummaryGraphPath.js index b3821d4c4fb0..2b9548aea3c2 100644 --- a/devtools/client/inspector/animation/components/graph/SummaryGraphPath.js +++ b/devtools/client/inspector/animation/components/graph/SummaryGraphPath.js @@ -4,7 +4,7 @@ "use strict"; -const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react"); +const { Component, createFactory } = require("devtools/client/shared/vendor/react"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); const dom = require("devtools/client/shared/vendor/react-dom-factories"); const ReactDOM = require("devtools/client/shared/vendor/react-dom"); @@ -18,7 +18,7 @@ const { DEFAULT_GRAPH_HEIGHT } = require("../../utils/graph-helper"); // Minimum opacity for semitransparent fill color for keyframes's easing graph. const MIN_KEYFRAMES_EASING_OPACITY = 0.5; -class SummaryGraphPath extends PureComponent { +class SummaryGraphPath extends Component { static get propTypes() { return { animation: PropTypes.object.isRequired, From 82c740d1b12b7b63e2181a40d79ee8a94f3162e2 Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Tue, 3 Apr 2018 12:13:48 +1200 Subject: [PATCH 23/25] Bug 1450360 - Respect the z-index property set on scrollbars. r=mstange --HG-- extra : rebase_source : 0288513aaa2216c14de501417ff3b9f405ee97a5 --- layout/generic/nsGfxScrollFrame.cpp | 45 +++++++++++-------- .../painting/RetainedDisplayListBuilder.cpp | 10 ++++- layout/painting/nsDisplayList.cpp | 3 +- layout/painting/nsDisplayList.h | 4 ++ 4 files changed, 41 insertions(+), 21 deletions(-) diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index b138409fe40e..510a61d40383 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -2998,12 +2998,16 @@ ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange, nsAtom* aOrig } } -static int32_t +static Maybe MaxZIndexInList(nsDisplayList* aList, nsDisplayListBuilder* aBuilder) { - int32_t maxZIndex = -1; + Maybe maxZIndex = Nothing(); for (nsDisplayItem* item = aList->GetBottom(); item; item = item->GetAbove()) { - maxZIndex = std::max(maxZIndex, item->ZIndex()); + if (!maxZIndex) { + maxZIndex = Some(item->ZIndex()); + } else { + maxZIndex = Some(std::max(maxZIndex.value(), item->ZIndex())); + } } return maxZIndex; } @@ -3012,10 +3016,10 @@ template static void AppendInternalItemToTop(const nsDisplayListSet& aLists, T* aItem, - int32_t aZIndex) + const Maybe& aZIndex) { - if (aZIndex >= 0) { - aItem->SetOverrideZIndex(aZIndex); + if (aZIndex) { + aItem->SetOverrideZIndex(aZIndex.value()); aLists.PositionedDescendants()->AppendToTop(aItem); } else { aLists.Content()->AppendToTop(aItem); @@ -3060,9 +3064,12 @@ AppendToTop(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists, // We want overlay scrollbars to always be on top of the scrolled content, // but we don't want them to unnecessarily cover overlapping elements from // outside our scroll frame. - int32_t zIndex = -1; + Maybe zIndex = Nothing(); if (aFlags & APPEND_OVERLAY) { zIndex = MaxZIndexInList(aLists.PositionedDescendants(), aBuilder); + } else if (aSourceFrame->StylePosition()->mZIndex.GetUnit() == eStyleUnit_Integer) { + zIndex = Some(aSourceFrame->StylePosition()->mZIndex.GetIntValue()); + } AppendInternalItemToTop(aLists, newItem, zIndex); } else { @@ -3145,6 +3152,10 @@ ScrollFrameHelper::AppendScrollPartsTo(nsDisplayListBuilder* aBuilder, flags |= nsDisplayOwnLayerFlags::eHorizontalScrollbar; appendToTopFlags |= APPEND_SCROLLBAR_CONTAINER; } + if (scrollParts[i] == mResizerBox && + !HasResizer()) { + continue; + } // The display port doesn't necessarily include the scrollbars, so just // include all of the scrollbars if we are in a RCD-RSF. We only do @@ -3153,6 +3164,9 @@ ScrollFrameHelper::AppendScrollPartsTo(nsDisplayListBuilder* aBuilder, nsRect visible = mIsRoot && mOuter->PresContext()->IsRootContentDocument() ? scrollParts[i]->GetVisualOverflowRectRelativeToParent() : aBuilder->GetVisibleRect(); + if (visible.IsEmpty()) { + continue; + } nsRect dirty = mIsRoot && mOuter->PresContext()->IsRootContentDocument() ? scrollParts[i]->GetVisualOverflowRectRelativeToParent() : aBuilder->GetDirtyRect(); @@ -3182,8 +3196,10 @@ ScrollFrameHelper::AppendScrollPartsTo(nsDisplayListBuilder* aBuilder, if (aPositioned) { appendToTopFlags |= APPEND_POSITIONED; } - if (overlayScrollbars) { + if (overlayScrollbars || + scrollParts[i] == mResizerBox) { appendToTopFlags |= APPEND_OVERLAY; + aBuilder->SetBuiltOverlayScrollbars(true); } { @@ -3328,15 +3344,6 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder, } } - // Adding overlay scrollbars requires us to look at the display list - // for the highest z-index item, which isn't possible during partial - // building. Mark the frame modified and do a full rebuild of this - // scrollframe. - if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) && - aBuilder->IsRetainingDisplayList()) { - aBuilder->MarkCurrentFrameModifiedDuringBuilding(); - } - // It's safe to get this value before the DecideScrollableLayer call below // because that call cannot create a displayport for root scroll frames, // and hence it cannot create an ignore scroll frame. @@ -3705,13 +3712,13 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder, nsDisplayCompositorHitTestInfo* hitInfo = MakeDisplayItem(aBuilder, mScrolledFrame, info, 1, Some(mScrollPort + aBuilder->ToReferenceFrame(mOuter))); - AppendInternalItemToTop(scrolledContent, hitInfo, INT32_MAX); + AppendInternalItemToTop(scrolledContent, hitInfo, Some(INT32_MAX)); } if (aBuilder->IsBuildingLayerEventRegions()) { nsDisplayLayerEventRegions* inactiveRegionItem = MakeDisplayItem(aBuilder, mScrolledFrame, 1); inactiveRegionItem->AddInactiveScrollPort(mScrolledFrame, mScrollPort + aBuilder->ToReferenceFrame(mOuter)); - AppendInternalItemToTop(scrolledContent, inactiveRegionItem, INT32_MAX); + AppendInternalItemToTop(scrolledContent, inactiveRegionItem, Some(INT32_MAX)); } } diff --git a/layout/painting/RetainedDisplayListBuilder.cpp b/layout/painting/RetainedDisplayListBuilder.cpp index ff646a566443..b06b4c0d183e 100644 --- a/layout/painting/RetainedDisplayListBuilder.cpp +++ b/layout/painting/RetainedDisplayListBuilder.cpp @@ -1109,7 +1109,15 @@ RetainedDisplayListBuilder::AttemptPartialUpdate( // Do not allow partial builds if the retained display list is empty, or if // ShouldBuildPartial heuristic fails. - const bool shouldBuildPartial = !mList.IsEmpty() && ShouldBuildPartial(modifiedFrames.Frames()); + bool shouldBuildPartial = !List()->IsEmpty() && ShouldBuildPartial(modifiedFrames.Frames()); + + // We don't support retaining with overlay scrollbars, since they require + // us to look at the display list and pick the highest z-index, which + // we can't do during partial building. + if (mBuilder.BuiltOverlayScrollbars()) { + shouldBuildPartial = false; + mBuilder.SetBuiltOverlayScrollbars(false); + } if (mPreviousCaret != mBuilder.GetCaretFrame()) { if (mPreviousCaret) { diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index 3ba4e55bcfd8..acbf1abd9c36 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -1003,7 +1003,8 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame, mBuildingInvisibleItems(false), mHitTestIsForVisibility(false), mIsBuilding(false), - mInInvalidSubtree(false) + mInInvalidSubtree(false), + mBuiltOverlayScrollbars(false) { MOZ_COUNT_CTOR(nsDisplayListBuilder); diff --git a/layout/painting/nsDisplayList.h b/layout/painting/nsDisplayList.h index 8fce01436ec8..2093740ea787 100644 --- a/layout/painting/nsDisplayList.h +++ b/layout/painting/nsDisplayList.h @@ -839,6 +839,9 @@ public: */ bool IsInSubdocument() { return mPresShellStates.Length() > 1; } + void SetBuiltOverlayScrollbars(bool aOverlayScrollbars) { mBuiltOverlayScrollbars = aOverlayScrollbars; } + bool BuiltOverlayScrollbars() { return mBuiltOverlayScrollbars; } + /** * Return true if we're currently building a display list for the presshell * of a chrome document, or if we're building the display list for a popup. @@ -2008,6 +2011,7 @@ private: bool mInInvalidSubtree; bool mBuildCompositorHitTestInfo; bool mLessEventRegionItems; + bool mBuiltOverlayScrollbars; }; class nsDisplayItem; From 0626b8ac5650f4b162a6c260f51e87e45148d0ed Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Mon, 2 Apr 2018 20:21:21 -0400 Subject: [PATCH 24/25] Bug 1442669 - guard against excessively large glyphs in SkFontHost_cairo. r=jfkthame MozReview-Commit-ID: 1X2524Q5K8E --- gfx/skia/skia/src/ports/SkFontHost_cairo.cpp | 52 +++++++++++--------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp b/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp index 884e97e47aa4..5790a9c1c621 100644 --- a/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp +++ b/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp @@ -642,10 +642,19 @@ void SkScalerContext_CairoFT::generateMetrics(SkGlyph* glyph) prepareGlyph(face->glyph); + if (fRec.fFlags & SkScalerContext::kVertical_Flag) { + glyph->fAdvanceX = -SkFDot6ToFloat(face->glyph->advance.x); + glyph->fAdvanceY = SkFDot6ToFloat(face->glyph->advance.y); + } else { + glyph->fAdvanceX = SkFDot6ToFloat(face->glyph->advance.x); + glyph->fAdvanceY = -SkFDot6ToFloat(face->glyph->advance.y); + } + + SkIRect bounds; switch (face->glyph->format) { case FT_GLYPH_FORMAT_OUTLINE: if (!face->glyph->outline.n_contours) { - break; + return; } FT_BBox bbox; @@ -654,10 +663,10 @@ void SkScalerContext_CairoFT::generateMetrics(SkGlyph* glyph) bbox.yMin &= ~63; bbox.xMax = (bbox.xMax + 63) & ~63; bbox.yMax = (bbox.yMax + 63) & ~63; - glyph->fWidth = SkToU16(SkFDot6Floor(bbox.xMax - bbox.xMin)); - glyph->fHeight = SkToU16(SkFDot6Floor(bbox.yMax - bbox.yMin)); - glyph->fTop = -SkToS16(SkFDot6Floor(bbox.yMax)); - glyph->fLeft = SkToS16(SkFDot6Floor(bbox.xMin)); + bounds = SkIRect::MakeLTRB(SkFDot6Floor(bbox.xMin), + -SkFDot6Floor(bbox.yMax), + SkFDot6Floor(bbox.xMax), + -SkFDot6Floor(bbox.yMin)); if (isLCD(fRec)) { // In FreeType < 2.8.1, LCD filtering, if explicitly used, may @@ -669,11 +678,9 @@ void SkScalerContext_CairoFT::generateMetrics(SkGlyph* glyph) // here. generateGlyphImage will detect if the mask is smaller // than the bounds and clip things appropriately. if (fRec.fFlags & kLCD_Vertical_Flag) { - glyph->fTop -= 1; - glyph->fHeight += 2; + bounds.outset(0, 1); } else { - glyph->fLeft -= 1; - glyph->fWidth += 2; + bounds.outset(1, 0); } } break; @@ -702,15 +709,15 @@ void SkScalerContext_CairoFT::generateMetrics(SkGlyph* glyph) SkRect destRect; fShapeMatrix.mapRect(&destRect, srcRect); SkIRect glyphRect = destRect.roundOut(); - glyph->fWidth = SkToU16(glyphRect.width()); - glyph->fHeight = SkToU16(glyphRect.height()); - glyph->fTop = SkToS16(SkScalarRoundToInt(destRect.fTop)); - glyph->fLeft = SkToS16(SkScalarRoundToInt(destRect.fLeft)); + bounds = SkIRect::MakeXYWH(SkScalarRoundToInt(destRect.fLeft), + SkScalarRoundToInt(destRect.fTop), + glyphRect.width(), + glyphRect.height()); } else { - glyph->fWidth = SkToU16(face->glyph->bitmap.width); - glyph->fHeight = SkToU16(face->glyph->bitmap.rows); - glyph->fTop = -SkToS16(face->glyph->bitmap_top); - glyph->fLeft = SkToS16(face->glyph->bitmap_left); + bounds = SkIRect::MakeXYWH(face->glyph->bitmap_left, + -face->glyph->bitmap_top, + face->glyph->bitmap.width, + face->glyph->bitmap.rows); } break; default: @@ -718,12 +725,11 @@ void SkScalerContext_CairoFT::generateMetrics(SkGlyph* glyph) return; } - if (fRec.fFlags & SkScalerContext::kVertical_Flag) { - glyph->fAdvanceX = -SkFDot6ToFloat(face->glyph->advance.x); - glyph->fAdvanceY = SkFDot6ToFloat(face->glyph->advance.y); - } else { - glyph->fAdvanceX = SkFDot6ToFloat(face->glyph->advance.x); - glyph->fAdvanceY = -SkFDot6ToFloat(face->glyph->advance.y); + if (SkIRect::MakeXYWH(SHRT_MIN, SHRT_MIN, USHRT_MAX, USHRT_MAX).contains(bounds)) { + glyph->fWidth = SkToU16(bounds.width()); + glyph->fHeight = SkToU16(bounds.height()); + glyph->fLeft = SkToS16(bounds.left()); + glyph->fTop = SkToS16(bounds.top()); } } From 361d95ecbf5c0f00f59ca1614088db1947abc565 Mon Sep 17 00:00:00 2001 From: Daisuke Akatsuka Date: Tue, 3 Apr 2018 10:29:12 +0900 Subject: [PATCH 25/25] Bug 1447259: Change the tested animation in browser_animation_summary-graph_computed-timing-path_different-timescale.js test since the animation can intermittently already be finished during selectNodeAndWaitForAnimations. r=gl --- ...ummary-graph_computed-timing-path_different-timescale.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/devtools/client/inspector/animation/test/browser_animation_summary-graph_computed-timing-path_different-timescale.js b/devtools/client/inspector/animation/test/browser_animation_summary-graph_computed-timing-path_different-timescale.js index 44089c9ce999..8885d5487c0c 100644 --- a/devtools/client/inspector/animation/test/browser_animation_summary-graph_computed-timing-path_different-timescale.js +++ b/devtools/client/inspector/animation/test/browser_animation_summary-graph_computed-timing-path_different-timescale.js @@ -10,14 +10,14 @@ add_task(async function() { const { inspector, panel } = await openAnimationInspector(); info("Checking the path for different time scale"); - await selectNodeAndWaitForAnimations(".no-compositor", inspector); + await selectNodeAndWaitForAnimations(".animated", inspector); const pathStringA = panel.querySelector(".animation-iteration-path").getAttribute("d"); info("Select animation which has different time scale from no-compositor"); - await selectNodeAndWaitForAnimations(".endDelayed", inspector); + await selectNodeAndWaitForAnimations("#endDelayed", inspector); info("Select no-compositor again"); - await selectNodeAndWaitForAnimations(".no-compositor", inspector); + await selectNodeAndWaitForAnimations(".animated", inspector); const pathStringB = panel.querySelector(".animation-iteration-path").getAttribute("d"); is(pathStringA, pathStringB, "Path string should be same even change the time scale"); });