tor-browser

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

commit 0e35e47a582b10eacf12ad77f2f694e4bf262d10
parent 43c97a062976252bce5968555060136810099ac2
Author: Emilio Cobos Álvarez <emilio@crisal.io>
Date:   Tue,  6 Jan 2026 12:10:33 +0000

Bug 2008108 - Simplify listbox select rendering. r=jwatt

No effective user visible behavior change, but this improves on the
previous state of things in a few ways. First, nsSelectsAreaFrame is
removed. This frame did three things:

 * Calculated the row bsize at the end of reflow. We can move that to
   nsListControlFrame::Reflow.

 * Drew the focus navigation outline: We can move it to
   ScrollContainerFrame and simplify a bit the logic while at it.

 * Made events go to the list control. We could do that with CSS if
   needed, but other browsers don't and no test seems to rely on it.

Secondly, nsDisplayListFocus now uses WebRender, avoiding slow fallback
code.

Differential Revision: https://phabricator.services.mozilla.com/D277704

Diffstat:
Mlayout/base/nsCSSFrameConstructor.cpp | 3+--
Mlayout/forms/moz.build | 1-
Mlayout/forms/nsListControlFrame.cpp | 94+++++--------------------------------------------------------------------------
Mlayout/forms/nsListControlFrame.h | 22++--------------------
Dlayout/forms/nsSelectsAreaFrame.cpp | 191-------------------------------------------------------------------------------
Dlayout/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, 85 insertions(+), 409 deletions(-)

diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp @@ -3034,8 +3034,7 @@ nsIFrame* nsCSSFrameConstructor::ConstructListBoxSelectFrame( nsContainerFrame* listFrame = NS_NewListControlFrame(mPresShell, computedStyle); - nsContainerFrame* scrolledFrame = - NS_NewSelectsAreaFrame(mPresShell, computedStyle); + nsContainerFrame* scrolledFrame = NS_NewBlockFrame(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,7 +31,6 @@ 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,22 +79,6 @@ 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(); } @@ -103,66 +87,7 @@ bool nsListControlFrame::IsFocused() const { return Select().State().HasState(ElementState::FOCUS); } -/** - * 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(); - } -} +void nsListControlFrame::InvalidateFocus() { InvalidateFrame(); } NS_QUERYFRAME_HEAD(nsListControlFrame) NS_QUERYFRAME_ENTRY(nsISelectControlFrame) @@ -216,7 +141,8 @@ nscoord nsListControlFrame::CalcBSizeOfARow() { // fonts, etc. nscoord rowBSize(0); if (GetContainSizeAxes().mBContained || - !GetMaxRowBSize(GetOptionsContainer(), GetWritingMode(), &rowBSize)) { + !GetMaxRowBSize(GetContentInsertionFrame(), 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.) @@ -326,6 +252,8 @@ 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?"); @@ -333,8 +261,6 @@ 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 @@ -357,13 +283,10 @@ 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 (!IsScrollbarUpdateSuppressed()) { - // All done. No need to do more reflow. + if (mBSizeOfARow == oldBSizeOfARow) { 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 @@ -392,11 +315,6 @@ 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,7 +10,6 @@ #include "mozilla/ScrollContainerFrame.h" #include "mozilla/StaticPtr.h" #include "nsISelectControlFrame.h" -#include "nsSelectsAreaFrame.h" class nsComboboxControlFrame; class nsPresContext; @@ -65,11 +64,6 @@ 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; @@ -176,10 +170,6 @@ 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. */ @@ -275,14 +265,10 @@ 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() { return GetOptionsContainer()->BSizeOfARow(); } + nscoord BSizeOfARow() const { return mBSizeOfARow; } /** * @return how many displayable options/optgroups this frame has. @@ -294,11 +280,9 @@ 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; @@ -313,8 +297,6 @@ 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 @@ -1,191 +0,0 @@ -/* -*- 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 @@ -1,57 +0,0 @@ -/* -*- 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,6 +59,7 @@ #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" @@ -74,6 +75,7 @@ #include "nsBidiUtils.h" #include "nsBlockFrame.h" #include "nsCOMPtr.h" +#include "nsCSSRendering.h" #include "nsContainerFrame.h" #include "nsContentCreatorFunctions.h" #include "nsContentUtils.h" @@ -90,6 +92,7 @@ #include "nsIScrollbarMediator.h" #include "nsIXULRuntime.h" #include "nsLayoutUtils.h" +#include "nsListControlFrame.h" #include "nsNameSpaceManager.h" #include "nsNodeInfoManager.h" #include "nsPresContext.h" @@ -258,7 +261,6 @@ ScrollContainerFrame::ScrollContainerFrame(ComputedStyle* aStyle, mFrameIsUpdatingScrollbar(false), mDidHistoryRestore(false), mIsRoot(aIsRoot), - mSuppressScrollbarUpdate(false), mSkippedScrollbarLayout(false), mHadNonInitialReflow(false), mFirstReflow(true), @@ -1516,20 +1518,16 @@ void ScrollContainerFrame::Reflow(nsPresContext* aPresContext, didOnlyVScrollbar != mOnlyNeedVScrollbarToScrollVVInsideLV || !oldScrollPort.IsEqualEdges(newScrollPort) || !oldScrolledAreaBounds.IsEqualEdges(newScrolledAreaBounds)) { - 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; - } + 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 (mIsRoot) { if (RefPtr<MobileViewportManager> manager = @@ -3731,6 +3729,53 @@ 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( @@ -4046,6 +4091,10 @@ 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>( @@ -6369,8 +6418,6 @@ 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,14 +1053,9 @@ 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; @@ -1449,10 +1444,6 @@ 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,8 +312,7 @@ static uint32_t DoCharCountOfLargestOption(nsIFrame* aContainer) { static uint32_t CharCountOfLargestOption(nsIFrame* aListControlFrame) { return DoCharCountOfLargestOption( - static_cast<nsListControlFrame*>(aListControlFrame) - ->GetOptionsContainer()); + aListControlFrame->GetContentInsertionFrame()); } void nsFontInflationData::ScanTextIn(nsIFrame* aFrame) { diff --git a/layout/generic/nsHTMLParts.h b/layout/generic/nsHTMLParts.h @@ -49,13 +49,6 @@ 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,11 +1042,12 @@ void nsCSSRendering::PaintNonThemedOutline(nsPresContext* aPresContext, PrintAsStringNewline(); } -void nsCSSRendering::PaintFocus(nsPresContext* aPresContext, - DrawTarget* aDrawTarget, - const nsRect& aFocusRect, nscolor aColor) { +nsCSSBorderRenderer nsCSSRendering::GetBorderRendererForFocus( + nsIFrame* aForFrame, DrawTarget* aDrawTarget, const nsRect& aFocusRect, + nscolor aColor) { + auto* pc = aForFrame->PresContext(); nscoord oneCSSPixel = nsPresContext::CSSPixelsToAppUnits(1); - nscoord oneDevPixel = aPresContext->DevPixelsToAppUnits(1); + nscoord oneDevPixel = pc->DevPixelsToAppUnits(1); Rect focusRect(NSRectToRect(aFocusRect, oneDevPixel)); @@ -1066,15 +1067,9 @@ void nsCSSRendering::PaintFocus(nsPresContext* aPresContext, // 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.) - // - // 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(); + return nsCSSBorderRenderer(pc, aDrawTarget, focusRect, focusRect, focusStyles, + focusWidths, focusRadii, focusColors, + !aForFrame->BackfaceIsHidden(), Nothing()); } // Thebes Border Rendering Code End diff --git a/layout/painting/nsCSSRendering.h b/layout/painting/nsCSSRendering.h @@ -247,8 +247,9 @@ struct nsCSSRendering { * Uses a fixed style equivalent to "1px dotted |aColor|". * Not used for controls, because the native theme may differ. */ - static void PaintFocus(nsPresContext* aPresContext, DrawTarget* aDrawTarget, - const nsRect& aFocusRect, nscolor aColor); + static nsCSSBorderRenderer GetBorderRendererForFocus(nsIFrame*, DrawTarget*, + const nsRect& aFocusRect, + nscolor aColor); /** * Render a gradient for an element.