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:
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.