Bug 1872300 - Make `nsFocusManager::ContentRemoved()` call `HTMLEditor::FinalizeSelec...
[gecko.git] / layout / forms / nsSelectsAreaFrame.cpp
blob121813602350560f1fabfda56778d5ec075c255e
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsSelectsAreaFrame.h"
8 #include "mozilla/PresShell.h"
9 #include "nsIContent.h"
10 #include "nsListControlFrame.h"
11 #include "nsDisplayList.h"
12 #include "WritingModes.h"
14 using namespace mozilla;
16 nsContainerFrame* NS_NewSelectsAreaFrame(PresShell* aShell,
17 ComputedStyle* aStyle) {
18 nsSelectsAreaFrame* it =
19 new (aShell) nsSelectsAreaFrame(aStyle, aShell->GetPresContext());
20 return it;
23 NS_IMPL_FRAMEARENA_HELPERS(nsSelectsAreaFrame)
25 NS_QUERYFRAME_HEAD(nsSelectsAreaFrame)
26 NS_QUERYFRAME_ENTRY(nsSelectsAreaFrame)
27 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
29 static nsListControlFrame* GetEnclosingListFrame(nsIFrame* aSelectsAreaFrame) {
30 nsIFrame* frame = aSelectsAreaFrame->GetParent();
31 while (frame) {
32 if (frame->IsListControlFrame())
33 return static_cast<nsListControlFrame*>(frame);
34 frame = frame->GetParent();
36 return nullptr;
39 void nsSelectsAreaFrame::Reflow(nsPresContext* aPresContext,
40 ReflowOutput& aDesiredSize,
41 const ReflowInput& aReflowInput,
42 nsReflowStatus& aStatus) {
43 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
45 nsListControlFrame* list = GetEnclosingListFrame(this);
46 NS_ASSERTION(list,
47 "Must have an nsListControlFrame! Frame constructor is "
48 "broken");
50 nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
52 // Check whether we need to suppress scrollbar updates. We want to do
53 // that if we're in a possible first pass and our block size of a row
54 // has changed.
55 if (list->MightNeedSecondPass()) {
56 nscoord newBSizeOfARow = list->CalcBSizeOfARow();
57 // We'll need a second pass if our block size of a row changed. For
58 // comboboxes, we'll also need it if our block size changed. If
59 // we're going to do a second pass, suppress scrollbar updates for
60 // this pass.
61 if (newBSizeOfARow != mBSizeOfARow) {
62 mBSizeOfARow = newBSizeOfARow;
63 list->SetSuppressScrollbarUpdate(true);
68 namespace mozilla {
69 /**
70 * This wrapper class lets us redirect mouse hits from the child frame of
71 * an option element to the element's own frame.
72 * REVIEW: This is what nsSelectsAreaFrame::GetFrameForPoint used to do
74 class nsDisplayOptionEventGrabber : public nsDisplayWrapList {
75 public:
76 nsDisplayOptionEventGrabber(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
77 nsDisplayItem* aItem)
78 : nsDisplayWrapList(aBuilder, aFrame, aItem) {}
79 nsDisplayOptionEventGrabber(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
80 nsDisplayList* aList)
81 : nsDisplayWrapList(aBuilder, aFrame, aList) {}
82 virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
83 HitTestState* aState,
84 nsTArray<nsIFrame*>* aOutFrames) override;
85 virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
86 return false;
88 void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override {
89 GetChildren()->Paint(aBuilder, aCtx,
90 mFrame->PresContext()->AppUnitsPerDevPixel());
92 NS_DISPLAY_DECL_NAME("OptionEventGrabber", TYPE_OPTION_EVENT_GRABBER)
95 void nsDisplayOptionEventGrabber::HitTest(nsDisplayListBuilder* aBuilder,
96 const nsRect& aRect,
97 HitTestState* aState,
98 nsTArray<nsIFrame*>* aOutFrames) {
99 nsTArray<nsIFrame*> outFrames;
100 mList.HitTest(aBuilder, aRect, aState, &outFrames);
102 for (uint32_t i = 0; i < outFrames.Length(); i++) {
103 nsIFrame* selectedFrame = outFrames.ElementAt(i);
104 while (selectedFrame &&
105 !(selectedFrame->GetContent() &&
106 selectedFrame->GetContent()->IsHTMLElement(nsGkAtoms::option))) {
107 selectedFrame = selectedFrame->GetParent();
109 if (selectedFrame) {
110 aOutFrames->AppendElement(selectedFrame);
111 } else {
112 // keep the original result, which could be this frame
113 aOutFrames->AppendElement(outFrames.ElementAt(i));
118 class nsOptionEventGrabberWrapper : public nsDisplayItemWrapper {
119 public:
120 nsOptionEventGrabberWrapper() = default;
121 virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder,
122 nsIFrame* aFrame,
123 nsDisplayList* aList) override {
124 return MakeDisplayItem<nsDisplayOptionEventGrabber>(aBuilder, aFrame,
125 aList);
127 virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder,
128 nsDisplayItem* aItem) override {
129 return MakeDisplayItem<nsDisplayOptionEventGrabber>(aBuilder,
130 aItem->Frame(), aItem);
134 class nsDisplayListFocus : public nsPaintedDisplayItem {
135 public:
136 nsDisplayListFocus(nsDisplayListBuilder* aBuilder, nsSelectsAreaFrame* aFrame)
137 : nsPaintedDisplayItem(aBuilder, aFrame) {
138 MOZ_COUNT_CTOR(nsDisplayListFocus);
140 MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayListFocus)
142 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
143 bool* aSnap) const override {
144 *aSnap = false;
145 // override bounds because the list item focus ring may extend outside
146 // the nsSelectsAreaFrame
147 nsListControlFrame* listFrame = GetEnclosingListFrame(Frame());
148 return listFrame->InkOverflowRectRelativeToSelf() +
149 listFrame->GetOffsetToCrossDoc(Frame()) + ToReferenceFrame();
151 virtual void Paint(nsDisplayListBuilder* aBuilder,
152 gfxContext* aCtx) override {
153 nsListControlFrame* listFrame = GetEnclosingListFrame(Frame());
154 // listFrame must be non-null or we wouldn't get called.
155 listFrame->PaintFocus(
156 aCtx->GetDrawTarget(),
157 listFrame->GetOffsetToCrossDoc(Frame()) + ToReferenceFrame());
159 NS_DISPLAY_DECL_NAME("ListFocus", TYPE_LIST_FOCUS)
162 } // namespace mozilla
164 void nsSelectsAreaFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
165 const nsDisplayListSet& aLists) {
166 if (!aBuilder->IsForEventDelivery()) {
167 BuildDisplayListInternal(aBuilder, aLists);
168 return;
171 nsDisplayListCollection set(aBuilder);
172 BuildDisplayListInternal(aBuilder, set);
174 nsOptionEventGrabberWrapper wrapper;
175 wrapper.WrapLists(aBuilder, this, set, aLists);
178 void nsSelectsAreaFrame::BuildDisplayListInternal(
179 nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) {
180 nsBlockFrame::BuildDisplayList(aBuilder, aLists);
182 nsListControlFrame* listFrame = GetEnclosingListFrame(this);
183 if (listFrame && listFrame->IsFocused()) {
184 // we can't just associate the display item with the list frame,
185 // because then the list's scrollframe won't clip it (the scrollframe
186 // only clips contained descendants).
187 aLists.Outlines()->AppendNewToTop<nsDisplayListFocus>(aBuilder, this);