Bumping manifests a=b2g-bump
[gecko.git] / layout / forms / nsSelectsAreaFrame.cpp
blobc72a552d21bf668813f038151e78a0d3fcc30957
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "nsSelectsAreaFrame.h"
6 #include "nsIContent.h"
7 #include "nsListControlFrame.h"
8 #include "nsDisplayList.h"
10 nsContainerFrame*
11 NS_NewSelectsAreaFrame(nsIPresShell* aShell, nsStyleContext* aContext, nsFrameState aFlags)
13 nsSelectsAreaFrame* it = new (aShell) nsSelectsAreaFrame(aContext);
15 // We need NS_BLOCK_FLOAT_MGR to ensure that the options inside the select
16 // aren't expanded by right floats outside the select.
17 it->SetFlags(aFlags | NS_BLOCK_FLOAT_MGR);
19 return it;
22 NS_IMPL_FRAMEARENA_HELPERS(nsSelectsAreaFrame)
24 //---------------------------------------------------------
25 /**
26 * This wrapper class lets us redirect mouse hits from the child frame of
27 * an option element to the element's own frame.
28 * REVIEW: This is what nsSelectsAreaFrame::GetFrameForPoint used to do
30 class nsDisplayOptionEventGrabber : public nsDisplayWrapList {
31 public:
32 nsDisplayOptionEventGrabber(nsDisplayListBuilder* aBuilder,
33 nsIFrame* aFrame, nsDisplayItem* aItem)
34 : nsDisplayWrapList(aBuilder, aFrame, aItem) {}
35 nsDisplayOptionEventGrabber(nsDisplayListBuilder* aBuilder,
36 nsIFrame* aFrame, nsDisplayList* aList)
37 : nsDisplayWrapList(aBuilder, aFrame, aList) {}
38 virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
39 HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) MOZ_OVERRIDE;
40 virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE {
41 return false;
43 NS_DISPLAY_DECL_NAME("OptionEventGrabber", TYPE_OPTION_EVENT_GRABBER)
46 void nsDisplayOptionEventGrabber::HitTest(nsDisplayListBuilder* aBuilder,
47 const nsRect& aRect, HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
49 nsTArray<nsIFrame*> outFrames;
50 mList.HitTest(aBuilder, aRect, aState, &outFrames);
52 for (uint32_t i = 0; i < outFrames.Length(); i++) {
53 nsIFrame* selectedFrame = outFrames.ElementAt(i);
54 while (selectedFrame &&
55 !(selectedFrame->GetContent() &&
56 selectedFrame->GetContent()->IsHTML(nsGkAtoms::option))) {
57 selectedFrame = selectedFrame->GetParent();
59 if (selectedFrame) {
60 aOutFrames->AppendElement(selectedFrame);
61 } else {
62 // keep the original result, which could be this frame
63 aOutFrames->AppendElement(outFrames.ElementAt(i));
68 class nsOptionEventGrabberWrapper : public nsDisplayWrapper
70 public:
71 nsOptionEventGrabberWrapper() {}
72 virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder,
73 nsIFrame* aFrame, nsDisplayList* aList) {
74 return new (aBuilder) nsDisplayOptionEventGrabber(aBuilder, aFrame, aList);
76 virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder,
77 nsDisplayItem* aItem) {
78 return new (aBuilder) nsDisplayOptionEventGrabber(aBuilder, aItem->Frame(), aItem);
82 static nsListControlFrame* GetEnclosingListFrame(nsIFrame* aSelectsAreaFrame)
84 nsIFrame* frame = aSelectsAreaFrame->GetParent();
85 while (frame) {
86 if (frame->GetType() == nsGkAtoms::listControlFrame)
87 return static_cast<nsListControlFrame*>(frame);
88 frame = frame->GetParent();
90 return nullptr;
93 class nsDisplayListFocus : public nsDisplayItem {
94 public:
95 nsDisplayListFocus(nsDisplayListBuilder* aBuilder,
96 nsSelectsAreaFrame* aFrame) :
97 nsDisplayItem(aBuilder, aFrame) {
98 MOZ_COUNT_CTOR(nsDisplayListFocus);
100 #ifdef NS_BUILD_REFCNT_LOGGING
101 virtual ~nsDisplayListFocus() {
102 MOZ_COUNT_DTOR(nsDisplayListFocus);
104 #endif
106 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE {
107 *aSnap = false;
108 // override bounds because the list item focus ring may extend outside
109 // the nsSelectsAreaFrame
110 nsListControlFrame* listFrame = GetEnclosingListFrame(Frame());
111 return listFrame->GetVisualOverflowRectRelativeToSelf() +
112 listFrame->GetOffsetToCrossDoc(ReferenceFrame());
114 virtual void Paint(nsDisplayListBuilder* aBuilder,
115 nsRenderingContext* aCtx) MOZ_OVERRIDE {
116 nsListControlFrame* listFrame = GetEnclosingListFrame(Frame());
117 // listFrame must be non-null or we wouldn't get called.
118 listFrame->PaintFocus(*aCtx, aBuilder->ToReferenceFrame(listFrame));
120 NS_DISPLAY_DECL_NAME("ListFocus", TYPE_LIST_FOCUS)
123 void
124 nsSelectsAreaFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
125 const nsRect& aDirtyRect,
126 const nsDisplayListSet& aLists)
128 if (!aBuilder->IsForEventDelivery()) {
129 BuildDisplayListInternal(aBuilder, aDirtyRect, aLists);
130 return;
133 nsDisplayListCollection set;
134 BuildDisplayListInternal(aBuilder, aDirtyRect, set);
136 nsOptionEventGrabberWrapper wrapper;
137 wrapper.WrapLists(aBuilder, this, set, aLists);
140 void
141 nsSelectsAreaFrame::BuildDisplayListInternal(nsDisplayListBuilder* aBuilder,
142 const nsRect& aDirtyRect,
143 const nsDisplayListSet& aLists)
145 nsBlockFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
147 nsListControlFrame* listFrame = GetEnclosingListFrame(this);
148 if (listFrame && listFrame->IsFocused()) {
149 // we can't just associate the display item with the list frame,
150 // because then the list's scrollframe won't clip it (the scrollframe
151 // only clips contained descendants).
152 aLists.Outlines()->AppendNewToTop(new (aBuilder)
153 nsDisplayListFocus(aBuilder, this));
157 void
158 nsSelectsAreaFrame::Reflow(nsPresContext* aPresContext,
159 nsHTMLReflowMetrics& aDesiredSize,
160 const nsHTMLReflowState& aReflowState,
161 nsReflowStatus& aStatus)
163 nsListControlFrame* list = GetEnclosingListFrame(this);
164 NS_ASSERTION(list,
165 "Must have an nsListControlFrame! Frame constructor is "
166 "broken");
168 bool isInDropdownMode = list->IsInDropDownMode();
170 // See similar logic in nsListControlFrame::Reflow and
171 // nsListControlFrame::ReflowAsDropdown. We need to match it here.
172 nscoord oldHeight;
173 if (isInDropdownMode) {
174 // Store the height now in case it changes during
175 // nsBlockFrame::Reflow for some odd reason.
176 if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
177 oldHeight = GetSize().height;
178 } else {
179 oldHeight = NS_UNCONSTRAINEDSIZE;
183 nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
185 // Check whether we need to suppress scrollbar updates. We want to do that if
186 // we're in a possible first pass and our height of a row has changed.
187 if (list->MightNeedSecondPass()) {
188 nscoord newHeightOfARow = list->CalcHeightOfARow();
189 // We'll need a second pass if our height of a row changed. For
190 // comboboxes, we'll also need it if our height changed. If we're going
191 // to do a second pass, suppress scrollbar updates for this pass.
192 if (newHeightOfARow != mHeightOfARow ||
193 (isInDropdownMode && (oldHeight != aDesiredSize.Height() ||
194 oldHeight != GetSize().height))) {
195 mHeightOfARow = newHeightOfARow;
196 list->SetSuppressScrollbarUpdate(true);