diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 8eff6bfd68..a0d1c5b791 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -2702,7 +2702,7 @@ nsCSSFrameConstructor::ConstructRootFrame() nsContainerFrame::SyncFrameViewProperties(mPresShell->GetPresContext(), viewportFrame, viewportPseudoStyle, rootView); nsContainerFrame::SyncWindowProperties(mPresShell->GetPresContext(), viewportFrame, - rootView); + rootView, nullptr, nsContainerFrame::SET_ASYNC); // Make it an absolute container for fixed-pos elements viewportFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); @@ -9762,7 +9762,7 @@ nsCSSFrameConstructor::CreateNeededAnonFlexOrGridItems( FCItemIterator iter(aItems); do { // Advance iter past children that don't want to be wrapped - if (iter.SkipItemsThatDontNeedAnonFlexOrGridItem(aState)) { + if (iter.SkipItemsThatDontNeedAnonFlexOrGridItem(aState, containerType)) { // Hit the end of the items without finding any remaining children that // need to be wrapped. We're finished! return; @@ -9784,7 +9784,8 @@ nsCSSFrameConstructor::CreateNeededAnonFlexOrGridItems( FCItemIterator afterWhitespaceIter(iter); bool hitEnd = afterWhitespaceIter.SkipWhitespace(aState); bool nextChildNeedsAnonItem = - !hitEnd && afterWhitespaceIter.item().NeedsAnonFlexOrGridItem(aState); + !hitEnd && afterWhitespaceIter.item().NeedsAnonFlexOrGridItem(aState, + containerType); if (!nextChildNeedsAnonItem) { // There's nothing after the whitespace that we need to wrap, so we @@ -9798,7 +9799,7 @@ nsCSSFrameConstructor::CreateNeededAnonFlexOrGridItems( // we jump back to the beginning of the loop to skip over that child // (and anything else non-wrappable after it) MOZ_ASSERT(!iter.IsDone() && - !iter.item().NeedsAnonFlexOrGridItem(aState), + !iter.item().NeedsAnonFlexOrGridItem(aState, containerType), "hitEnd and/or nextChildNeedsAnonItem lied"); continue; } @@ -9808,7 +9809,7 @@ nsCSSFrameConstructor::CreateNeededAnonFlexOrGridItems( // anonymous flex/grid item. Now we see how many children after it also want // to be wrapped in an anonymous flex/grid item. FCItemIterator endIter(iter); // iterator to find the end of the group - endIter.SkipItemsThatNeedAnonFlexOrGridItem(aState); + endIter.SkipItemsThatNeedAnonFlexOrGridItem(aState, containerType); NS_ASSERTION(iter != endIter, "Should've had at least one wrappable child to seek past"); @@ -12008,8 +12009,9 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, // Check if we're adding to-be-wrapped content right *after* an existing // anonymous flex or grid item (which would need to absorb this content). + nsIAtom* containerType = aFrame->GetType(); if (aPrevSibling && IsAnonymousFlexOrGridItem(aPrevSibling) && - iter.item().NeedsAnonFlexOrGridItem(aState)) { + iter.item().NeedsAnonFlexOrGridItem(aState, containerType)) { RecreateFramesForContent(aFrame->GetContent(), true, REMOVE_FOR_RECONSTRUCTION, nullptr); return true; @@ -12021,7 +12023,7 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, // Jump to the last entry in the list iter.SetToEnd(); iter.Prev(); - if (iter.item().NeedsAnonFlexOrGridItem(aState)) { + if (iter.item().NeedsAnonFlexOrGridItem(aState, containerType)) { RecreateFramesForContent(aFrame->GetContent(), true, REMOVE_FOR_RECONSTRUCTION, nullptr); return true; @@ -12046,10 +12048,11 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, FCItemIterator iter(aItems); // Skip over things that _do_ need an anonymous flex item, because // they're perfectly happy to go here -- they won't cause a reframe. - if (!iter.SkipItemsThatNeedAnonFlexOrGridItem(aState)) { + nsIFrame* containerFrame = aFrame->GetParent(); + if (!iter.SkipItemsThatNeedAnonFlexOrGridItem(aState, + containerFrame->GetType())) { // We hit something that _doesn't_ need an anonymous flex item! // Rebuild the flex container to bust it out. - nsIFrame* containerFrame = aFrame->GetParent(); RecreateFramesForContent(containerFrame->GetContent(), true, REMOVE_FOR_RECONSTRUCTION, nullptr); return true; @@ -12501,14 +12504,17 @@ Iterator::SkipItemsNotWantingParentType(ParentType aParentType) bool nsCSSFrameConstructor::FrameConstructionItem:: - NeedsAnonFlexOrGridItem(const nsFrameConstructorState& aState) + NeedsAnonFlexOrGridItem(const nsFrameConstructorState& aState, + nsIAtom* aContainerType) { if (mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) { // This will be an inline non-replaced box. return true; } - if (!(mFCData->mBits & FCDATA_DISALLOW_OUT_OF_FLOW) && + // Bug 874718: Flex containers still wrap placeholders; Grid containers don't. + if (aContainerType == nsGkAtoms::flexContainerFrame && + !(mFCData->mBits & FCDATA_DISALLOW_OUT_OF_FLOW) && aState.GetGeometricParent(mStyleContext->StyleDisplay(), nullptr)) { // We're abspos or fixedpos, which means we'll spawn a placeholder which // we'll need to wrap in an anonymous flex item. So, we just treat @@ -12523,10 +12529,11 @@ nsCSSFrameConstructor::FrameConstructionItem:: inline bool nsCSSFrameConstructor::FrameConstructionItemList:: Iterator::SkipItemsThatNeedAnonFlexOrGridItem( - const nsFrameConstructorState& aState) + const nsFrameConstructorState& aState, + nsIAtom* aContainerType) { NS_PRECONDITION(!IsDone(), "Shouldn't be done yet"); - while (item().NeedsAnonFlexOrGridItem(aState)) { + while (item().NeedsAnonFlexOrGridItem(aState, aContainerType)) { Next(); if (IsDone()) { return true; @@ -12538,10 +12545,11 @@ Iterator::SkipItemsThatNeedAnonFlexOrGridItem( inline bool nsCSSFrameConstructor::FrameConstructionItemList:: Iterator::SkipItemsThatDontNeedAnonFlexOrGridItem( - const nsFrameConstructorState& aState) + const nsFrameConstructorState& aState, + nsIAtom* aContainerType) { NS_PRECONDITION(!IsDone(), "Shouldn't be done yet"); - while (!(item().NeedsAnonFlexOrGridItem(aState))) { + while (!(item().NeedsAnonFlexOrGridItem(aState, aContainerType))) { Next(); if (IsDone()) { return true; diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h index 8eecb303f2..b17db4663c 100644 --- a/layout/base/nsCSSFrameConstructor.h +++ b/layout/base/nsCSSFrameConstructor.h @@ -960,13 +960,13 @@ private: // Return whether the iterator is done after doing that. // The iterator must not be done when this is called. inline bool SkipItemsThatNeedAnonFlexOrGridItem( - const nsFrameConstructorState& aState); + const nsFrameConstructorState& aState, nsIAtom* aContainerType); // Skip to the first frame that is a non-replaced inline or is // positioned. Return whether the iterator is done after doing that. // The iterator must not be done when this is called. inline bool SkipItemsThatDontNeedAnonFlexOrGridItem( - const nsFrameConstructorState& aState); + const nsFrameConstructorState& aState, nsIAtom* aContainerType); // Skip over all items that do not want a ruby parent. Return whether // the iterator is done after doing that. The iterator must not be done @@ -1103,7 +1103,8 @@ private: // Indicates whether (when in a flex or grid container) this item needs // to be wrapped in an anonymous block. - bool NeedsAnonFlexOrGridItem(const nsFrameConstructorState& aState); + bool NeedsAnonFlexOrGridItem(const nsFrameConstructorState& aState, + nsIAtom* aContainerType); // Don't call this unless the frametree really depends on the answer! // Especially so for generated content, where we don't want to reframe diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index e93a65a8e4..3c02e85481 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -138,10 +138,10 @@ typedef struct CapturingContentInfo { nsIContent* mContent; } CapturingContentInfo; -// d910f009-d209-74c1-6b04-30c83c051c78 +// a7ef8bb3-d628-4965-80f3-a326e089fb7f #define NS_IPRESSHELL_IID \ - { 0xd910f009, 0xd209, 0x74c1, \ - { 0x6b, 0x04, 0x30, 0xc8, 0x3c, 0x05, 0x1c, 0x78 } } +{ 0xa7ef8bb3, 0xd628, 0x4965, \ + { 0x80, 0xf3, 0xa3, 0x26, 0xe0, 0x89, 0xfb, 0x7f } } // debug VerifyReflow flags #define VERIFY_REFLOW_ON 0x01 @@ -1672,6 +1672,8 @@ public: bool HasPendingReflow() const { return mReflowScheduled || mReflowContinueTimer; } + void SyncWindowProperties(nsView* aView); + protected: friend class nsRefreshDriver; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 03fba052c6..1f715bbc6d 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -9276,7 +9276,8 @@ PresShell::DoReflow(nsIFrame* target, bool aInterruptible) target->GetView(), boundsRelativeToTarget); nsContainerFrame::SyncWindowProperties(mPresContext, target, - target->GetView(), &rcx); + target->GetView(), &rcx, + nsContainerFrame::SET_ASYNC); target->DidReflow(mPresContext, nullptr, nsDidReflowStatus::FINISHED); if (target == rootFrame && size.BSize(wm) == NS_UNCONSTRAINEDSIZE) { @@ -11110,3 +11111,13 @@ PresShell::ResumePainting() mPaintingIsFrozen = false; GetPresContext()->RefreshDriver()->Thaw(); } + +void +nsIPresShell::SyncWindowProperties(nsView* aView) +{ + nsIFrame* frame = aView->GetFrame(); + if (frame && mPresContext) { + nsRenderingContext rcx(CreateReferenceRenderingContext()); + nsContainerFrame::SyncWindowProperties(mPresContext, frame, aView, &rcx, 0); + } +} diff --git a/layout/forms/nsNumberControlFrame.cpp b/layout/forms/nsNumberControlFrame.cpp index 9b74d31a25..8d82d63415 100644 --- a/layout/forms/nsNumberControlFrame.cpp +++ b/layout/forms/nsNumberControlFrame.cpp @@ -124,78 +124,106 @@ nsNumberControlFrame::Reflow(nsPresContext* aPresContext, nsFormControlFrame::RegUnRegAccessKey(this, true); } - // The width of our content box, which is the available width + const WritingMode myWM = aReflowState.GetWritingMode(); + + // The ISize of our content box, which is the available ISize // for our anonymous content: - const nscoord contentBoxWidth = aReflowState.ComputedWidth(); - nscoord contentBoxHeight = aReflowState.ComputedHeight(); + const nscoord contentBoxISize = aReflowState.ComputedISize(); + nscoord contentBoxBSize = aReflowState.ComputedBSize(); + + // Figure out our border-box sizes as well (by adding borderPadding to + // content-box sizes): + const nscoord borderBoxISize = contentBoxISize + + aReflowState.ComputedLogicalBorderPadding().IStartEnd(myWM); + + nscoord borderBoxBSize; + if (contentBoxBSize != NS_INTRINSICSIZE) { + borderBoxBSize = contentBoxBSize + + aReflowState.ComputedLogicalBorderPadding().BStartEnd(myWM); + } // else, we'll figure out borderBoxBSize after we resolve contentBoxBSize. nsIFrame* outerWrapperFrame = mOuterWrapper->GetPrimaryFrame(); if (!outerWrapperFrame) { // display:none? - if (contentBoxHeight == NS_INTRINSICSIZE) { - contentBoxHeight = 0; + if (contentBoxBSize == NS_INTRINSICSIZE) { + contentBoxBSize = 0; + borderBoxBSize = + aReflowState.ComputedLogicalBorderPadding().BStartEnd(myWM); } } else { NS_ASSERTION(outerWrapperFrame == mFrames.FirstChild(), "huh?"); nsHTMLReflowMetrics wrappersDesiredSize(aReflowState); - WritingMode wm = outerWrapperFrame->GetWritingMode(); - LogicalSize availSize = aReflowState.ComputedSize(wm); - availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; + WritingMode wrapperWM = outerWrapperFrame->GetWritingMode(); + LogicalSize availSize = aReflowState.ComputedSize(wrapperWM); + availSize.BSize(wrapperWM) = NS_UNCONSTRAINEDSIZE; nsHTMLReflowState wrapperReflowState(aPresContext, aReflowState, outerWrapperFrame, availSize); - // offsets of wrapper frame - nscoord xoffset = aReflowState.ComputedPhysicalBorderPadding().left + - wrapperReflowState.ComputedPhysicalMargin().left; - nscoord yoffset = aReflowState.ComputedPhysicalBorderPadding().top + - wrapperReflowState.ComputedPhysicalMargin().top; + // Convert wrapper margin into my own writing-mode (in case it differs): + LogicalMargin wrapperMargin = + wrapperReflowState.ComputedLogicalMargin().ConvertTo(myWM, wrapperWM); + + // offsets of wrapper frame within this frame: + LogicalPoint + wrapperOffset(myWM, + aReflowState.ComputedLogicalBorderPadding().IStart(myWM) + + wrapperMargin.IStart(myWM), + aReflowState.ComputedLogicalBorderPadding().BStart(myWM) + + wrapperMargin.BStart(myWM)); nsReflowStatus childStatus; ReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize, - wrapperReflowState, xoffset, yoffset, 0, childStatus); + wrapperReflowState, myWM, wrapperOffset, 0, 0, childStatus); MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus), - "We gave our child unconstrained height, so it should be complete"); + "We gave our child unconstrained available block-size, " + "so it should be complete"); - nscoord wrappersMarginBoxHeight = wrappersDesiredSize.Height() + - wrapperReflowState.ComputedPhysicalMargin().TopBottom(); + nscoord wrappersMarginBoxBSize = + wrappersDesiredSize.BSize(myWM) + wrapperMargin.BStartEnd(myWM); - if (contentBoxHeight == NS_INTRINSICSIZE) { + if (contentBoxBSize == NS_INTRINSICSIZE) { // We are intrinsically sized -- we should shrinkwrap the outer wrapper's - // height: - contentBoxHeight = wrappersMarginBoxHeight; + // block-size: + contentBoxBSize = wrappersMarginBoxBSize; - // Make sure we obey min/max-height in the case when we're doing intrinsic + // Make sure we obey min/max-bsize in the case when we're doing intrinsic // sizing (we get it for free when we have a non-intrinsic - // aReflowState.ComputedHeight()). Note that we do this before - // adjusting for borderpadding, since mComputedMaxHeight and - // mComputedMinHeight are content heights. - contentBoxHeight = - NS_CSS_MINMAX(contentBoxHeight, - aReflowState.ComputedMinHeight(), - aReflowState.ComputedMaxHeight()); + // aReflowState.ComputedBSize()). Note that we do this before + // adjusting for borderpadding, since ComputedMaxBSize and + // ComputedMinBSize are content heights. + contentBoxBSize = + NS_CSS_MINMAX(contentBoxBSize, + aReflowState.ComputedMinBSize(), + aReflowState.ComputedMaxBSize()); + + borderBoxBSize = contentBoxBSize + + aReflowState.ComputedLogicalBorderPadding().BStartEnd(myWM); } - // Center child vertically - nscoord extraSpace = contentBoxHeight - wrappersMarginBoxHeight; - yoffset += std::max(0, extraSpace / 2); + // Center child in block axis + nscoord extraSpace = contentBoxBSize - wrappersMarginBoxBSize; + wrapperOffset.B(myWM) += std::max(0, extraSpace / 2); + + // Needed in FinishReflowChild, for logical-to-physical conversion: + nscoord borderBoxWidth = myWM.IsVertical() ? + borderBoxBSize : borderBoxISize; // Place the child FinishReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize, - &wrapperReflowState, xoffset, yoffset, 0); + &wrapperReflowState, myWM, wrapperOffset, + borderBoxWidth, 0); aDesiredSize.SetBlockStartAscent( wrappersDesiredSize.BlockStartAscent() + outerWrapperFrame->BStart(aReflowState.GetWritingMode(), - contentBoxWidth)); + contentBoxISize)); } - aDesiredSize.Width() = contentBoxWidth + - aReflowState.ComputedPhysicalBorderPadding().LeftRight(); - aDesiredSize.Height() = contentBoxHeight + - aReflowState.ComputedPhysicalBorderPadding().TopBottom(); + LogicalSize logicalDesiredSize(myWM, borderBoxISize, borderBoxBSize); + aDesiredSize.SetSize(myWM, logicalDesiredSize); aDesiredSize.SetOverflowAreasToDesiredBounds(); diff --git a/layout/generic/nsBlockReflowState.cpp b/layout/generic/nsBlockReflowState.cpp index 35073b2031..75816d7bbd 100644 --- a/layout/generic/nsBlockReflowState.cpp +++ b/layout/generic/nsBlockReflowState.cpp @@ -670,6 +670,11 @@ nsBlockReflowState::CanPlaceFloat(nscoord aFloatISize, aFloatISize; } +// Return the inline-size that the float (including margins) will take up +// in the writing mode of the containing block. If this returns +// NS_UNCONSTRAINEDSIZE, we're dealing with an orthogonal block that +// has block-size:auto, and we'll need to actually reflow it to find out +// how much inline-size it will occupy in the containing block's mode. static nscoord FloatMarginISize(const nsHTMLReflowState& aCBReflowState, nscoord aFloatAvailableISize, @@ -691,9 +696,17 @@ FloatMarginISize(const nsHTMLReflowState& aCBReflowState, aFloatOffsetState.ComputedLogicalPadding().Size(wm), nsIFrame::ComputeSizeFlags::eShrinkWrap); - return floatSize.ISize(wm) + - aFloatOffsetState.ComputedLogicalMargin().IStartEnd(wm) + - aFloatOffsetState.ComputedLogicalBorderPadding().IStartEnd(wm); + WritingMode cbwm = aCBReflowState.GetWritingMode(); + nscoord floatISize = floatSize.ConvertTo(cbwm, wm).ISize(cbwm); + if (floatISize == NS_UNCONSTRAINEDSIZE) { + return NS_UNCONSTRAINEDSIZE; // reflow is needed to get the true size + } + + return floatISize + + aFloatOffsetState.ComputedLogicalMargin().Size(wm). + ConvertTo(cbwm, wm).ISize(cbwm) + + aFloatOffsetState.ComputedLogicalBorderPadding().Size(wm). + ConvertTo(cbwm, wm).ISize(cbwm); } bool @@ -749,14 +762,20 @@ nsBlockReflowState::FlowAndPlaceFloat(nsIFrame* aFloat) // If it's a floating first-letter, we need to reflow it before we // know how wide it is (since we don't compute which letters are part // of the first letter until reflow!). - bool isLetter = aFloat->GetType() == nsGkAtoms::letterFrame; - if (isLetter) { + // We also need to do this early reflow if FloatMarginISize returned + // an unconstrained inline-size, which can occur if the float had an + // orthogonal writing mode and 'auto' block-size (in its mode). + bool earlyFloatReflow = + aFloat->GetType() == nsGkAtoms::letterFrame || + floatMarginISize == NS_UNCONSTRAINEDSIZE; + if (earlyFloatReflow) { mBlock->ReflowFloat(*this, adjustedAvailableSpace, aFloat, floatMargin, floatOffsets, false, reflowStatus); floatMarginISize = aFloat->ISize(wm) + floatMargin.IStartEnd(wm); NS_ASSERTION(NS_FRAME_IS_COMPLETE(reflowStatus), - "letter frames shouldn't break, and if they do now, " - "then they're breaking at the wrong point"); + "letter frames and orthogonal floats with auto block-size " + "shouldn't break, and if they do now, then they're breaking " + "at the wrong point"); } // Find a place to place the float. The CSS2 spec doesn't want @@ -881,7 +900,7 @@ nsBlockReflowState::FlowAndPlaceFloat(nsIFrame* aFloat) // Reflow the float after computing its vertical position so it knows // where to break. - if (!isLetter) { + if (!earlyFloatReflow) { bool pushedDown = mBCoord != saveBCoord; mBlock->ReflowFloat(*this, adjustedAvailableSpace, aFloat, floatMargin, floatOffsets, pushedDown, reflowStatus); diff --git a/layout/generic/nsContainerFrame.cpp b/layout/generic/nsContainerFrame.cpp index 0775900d60..9e0deeb645 100644 --- a/layout/generic/nsContainerFrame.cpp +++ b/layout/generic/nsContainerFrame.cpp @@ -609,14 +609,15 @@ IsTopLevelWidget(nsIWidget* aWidget) void nsContainerFrame::SyncWindowProperties(nsPresContext* aPresContext, nsIFrame* aFrame, - nsView* aView, - nsRenderingContext* aRC) + nsView* aView, + nsRenderingContext* aRC, + uint32_t aFlags) { #ifdef MOZ_XUL if (!aView || !nsCSSRendering::IsCanvasFrame(aFrame) || !aView->HasWidget()) return; - nsIWidget* windowWidget = GetPresContextContainerWidget(aPresContext); + nsCOMPtr windowWidget = GetPresContextContainerWidget(aPresContext); if (!windowWidget || !IsTopLevelWidget(windowWidget)) return; @@ -650,14 +651,27 @@ nsContainerFrame::SyncWindowProperties(nsPresContext* aPresContext, if (!rootFrame) return; + if (aFlags & SET_ASYNC) { + aView->SetNeedsWindowPropertiesSync(); + return; + } + + nsRefPtr kungFuDeathGrip(aPresContext); + nsWeakFrame weak(rootFrame); + nsTransparencyMode mode = nsLayoutUtils::GetFrameTransparency(aFrame, rootFrame); - nsIWidget* viewWidget = aView->GetWidget(); + int32_t shadow = rootFrame->StyleUIReset()->mWindowShadow; + nsCOMPtr viewWidget = aView->GetWidget(); viewWidget->SetTransparencyMode(mode); - windowWidget->SetWindowShadowStyle(rootFrame->StyleUIReset()->mWindowShadow); + windowWidget->SetWindowShadowStyle(shadow); if (!aRC) return; - + + if (!weak.IsAlive()) { + return; + } + nsBoxLayoutState aState(aPresContext, aRC); nsSize minSize = rootFrame->GetMinSize(aState); nsSize maxSize = rootFrame->GetMaxSize(aState); @@ -968,7 +982,7 @@ nsContainerFrame::ReflowChild(nsIFrame* aKidFrame, NS_PRECONDITION(aReflowState.frame == aKidFrame, "bad reflow state"); if (aWM.IsVerticalRL() || (!aWM.IsVertical() && !aWM.IsBidiLTR())) { NS_ASSERTION(aContainerWidth != NS_UNCONSTRAINEDSIZE, - "FinishReflowChild with unconstrained container width!"); + "ReflowChild with unconstrained container width!"); } // Position the child frame and its view if requested. diff --git a/layout/generic/nsContainerFrame.h b/layout/generic/nsContainerFrame.h index c4facc6635..2911361f0f 100644 --- a/layout/generic/nsContainerFrame.h +++ b/layout/generic/nsContainerFrame.h @@ -180,10 +180,16 @@ public: // Syncs properties to the top level view and window, like transparency and // shadow. + // The SET_ASYNC indicates that the actual nsIWidget calls to sync the window + // properties should be done async. + enum { + SET_ASYNC = 0x01, + }; static void SyncWindowProperties(nsPresContext* aPresContext, nsIFrame* aFrame, - nsView* aView, - nsRenderingContext* aRC = nullptr); + nsView* aView, + nsRenderingContext* aRC, + uint32_t aFlags); // Sets the view's attributes from the frame style. // - visibility @@ -235,10 +241,12 @@ public: * If the reflow status after reflowing the child is FULLY_COMPLETE then any * next-in-flows are deleted using DeleteNextInFlowChild(). * + * @param aContainerWidth width of the border-box of the containing frame + * * Flags: * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you * don't want to automatically sync the frame and view - * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aX and aY are ignored in this + * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aPos is ignored in this * case. Also implies NS_FRAME_NO_MOVE_VIEW */ void ReflowChild(nsIFrame* aChildFrame, @@ -262,8 +270,10 @@ public: * - sets the view's visibility, opacity, content transparency, and clip * - invoked the DidReflow() function * + * @param aContainerWidth width of the border-box of the containing frame + * * Flags: - * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aX and aY are ignored in this + * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aPos is ignored in this * case. Also implies NS_FRAME_NO_MOVE_VIEW * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you * don't want to automatically sync the frame and view diff --git a/layout/generic/nsGridContainerFrame.cpp b/layout/generic/nsGridContainerFrame.cpp index 120c0d65d3..68ff06cab2 100644 --- a/layout/generic/nsGridContainerFrame.cpp +++ b/layout/generic/nsGridContainerFrame.cpp @@ -31,11 +31,14 @@ class nsGridContainerFrame::GridItemCSSOrderIterator { public: enum OrderState { eUnknownOrder, eKnownOrdered, eKnownUnordered }; + enum ChildFilter { eSkipPlaceholders, eIncludeAll }; GridItemCSSOrderIterator(nsIFrame* aGridContainer, nsIFrame::ChildListID aListID, + ChildFilter aFilter = eSkipPlaceholders, OrderState aState = eUnknownOrder) : mChildren(aGridContainer->GetChildList(aListID)) , mArrayIndex(0) + , mSkipPlaceholders(aFilter == eSkipPlaceholders) #ifdef DEBUG , mGridContainer(aGridContainer) , mListID(aListID) @@ -66,6 +69,10 @@ public: // XXX replace this with nsTArray::StableSort when bug 1147091 is fixed. std::stable_sort(mArray->begin(), mArray->end(), IsCSSOrderLessThan); } + + if (mSkipPlaceholders) { + SkipPlaceholders(); + } } nsIFrame* operator*() const @@ -77,6 +84,28 @@ public: return (*mArray)[mArrayIndex]; } + /** + * Skip over placeholder children. + */ + void SkipPlaceholders() + { + if (mEnumerator) { + for (; !mEnumerator->AtEnd(); mEnumerator->Next()) { + nsIFrame* child = mEnumerator->get(); + if (child->GetType() != nsGkAtoms::placeholderFrame) { + return; + } + } + } else { + for (; mArrayIndex < mArray->Length(); ++mArrayIndex) { + nsIFrame* child = (*mArray)[mArrayIndex]; + if (child->GetType() != nsGkAtoms::placeholderFrame) { + return; + } + } + } + } + bool AtEnd() const { MOZ_ASSERT(mEnumerator || mArrayIndex <= mArray->Length()); @@ -97,9 +126,12 @@ public: MOZ_ASSERT(mArrayIndex < mArray->Length(), "iterating past end"); ++mArrayIndex; } + if (mSkipPlaceholders) { + SkipPlaceholders(); + } } - void Reset() + void Reset(ChildFilter aFilter = eSkipPlaceholders) { if (mEnumerator) { mEnumerator.reset(); @@ -107,6 +139,10 @@ public: } else { mArrayIndex = 0; } + mSkipPlaceholders = aFilter == eSkipPlaceholders; + if (mSkipPlaceholders) { + SkipPlaceholders(); + } } bool ItemsAreAlreadyInOrder() const { return mEnumerator.isSome(); } @@ -121,6 +157,8 @@ private: // Used if child list is *not* in ascending 'order'. Maybe> mArray; size_t mArrayIndex; + // Skip placeholder children in the iteration? + bool mSkipPlaceholders; #ifdef DEBUG nsIFrame* mGridContainer; nsIFrame::ChildListID mListID; @@ -1119,33 +1157,49 @@ nsGridContainerFrame::ReflowChildren(GridItemCSSOrderIterator& aIter, { WritingMode wm = aReflowState.GetWritingMode(); const LogicalPoint gridOrigin(aContentArea.Origin(wm)); - const nscoord gridWidth = aContentArea.Width(wm); + const nscoord containerWidth = aContentArea.Width(wm) + + aReflowState.ComputedPhysicalBorderPadding().LeftRight(); nsPresContext* pc = PresContext(); for (; !aIter.AtEnd(); aIter.Next()) { nsIFrame* child = *aIter; - GridArea* area = GetGridAreaForChild(child); - MOZ_ASSERT(area && area->IsDefinite()); - LogicalRect cb = ContainingBlockFor(wm, *area, aColSizes, aRowSizes); - cb += gridOrigin; - nsHTMLReflowState childRS(pc, aReflowState, child, cb.Size(wm)); + const bool isGridItem = child->GetType() != nsGkAtoms::placeholderFrame; + LogicalRect cb(wm); + if (MOZ_LIKELY(isGridItem)) { + GridArea* area = GetGridAreaForChild(child); + MOZ_ASSERT(area && area->IsDefinite()); + cb = ContainingBlockFor(wm, *area, aColSizes, aRowSizes); + cb += gridOrigin; + } else { + cb = aContentArea; + } + WritingMode childWM = child->GetWritingMode(); + LogicalSize childCBSize = cb.Size(wm).ConvertTo(childWM, wm); + nsHTMLReflowState childRS(pc, aReflowState, child, childCBSize); const LogicalMargin margin = childRS.ComputedLogicalMargin(); - if (childRS.ComputedBSize() == NS_AUTOHEIGHT) { + if (childRS.ComputedBSize() == NS_AUTOHEIGHT && MOZ_LIKELY(isGridItem)) { // XXX the start of an align-self:stretch impl. Needs min-/max-bsize // clamping though, and check the prop value is actually 'stretch'! LogicalMargin bp = childRS.ComputedLogicalBorderPadding(); bp.ApplySkipSides(child->GetLogicalSkipSides()); - nscoord bSize = cb.BSize(wm) - bp.BStartEnd(wm) - margin.BStartEnd(wm); + nscoord bSize = childCBSize.BSize(childWM) - bp.BStartEnd(childWM) - + margin.BStartEnd(childWM); childRS.SetComputedBSize(std::max(bSize, 0)); } - LogicalPoint childPos = cb.Origin(wm); - childPos.I(wm) += margin.IStart(wm); - childPos.B(wm) += margin.BStart(wm); + // We need the width of the child before we can correctly convert + // the writing-mode of its origin, so we reflow at (0, 0) and then + // pass the correct position to FinishReflowChild. nsHTMLReflowMetrics childSize(childRS); nsReflowStatus childStatus; - ReflowChild(child, pc, childSize, childRS, wm, childPos, - gridWidth, 0, childStatus); - FinishReflowChild(child, pc, childSize, &childRS, wm, childPos, - gridWidth, 0); + ReflowChild(child, pc, childSize, childRS, childWM, LogicalPoint(childWM), + 0, 0, childStatus); + LogicalPoint childPos = + cb.Origin(wm).ConvertTo(childWM, wm, containerWidth - childSize.Width() - + margin.LeftRight(childWM)); + childPos.I(childWM) += margin.IStart(childWM); + childPos.B(childWM) += margin.BStart(childWM); + childRS.ApplyRelativePositioning(&childPos, containerWidth); + FinishReflowChild(child, pc, childSize, &childRS, childWM, childPos, + containerWidth, 0); ConsiderChildOverflow(aDesiredSize.mOverflowAreas, child); // XXX deal with 'childStatus' not being COMPLETE } @@ -1175,7 +1229,7 @@ nsGridContainerFrame::ReflowChildren(GridItemCSSOrderIterator& aIter, cb = new nsRect; child->Properties().Set(GridItemContainingBlockRect(), cb); } - *cb = itemCB.GetPhysicalRect(wm, gridWidth); + *cb = itemCB.GetPhysicalRect(wm, containerWidth); } // This rect isn't used at all for layout so we use it to optimize // away the virtual GetType() call in the callee in most cases. @@ -1240,7 +1294,7 @@ nsGridContainerFrame::Reflow(nsPresContext* aPresContext, LogicalRect contentArea(wm, bp.IStart(wm), bp.BStart(wm), computedISize, bSize); - normalFlowIter.Reset(); + normalFlowIter.Reset(GridItemCSSOrderIterator::eIncludeAll); ReflowChildren(normalFlowIter, contentArea, colSizes, rowSizes, aDesiredSize, aReflowState, aStatus); @@ -1277,7 +1331,8 @@ nsGridContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, typedef GridItemCSSOrderIterator::OrderState OrderState; OrderState order = mIsNormalFlowInCSSOrder ? OrderState::eKnownOrdered : OrderState::eKnownUnordered; - GridItemCSSOrderIterator iter(this, kPrincipalList, order); + GridItemCSSOrderIterator iter(this, kPrincipalList, + GridItemCSSOrderIterator::eIncludeAll, order); for (; !iter.AtEnd(); iter.Next()) { nsIFrame* child = *iter; BuildDisplayListForChild(aBuilder, child, aDirtyRect, childLists, @@ -1350,8 +1405,7 @@ FrameWantsToBeInAnonymousGridItem(nsIFrame* aFrame) { // Note: This needs to match the logic in // nsCSSFrameConstructor::FrameConstructionItem::NeedsAnonFlexOrGridItem() - return (aFrame->IsFrameOfType(nsIFrame::eLineParticipant) || - nsGkAtoms::placeholderFrame == aFrame->GetType()); + return aFrame->IsFrameOfType(nsIFrame::eLineParticipant); } // Debugging method, to let us assert that our anonymous grid items are diff --git a/layout/reftests/css-grid/grid-abspos-items-001-ref.html b/layout/reftests/css-grid/grid-abspos-items-001-ref.html index 2eb05af915..216f0eae49 100644 --- a/layout/reftests/css-grid/grid-abspos-items-001-ref.html +++ b/layout/reftests/css-grid/grid-abspos-items-001-ref.html @@ -25,7 +25,7 @@ body,html { color:black; background:white; font-size:16px; padding:0; margin:0; height: 100px; } .zero-size { width:0; height:0; } -.auto-size { width:auto; height:17px; } +.auto-size { width:auto; height:0px; } .a { position: absolute; @@ -88,7 +88,7 @@ span {
a b -c +c d e f @@ -96,7 +96,7 @@ span {
-b +b
@@ -111,9 +111,9 @@ span {
-b -c -d +b +c +d
diff --git a/layout/reftests/css-grid/grid-abspos-items-002-ref.html b/layout/reftests/css-grid/grid-abspos-items-002-ref.html index 7e1f6b4b83..4c23081cb5 100644 --- a/layout/reftests/css-grid/grid-abspos-items-002-ref.html +++ b/layout/reftests/css-grid/grid-abspos-items-002-ref.html @@ -26,7 +26,7 @@ body,html { color:black; background:white; font-size:16px; padding:0; margin:0; height: 100px; } .zero-size { width:0; height:0; } -.auto-size { width:auto; height:17px; } +.auto-size { width:auto; height:0px; } .a { position: absolute; @@ -89,7 +89,7 @@ span {
a b -c +c d e f @@ -97,7 +97,7 @@ span {
-b +b
diff --git a/layout/reftests/css-grid/grid-item-dir-001-ref.html b/layout/reftests/css-grid/grid-item-dir-001-ref.html new file mode 100644 index 0000000000..d26bb6c212 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-dir-001-ref.html @@ -0,0 +1,45 @@ + + + + + + + + +
+
+
+
+

+
+
+
+

+
+
+
+
+
+ + diff --git a/layout/reftests/css-grid/grid-item-dir-001.html b/layout/reftests/css-grid/grid-item-dir-001.html new file mode 100644 index 0000000000..1b6b9d202f --- /dev/null +++ b/layout/reftests/css-grid/grid-item-dir-001.html @@ -0,0 +1,46 @@ + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + diff --git a/layout/reftests/css-grid/grid-relpos-items-001-ref.html b/layout/reftests/css-grid/grid-relpos-items-001-ref.html new file mode 100644 index 0000000000..5152107a2e --- /dev/null +++ b/layout/reftests/css-grid/grid-relpos-items-001-ref.html @@ -0,0 +1,60 @@ + + + + CSS Test: Testing layout of rel.pos. grid items + + + + + + +
+1 + + +r3 +r4 +
+ + + diff --git a/layout/reftests/css-grid/grid-relpos-items-001.html b/layout/reftests/css-grid/grid-relpos-items-001.html new file mode 100644 index 0000000000..153d56f7ea --- /dev/null +++ b/layout/reftests/css-grid/grid-relpos-items-001.html @@ -0,0 +1,62 @@ + + + + CSS Test: Testing layout of rel.pos. grid items + + + + + + + + +
+1 + + +r3 +r4 +
+ + + diff --git a/layout/reftests/css-grid/reftest.list b/layout/reftests/css-grid/reftest.list index 7d12996755..962a66a407 100644 --- a/layout/reftests/css-grid/reftest.list +++ b/layout/reftests/css-grid/reftest.list @@ -15,3 +15,9 @@ fails == grid-whitespace-handling-1b.xhtml grid-whitespace-handling-1-ref.xhtml == grid-order-abspos-items-001.html grid-order-abspos-items-001-ref.html == grid-order-placement-auto-001.html grid-order-placement-auto-001-ref.html == grid-order-placement-definite-001.html grid-order-placement-definite-001-ref.html +pref(layout.css.vertical-text.enabled,true) == rtl-grid-placement-definite-001.html rtl-grid-placement-definite-001-ref.html +pref(layout.css.vertical-text.enabled,true) == rtl-grid-placement-auto-row-sparse-001.html rtl-grid-placement-auto-row-sparse-001-ref.html +pref(layout.css.vertical-text.enabled,true) == vlr-grid-placement-auto-row-sparse-001.html vlr-grid-placement-auto-row-sparse-001-ref.html +pref(layout.css.vertical-text.enabled,true) == vrl-grid-placement-auto-row-sparse-001.html vrl-grid-placement-auto-row-sparse-001-ref.html +== grid-relpos-items-001.html grid-relpos-items-001-ref.html +== grid-item-dir-001.html grid-item-dir-001-ref.html diff --git a/layout/reftests/css-grid/rtl-grid-placement-auto-row-sparse-001-ref.html b/layout/reftests/css-grid/rtl-grid-placement-auto-row-sparse-001-ref.html new file mode 100644 index 0000000000..adfdcddeb2 --- /dev/null +++ b/layout/reftests/css-grid/rtl-grid-placement-auto-row-sparse-001-ref.html @@ -0,0 +1,198 @@ + + + + + + + + +
+a +b +c +d +D +e +
+ +
+a +b +c +d +D +e +
+ +
+a +b +c +d +D +e +
+ +
+c +d +D +e +
+ +
+c +d +D +e +
+ +
+a +b +c +d +D +e +
+ + + diff --git a/layout/reftests/css-grid/rtl-grid-placement-auto-row-sparse-001.html b/layout/reftests/css-grid/rtl-grid-placement-auto-row-sparse-001.html new file mode 100644 index 0000000000..a0f87ced78 --- /dev/null +++ b/layout/reftests/css-grid/rtl-grid-placement-auto-row-sparse-001.html @@ -0,0 +1,136 @@ + + + + + + + + +
+a +b +c +d +D +e +
+ +
+a +b +c +d +D +e +
+ +
+a +b +c +d +D +e +
+ +
+c +d +D +e +
+ +
+c +d +D +e +
+ +
+a +b +c +d +D +e +
+ + + diff --git a/layout/reftests/css-grid/rtl-grid-placement-definite-001-ref.html b/layout/reftests/css-grid/rtl-grid-placement-definite-001-ref.html new file mode 100644 index 0000000000..fd4d7433f8 --- /dev/null +++ b/layout/reftests/css-grid/rtl-grid-placement-definite-001-ref.html @@ -0,0 +1,68 @@ + + + + + + + + + +
+a +b +c +d +
+ +
+a +b +c +d +
+ + + diff --git a/layout/reftests/css-grid/rtl-grid-placement-definite-001.html b/layout/reftests/css-grid/rtl-grid-placement-definite-001.html new file mode 100644 index 0000000000..ec60bc3d77 --- /dev/null +++ b/layout/reftests/css-grid/rtl-grid-placement-definite-001.html @@ -0,0 +1,64 @@ + + + + + + + + + +
+a +b +c +d +
+ +
+a +b +c +d +
+ + + diff --git a/layout/reftests/css-grid/vlr-grid-placement-auto-row-sparse-001-ref.html b/layout/reftests/css-grid/vlr-grid-placement-auto-row-sparse-001-ref.html new file mode 100644 index 0000000000..b311ac2a05 --- /dev/null +++ b/layout/reftests/css-grid/vlr-grid-placement-auto-row-sparse-001-ref.html @@ -0,0 +1,203 @@ + + + + + + + + +
+a +b +c +d +D +e +
+ +
+a +b +c +d +D +e +
+ +
+a +b +c +d +D +e +
+ +
+c +d +D +e +
+ +
+c +d +D +e +
+ +
+a +b +c +d +D +e +
+ + + diff --git a/layout/reftests/css-grid/vlr-grid-placement-auto-row-sparse-001.html b/layout/reftests/css-grid/vlr-grid-placement-auto-row-sparse-001.html new file mode 100644 index 0000000000..eab163cd05 --- /dev/null +++ b/layout/reftests/css-grid/vlr-grid-placement-auto-row-sparse-001.html @@ -0,0 +1,141 @@ + + + + + + + + +
+a +b +c +d +D +e +
+ +
+a +b +c +d +D +e +
+ +
+a +b +c +d +D +e +
+ +
+c +d +D +e +
+ +
+c +d +D +e +
+ +
+a +b +c +d +D +e +
+ + + diff --git a/layout/reftests/css-grid/vrl-grid-placement-auto-row-sparse-001-ref.html b/layout/reftests/css-grid/vrl-grid-placement-auto-row-sparse-001-ref.html new file mode 100644 index 0000000000..4c9844bc4d --- /dev/null +++ b/layout/reftests/css-grid/vrl-grid-placement-auto-row-sparse-001-ref.html @@ -0,0 +1,221 @@ + + + + + + + + +
+a +b +c +d +D +e +
+ +
+a +b +c +d +D +e +
+ +
+a +b +c +d +D +e +
+ +
+c +d +D +e +
+ +
+c +d +D +e +
+ +
+a +b +c +d +D +e +
+ + + diff --git a/layout/reftests/css-grid/vrl-grid-placement-auto-row-sparse-001.html b/layout/reftests/css-grid/vrl-grid-placement-auto-row-sparse-001.html new file mode 100644 index 0000000000..3553645bdc --- /dev/null +++ b/layout/reftests/css-grid/vrl-grid-placement-auto-row-sparse-001.html @@ -0,0 +1,141 @@ + + + + + + + + +
+a +b +c +d +D +e +
+ +
+a +b +c +d +D +e +
+ +
+a +b +c +d +D +e +
+ +
+c +d +D +e +
+ +
+c +d +D +e +
+ +
+a +b +c +d +D +e +
+ + + diff --git a/layout/reftests/floats/orthogonal-floats-1-ref.html b/layout/reftests/floats/orthogonal-floats-1-ref.html new file mode 100644 index 0000000000..a5ccc8a3c8 --- /dev/null +++ b/layout/reftests/floats/orthogonal-floats-1-ref.html @@ -0,0 +1,39 @@ + + + + Bug 1141867 - Contiguous right-floating boxes with vertical writing mode + + + + + + + +
+ +
+ +

Test passes if there is a filled green square and no red.

+ + + diff --git a/layout/reftests/floats/orthogonal-floats-1a.html b/layout/reftests/floats/orthogonal-floats-1a.html new file mode 100644 index 0000000000..87aa34da58 --- /dev/null +++ b/layout/reftests/floats/orthogonal-floats-1a.html @@ -0,0 +1,54 @@ + + + + Bug 1141867 - Contiguous right-floating boxes with vertical writing mode + + + + + + + +
abcde
+ +
fghijk
+ +
lmnopq
+ +
rstuv
+ +
wxyz!
+ +
+ +

Test passes if there is a filled green square and no red.

+ + + diff --git a/layout/reftests/floats/orthogonal-floats-1b.html b/layout/reftests/floats/orthogonal-floats-1b.html new file mode 100644 index 0000000000..f4890f32d1 --- /dev/null +++ b/layout/reftests/floats/orthogonal-floats-1b.html @@ -0,0 +1,54 @@ + + + + Bug 1141867 - Contiguous right-floating boxes with vertical writing mode + + + + + + + +
abcde
+ +
fghijk
+ +
lmnopq
+ +
rstuv
+ +
wxyz!
+ +
+ +

Test passes if there is a filled green square and no red.

+ + + diff --git a/layout/reftests/floats/orthogonal-floats-1c.html b/layout/reftests/floats/orthogonal-floats-1c.html new file mode 100644 index 0000000000..3c447b0ce9 --- /dev/null +++ b/layout/reftests/floats/orthogonal-floats-1c.html @@ -0,0 +1,56 @@ + + + + Bug 1141867 - Contiguous right-floating boxes with vertical writing mode + + + + + + + +
abcde
+ +
fghij
+ +
klmno
+ +
qrstu
+ +
vwxyz
+ +
+ +

Test passes if there is a filled green square and no red.

+ + + diff --git a/layout/reftests/floats/orthogonal-floats-1d.html b/layout/reftests/floats/orthogonal-floats-1d.html new file mode 100644 index 0000000000..97a22e1a66 --- /dev/null +++ b/layout/reftests/floats/orthogonal-floats-1d.html @@ -0,0 +1,56 @@ + + + + Bug 1141867 - Contiguous right-floating boxes with vertical writing mode + + + + + + + +
abcde
+ +
fghij
+ +
klmno
+ +
qrstu
+ +
vwxyz
+ +
+ +

Test passes if there is a filled green square and no red.

+ + + diff --git a/layout/reftests/floats/reftest.list b/layout/reftests/floats/reftest.list index 6715feb08e..3b9bcbb654 100644 --- a/layout/reftests/floats/reftest.list +++ b/layout/reftests/floats/reftest.list @@ -45,3 +45,8 @@ fails == 345369-2.html 345369-2-ref.html == bfc-displace-3b.html bfc-displace-3b-ref.html == bfc-displace-4.html bfc-displace-4-ref.html == bfc-shrink-1.html bfc-shrink-1-ref.html + +pref(layout.css.vertical-text.enabled,true) fuzzy-if(OSX==1010,26,7) fuzzy-if(Android,16,2) == orthogonal-floats-1a.html orthogonal-floats-1-ref.html +pref(layout.css.vertical-text.enabled,true) fuzzy-if(OSX==1010,26,7) == orthogonal-floats-1b.html orthogonal-floats-1-ref.html +pref(layout.css.vertical-text.enabled,true) fuzzy-if(OSX==1010,103,802) fuzzy-if(winWidget,116,700) HTTP(..) == orthogonal-floats-1c.html orthogonal-floats-1-ref.html +pref(layout.css.vertical-text.enabled,true) fuzzy-if(OSX==1010,103,802) fuzzy-if(winWidget,116,700) HTTP(..) == orthogonal-floats-1d.html orthogonal-floats-1-ref.html diff --git a/layout/reftests/forms/input/number/number-similar-to-text-unthemed-rtl-ref.html b/layout/reftests/forms/input/number/number-similar-to-text-unthemed-rtl-ref.html new file mode 100644 index 0000000000..79644edc8b --- /dev/null +++ b/layout/reftests/forms/input/number/number-similar-to-text-unthemed-rtl-ref.html @@ -0,0 +1,8 @@ + + + + + +
+ + diff --git a/layout/reftests/forms/input/number/number-similar-to-text-unthemed-rtl.html b/layout/reftests/forms/input/number/number-similar-to-text-unthemed-rtl.html new file mode 100644 index 0000000000..36ffc06299 --- /dev/null +++ b/layout/reftests/forms/input/number/number-similar-to-text-unthemed-rtl.html @@ -0,0 +1,8 @@ + + + + + +
+ + diff --git a/layout/reftests/forms/input/number/reftest.list b/layout/reftests/forms/input/number/reftest.list index bbf6fa9fb0..3d726c7425 100644 --- a/layout/reftests/forms/input/number/reftest.list +++ b/layout/reftests/forms/input/number/reftest.list @@ -9,6 +9,7 @@ skip-if(!Android&&!B2G&&!Mulet) == number-same-as-text-unthemed.html number-same # should look the same as type=text, except for the spin box == number-similar-to-text-unthemed.html number-similar-to-text-unthemed-ref.html +== number-similar-to-text-unthemed-rtl.html number-similar-to-text-unthemed-rtl-ref.html # dynamic type changes: fuzzy-if(/^Windows\x20NT\x205\.1/.test(http.oscpu),64,4) fuzzy-if(cocoaWidget,63,4) == to-number-from-other-type-unthemed-1.html to-number-from-other-type-unthemed-1-ref.html diff --git a/layout/style/CSSStyleSheet.cpp b/layout/style/CSSStyleSheet.cpp index c5cb05b75b..ece3ab1cc7 100644 --- a/layout/style/CSSStyleSheet.cpp +++ b/layout/style/CSSStyleSheet.cpp @@ -17,6 +17,7 @@ #include "mozilla/css/NameSpaceRule.h" #include "mozilla/css/GroupRule.h" #include "mozilla/css/ImportRule.h" +#include "nsCSSRules.h" #include "nsIMediaList.h" #include "nsIDocument.h" #include "nsPresContext.h" @@ -332,6 +333,96 @@ nsMediaQueryResultCacheKey::Matches(nsPresContext* aPresContext) const return true; } +bool +nsDocumentRuleResultCacheKey::AddMatchingRule(css::DocumentRule* aRule) +{ + MOZ_ASSERT(!mFinalized); + return mMatchingRules.AppendElement(aRule); +} + +void +nsDocumentRuleResultCacheKey::Finalize() +{ + mMatchingRules.Sort(); +#ifdef DEBUG + mFinalized = true; +#endif +} + +bool +nsDocumentRuleResultCacheKey::Matches( + nsPresContext* aPresContext, + const nsTArray& aRules) const +{ + MOZ_ASSERT(mFinalized); + + // First check that aPresContext matches all the rules listed in + // mMatchingRules. + for (css::DocumentRule* rule : mMatchingRules) { + if (!rule->UseForPresentation(aPresContext)) { + return false; + } + } + + // Then check that all the rules in aRules that aren't also in + // mMatchingRules do not match. + + // pointer to matching rules + auto pm = mMatchingRules.begin(); + auto pm_end = mMatchingRules.end(); + + // pointer to all rules + auto pr = aRules.begin(); + auto pr_end = aRules.end(); + + // mMatchingRules and aRules are both sorted by their pointer values, + // so we can iterate over the two lists simultaneously. + while (pr < pr_end) { + while (pm < pm_end && *pm < *pr) { + ++pm; + MOZ_ASSERT(pm >= pm_end || *pm == *pr, + "shouldn't find rule in mMatchingRules that is not in aRules"); + } + if (pm >= pm_end || *pm != *pr) { + if ((*pr)->UseForPresentation(aPresContext)) { + return false; + } + } + ++pr; + } + return true; +} + +#ifdef DEBUG +void +nsDocumentRuleResultCacheKey::List(FILE* aOut, int32_t aIndent) const +{ + for (css::DocumentRule* rule : mMatchingRules) { + nsCString str; + + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + str.AppendLiteral("{ "); + + nsString condition; + rule->GetConditionText(condition); + AppendUTF16toUTF8(condition, str); + + str.AppendLiteral(" }\n"); + fprintf_stderr(aOut, "%s", str.get()); + } +} +#endif + +size_t +nsDocumentRuleResultCacheKey::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const +{ + size_t n = 0; + n += mMatchingRules.SizeOfExcludingThis(aMallocSizeOf); + return n; +} + void nsMediaQuery::AppendToString(nsAString& aString) const { diff --git a/layout/style/nsCSSRules.cpp b/layout/style/nsCSSRules.cpp index 20a609dcbe..d31c47d4a6 100644 --- a/layout/style/nsCSSRules.cpp +++ b/layout/style/nsCSSRules.cpp @@ -1147,6 +1147,12 @@ DocumentRule::SetConditionText(const nsAString& aConditionText) /* virtual */ bool DocumentRule::UseForPresentation(nsPresContext* aPresContext, nsMediaQueryResultCacheKey& aKey) +{ + return UseForPresentation(aPresContext); +} + +bool +DocumentRule::UseForPresentation(nsPresContext* aPresContext) { nsIDocument *doc = aPresContext->Document(); nsIURI *docURI = doc->GetDocumentURI(); diff --git a/layout/style/nsCSSRules.h b/layout/style/nsCSSRules.h index 90e1fc1112..a717c28a35 100644 --- a/layout/style/nsCSSRules.h +++ b/layout/style/nsCSSRules.h @@ -147,6 +147,8 @@ public: virtual bool UseForPresentation(nsPresContext* aPresContext, nsMediaQueryResultCacheKey& aKey) override; + bool UseForPresentation(nsPresContext* aPresContext); + enum Function { eURL, eURLPrefix, diff --git a/layout/style/nsIMediaList.h b/layout/style/nsIMediaList.h index ac9c686f48..bb61cbce6f 100644 --- a/layout/style/nsIMediaList.h +++ b/layout/style/nsIMediaList.h @@ -26,6 +26,9 @@ struct nsMediaFeature; namespace mozilla { class CSSStyleSheet; +namespace css { +class DocumentRule; +} // namespace css } // namespace mozilla struct nsMediaExpression { @@ -129,6 +132,64 @@ private: nsTArray mFeatureCache; }; +/** + * nsDocumentRuleResultCacheKey is analagous to nsMediaQueryResultCacheKey + * and stores the result of matching the @-moz-document rules from a set + * of style sheets. nsCSSRuleProcessor builds up an + * nsDocumentRuleResultCacheKey as it visits the @-moz-document rules + * while building its RuleCascadeData. + * + * Rather than represent the result using a list of both the matching and + * non-matching rules, we just store the matched rules. The assumption is + * that in situations where we have a large number of rules -- such as the + * thousands added by AdBlock Plus -- that only a small number will be + * matched. Thus to check if the nsDocumentRuleResultCacheKey matches a + * given nsPresContext, we also need the entire list of @-moz-document + * rules to know which rules must not match. + */ +class nsDocumentRuleResultCacheKey +{ +public: +#ifdef DEBUG + nsDocumentRuleResultCacheKey() + : mFinalized(false) {} +#endif + + bool AddMatchingRule(mozilla::css::DocumentRule* aRule); + bool Matches(nsPresContext* aPresContext, + const nsTArray& aRules) const; + + bool operator==(const nsDocumentRuleResultCacheKey& aOther) const { + MOZ_ASSERT(mFinalized); + MOZ_ASSERT(aOther.mFinalized); + return mMatchingRules == aOther.mMatchingRules; + } + bool operator!=(const nsDocumentRuleResultCacheKey& aOther) const { + return !(*this == aOther); + } + + void Swap(nsDocumentRuleResultCacheKey& aOther) { + mMatchingRules.SwapElements(aOther.mMatchingRules); +#ifdef DEBUG + std::swap(mFinalized, aOther.mFinalized); +#endif + } + + void Finalize(); + +#ifdef DEBUG + void List(FILE* aOut = stdout, int32_t aIndex = 0) const; +#endif + + size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; + +private: + nsTArray mMatchingRules; +#ifdef DEBUG + bool mFinalized; +#endif +}; + class nsMediaQuery { public: nsMediaQuery() diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index 0a6189e059..94538d747f 100644 --- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -5707,7 +5707,7 @@ nsTableFrame::CalcBCBorders() // segments that are on the table border edges need // to be initialized only once bool tableBorderReset[4]; - for (uint32_t sideX = eLogicalSideBStart; sideX <= eLogicalSideIStart; sideX++) { + for (uint32_t sideX = 0; sideX < ArrayLength(tableBorderReset); sideX++) { tableBorderReset[sideX] = false; } @@ -6172,12 +6172,12 @@ struct BCBlockDirSeg void Initialize(BCPaintBorderIterator& aIter); void GetBEndCorner(BCPaintBorderIterator& aIter, - BCPixelSize aInlineSegBSize); + BCPixelSize aInlineSegBSize); - void Paint(BCPaintBorderIterator& aIter, - nsRenderingContext& aRenderingContext, - BCPixelSize aInlineSegBSize); + void Paint(BCPaintBorderIterator& aIter, + nsRenderingContext& aRenderingContext, + BCPixelSize aInlineSegBSize); void AdvanceOffsetB(); void IncludeCurrentBorder(BCPaintBorderIterator& aIter); @@ -6222,12 +6222,12 @@ struct BCInlineDirSeg BCBorderOwner aBorderOwner, BCPixelSize aBEndBlockSegISize, BCPixelSize aInlineSegBSize); - void GetIEndCorner(BCPaintBorderIterator& aIter, - BCPixelSize aIStartSegISize); - void AdvanceOffsetI(); - void IncludeCurrentBorder(BCPaintBorderIterator& aIter); - void Paint(BCPaintBorderIterator& aIter, - nsRenderingContext& aRenderingContext); + void GetIEndCorner(BCPaintBorderIterator& aIter, + BCPixelSize aIStartSegISize); + void AdvanceOffsetI(); + void IncludeCurrentBorder(BCPaintBorderIterator& aIter); + void Paint(BCPaintBorderIterator& aIter, + nsRenderingContext& aRenderingContext); nscoord mOffsetI; // i-offset with respect to the table edge nscoord mOffsetB; // b-offset with respect to the table edge @@ -6909,7 +6909,7 @@ BCBlockDirSeg::GetBEndCorner(BCPaintBorderIterator& aIter, */ void BCBlockDirSeg::Paint(BCPaintBorderIterator& aIter, - nsRenderingContext& aRenderingContext, + nsRenderingContext& aRenderingContext, BCPixelSize aInlineSegBSize) { // get the border style, color and paint the segment @@ -6982,16 +6982,40 @@ BCBlockDirSeg::Paint(BCPaintBorderIterator& aIter, aIter.mTable->PresContext()->DevPixelsToAppUnits(mBEndInlineSegBSize) : 0; LogicalSide bEndBevelSide = (aInlineSegBSize > 0) ? eLogicalSideIEnd : eLogicalSideIStart; + + // Convert logical to physical sides/coordinates for DrawTableBorderSegment. + + nsRect physicalRect = segRect.GetPhysicalRect(aIter.mTableWM, + aIter.mTable->GetSize().width); + // XXX For reversed vertical writing-modes (with direction:rtl), we need to + // invert physicalRect's y-position here, with respect to the table. + // However, it's not worth fixing the border positions here until the + // ordering of the table columns themselves is also fixed (bug 1180528). + + uint8_t startBevelSide = aIter.mTableWM.PhysicalSide(mBStartBevelSide); + uint8_t endBevelSide = aIter.mTableWM.PhysicalSide(bEndBevelSide); + nscoord startBevelOffset = mBStartBevelOffset; + nscoord endBevelOffset = bEndBevelOffset; + // In vertical-rl mode, the 'start' and 'end' of the block-dir (horizontal) + // border segment need to be swapped because DrawTableBorderSegment will + // apply the 'start' bevel at the left edge, and 'end' at the right. + // (Note: In this case, startBevelSide/endBevelSide will usually both be + // "top" or "bottom". DrawTableBorderSegment works purely with physical + // coordinates, so it expects startBevelOffset to be the indentation-from- + // the-left for the "start" (left) end of the border-segment, and + // endBevelOffset is the indentation-from-the-right for the "end" (right) + // end of the border-segment. We've got them reversed, since our block dir + // is RTL, so we have to swap them here.) + if (aIter.mTableWM.IsVerticalRL()) { + Swap(startBevelSide, endBevelSide); + Swap(startBevelOffset, endBevelOffset); + } nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, - aIter.mTableBgColor, - segRect.GetPhysicalRect(aIter.mTableWM, - aIter.mTable->GetSize().width), + aIter.mTableBgColor, physicalRect, appUnitsPerDevPixel, aIter.mTable->PresContext()->AppUnitsPerDevPixel(), - aIter.mTableWM.PhysicalSide(mBStartBevelSide), - mBStartBevelOffset, - aIter.mTableWM.PhysicalSide(bEndBevelSide), - bEndBevelOffset); + startBevelSide, startBevelOffset, + endBevelSide, endBevelOffset); } /** @@ -7167,29 +7191,37 @@ BCInlineDirSeg::Paint(BCPaintBorderIterator& aIter, mOffsetB - nsPresContext::CSSPixelsToAppUnits(largeHalf), mLength, aIter.mTable->PresContext()->DevPixelsToAppUnits(mWidth)); - if (aIter.mTableWM.IsBidiLTR()) { - nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, - aIter.mTableBgColor, - segRect.GetPhysicalRect(aIter.mTableWM, - aIter.mTable->GetSize().width), - appUnitsPerDevPixel, - aIter.mTable->PresContext()->AppUnitsPerDevPixel(), - aIter.mTableWM.PhysicalSide(mIStartBevelSide), - aIter.mTable->PresContext()->DevPixelsToAppUnits(mIStartBevelOffset), - aIter.mTableWM.PhysicalSide(mIEndBevelSide), - mIEndBevelOffset); - } else { - nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, - aIter.mTableBgColor, - segRect.GetPhysicalRect(aIter.mTableWM, - aIter.mTable->GetSize().width), - appUnitsPerDevPixel, - aIter.mTable->PresContext()->AppUnitsPerDevPixel(), - aIter.mTableWM.PhysicalSide(mIEndBevelSide), - mIEndBevelOffset, - aIter.mTableWM.PhysicalSide(mIStartBevelSide), - aIter.mTable->PresContext()->DevPixelsToAppUnits(mIStartBevelOffset)); + + // Convert logical to physical sides/coordinates for DrawTableBorderSegment. + nsRect physicalRect = segRect.GetPhysicalRect(aIter.mTableWM, + aIter.mTable->GetSize().width); + uint8_t startBevelSide = aIter.mTableWM.PhysicalSide(mIStartBevelSide); + uint8_t endBevelSide = aIter.mTableWM.PhysicalSide(mIEndBevelSide); + nscoord startBevelOffset = + aIter.mTable->PresContext()->DevPixelsToAppUnits(mIStartBevelOffset); + nscoord endBevelOffset = mIEndBevelOffset; + // With inline-RTL directionality, the 'start' and 'end' of the inline-dir + // border segment need to be swapped because DrawTableBorderSegment will + // apply the 'start' bevel physically at the left or top edge, and 'end' at + // the right or bottom. + // (Note: startBevelSide/endBevelSide will be "top" or "bottom" in horizontal + // writing mode, or "left" or "right" in vertical mode. + // DrawTableBorderSegment works purely with physical coordinates, so it + // expects startBevelOffset to be the indentation-from-the-left or top end + // of the border-segment, and endBevelOffset is the indentation-from-the- + // right or bottom end. If the writing mode is inline-RTL, our "start" and + // "end" will be reversed from this physical-coord view, so we have to swap + // them here. + if (!aIter.mTableWM.IsBidiLTR()) { + Swap(startBevelSide, endBevelSide); + Swap(startBevelOffset, endBevelOffset); } + nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, + aIter.mTableBgColor, physicalRect, + appUnitsPerDevPixel, + aIter.mTable->PresContext()->AppUnitsPerDevPixel(), + startBevelSide, startBevelOffset, + endBevelSide, endBevelOffset); } /** diff --git a/view/nsView.cpp b/view/nsView.cpp index 1e3ace2f7b..9b84782a31 100644 --- a/view/nsView.cpp +++ b/view/nsView.cpp @@ -677,6 +677,16 @@ nsView::InitializeWindow(bool aEnableDragDrop, bool aResetVisibility) } } +void +nsView::SetNeedsWindowPropertiesSync() +{ + mNeedsWindowPropertiesSync = true; + if (mViewManager) { + mViewManager->PostPendingUpdate(); + } +} + + // Attach to a top level widget and start receiving mirrored events. nsresult nsView::AttachToTopLevelWidget(nsIWidget* aWidget) { diff --git a/view/nsView.h b/view/nsView.h index 983caf1e86..53ae456330 100644 --- a/view/nsView.h +++ b/view/nsView.h @@ -298,6 +298,8 @@ public: mForcedRepaint = aForceRepaint; } + void SetNeedsWindowPropertiesSync(); + /** * Make aWidget direct its events to this view. * The caller must call DetachWidgetEventHandler before this view @@ -474,6 +476,7 @@ private: uint32_t mVFlags; bool mWidgetIsTopLevel; bool mForcedRepaint; + bool mNeedsWindowPropertiesSync; }; #endif diff --git a/view/nsViewManager.cpp b/view/nsViewManager.cpp index 0f7aa9281b..f6525cb4a1 100644 --- a/view/nsViewManager.cpp +++ b/view/nsViewManager.cpp @@ -367,6 +367,17 @@ nsViewManager::ProcessPendingUpdatesForView(nsView* aView, aView->GetViewManager()->ProcessPendingUpdatesRecurse(aView, widgets); for (uint32_t i = 0; i < widgets.Length(); ++i) { nsView* view = nsView::GetViewFor(widgets[i]); + if (view) { + if (view->mNeedsWindowPropertiesSync) { + view->mNeedsWindowPropertiesSync = false; + if (nsViewManager* vm = view->GetViewManager()) { + if (nsIPresShell* ps = vm->GetPresShell()) { + ps->SyncWindowProperties(view); + } + } + } + } + view = nsView::GetViewFor(widgets[i]); if (view) { view->ResetWidgetBounds(false, true); }