Bug 767636 - Expose plugin fallback type to extensions. r=josh
[gecko.git] / layout / forms / nsSelectsAreaFrame.cpp
blobd5df172f048a5597212636819a88d415f1819352
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 "nsCOMPtr.h"
7 #include "nsIDOMHTMLOptionElement.h"
8 #include "nsIContent.h"
9 #include "nsListControlFrame.h"
10 #include "nsDisplayList.h"
12 nsIFrame*
13 NS_NewSelectsAreaFrame(nsIPresShell* aShell, nsStyleContext* aContext, PRUint32 aFlags)
15 nsSelectsAreaFrame* it = new (aShell) nsSelectsAreaFrame(aContext);
17 if (it) {
18 // We need NS_BLOCK_FLOAT_MGR to ensure that the options inside the select
19 // aren't expanded by right floats outside the select.
20 it->SetFlags(aFlags | NS_BLOCK_FLOAT_MGR);
23 return it;
26 NS_IMPL_FRAMEARENA_HELPERS(nsSelectsAreaFrame)
28 //---------------------------------------------------------
29 /**
30 * This wrapper class lets us redirect mouse hits from the child frame of
31 * an option element to the element's own frame.
32 * REVIEW: This is what nsSelectsAreaFrame::GetFrameForPoint used to do
34 class nsDisplayOptionEventGrabber : public nsDisplayWrapList {
35 public:
36 nsDisplayOptionEventGrabber(nsDisplayListBuilder* aBuilder,
37 nsIFrame* aFrame, nsDisplayItem* aItem)
38 : nsDisplayWrapList(aBuilder, aFrame, aItem) {}
39 nsDisplayOptionEventGrabber(nsDisplayListBuilder* aBuilder,
40 nsIFrame* aFrame, nsDisplayList* aList)
41 : nsDisplayWrapList(aBuilder, aFrame, aList) {}
42 virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
43 HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames);
44 NS_DISPLAY_DECL_NAME("OptionEventGrabber", TYPE_OPTION_EVENT_GRABBER)
46 virtual nsDisplayWrapList* WrapWithClone(nsDisplayListBuilder* aBuilder,
47 nsDisplayItem* aItem);
50 void nsDisplayOptionEventGrabber::HitTest(nsDisplayListBuilder* aBuilder,
51 const nsRect& aRect, HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
53 nsTArray<nsIFrame*> outFrames;
54 mList.HitTest(aBuilder, aRect, aState, &outFrames);
56 for (PRUint32 i = 0; i < outFrames.Length(); i++) {
57 nsIFrame* selectedFrame = outFrames.ElementAt(i);
58 while (selectedFrame &&
59 !(selectedFrame->GetContent() &&
60 selectedFrame->GetContent()->IsHTML(nsGkAtoms::option))) {
61 selectedFrame = selectedFrame->GetParent();
63 if (selectedFrame) {
64 aOutFrames->AppendElement(selectedFrame);
65 } else {
66 // keep the original result, which could be this frame
67 aOutFrames->AppendElement(outFrames.ElementAt(i));
73 nsDisplayWrapList* nsDisplayOptionEventGrabber::WrapWithClone(
74 nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) {
75 return new (aBuilder)
76 nsDisplayOptionEventGrabber(aBuilder, aItem->GetUnderlyingFrame(), aItem);
79 class nsOptionEventGrabberWrapper : public nsDisplayWrapper
81 public:
82 nsOptionEventGrabberWrapper() {}
83 virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder,
84 nsIFrame* aFrame, nsDisplayList* aList) {
85 // We can't specify the underlying frame here. We need this list to be
86 // exploded if sorted.
87 return new (aBuilder) nsDisplayOptionEventGrabber(aBuilder, nullptr, aList);
89 virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder,
90 nsDisplayItem* aItem) {
91 return new (aBuilder) nsDisplayOptionEventGrabber(aBuilder, aItem->GetUnderlyingFrame(), aItem);
95 static nsListControlFrame* GetEnclosingListFrame(nsIFrame* aSelectsAreaFrame)
97 nsIFrame* frame = aSelectsAreaFrame->GetParent();
98 while (frame) {
99 if (frame->GetType() == nsGkAtoms::listControlFrame)
100 return static_cast<nsListControlFrame*>(frame);
101 frame = frame->GetParent();
103 return nullptr;
106 class nsDisplayListFocus : public nsDisplayItem {
107 public:
108 nsDisplayListFocus(nsDisplayListBuilder* aBuilder,
109 nsSelectsAreaFrame* aFrame) :
110 nsDisplayItem(aBuilder, aFrame) {
111 MOZ_COUNT_CTOR(nsDisplayListFocus);
113 #ifdef NS_BUILD_REFCNT_LOGGING
114 virtual ~nsDisplayListFocus() {
115 MOZ_COUNT_DTOR(nsDisplayListFocus);
117 #endif
119 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
120 *aSnap = false;
121 // override bounds because the list item focus ring may extend outside
122 // the nsSelectsAreaFrame
123 nsListControlFrame* listFrame = GetEnclosingListFrame(GetUnderlyingFrame());
124 return listFrame->GetVisualOverflowRectRelativeToSelf() +
125 aBuilder->ToReferenceFrame(listFrame);
127 virtual void Paint(nsDisplayListBuilder* aBuilder,
128 nsRenderingContext* aCtx) {
129 nsListControlFrame* listFrame = GetEnclosingListFrame(GetUnderlyingFrame());
130 // listFrame must be non-null or we wouldn't get called.
131 listFrame->PaintFocus(*aCtx, aBuilder->ToReferenceFrame(listFrame));
133 NS_DISPLAY_DECL_NAME("ListFocus", TYPE_LIST_FOCUS)
136 NS_IMETHODIMP
137 nsSelectsAreaFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
138 const nsRect& aDirtyRect,
139 const nsDisplayListSet& aLists)
141 if (!aBuilder->IsForEventDelivery())
142 return BuildDisplayListInternal(aBuilder, aDirtyRect, aLists);
144 nsDisplayListCollection set;
145 nsresult rv = BuildDisplayListInternal(aBuilder, aDirtyRect, set);
146 NS_ENSURE_SUCCESS(rv, rv);
148 nsOptionEventGrabberWrapper wrapper;
149 return wrapper.WrapLists(aBuilder, this, set, aLists);
152 nsresult
153 nsSelectsAreaFrame::BuildDisplayListInternal(nsDisplayListBuilder* aBuilder,
154 const nsRect& aDirtyRect,
155 const nsDisplayListSet& aLists)
157 nsresult rv = nsBlockFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
158 NS_ENSURE_SUCCESS(rv, rv);
160 nsListControlFrame* listFrame = GetEnclosingListFrame(this);
161 if (listFrame && listFrame->IsFocused()) {
162 // we can't just associate the display item with the list frame,
163 // because then the list's scrollframe won't clip it (the scrollframe
164 // only clips contained descendants).
165 return aLists.Outlines()->AppendNewToTop(new (aBuilder)
166 nsDisplayListFocus(aBuilder, this));
169 return NS_OK;
172 NS_IMETHODIMP
173 nsSelectsAreaFrame::Reflow(nsPresContext* aPresContext,
174 nsHTMLReflowMetrics& aDesiredSize,
175 const nsHTMLReflowState& aReflowState,
176 nsReflowStatus& aStatus)
178 nsListControlFrame* list = GetEnclosingListFrame(this);
179 NS_ASSERTION(list,
180 "Must have an nsListControlFrame! Frame constructor is "
181 "broken");
183 bool isInDropdownMode = list->IsInDropDownMode();
185 // See similar logic in nsListControlFrame::Reflow and
186 // nsListControlFrame::ReflowAsDropdown. We need to match it here.
187 nscoord oldHeight;
188 if (isInDropdownMode) {
189 // Store the height now in case it changes during
190 // nsBlockFrame::Reflow for some odd reason.
191 if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
192 oldHeight = GetSize().height;
193 } else {
194 oldHeight = NS_UNCONSTRAINEDSIZE;
198 nsresult rv = nsBlockFrame::Reflow(aPresContext, aDesiredSize,
199 aReflowState, aStatus);
200 NS_ENSURE_SUCCESS(rv, rv);
202 // Check whether we need to suppress scrolbar updates. We want to do that if
203 // we're in a possible first pass and our height of a row has changed.
204 if (list->MightNeedSecondPass()) {
205 nscoord newHeightOfARow = list->CalcHeightOfARow();
206 // We'll need a second pass if our height of a row changed. For
207 // comboboxes, we'll also need it if our height changed. If we're going
208 // to do a second pass, suppress scrollbar updates for this pass.
209 if (newHeightOfARow != mHeightOfARow ||
210 (isInDropdownMode && (oldHeight != aDesiredSize.height ||
211 oldHeight != GetSize().height))) {
212 mHeightOfARow = newHeightOfARow;
213 list->SetSuppressScrollbarUpdate(true);
217 return rv;