Bug 1869043 allow a device to be specified with MediaTrackGraph::NotifyWhenDeviceStar...
[gecko.git] / layout / forms / nsSelectsAreaFrame.cpp
blob3c3fad14348a46f0187f33395b99d4714c58fb64
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());
21 // We need NS_BLOCK_STATIC_BFC to ensure that the options inside the select
22 // aren't expanded by right floats outside the select.
23 it->AddStateBits(NS_BLOCK_STATIC_BFC);
25 return it;
28 NS_IMPL_FRAMEARENA_HELPERS(nsSelectsAreaFrame)
30 static nsListControlFrame* GetEnclosingListFrame(nsIFrame* aSelectsAreaFrame) {
31 nsIFrame* frame = aSelectsAreaFrame->GetParent();
32 while (frame) {
33 if (frame->IsListControlFrame())
34 return static_cast<nsListControlFrame*>(frame);
35 frame = frame->GetParent();
37 return nullptr;
40 void nsSelectsAreaFrame::Reflow(nsPresContext* aPresContext,
41 ReflowOutput& aDesiredSize,
42 const ReflowInput& aReflowInput,
43 nsReflowStatus& aStatus) {
44 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
46 nsListControlFrame* list = GetEnclosingListFrame(this);
47 NS_ASSERTION(list,
48 "Must have an nsListControlFrame! Frame constructor is "
49 "broken");
51 nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
53 // Check whether we need to suppress scrollbar updates. We want to do
54 // that if we're in a possible first pass and our block size of a row
55 // has changed.
56 if (list->MightNeedSecondPass()) {
57 nscoord newBSizeOfARow = list->CalcBSizeOfARow();
58 // We'll need a second pass if our block size of a row changed. For
59 // comboboxes, we'll also need it if our block size changed. If
60 // we're going to do a second pass, suppress scrollbar updates for
61 // this pass.
62 if (newBSizeOfARow != mBSizeOfARow) {
63 mBSizeOfARow = newBSizeOfARow;
64 list->SetSuppressScrollbarUpdate(true);
69 namespace mozilla {
70 /**
71 * This wrapper class lets us redirect mouse hits from the child frame of
72 * an option element to the element's own frame.
73 * REVIEW: This is what nsSelectsAreaFrame::GetFrameForPoint used to do
75 class nsDisplayOptionEventGrabber : public nsDisplayWrapList {
76 public:
77 nsDisplayOptionEventGrabber(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
78 nsDisplayItem* aItem)
79 : nsDisplayWrapList(aBuilder, aFrame, aItem) {}
80 nsDisplayOptionEventGrabber(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
81 nsDisplayList* aList)
82 : nsDisplayWrapList(aBuilder, aFrame, aList) {}
83 virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
84 HitTestState* aState,
85 nsTArray<nsIFrame*>* aOutFrames) override;
86 virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
87 return false;
89 void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override {
90 GetChildren()->Paint(aBuilder, aCtx,
91 mFrame->PresContext()->AppUnitsPerDevPixel());
93 NS_DISPLAY_DECL_NAME("OptionEventGrabber", TYPE_OPTION_EVENT_GRABBER)
96 void nsDisplayOptionEventGrabber::HitTest(nsDisplayListBuilder* aBuilder,
97 const nsRect& aRect,
98 HitTestState* aState,
99 nsTArray<nsIFrame*>* aOutFrames) {
100 nsTArray<nsIFrame*> outFrames;
101 mList.HitTest(aBuilder, aRect, aState, &outFrames);
103 for (uint32_t i = 0; i < outFrames.Length(); i++) {
104 nsIFrame* selectedFrame = outFrames.ElementAt(i);
105 while (selectedFrame &&
106 !(selectedFrame->GetContent() &&
107 selectedFrame->GetContent()->IsHTMLElement(nsGkAtoms::option))) {
108 selectedFrame = selectedFrame->GetParent();
110 if (selectedFrame) {
111 aOutFrames->AppendElement(selectedFrame);
112 } else {
113 // keep the original result, which could be this frame
114 aOutFrames->AppendElement(outFrames.ElementAt(i));
119 class nsOptionEventGrabberWrapper : public nsDisplayItemWrapper {
120 public:
121 nsOptionEventGrabberWrapper() = default;
122 virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder,
123 nsIFrame* aFrame,
124 nsDisplayList* aList) override {
125 return MakeDisplayItem<nsDisplayOptionEventGrabber>(aBuilder, aFrame,
126 aList);
128 virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder,
129 nsDisplayItem* aItem) override {
130 return MakeDisplayItem<nsDisplayOptionEventGrabber>(aBuilder,
131 aItem->Frame(), aItem);
135 class nsDisplayListFocus : public nsPaintedDisplayItem {
136 public:
137 nsDisplayListFocus(nsDisplayListBuilder* aBuilder, nsSelectsAreaFrame* aFrame)
138 : nsPaintedDisplayItem(aBuilder, aFrame) {
139 MOZ_COUNT_CTOR(nsDisplayListFocus);
141 MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayListFocus)
143 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
144 bool* aSnap) const override {
145 *aSnap = false;
146 // override bounds because the list item focus ring may extend outside
147 // the nsSelectsAreaFrame
148 nsListControlFrame* listFrame = GetEnclosingListFrame(Frame());
149 return listFrame->InkOverflowRectRelativeToSelf() +
150 listFrame->GetOffsetToCrossDoc(Frame()) + ToReferenceFrame();
152 virtual void Paint(nsDisplayListBuilder* aBuilder,
153 gfxContext* aCtx) override {
154 nsListControlFrame* listFrame = GetEnclosingListFrame(Frame());
155 // listFrame must be non-null or we wouldn't get called.
156 listFrame->PaintFocus(
157 aCtx->GetDrawTarget(),
158 listFrame->GetOffsetToCrossDoc(Frame()) + ToReferenceFrame());
160 NS_DISPLAY_DECL_NAME("ListFocus", TYPE_LIST_FOCUS)
163 } // namespace mozilla
165 void nsSelectsAreaFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
166 const nsDisplayListSet& aLists) {
167 if (!aBuilder->IsForEventDelivery()) {
168 BuildDisplayListInternal(aBuilder, aLists);
169 return;
172 nsDisplayListCollection set(aBuilder);
173 BuildDisplayListInternal(aBuilder, set);
175 nsOptionEventGrabberWrapper wrapper;
176 wrapper.WrapLists(aBuilder, this, set, aLists);
179 void nsSelectsAreaFrame::BuildDisplayListInternal(
180 nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) {
181 nsBlockFrame::BuildDisplayList(aBuilder, aLists);
183 nsListControlFrame* listFrame = GetEnclosingListFrame(this);
184 if (listFrame && listFrame->IsFocused()) {
185 // we can't just associate the display item with the list frame,
186 // because then the list's scrollframe won't clip it (the scrollframe
187 // only clips contained descendants).
188 aLists.Outlines()->AppendNewToTop<nsDisplayListFocus>(aBuilder, this);