Bug 508473 part III: Pass destruction root to frame destruction methods r=bz sr=roc
[gecko.git] / layout / xul / base / src / nsPopupSetFrame.cpp
blobdf763798b64adbb095e218e7522165ede5f279ff
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
13 * License.
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.
22 * Contributor(s):
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"
44 #include "nsCOMPtr.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)
53 :mNextPopup(aNext),
54 mPopupFrame(nsnull),
55 mPopupContent(aPopupContent)
59 void nsPopupFrameList::Destroy(nsIFrame* aDestructRoot)
61 if (mPopupFrame) {
62 nsIFrame* prevSib = mPopupFrame->GetPrevSibling();
63 if (prevSib)
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
75 nsIFrame*
76 NS_NewPopupSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
78 return new (aPresShell) nsPopupSetFrame (aPresShell, aContext);
81 NS_IMPL_FRAMEARENA_HELPERS(nsPopupSetFrame)
83 NS_IMETHODIMP
84 nsPopupSetFrame::Init(nsIContent* aContent,
85 nsIFrame* aParent,
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());
93 if (rootBox) {
94 rootBox->SetPopupSetFrame(this);
97 return rv;
100 nsIAtom*
101 nsPopupSetFrame::GetType() const
103 return nsGkAtoms::popupSetFrame;
106 NS_IMETHODIMP
107 nsPopupSetFrame::AppendFrames(nsIAtom* aListName,
108 nsFrameList& aFrameList)
110 if (aListName == nsGkAtoms::popupList) {
111 return AddPopupFrameList(aFrameList);
113 return nsBoxFrame::AppendFrames(aListName, aFrameList);
116 NS_IMETHODIMP
117 nsPopupSetFrame::RemoveFrame(nsIAtom* aListName,
118 nsIFrame* aOldFrame)
120 if (aListName == nsGkAtoms::popupList) {
121 return RemovePopupFrame(aOldFrame);
123 return nsBoxFrame::RemoveFrame(aListName, aOldFrame);
126 NS_IMETHODIMP
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);
137 NS_IMETHODIMP
138 nsPopupSetFrame::SetInitialChildList(nsIAtom* aListName,
139 nsFrameList& aChildList)
141 if (aListName == nsGkAtoms::popupList) {
142 return AddPopupFrameList(aChildList);
144 return nsBoxFrame::SetInitialChildList(aListName, aChildList);
147 void
148 nsPopupSetFrame::DestroyFrom(nsIFrame* aDestructRoot)
150 // remove each popup from the list as we go.
151 while (mPopupList) {
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());
160 if (rootBox) {
161 rootBox->SetPopupSetFrame(nsnull);
164 nsBoxFrame::DestroyFrom(aDestructRoot);
167 NS_IMETHODIMP
168 nsPopupSetFrame::DoLayout(nsBoxLayoutState& aState)
170 // lay us out
171 nsresult rv = nsBoxFrame::DoLayout(aState);
173 // lay out all of our currently open popups.
174 nsPopupFrameList* currEntry = mPopupList;
175 while (currEntry) {
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);
194 if (scrollframe &&
195 scrollframe->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_AUTO) {
196 // if our pref height
197 if (bounds.height < prefSize.height) {
198 // layout the child
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);
210 // layout the child
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;
229 return rv;
232 nsresult
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.
237 #ifdef DEBUG
238 PRBool found = PR_FALSE;
239 #endif
240 nsPopupFrameList* currEntry = mPopupList;
241 nsPopupFrameList* temp = nsnull;
242 while (currEntry) {
243 if (currEntry->mPopupFrame == aPopup) {
244 // Remove this entry.
245 if (temp)
246 temp->mNextPopup = currEntry->mNextPopup;
247 else
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");
253 // Delete the entry.
254 currEntry->mNextPopup = nsnull;
255 currEntry->Destroy(); // destroys the frame
256 #ifdef DEBUG
257 found = PR_TRUE;
258 #endif
260 // Break out of the loop.
261 break;
264 temp = currEntry;
265 currEntry = currEntry->mNextPopup;
268 NS_ASSERTION(found, "frame to remove is not in our ::popupList");
269 return NS_OK;
272 nsresult
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);
284 return NS_OK;
287 nsresult
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;
301 if (!entry) {
302 entry = new nsPopupFrameList(content, mPopupList);
303 if (!entry)
304 return NS_ERROR_OUT_OF_MEMORY;
305 mPopupList = entry;
307 else {
308 NS_ASSERTION(!entry->mPopupFrame, "Leaking a popup frame");
311 // Set the frame connection.
312 entry->mPopupFrame = static_cast<nsMenuPopupFrame *>(aPopup);
314 return NS_OK;
317 #ifdef DEBUG
318 NS_IMETHODIMP
319 nsPopupSetFrame::List(FILE* out, PRInt32 aIndent) const
321 IndentBy(out, aIndent);
322 ListTag(out);
323 #ifdef DEBUG_waterson
324 fprintf(out, " [parent=%p]", static_cast<void*>(mParent));
325 #endif
326 if (HasView()) {
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);
339 if (0 != mState) {
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();
351 if (pseudoTag) {
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;
362 do {
363 nsIFrame* kid = GetFirstChild(listName);
364 if (nsnull != kid) {
365 if (outputOneList) {
366 IndentBy(out, aIndent);
368 outputOneList = PR_TRUE;
369 nsAutoString tmp;
370 if (nsnull != listName) {
371 listName->ToString(tmp);
372 fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
374 fputs("<\n", 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);
384 fputs(">\n", out);
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.
392 if (mPopupList) {
393 fputs("<\n", out);
394 ++aIndent;
395 IndentBy(out, aIndent);
396 nsAutoString tmp;
397 nsGkAtoms::popupList->ToString(tmp);
398 fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
399 fputs(" for ", out);
400 ListTag(out);
401 fputs(" <\n", out);
402 ++aIndent;
403 for (nsPopupFrameList* l = mPopupList; l; l = l->mNextPopup) {
404 l->mPopupFrame->List(out, aIndent);
406 --aIndent;
407 IndentBy(out, aIndent);
408 fputs(">\n", out);
409 --aIndent;
410 IndentBy(out, aIndent);
411 fputs(">\n", out);
412 outputOneList = PR_TRUE;
415 if (!outputOneList) {
416 fputs("<>\n", out);
419 return NS_OK;
421 #endif