Backout a74bd5095902, Bug 959405 - Please update the Buri Moz-central, 1.3, 1.2 with...
[gecko.git] / layout / forms / nsSelectsAreaFrame.cpp
blob0ce0ba628ab5ee6d07e8acfa341375c233beba76
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 nsIFrame*
11 NS_NewSelectsAreaFrame(nsIPresShell* aShell, nsStyleContext* aContext, uint32_t 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);
40 NS_DISPLAY_DECL_NAME("OptionEventGrabber", TYPE_OPTION_EVENT_GRABBER)
43 void nsDisplayOptionEventGrabber::HitTest(nsDisplayListBuilder* aBuilder,
44 const nsRect& aRect, HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
46 nsTArray<nsIFrame*> outFrames;
47 mList.HitTest(aBuilder, aRect, aState, &outFrames);
49 for (uint32_t i = 0; i < outFrames.Length(); i++) {
50 nsIFrame* selectedFrame = outFrames.ElementAt(i);
51 while (selectedFrame &&
52 !(selectedFrame->GetContent() &&
53 selectedFrame->GetContent()->IsHTML(nsGkAtoms::option))) {
54 selectedFrame = selectedFrame->GetParent();
56 if (selectedFrame) {
57 aOutFrames->AppendElement(selectedFrame);
58 } else {
59 // keep the original result, which could be this frame
60 aOutFrames->AppendElement(outFrames.ElementAt(i));
65 class nsOptionEventGrabberWrapper : public nsDisplayWrapper
67 public:
68 nsOptionEventGrabberWrapper() {}
69 virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder,
70 nsIFrame* aFrame, nsDisplayList* aList) {
71 return new (aBuilder) nsDisplayOptionEventGrabber(aBuilder, aFrame, aList);
73 virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder,
74 nsDisplayItem* aItem) {
75 return new (aBuilder) nsDisplayOptionEventGrabber(aBuilder, aItem->Frame(), aItem);
79 static nsListControlFrame* GetEnclosingListFrame(nsIFrame* aSelectsAreaFrame)
81 nsIFrame* frame = aSelectsAreaFrame->GetParent();
82 while (frame) {
83 if (frame->GetType() == nsGkAtoms::listControlFrame)
84 return static_cast<nsListControlFrame*>(frame);
85 frame = frame->GetParent();
87 return nullptr;
90 class nsDisplayListFocus : public nsDisplayItem {
91 public:
92 nsDisplayListFocus(nsDisplayListBuilder* aBuilder,
93 nsSelectsAreaFrame* aFrame) :
94 nsDisplayItem(aBuilder, aFrame) {
95 MOZ_COUNT_CTOR(nsDisplayListFocus);
97 #ifdef NS_BUILD_REFCNT_LOGGING
98 virtual ~nsDisplayListFocus() {
99 MOZ_COUNT_DTOR(nsDisplayListFocus);
101 #endif
103 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
104 *aSnap = false;
105 // override bounds because the list item focus ring may extend outside
106 // the nsSelectsAreaFrame
107 nsListControlFrame* listFrame = GetEnclosingListFrame(Frame());
108 return listFrame->GetVisualOverflowRectRelativeToSelf() +
109 listFrame->GetOffsetToCrossDoc(ReferenceFrame());
111 virtual void Paint(nsDisplayListBuilder* aBuilder,
112 nsRenderingContext* aCtx) {
113 nsListControlFrame* listFrame = GetEnclosingListFrame(Frame());
114 // listFrame must be non-null or we wouldn't get called.
115 listFrame->PaintFocus(*aCtx, aBuilder->ToReferenceFrame(listFrame));
117 NS_DISPLAY_DECL_NAME("ListFocus", TYPE_LIST_FOCUS)
120 void
121 nsSelectsAreaFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
122 const nsRect& aDirtyRect,
123 const nsDisplayListSet& aLists)
125 if (!aBuilder->IsForEventDelivery()) {
126 BuildDisplayListInternal(aBuilder, aDirtyRect, aLists);
127 return;
130 nsDisplayListCollection set;
131 BuildDisplayListInternal(aBuilder, aDirtyRect, set);
133 nsOptionEventGrabberWrapper wrapper;
134 wrapper.WrapLists(aBuilder, this, set, aLists);
137 void
138 nsSelectsAreaFrame::BuildDisplayListInternal(nsDisplayListBuilder* aBuilder,
139 const nsRect& aDirtyRect,
140 const nsDisplayListSet& aLists)
142 nsBlockFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
144 nsListControlFrame* listFrame = GetEnclosingListFrame(this);
145 if (listFrame && listFrame->IsFocused()) {
146 // we can't just associate the display item with the list frame,
147 // because then the list's scrollframe won't clip it (the scrollframe
148 // only clips contained descendants).
149 aLists.Outlines()->AppendNewToTop(new (aBuilder)
150 nsDisplayListFocus(aBuilder, this));
154 NS_IMETHODIMP
155 nsSelectsAreaFrame::Reflow(nsPresContext* aPresContext,
156 nsHTMLReflowMetrics& aDesiredSize,
157 const nsHTMLReflowState& aReflowState,
158 nsReflowStatus& aStatus)
160 nsListControlFrame* list = GetEnclosingListFrame(this);
161 NS_ASSERTION(list,
162 "Must have an nsListControlFrame! Frame constructor is "
163 "broken");
165 bool isInDropdownMode = list->IsInDropDownMode();
167 // See similar logic in nsListControlFrame::Reflow and
168 // nsListControlFrame::ReflowAsDropdown. We need to match it here.
169 nscoord oldHeight;
170 if (isInDropdownMode) {
171 // Store the height now in case it changes during
172 // nsBlockFrame::Reflow for some odd reason.
173 if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
174 oldHeight = GetSize().height;
175 } else {
176 oldHeight = NS_UNCONSTRAINEDSIZE;
180 nsresult rv = nsBlockFrame::Reflow(aPresContext, aDesiredSize,
181 aReflowState, aStatus);
182 NS_ENSURE_SUCCESS(rv, rv);
184 // Check whether we need to suppress scrollbar updates. We want to do that if
185 // we're in a possible first pass and our height of a row has changed.
186 if (list->MightNeedSecondPass()) {
187 nscoord newHeightOfARow = list->CalcHeightOfARow();
188 // We'll need a second pass if our height of a row changed. For
189 // comboboxes, we'll also need it if our height changed. If we're going
190 // to do a second pass, suppress scrollbar updates for this pass.
191 if (newHeightOfARow != mHeightOfARow ||
192 (isInDropdownMode && (oldHeight != aDesiredSize.Height() ||
193 oldHeight != GetSize().height))) {
194 mHeightOfARow = newHeightOfARow;
195 list->SetSuppressScrollbarUpdate(true);
199 return rv;