tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit e7efe8e2d1b972ed4a073065bc99cfb08f6c31e9
parent e911c81747e2d7d063600c6a0cd1ecc126175943
Author: Serban Stanca <sstanca@mozilla.com>
Date:   Tue,  6 Jan 2026 18:02:00 +0200

Revert "Bug 2008108 - Simplify listbox select rendering. r=jwatt" for causing wpt failures in auto-007.html.

This reverts commit 0e35e47a582b10eacf12ad77f2f694e4bf262d10.

Diffstat:
Mlayout/base/nsCSSFrameConstructor.cpp | 3++-
Mlayout/forms/moz.build | 1+
Mlayout/forms/nsListControlFrame.cpp | 94++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mlayout/forms/nsListControlFrame.h | 22++++++++++++++++++++--
Alayout/forms/nsSelectsAreaFrame.cpp | 191+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alayout/forms/nsSelectsAreaFrame.h | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlayout/generic/ScrollContainerFrame.cpp | 81+++++++++++++++++--------------------------------------------------------------
Mlayout/generic/ScrollContainerFrame.h | 9+++++++++
Mlayout/generic/nsFontInflationData.cpp | 3++-
Mlayout/generic/nsHTMLParts.h | 7+++++++
Mlayout/painting/nsCSSRendering.cpp | 21+++++++++++++--------
Mlayout/painting/nsCSSRendering.h | 5++---
12 files changed, 409 insertions(+), 85 deletions(-)

diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp @@ -3034,7 +3034,8 @@ nsIFrame* nsCSSFrameConstructor::ConstructListBoxSelectFrame( nsContainerFrame* listFrame = NS_NewListControlFrame(mPresShell, computedStyle); - nsContainerFrame* scrolledFrame = NS_NewBlockFrame(mPresShell, computedStyle); + nsContainerFrame* scrolledFrame = + NS_NewSelectsAreaFrame(mPresShell, computedStyle); // ******* this code stolen from Initialze ScrollFrame ******** // please adjust this code to use BuildScrollFrame. diff --git a/layout/forms/moz.build b/layout/forms/moz.build @@ -31,6 +31,7 @@ UNIFIED_SOURCES += [ "nsProgressFrame.cpp", "nsRangeFrame.cpp", "nsSearchControlFrame.cpp", + "nsSelectsAreaFrame.cpp", "nsTextControlFrame.cpp", ] diff --git a/layout/forms/nsListControlFrame.cpp b/layout/forms/nsListControlFrame.cpp @@ -79,6 +79,22 @@ void nsListControlFrame::Destroy(DestroyContext& aContext) { ScrollContainerFrame::Destroy(aContext); } +void nsListControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsDisplayListSet& aLists) { + // We allow visibility:hidden <select>s to contain visible options. + + // Don't allow painting of list controls when painting is suppressed. + // XXX why do we need this here? we should never reach this. Maybe + // because these can have widgets? Hmm + if (aBuilder->IsBackgroundOnly()) { + return; + } + + DO_GLOBAL_REFLOW_COUNT_DSP("nsListControlFrame"); + + ScrollContainerFrame::BuildDisplayList(aBuilder, aLists); +} + HTMLOptionElement* nsListControlFrame::GetCurrentOption() const { return mEventListener->GetCurrentOption(); } @@ -87,7 +103,66 @@ bool nsListControlFrame::IsFocused() const { return Select().State().HasState(ElementState::FOCUS); } -void nsListControlFrame::InvalidateFocus() { InvalidateFrame(); } +/** + * This is called by the SelectsAreaFrame, which is the same + * as the frame returned by GetOptionsContainer. It's the frame which is + * scrolled by us. + * @param aPt the offset of this frame, relative to the rendering reference + * frame + */ +void nsListControlFrame::PaintFocus(DrawTarget* aDrawTarget, nsPoint aPt) { + if (!IsFocused()) { + return; + } + + nsIFrame* containerFrame = GetOptionsContainer(); + if (!containerFrame) { + return; + } + + nsIFrame* childframe = nullptr; + nsCOMPtr<nsIContent> focusedContent = GetCurrentOption(); + if (focusedContent) { + childframe = focusedContent->GetPrimaryFrame(); + } + + nsRect fRect; + if (childframe) { + // get the child rect + fRect = childframe->GetRect(); + // get it into our coordinates + fRect.MoveBy(childframe->GetParent()->GetOffsetTo(this)); + } else { + float inflation = nsLayoutUtils::FontSizeInflationFor(this); + fRect.x = fRect.y = 0; + if (GetWritingMode().IsVertical()) { + fRect.width = GetScrollPortRect().width; + fRect.height = CalcFallbackRowBSize(inflation); + } else { + fRect.width = CalcFallbackRowBSize(inflation); + fRect.height = GetScrollPortRect().height; + } + fRect.MoveBy(containerFrame->GetOffsetTo(this)); + } + fRect += aPt; + + const auto* domOpt = HTMLOptionElement::FromNodeOrNull(focusedContent); + const bool isSelected = domOpt && domOpt->Selected(); + + // Set up back stop colors and then ask L&F service for the real colors + nscolor color = + LookAndFeel::Color(isSelected ? LookAndFeel::ColorID::Selecteditemtext + : LookAndFeel::ColorID::Selecteditem, + this); + + nsCSSRendering::PaintFocus(PresContext(), aDrawTarget, fRect, color); +} + +void nsListControlFrame::InvalidateFocus() { + if (nsIFrame* containerFrame = GetOptionsContainer()) { + containerFrame->InvalidateFrame(); + } +} NS_QUERYFRAME_HEAD(nsListControlFrame) NS_QUERYFRAME_ENTRY(nsISelectControlFrame) @@ -141,8 +216,7 @@ nscoord nsListControlFrame::CalcBSizeOfARow() { // fonts, etc. nscoord rowBSize(0); if (GetContainSizeAxes().mBContained || - !GetMaxRowBSize(GetContentInsertionFrame(), GetWritingMode(), - &rowBSize)) { + !GetMaxRowBSize(GetOptionsContainer(), GetWritingMode(), &rowBSize)) { // We don't have any <option>s or <optgroup> labels with a frame. // (Or we're size-contained in block axis, which has the same outcome for // our sizing.) @@ -252,8 +326,6 @@ void nsListControlFrame::Reflow(nsPresContext* aPresContext, ScrollContainerFrame::Reflow(aPresContext, aDesiredSize, state, aStatus); - mBSizeOfARow = CalcBSizeOfARow(); - if (!mMightNeedSecondPass) { NS_ASSERTION(!autoBSize || BSizeOfARow() == oldBSizeOfARow, "How did our BSize of a row change if nothing was dirty?"); @@ -261,6 +333,8 @@ void nsListControlFrame::Reflow(nsPresContext* aPresContext, usingContainBSize, "How do we not need a second pass during initial reflow at " "auto BSize?"); + NS_ASSERTION(!IsScrollbarUpdateSuppressed(), + "Shouldn't be suppressing if we don't need a second pass!"); if (!autoBSize || usingContainBSize) { // Update our mNumDisplayRows based on our new row block size now // that we know it. Note that if autoBSize and we landed in this @@ -283,10 +357,13 @@ void nsListControlFrame::Reflow(nsPresContext* aPresContext, // Now see whether we need a second pass. If we do, our // nsSelectsAreaFrame will have suppressed the scrollbar update. - if (mBSizeOfARow == oldBSizeOfARow) { + if (!IsScrollbarUpdateSuppressed()) { + // All done. No need to do more reflow. return; } + SetSuppressScrollbarUpdate(false); + // Gotta reflow again. // XXXbz We're just changing the block size here; do we need to dirty // ourselves or anything like that? We might need to, per the letter @@ -315,6 +392,11 @@ bool nsListControlFrame::ShouldPropagateComputedBSizeToScrolledContent() const { } //--------------------------------------------------------- +nsContainerFrame* nsListControlFrame::GetContentInsertionFrame() { + return GetOptionsContainer()->GetContentInsertionFrame(); +} + +//--------------------------------------------------------- bool nsListControlFrame::ExtendedSelection(int32_t aStartIndex, int32_t aEndIndex, bool aClearAll) { return SetOptionsSelectedFromFrame(aStartIndex, aEndIndex, true, aClearAll); diff --git a/layout/forms/nsListControlFrame.h b/layout/forms/nsListControlFrame.h @@ -10,6 +10,7 @@ #include "mozilla/ScrollContainerFrame.h" #include "mozilla/StaticPtr.h" #include "nsISelectControlFrame.h" +#include "nsSelectsAreaFrame.h" class nsComboboxControlFrame; class nsPresContext; @@ -64,6 +65,11 @@ class nsListControlFrame final : public mozilla::ScrollContainerFrame, bool ReflowFinished() final; void Destroy(DestroyContext&) override; + void BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsDisplayListSet& aLists) final; + + nsContainerFrame* GetContentInsertionFrame() final; + int32_t GetEndSelectionIndex() const { return mEndSelectionIndex; } mozilla::dom::HTMLOptionElement* GetCurrentOption() const; @@ -170,6 +176,10 @@ class nsListControlFrame final : public mozilla::ScrollContainerFrame, */ bool MightNeedSecondPass() const { return mMightNeedSecondPass; } + void SetSuppressScrollbarUpdate(bool aSuppress) { + ScrollContainerFrame::SetSuppressScrollbarUpdate(aSuppress); + } + /** * Return the number of displayed rows in the list. */ @@ -265,10 +275,14 @@ class nsListControlFrame final : public mozilla::ScrollContainerFrame, void InitSelectionRange(int32_t aClickedIndex); public: + nsSelectsAreaFrame* GetOptionsContainer() const { + return static_cast<nsSelectsAreaFrame*>(GetScrolledFrame()); + } + static constexpr int32_t kNothingSelected = -1; protected: - nscoord BSizeOfARow() const { return mBSizeOfARow; } + nscoord BSizeOfARow() { return GetOptionsContainer()->BSizeOfARow(); } /** * @return how many displayable options/optgroups this frame has. @@ -280,9 +294,11 @@ class nsListControlFrame final : public mozilla::ScrollContainerFrame, int32_t mEndSelectionIndex = 0; uint32_t mNumDisplayRows = 0; - nscoord mBSizeOfARow = -1; bool mChangesSinceDragStart : 1; + // Has the user selected a visible item since we showed the dropdown? + bool mItemSelectionStarted : 1; + bool mIsAllContentHere : 1; bool mIsAllFramesHere : 1; bool mHasBeenInitialized : 1; @@ -297,6 +313,8 @@ class nsListControlFrame final : public mozilla::ScrollContainerFrame, bool mReflowWasInterrupted : 1; RefPtr<mozilla::HTMLSelectEventListener> mEventListener; + + static nsListControlFrame* sFocused; }; #endif /* nsListControlFrame_h___ */ diff --git a/layout/forms/nsSelectsAreaFrame.cpp b/layout/forms/nsSelectsAreaFrame.cpp @@ -0,0 +1,191 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ +#include "nsSelectsAreaFrame.h" + +#include "WritingModes.h" +#include "mozilla/PresShell.h" +#include "nsDisplayList.h" +#include "nsIContent.h" +#include "nsListControlFrame.h" + +using namespace mozilla; + +nsContainerFrame* NS_NewSelectsAreaFrame(PresShell* aShell, + ComputedStyle* aStyle) { + nsSelectsAreaFrame* it = + new (aShell) nsSelectsAreaFrame(aStyle, aShell->GetPresContext()); + return it; +} + +NS_IMPL_FRAMEARENA_HELPERS(nsSelectsAreaFrame) + +NS_QUERYFRAME_HEAD(nsSelectsAreaFrame) + NS_QUERYFRAME_ENTRY(nsSelectsAreaFrame) +NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame) + +static nsListControlFrame* GetEnclosingListFrame(nsIFrame* aSelectsAreaFrame) { + nsIFrame* frame = aSelectsAreaFrame->GetParent(); + while (frame) { + if (frame->IsListControlFrame()) { + return static_cast<nsListControlFrame*>(frame); + } + frame = frame->GetParent(); + } + return nullptr; +} + +void nsSelectsAreaFrame::Reflow(nsPresContext* aPresContext, + ReflowOutput& aDesiredSize, + const ReflowInput& aReflowInput, + nsReflowStatus& aStatus) { + MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); + + nsListControlFrame* list = GetEnclosingListFrame(this); + NS_ASSERTION(list, + "Must have an nsListControlFrame! Frame constructor is " + "broken"); + + nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus); + + // Check whether we need to suppress scrollbar updates. We want to do + // that if we're in a possible first pass and our block size of a row + // has changed. + if (list->MightNeedSecondPass()) { + nscoord newBSizeOfARow = list->CalcBSizeOfARow(); + // We'll need a second pass if our block size of a row changed. For + // comboboxes, we'll also need it if our block size changed. If + // we're going to do a second pass, suppress scrollbar updates for + // this pass. + if (newBSizeOfARow != mBSizeOfARow) { + mBSizeOfARow = newBSizeOfARow; + list->SetSuppressScrollbarUpdate(true); + } + } +} + +namespace mozilla { +/** + * This wrapper class lets us redirect mouse hits from the child frame of + * an option element to the element's own frame. + * REVIEW: This is what nsSelectsAreaFrame::GetFrameForPoint used to do + */ +class nsDisplayOptionEventGrabber final : public nsDisplayWrapList { + public: + nsDisplayOptionEventGrabber(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, + nsDisplayItem* aItem) + : nsDisplayWrapList(aBuilder, aFrame, aItem) {} + nsDisplayOptionEventGrabber(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, + nsDisplayList* aList) + : nsDisplayWrapList(aBuilder, aFrame, aList) {} + virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, + HitTestState* aState, + nsTArray<nsIFrame*>* aOutFrames) override; + virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override { + return false; + } + void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override { + GetChildren()->Paint(aBuilder, aCtx, + mFrame->PresContext()->AppUnitsPerDevPixel()); + } + NS_DISPLAY_DECL_NAME("OptionEventGrabber", TYPE_OPTION_EVENT_GRABBER) +}; + +void nsDisplayOptionEventGrabber::HitTest(nsDisplayListBuilder* aBuilder, + const nsRect& aRect, + HitTestState* aState, + nsTArray<nsIFrame*>* aOutFrames) { + nsTArray<nsIFrame*> outFrames; + mList.HitTest(aBuilder, aRect, aState, &outFrames); + + for (uint32_t i = 0; i < outFrames.Length(); i++) { + nsIFrame* selectedFrame = outFrames.ElementAt(i); + while (selectedFrame && + !(selectedFrame->GetContent() && + selectedFrame->GetContent()->IsHTMLElement(nsGkAtoms::option))) { + selectedFrame = selectedFrame->GetParent(); + } + if (selectedFrame) { + aOutFrames->AppendElement(selectedFrame); + } else { + // keep the original result, which could be this frame + aOutFrames->AppendElement(outFrames.ElementAt(i)); + } + } +} + +class nsOptionEventGrabberWrapper : public nsDisplayItemWrapper { + public: + nsOptionEventGrabberWrapper() = default; + virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder, + nsIFrame* aFrame, + nsDisplayList* aList) override { + return MakeDisplayItem<nsDisplayOptionEventGrabber>(aBuilder, aFrame, + aList); + } + virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder, + nsDisplayItem* aItem) override { + return MakeDisplayItem<nsDisplayOptionEventGrabber>(aBuilder, + aItem->Frame(), aItem); + } +}; + +class nsDisplayListFocus final : public nsPaintedDisplayItem { + public: + nsDisplayListFocus(nsDisplayListBuilder* aBuilder, nsSelectsAreaFrame* aFrame) + : nsPaintedDisplayItem(aBuilder, aFrame) { + MOZ_COUNT_CTOR(nsDisplayListFocus); + } + + MOZ_COUNTED_DTOR_FINAL(nsDisplayListFocus) + + virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, + bool* aSnap) const override { + *aSnap = false; + // override bounds because the list item focus ring may extend outside + // the nsSelectsAreaFrame + nsListControlFrame* listFrame = GetEnclosingListFrame(Frame()); + return listFrame->InkOverflowRectRelativeToSelf() + + listFrame->GetOffsetToCrossDoc(Frame()) + ToReferenceFrame(); + } + virtual void Paint(nsDisplayListBuilder* aBuilder, + gfxContext* aCtx) override { + nsListControlFrame* listFrame = GetEnclosingListFrame(Frame()); + // listFrame must be non-null or we wouldn't get called. + listFrame->PaintFocus( + aCtx->GetDrawTarget(), + listFrame->GetOffsetToCrossDoc(Frame()) + ToReferenceFrame()); + } + NS_DISPLAY_DECL_NAME("ListFocus", TYPE_LIST_FOCUS) +}; + +} // namespace mozilla + +void nsSelectsAreaFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsDisplayListSet& aLists) { + if (!aBuilder->IsForEventDelivery()) { + BuildDisplayListInternal(aBuilder, aLists); + return; + } + + nsDisplayListCollection set(aBuilder); + BuildDisplayListInternal(aBuilder, set); + + nsOptionEventGrabberWrapper wrapper; + wrapper.WrapLists(aBuilder, this, set, aLists); +} + +void nsSelectsAreaFrame::BuildDisplayListInternal( + nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) { + nsBlockFrame::BuildDisplayList(aBuilder, aLists); + + nsListControlFrame* listFrame = GetEnclosingListFrame(this); + if (listFrame && listFrame->IsFocused()) { + // we can't just associate the display item with the list frame, + // because then the list's scrollframe won't clip it (the scrollframe + // only clips contained descendants). + aLists.Outlines()->AppendNewToTop<nsDisplayListFocus>(aBuilder, this); + } +} diff --git a/layout/forms/nsSelectsAreaFrame.h b/layout/forms/nsSelectsAreaFrame.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ +#ifndef nsSelectsAreaFrame_h___ +#define nsSelectsAreaFrame_h___ + +#include "nsBlockFrame.h" + +namespace mozilla { +class PresShell; +} // namespace mozilla + +class nsSelectsAreaFrame final : public nsBlockFrame { + public: + NS_DECL_QUERYFRAME + NS_DECL_FRAMEARENA_HELPERS(nsSelectsAreaFrame) + + friend nsContainerFrame* NS_NewSelectsAreaFrame(mozilla::PresShell* aShell, + ComputedStyle* aStyle); + + void BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsDisplayListSet& aLists) override; + + void BuildDisplayListInternal(nsDisplayListBuilder* aBuilder, + const nsDisplayListSet& aLists); + + void Reflow(nsPresContext* aCX, ReflowOutput& aDesiredSize, + const ReflowInput& aReflowInput, + nsReflowStatus& aStatus) override; + + nscoord BSizeOfARow() const { return mBSizeOfARow; } + +#ifdef DEBUG_FRAME_DUMP + nsresult GetFrameName(nsAString& aResult) const override { + return MakeFrameName(u"SelectsArea"_ns, aResult); + } +#endif + + protected: + explicit nsSelectsAreaFrame(ComputedStyle* aStyle, + nsPresContext* aPresContext) + : nsBlockFrame(aStyle, aPresContext, kClassID), + // initialize to wacky value so first call of + // nsSelectsAreaFrame::Reflow will always invalidate + mBSizeOfARow(nscoord_MIN) {} + + // We cache the block size of a single row so that changes to the + // "size" attribute, padding, etc. can all be handled with only one + // reflow. We'll have to reflow twice if someone changes our font + // size or something like that, so that the block size of our options + // will change. + nscoord mBSizeOfARow; +}; + +#endif /* nsSelectsAreaFrame_h___ */ diff --git a/layout/generic/ScrollContainerFrame.cpp b/layout/generic/ScrollContainerFrame.cpp @@ -59,7 +59,6 @@ #include "mozilla/dom/Element.h" #include "mozilla/dom/Event.h" #include "mozilla/dom/HTMLMarqueeElement.h" -#include "mozilla/dom/HTMLOptionElement.h" #include "mozilla/dom/NodeInfo.h" #include "mozilla/dom/ScrollTimeline.h" #include "mozilla/gfx/gfxVars.h" @@ -75,7 +74,6 @@ #include "nsBidiUtils.h" #include "nsBlockFrame.h" #include "nsCOMPtr.h" -#include "nsCSSRendering.h" #include "nsContainerFrame.h" #include "nsContentCreatorFunctions.h" #include "nsContentUtils.h" @@ -92,7 +90,6 @@ #include "nsIScrollbarMediator.h" #include "nsIXULRuntime.h" #include "nsLayoutUtils.h" -#include "nsListControlFrame.h" #include "nsNameSpaceManager.h" #include "nsNodeInfoManager.h" #include "nsPresContext.h" @@ -261,6 +258,7 @@ ScrollContainerFrame::ScrollContainerFrame(ComputedStyle* aStyle, mFrameIsUpdatingScrollbar(false), mDidHistoryRestore(false), mIsRoot(aIsRoot), + mSuppressScrollbarUpdate(false), mSkippedScrollbarLayout(false), mHadNonInitialReflow(false), mFirstReflow(true), @@ -1518,16 +1516,20 @@ void ScrollContainerFrame::Reflow(nsPresContext* aPresContext, didOnlyVScrollbar != mOnlyNeedVScrollbarToScrollVVInsideLV || !oldScrollPort.IsEqualEdges(newScrollPort) || !oldScrolledAreaBounds.IsEqualEdges(newScrolledAreaBounds)) { - mSkippedScrollbarLayout = false; - ScrollContainerFrame::SetScrollbarVisibility(mHScrollbarBox, - state.mShowHScrollbar); - ScrollContainerFrame::SetScrollbarVisibility(mVScrollbarBox, - state.mShowVScrollbar); - // place and reflow scrollbars - const nsRect insideBorderArea( - nsPoint(state.mComputedBorder.left, state.mComputedBorder.top), - layoutSize); - LayoutScrollbars(state, insideBorderArea, oldScrollPort); + if (!mSuppressScrollbarUpdate) { + mSkippedScrollbarLayout = false; + ScrollContainerFrame::SetScrollbarVisibility(mHScrollbarBox, + state.mShowHScrollbar); + ScrollContainerFrame::SetScrollbarVisibility(mVScrollbarBox, + state.mShowVScrollbar); + // place and reflow scrollbars + const nsRect insideBorderArea( + nsPoint(state.mComputedBorder.left, state.mComputedBorder.top), + layoutSize); + LayoutScrollbars(state, insideBorderArea, oldScrollPort); + } else { + mSkippedScrollbarLayout = true; + } } if (mIsRoot) { if (RefPtr<MobileViewportManager> manager = @@ -3729,53 +3731,6 @@ void ScrollContainerFrame::MaybeCreateTopLayerAndWrapRootItems( } } -class nsDisplayListFocus final : public nsPaintedDisplayItem { - public: - nsDisplayListFocus(nsDisplayListBuilder* aBuilder, nsListControlFrame* aFrame) - : nsPaintedDisplayItem(aBuilder, aFrame) { - MOZ_COUNT_CTOR(nsDisplayListFocus); - } - - Maybe<nsCSSBorderRenderer> Renderer(DrawTarget* aDt) const { - auto* listFrame = static_cast<nsListControlFrame*>(Frame()); - auto* option = listFrame->GetCurrentOption(); - if (!option) { - return {}; - } - nsIFrame* frame = option->GetPrimaryFrame(); - if (!frame) { - return {}; - } - nscolor color = LookAndFeel::Color( - option->Selected() ? LookAndFeel::ColorID::Selecteditemtext - : LookAndFeel::ColorID::Selecteditem, - frame); - auto rect = frame->GetRectRelativeToSelf() + frame->GetOffsetTo(listFrame) + - ToReferenceFrame(); - return Some( - nsCSSRendering::GetBorderRendererForFocus(listFrame, aDt, rect, color)); - } - - MOZ_COUNTED_DTOR_FINAL(nsDisplayListFocus) - - void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override { - if (auto br = Renderer(aCtx->GetDrawTarget())) { - br->DrawBorders(); - } - } - bool CreateWebRenderCommands( - wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, - const StackingContextHelper& aSc, - layers::RenderRootStateManager* aManager, - nsDisplayListBuilder* aDisplayListBuilder) override { - if (auto br = Renderer(nullptr)) { - br->CreateWebRenderCommands(this, aBuilder, aResources, aSc); - } - return true; - } - NS_DISPLAY_DECL_NAME("ListFocus", TYPE_LIST_FOCUS) -}; - void ScrollContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) { SetAndNullOnExit<const nsIFrame> tmpBuilder( @@ -4091,10 +4046,6 @@ void ScrollContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, BuildDisplayListForChild(aBuilder, mScrolledFrame, set); - if (nsListControlFrame* lc = do_QueryFrame(this); lc && lc->IsFocused()) { - set.Outlines()->AppendNewToTop<nsDisplayListFocus>(aBuilder, lc); - } - if (dirtyRectHasBeenOverriden && StaticPrefs::layout_display_list_show_rebuild_area()) { nsDisplaySolidColor* color = MakeDisplayItem<nsDisplaySolidColor>( @@ -6418,6 +6369,8 @@ void ScrollContainerFrame::LayoutScrollbarPartAtRect( void ScrollContainerFrame::LayoutScrollbars(ScrollReflowInput& aState, const nsRect& aInsideBorderArea, const nsRect& aOldScrollPort) { + NS_ASSERTION(!mSuppressScrollbarUpdate, "This should have been suppressed"); + const bool scrollbarOnLeft = !IsScrollbarOnRight(); const bool overlayScrollbars = UsesOverlayScrollbars(); const bool overlayScrollBarsOnRoot = overlayScrollbars && mIsRoot; diff --git a/layout/generic/ScrollContainerFrame.h b/layout/generic/ScrollContainerFrame.h @@ -1053,9 +1053,14 @@ class ScrollContainerFrame : public nsContainerFrame, ScrollContainerFrame(ComputedStyle* aStyle, nsPresContext* aPresContext, nsIFrame::ClassID aID, bool aIsRoot); ~ScrollContainerFrame(); + void SetSuppressScrollbarUpdate(bool aSuppress) { + mSuppressScrollbarUpdate = aSuppress; + } bool GuessHScrollbarNeeded(const ScrollReflowInput& aState); bool GuessVScrollbarNeeded(const ScrollReflowInput& aState); + bool IsScrollbarUpdateSuppressed() const { return mSuppressScrollbarUpdate; } + // Return whether we're in an "initial" reflow. Some reflows with // NS_FRAME_FIRST_REFLOW set are NOT "initial" as far as we're concerned. bool InInitialReflow() const; @@ -1444,6 +1449,10 @@ class ScrollContainerFrame : public nsContainerFrame, bool mDidHistoryRestore : 1; // Is this the scrollframe for the document's viewport? bool mIsRoot : 1; + // If true, don't try to layout the scrollbars in Reflow(). This can be + // useful if multiple passes are involved, because we don't want to place the + // scrollbars at the wrong size. + bool mSuppressScrollbarUpdate : 1; // If true, we skipped a scrollbar layout due to mSuppressScrollbarUpdate // being set at some point. That means we should lay out scrollbars even if // it might not strictly be needed next time mSuppressScrollbarUpdate is diff --git a/layout/generic/nsFontInflationData.cpp b/layout/generic/nsFontInflationData.cpp @@ -312,7 +312,8 @@ static uint32_t DoCharCountOfLargestOption(nsIFrame* aContainer) { static uint32_t CharCountOfLargestOption(nsIFrame* aListControlFrame) { return DoCharCountOfLargestOption( - aListControlFrame->GetContentInsertionFrame()); + static_cast<nsListControlFrame*>(aListControlFrame) + ->GetOptionsContainer()); } void nsFontInflationData::ScanTextIn(nsIFrame* aFrame) { diff --git a/layout/generic/nsHTMLParts.h b/layout/generic/nsHTMLParts.h @@ -49,6 +49,13 @@ nsresult NS_NewAttributeContent(nsNodeInfoManager* aNodeInfoManager, int32_t aNameSpaceID, nsAtom* aAttrName, nsAtom* aFallback, nsIContent** aResult); +// Create a basic area frame but the GetFrameForPoint is overridden to always +// return the option frame +// By default, area frames will extend +// their height to cover any children that "stick out". +nsContainerFrame* NS_NewSelectsAreaFrame(mozilla::PresShell* aPresShell, + mozilla::ComputedStyle* aStyle); + nsIFrame* NS_NewBRFrame(mozilla::PresShell* aPresShell, mozilla::ComputedStyle* aStyle); diff --git a/layout/painting/nsCSSRendering.cpp b/layout/painting/nsCSSRendering.cpp @@ -1042,12 +1042,11 @@ void nsCSSRendering::PaintNonThemedOutline(nsPresContext* aPresContext, PrintAsStringNewline(); } -nsCSSBorderRenderer nsCSSRendering::GetBorderRendererForFocus( - nsIFrame* aForFrame, DrawTarget* aDrawTarget, const nsRect& aFocusRect, - nscolor aColor) { - auto* pc = aForFrame->PresContext(); +void nsCSSRendering::PaintFocus(nsPresContext* aPresContext, + DrawTarget* aDrawTarget, + const nsRect& aFocusRect, nscolor aColor) { nscoord oneCSSPixel = nsPresContext::CSSPixelsToAppUnits(1); - nscoord oneDevPixel = pc->DevPixelsToAppUnits(1); + nscoord oneDevPixel = aPresContext->DevPixelsToAppUnits(1); Rect focusRect(NSRectToRect(aFocusRect, oneDevPixel)); @@ -1067,9 +1066,15 @@ nsCSSBorderRenderer nsCSSRendering::GetBorderRendererForFocus( // something that CSS can style, this function will then have access // to a ComputedStyle and can use the same logic that PaintBorder // and PaintOutline do.) - return nsCSSBorderRenderer(pc, aDrawTarget, focusRect, focusRect, focusStyles, - focusWidths, focusRadii, focusColors, - !aForFrame->BackfaceIsHidden(), Nothing()); + // + // WebRender layers-free mode don't use PaintFocus function. Just assign + // the backface-visibility to true for this case. + nsCSSBorderRenderer br(aPresContext, aDrawTarget, focusRect, focusRect, + focusStyles, focusWidths, focusRadii, focusColors, + true, Nothing()); + br.DrawBorders(); + + PrintAsStringNewline(); } // Thebes Border Rendering Code End diff --git a/layout/painting/nsCSSRendering.h b/layout/painting/nsCSSRendering.h @@ -247,9 +247,8 @@ struct nsCSSRendering { * Uses a fixed style equivalent to "1px dotted |aColor|". * Not used for controls, because the native theme may differ. */ - static nsCSSBorderRenderer GetBorderRendererForFocus(nsIFrame*, DrawTarget*, - const nsRect& aFocusRect, - nscolor aColor); + static void PaintFocus(nsPresContext* aPresContext, DrawTarget* aDrawTarget, + const nsRect& aFocusRect, nscolor aColor); /** * Render a gradient for an element.