1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Original Author: David W. Hyatt (hyatt@netscape.com)
24 * Pierre Phaneuf <pp@ludusdesign.com>
25 * Dean Tessman <dean_tessman@hotmail.com>
26 * Mats Palmgren <mats.palmgren@bredband.net>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
42 #include "nsPopupSetFrame.h"
43 #include "nsGkAtoms.h"
45 #include "nsIContent.h"
46 #include "nsPresContext.h"
47 #include "nsStyleContext.h"
48 #include "nsBoxLayoutState.h"
49 #include "nsIScrollableFrame.h"
50 #include "nsIRootBox.h"
52 nsPopupFrameList::nsPopupFrameList(nsIContent
* aPopupContent
, nsPopupFrameList
* aNext
)
55 mPopupContent(aPopupContent
)
59 void nsPopupFrameList::Destroy(nsIFrame
* aDestructRoot
)
62 nsIFrame
* prevSib
= mPopupFrame
->GetPrevSibling();
64 prevSib
->SetNextSibling(mPopupFrame
->GetNextSibling());
65 mPopupFrame
->SetNextSibling(nsnull
);
66 mPopupFrame
->DestroyFrom((aDestructRoot
) ? aDestructRoot
: mPopupFrame
);
71 // NS_NewPopupSetFrame
73 // Wrapper for creating a new menu popup container
76 NS_NewPopupSetFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
78 return new (aPresShell
) nsPopupSetFrame (aPresShell
, aContext
);
81 NS_IMPL_FRAMEARENA_HELPERS(nsPopupSetFrame
)
84 nsPopupSetFrame::Init(nsIContent
* aContent
,
86 nsIFrame
* aPrevInFlow
)
88 nsresult rv
= nsBoxFrame::Init(aContent
, aParent
, aPrevInFlow
);
90 // Normally the root box is our grandparent, but in case of wrapping
91 // it can be our great-grandparent.
92 nsIRootBox
*rootBox
= nsIRootBox::GetRootBox(PresContext()->GetPresShell());
94 rootBox
->SetPopupSetFrame(this);
101 nsPopupSetFrame::GetType() const
103 return nsGkAtoms::popupSetFrame
;
107 nsPopupSetFrame::AppendFrames(nsIAtom
* aListName
,
108 nsFrameList
& aFrameList
)
110 if (aListName
== nsGkAtoms::popupList
) {
111 return AddPopupFrameList(aFrameList
);
113 return nsBoxFrame::AppendFrames(aListName
, aFrameList
);
117 nsPopupSetFrame::RemoveFrame(nsIAtom
* aListName
,
120 if (aListName
== nsGkAtoms::popupList
) {
121 return RemovePopupFrame(aOldFrame
);
123 return nsBoxFrame::RemoveFrame(aListName
, aOldFrame
);
127 nsPopupSetFrame::InsertFrames(nsIAtom
* aListName
,
128 nsIFrame
* aPrevFrame
,
129 nsFrameList
& aFrameList
)
131 if (aListName
== nsGkAtoms::popupList
) {
132 return AddPopupFrameList(aFrameList
);
134 return nsBoxFrame::InsertFrames(aListName
, aPrevFrame
, aFrameList
);
138 nsPopupSetFrame::SetInitialChildList(nsIAtom
* aListName
,
139 nsFrameList
& aChildList
)
141 if (aListName
== nsGkAtoms::popupList
) {
142 return AddPopupFrameList(aChildList
);
144 return nsBoxFrame::SetInitialChildList(aListName
, aChildList
);
148 nsPopupSetFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
150 // remove each popup from the list as we go.
152 nsPopupFrameList
* temp
= mPopupList
;
153 mPopupList
= mPopupList
->mNextPopup
;
154 temp
->Destroy(aDestructRoot
); // destroys frame
157 // Normally the root box is our grandparent, but in case of wrapping
158 // it can be our great-grandparent.
159 nsIRootBox
*rootBox
= nsIRootBox::GetRootBox(PresContext()->GetPresShell());
161 rootBox
->SetPopupSetFrame(nsnull
);
164 nsBoxFrame::DestroyFrom(aDestructRoot
);
168 nsPopupSetFrame::DoLayout(nsBoxLayoutState
& aState
)
171 nsresult rv
= nsBoxFrame::DoLayout(aState
);
173 // lay out all of our currently open popups.
174 nsPopupFrameList
* currEntry
= mPopupList
;
176 nsMenuPopupFrame
* popupChild
= currEntry
->mPopupFrame
;
177 if (popupChild
&& popupChild
->IsOpen()) {
178 // then get its preferred size
179 nsSize prefSize
= popupChild
->GetPrefSize(aState
);
180 nsSize minSize
= popupChild
->GetMinSize(aState
);
181 nsSize maxSize
= popupChild
->GetMaxSize(aState
);
183 prefSize
= BoundsCheck(minSize
, prefSize
, maxSize
);
185 popupChild
->SetPreferredBounds(aState
, nsRect(0,0,prefSize
.width
, prefSize
.height
));
186 popupChild
->SetPopupPosition(nsnull
);
188 // is the new size too small? Make sure we handle scrollbars correctly
189 nsIBox
* child
= popupChild
->GetChildBox();
191 nsRect
bounds(popupChild
->GetRect());
193 nsIScrollableFrame
*scrollframe
= do_QueryFrame(child
);
195 scrollframe
->GetScrollbarStyles().mVertical
== NS_STYLE_OVERFLOW_AUTO
) {
196 // if our pref height
197 if (bounds
.height
< prefSize
.height
) {
199 popupChild
->Layout(aState
);
201 nsMargin scrollbars
= scrollframe
->GetActualScrollbarSizes();
202 if (bounds
.width
< prefSize
.width
+ scrollbars
.left
+ scrollbars
.right
)
204 bounds
.width
+= scrollbars
.left
+ scrollbars
.right
;
205 popupChild
->SetBounds(aState
, bounds
);
211 popupChild
->Layout(aState
);
212 // if the width or height changed, readjust the popup position. This is a
213 // special case for tooltips where the preferred height doesn't include the
214 // real height for its inline element, but does once it is laid out.
215 // This is bug 228673 which doesn't have a simple fix.
216 if (popupChild
->GetRect().width
> bounds
.width
||
217 popupChild
->GetRect().height
> bounds
.height
) {
218 // the size after layout was larger than the preferred size,
219 // so set the preferred size accordingly
220 popupChild
->SetPreferredSize(popupChild
->GetSize());
221 popupChild
->SetPopupPosition(nsnull
);
223 popupChild
->AdjustView();
226 currEntry
= currEntry
->mNextPopup
;
233 nsPopupSetFrame::RemovePopupFrame(nsIFrame
* aPopup
)
235 // This was called by the Destroy() method of the popup, so all we have to do is
236 // get the popup out of our list, so we don't reflow it later.
238 PRBool found
= PR_FALSE
;
240 nsPopupFrameList
* currEntry
= mPopupList
;
241 nsPopupFrameList
* temp
= nsnull
;
243 if (currEntry
->mPopupFrame
== aPopup
) {
244 // Remove this entry.
246 temp
->mNextPopup
= currEntry
->mNextPopup
;
248 mPopupList
= currEntry
->mNextPopup
;
250 NS_ASSERTION((aPopup
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
) &&
251 aPopup
->GetType() == nsGkAtoms::menuPopupFrame
,
252 "found wrong type of frame in popupset's ::popupList");
254 currEntry
->mNextPopup
= nsnull
;
255 currEntry
->Destroy(); // destroys the frame
260 // Break out of the loop.
265 currEntry
= currEntry
->mNextPopup
;
268 NS_ASSERTION(found
, "frame to remove is not in our ::popupList");
273 nsPopupSetFrame::AddPopupFrameList(nsFrameList
& aPopupFrameList
)
275 while (!aPopupFrameList
.IsEmpty()) {
276 nsIFrame
* f
= aPopupFrameList
.FirstChild();
277 // Clears out prev/next sibling points appropriately. Every frame
278 // in our popup list has null next and prev pointers, they're logically
279 // each in their own list.
280 aPopupFrameList
.RemoveFrame(f
);
281 nsresult rv
= AddPopupFrame(f
);
282 NS_ENSURE_SUCCESS(rv
, rv
);
288 nsPopupSetFrame::AddPopupFrame(nsIFrame
* aPopup
)
290 NS_ASSERTION((aPopup
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
) &&
291 aPopup
->GetType() == nsGkAtoms::menuPopupFrame
,
292 "adding wrong type of frame in popupset's ::popupList");
294 // The entry should already exist, but might not (if someone decided to make their
295 // popup visible straightaway, e.g., the autocomplete widget).
296 // First look for an entry by content.
297 nsIContent
* content
= aPopup
->GetContent();
298 nsPopupFrameList
* entry
= mPopupList
;
299 while (entry
&& entry
->mPopupContent
!= content
)
300 entry
= entry
->mNextPopup
;
302 entry
= new nsPopupFrameList(content
, mPopupList
);
304 return NS_ERROR_OUT_OF_MEMORY
;
308 NS_ASSERTION(!entry
->mPopupFrame
, "Leaking a popup frame");
311 // Set the frame connection.
312 entry
->mPopupFrame
= static_cast<nsMenuPopupFrame
*>(aPopup
);
319 nsPopupSetFrame::List(FILE* out
, PRInt32 aIndent
) const
321 IndentBy(out
, aIndent
);
323 #ifdef DEBUG_waterson
324 fprintf(out
, " [parent=%p]", static_cast<void*>(mParent
));
327 fprintf(out
, " [view=%p]", static_cast<void*>(GetView()));
329 if (GetNextSibling()) {
330 fprintf(out
, " next=%p", static_cast<void*>(GetNextSibling()));
332 if (nsnull
!= GetPrevContinuation()) {
333 fprintf(out
, " prev-continuation=%p", static_cast<void*>(GetPrevContinuation()));
335 if (nsnull
!= GetNextContinuation()) {
336 fprintf(out
, " next-continuation=%p", static_cast<void*>(GetNextContinuation()));
338 fprintf(out
, " {%d,%d,%d,%d}", mRect
.x
, mRect
.y
, mRect
.width
, mRect
.height
);
340 fprintf(out
, " [state=%08x]", mState
);
342 fprintf(out
, " [content=%p]", static_cast<void*>(mContent
));
343 nsPopupSetFrame
* f
= const_cast<nsPopupSetFrame
*>(this);
344 if (f
->HasOverflowRect()) {
345 nsRect overflowArea
= f
->GetOverflowRect();
346 fprintf(out
, " [overflow=%d,%d,%d,%d]", overflowArea
.x
, overflowArea
.y
,
347 overflowArea
.width
, overflowArea
.height
);
349 fprintf(out
, " [sc=%p]", static_cast<void*>(mStyleContext
));
350 nsIAtom
* pseudoTag
= mStyleContext
->GetPseudo();
352 nsAutoString atomString
;
353 pseudoTag
->ToString(atomString
);
354 fprintf(out
, " pst=%s",
355 NS_LossyConvertUTF16toASCII(atomString
).get());
358 // Output the children
359 nsIAtom
* listName
= nsnull
;
360 PRInt32 listIndex
= 0;
361 PRBool outputOneList
= PR_FALSE
;
363 nsIFrame
* kid
= GetFirstChild(listName
);
366 IndentBy(out
, aIndent
);
368 outputOneList
= PR_TRUE
;
370 if (nsnull
!= listName
) {
371 listName
->ToString(tmp
);
372 fputs(NS_LossyConvertUTF16toASCII(tmp
).get(), out
);
375 while (nsnull
!= kid
) {
376 // Verify the child frame's parent frame pointer is correct
377 NS_ASSERTION(kid
->GetParent() == (nsIFrame
*)this, "bad parent frame pointer");
379 // Have the child frame list
380 kid
->List(out
, aIndent
+ 1);
381 kid
= kid
->GetNextSibling();
383 IndentBy(out
, aIndent
);
386 listName
= GetAdditionalChildListName(listIndex
++);
387 } while(nsnull
!= listName
);
389 // XXXmats the above is copy-pasted from nsContainerFrame::List which is lame,
390 // clean this up after bug 399111 is implemented.
395 IndentBy(out
, aIndent
);
397 nsGkAtoms::popupList
->ToString(tmp
);
398 fputs(NS_LossyConvertUTF16toASCII(tmp
).get(), out
);
403 for (nsPopupFrameList
* l
= mPopupList
; l
; l
= l
->mNextPopup
) {
404 l
->mPopupFrame
->List(out
, aIndent
);
407 IndentBy(out
, aIndent
);
410 IndentBy(out
, aIndent
);
412 outputOneList
= PR_TRUE
;
415 if (!outputOneList
) {