diff --git a/mozilla/content/base/src/nsGkAtomList.h b/mozilla/content/base/src/nsGkAtomList.h index 0e1c431dabb..9be5617ff37 100755 --- a/mozilla/content/base/src/nsGkAtomList.h +++ b/mozilla/content/base/src/nsGkAtomList.h @@ -1301,6 +1301,7 @@ GK_ATOM(boxFrame, "BoxFrame") GK_ATOM(brFrame, "BRFrame") GK_ATOM(bulletFrame, "BulletFrame") GK_ATOM(columnSetFrame, "ColumnSetFrame") +GK_ATOM(comboboxControlFrame, "ComboboxControlFrame") GK_ATOM(directionalFrame, "DirectionalFrame") GK_ATOM(fieldSetFrame, "FieldSetFrame") GK_ATOM(frameSetFrame, "FrameSetFrame") diff --git a/mozilla/content/html/content/src/nsGenericHTMLElement.cpp b/mozilla/content/html/content/src/nsGenericHTMLElement.cpp index a08ce65c5fd..10f3f50daf2 100644 --- a/mozilla/content/html/content/src/nsGenericHTMLElement.cpp +++ b/mozilla/content/html/content/src/nsGenericHTMLElement.cpp @@ -1179,7 +1179,7 @@ nsGenericHTMLElement::GetClientHeight(PRInt32* aClientHeight) *aClientHeight = NSTwipsToIntPixels(r.height, t2p); } else if (frame && (frame->GetStyleDisplay()->mDisplay != NS_STYLE_DISPLAY_INLINE || - (frame->GetStateBits() & NS_FRAME_REPLACED_ELEMENT))) { + (frame->IsFrameOfType(nsIFrame::eReplaced)))) { // Special case code to make clientHeight work even when there isn't // a scroll view, see bug 180552 and bug 227567. @@ -1207,7 +1207,7 @@ nsGenericHTMLElement::GetClientWidth(PRInt32* aClientWidth) *aClientWidth = NSTwipsToIntPixels(r.width, t2p); } else if (frame && (frame->GetStyleDisplay()->mDisplay != NS_STYLE_DISPLAY_INLINE || - (frame->GetStateBits() & NS_FRAME_REPLACED_ELEMENT))) { + (frame->IsFrameOfType(nsIFrame::eReplaced)))) { // Special case code to make clientWidth work even when there isn't // a scroll view, see bug 180552 and bug 227567. diff --git a/mozilla/layout/base/nsBidiPresUtils.cpp b/mozilla/layout/base/nsBidiPresUtils.cpp index 3ffed390324..0b99733d9c2 100644 --- a/mozilla/layout/base/nsBidiPresUtils.cpp +++ b/mozilla/layout/base/nsBidiPresUtils.cpp @@ -463,10 +463,11 @@ nsBidiPresUtils::Resolve(nsPresContext* aPresContext, PRBool IsBidiLeaf(nsIFrame* aFrame) { nsIAtom* frameType = aFrame->GetType(); nsIFrame* kid = aFrame->GetFirstChild(nsnull); + // Need the IsBlockLevel() check because nsFirstLetterFrame is + // always of type eBidiInlineContainer, even if it's floating. return !kid - || aFrame->GetStyleDisplay()->IsBlockLevel() - || !(aFrame->IsFrameOfType(nsIFrame::eBidiInlineContainer) - || nsLayoutAtoms::blockFrame == frameType); + || !aFrame->IsFrameOfType(nsIFrame::eBidiInlineContainer) + || aFrame->GetStyleDisplay()->IsBlockLevel(); } nsresult diff --git a/mozilla/layout/base/nsCSSFrameConstructor.cpp b/mozilla/layout/base/nsCSSFrameConstructor.cpp index 9d0ec083152..b624a760fc6 100644 --- a/mozilla/layout/base/nsCSSFrameConstructor.cpp +++ b/mozilla/layout/base/nsCSSFrameConstructor.cpp @@ -5103,6 +5103,7 @@ nsCSSFrameConstructor::ConstructButtonFrame(nsFrameConstructorState& aState, return NS_OK; } + nsresult nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState, nsIContent* aContent, @@ -5487,10 +5488,6 @@ nsCSSFrameConstructor::ConstructTextFrame(nsFrameConstructorState& aState, if (NS_UNLIKELY(!newFrame)) return NS_ERROR_OUT_OF_MEMORY; - // Set the frame state bit for text frames to mark them as replaced. - // XXX kipp: temporary - newFrame->AddStateBits(NS_FRAME_REPLACED_ELEMENT); - nsresult rv = InitAndRestoreFrame(aState, aContent, aParentFrame, nsnull, newFrame); @@ -5532,7 +5529,6 @@ nsCSSFrameConstructor::ConstructHTMLFrame(nsFrameConstructorState& aState, PRBool frameHasBeenInitialized = PR_FALSE; nsIFrame* newFrame = nsnull; // the frame we construct - PRBool isReplaced = PR_FALSE; PRBool addToHashTable = PR_TRUE; PRBool isFloatContainer = PR_FALSE; PRBool addedToFrameList = PR_FALSE; @@ -5546,7 +5542,6 @@ nsCSSFrameConstructor::ConstructHTMLFrame(nsFrameConstructorState& aState, // Create a frame based on the tag if (nsHTMLAtoms::img == aTag) { // Make sure to keep IsSpecialContent in synch with this code - isReplaced = PR_TRUE; rv = CreateHTMLImageFrame(aContent, aStyleContext, NS_NewImageFrame, &newFrame); if (newFrame) { @@ -5562,7 +5557,6 @@ nsCSSFrameConstructor::ConstructHTMLFrame(nsFrameConstructorState& aState, newFrame = NS_NewBRFrame(mPresShell, aStyleContext); triedFrame = PR_TRUE; - isReplaced = PR_TRUE; // BR frames don't go in the content->frame hash table: typically // there are many BR content objects and this would increase the size // of the hash table, and it's doubtful we need the mapping anyway @@ -5584,13 +5578,11 @@ nsCSSFrameConstructor::ConstructHTMLFrame(nsFrameConstructorState& aState, aTag, aStyleContext, &newFrame, display, frameHasBeenInitialized, addedToFrameList, aFrameItems); - isReplaced = PR_TRUE; } else if (nsHTMLAtoms::textarea == aTag) { if (!aHasPseudoParent && !aState.mPseudoFrames.IsEmpty()) { ProcessPseudoFrames(aState, aFrameItems); } - isReplaced = PR_TRUE; newFrame = NS_NewTextControlFrame(mPresShell, aStyleContext); triedFrame = PR_TRUE; } @@ -5599,15 +5591,16 @@ nsCSSFrameConstructor::ConstructHTMLFrame(nsFrameConstructorState& aState, if (!aHasPseudoParent && !aState.mPseudoFrames.IsEmpty()) { ProcessPseudoFrames(aState, aFrameItems); } - isReplaced = PR_TRUE; rv = ConstructSelectFrame(aState, aContent, aParentFrame, aTag, aStyleContext, newFrame, display, frameHasBeenInitialized, aFrameItems); - NS_ASSERTION(nsPlaceholderFrame::GetRealFrameFor(aFrameItems.lastChild) == - newFrame, - "Frame didn't get added to aFrameItems?"); - addedToFrameList = PR_TRUE; + if (newFrame) { + NS_ASSERTION(nsPlaceholderFrame::GetRealFrameFor(aFrameItems.lastChild) == + newFrame, + "Frame didn't get added to aFrameItems?"); + addedToFrameList = PR_TRUE; + } } } else if (nsHTMLAtoms::object == aTag || @@ -5620,7 +5613,6 @@ nsCSSFrameConstructor::ConstructHTMLFrame(nsFrameConstructorState& aState, if (!aHasPseudoParent && !aState.mPseudoFrames.IsEmpty()) { ProcessPseudoFrames(aState, aFrameItems); } - isReplaced = PR_TRUE; nsCOMPtr objContent(do_QueryInterface(aContent)); NS_ASSERTION(objContent, @@ -5634,6 +5626,8 @@ nsCSSFrameConstructor::ConstructHTMLFrame(nsFrameConstructorState& aState, objContent->GetDisplayedType(&type); if (type == nsIObjectLoadingContent::TYPE_LOADING) { // Ideally, this should show the standby attribute + // XXX Should we return something that is replaced, or make + // nsFrame replaced but not its subclasses? newFrame = NS_NewEmptyFrame(mPresShell, aStyleContext); } else if (type == nsIObjectLoadingContent::TYPE_PLUGIN) @@ -5691,7 +5685,6 @@ nsCSSFrameConstructor::ConstructHTMLFrame(nsFrameConstructorState& aState, ProcessPseudoFrames(aState, aFrameItems); } - isReplaced = PR_TRUE; newFrame = NS_NewSubDocumentFrame(mPresShell, aStyleContext); triedFrame = PR_TRUE; @@ -5727,7 +5720,6 @@ nsCSSFrameConstructor::ConstructHTMLFrame(nsFrameConstructorState& aState, // so it must be replaced or html outside it will // draw into its borders. -EDV frameHasBeenInitialized = PR_TRUE; - isReplaced = PR_TRUE; addedToFrameList = PR_TRUE; isFloatContainer = PR_TRUE; } @@ -5735,7 +5727,6 @@ nsCSSFrameConstructor::ConstructHTMLFrame(nsFrameConstructorState& aState, if (!aHasPseudoParent && !aState.mPseudoFrames.IsEmpty()) { ProcessPseudoFrames(aState, aFrameItems); } - isReplaced = PR_FALSE; newFrame = NS_NewIsIndexFrame(mPresShell, aStyleContext); triedFrame = PR_TRUE; } @@ -5743,7 +5734,6 @@ nsCSSFrameConstructor::ConstructHTMLFrame(nsFrameConstructorState& aState, if (!aHasPseudoParent && !aState.mPseudoFrames.IsEmpty()) { ProcessPseudoFrames(aState, aFrameItems); } - isReplaced = PR_TRUE; newFrame = NS_NewHTMLCanvasFrame(mPresShell, aStyleContext); triedFrame = PR_TRUE; } @@ -5758,11 +5748,6 @@ nsCSSFrameConstructor::ConstructHTMLFrame(nsFrameConstructorState& aState, // If we succeeded in creating a frame then initialize it, process its // children (if requested), and set the initial child list - // If the frame is a replaced element, then set the frame state bit - if (isReplaced) { - newFrame->AddStateBits(NS_FRAME_REPLACED_ELEMENT); - } - // Note: at this point we should construct kids for newFrame only if // it's not a leaf and hasn't been initialized yet. @@ -6030,7 +6015,6 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, PRBool primaryFrameSet = PR_FALSE; nsresult rv = NS_OK; PRBool isPopup = PR_FALSE; - PRBool isReplaced = PR_FALSE; PRBool frameHasBeenInitialized = PR_FALSE; // XXXbz somewhere here we should process pseudo frames if !aHasPseudoParent @@ -6073,7 +6057,6 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, #ifdef MOZ_XUL // BUTTON CONSTRUCTION if (aTag == nsXULAtoms::button || aTag == nsXULAtoms::checkbox || aTag == nsXULAtoms::radio) { - isReplaced = PR_TRUE; newFrame = NS_NewButtonBoxFrame(mPresShell, aStyleContext); // Boxes can scroll. @@ -6081,7 +6064,6 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, } // End of BUTTON CONSTRUCTION logic // AUTOREPEATBUTTON CONSTRUCTION else if (aTag == nsXULAtoms::autorepeatbutton) { - isReplaced = PR_TRUE; newFrame = NS_NewAutoRepeatBoxFrame(mPresShell, aStyleContext); // Boxes can scroll. @@ -6090,7 +6072,6 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, // TITLEBAR CONSTRUCTION else if (aTag == nsXULAtoms::titlebar) { - isReplaced = PR_TRUE; newFrame = NS_NewTitleBarFrame(mPresShell, aStyleContext); // Boxes can scroll. @@ -6099,7 +6080,6 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, // RESIZER CONSTRUCTION else if (aTag == nsXULAtoms::resizer) { - isReplaced = PR_TRUE; newFrame = NS_NewResizerFrame(mPresShell, aStyleContext); // Boxes can scroll. @@ -6107,20 +6087,16 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, } // End of RESIZER CONSTRUCTION logic else if (aTag == nsXULAtoms::image) { - isReplaced = PR_TRUE; newFrame = NS_NewImageBoxFrame(mPresShell, aStyleContext); } else if (aTag == nsXULAtoms::spring || aTag == nsHTMLAtoms::spacer) { - isReplaced = PR_TRUE; newFrame = NS_NewLeafBoxFrame(mPresShell, aStyleContext); } else if (aTag == nsXULAtoms::treechildren) { - isReplaced = PR_TRUE; newFrame = NS_NewTreeBodyFrame(mPresShell, aStyleContext); } else if (aTag == nsXULAtoms::treecol) { - isReplaced = PR_TRUE; newFrame = NS_NewTreeColFrame(mPresShell, aStyleContext); } // TEXT CONSTRUCTION @@ -6132,7 +6108,6 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, NS_BLOCK_SPACE_MGR | NS_BLOCK_SHRINK_WRAP | NS_BLOCK_MARGIN_ROOT); } else { - isReplaced = PR_TRUE; newFrame = NS_NewTextBoxFrame(mPresShell, aStyleContext); } } @@ -6145,7 +6120,6 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, // A derived class box frame // that has custom reflow to prevent menu children // from becoming part of the flow. - isReplaced = PR_TRUE; newFrame = NS_NewMenuFrame(mPresShell, aStyleContext, (aTag != nsXULAtoms::menuitem)); } @@ -6179,17 +6153,14 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, } else if (aTag == nsXULAtoms::popupgroup) { // This frame contains child popups - isReplaced = PR_TRUE; newFrame = NS_NewPopupSetFrame(mPresShell, aStyleContext); } else if (aTag == nsXULAtoms::iframe || aTag == nsXULAtoms::editor || aTag == nsXULAtoms::browser) { - isReplaced = PR_TRUE; newFrame = NS_NewSubDocumentFrame(mPresShell, aStyleContext); } // PROGRESS METER CONSTRUCTION else if (aTag == nsXULAtoms::progressmeter) { - isReplaced = PR_TRUE; newFrame = NS_NewProgressMeterFrame(mPresShell, aStyleContext); } // End of PROGRESS METER CONSTRUCTION logic @@ -6197,25 +6168,21 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, #endif // SLIDER CONSTRUCTION if (aTag == nsXULAtoms::slider) { - isReplaced = PR_TRUE; newFrame = NS_NewSliderFrame(mPresShell, aStyleContext); } // End of SLIDER CONSTRUCTION logic // SCROLLBAR CONSTRUCTION else if (aTag == nsXULAtoms::scrollbar) { - isReplaced = PR_TRUE; newFrame = NS_NewScrollbarFrame(mPresShell, aStyleContext); } else if (aTag == nsXULAtoms::nativescrollbar) { - isReplaced = PR_TRUE; newFrame = NS_NewNativeScrollbarFrame(mPresShell, aStyleContext); } // End of SCROLLBAR CONSTRUCTION logic // SCROLLBUTTON CONSTRUCTION else if (aTag == nsXULAtoms::scrollbarbutton) { - isReplaced = PR_TRUE; newFrame = NS_NewScrollbarButtonFrame(mPresShell, aStyleContext); } // End of SCROLLBUTTON CONSTRUCTION logic @@ -6223,7 +6190,6 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, #ifdef MOZ_XUL // SPLITTER CONSTRUCTION else if (aTag == nsXULAtoms::splitter) { - isReplaced = PR_TRUE; newFrame = NS_NewSplitterFrame(mPresShell, aStyleContext); } // End of SPLITTER CONSTRUCTION logic @@ -6242,8 +6208,6 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, if (display->mDisplay == NS_STYLE_DISPLAY_INLINE_BOX || display->mDisplay == NS_STYLE_DISPLAY_BOX) { - isReplaced = PR_TRUE; - newFrame = NS_NewBoxFrame(mPresShell, aStyleContext, PR_FALSE, nsnull); // Boxes can scroll. @@ -6253,7 +6217,6 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, // ------- Begin Grid --------- else if (display->mDisplay == NS_STYLE_DISPLAY_INLINE_GRID || display->mDisplay == NS_STYLE_DISPLAY_GRID) { - isReplaced = PR_TRUE; nsCOMPtr layout; NS_NewGridLayout2(mPresShell, getter_AddRefs(layout)); newFrame = NS_NewBoxFrame(mPresShell, aStyleContext, PR_FALSE, layout); @@ -6264,8 +6227,6 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, // ------- Begin Rows/Columns --------- else if (display->mDisplay == NS_STYLE_DISPLAY_GRID_GROUP) { - isReplaced = PR_TRUE; - nsCOMPtr layout; if (aTag == nsXULAtoms::listboxbody) { @@ -6295,8 +6256,6 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, // ------- Begin Row/Column --------- else if (display->mDisplay == NS_STYLE_DISPLAY_GRID_LINE) { - isReplaced = PR_TRUE; - nsCOMPtr layout; @@ -6313,13 +6272,11 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, // End of STACK CONSTRUCTION logic // DECK CONSTRUCTION else if (display->mDisplay == NS_STYLE_DISPLAY_DECK) { - isReplaced = PR_TRUE; newFrame = NS_NewDeckFrame(mPresShell, aStyleContext); } // End of DECK CONSTRUCTION logic else if (display->mDisplay == NS_STYLE_DISPLAY_GROUPBOX) { newFrame = NS_NewGroupBoxFrame(mPresShell, aStyleContext); - isReplaced = PR_TRUE; // Boxes can scroll. mayBeScrollable = PR_TRUE; @@ -6327,8 +6284,6 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, // STACK CONSTRUCTION else if (display->mDisplay == NS_STYLE_DISPLAY_STACK || display->mDisplay == NS_STYLE_DISPLAY_INLINE_STACK) { - isReplaced = PR_TRUE; - newFrame = NS_NewStackFrame(mPresShell, aStyleContext); mayBeScrollable = PR_TRUE; @@ -6359,7 +6314,6 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, } // This is its own frame that derives from box. - isReplaced = PR_TRUE; newFrame = NS_NewMenuPopupFrame(mPresShell, aStyleContext); if (aTag == nsXULAtoms::tooltip) { @@ -6406,11 +6360,6 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, if (topFrame == nsnull) topFrame = newFrame; - // If the frame is a replaced element, then set the frame state bit - if (isReplaced) { - newFrame->AddStateBits(NS_FRAME_REPLACED_ELEMENT); - } - // xul does not support absolute positioning nsIFrame* geometricParent; #ifdef MOZ_XUL @@ -7074,7 +7023,6 @@ nsCSSFrameConstructor::ConstructMathMLFrame(nsFrameConstructorState& aState, return NS_OK; nsresult rv = NS_OK; - PRBool isReplaced = PR_FALSE; PRBool ignoreInterTagWhitespace = PR_TRUE; NS_ASSERTION(aTag != nsnull, "null MathML tag"); @@ -7226,10 +7174,6 @@ nsCSSFrameConstructor::ConstructMathMLFrame(nsFrameConstructorState& aState, // If we succeeded in creating a frame then initialize it, process its // children (if requested), and set the initial child list if (newFrame) { - // If the frame is a replaced element, then set the frame state bit - if (isReplaced) { - newFrame->AddStateBits(NS_FRAME_REPLACED_ELEMENT); - } // record that children that are ignorable whitespace should be excluded if (ignoreInterTagWhitespace) { newFrame->AddStateBits(NS_FRAME_EXCLUDE_IGNORABLE_WHITESPACE); @@ -10158,22 +10102,15 @@ nsCSSFrameConstructor::StyleChangeReflow(nsIFrame* aFrame, } #endif - // Is it a box? If so we can coelesce. - if (aFrame->IsBoxFrame()) { - nsBoxLayoutState state(mPresShell->GetPresContext()); - aFrame->MarkStyleChange(state); - } - else { - // If the frame is part of a split block-in-inline hierarchy, then - // target the style-change reflow at the first ``normal'' ancestor - // so we're sure that the style change will propagate to any - // anonymously created siblings. - if (IsFrameSpecial(aFrame)) - aFrame = GetIBContainingBlockFor(aFrame); + // If the frame is part of a split block-in-inline hierarchy, then + // target the style-change reflow at the first ``normal'' ancestor + // so we're sure that the style change will propagate to any + // anonymously created siblings. + if (IsFrameSpecial(aFrame)) + aFrame = GetIBContainingBlockFor(aFrame); - // Target a style-change reflow at the frame. - mPresShell->AppendReflowCommand(aFrame, eReflowType_StyleChanged, nsnull); - } + aFrame->AddStateBits(NS_FRAME_IS_DIRTY); + mPresShell->FrameNeedsReflow(aFrame, nsIPresShell::eStyleChange); return NS_OK; } diff --git a/mozilla/layout/base/nsCSSFrameConstructor.h b/mozilla/layout/base/nsCSSFrameConstructor.h index 4b4d8d209b8..4fe691ddad8 100644 --- a/mozilla/layout/base/nsCSSFrameConstructor.h +++ b/mozilla/layout/base/nsCSSFrameConstructor.h @@ -57,7 +57,6 @@ struct nsFrameItems; struct nsAbsoluteItems; struct nsTableCreator; class nsStyleContext; -struct nsTableList; struct nsStyleContent; struct nsStyleDisplay; class nsIPresShell; diff --git a/mozilla/layout/base/nsDocumentViewer.cpp b/mozilla/layout/base/nsDocumentViewer.cpp index 3274b10c52d..c2494583009 100644 --- a/mozilla/layout/base/nsDocumentViewer.cpp +++ b/mozilla/layout/base/nsDocumentViewer.cpp @@ -3136,9 +3136,20 @@ NS_IMETHODIMP DocumentViewerImpl::SizeToContent() // Flush out all content and style updates. We can't use a resize reflow // because it won't change some sizes that a style change reflow will. mDocument->FlushPendingNotifications(Flush_Layout); - - NS_ENSURE_SUCCESS(presShell->ResizeReflow(NS_UNCONSTRAINEDSIZE, - NS_UNCONSTRAINEDSIZE), NS_ERROR_FAILURE); + + nsIFrame *root = presShell->GetRootFrame(); + NS_ENSURE_TRUE(root, NS_ERROR_FAILURE); + + nscoord prefWidth; + { + nsCOMPtr rcx; + presShell->CreateRenderingContext(root, getter_AddRefs(rcx)); + NS_ENSURE_TRUE(rcx, NS_ERROR_FAILURE); + prefWidth = root->GetPrefWidth(rcx); + } + + nsresult rv = presShell->ResizeReflow(prefWidth, NS_UNCONSTRAINEDSIZE); + NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr presContext; GetPresContext(getter_AddRefs(presContext)); diff --git a/mozilla/layout/base/nsFrameManager.cpp b/mozilla/layout/base/nsFrameManager.cpp index 3c8bb21328f..f5f1e19b31e 100644 --- a/mozilla/layout/base/nsFrameManager.cpp +++ b/mozilla/layout/base/nsFrameManager.cpp @@ -701,6 +701,12 @@ nsFrameManager::RemoveFrame(nsIFrame* aParentFrame, nsIAtom* aListName, nsIFrame* aOldFrame) { + // In case the reflow doesn't invalidate anything since it just leaves + // a gap where the old frame was, we invalidate it here. (This is + // reasonably likely to happen when removing a last child in a way + // that doesn't change the size of the parent.) + aOldFrame->Invalidate(nsRect(nsPoint(0, 0), aOldFrame->GetSize())); + return aParentFrame->RemoveFrame(aListName, aOldFrame); } diff --git a/mozilla/layout/base/nsIPresShell.h b/mozilla/layout/base/nsIPresShell.h index baec14a8b8a..0e61a289bbc 100644 --- a/mozilla/layout/base/nsIPresShell.h +++ b/mozilla/layout/base/nsIPresShell.h @@ -59,7 +59,6 @@ #include "nsRect.h" #include "nsColor.h" #include "nsEvent.h" -#include "nsReflowType.h" #include "nsCompatibility.h" #include "nsFrameManagerBase.h" #include "mozFlushType.h" @@ -98,10 +97,10 @@ class nsWeakFrame; typedef short SelectionType; -// 845BA869-F93B-4026-8F42-CB058F0E4D87 +// b6cf677a-aa50-47c2-b381-5a82e7e792da #define NS_IPRESSHELL_IID \ -{ 0x845BA869, 0xF93B, 0x4026, \ - { 0x8F, 0x42, 0xCB, 0x05, 0x8F, 0x0E, 0x4D, 0x87 } } +{ 0xb6cf677a, 0xaa50, 0x47c2, \ + { 0xb3, 0x81, 0x5a, 0x82, 0xe7, 0xe7, 0x92, 0xda } } // Constants uses for ScrollFrameIntoView() function #define NS_PRESSHELL_SCROLL_TOP 0 @@ -338,14 +337,21 @@ public: nsIFrame** aPlaceholderFrame) const = 0; /** - * Reflow commands + * Tell the pres shell that a frame is dirty (as indicated by bits) + * and needs Reflow. It's OK if this is an ancestor of the frame needing + * reflow as long as the ancestor chain between them doesn't cross a reflow + * root. */ - NS_IMETHOD AppendReflowCommand(nsIFrame* aTargetFrame, - nsReflowType aReflowType, - nsIAtom* aChildListName) = 0; - // XXXbz don't we need a child list name on this too? - NS_IMETHOD CancelReflowCommand(nsIFrame* aTargetFrame, nsReflowType* aCmdType) = 0; - NS_IMETHOD CancelAllReflowCommands() = 0; + enum IntrinsicDirty { + // XXXldb eResize should be renamed + eResize, // don't mark any intrinsic widths dirty + eTreeChange, // mark intrinsic widths dirty on aFrame and its ancestors + eStyleChange // Do eTreeChange, plus all of aFrame's descendants + }; + NS_IMETHOD FrameNeedsReflow(nsIFrame *aFrame, + IntrinsicDirty aIntrinsicDirty) = 0; + + NS_IMETHOD CancelAllPendingReflows() = 0; /** * Recreates the frames for a node @@ -652,7 +658,7 @@ public: #ifdef MOZ_REFLOW_PERF NS_IMETHOD DumpReflows() = 0; - NS_IMETHOD CountReflows(const char * aName, PRUint32 aType, nsIFrame * aFrame) = 0; + NS_IMETHOD CountReflows(const char * aName, nsIFrame * aFrame) = 0; NS_IMETHOD PaintCount(const char * aName, nsIRenderingContext* aRenderingContext, nsPresContext * aPresContext, diff --git a/mozilla/layout/base/nsLayoutUtils.cpp b/mozilla/layout/base/nsLayoutUtils.cpp index d0629337200..e7cf5476488 100644 --- a/mozilla/layout/base/nsLayoutUtils.cpp +++ b/mozilla/layout/base/nsLayoutUtils.cpp @@ -21,6 +21,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * L. David Baron , Mozilla Corporation * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -1085,3 +1086,453 @@ nsLayoutUtils::IsViewportScrollbarFrame(nsIFrame* aFrame) return !(rootScrolledFrame == aFrame || IsProperAncestorFrame(rootScrolledFrame, aFrame)); } + +static nscoord AddPercents(nsLayoutUtils::IntrinsicWidthType aType, + nscoord aCurrent, float aPercent) +{ + nscoord result = aCurrent; + if (aPercent > 0.0f && aType == nsLayoutUtils::PREF_WIDTH) { + // XXX Should we also consider percentages for min widths, up to a + // limit? + if (aPercent >= 1.0f) + result = nscoord_MAX; + else + result = NSToCoordRound(float(result) / (1.0f - aPercent)); + } + return result; +} + +#undef DEBUG_INTRINSIC_WIDTH + +#ifdef DEBUG_INTRINSIC_WIDTH +static PRInt32 gNoiseIndent = 0; +#endif + +/* static */ nscoord +nsLayoutUtils::IntrinsicForContainer(nsIRenderingContext *aRenderingContext, + nsIFrame *aFrame, + IntrinsicWidthType aType) +{ + NS_PRECONDITION(aFrame, "null frame"); + NS_PRECONDITION(aType == MIN_WIDTH || aType == PREF_WIDTH, "bad type"); + +#ifdef DEBUG_INTRINSIC_WIDTH + nsFrame::IndentBy(stdout, gNoiseIndent); + NS_STATIC_CAST(nsFrame*, aFrame)->ListTag(stdout); + printf(" %s intrinsic width for container:\n", + aType == MIN_WIDTH ? "min" : "pref"); +#endif + + nsIFrame::IntrinsicWidthOffsetData offsets = aFrame->IntrinsicWidthOffsets(); + + const nsStylePosition *stylePos = aFrame->GetStylePosition(); + const PRUint8 boxSizing = stylePos->mBoxSizing; + const nsStyleCoord &styleWidth = stylePos->mWidth; + const nsStyleCoord &styleMinWidth = stylePos->mMinWidth; + const nsStyleCoord &styleMaxWidth = stylePos->mMaxWidth; + + // We build up two values starting with the content box, and then + // adding padding, border and margin. The result is normally + // |result|. Then, when we handle 'width', 'min-width', and + // 'max-width', we use the results we've been building in |min| as a + // minimum, overriding 'min-width'. This ensures two things: + // * that we don't let a value of 'box-sizing' specifying a width + // smaller than the padding/border inside the box-sizing box give + // a content width less than zero + // * that we prevent tables from becoming smaller than their + // intrinsic minimum width + nscoord result = 0, min = 0; + + // If we have a specified width (or a specified 'min-width' greater + // than the specified 'max-width', which works out to the same thing), + // don't even bother getting the frame's intrinsic width. + if (styleWidth.GetUnit() != eStyleUnit_Coord && + (styleMinWidth.GetUnit() != eStyleUnit_Coord || + styleMaxWidth.GetUnit() != eStyleUnit_Coord || + styleMaxWidth.GetCoordValue() > styleMinWidth.GetCoordValue())) { +#ifdef DEBUG_INTRINSIC_WIDTH + ++gNoiseIndent; +#endif + if (aType == MIN_WIDTH) + result = aFrame->GetMinWidth(aRenderingContext); + else + result = aFrame->GetPrefWidth(aRenderingContext); +#ifdef DEBUG_INTRINSIC_WIDTH + --gNoiseIndent; + nsFrame::IndentBy(stdout, gNoiseIndent); + NS_STATIC_CAST(nsFrame*, aFrame)->ListTag(stdout); + printf(" %s intrinsic width from frame is %d.\n", + aType == MIN_WIDTH ? "min" : "pref", result); +#endif + } + + if (aFrame->GetType() == nsLayoutAtoms::tableFrame) { + // Tables can't shrink smaller than their intrinsic minimum width, + // no matter what. + min = aFrame->GetMinWidth(aRenderingContext); + } + + // We also need to track what has been added on outside of the box + // (controlled by 'box-sizing') where 'width', 'min-width' and + // 'max-width' are applied. We have to account for these properties + // after getting all the offsets (margin, border, padding) because + // percentages do not operate linearly. + // Doing this is ok because although percentages aren't handled + // linearly, they are handled monotonically. + nscoord coordOutsideWidth = offsets.hPadding; + float pctOutsideWidth = offsets.hPctPadding; + + float pctTotal = 0.0f; + + if (boxSizing == NS_STYLE_BOX_SIZING_PADDING) { + min += coordOutsideWidth; + result += coordOutsideWidth; + pctTotal += pctOutsideWidth; + + coordOutsideWidth = 0; + pctOutsideWidth = 0.0f; + } + + coordOutsideWidth += offsets.hBorder; + + if (boxSizing == NS_STYLE_BOX_SIZING_BORDER) { + min += coordOutsideWidth; + result += coordOutsideWidth; + pctTotal += pctOutsideWidth; + + coordOutsideWidth = 0; + pctOutsideWidth = 0.0f; + } + + coordOutsideWidth += offsets.hMargin; + pctOutsideWidth += offsets.hPctMargin; + + min += coordOutsideWidth; + result += coordOutsideWidth; + pctTotal += pctOutsideWidth; + + result = AddPercents(aType, result, pctTotal); + + switch (styleWidth.GetUnit()) { + case eStyleUnit_Coord: + result = AddPercents(aType, + styleWidth.GetCoordValue() + coordOutsideWidth, + pctOutsideWidth); + break; + case eStyleUnit_Percent: + if (aType == MIN_WIDTH && aFrame->IsFrameOfType(nsIFrame::eReplaced)) { + // A percentage width on replaced elements means they can shrink to 0. + result = 0; // let |min| handle padding/border/margin + } + break; + } + + if (styleMaxWidth.GetUnit() == eStyleUnit_Coord) { + nscoord maxw = AddPercents(aType, + styleMaxWidth.GetCoordValue() + coordOutsideWidth, pctOutsideWidth); + if (result > maxw) + result = maxw; + } + + if (styleMinWidth.GetUnit() == eStyleUnit_Coord) { + nscoord minw = AddPercents(aType, + styleMinWidth.GetCoordValue() + coordOutsideWidth, pctOutsideWidth); + if (result < minw) + result = minw; + } + + min = AddPercents(aType, min, pctTotal); + if (result < min) + result = min; + +#ifdef DEBUG_INTRINSIC_WIDTH + nsFrame::IndentBy(stdout, gNoiseIndent); + NS_STATIC_CAST(nsFrame*, aFrame)->ListTag(stdout); + printf(" %s intrinsic width for container is %d twips.\n", + aType == MIN_WIDTH ? "min" : "pref", result); +#endif + + return result; +} + +/* static */ nscoord +nsLayoutUtils::ComputeHorizontalValue(nsIRenderingContext* aRenderingContext, + nsIFrame* aFrame, + nscoord aContainingBlockWidth, + const nsStyleCoord& aCoord) +{ + NS_PRECONDITION(aFrame, "non-null frame expected"); + NS_PRECONDITION(aRenderingContext, "non-null rendering context expected"); + NS_PRECONDITION(aContainingBlockWidth != NS_UNCONSTRAINEDSIZE, + "unconstrained widths no longer supported"); + + nscoord result = 0; + nsStyleUnit unit = aCoord.GetUnit(); + if (eStyleUnit_Percent == unit) { + result = NSToCoordFloor(aContainingBlockWidth * aCoord.GetPercentValue()); + } else if (eStyleUnit_Coord == unit) { + result = aCoord.GetCoordValue(); + } + else if (eStyleUnit_Chars == unit) { + SetFontFromStyle(aRenderingContext, aFrame->GetStyleContext()); + nscoord fontWidth; + aRenderingContext->GetWidth('M', fontWidth); + result = aCoord.GetIntValue() * fontWidth; + } + return result; +} + +/* static */ nscoord +nsLayoutUtils::ComputeVerticalValue(nsIRenderingContext* aRenderingContext, + nsIFrame* aFrame, + nscoord aContainingBlockHeight, + const nsStyleCoord& aCoord) +{ + NS_PRECONDITION(aFrame, "non-null frame expected"); + NS_PRECONDITION(aRenderingContext, "non-null rendering context expected"); + nscoord result = 0; + nsStyleUnit unit = aCoord.GetUnit(); + if (eStyleUnit_Percent == unit) { + // XXXldb Some callers explicitly check aContainingBlockHeight + // against NS_AUTOHEIGHT *and* unit against eStyleUnit_Percent + // before calling this function, so this assertion probably needs to + // be inside the percentage case. However, it would be much more + // likely to catch problems if it were at the start of the function. + // XXXldb Many callers pass a non-'auto' containing block height when + // according to CSS2.1 they should be passing 'auto'. + NS_PRECONDITION(NS_AUTOHEIGHT != aContainingBlockHeight, + "unexpected 'containing block height'"); + + if (NS_AUTOHEIGHT != aContainingBlockHeight) { + result = + NSToCoordFloor(aContainingBlockHeight * aCoord.GetPercentValue()); + } + } else if (eStyleUnit_Coord == unit) { + result = aCoord.GetCoordValue(); + } + return result; +} + +inline PRBool +IsAutoHeight(const nsStyleCoord &aCoord, nscoord aCBHeight) +{ + nsStyleUnit unit = aCoord.GetUnit(); + return unit == eStyleUnit_Auto || // only for 'height' + unit == eStyleUnit_Null || // only for 'max-height' + (unit == eStyleUnit_Percent && + aCBHeight == NS_AUTOHEIGHT); +} + + +/* static */ nsSize +nsLayoutUtils::ComputeSizeWithIntrinsicDimensions( + nsIRenderingContext* aRenderingContext, + nsIFrame* aFrame, nsSize aIntrinsicSize, nsSize aCBSize, + nsSize aBorder, nsSize aPadding) +{ + const nsStylePosition *stylePos = aFrame->GetStylePosition(); + // Handle intrinsic sizes and their interaction with + // {min-,max-,}{width,height} according to the rules in + // http://www.w3.org/TR/CSS21/visudet.html#min-max-widths + + // Note: throughout the following section of the function, I avoid + // a * (b / c) because of its reduced accuracy relative to a * b / c + // or (a * b) / c (which are equivalent). + + PRBool isAutoWidth = stylePos->mWidth.GetUnit() == eStyleUnit_Auto; + PRBool isAutoHeight = IsAutoHeight(stylePos->mHeight, aCBSize.height); + + nsSize boxSizingAdjust(0,0); + switch (stylePos->mBoxSizing) { + case NS_STYLE_BOX_SIZING_BORDER: + boxSizingAdjust += aBorder; + // fall through + case NS_STYLE_BOX_SIZING_PADDING: + boxSizingAdjust += aPadding; + } + + nscoord width, minWidth, maxWidth, height, minHeight, maxHeight; + + if (!isAutoWidth) { + width = nsLayoutUtils::ComputeHorizontalValue(aRenderingContext, + aFrame, aCBSize.width, stylePos->mWidth) - + boxSizingAdjust.width; + if (width < 0) + width = 0; + } + + if (stylePos->mMaxWidth.GetUnit() != eStyleUnit_Null) { + maxWidth = nsLayoutUtils::ComputeHorizontalValue(aRenderingContext, + aFrame, aCBSize.width, stylePos->mMaxWidth) - + boxSizingAdjust.width; + if (maxWidth < 0) + maxWidth = 0; + } else { + maxWidth = nscoord_MAX; + } + + minWidth = nsLayoutUtils::ComputeHorizontalValue(aRenderingContext, + aFrame, aCBSize.width, stylePos->mMinWidth) - + boxSizingAdjust.width; + if (minWidth < 0) + minWidth = 0; + + if (!isAutoHeight) { + height = nsLayoutUtils::ComputeVerticalValue(aRenderingContext, + aFrame, aCBSize.height, stylePos->mHeight) - + boxSizingAdjust.height; + if (height < 0) + height = 0; + } + + if (!IsAutoHeight(stylePos->mMaxHeight, aCBSize.height)) { + maxHeight = nsLayoutUtils::ComputeVerticalValue(aRenderingContext, + aFrame, aCBSize.height, stylePos->mMaxHeight) - + boxSizingAdjust.height; + if (maxHeight < 0) + maxHeight = 0; + } else { + maxHeight = nscoord_MAX; + } + + if (!IsAutoHeight(stylePos->mMinHeight, aCBSize.height)) { + minHeight = nsLayoutUtils::ComputeVerticalValue(aRenderingContext, + aFrame, aCBSize.height, stylePos->mMinHeight) - + boxSizingAdjust.height; + if (minHeight < 0) + minHeight = 0; + } else { + minHeight = 0; + } + + if (isAutoWidth) { + if (isAutoHeight) { + + // 'auto' width, 'auto' height + if (minWidth > maxWidth) + maxWidth = minWidth; + if (minHeight > maxHeight) + maxHeight = minHeight; + + nscoord heightAtMaxWidth, heightAtMinWidth, + widthAtMaxHeight, widthAtMinHeight; + if (aIntrinsicSize.width > 0) { + heightAtMaxWidth = maxWidth * aIntrinsicSize.height / aIntrinsicSize.width; + if (heightAtMaxWidth < minHeight) + heightAtMaxWidth = minHeight; + heightAtMinWidth = minWidth * aIntrinsicSize.height / aIntrinsicSize.width; + if (heightAtMinWidth > maxHeight) + heightAtMinWidth = maxHeight; + } else { + heightAtMaxWidth = aIntrinsicSize.height; + heightAtMinWidth = aIntrinsicSize.height; + } + + if (aIntrinsicSize.height > 0) { + widthAtMaxHeight = maxHeight * aIntrinsicSize.width / aIntrinsicSize.height; + if (widthAtMaxHeight < minWidth) + widthAtMaxHeight = minWidth; + widthAtMinHeight = minHeight * aIntrinsicSize.width / aIntrinsicSize.height; + if (widthAtMinHeight > maxWidth) + widthAtMinHeight = maxWidth; + } else { + widthAtMaxHeight = aIntrinsicSize.width; + widthAtMinHeight = aIntrinsicSize.width; + } + + if (aIntrinsicSize.width > maxWidth) { + if (aIntrinsicSize.height > maxHeight) { + if (maxWidth * aIntrinsicSize.height <= maxHeight * aIntrinsicSize.width) { + width = maxWidth; + height = heightAtMaxWidth; + } else { + height = maxHeight; + width = widthAtMaxHeight; + } + } else { + width = maxWidth; + height = heightAtMaxWidth; + } + } else if (aIntrinsicSize.width < minWidth) { + if (aIntrinsicSize.height < minHeight) { + if (minWidth * aIntrinsicSize.height <= minHeight * aIntrinsicSize.width) { + height = minHeight; + width = widthAtMinHeight; + } else { + width = minWidth; + height = heightAtMinWidth; + } + } else { + width = minWidth; + height = heightAtMinWidth; + } + } else { + if (aIntrinsicSize.height > maxHeight) { + height = maxHeight; + width = widthAtMaxHeight; + } else if (aIntrinsicSize.height < minHeight) { + height = minHeight; + width = widthAtMinHeight; + } else { + width = aIntrinsicSize.width; + height = aIntrinsicSize.height; + } + } + + } else { + + // 'auto' width, non-'auto' height + height = NS_CSS_MINMAX(height, minHeight, maxHeight); + if (aIntrinsicSize.height != 0) { + width = aIntrinsicSize.width * height / aIntrinsicSize.height; + } else { + width = aIntrinsicSize.width; + } + width = NS_CSS_MINMAX(width, minWidth, maxWidth); + + } + } else { + if (isAutoHeight) { + + // non-'auto' width, 'auto' height + width = NS_CSS_MINMAX(width, minWidth, maxWidth); + if (aIntrinsicSize.width != 0) { + height = aIntrinsicSize.height * width / aIntrinsicSize.width; + } else { + height = aIntrinsicSize.height; + } + height = NS_CSS_MINMAX(height, minHeight, maxHeight); + + } else { + + // non-'auto' width, non-'auto' height + height = NS_CSS_MINMAX(height, minHeight, maxHeight); + width = NS_CSS_MINMAX(width, minWidth, maxWidth); + + } + } + + return nsSize(width, height); +} + +/* static */ nscoord +nsLayoutUtils::MinWidthFromInline(nsIFrame *aFrame, + nsIRenderingContext *aRenderingContext) +{ + nsIFrame::InlineMinWidthData data; + DISPLAY_MIN_WIDTH(aFrame, data.prevLines); + aFrame->AddInlineMinWidth(aRenderingContext, &data); + data.Break(aRenderingContext); + return data.prevLines; +} + +/* static */ nscoord +nsLayoutUtils::PrefWidthFromInline(nsIFrame *aFrame, + nsIRenderingContext *aRenderingContext) +{ + nsIFrame::InlinePrefWidthData data; + DISPLAY_PREF_WIDTH(aFrame, data.prevLines); + aFrame->AddInlinePrefWidth(aRenderingContext, &data); + data.Break(aRenderingContext); + return data.prevLines; +} diff --git a/mozilla/layout/base/nsLayoutUtils.h b/mozilla/layout/base/nsLayoutUtils.h index 9cd80976341..76feb76e568 100644 --- a/mozilla/layout/base/nsLayoutUtils.h +++ b/mozilla/layout/base/nsLayoutUtils.h @@ -461,6 +461,40 @@ public: * the root content. */ static PRBool IsViewportScrollbarFrame(nsIFrame* aFrame); + + /** + * Get the contribution of aFrame to its containing block's intrinsic + * width. This considers the child's intrinsic width, its 'width', + * 'min-width', and 'max-width' properties, and its padding, border, + * and margin. + */ + enum IntrinsicWidthType { MIN_WIDTH, PREF_WIDTH }; + static nscoord IntrinsicForContainer(nsIRenderingContext* aRenderingContext, + nsIFrame* aFrame, + IntrinsicWidthType aType); + + static nscoord ComputeHorizontalValue(nsIRenderingContext* aRenderingContext, + nsIFrame *aFrame, + nscoord aContainingBlockWidth, + const nsStyleCoord& aCoord); + + static nscoord ComputeVerticalValue(nsIRenderingContext* aRenderingContext, + nsIFrame *aFrame, + nscoord aContainingBlockHeight, + const nsStyleCoord& aCoord); + + static nsSize ComputeSizeWithIntrinsicDimensions( + nsIRenderingContext* aRenderingContext, + nsIFrame* aFrame, nsSize aIntrinsicSize, nsSize aCBSize, + nsSize aBorder, nsSize aPadding); + + // Implement nsIFrame::GetPrefWidth in terms of nsIFrame::AddInlinePrefWidth + static nscoord PrefWidthFromInline(nsIFrame* aFrame, + nsIRenderingContext* aRenderingContext); + + // Implement nsIFrame::GetMinWidth in terms of nsIFrame::AddInlineMinWidth + static nscoord MinWidthFromInline(nsIFrame* aFrame, + nsIRenderingContext* aRenderingContext); }; #endif // nsLayoutUtils_h__ diff --git a/mozilla/layout/base/nsPresContext.cpp b/mozilla/layout/base/nsPresContext.cpp index 0115c15afdb..d977d14f7c2 100644 --- a/mozilla/layout/base/nsPresContext.cpp +++ b/mozilla/layout/base/nsPresContext.cpp @@ -1387,11 +1387,10 @@ nsPresContext::EnsureVisible(PRBool aUnsuppressFocus) #ifdef MOZ_REFLOW_PERF void -nsPresContext::CountReflows(const char * aName, - PRUint32 aType, nsIFrame * aFrame) +nsPresContext::CountReflows(const char * aName, nsIFrame * aFrame) { if (mShell) { - mShell->CountReflows(aName, aType, aFrame); + mShell->CountReflows(aName, aFrame); } } #endif diff --git a/mozilla/layout/base/nsPresContext.h b/mozilla/layout/base/nsPresContext.h index 7f329902edf..40b808e22a8 100644 --- a/mozilla/layout/base/nsPresContext.h +++ b/mozilla/layout/base/nsPresContext.h @@ -650,7 +650,7 @@ public: #ifdef MOZ_REFLOW_PERF NS_HIDDEN_(void) CountReflows(const char * aName, - PRUint32 aType, nsIFrame * aFrame); + nsIFrame * aFrame); #endif /** @@ -930,10 +930,10 @@ private: #ifdef MOZ_REFLOW_PERF -#define DO_GLOBAL_REFLOW_COUNT(_name, _type) \ - aPresContext->CountReflows((_name), (_type), (nsIFrame*)this); +#define DO_GLOBAL_REFLOW_COUNT(_name) \ + aPresContext->CountReflows((_name), (nsIFrame*)this); #else -#define DO_GLOBAL_REFLOW_COUNT(_name, _type) +#define DO_GLOBAL_REFLOW_COUNT(_name) #endif // MOZ_REFLOW_PERF #endif /* nsPresContext_h___ */ diff --git a/mozilla/layout/base/nsPresShell.cpp b/mozilla/layout/base/nsPresShell.cpp index dde9fd5cc22..48922126ccf 100644 --- a/mozilla/layout/base/nsPresShell.cpp +++ b/mozilla/layout/base/nsPresShell.cpp @@ -69,7 +69,6 @@ #include "nsINameSpaceManager.h" // for Pref-related rule management (bugs 22963,20760,31816) #include "nsIServiceManager.h" #include "nsFrame.h" -#include "nsHTMLReflowCommand.h" #include "nsIViewManager.h" #include "nsCRT.h" #include "nsCRTGlue.h" @@ -90,7 +89,6 @@ #include "nsContentUtils.h" #include "nsISelection.h" #include "nsISelectionController.h" -#include "nsReflowPath.h" #include "nsLayoutCID.h" #include "nsLayoutAtoms.h" #include "nsIDOMRange.h" @@ -259,7 +257,6 @@ ShowVerifyReflowFlags() class ReflowCountMgr; static const char kGrandTotalsStr[] = "Grand Totals"; -#define NUM_REFLOW_TYPES 5 // Counting Class class ReflowCounter { @@ -272,22 +269,22 @@ public: void DisplayDiffTotals(const char * aStr); void DisplayHTMLTotals(const char * aStr); - void Add(nsReflowReason aType) { mTotals[aType]++; } - void Add(nsReflowReason aType, PRUint32 aTotal) { mTotals[aType] += aTotal; } + void Add() { mTotal++; } + void Add(PRUint32 aTotal) { mTotal += aTotal; } void CalcDiffInTotals(); void SetTotalsCache(); void SetMgr(ReflowCountMgr * aMgr) { mMgr = aMgr; } - PRUint32 GetTotalByType(nsReflowReason aType) { if (aType >= eReflowReason_Initial && aType <= eReflowReason_Dirty) return mTotals[aType]; else return 0; } + PRUint32 GetTotal() { return mTotal; } protected: - void DisplayTotals(PRUint32 * aArray, const char * aTitle); - void DisplayHTMLTotals(PRUint32 * aArray, const char * aTitle); + void DisplayTotals(PRUint32 aTotal, const char * aTitle); + void DisplayHTMLTotals(PRUint32 aTotal, const char * aTitle); - PRUint32 mTotals[NUM_REFLOW_TYPES]; - PRUint32 mCacheTotals[NUM_REFLOW_TYPES]; + PRUint32 mTotal; + PRUint32 mCacheTotal; ReflowCountMgr * mMgr; // weak reference (don't delete) }; @@ -329,7 +326,7 @@ public: void DisplayHTMLTotals(const char * aStr); void DisplayDiffsInTotals(const char * aStr); - void Add(const char * aName, nsReflowReason aType, nsIFrame * aFrame); + void Add(const char * aName, nsIFrame * aFrame); ReflowCounter * LookUp(const char * aName); void PaintCount(const char * aName, nsIRenderingContext* aRenderingContext, nsPresContext* aPresContext, nsIFrame * aFrame, PRUint32 aColor); @@ -346,8 +343,8 @@ public: void SetPaintFrameCounts(PRBool aVal) { mPaintFrameByFrameCounts = aVal; } protected: - void DisplayTotals(PRUint32 * aArray, PRUint32 * aDupArray, char * aTitle); - void DisplayHTMLTotals(PRUint32 * aArray, PRUint32 * aDupArray, char * aTitle); + void DisplayTotals(PRUint32 aTotal, PRUint32 * aDupArray, char * aTitle); + void DisplayHTMLTotals(PRUint32 aTotal, PRUint32 * aDupArray, char * aTitle); PR_STATIC_CALLBACK(PRIntn) RemoveItems(PLHashEntry *he, PRIntn i, void *arg); PR_STATIC_CALLBACK(PRIntn) RemoveIndiItems(PLHashEntry *he, PRIntn i, void *arg); @@ -779,249 +776,6 @@ struct nsCallbackEventRequest nsCallbackEventRequest* next; }; - -// ---------------------------------------------------------------------------- - -/** - * Used to build and maintain the incremental reflow tree, and - * dispatch incremental reflows to individual reflow roots. - */ -class IncrementalReflow -{ -public: - ~IncrementalReflow(); - - /** - * Add a reflow command to the set of commands that are to be - * dispatched in the incremental reflow. - */ - enum AddCommandResult { - eEnqueued, // the command was successfully added - eTryLater, // the command could not be added; try again - eCancel, // the command was not added; delete it - eOOM // Out of memory. - }; - AddCommandResult - AddCommand(nsPresContext *aPresContext, - nsHTMLReflowCommand *aCommand); - - /** - * Dispatch the incremental reflow. - */ - void - Dispatch(nsPresContext *aPresContext, - nsHTMLReflowMetrics &aDesiredSize, - const nsSize &aMaxSize, - nsIRenderingContext &aRendContext); - -#ifdef NS_DEBUG - /** - * Dump the incremental reflow state. - */ - void - Dump(nsPresContext *aPresContext) const; -#endif - -protected: - /** - * The set of incremental reflow roots. - */ - nsAutoVoidArray mRoots; -}; - -IncrementalReflow::~IncrementalReflow() -{ - for (PRInt32 i = mRoots.Count() - 1; i >= 0; --i) - delete NS_STATIC_CAST(nsReflowPath *, mRoots[i]); -} - -void -IncrementalReflow::Dispatch(nsPresContext *aPresContext, - nsHTMLReflowMetrics &aDesiredSize, - const nsSize &aMaxSize, - nsIRenderingContext &aRendContext) -{ - for (PRInt32 i = mRoots.Count() - 1; i >= 0; --i) { - // Send an incremental reflow notification to the first frame in the - // path. - nsReflowPath *path = NS_STATIC_CAST(nsReflowPath *, mRoots[i]); - nsIFrame *first = path->mFrame; - - nsIFrame* root = aPresContext->PresShell()->FrameManager()->GetRootFrame(); - - first->WillReflow(aPresContext); - nsContainerFrame::PositionFrameView(first); - - // If the first frame in the path is the root of the frame - // hierarchy, then use all the available space. If it's simply a - // `reflow root', then use the first frame's size as the available - // space. - nsSize size; - if (first == root) - size = aMaxSize; - else - size = first->GetSize(); - - nsHTMLReflowState reflowState(aPresContext, first, path, - &aRendContext, size); - - nsReflowStatus status; - first->Reflow(aPresContext, aDesiredSize, reflowState, status); - - // If an incremental reflow is initiated at a frame other than the - // root frame, then its desired size had better not change! - NS_ASSERTION(first == root || - (aDesiredSize.width == size.width && aDesiredSize.height == size.height), - "non-root frame's desired size changed during an incremental reflow"); - - first->SetSize(nsSize(aDesiredSize.width, aDesiredSize.height)); - - nsContainerFrame::SyncFrameViewAfterReflow(aPresContext, first, first->GetView(), - &aDesiredSize.mOverflowArea); - - first->DidReflow(aPresContext, nsnull, NS_FRAME_REFLOW_FINISHED); - } -} - -IncrementalReflow::AddCommandResult -IncrementalReflow::AddCommand(nsPresContext *aPresContext, - nsHTMLReflowCommand *aCommand) -{ - nsIFrame *frame; - aCommand->GetTarget(frame); - NS_ASSERTION(frame != nsnull, "reflow command with no target"); - - // Construct the reflow path by walking up the through the frames' - // parent chain until we reach either a `reflow root' or the root - // frame in the frame hierarchy. - nsAutoVoidArray path; - do { - path.AppendElement(frame); - } while (!(frame->GetStateBits() & NS_FRAME_REFLOW_ROOT) && - (frame = frame->GetParent()) != nsnull); - - // Pop off the root, add it to the set if it's not there already. - PRInt32 lastIndex = path.Count() - 1; - nsIFrame *rootFrame = NS_STATIC_CAST(nsIFrame *, path[lastIndex]); - path.RemoveElementAt(lastIndex); - - // Prevent an incremental reflow from being posted inside a reflow - // root if the reflow root's container has not yet been reflowed. - // This can cause problems like bug 228156. - if (rootFrame->GetParent() && - (rootFrame->GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { - return eCancel; - } - - nsReflowPath *root = nsnull; - - PRInt32 i; - for (i = mRoots.Count() - 1; i >= 0; --i) { - nsReflowPath *r = NS_STATIC_CAST(nsReflowPath *, mRoots[i]); - if (r->mFrame == rootFrame) { - root = r; - break; - } - } - - if (! root) { - root = new nsReflowPath(rootFrame); - if (! root) - return eOOM; - - root->mReflowCommand = nsnull; - mRoots.AppendElement(root); - } - - // Now walk the path from the root to the leaf, adding to the reflow - // tree as necessary. - nsReflowPath *target = root; - for (i = path.Count() - 1; i >= 0; --i) { - nsIFrame *f = NS_STATIC_CAST(nsIFrame *, path[i]); - target = target->EnsureSubtreeFor(f); - - // Out of memory. Ugh. - if (! target) - return eOOM; - } - - // Place the reflow command in the leaf, if one isn't there already. - if (target->mReflowCommand) { - // XXXwaterson it's probably possible to have some notion of - // `promotion' here that would avoid any re-queuing; for example, - // promote a dirty reflow to a style changed. For now, let's punt - // and not worry about it. -#ifdef NS_DEBUG - if (gVerifyReflowFlags & VERIFY_REFLOW_NOISY_RC) - printf("requeuing command %p because %p was already scheduled " - "for the same frame", - (void*)aCommand, (void*)target->mReflowCommand); -#endif - - return eTryLater; - } - - target->mReflowCommand = aCommand; - return eEnqueued; -} - - -#ifdef NS_DEBUG -void -IncrementalReflow::Dump(nsPresContext *aPresContext) const -{ - for (PRInt32 i = mRoots.Count() - 1; i >= 0; --i) - NS_STATIC_CAST(nsReflowPath *, mRoots[i])->Dump(aPresContext, stdout, 0); -} -#endif - -// ---------------------------------------------------------------------------- - -struct ReflowCommandEntry : public PLDHashEntryHdr -{ - nsHTMLReflowCommand* mCommand; -}; - -PR_STATIC_CALLBACK(const void *) -ReflowCommandHashGetKey(PLDHashTable *table, PLDHashEntryHdr *entry) -{ - ReflowCommandEntry *e = NS_STATIC_CAST(ReflowCommandEntry *, entry); - - return e->mCommand; -} - -PR_STATIC_CALLBACK(PLDHashNumber) -ReflowCommandHashHashKey(PLDHashTable *table, const void *key) -{ - const nsHTMLReflowCommand* command = - NS_STATIC_CAST(const nsHTMLReflowCommand*, key); - - // The target is going to be reasonably unique, if we shift out the - // always-zero low-order bits, the type comes from an enum and we just don't - // have that many types, and the child list name is either null or has the - // same high-order bits as all the other child list names. - return - (NS_PTR_TO_INT32(command->GetTarget()) >> 2) ^ - (command->Type() << 17) ^ - (NS_PTR_TO_INT32(command->GetChildListName()) << 20); -} - -PR_STATIC_CALLBACK(PRBool) -ReflowCommandHashMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *entry, - const void *key) -{ - const ReflowCommandEntry *e = - NS_STATIC_CAST(const ReflowCommandEntry *, entry); - const nsHTMLReflowCommand *command = e->mCommand; - const nsHTMLReflowCommand *command2 = - NS_STATIC_CAST(const nsHTMLReflowCommand *, key); - - return - command->GetTarget() == command2->GetTarget() && - command->Type() == command2->Type() && - command->GetChildListName() == command2->GetChildListName(); -} - // ---------------------------------------------------------------------------- class nsPresShellEventCB; @@ -1080,14 +834,8 @@ public: nsISupports** aResult) const; NS_IMETHOD GetPlaceholderFrameFor(nsIFrame* aFrame, nsIFrame** aPlaceholderFrame) const; - NS_IMETHOD AppendReflowCommand(nsIFrame* aTargetFrame, - nsReflowType aReflowType, - nsIAtom* aChildListName); - NS_IMETHOD CancelReflowCommand(nsIFrame* aTargetFrame, - nsReflowType* aCmdType); - void CancelReflowCommandInternal(nsIFrame* aTargetFrame, - nsReflowType* aCmdType); - NS_IMETHOD CancelAllReflowCommands(); + NS_IMETHOD FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty); + NS_IMETHOD CancelAllPendingReflows(); NS_IMETHOD IsSafeToFlush(PRBool& aIsSafeToFlush); NS_IMETHOD FlushPendingNotifications(mozFlushType aType); @@ -1274,7 +1022,7 @@ public: #ifdef MOZ_REFLOW_PERF NS_IMETHOD DumpReflows(); - NS_IMETHOD CountReflows(const char * aName, PRUint32 aType, nsIFrame * aFrame); + NS_IMETHOD CountReflows(const char * aName, nsIFrame * aFrame); NS_IMETHOD PaintCount(const char * aName, nsIRenderingContext* aRenderingContext, nsPresContext* aPresContext, nsIFrame * aFrame, PRUint32 aColor); NS_IMETHOD SetPaintFrameCount(PRBool aOn); @@ -1302,15 +1050,12 @@ protected: void UnsuppressAndInvalidate(); - nsresult ReflowCommandAdded(nsHTMLReflowCommand* aRC); - nsresult ReflowCommandRemoved(nsHTMLReflowCommand* aRC); - - // This method should be called after reflow commands have been - // removed from the queue, but after the state in the presshell is - // such that it's safe to flush (i.e. mIsReflowing == PR_FALSE) - // If there are no load-created reflow commands and we blocked - // onload on the document, we'll unblock it. - void DoneRemovingReflowCommands(); + // This method should be called after mDirtyRoots has been emptied, + // but after the state in the presshell is such that it's safe to + // flush (i.e. mIsReflowing == PR_FALSE) If there are no load-created + // reflow commands and we blocked onload on the document, we'll + // unblock it. + void DoneRemovingDirtyRoots(); void WillCauseReflow() { ++mChangeNestCount; } nsresult DidCauseReflow(); @@ -1320,11 +1065,6 @@ protected: void ClearReflowEventStatus(); void PostReflowEvent(); - // Note: when PR_FALSE is returned, AlreadyInQueue assumes the command will - // in fact be added to the queue. If it's not, it needs to be removed from - // mReflowCommandTable (AlreadyInQueue will insert it in that table). - PRBool AlreadyInQueue(nsHTMLReflowCommand* aReflowCommand); - friend class nsPresShellEventCB; class ReflowEvent; @@ -1379,9 +1119,8 @@ protected: #ifdef DEBUG PRUint32 mUpdateCount; #endif - // normal reflow commands - nsVoidArray mReflowCommands; - PLDHashTable mReflowCommandTable; + // reflow roots that need to be reflowed, as both a queue and a hashtable + nsVoidArray mDirtyRoots; PRPackedBool mDocumentLoading; PRPackedBool mDocumentOnloadBlocked; @@ -1414,7 +1153,6 @@ protected: FrameArena mFrameArena; StackArena* mStackArena; nsCOMPtr mDragService; - PRInt32 mRCCreatedDuringLoad; // Counter to keep track of reflow commands created during doc nsRevocableEventPtr mReflowEvent; @@ -1749,25 +1487,6 @@ PresShell::Init(nsIDocument* aDocument, NS_ADDREF(mPresContext); aPresContext->SetShell(this); - // Create our reflow command hashtable - static PLDHashTableOps reflowCommandOps = - { - PL_DHashAllocTable, - PL_DHashFreeTable, - ReflowCommandHashGetKey, - ReflowCommandHashHashKey, - ReflowCommandHashMatchEntry, - PL_DHashMoveEntryStub, - PL_DHashClearEntryStub, - PL_DHashFinalizeStub - }; - - if (!PL_DHashTableInit(&mReflowCommandTable, &reflowCommandOps, - nsnull, sizeof(ReflowCommandEntry), 16)) { - mReflowCommandTable.ops = nsnull; - return NS_ERROR_OUT_OF_MEMORY; - } - // Now we can initialize the style set. nsresult result = aStyleSet->Init(aPresContext); NS_ENSURE_SUCCESS(result, result); @@ -1989,18 +1708,13 @@ PresShell::Destroy() // Revoke any pending reflow event mReflowEvent.Revoke(); - CancelAllReflowCommands(); + CancelAllPendingReflows(); NS_ASSERTION(!mDocumentOnloadBlocked, - "CancelAllReflowCommands() didn't unblock onload?"); + "CancelAllPendingReflows() didn't unblock onload?"); KillResizeEventTimer(); - // Now that mReflowCommandTable won't be accessed anymore, finish it - if (mReflowCommandTable.ops) { - PL_DHashTableFinish(&mReflowCommandTable); - } - mHaveShutDown = PR_TRUE; return NS_OK; @@ -2859,6 +2573,9 @@ PresShell::InitialReflow(nscoord aWidth, nscoord aHeight) // start batching widget changes mViewManager->BeginUpdateViewBatch(); + // XXX Do a full invalidate at the beginning so that invalidates along + // the way don't have region accumulation issues? + WillCauseReflow(); WillDoReflow(); @@ -2922,7 +2639,7 @@ PresShell::InitialReflow(nscoord aWidth, nscoord aHeight) #endif nsRect bounds = mPresContext->GetVisibleArea(); nsSize maxSize(bounds.width, bounds.height); - nsHTMLReflowMetrics desiredSize(nsnull); + nsHTMLReflowMetrics desiredSize; nsReflowStatus status; nsIRenderingContext* rcx = nsnull; @@ -2932,8 +2649,7 @@ PresShell::InitialReflow(nscoord aWidth, nscoord aHeight) AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow); mIsReflowing = PR_TRUE; - nsHTMLReflowState reflowState(mPresContext, rootFrame, - eReflowReason_Initial, rcx, maxSize); + nsHTMLReflowState reflowState(mPresContext, rootFrame, rcx, maxSize); rootFrame->WillReflow(mPresContext); nsContainerFrame::PositionFrameView(rootFrame); rootFrame->Reflow(mPresContext, desiredSize, reflowState, status); @@ -3028,6 +2744,9 @@ PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight) NS_ASSERTION(mViewManager, "Must have view manager"); mViewManager->BeginUpdateViewBatch(); + // XXX Do a full invalidate at the beginning so that invalidates along + // the way don't have region accumulation issues? + WillCauseReflow(); WillDoReflow(); @@ -3065,7 +2784,7 @@ PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight) #endif nsRect bounds = mPresContext->GetVisibleArea(); nsSize maxSize(bounds.width, bounds.height); - nsHTMLReflowMetrics desiredSize(nsnull); + nsHTMLReflowMetrics desiredSize; nsReflowStatus status; nsIRenderingContext* rcx = nsnull; @@ -3075,8 +2794,7 @@ PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight) AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow); // XXXldb Set mIsReflowing (and unset it later)? - nsHTMLReflowState reflowState(mPresContext, rootFrame, - eReflowReason_Resize, rcx, maxSize); + nsHTMLReflowState reflowState(mPresContext, rootFrame, rcx, maxSize); rootFrame->WillReflow(mPresContext); nsContainerFrame::PositionFrameView(rootFrame); @@ -3195,8 +2913,7 @@ PresShell::NotifyDestroyingFrame(nsIFrame* aFrame) if (!mIgnoreFrameDestruction) { mFrameConstructor->NotifyDestroyingFrame(aFrame); - // Cancel any pending reflow commands targeted at this frame - CancelReflowCommandInternal(aFrame, nsnull); + mDirtyRoots.RemoveElement(aFrame); // Notify the frame manager FrameManager()->NotifyDestroyingFrame(aFrame); @@ -3509,6 +3226,10 @@ PresShell::StyleChangeReflow() nsIFrame* rootFrame = FrameManager()->GetRootFrame(); if (rootFrame) { + // Mark everything dirty + rootFrame->AddStateBits(NS_FRAME_IS_DIRTY); + FrameNeedsReflow(rootFrame, eStyleChange); + // Kick off a top-down reflow NS_FRAME_LOG(NS_FRAME_TRACE_CALLS, ("enter nsPresShell::StyleChangeReflow")); @@ -3524,7 +3245,7 @@ PresShell::StyleChangeReflow() #endif nsRect bounds = mPresContext->GetVisibleArea(); nsSize maxSize(bounds.width, bounds.height); - nsHTMLReflowMetrics desiredSize(nsnull); + nsHTMLReflowMetrics desiredSize; nsReflowStatus status; nsIRenderingContext* rcx = nsnull; @@ -3534,8 +3255,7 @@ PresShell::StyleChangeReflow() AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow); // XXXldb Set mIsReflowing (and unset it later)? - nsHTMLReflowState reflowState(mPresContext, rootFrame, - eReflowReason_StyleChange, rcx, maxSize); + nsHTMLReflowState reflowState(mPresContext, rootFrame, rcx, maxSize); rootFrame->WillReflow(mPresContext); nsContainerFrame::PositionFrameView(rootFrame); @@ -3696,48 +3416,16 @@ PresShell::EndLoad(nsIDocument *aDocument) mDocumentLoading = PR_FALSE; } -// aReflowCommand is considered to be already in the queue if the -// frame it targets is targeted by a pre-existing reflow command in -// the queue. -PRBool -PresShell::AlreadyInQueue(nsHTMLReflowCommand* aReflowCommand) -{ - if (!mReflowCommandTable.ops) { - // We're already destroyed - NS_ERROR("We really shouldn't be posting reflow commands here"); - } - - ReflowCommandEntry* e = - NS_STATIC_CAST(ReflowCommandEntry*, - PL_DHashTableOperate(&mReflowCommandTable, aReflowCommand, - PL_DHASH_ADD)); - - if (!e) { - // We lie no matter what we say here - return PR_FALSE; - } - - // We're using the stub ClearEntry, which zeros out entries, so a - // non-null mCommand means we're in the queue already. - if (e->mCommand) { -#ifdef DEBUG - if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) { - printf("*** PresShell::AlreadyInQueue(): Discarding reflow command: this=%p\n", (void*)this); - aReflowCommand->List(stdout); - } -#endif - return PR_TRUE; - } - - e->mCommand = aReflowCommand; - return PR_FALSE; -} - NS_IMETHODIMP -PresShell::AppendReflowCommand(nsIFrame* aTargetFrame, - nsReflowType aReflowType, - nsIAtom* aChildListName) +PresShell::FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty) { + NS_PRECONDITION(aFrame->GetStateBits() & + (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN), + "frame not dirty"); + + // XXX Add this assertion at some point!? nsSliderFrame triggers it a lot. + //NS_ASSERTION(!mIsReflowing, "can't mark frame dirty during reflow"); + // If we've not yet done the initial reflow, then don't bother // enqueuing a reflow command yet. if (! mDidInitialReflow) @@ -3752,19 +3440,8 @@ PresShell::AppendReflowCommand(nsIFrame* aTargetFrame, if (mInVerifyReflow) { return NS_OK; } -#endif - - nsHTMLReflowCommand* command = new nsHTMLReflowCommand(aTargetFrame, - aReflowType, - aChildListName); - if (!command) { - return NS_ERROR_OUT_OF_MEMORY; - } - -#ifdef DEBUG if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) { - printf("\nPresShell@%p: adding reflow command\n", (void*)this); - command->List(stdout); + printf("\nPresShell@%p: frame %p needs reflow\n", (void*)this, (void*)aFrame); if (VERIFY_REFLOW_REALLY_NOISY_RC & gVerifyReflowFlags) { printf("Current content model:\n"); nsIContent *rootContent = mDocument->GetRootContent(); @@ -3775,22 +3452,68 @@ PresShell::AppendReflowCommand(nsIFrame* aTargetFrame, } #endif - // Add the reflow command to the queue - nsresult rv = NS_OK; - if (!AlreadyInQueue(command)) { - if (mReflowCommands.AppendElement(command)) { - ReflowCommandAdded(command); - } else { - // Drop this command.... we're out of memory - PL_DHashTableOperate(&mReflowCommandTable, command, - PL_DHASH_REMOVE); - delete command; - rv = NS_ERROR_OUT_OF_MEMORY; + // Mark the intrinsic widths as dirty on the frame, all of its ancestors, + // and all of its descendants, if needed: + + if (aIntrinsicDirty != eResize) { + // Mark argument and all ancestors dirty (unless we hit a reflow root + // other than aFrame) + for (nsIFrame *a = aFrame; + a && (!(a->GetStateBits() & NS_FRAME_REFLOW_ROOT) || a == aFrame); + a = a->GetParent()) + a->MarkIntrinsicWidthsDirty(); + } + + if (aIntrinsicDirty == eStyleChange) { + // Mark all descendants dirty (using an nsVoidArray stack rather than + // recursion). + nsVoidArray stack; + stack.AppendElement(aFrame); + + while (stack.Count() != 0) { + nsIFrame *f = + NS_STATIC_CAST(nsIFrame*, stack.FastElementAt(stack.Count() - 1)); + stack.RemoveElementAt(stack.Count() - 1); + + PRInt32 childListIndex = 0; + nsIAtom *childListName; + do { + childListName = f->GetAdditionalChildListName(childListIndex++); + for (nsIFrame *kid = f->GetFirstChild(childListName); kid; + kid = kid->GetNextSibling()) { + kid->MarkIntrinsicWidthsDirty(); + stack.AppendElement(kid); + } + } while (childListName); } } - else { - // We're not going to process this reflow command. - delete command; + + // Set NS_FRAME_HAS_DIRTY_CHILDREN bits (via nsIFrame::ChildIsDirty) up the + // tree until we reach either a frame that's already dirty or a reflow root. + nsIFrame *f = aFrame; + PRBool wasDirty = PR_TRUE; + for (;;) { + if (((f->GetStateBits() & NS_FRAME_REFLOW_ROOT) && f != aFrame) || + !f->GetParent()) { + // we've hit a reflow root or the root frame + if (!wasDirty) { + NS_ASSERTION(mDirtyRoots.IndexOf(f) == -1, "wasDirty lied"); + mDirtyRoots.AppendElement(f); + } + break; + } + + nsIFrame *child = f; + f = f->GetParent(); + wasDirty = ((f->GetStateBits() & + (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) != 0); + f->ChildIsDirty(child); + NS_ASSERTION(f->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN, + "ChildIsDirty didn't do its job"); + if (wasDirty) { + // This frame was already marked dirty. + break; + } } // Post a reflow event if we are not batching reflow commands. @@ -3803,7 +3526,7 @@ PresShell::AppendReflowCommand(nsIFrame* aTargetFrame, PostReflowEvent(); } - return rv; + return NS_OK; } @@ -3867,57 +3590,12 @@ PresShell::GetViewToScroll(nsLayoutUtils::Direction aDirection) return scrollView; } -void -PresShell::CancelReflowCommandInternal(nsIFrame* aTargetFrame, - nsReflowType* aCmdType) -{ - PRInt32 i, n = mReflowCommands.Count(); - for (i = 0; i < n; i++) { - nsHTMLReflowCommand* rc = (nsHTMLReflowCommand*) mReflowCommands.ElementAt(i); - if (rc && rc->GetTarget() == aTargetFrame && - (!aCmdType || rc->Type() == *aCmdType)) { -#ifdef DEBUG - if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) { - printf("PresShell: removing rc=%p for frame ", (void*)rc); - nsFrame::ListTag(stdout, aTargetFrame); - printf("\n"); - } -#endif - mReflowCommands.RemoveElementAt(i); - ReflowCommandRemoved(rc); - delete rc; - n--; - i--; - } - } - - DoneRemovingReflowCommands(); -} - NS_IMETHODIMP -PresShell::CancelReflowCommand(nsIFrame* aTargetFrame, - nsReflowType* aCmdType) +PresShell::CancelAllPendingReflows() { - CancelReflowCommandInternal(aTargetFrame, aCmdType); - return NS_OK; -} + mDirtyRoots.Clear(); - -NS_IMETHODIMP -PresShell::CancelAllReflowCommands() -{ - PRInt32 n = mReflowCommands.Count(); - nsHTMLReflowCommand* rc; - PRInt32 i; - for (i = 0; i < n; i++) { - rc = NS_STATIC_CAST(nsHTMLReflowCommand*, mReflowCommands.ElementAt(i)); - ReflowCommandRemoved(rc); - delete rc; - } - NS_ASSERTION(n == mReflowCommands.Count(),"reflow command list changed during cancel!"); - mReflowCommands.Clear(); - - DoneRemovingReflowCommands(); + DoneRemovingDirtyRoots(); return NS_OK; } @@ -4908,7 +4586,7 @@ PresShell::UnsuppressPainting() // the reflows and get all the frames where we want them // before actually unlocking the painting. Otherwise // go ahead and unlock now. - if (mReflowCommands.Count() > 0) + if (mDirtyRoots.Count() > 0) mShouldUnsuppressPainting = PR_TRUE; else UnsuppressAndInvalidate(); @@ -6444,9 +6122,15 @@ void PresShell::PostReflowEvent() { if (mReflowEvent.IsPending() || mIsDestroying || mIsReflowing || - mReflowCommands.Count() == 0) + mDirtyRoots.Count() == 0) return; + // Block onload if needed until the event fires + if (mDocumentLoading && !mDocumentOnloadBlocked) { + mDocument->BlockOnload(); + mDocumentOnloadBlocked = PR_TRUE; + } + nsRefPtr ev = new ReflowEvent(this); if (NS_FAILED(NS_DispatchToCurrentThread(ev))) { NS_WARNING("failed to dispatch reflow event"); @@ -6507,8 +6191,8 @@ PresShell::ProcessReflowCommands(PRBool aInterruptible) MOZ_TIMER_DEBUGLOG(("Start: Reflow: PresShell::ProcessReflowCommands(), this=%p\n", this)); MOZ_TIMER_START(mReflowWatch); - if (0 != mReflowCommands.Count()) { - nsHTMLReflowMetrics desiredSize(nsnull); + if (0 != mDirtyRoots.Count()) { + nsHTMLReflowMetrics desiredSize; nsIRenderingContext* rcx; nsIFrame* rootFrame = FrameManager()->GetRootFrame(); nsSize maxSize = rootFrame->GetSize(); @@ -6522,15 +6206,6 @@ PresShell::ProcessReflowCommands(PRBool aInterruptible) printf("ProcessReflowCommands: begin incremental reflow\n"); } } - if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) { - PRInt32 i, n = mReflowCommands.Count(); - printf("\nPresShell::ProcessReflowCommands: this=%p, count=%d\n", (void*)this, n); - for (i = 0; i < n; i++) { - nsHTMLReflowCommand* rc = (nsHTMLReflowCommand*) - mReflowCommands.ElementAt(i); - rc->List(stdout); - } - } #endif WillDoReflow(); @@ -6550,44 +6225,57 @@ PresShell::ProcessReflowCommands(PRBool aInterruptible) mIsReflowing = PR_TRUE; do { - // Coalesce the reflow commands into a tree. - IncrementalReflow reflow; - for (PRInt32 i = mReflowCommands.Count() - 1; i >= 0; --i) { - nsHTMLReflowCommand *command = - NS_STATIC_CAST(nsHTMLReflowCommand *, mReflowCommands[i]); + // Send an incremental reflow notification to the target frame. + PRInt32 idx = mDirtyRoots.Count() - 1; + nsIFrame *target = NS_STATIC_CAST(nsIFrame*, mDirtyRoots[idx]); + mDirtyRoots.RemoveElementAt(idx); - IncrementalReflow::AddCommandResult res = - reflow.AddCommand(mPresContext, command); - if (res == IncrementalReflow::eEnqueued || - res == IncrementalReflow::eCancel) { - // Remove the command from the queue. - mReflowCommands.RemoveElementAt(i); - ReflowCommandRemoved(command); - if (res == IncrementalReflow::eCancel) - delete command; - } - else { - // The reflow command couldn't be added to the tree; leave - // it in the queue, and we'll handle it next time. -#ifdef DEBUG - printf("WARNING: Couldn't add reflow command, so splitting.\n"); -#endif - } + if (!(target->GetStateBits() & + (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) { + // It's not dirty anymore, which probably means the notification + // was posted in the middle of a reflow (perhaps with a reflow + // root in the middle). Don't do anything. + continue; } -#ifdef DEBUG - if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) { - printf("Incremental reflow tree:\n"); - reflow.Dump(mPresContext); - } -#endif + nsIFrame* root = mPresContext->FrameManager()->GetRootFrame(); - // Dispatch an incremental reflow. - reflow.Dispatch(mPresContext, desiredSize, maxSize, *rcx); + target->WillReflow(mPresContext); + nsContainerFrame::PositionFrameView(target); + + // If the target frame is the root of the frame hierarchy, then + // use all the available space. If it's simply a `reflow root', + // then use the target frame's size as the available space. + nsSize size; + if (target == root) + size = maxSize; + else + size = target->GetSize(); + + nsHTMLReflowState reflowState(mPresContext, target, rcx, size); + + nsReflowStatus status; + target->Reflow(mPresContext, desiredSize, reflowState, status); + + // If an incremental reflow is initiated at a frame other than the + // root frame, then its desired size had better not change! + NS_ASSERTION(target == root || + (desiredSize.width == size.width && + desiredSize.height == size.height), + "non-root frame's desired size changed during an " + "incremental reflow"); + + target->SetSize(nsSize(desiredSize.width, desiredSize.height)); + + nsContainerFrame::SyncFrameViewAfterReflow(mPresContext, target, + target->GetView(), + &desiredSize.mOverflowArea); + + target->DidReflow(mPresContext, nsnull, NS_FRAME_REFLOW_FINISHED); // Keep going until we're out of reflow commands, or we've run // past our deadline. - } while (mReflowCommands.Count() && + } while (mDirtyRoots.Count() && (!aInterruptible || PR_IntervalNow() < deadline)); // XXXwaterson for interruptible reflow, examine the tree and @@ -6600,7 +6288,7 @@ PresShell::ProcessReflowCommands(PRBool aInterruptible) // If any new reflow commands were enqueued during the reflow, // schedule another reflow event to process them. - if (mReflowCommands.Count()) + if (mDirtyRoots.Count()) PostReflowEvent(); #ifdef DEBUG @@ -6631,7 +6319,7 @@ PresShell::ProcessReflowCommands(PRBool aInterruptible) ok ? "ok" : "failed"); } - if (0 != mReflowCommands.Count()) { + if (0 != mDirtyRoots.Count()) { printf("XXX yikes! reflow commands queued during verify-reflow\n"); } } @@ -6639,7 +6327,7 @@ PresShell::ProcessReflowCommands(PRBool aInterruptible) // If there are no more reflow commands in the queue, we'll want // to unblock onload. - DoneRemovingReflowCommands(); + DoneRemovingDirtyRoots(); DidDoReflow(); } @@ -6647,7 +6335,7 @@ PresShell::ProcessReflowCommands(PRBool aInterruptible) MOZ_TIMER_DEBUGLOG(("Stop: Reflow: PresShell::ProcessReflowCommands(), this=%p\n", this)); MOZ_TIMER_STOP(mReflowWatch); - if (mShouldUnsuppressPainting && mReflowCommands.Count() == 0) { + if (mShouldUnsuppressPainting && mDirtyRoots.Count() == 0) { // We only unlock if we're out of reflows. It's pointless // to unlock if reflows are still pending, since reflows // are just going to thrash the frames around some more. By @@ -6665,66 +6353,14 @@ PresShell::ClearReflowEventStatus() mReflowEvent.Forget(); } -nsresult -PresShell::ReflowCommandAdded(nsHTMLReflowCommand* aRC) -{ - - NS_PRECONDITION(mRCCreatedDuringLoad >= 0, "PresShell's reflow command queue is in a bad state."); - if (mDocumentLoading) { - aRC->AddFlagBits(NS_RC_CREATED_DURING_DOCUMENT_LOAD); - mRCCreatedDuringLoad++; - -#ifdef PR_LOGGING - if (PR_LOG_TEST(gLog, PR_LOG_DEBUG)) { - nsIFrame* target; - aRC->GetTarget(target); - - nsIAtom* type = target->GetType(); - NS_ASSERTION(type, "frame didn't override GetType()"); - - nsAutoString typeStr(NS_LITERAL_STRING("unknown")); - if (type) - type->ToString(typeStr); - - PR_LOG(gLog, PR_LOG_DEBUG, - ("presshell=%p, ReflowCommandAdded(%p) target=%p[%s] mRCCreatedDuringLoad=%d\n", - this, aRC, target, NS_ConvertUTF16toUTF8(typeStr).get(), mRCCreatedDuringLoad)); - } -#endif - - if (!mDocumentOnloadBlocked) { - mDocument->BlockOnload(); - mDocumentOnloadBlocked = PR_TRUE; - } - } - return NS_OK; -} - -nsresult -PresShell::ReflowCommandRemoved(nsHTMLReflowCommand* aRC) -{ - NS_PRECONDITION(mReflowCommandTable.ops, "How did that happen?"); - - PL_DHashTableOperate(&mReflowCommandTable, aRC, PL_DHASH_REMOVE); - - NS_PRECONDITION(mRCCreatedDuringLoad >= 0, "PresShell's reflow command queue is in a bad state."); - if (aRC->GetFlagBits() & NS_RC_CREATED_DURING_DOCUMENT_LOAD) { - mRCCreatedDuringLoad--; - - PR_LOG(gLog, PR_LOG_DEBUG, - ("presshell=%p, ReflowCommandRemoved(%p) mRCCreatedDuringLoad=%d\n", - this, aRC, mRCCreatedDuringLoad)); - } - return NS_OK; -} - void -PresShell::DoneRemovingReflowCommands() +PresShell::DoneRemovingDirtyRoots() { // We want to unblock here even if we're destroying, since onload // can well fire with no presentation in sight. So just check // whether we actually blocked onload. - if (mRCCreatedDuringLoad == 0 && mDocumentOnloadBlocked) { + // XXXldb Do we want to readd the mIsReflowing check? + if (mDocumentOnloadBlocked && mDirtyRoots.Count() == 0 && !mIsReflowing) { mDocument->UnblockOnload(PR_FALSE); mDocumentOnloadBlocked = PR_FALSE; } @@ -7552,10 +7188,10 @@ PresShell::DumpReflows() //------------------------------------------------------------- NS_IMETHODIMP -PresShell::CountReflows(const char * aName, PRUint32 aType, nsIFrame * aFrame) +PresShell::CountReflows(const char * aName, nsIFrame * aFrame) { if (mReflowCountMgr) { - mReflowCountMgr->Add(aName, (nsReflowReason)aType, aFrame); + mReflowCountMgr->Add(aName, aFrame); } return NS_OK; @@ -7602,81 +7238,59 @@ ReflowCounter::~ReflowCounter() //------------------------------------------------------------------ void ReflowCounter::ClearTotals() { - for (PRUint32 i=0;iLookUp(kGrandTotalsStr); printf("%25s\t", aTitle); - for (i=0;i 0) { - gTots->Add((nsReflowReason)i, aArray[i]); - } + printf("%d\t", aTotal); + if (gTots != this && aTotal > 0) { + gTots->Add(aTotal); } - printf("%d\n", total); } //------------------------------------------------------------------ -void ReflowCounter::DisplayHTMLTotals(PRUint32 * aArray, const char * aTitle) +void ReflowCounter::DisplayHTMLTotals(PRUint32 aTotal, const char * aTitle) { - // figure total - PRUint32 total = 0; - PRUint32 i; - for (i=0;i
%s
", aTitle); - for (i=0;i
"); - if (aArray[i]) { - fprintf(fd, "%d", aArray[i]); - } else { - fprintf(fd, " "); - } - fprintf(fd, "
"); - if (gTots != this && aArray[i] > 0) { - gTots->Add((nsReflowReason)i, aArray[i]); - } + fprintf(fd, "
%d
\n", aTotal); + + if (gTots != this && aTotal > 0) { + gTots->Add(aTotal); } - fprintf(fd, "
%d
\n", total); } //------------------------------------------------------------------ @@ -7735,7 +7341,7 @@ ReflowCounter * ReflowCountMgr::LookUp(const char * aName) } //------------------------------------------------------------------ -void ReflowCountMgr::Add(const char * aName, nsReflowReason aType, nsIFrame * aFrame) +void ReflowCountMgr::Add(const char * aName, nsIFrame * aFrame) { NS_ASSERTION(aName != nsnull, "Name shouldn't be null!"); @@ -7748,7 +7354,7 @@ void ReflowCountMgr::Add(const char * aName, nsReflowReason aType, nsIFrame * aF NS_ASSERTION(name != nsnull, "null ptr"); PL_HashTableAdd(mCounts, name, counter); } - counter->Add(aType); + counter->Add(); } if ((mDumpFrameByFrameCounts || mPaintFrameByFrameCounts) && @@ -7767,7 +7373,7 @@ void ReflowCountMgr::Add(const char * aName, nsReflowReason aType, nsIFrame * aF // this eliminates extra counts from super classes if (counter != nsnull && counter->mName.EqualsASCII(aName)) { counter->mCount++; - counter->mCounter.Add(aType, 1); + counter->mCounter.Add(1); } } } @@ -7893,14 +7499,8 @@ void ReflowCountMgr::DoGrandTotals() gTots->ClearTotals(); } - static const char * title[] = {"Init", "Incrm", "Resze", "Style", "Dirty", "Total"}; - printf("\t\t\t"); - PRUint32 i; - for (i=0;imName); for (PRInt32 i=0;imCount); - for (PRInt32 inx=0;inx<5;inx++) { - if (inx != 0) printf(","); - printf("%d", counter->mCounter.GetTotalByType(nsReflowReason(inx))); - } + printf("%d", counter->mCounter.GetTotal()); printf("]\n"); nsMemory::Free(name); } @@ -7948,10 +7545,7 @@ PRIntn ReflowCountMgr::DoSingleIndi(PLHashEntry *he, PRIntn i, void *arg) if (counter && !counter->mHasBeenOutput) { char * name = ToNewCString(counter->mName); printf("%s - %p [%d][", name, (void*)counter->mFrame, counter->mCount); - for (PRInt32 inx=0;inx<5;inx++) { - if (inx != 0) printf(","); - printf("%d", counter->mCounter.GetTotalByType(nsReflowReason(inx))); - } + printf("%d", counter->mCounter.GetTotal()); printf("]\n"); nsMemory::Free(name); } @@ -8000,10 +7594,9 @@ void ReflowCountMgr::DoGrandHTMLTotals() gTots->ClearTotals(); } - static const char * title[] = {"Class", "Init", "Incrm", "Resze", "Style", "Dirty", "Total"}; + static const char * title[] = {"Class", "Reflows"}; fprintf(mFD, ""); - PRUint32 i; - for (i=0;i
%s
", title[i]); } fprintf(mFD, "\n"); diff --git a/mozilla/layout/forms/nsComboboxControlFrame.cpp b/mozilla/layout/forms/nsComboboxControlFrame.cpp index c5bd83a471e..eed89790c1d 100644 --- a/mozilla/layout/forms/nsComboboxControlFrame.cpp +++ b/mozilla/layout/forms/nsComboboxControlFrame.cpp @@ -90,14 +90,6 @@ #include "nsLayoutUtils.h" #include "nsDisplayList.h" -#ifdef MOZ_XUL -#include "nsIXULDocument.h" // Temporary fix for Bug 36558 -#endif - -#ifdef DO_NEW_REFLOW -#include "nsIFontMetrics.h" -#endif - NS_IMETHODIMP nsComboboxControlFrame::RedisplayTextEvent::Run() { @@ -285,7 +277,8 @@ if (aReflowState.mComputedWidth != NS_UNCONSTRAINEDSIZE) { \ //------------------------------------------------------ nsComboboxControlFrame::nsComboboxControlFrame(nsStyleContext* aContext) - : nsAreaFrame(aContext) + : nsAreaFrame(aContext), + mDisplayWidth(0) { mListControlFrame = nsnull; mDroppedDown = PR_FALSE; @@ -293,18 +286,6 @@ nsComboboxControlFrame::nsComboboxControlFrame(nsStyleContext* aContext) mButtonFrame = nsnull; mDropdownFrame = nsnull; - mCacheSize.width = kSizeNotSet; - mCacheSize.height = kSizeNotSet; - mCachedAscent = kSizeNotSet; - mCachedMaxElementWidth = kSizeNotSet; - mCachedAvailableSize.width = kSizeNotSet; - mCachedAvailableSize.height = kSizeNotSet; - mCachedUncDropdownSize.width = kSizeNotSet; - mCachedUncDropdownSize.height = kSizeNotSet; - mCachedUncComboSize.width = kSizeNotSet; - mCachedUncComboSize.height = kSizeNotSet; - mItemDisplayWidth = 0; - mInRedisplayText = PR_FALSE; mRecentSelectedIndex = NS_SKIP_NOTIFY_INDEX; @@ -476,23 +457,33 @@ nsComboboxControlFrame::ShowList(nsPresContext* aPresContext, PRBool aShowList) } nsresult -nsComboboxControlFrame::ReflowComboChildFrame(nsIFrame* aFrame, - nsPresContext* aPresContext, - nsHTMLReflowMetrics& aDesiredSize, - const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus, - nscoord aAvailableWidth, - nscoord aAvailableHeight) +nsComboboxControlFrame::ReflowDropdown(nsPresContext* aPresContext, + const nsHTMLReflowState& aReflowState) { - // Constrain the child's width and height to aAvailableWidth and aAvailableHeight - nsSize availSize(aAvailableWidth, aAvailableHeight); - nsHTMLReflowState kidReflowState(aPresContext, aReflowState, aFrame, + // All we want out of it later on, really, is the height of a row, so we + // don't even need to cache mDropdownFrame's ascent or anything. If we don't + // need to reflow it, just bail out here. + if (!aReflowState.ShouldReflowAllKids() && + !(mDropdownFrame->GetStateBits() & (NS_FRAME_IS_DIRTY | + NS_FRAME_HAS_DIRTY_CHILDREN))) { + return NS_OK; + } + + // XXXbz this will, for small-height dropdowns, have extra space on the right + // edge for the scrollbar we don't show... but that's the best we can do here + // for now. + nsSize availSize(aReflowState.availableWidth, NS_UNCONSTRAINEDSIZE); + nsHTMLReflowState kidReflowState(aPresContext, aReflowState, mDropdownFrame, availSize); - kidReflowState.mComputedWidth = aAvailableWidth; - kidReflowState.mComputedHeight = aAvailableHeight; + + // If the dropdown's intrinsic width is narrower than our specified width, + // then expand it out. We want our border-box width to end up the same as + // the dropdown's so account for both sets of mComputedBorderPadding. + kidReflowState.mComputedWidth = PR_MAX(kidReflowState.mComputedWidth, + aReflowState.mComputedWidth); // ensure we start off hidden - if (aReflowState.reason == eReflowReason_Initial) { + if (GetStateBits() & NS_FRAME_FIRST_REFLOW) { nsIView* view = mDropdownFrame->GetView(); nsIViewManager* viewManager = view->GetViewManager(); viewManager->SetViewVisibility(view, nsViewVisibility_kHide); @@ -506,40 +497,19 @@ nsComboboxControlFrame::ReflowComboChildFrame(nsIFrame* aFrame, if (mDroppedDown) { flags = 0; } - nsRect rect = aFrame->GetRect(); - nsresult rv = ReflowChild(aFrame, aPresContext, aDesiredSize, kidReflowState, - rect.x, rect.y, flags, aStatus); + nsRect rect = mDropdownFrame->GetRect(); + nsHTMLReflowMetrics desiredSize; + nsReflowStatus ignoredStatus; + nsresult rv = ReflowChild(mDropdownFrame, aPresContext, desiredSize, + kidReflowState, rect.x, rect.y, flags, + ignoredStatus); // Set the child's width and height to it's desired size - FinishReflowChild(aFrame, aPresContext, &kidReflowState, aDesiredSize, - rect.x, rect.y, flags); + FinishReflowChild(mDropdownFrame, aPresContext, &kidReflowState, + desiredSize, rect.x, rect.y, flags); return rv; } -// Resize the child button frame to the specified size. -void -nsComboboxControlFrame::SetButtonFrameSize(const nsSize& aSize) -{ - // Check that the child frame being resized is an nsGfxButtonControlFrame. - if (mButtonFrame->GetType() == nsLayoutAtoms::gfxButtonControlFrame) { - NS_STATIC_CAST(nsGfxButtonControlFrame*, mButtonFrame)->SetSuggestedSize(aSize); - } else { - // This function should never be called with another frame type. - NS_NOTREACHED("Wrong type in SetButtonFrameSize"); - } -} - -nsresult -nsComboboxControlFrame::GetPrimaryComboFrame(nsPresContext* aPresContext, nsIContent* aContent, nsIFrame** aFrame) -{ - // Get the primary frame from the presentation shell. - nsIPresShell *presShell = aPresContext->GetPresShell(); - if (presShell) { - *aFrame = presShell->GetPrimaryFrameFor(aContent); - } - return NS_OK; -} - nsresult nsComboboxControlFrame::PositionDropdown(nsPresContext* aPresContext, nscoord aHeight, @@ -591,346 +561,6 @@ nsComboboxControlFrame::PositionDropdown(nsPresContext* aPresContext, return rv; } - -//////////////////////////////////////////////////////////////// -// Experimental Reflow -//////////////////////////////////////////////////////////////// -#if defined(DO_NEW_REFLOW) || defined(DO_REFLOW_COUNTER) -//--------------------------------------------------------- -// Returns the nsIDOMHTMLOptionElement for a given index -// in the select's collection -//--------------------------------------------------------- -static already_AddRefed -GetOption(nsIDOMHTMLOptionsCollection* aCollection, PRInt32 aIndex) -{ - nsIDOMHTMLOptionElement* option = nsnull; - - nsCOMPtr node; - if (NS_SUCCEEDED(aCollection->Item(aIndex, getter_AddRefs(node))) && node) { - CallQueryInterface(node, &option); - } - - return option; -} -//--------------------------------------------------------- -//--------------------------------------------------------- -// This returns the collection for nsIDOMHTMLSelectElement or -// the nsIContent object is the select is null (AddRefs) -//--------------------------------------------------------- -static already_AddRefed -GetOptions(nsIContent * aContent) -{ - nsIDOMHTMLOptionsCollection* options = nsnull; - nsCOMPtr selectElement = do_QueryInterface(aContent); - if (selectElement) { - selectElement->GetOptions(&options); // AddRefs (1) - } - return options; -} - -#ifdef DO_NEW_REFLOW -NS_IMETHODIMP -nsComboboxControlFrame::ReflowItems(nsPresContext* aPresContext, - const nsHTMLReflowState& aReflowState, - nsHTMLReflowMetrics& aDesiredSize) -{ - //printf("*****************\n"); - nscoord visibleHeight = 0; - nsCOMPtr fontMet; - nsresult res = - nsLayoutUtils::GetFontMetricsForFrame(mDisplayFrame, - getter_AddRefs(fontMet)); - if (fontMet) { - fontMet->GetHeight(visibleHeight); - } - - nsAutoString maxStr; - nscoord maxWidth = 0; - //nsIRenderingContext * rc = aReflowState.rendContext; - nsresult rv = NS_ERROR_FAILURE; - nsCOMPtr options = GetOptions(mContent); - if (options) { - PRUint32 numOptions; - options->GetLength(&numOptions); - //printf("--- Num of Items %d ---\n", numOptions); - for (PRUint32 i=0;i optionElement = GetOption(options, i); - if (optionElement) { - nsAutoString text; - optionElement->GetLabel(text); - if (text.IsEmpty() && - NS_SUCCEEDED(optionElement->GetText(text))) { - nscoord width; - aReflowState.rendContext->GetWidth(text, width); - if (width > maxWidth) { - maxStr = text; - maxWidth = width; - } - //maxWidth = PR_MAX(width, maxWidth); - //printf("[%d] - %d %s \n", i, width, NS_LossyConvertUTF16toASCII(text).get()); - } - } - } - } - if (maxWidth == 0) { - maxWidth = 11 * 15; - } - char * str = ToNewCString(maxStr); - printf("id: %d maxWidth %d [%s]\n", mReflowId, maxWidth, str); - delete [] str; - - // get the borderPadding for the display area - nsMargin dspBorderPadding(0, 0, 0, 0); - mDisplayFrame->CalcBorderPadding(dspBorderPadding); - - nscoord frmWidth = maxWidth+dspBorderPadding.left+dspBorderPadding.right+ - aReflowState.mComputedBorderPadding.left + aReflowState.mComputedBorderPadding.right; - nscoord frmHeight = visibleHeight+dspBorderPadding.top+dspBorderPadding.bottom+ - aReflowState.mComputedBorderPadding.top + aReflowState.mComputedBorderPadding.bottom; - -#if 0 - aDesiredSize.width = frmWidth; - aDesiredSize.height = frmHeight; -#else - printf("Size frm:%d,%d DS:%d,%d DIF:%d,%d(tp) %d,%d(px)\n", - frmWidth, frmHeight, - aDesiredSize.width, aDesiredSize.height, - frmWidth-aDesiredSize.width, frmHeight-aDesiredSize.height, - (frmWidth-aDesiredSize.width)/15, (frmHeight-aDesiredSize.height)/15); -#endif - return NS_OK; -} -#endif - -#endif - -//------------------------------------------------------------------ -// This Method reflow just the contents of the ComboBox -// The contents are a Block frame containing a Text Frame - This is the display area -// and then the GfxButton - The dropdown button -//-------------------------------------------------------------------------- -void -nsComboboxControlFrame::ReflowCombobox(nsPresContext * aPresContext, - const nsHTMLReflowState& aReflowState, - nsHTMLReflowMetrics& aDesiredSize, - nsReflowStatus& aStatus, - nsIFrame * aDisplayFrame, - nscoord& aDisplayWidth, - nscoord aBtnWidth, - const nsMargin& aBorderPadding, - nscoord aFallBackHgt, - PRBool aCheckHeight) -{ - // start out by using the cached height - // XXX later this will change when we better handle constrained height - nscoord dispHeight = mCacheSize.height - aBorderPadding.top - aBorderPadding.bottom; - nscoord dispWidth = aDisplayWidth; - - REFLOW_NOISY_MSG3("+++1 AdjustCombo DW:%d DH:%d ", PX(dispWidth), PX(dispHeight)); - REFLOW_NOISY_MSG3("BW:%d BH:%d ", PX(aBtnWidth), PX(dispHeight)); - REFLOW_NOISY_MSG3("mCacheSize.height:%d - %d\n", PX(mCacheSize.height), PX((aBorderPadding.top + aBorderPadding.bottom))); - - // get the border and padding for the DisplayArea (block frame & textframe) - nsMargin dspBorderPadding(0, 0, 0, 0); - mDisplayFrame->CalcBorderPadding(dspBorderPadding); - - // adjust the height - if (mCacheSize.height == kSizeNotSet) { - if (aFallBackHgt == kSizeNotSet) { - NS_ASSERTION(aFallBackHgt != kSizeNotSet, "Fallback can't be kSizeNotSet when mCacheSize.height == kSizeNotSet"); - } else { - dispHeight = aFallBackHgt; - REFLOW_NOISY_MSG2("+++3 Adding (dspBorderPadding.top + dspBorderPadding.bottom): %d\n", (dspBorderPadding.top + dspBorderPadding.bottom)); - dispHeight += (dspBorderPadding.top + dspBorderPadding.bottom); - } - } - - // Fix for Bug 58220 (part of it) - // make sure we size correctly if the CSS width is set to something really small like 0, 1, or 2 pixels - nscoord computedWidth = aReflowState.mComputedWidth + aBorderPadding.left + aBorderPadding.right; - if ((aReflowState.mComputedWidth != NS_UNCONSTRAINEDSIZE && computedWidth <= 0) || aReflowState.mComputedWidth == 0) { - nsRect buttonRect(0,0,0,0); - nsRect displayRect(0,0,0,0); - aBtnWidth = 0; - aDisplayFrame->SetRect(displayRect); - mButtonFrame->SetRect(buttonRect); - SetButtonFrameSize(nsSize(aBtnWidth, aDesiredSize.height)); - aDesiredSize.width = 0; - aDesiredSize.height = dispHeight + aBorderPadding.top + aBorderPadding.bottom; - // XXX What about ascent and descent? - return; - } - - REFLOW_NOISY_MSG3("+++2 AdjustCombo DW:%d DH:%d ", PX(dispWidth), PX(dispHeight)); - REFLOW_NOISY_MSG3(" BW:%d BH:%d\n", PX(aBtnWidth), PX(dispHeight)); - - // This sets the button to be a specific size - // so no matter what it reflows at these values - SetButtonFrameSize(nsSize(aBtnWidth, dispHeight)); - -#ifdef FIX_FOR_BUG_53259 - // Make sure we obey min/max-width and min/max-height - if (dispWidth > aReflowState.mComputedMaxWidth) { - dispWidth = aReflowState.mComputedMaxWidth - aBorderPadding.left - aBorderPadding.right; - } - if (dispWidth < aReflowState.mComputedMinWidth) { - dispWidth = aReflowState.mComputedMinWidth - aBorderPadding.left - aBorderPadding.right; - } - - if (dispHeight > aReflowState.mComputedMaxHeight) { - dispHeight = aReflowState.mComputedMaxHeight - aBorderPadding.top - aBorderPadding.bottom; - } - if (dispHeight < aReflowState.mComputedMinHeight) { - dispHeight = aReflowState.mComputedMinHeight - aBorderPadding.top - aBorderPadding.bottom; - } -#endif - - // Make sure we get the reflow reason right. If an incremental - // reflow arrives that's targeted directly at the top-level combobox - // frame, then we can't pass it down to the children ``as is'': - // we're the last frame in the reflow command's chain. So, convert - // it to a resize reflow. - nsReflowReason reason = aReflowState.reason; - if (reason == eReflowReason_Incremental) { - if (aReflowState.path->mReflowCommand) - reason = eReflowReason_Resize; - } - - // now that we know what the overall display width & height will be - // set up a new reflow state and reflow the area frame at that size - nsSize availSize(dispWidth + aBorderPadding.left + aBorderPadding.right, - dispHeight + aBorderPadding.top + aBorderPadding.bottom); - nsHTMLReflowState kidReflowState(aReflowState); - kidReflowState.availableWidth = availSize.width; - kidReflowState.availableHeight = availSize.height; - kidReflowState.mComputedWidth = dispWidth; - kidReflowState.mComputedHeight = dispHeight; - kidReflowState.reason = reason; - -#ifdef IBMBIDI - const nsStyleVisibility* vis = GetStyleVisibility(); - - // M14 didn't calculate the RightEdge in the reflow - // Unless we set the width to some thing other than unrestricted - // the code changed this may not be the best place to put it - // in this->Reflow like this : - // - // Reflow display + button - // nsAreaFrame::Reflow(aPresContext, aDesiredSize, firstPassState, aStatus); - - if (vis->mDirection == NS_STYLE_DIRECTION_RTL) - { - kidReflowState.mComputedWidth = 0; - } -#endif // IBMBIDI - - // do reflow - nsAreaFrame::Reflow(aPresContext, aDesiredSize, kidReflowState, aStatus); - - ///////////////////////////////////////////////////////// - // The DisplayFrame is a Block frame containing a TextFrame - // and it is completely anonymous, so we must manually reflow it - nsHTMLReflowMetrics txtKidSize(PR_TRUE); - nsSize txtAvailSize(dispWidth - aBtnWidth, dispHeight); - nsHTMLReflowState txtKidReflowState(aPresContext, aReflowState, aDisplayFrame, txtAvailSize, reason); - - aDisplayFrame->WillReflow(aPresContext); - //aDisplayFrame->SetPosition(nsPoint(dspBorderPadding.left + aBorderPadding.left, dspBorderPadding.top + aBorderPadding.top)); - aDisplayFrame->SetPosition(nsPoint(aBorderPadding.left, aBorderPadding.top)); - nsAreaFrame::PositionFrameView(aDisplayFrame); - nsReflowStatus status; - nsresult rv = aDisplayFrame->Reflow(aPresContext, txtKidSize, txtKidReflowState, status); - if (NS_FAILED(rv)) return; - - ///////////////////////////////////////////////////////// - // If we are Constrained then the AreaFrame Reflow is the correct size - // if we are unconstrained then - //if (aReflowState.mComputedWidth == NS_UNCONSTRAINEDSIZE) { - // aDesiredSize.width += txtKidSize.width; - //} - - // Apparently, XUL lays out differently than HTML - // (the code above works for HTML and not XUL), - // so instead of using the above calculation - // I just set it to what it should be. - aDesiredSize.width = availSize.width; - //aDesiredSize.height = availSize.height; - - // now we need to adjust layout, because the AreaFrame - // doesn't position things exactly where we want them - nscoord insideHeight = aDesiredSize.height - aBorderPadding.top - aBorderPadding.bottom; - - // If the css width has been set to something very small - //i.e. smaller than the dropdown button, set the button's width to zero - if (aBtnWidth > dispWidth) { - aBtnWidth = 0; - } - // set the display rect to be left justifed and - // fills the entire area except the button - nscoord x = aBorderPadding.left; - nsRect displayRect(x, aBorderPadding.top, PR_MAX(dispWidth - aBtnWidth, 0), insideHeight); - aDisplayFrame->SetRect(displayRect); - x += displayRect.width; - - // right justify the button - nsRect buttonRect(x, aBorderPadding.top, aBtnWidth, insideHeight); -#ifdef IBMBIDI - if (vis->mDirection == NS_STYLE_DIRECTION_RTL) - { - if (buttonRect.x > displayRect.x) - { - buttonRect.x = displayRect.x; - displayRect.x += buttonRect.width; - aDisplayFrame->SetRect(displayRect); - } - } -#endif // IBMBIDI - mButtonFrame->SetRect(buttonRect); - - // since we have changed the height of the button - // make sure it has these new values - SetButtonFrameSize(nsSize(aBtnWidth, aDesiredSize.height)); - - // This is a last minute adjustment, if the CSS width was set and - // we calculated it to be a little big, then make sure we are no bigger the computed size - // this only comes into play when the css width has been set to something smaller than - // the dropdown arrow - if (aReflowState.mComputedWidth != NS_UNCONSTRAINEDSIZE && aDesiredSize.width > computedWidth) { - aDesiredSize.width = computedWidth; - } - - REFLOW_NOISY_MSG3("**AdjustCombobox - Reflow: WW: %d HH: %d\n", aDesiredSize.width, aDesiredSize.height); - - if (aDesiredSize.mComputeMEW) { - aDesiredSize.SetMEWToActualWidth(aReflowState.mStylePosition->mWidth.GetUnit()); - } - - aDesiredSize.ascent = - txtKidSize.ascent + aReflowState.mComputedBorderPadding.top; - aDesiredSize.descent = aDesiredSize.height - aDesiredSize.ascent; - - // Now cache the available height as our height without border and padding - // This sets up the optimization for if a new available width comes in and we are equal or - // less than it we can bail - if (aDesiredSize.width != mCacheSize.width || aDesiredSize.height != mCacheSize.height) { - if (aReflowState.availableWidth != NS_UNCONSTRAINEDSIZE) { - mCachedAvailableSize.width = aDesiredSize.width - (aBorderPadding.left + aBorderPadding.right); - } - if (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE) { - mCachedAvailableSize.height = aDesiredSize.height - (aBorderPadding.top + aBorderPadding.bottom); - } - nsFormControlFrame::SetupCachedSizes(mCacheSize, mCachedAscent, - mCachedMaxElementWidth, aDesiredSize); - } - - /////////////////////////////////////////////////////////////////// - // This is an experimental reflow that is turned off in the build -#ifdef DO_NEW_REFLOW - ReflowItems(aPresContext, aReflowState, aDesiredSize); -#endif - /////////////////////////////////////////////////////////////////// -} - //---------------------------------------------------------- // //---------------------------------------------------------- @@ -951,109 +581,61 @@ static void printSize(char * aDesc, nscoord aSize) //------------------------------------------------------------------- //-- Main Reflow for the Combobox //------------------------------------------------------------------- + +nscoord +nsComboboxControlFrame::GetMinWidth(nsIRenderingContext *aRenderingContext) +{ + // Note: to fix the combobox equivalent of bug 40596 while still working + // correctly in general, we want to return our preferred width as our min + // width if our style width is auto. Otherwise, we're ok with shrinking as + // small as needed. + nscoord result; + DISPLAY_MIN_WIDTH(this, result); + + if (GetStylePosition()->mWidth.GetUnit() == eStyleUnit_Auto) { + result = GetPrefWidth(aRenderingContext); + } else { + result = 0; + } + + return result; +} + +nscoord +nsComboboxControlFrame::GetPrefWidth(nsIRenderingContext *aRenderingContext) +{ + nscoord result; + DISPLAY_PREF_WIDTH(this, result); + + if (NS_LIKELY(mDropdownFrame != nsnull)) { + result = mDropdownFrame->GetPrefWidth(aRenderingContext); + } else { + result = 0; + } + + return result; +} + NS_IMETHODIMP nsComboboxControlFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { - DO_GLOBAL_REFLOW_COUNT("nsComboboxControlFrame", aReflowState.reason); - DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); + // Constraints we try to satisfy: - aStatus = NS_FRAME_COMPLETE; + // 1) Default width of button is the vertical scrollbar size + // 2) If the width of button is bigger than our width, set width of + // button to 0. + // 3) Default height of button is height of display area + // 4) Width of display area is whatever is left over from our width after + // allocating width for the button. + // 5) Height of display area is GetHeightOfARow() on the + // mListControlFrame. - REFLOW_COUNTER_REQUEST(); - -#ifdef DO_REFLOW_DEBUG - printf("-------------Starting Combobox Reflow ----------------------------\n"); - printf("%p ** Id: %d nsCCF::Reflow %d R: ", this, mReflowId, myCounter++); - switch (aReflowState.reason) { - case eReflowReason_Initial: - printf("Ini");break; - case eReflowReason_Incremental: - printf("Inc");break; - case eReflowReason_Resize: - printf("Rsz");break; - case eReflowReason_StyleChange: - printf("Sty");break; - case eReflowReason_Dirty: - printf("Drt "); - break; - default:printf("%d", aReflowState.reason);break; - } - - printSize("AW", aReflowState.availableWidth); - printSize("AH", aReflowState.availableHeight); - printSize("CW", aReflowState.mComputedWidth); - printSize("CH", aReflowState.mComputedHeight); - - nsCOMPtr optionsTemp = GetOptions(mContent); - PRUint32 numOptions; - optionsTemp->GetLength(&numOptions); - printSize("NO", (nscoord)numOptions); - - printf(" *\n"); - -#endif - - - PRBool bailOnWidth; - PRBool bailOnHeight; - - // Do initial check to see if we can bail out - // If it is an Initial or Incremental Reflow we never bail out here - // XXX right now we only bail if the width meets the criteria - // - // We bail: - // if mComputedWidth == NS_UNCONSTRAINEDSIZE and - // availableWidth == NS_UNCONSTRAINEDSIZE and - // we have cached an available size - // - // We bail: - // if mComputedWidth == NS_UNCONSTRAINEDSIZE and - // availableWidth != NS_UNCONSTRAINEDSIZE and - // availableWidth minus its border equals our cached available size - // - // We bail: - // if mComputedWidth != NS_UNCONSTRAINEDSIZE and - // cached availableSize.width == aReflowState.mComputedWidth and - // cached AvailableSize.width == aCacheSize.width - // - // NOTE: this returns whether we are doing an Incremental reflow - nsFormControlFrame::SkipResizeReflow(mCacheSize, - mCachedAscent, - mCachedMaxElementWidth, - mCachedAvailableSize, - aDesiredSize, aReflowState, - aStatus, - bailOnWidth, bailOnHeight); - if (bailOnWidth) { -#ifdef DO_REFLOW_DEBUG // check or size - nsMargin borderPadding(0, 0, 0, 0); - CalcBorderPadding(borderPadding); - UNCONSTRAINED_CHECK(); -#endif - REFLOW_DEBUG_MSG3("^** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height)); - NS_ASSERTION(aDesiredSize.width != kSizeNotSet, "aDesiredSize.width != kSizeNotSet"); - NS_ASSERTION(aDesiredSize.height != kSizeNotSet, "aDesiredSize.height != kSizeNotSet"); - aDesiredSize.mOverflowArea.x = 0; - aDesiredSize.mOverflowArea.y = 0; - aDesiredSize.mOverflowArea.width = aDesiredSize.width; - aDesiredSize.mOverflowArea.height = aDesiredSize.height; - FinishAndStoreOverflow(&aDesiredSize); - return NS_OK; - } - - // Go get all of the important frame - nsresult rv = NS_OK; - // Don't try to do any special sizing and positioning unless all of the frames - // have been created. - if ((nsnull == mDisplayFrame) || - (nsnull == mButtonFrame) || - (nsnull == mDropdownFrame)) - { - // Since combobox frames are missing just do a normal area frame reflow - return nsAreaFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); + if (!mDisplayFrame || !mButtonFrame || !mDropdownFrame) { + NS_ERROR("Why did the frame constructor allow this to happen? Fix it!!"); + return NS_ERROR_UNEXPECTED; } // Make sure the displayed text is the same as the selected option, bug 297389. @@ -1074,411 +656,72 @@ nsComboboxControlFrame::Reflow(nsPresContext* aPresContext, RedisplayText(selectedIndex); } - // We should cache this instead getting it everytime - // the default size of the of scrollbar - // that will be the default width of the dropdown button - // the height will be the height of the text + // First reflow our dropdown so that we know how tall we should be. + ReflowDropdown(aPresContext, aReflowState); + + // Get the default size of the scrollbar. + // That will be the default width of the dropdown button. + // The height will be the height of the text + // Can we cache this in a meaningful way? float w, h; // Get the width in Device pixels times p2t aPresContext->DeviceContext()->GetScrollBarDimensions(w, h); - nscoord scrollbarWidth = NSToCoordRound(w); - - // set up a new reflow state for use throughout - nsHTMLReflowState firstPassState(aReflowState); - nsHTMLReflowMetrics dropdownDesiredSize(nsnull); - // Check to see if this a fully unconstrained reflow - PRBool fullyUnconstrained = firstPassState.mComputedWidth == NS_UNCONSTRAINEDSIZE; + nscoord buttonWidth = NSToCoordRound(w); - PRBool forceReflow = PR_FALSE; - - // Only reflow the display and button - // if they are the target of the incremental reflow, unless they change size. - if (eReflowReason_Incremental == aReflowState.reason) { - nsHTMLReflowCommand *command = firstPassState.path->mReflowCommand; - - // Check to see if we are the target of the Incremental Reflow - if (command) { - // We need to check here to see if we can get away with just reflowing - // the combobox and not the dropdown - REFLOW_DEBUG_MSG("-----------------Target is Combobox------------\n"); - - // If the mComputedWidth matches our cached display width - // then we get away with bailing out - PRBool doFullReflow = firstPassState.mComputedWidth != NS_UNCONSTRAINEDSIZE && - firstPassState.mComputedWidth != mItemDisplayWidth; - if (!doFullReflow) { - // OK, so we got lucky and the size didn't change - // so do a simple reflow and bail out - REFLOW_DEBUG_MSG("------------Reflowing AreaFrame and bailing----\n\n"); - ReflowCombobox(aPresContext, firstPassState, aDesiredSize, aStatus, - mDisplayFrame, mItemDisplayWidth, - scrollbarWidth, aReflowState.mComputedBorderPadding); - REFLOW_COUNTER(); - UNCONSTRAINED_CHECK(); - REFLOW_DEBUG_MSG3("&** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height)); - NS_ASSERTION(aDesiredSize.width != kSizeNotSet, "aDesiredSize.width != kSizeNotSet"); - NS_ASSERTION(aDesiredSize.height != kSizeNotSet, "aDesiredSize.height != kSizeNotSet"); - aDesiredSize.mOverflowArea.x = 0; - aDesiredSize.mOverflowArea.y = 0; - aDesiredSize.mOverflowArea.width = aDesiredSize.width; - aDesiredSize.mOverflowArea.height = aDesiredSize.height; - } - else { - // Nope, something changed that affected our size - // so we need to do a full reflow and resize ourself - REFLOW_DEBUG_MSG("------------Do Full Reflow----\n\n"); - firstPassState.reason = eReflowReason_StyleChange; - firstPassState.path = nsnull; - forceReflow = PR_TRUE; - } - } - - // See if any of the children are targets, as well. - nsReflowPath::iterator iter = aReflowState.path->FirstChild(); - nsReflowPath::iterator end = aReflowState.path->EndChildren(); - for ( ; iter != end; ++iter) { - // Now, see if our target is the dropdown - // If so, maybe an items was added or some style changed etc. - // OR - // We get an Incremental reflow on the dropdown when it is being - // shown or hidden. - if (*iter == mDropdownFrame) { - REFLOW_DEBUG_MSG("---------Target is Dropdown (Clearing Unc DD Size)---\n"); - // Nope, we were unlucky so now we do a full reflow - mCachedUncDropdownSize.width = kSizeNotSet; - mCachedUncDropdownSize.height = kSizeNotSet; - REFLOW_DEBUG_MSG("---- Doing Full Reflow\n"); - // This is an incremental reflow targeted at the dropdown list - // and it didn't have anything to do with being show or hidden. - // - // The incremental reflow will not get to the dropdown list - // because it is in the "popup" list - // when this flow of control drops out of this if it will do a reflow - // on the AreaFrame which SHOULD make it get tothe drop down - // except that it is in the popup list, so we have it reflowed as - // a StyleChange, this is not as efficient as doing an Incremental - // - // At this point we want to by pass the reflow optimization in the dropdown - // because we aren't why it is getting an incremental reflow, but we do - // know that it needs to be resized or restyled - //mListControlFrame->SetOverrideReflowOptimization(PR_TRUE); - - } else if (*iter == mDisplayFrame || *iter == mButtonFrame) { - REFLOW_DEBUG_MSG2("-----------------Target is %s------------\n", (*iter == mDisplayFrame?"DisplayItem Frame":"DropDown Btn Frame")); - // The incremental reflow is targeted at either the block or the button - REFLOW_DEBUG_MSG("---- Doing AreaFrame Reflow and then bailing out\n"); - // Do simple reflow and bail out - ReflowCombobox(aPresContext, firstPassState, aDesiredSize, aStatus, - mDisplayFrame, mItemDisplayWidth, scrollbarWidth, - aReflowState.mComputedBorderPadding, - kSizeNotSet, PR_TRUE); - REFLOW_DEBUG_MSG3("+** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height)); - REFLOW_COUNTER(); - UNCONSTRAINED_CHECK(); - NS_ASSERTION(aDesiredSize.width != kSizeNotSet, "aDesiredSize.width != kSizeNotSet"); - NS_ASSERTION(aDesiredSize.height != kSizeNotSet, "aDesiredSize.height != kSizeNotSet"); - aDesiredSize.mOverflowArea.x = 0; - aDesiredSize.mOverflowArea.y = 0; - aDesiredSize.mOverflowArea.width = aDesiredSize.width; - aDesiredSize.mOverflowArea.height = aDesiredSize.height; - continue; - } else { - nsIFrame * plainLstFrame; - if (NS_SUCCEEDED(mListControlFrame->QueryInterface(NS_GET_IID(nsIFrame), (void**)&plainLstFrame))) { - nsIFrame * frame = plainLstFrame->GetFirstChild(nsnull); - nsIScrollableFrame * scrollFrame; - if (NS_SUCCEEDED(frame->QueryInterface(NS_GET_IID(nsIScrollableFrame), (void**)&scrollFrame))) { - plainLstFrame->Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); - - aDesiredSize.width = mCacheSize.width; - aDesiredSize.height = mCacheSize.height; - aDesiredSize.ascent = mCachedAscent; - aDesiredSize.descent = aDesiredSize.height - aDesiredSize.ascent; - - if (aDesiredSize.mComputeMEW) { - aDesiredSize.mMaxElementWidth = mCachedMaxElementWidth; - } - NS_ASSERTION(aDesiredSize.width != kSizeNotSet, "aDesiredSize.width != kSizeNotSet"); - NS_ASSERTION(aDesiredSize.height != kSizeNotSet, "aDesiredSize.height != kSizeNotSet"); - aDesiredSize.mOverflowArea.x = 0; - aDesiredSize.mOverflowArea.y = 0; - aDesiredSize.mOverflowArea.width = aDesiredSize.width; - aDesiredSize.mOverflowArea.height = aDesiredSize.height; - continue; - } - } - - // Here the target of the reflow was a child of the dropdown list - // so we must do a full reflow - REFLOW_DEBUG_MSG("-----------------Target is Dropdown's Child (Option Item)------------\n"); - REFLOW_DEBUG_MSG("---- Doing Reflow as StyleChange\n"); - } - firstPassState.reason = eReflowReason_StyleChange; - firstPassState.path = nsnull; - mListControlFrame->SetOverrideReflowOptimization(PR_TRUE); - forceReflow = PR_TRUE; - } + if (buttonWidth > aReflowState.mComputedWidth) { + buttonWidth = 0; } -#ifdef IBMBIDI - else if (eReflowReason_StyleChange == aReflowState.reason) { - forceReflow = PR_TRUE; + mDisplayWidth = aReflowState.mComputedWidth - buttonWidth; + + nsresult rv = nsAreaFrame::Reflow(aPresContext, aDesiredSize, aReflowState, + aStatus); + NS_ENSURE_SUCCESS(rv, rv); + + // Now set the correct width and height on our button. The width we need to + // set always, the height only if we had an auto height. + nsRect buttonRect = mButtonFrame->GetRect(); + // If we have a non-intrinsic computed height, our kids should have sized + // themselves properly on their own. + if (aReflowState.mComputedHeight == NS_INTRINSICSIZE) { + // The display frame is going to be the right height and width at this + // point. Use its height as the button height. + nsRect displayRect = mDisplayFrame->GetRect(); + buttonRect.height = displayRect.height; + buttonRect.y = displayRect.y; } -#endif // IBMBIDI - - // Here is another special optimization - // Only reflow the dropdown if it has never been reflowed unconstrained - // - // Or someone up above here may want to force it to be reflowed - // by setting one or both of these to kSizeNotSet - if ((mCachedUncDropdownSize.width == kSizeNotSet && - mCachedUncDropdownSize.height == kSizeNotSet) || forceReflow) { - REFLOW_DEBUG_MSG3("---Re %d,%d\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height)); - - // Tell it we are doing the first pass, which means it will - // do the unconstained reflow and skip the second reflow this time around - nsListControlFrame * lcf = NS_STATIC_CAST(nsListControlFrame*, mDropdownFrame); - lcf->SetPassId(1); - // A width has not been specified for the select so size the display area - // to match the width of the longest item in the drop-down list. The dropdown - // list has already been reflowed and sized to shrink around its contents above. - ReflowComboChildFrame(mDropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, - aStatus, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); - lcf->SetPassId(0); // reset it back - - if (forceReflow) { - mCachedUncDropdownSize.width = dropdownDesiredSize.width; - mCachedUncDropdownSize.height = dropdownDesiredSize.height; - } - } else { - // Here we pretended we did an unconstrained reflow - // so we set the cached values and continue on - REFLOW_DEBUG_MSG3("--- Using Cached ListBox Size %d,%d\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height)); - dropdownDesiredSize.width = mCachedUncDropdownSize.width; - dropdownDesiredSize.height = mCachedUncDropdownSize.height; +#ifdef DEBUG + else { + NS_ASSERTION(mButtonFrame->GetSize().height == + mDisplayFrame->GetSize().height, + "Different heights?"); } - - ///////////////////////////////////////////////////////////////////////// - // XXX - I need to clean this nect part up a little it is very redundant - - // Check here to if this is a mComputed unconstrained reflow - PRBool computedUnconstrained = firstPassState.mComputedWidth == NS_UNCONSTRAINEDSIZE; - if (computedUnconstrained && !forceReflow) { - // Because Incremental reflows aren't actually getting to the dropdown - // we cache the size from when it did a fully unconstrained reflow - // we then check to see if the size changed at all, - // if not then bail out we don't need to worry - if (mCachedUncDropdownSize.width == kSizeNotSet && mCachedUncDropdownSize.height == kSizeNotSet) { - mCachedUncDropdownSize.width = dropdownDesiredSize.width; - mCachedUncDropdownSize.height = dropdownDesiredSize.height; - REFLOW_DEBUG_MSG3("---1 Caching mCachedUncDropdownSize %d,%d\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height)); - - } else if (mCachedUncDropdownSize.width == dropdownDesiredSize.width && - mCachedUncDropdownSize.height == dropdownDesiredSize.height) { - - if (mCachedUncComboSize.width != kSizeNotSet && mCachedUncComboSize.height != kSizeNotSet) { - REFLOW_DEBUG_MSG3("--- Bailing because of mCachedUncDropdownSize %d,%d\n\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height)); - aDesiredSize.width = mCachedUncComboSize.width; - aDesiredSize.height = mCachedUncComboSize.height; - aDesiredSize.ascent = mCachedAscent; - aDesiredSize.descent = aDesiredSize.height - aDesiredSize.ascent; - - if (aDesiredSize.mComputeMEW) { - aDesiredSize.mMaxElementWidth = mCachedMaxElementWidth; - } - UNCONSTRAINED_CHECK(); - REFLOW_DEBUG_MSG3("#** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height)); - NS_ASSERTION(aDesiredSize.width != kSizeNotSet, "aDesiredSize.width != kSizeNotSet"); - NS_ASSERTION(aDesiredSize.height != kSizeNotSet, "aDesiredSize.height != kSizeNotSet"); - aDesiredSize.mOverflowArea.x = 0; - aDesiredSize.mOverflowArea.y = 0; - aDesiredSize.mOverflowArea.width = aDesiredSize.width; - aDesiredSize.mOverflowArea.height = aDesiredSize.height; - FinishAndStoreOverflow(&aDesiredSize); - return NS_OK; - } - } else { - mCachedUncDropdownSize.width = dropdownDesiredSize.width; - mCachedUncDropdownSize.height = dropdownDesiredSize.height; - } - } - // clean up stops here - ///////////////////////////////////////////////////////////////////////// - - // So this point we know we flowed the dropdown unconstrained - // now we get to figure out how big we need to be and - // - // We don't reflow the combobox here at the new size - // we cache its new size and reflow it on the dropdown - nsSize size; - - // dropdownRect will hold the content size (minus border padding) - // for the display area - nsRect dropdownRect = mDropdownFrame->GetRect(); - if (eReflowReason_Resize == aReflowState.reason) { - dropdownRect.Deflate(aReflowState.mComputedBorderPadding); - } - - // Get maximum size of the largest item in the dropdown - // The height of the display frame will be that height - // the width will be the same as - // the dropdown width (minus its borderPadding) OR - // a caculation off the mComputedWidth from reflow - size = mListControlFrame->GetMaximumSize(); - - // the variable "size" will now be - // the default size of the dropdown btn - if (scrollbarWidth > 0) { - size.width = scrollbarWidth; - } - - // Get the border and padding for the dropdown - nsMargin dropBorderPadding(0, 0, 0, 0); - mDropdownFrame->CalcBorderPadding(dropBorderPadding); - - // get the borderPadding for the display area - nsMargin dspBorderPadding(0, 0, 0, 0); - mDisplayFrame->CalcBorderPadding(dspBorderPadding); - - // Subtract dropdown's borderPadding from the width of the dropdown rect - // to get the size of the content area - // - // the height will come from the mDisplayFrame's height - // declare a size for the item display frame - - //Set the desired size for the button and display frame - if (NS_UNCONSTRAINEDSIZE == firstPassState.mComputedWidth) { - REFLOW_DEBUG_MSG("Unconstrained.....\n"); - REFLOW_DEBUG_MSG4("*B mItemDisplayWidth %d dropdownRect.width:%d dropdownRect.w+h %d\n", PX(mItemDisplayWidth), PX(dropdownRect.width), PX((dropBorderPadding.left + dropBorderPadding.right))); - - // Start with the dropdown rect's width (at this stage, it's the - // natural width of the content in the list, i.e., the width of - // the widest content, i.e. the preferred width for the display - // frame) and add room for the button, which is assumed to match - // the width of the scrollbar (note that the scrollbarWidth is - // passed as aBtnWidth to ReflowCombobox). (When the dropdown was - // an nsScrollFrame the scrollbar width seems to have already been - // added to its unconstrained width.) - mItemDisplayWidth = dropdownRect.width + scrollbarWidth; - - REFLOW_DEBUG_MSG2("* mItemDisplayWidth %d\n", PX(mItemDisplayWidth)); - - // mItemDisplayWidth must be the size of the "display" frame including it's - // border and padding, but NOT including the comboboxes border and padding - mItemDisplayWidth += dspBorderPadding.left + dspBorderPadding.right; - mItemDisplayWidth -= aReflowState.mComputedBorderPadding.left + aReflowState.mComputedBorderPadding.right; - - REFLOW_DEBUG_MSG2("*A mItemDisplayWidth %d\n", PX(mItemDisplayWidth)); - - } else { - REFLOW_DEBUG_MSG("Constrained.....\n"); - if (firstPassState.mComputedWidth > 0) { - // Compute the display item's width from reflow's mComputedWidth - // mComputedWidth has already excluded border and padding - // so subtract off the button's size - REFLOW_DEBUG_MSG3("B mItemDisplayWidth %d %d\n", PX(mItemDisplayWidth), PX(dspBorderPadding.right)); - // Display Frame's width comes from the mComputedWidth and therefore implies that it - // includes the "display" frame's border and padding. - mItemDisplayWidth = firstPassState.mComputedWidth; - REFLOW_DEBUG_MSG2("A mItemDisplayWidth %d\n", PX(mItemDisplayWidth)); - REFLOW_DEBUG_MSG4("firstPassState.mComputedWidth %d - size.width %d dspBorderPadding.right %d\n", PX(firstPassState.mComputedWidth), PX(size.width), PX(dspBorderPadding.right)); - } - } - - // Fix for Bug 44788 (remove this comment later) - if (firstPassState.mComputedHeight > 0 && NS_UNCONSTRAINEDSIZE != firstPassState.mComputedHeight) { - size.height = firstPassState.mComputedHeight; - } - - if (mCacheSize.height != size.height) { - // if the cached height is not equal to the current height, - // the cached height is reset. - mCacheSize.height = kSizeNotSet; - } - - // this reflows and makes and last minute adjustments - ReflowCombobox(aPresContext, firstPassState, aDesiredSize, aStatus, - mDisplayFrame, mItemDisplayWidth, scrollbarWidth, - aReflowState.mComputedBorderPadding, size.height); - - // The dropdown was reflowed UNCONSTRAINED before, now we need to reflow it - // so that all children match the desired width. - // The dropdown MUST always be either the same size as the combo or larger - // if necessary. Note that individual children can be narrower in case they - // are constrained by 'width', 'max-width' etc. - if (eReflowReason_Initial == firstPassState.reason) { - firstPassState.reason = eReflowReason_Resize; - } - REFLOW_DEBUG_MSG3("*** Reflowing ListBox to width: %d it was %d\n", PX(aDesiredSize.width), PX(dropdownDesiredSize.width)); - - // Tell it we are doing the second pass, which means we will skip - // doing the unconstained reflow, we already know that size - nsListControlFrame * lcf = NS_STATIC_CAST(nsListControlFrame*, mDropdownFrame); - lcf->SetPassId(2); - // Reflow the dropdown list to be - // MAX(width of the display + button, width of the widest option). bug 305705. - const nscoord availableWidth = - PR_MAX(aDesiredSize.width, dropdownDesiredSize.width - - aReflowState.mComputedBorderPadding.left - - aReflowState.mComputedBorderPadding.right); - ReflowComboChildFrame(mDropdownFrame, aPresContext, dropdownDesiredSize, - firstPassState, aStatus, - availableWidth, NS_UNCONSTRAINEDSIZE); - lcf->SetPassId(0); - - // Set the max element size to be the same as the desired element size. - if (aDesiredSize.mComputeMEW) { - aDesiredSize.SetMEWToActualWidth(aReflowState.mStylePosition->mWidth.GetUnit()); - } - -#if 0 - COMPARE_QUIRK_SIZE("nsComboboxControlFrame", 127, 22) #endif - - // cache the availabe size to be our desired size minus the borders - // this is so if our cached available size is ever equal to or less - // than the real available size we can bail out - if (aReflowState.availableWidth != NS_UNCONSTRAINEDSIZE) { - mCachedAvailableSize.width = aDesiredSize.width - - (aReflowState.mComputedBorderPadding.left + - aReflowState.mComputedBorderPadding.right); + + if (GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { + // Make sure the right edge of the button frame stays where it is now + buttonRect.x -= buttonWidth - buttonRect.width; } - if (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE) { - mCachedAvailableSize.height = aDesiredSize.height - - (aReflowState.mComputedBorderPadding.top + - aReflowState.mComputedBorderPadding.bottom); - } - - nsFormControlFrame::SetupCachedSizes(mCacheSize, mCachedAscent, mCachedMaxElementWidth, aDesiredSize); - - REFLOW_DEBUG_MSG3("** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height)); - REFLOW_COUNTER(); - UNCONSTRAINED_CHECK(); - - // If this was a fully unconstrained reflow we cache - // the combobox's unconstrained size - if (fullyUnconstrained) { - mCachedUncComboSize.width = aDesiredSize.width; - mCachedUncComboSize.height = aDesiredSize.height; - } - - NS_ASSERTION(aDesiredSize.width != kSizeNotSet, "aDesiredSize.width != kSizeNotSet"); - NS_ASSERTION(aDesiredSize.height != kSizeNotSet, "aDesiredSize.height != kSizeNotSet"); - aDesiredSize.mOverflowArea.x = 0; - aDesiredSize.mOverflowArea.y = 0; - aDesiredSize.mOverflowArea.width = aDesiredSize.width; - aDesiredSize.mOverflowArea.height = aDesiredSize.height; - - FinishAndStoreOverflow(&aDesiredSize); - NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); + buttonRect.width = buttonWidth; + mButtonFrame->SetRect(buttonRect); + return rv; - } //-------------------------------------------------------------- -//-------------------------------------------------------------- +nsIAtom* +nsComboboxControlFrame::GetType() const +{ + return nsLayoutAtoms::comboboxControlFrame; +} + +PRBool +nsComboboxControlFrame::IsFrameOfType(PRUint32 aFlags) const +{ + return !(aFlags & ~(eReplaced | eReplacedContainsBlock)); +} #ifdef NS_DEBUG NS_IMETHODIMP @@ -1606,7 +849,9 @@ nsComboboxControlFrame::HandleRedisplayTextEvent() ActuallyDisplayText(PR_TRUE); mDisplayFrame->AddStateBits(NS_FRAME_IS_DIRTY); - ReflowDirtyChild(GetPresContext()->PresShell(), mDisplayFrame); + // XXXbz This should perhaps be eResize. Check. + GetPresContext()->PresShell()->FrameNeedsReflow(mDisplayFrame, + nsIPresShell::eStyleChange); mInRedisplayText = PR_FALSE; } @@ -1775,8 +1020,6 @@ nsComboboxControlFrame::CreateAnonymousContent(nsPresContext* aPresContext, // // Note: The value attribute of the display content is set when an item is selected in the dropdown list. // If the content specified below does not honor the value attribute than nothing will be displayed. - // In addition, if the frame created by content below for the button is not an nsGfxScrollFrame - // things will go wrong ... see SetButtonFrameSize. // For now the content that is created corresponds to two input buttons. It would be better to create the // tag as something other than input, but then there isn't any way to create a button frame since it @@ -1831,6 +1074,79 @@ nsComboboxControlFrame::CreateAnonymousContent(nsPresContext* aPresContext, return NS_OK; } +// XXXbz this is a for-now hack until display:inline-block works. +class nsComboboxDisplayFrame; + +static nsComboboxDisplayFrame* +NS_NewComboboxDisplayFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, + nsComboboxControlFrame* aComboBox); + +class nsComboboxDisplayFrame : public nsBlockFrame { +public: + friend nsComboboxDisplayFrame* + NS_NewComboboxDisplayFrame(nsIPresShell* aPresShell, + nsStyleContext* aContext, + nsComboboxControlFrame* aComboBox); + + nsComboboxDisplayFrame (nsStyleContext* aContext, + nsComboboxControlFrame* aComboBox) + : nsBlockFrame(aContext), + mComboBox(aComboBox) + {} + + virtual PRBool IsFrameOfType(PRUint32 aFlags) const; + + NS_IMETHOD Reflow(nsPresContext* aPresContext, + nsHTMLReflowMetrics& aDesiredSize, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus); + +protected: + nsComboboxControlFrame* mComboBox; +}; + +PRBool +nsComboboxDisplayFrame::IsFrameOfType(PRUint32 aFlags) const +{ + return !(aFlags & ~(eReplacedContainsBlock)); +} + +NS_IMETHODIMP +nsComboboxDisplayFrame::Reflow(nsPresContext* aPresContext, + nsHTMLReflowMetrics& aDesiredSize, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus) +{ + nsHTMLReflowState state(aReflowState); + if (state.mComputedHeight == NS_INTRINSICSIZE) { + // Note that the only way we can have a computed height here is if the + // combobox had a specified height. If it didn't, size based on what our + // rows look like, for lack of anything better. + state.mComputedHeight = mComboBox->mListControlFrame->GetHeightOfARow(); + } + state.mComputedWidth = mComboBox->mDisplayWidth - + state.mComputedBorderPadding.LeftRight(); + if (state.mComputedWidth < 0) { + state.mComputedWidth = 0; + } + + return nsBlockFrame::Reflow(aPresContext, aDesiredSize, state, aStatus); +} + +static nsComboboxDisplayFrame* +NS_NewComboboxDisplayFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, + nsComboboxControlFrame* aComboBox) +{ + nsComboboxDisplayFrame* it = + new (aPresShell) nsComboboxDisplayFrame(aContext, aComboBox); + + if (it) { + it->SetFlags(NS_BLOCK_SPACE_MGR); + } + + return it; +} + NS_IMETHODIMP nsComboboxControlFrame::CreateFrameFor(nsPresContext* aPresContext, nsIContent * aContent, @@ -1869,7 +1185,7 @@ nsComboboxControlFrame::CreateFrameFor(nsPresContext* aPresContext, } // Start by by creating our anonymous block frame - mDisplayFrame = NS_NewBlockFrame(shell, styleContext, NS_BLOCK_SPACE_MGR); + mDisplayFrame = NS_NewComboboxDisplayFrame(shell, styleContext, this); if (NS_UNLIKELY(!mDisplayFrame)) { return NS_ERROR_OUT_OF_MEMORY; } diff --git a/mozilla/layout/forms/nsComboboxControlFrame.h b/mozilla/layout/forms/nsComboboxControlFrame.h index 80d900a424a..b6ebcf1bd19 100644 --- a/mozilla/layout/forms/nsComboboxControlFrame.h +++ b/mozilla/layout/forms/nsComboboxControlFrame.h @@ -72,6 +72,7 @@ class nsIView; class nsStyleContext; class nsIListControlFrame; class nsIScrollableView; +class nsComboboxDisplayFrame; /** * Child list name indices @@ -90,6 +91,7 @@ class nsComboboxControlFrame : public nsAreaFrame, { public: friend nsIFrame* NS_NewComboboxControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, PRUint32 aFlags); + friend class nsComboboxDisplayFrame; nsComboboxControlFrame(nsStyleContext* aContext); ~nsComboboxControlFrame(); @@ -108,6 +110,10 @@ public: NS_IMETHOD GetAccessible(nsIAccessible** aAccessible); #endif + virtual nscoord GetMinWidth(nsIRenderingContext *aRenderingContext); + + virtual nscoord GetPrefWidth(nsIRenderingContext *aRenderingContext); + NS_IMETHOD Reflow(nsPresContext* aCX, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, @@ -123,6 +129,13 @@ public: void PaintFocus(nsIRenderingContext& aRenderingContext, nsPoint aPt); + // XXXbz this is only needed to prevent the quirk percent height stuff from + // leaking out of the combobox. We may be able to get rid of this as more + // things move to IsFrameOfType. + virtual nsIAtom* GetType() const; + + virtual PRBool IsFrameOfType(PRUint32 aFlags) const; + #ifdef NS_DEBUG NS_IMETHOD GetFrameName(nsAString& aResult) const; #endif @@ -186,20 +199,9 @@ public: protected: -#ifdef DO_NEW_REFLOW - NS_IMETHOD ReflowItems(nsPresContext* aPresContext, - const nsHTMLReflowState& aReflowState, - nsHTMLReflowMetrics& aDesiredSize); -#endif - - // Utilities - nsresult ReflowComboChildFrame(nsIFrame* aFrame, - nsPresContext* aPresContext, - nsHTMLReflowMetrics& aDesiredSize, - const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus, - nscoord aAvailableWidth, - nscoord aAvailableHeight); + // Utilities + nsresult ReflowDropdown(nsPresContext* aPresContext, + const nsHTMLReflowState& aReflowState); public: nsresult PositionDropdown(nsPresContext* aPresContext, @@ -221,26 +223,13 @@ protected: void ShowPopup(PRBool aShowPopup); void ShowList(nsPresContext* aPresContext, PRBool aShowList); - void SetButtonFrameSize(const nsSize& aSize); void CheckFireOnChange(); void FireValueChangeEvent(); nsresult RedisplayText(PRInt32 aIndex); void HandleRedisplayTextEvent(); void ActuallyDisplayText(PRBool aNotify); - nsresult GetPrimaryComboFrame(nsPresContext* aPresContext, nsIContent* aContent, nsIFrame** aFrame); NS_IMETHOD ToggleList(nsPresContext* aPresContext); - void ReflowCombobox(nsPresContext * aPresContext, - const nsHTMLReflowState& aReflowState, - nsHTMLReflowMetrics& aDesiredSize, - nsReflowStatus& aStatus, - nsIFrame * aDisplayFrame, - nscoord& aDisplayWidth, - nscoord aBtnWidth, - const nsMargin& aBorderPadding, - nscoord aFallBackHgt = -1, - PRBool aCheckHeight = PR_FALSE); - nsFrameList mPopupFrames; // additional named child list nsCOMPtr mDisplayContent; // Anonymous content used to display the current selection nsIFrame* mDisplayFrame; // frame to display selection @@ -249,18 +238,10 @@ protected: nsIFrame* mTextFrame; // display area frame nsIListControlFrame * mListControlFrame; // ListControl Interface for the dropdown frame - // Resize Reflow Optimization - nsSize mCacheSize; - nsSize mCachedAvailableSize; - nscoord mCachedMaxElementWidth; - nscoord mCachedAscent; - - nsSize mCachedUncDropdownSize; - nsSize mCachedUncComboSize; - - nscoord mItemDisplayWidth; - //nscoord mItemDisplayHeight; - + // The width of our display area. Used by that frame's reflow to + // size to the full width except the drop-marker. + nscoord mDisplayWidth; + PRPackedBool mDroppedDown; // Current state of the dropdown list, PR_TRUE is dropped down PRPackedBool mInRedisplayText; diff --git a/mozilla/layout/forms/nsFieldSetFrame.cpp b/mozilla/layout/forms/nsFieldSetFrame.cpp index b9f1c8135fd..e2e336b44c4 100644 --- a/mozilla/layout/forms/nsFieldSetFrame.cpp +++ b/mozilla/layout/forms/nsFieldSetFrame.cpp @@ -73,6 +73,17 @@ public: NS_IMETHOD SetInitialChildList(nsIAtom* aListName, nsIFrame* aChildList); + NS_HIDDEN_(nscoord) + GetLegendPrefWidth(nsIRenderingContext* aRenderingContext); + NS_HIDDEN_(nscoord) + GetContentMinWidth(nsIRenderingContext* aRenderingContext); + virtual nscoord GetMinWidth(nsIRenderingContext* aRenderingContext); + virtual nscoord GetPrefWidth(nsIRenderingContext* aRenderingContext); + virtual nsSize ComputeSize(nsIRenderingContext *aRenderingContext, + nsSize aCBSize, nscoord aAvailableWidth, + nsSize aMargin, nsSize aBorder, nsSize aPadding, + PRBool aShrinkWrap); + NS_IMETHOD Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, @@ -311,221 +322,259 @@ nsFieldSetFrame::PaintBorderBackground(nsIRenderingContext& aRenderingContext, } } +// XXXbz This duplicates code in nsLayoutAtoms (near IntrinsicForContainer) + +static nscoord GetCoord(const nsStyleCoord& aCoord, nscoord aIfNotCoord) +{ + return aCoord.GetUnit() == eStyleUnit_Coord + ? aCoord.GetCoordValue() + : aIfNotCoord; +} + +nscoord +nsFieldSetFrame::GetLegendPrefWidth(nsIRenderingContext* aRenderingContext) +{ + NS_ASSERTION(mLegendFrame, "Don't call me if there is no legend frame!"); + + // We don't want to use nsLayoutUtils::IntrinsicForContainer, + // because legends ignore their CSS-specified width. + nscoord result = mLegendFrame->GetPrefWidth(aRenderingContext); + + nsStyleCoord tmp; + + const nsStylePadding *stylePadding = mLegendFrame->GetStylePadding(); + result += GetCoord(stylePadding->mPadding.GetLeft(tmp), 0); + result += GetCoord(stylePadding->mPadding.GetRight(tmp), 0); + + const nsStyleBorder *styleBorder = mLegendFrame->GetStyleBorder(); + result += styleBorder->GetBorderWidth(NS_SIDE_LEFT); + result += styleBorder->GetBorderWidth(NS_SIDE_RIGHT); + + const nsStyleMargin *styleMargin = mLegendFrame->GetStyleMargin(); + result += GetCoord(styleMargin->mMargin.GetLeft(tmp), 0); + result += GetCoord(styleMargin->mMargin.GetRight(tmp), 0); + + return result; +} + +nscoord +nsFieldSetFrame::GetContentMinWidth(nsIRenderingContext* aRenderingContext) +{ + NS_ASSERTION(mContentFrame, "Don't call me if there is no legend frame!"); + + return nsLayoutUtils::IntrinsicForContainer(aRenderingContext, mContentFrame, + nsLayoutUtils::MIN_WIDTH); +} + +nscoord +nsFieldSetFrame::GetMinWidth(nsIRenderingContext* aRenderingContext) +{ + nscoord result = 0; + DISPLAY_MIN_WIDTH(this, result); + + nscoord legendPrefWidth = 0; + nscoord contentMinWidth = 0; + if (mLegendFrame) { + legendPrefWidth = GetLegendPrefWidth(aRenderingContext); + } + + if (mContentFrame) { + contentMinWidth = GetContentMinWidth(aRenderingContext); + } + + result = PR_MAX(legendPrefWidth, contentMinWidth); + return result; +} + +nscoord +nsFieldSetFrame::GetPrefWidth(nsIRenderingContext* aRenderingContext) +{ + nscoord result = 0; + DISPLAY_PREF_WIDTH(this, result); + + nscoord legendPrefWidth = 0; + nscoord contentPrefWidth = 0; + if (mLegendFrame) { + legendPrefWidth = GetLegendPrefWidth(aRenderingContext); + } + + if (mContentFrame) { + contentPrefWidth = + nsLayoutUtils::IntrinsicForContainer(aRenderingContext, mContentFrame, + nsLayoutUtils::PREF_WIDTH); + } + + result = PR_MAX(legendPrefWidth, contentPrefWidth); + return result; +} + +/* virtual */ nsSize +nsFieldSetFrame::ComputeSize(nsIRenderingContext *aRenderingContext, + nsSize aCBSize, nscoord aAvailableWidth, + nsSize aMargin, nsSize aBorder, nsSize aPadding, + PRBool aShrinkWrap) +{ + nsSize result = + nsHTMLContainerFrame::ComputeSize(aRenderingContext, aCBSize, + aAvailableWidth, + aMargin, aBorder, aPadding, aShrinkWrap); + + // Fieldsets never shrink below their min width. + nscoord minWidth = GetMinWidth(aRenderingContext); + if (minWidth > result.width) + result.width = minWidth; + + return result; +} + NS_IMETHODIMP nsFieldSetFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { - DO_GLOBAL_REFLOW_COUNT("nsFieldSetFrame", aReflowState.reason); + DO_GLOBAL_REFLOW_COUNT("nsFieldSetFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); + NS_PRECONDITION(aReflowState.mComputedWidth != NS_INTRINSICSIZE, + "Should have a precomputed width!"); + // Initialize OUT parameter aStatus = NS_FRAME_COMPLETE; //------------ Handle Incremental Reflow ----------------- - PRBool reflowContent = PR_TRUE; - PRBool reflowLegend = PR_TRUE; - nsReflowReason reason = aReflowState.reason; + PRBool reflowContent; + PRBool reflowLegend; - if (reason == eReflowReason_Incremental) { - nsHTMLReflowCommand *command = aReflowState.path->mReflowCommand; + if (aReflowState.ShouldReflowAllKids()) { + reflowContent = mContentFrame != nsnull; + reflowLegend = mLegendFrame != nsnull; + } else { + reflowContent = mContentFrame && + (mContentFrame->GetStateBits() & + (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) != 0; - // See if it's targeted at us - if (command) { - nsReflowType reflowType; - command->GetType(reflowType); - - switch (reflowType) { - case eReflowType_StyleChanged: - reason = eReflowReason_StyleChange; - break; - - case eReflowType_ReflowDirty: - reason = eReflowReason_Dirty; - break; - - default: - NS_ERROR("Unexpected Reflow Type"); - } - } else { - reflowContent = PR_FALSE; - reflowLegend = PR_FALSE; - - nsReflowPath::iterator iter = aReflowState.path->FirstChild(); - nsReflowPath::iterator end = aReflowState.path->EndChildren(); - - for ( ; iter != end; ++iter) { - if (*iter == mLegendFrame) - reflowLegend = PR_TRUE; - else if (*iter == mContentFrame) - reflowContent = PR_TRUE; - } - } + reflowLegend = mLegendFrame && + (mLegendFrame->GetStateBits() & + (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) != 0; } - if (aDesiredSize.mFlags & NS_REFLOW_CALC_MAX_WIDTH) { - reflowLegend = PR_TRUE; - reflowContent = PR_TRUE; - } else if (reason == eReflowReason_Dirty) { - // if dirty then check dirty flags - if (GetStateBits() & NS_FRAME_IS_DIRTY) { - reflowLegend = PR_TRUE; - reflowContent = PR_TRUE; - } else { - if (reflowContent) { - reflowContent = mContentFrame ? - (mContentFrame->GetStateBits() - & (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) != 0 : PR_FALSE; - } - - if (reflowLegend) { - reflowLegend = mLegendFrame ? - (mLegendFrame->GetStateBits() - & (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) != 0 : PR_FALSE; - } - } - } - - // availSize could have unconstrained values, don't perform any addition on them nsSize availSize(aReflowState.mComputedWidth, aReflowState.availableHeight); + NS_ASSERTION(!mContentFrame || + GetContentMinWidth(aReflowState.rendContext) <= availSize.width, + "Bogus availSize.width; should be bigger"); // get our border and padding const nsMargin &borderPadding = aReflowState.mComputedBorderPadding; const nsMargin &padding = aReflowState.mComputedPadding; - nsMargin border = borderPadding - padding; - if (aDesiredSize.mComputeMEW) { - aDesiredSize.mMaxElementWidth = borderPadding.left + borderPadding.right; - } - + nsMargin border = borderPadding - padding; // Figure out how big the legend is if there is one. // get the legend's margin nsMargin legendMargin(0,0,0,0); // reflow the legend only if needed - if (mLegendFrame) { + if (reflowLegend) { const nsStyleMargin* marginStyle = mLegendFrame->GetStyleMargin(); marginStyle->GetMargin(legendMargin); - if (reflowLegend) { - nsHTMLReflowState legendReflowState(aPresContext, aReflowState, - mLegendFrame, nsSize(NS_INTRINSICSIZE,NS_INTRINSICSIZE), - reason); + // Give the legend all the space it wants. + nsSize legendAvailSize(GetLegendPrefWidth(aReflowState.rendContext), + NS_INTRINSICSIZE); - // always give the legend as much size as it needs - legendReflowState.mComputedWidth = NS_INTRINSICSIZE; - legendReflowState.mComputedHeight = NS_INTRINSICSIZE; + nsHTMLReflowState legendReflowState(aPresContext, aReflowState, + mLegendFrame, + legendAvailSize); - nsHTMLReflowMetrics legendDesiredSize(0,0); + // always give the legend as much size as it wants + legendReflowState.mComputedWidth = + mLegendFrame->GetPrefWidth(aReflowState.rendContext); + legendReflowState.mComputedHeight = NS_INTRINSICSIZE; - ReflowChild(mLegendFrame, aPresContext, legendDesiredSize, legendReflowState, - 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus); + nsHTMLReflowMetrics legendDesiredSize; + + ReflowChild(mLegendFrame, aPresContext, legendDesiredSize, legendReflowState, + 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus); #ifdef NOISY_REFLOW - printf(" returned (%d, %d)\n", legendDesiredSize.width, legendDesiredSize.height); - if (legendDesiredSize.mComputeMEW) - printf(" and maxEW %d\n", - legendDesiredSize.mMaxElementWidth); + printf(" returned (%d, %d)\n", legendDesiredSize.width, legendDesiredSize.height); #endif - // figure out the legend's rectangle - mLegendRect.width = legendDesiredSize.width + legendMargin.left + legendMargin.right; - mLegendRect.height = legendDesiredSize.height + legendMargin.top + legendMargin.bottom; - mLegendRect.x = borderPadding.left; - mLegendRect.y = 0; - - nscoord oldSpace = mLegendSpace; - mLegendSpace = 0; - if (mLegendRect.height > border.top) { - // center the border on the legend - mLegendSpace = mLegendRect.height - border.top; - } else { - mLegendRect.y = (border.top - mLegendRect.height)/2; - } - - // if the legend space changes then we need to reflow the - // content area as well. - if (mLegendSpace != oldSpace) { - if (reflowContent == PR_FALSE || reason == eReflowReason_Dirty) { - reflowContent = PR_TRUE; - reason = eReflowReason_Resize; - } - } - - // if we are contrained then remove the legend from our available height. - if (NS_INTRINSICSIZE != availSize.height) { - if (availSize.height >= mLegendSpace) - availSize.height -= mLegendSpace; - } - - // don't get any smaller than the legend - if (NS_INTRINSICSIZE != availSize.width) { - if (availSize.width < mLegendRect.width) - availSize.width = mLegendRect.width; - } - - FinishReflowChild(mLegendFrame, aPresContext, &legendReflowState, - legendDesiredSize, 0, 0, NS_FRAME_NO_MOVE_FRAME); + // figure out the legend's rectangle + mLegendRect.width = legendDesiredSize.width + legendMargin.left + legendMargin.right; + mLegendRect.height = legendDesiredSize.height + legendMargin.top + legendMargin.bottom; + mLegendRect.x = borderPadding.left; + mLegendRect.y = 0; + nscoord oldSpace = mLegendSpace; + mLegendSpace = 0; + if (mLegendRect.height > border.top) { + // center the border on the legend + mLegendSpace = mLegendRect.height - border.top; + } else { + mLegendRect.y = (border.top - mLegendRect.height)/2; } - } else { + + // if the legend space changes then we need to reflow the + // content area as well. + if (mLegendSpace != oldSpace && mContentFrame) { + reflowContent = PR_TRUE; + } + + // if we are contrained then remove the legend from our available height. + if (NS_INTRINSICSIZE != availSize.height) { + availSize.height -= mLegendSpace; + availSize.height = PR_MAX(availSize.height, 0); + } + + NS_ASSERTION(availSize.width >= mLegendRect.width, + "Bogus availSize.width. Should be bigger"); + + FinishReflowChild(mLegendFrame, aPresContext, &legendReflowState, + legendDesiredSize, 0, 0, NS_FRAME_NO_MOVE_FRAME); + } else if (!mLegendFrame) { mLegendRect.Empty(); mLegendSpace = 0; - } + } // else mLegendSpace and mLegendRect haven't changed... nsRect contentRect; // reflow the content frame only if needed - if (mContentFrame) { - if (reflowContent) { - availSize.width = aReflowState.mComputedWidth; + if (reflowContent) { + nsHTMLReflowState kidReflowState(aPresContext, aReflowState, mContentFrame, + availSize); - nsHTMLReflowState kidReflowState(aPresContext, aReflowState, mContentFrame, - availSize, reason); + nsHTMLReflowMetrics kidDesiredSize(aDesiredSize.mFlags); + // Reflow the frame + ReflowChild(mContentFrame, aPresContext, kidDesiredSize, kidReflowState, + borderPadding.left + kidReflowState.mComputedMargin.left, + borderPadding.top + mLegendSpace + kidReflowState.mComputedMargin.top, + 0, aStatus); - nsHTMLReflowMetrics kidDesiredSize(aDesiredSize.mComputeMEW, aDesiredSize.mFlags); - // Reflow the frame - ReflowChild(mContentFrame, aPresContext, kidDesiredSize, kidReflowState, - borderPadding.left + kidReflowState.mComputedMargin.left, - borderPadding.top + mLegendSpace + kidReflowState.mComputedMargin.top, - 0, aStatus); - - // set the rect. make sure we add the margin back in. - contentRect.SetRect(borderPadding.left,borderPadding.top + mLegendSpace,kidDesiredSize.width ,kidDesiredSize.height); - if (aReflowState.mComputedHeight != NS_INTRINSICSIZE && - borderPadding.top + mLegendSpace+kidDesiredSize.height > aReflowState.mComputedHeight) { - kidDesiredSize.height = aReflowState.mComputedHeight-(borderPadding.top + mLegendSpace); - } - - FinishReflowChild(mContentFrame, aPresContext, &kidReflowState, - kidDesiredSize, contentRect.x, contentRect.y, 0); - if (aDesiredSize.mComputeMEW) { - aDesiredSize.mMaxElementWidth = kidDesiredSize.mMaxElementWidth; - if (eStyleUnit_Coord == aReflowState.mStylePosition->mWidth.GetUnit() && - NS_INTRINSICSIZE != aReflowState.mComputedWidth) - aDesiredSize.mMaxElementWidth = aReflowState.mComputedWidth; - if (eStyleUnit_Percent == aReflowState.mStylePosition->mWidth.GetUnit()) - aDesiredSize.mMaxElementWidth = 0; - aDesiredSize.mMaxElementWidth += borderPadding.left + borderPadding.right; - } - if (aDesiredSize.mFlags & NS_REFLOW_CALC_MAX_WIDTH) { - aDesiredSize.mMaximumWidth = kidDesiredSize.mMaximumWidth + - borderPadding.left + borderPadding.right; - } - NS_FRAME_TRACE_REFLOW_OUT("FieldSet::Reflow", aStatus); - - } else { - // if we don't need to reflow just get the old size - contentRect = mContentFrame->GetRect(); - const nsStyleMargin* marginStyle = mContentFrame->GetStyleMargin(); - - nsMargin m(0,0,0,0); - marginStyle->GetMargin(m); - contentRect.Inflate(m); + // set the rect. make sure we add the margin back in. + contentRect.SetRect(borderPadding.left,borderPadding.top + mLegendSpace,kidDesiredSize.width ,kidDesiredSize.height); + if (aReflowState.mComputedHeight != NS_INTRINSICSIZE && + borderPadding.top + mLegendSpace+kidDesiredSize.height > aReflowState.mComputedHeight) { + kidDesiredSize.height = aReflowState.mComputedHeight-(borderPadding.top + mLegendSpace); } + + FinishReflowChild(mContentFrame, aPresContext, &kidReflowState, + kidDesiredSize, contentRect.x, contentRect.y, 0); + NS_FRAME_TRACE_REFLOW_OUT("FieldSet::Reflow", aStatus); + + } else if (mContentFrame) { + // if we don't need to reflow just get the old size + // XXXbz what about auto or percent margins? Those wouldn't be in + // the style! + contentRect = mContentFrame->GetRect(); + const nsStyleMargin* marginStyle = mContentFrame->GetStyleMargin(); + + nsMargin m(0,0,0,0); + marginStyle->GetMargin(m); + contentRect.Inflate(m); + } else { + contentRect.Empty(); } // use the computed width if the inner content does not fill it - if (aReflowState.mComputedWidth != NS_INTRINSICSIZE && - aReflowState.mComputedWidth > contentRect.width) { + if (aReflowState.mComputedWidth > contentRect.width) { contentRect.width = aReflowState.mComputedWidth; } @@ -581,17 +630,7 @@ nsFieldSetFrame::Reflow(nsPresContext* aPresContext, aDesiredSize.width = contentRect.width + borderPadding.left + borderPadding.right; aDesiredSize.ascent = aDesiredSize.height; aDesiredSize.descent = 0; - if (aDesiredSize.mComputeMEW) { - // if the legend is wider use it - if (aDesiredSize.mMaxElementWidth < mLegendRect.width + borderPadding.left + borderPadding.right) - aDesiredSize.mMaxElementWidth = mLegendRect.width + borderPadding.left + borderPadding.right; - } aDesiredSize.mOverflowArea = nsRect(0, 0, aDesiredSize.width, aDesiredSize.height); - // make the mMaximumWidth large enough if the legendframe determines the size - if ((aDesiredSize.mFlags & NS_REFLOW_CALC_MAX_WIDTH) && mLegendFrame) { - aDesiredSize.mMaximumWidth = PR_MAX(aDesiredSize.mMaximumWidth, mLegendRect.width + - borderPadding.left + borderPadding.right); - } if (mLegendFrame) ConsiderChildOverflow(aDesiredSize.mOverflowArea, mLegendFrame); if (mContentFrame) @@ -656,7 +695,7 @@ nsFieldSetFrame::RemoveFrame(nsIAtom* aListName, mLegendFrame = nsnull; AddStateBits(NS_FRAME_IS_DIRTY); if (GetParent()) { - GetParent()->ReflowDirtyChild(GetPresContext()->GetPresShell(), this); + GetParent()->ChildIsDirty(this); } return NS_OK; } @@ -687,7 +726,7 @@ nsFieldSetFrame::MaybeSetLegend(nsIFrame* aFrameList, nsIAtom* aListName) mFrames.SetFrames(mLegendFrame); AddStateBits(NS_FRAME_IS_DIRTY); if (GetParent()) { - GetParent()->ReflowDirtyChild(mLegendFrame->GetPresContext()->GetPresShell(), this); + GetParent()->ChildIsDirty(this); } } return aFrameList; diff --git a/mozilla/layout/forms/nsFileControlFrame.cpp b/mozilla/layout/forms/nsFileControlFrame.cpp index fbdee7c7a96..76bf189cdc8 100644 --- a/mozilla/layout/forms/nsFileControlFrame.cpp +++ b/mozilla/layout/forms/nsFileControlFrame.cpp @@ -355,18 +355,28 @@ nsFileControlFrame::MouseClick(nsIDOMEvent* aMouseEvent) return NS_FAILED(result) ? result : NS_ERROR_FAILURE; } +nscoord +nsFileControlFrame::GetMinWidth(nsIRenderingContext *aRenderingContext) +{ + nscoord result; + DISPLAY_MIN_WIDTH(this, result); + + // Our min width is our pref width + result = GetPrefWidth(aRenderingContext); + return result; +} NS_IMETHODIMP nsFileControlFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { - DO_GLOBAL_REFLOW_COUNT("nsFileControlFrame", aReflowState.reason); + DO_GLOBAL_REFLOW_COUNT("nsFileControlFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); aStatus = NS_FRAME_COMPLETE; - if (eReflowReason_Initial == aReflowState.reason) { + if (mState & NS_FRAME_FIRST_REFLOW) { mTextFrame = GetTextControlFrame(aPresContext, this); NS_ENSURE_TRUE(mTextFrame, NS_ERROR_UNEXPECTED); if (mCachedState) { @@ -378,7 +388,10 @@ NS_IMETHODIMP nsFileControlFrame::Reflow(nsPresContext* aPresContext, // The Areaframe takes care of all our reflow // except for when style is used to change its size. - nsresult rv = nsAreaFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); + // XXXbz do we care? This setup just needs to die.... Leaving it + // in for now just because I don't want to regress things, but.... + nsresult rv = nsAreaFrame::Reflow(aPresContext, aDesiredSize, aReflowState, + aStatus); if (NS_SUCCEEDED(rv) && mTextFrame != nsnull) { nsIFrame* child = GetFirstChild(nsnull); if (child == mTextFrame) { @@ -394,12 +407,11 @@ NS_IMETHODIMP nsFileControlFrame::Reflow(nsPresContext* aPresContext, // messes up the button's rect if (txtRect.width + buttonRect.width != aDesiredSize.width || txtRect.height != aDesiredSize.height) { - nsHTMLReflowMetrics txtKidSize(PR_TRUE); + nsHTMLReflowMetrics txtKidSize; nsSize txtAvailSize(aReflowState.availableWidth, aDesiredSize.height); nsHTMLReflowState txtKidReflowState(aPresContext, *aReflowState.parentReflowState, - this, txtAvailSize, - eReflowReason_Resize); + this, txtAvailSize); txtKidReflowState.mComputedHeight = aDesiredSize.height; rv = nsAreaFrame::WillReflow(aPresContext); NS_ASSERTION(NS_SUCCEEDED(rv), "Should have succeeded"); @@ -408,14 +420,11 @@ NS_IMETHODIMP nsFileControlFrame::Reflow(nsPresContext* aPresContext, rv = nsAreaFrame::DidReflow(aPresContext, &txtKidReflowState, aStatus); NS_ASSERTION(NS_SUCCEEDED(rv), "Should have succeeded"); - // Re-calc and set the correct rect + // And now manually resize the frame... + txtRect = mTextFrame->GetRect(); txtRect.y = aReflowState.mComputedBorderPadding.top; txtRect.height = aDesiredSize.height; mTextFrame->SetRect(txtRect); - - if (aDesiredSize.mComputeMEW) { - aDesiredSize.SetMEWToActualWidth(aReflowState.mStylePosition->mWidth.GetUnit()); - } } } } @@ -611,4 +620,8 @@ nsFileControlFrame::MouseListener::MouseClick(nsIDOMEvent* aMouseEvent) return NS_OK; } - +PRBool +nsFileControlFrame::IsFrameOfType(PRUint32 aFlags) const +{ + return !(aFlags & ~(eReplaced | eReplacedContainsBlock)); +} diff --git a/mozilla/layout/forms/nsFileControlFrame.h b/mozilla/layout/forms/nsFileControlFrame.h index f4186e76828..79e427fa276 100644 --- a/mozilla/layout/forms/nsFileControlFrame.h +++ b/mozilla/layout/forms/nsFileControlFrame.h @@ -72,6 +72,8 @@ public: virtual nsresult GetFormProperty(nsIAtom* aName, nsAString& aValue) const; virtual void SetFocus(PRBool aOn, PRBool aRepaint); + virtual nscoord GetMinWidth(nsIRenderingContext *aRenderingContext); + NS_IMETHOD Reflow(nsPresContext* aCX, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, @@ -128,6 +130,8 @@ protected: nsresult MouseClick(nsIDOMEvent* aMouseEvent); + virtual PRBool IsFrameOfType(PRUint32 aFlags) const; + virtual PRIntn GetSkipSides() const; /** diff --git a/mozilla/layout/forms/nsFormControlFrame.cpp b/mozilla/layout/forms/nsFormControlFrame.cpp index 975e5a5c696..1577c664948 100644 --- a/mozilla/layout/forms/nsFormControlFrame.cpp +++ b/mozilla/layout/forms/nsFormControlFrame.cpp @@ -80,157 +80,22 @@ nsFormControlFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) return nsLeafFrame::QueryInterface(aIID, aInstancePtr); } -void nsFormControlFrame::SetupCachedSizes(nsSize& aCacheSize, - nscoord& aCachedAscent, - nscoord& aCachedMaxElementWidth, - nsHTMLReflowMetrics& aDesiredSize) +nscoord +nsFormControlFrame::GetIntrinsicWidth() { - aCacheSize.width = aDesiredSize.width; - aCacheSize.height = aDesiredSize.height; - aCachedAscent = aDesiredSize.ascent; - if (aDesiredSize.mComputeMEW) { - aCachedMaxElementWidth = aDesiredSize.mMaxElementWidth; - } + // Intrinsic width is 144 twips. Why? I have no idea; that's what + // it was before I touched this code, and the original checkin + // comment is not so helpful. + return 144; } -//------------------------------------------------------------ -void nsFormControlFrame::SkipResizeReflow(nsSize& aCacheSize, - nscoord& aCachedAscent, - nscoord& aCachedMaxElementWidth, - nsSize& aCachedAvailableSize, - nsHTMLReflowMetrics& aDesiredSize, - const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus, - PRBool& aBailOnWidth, - PRBool& aBailOnHeight) +nscoord +nsFormControlFrame::GetIntrinsicHeight() { - - if (aReflowState.reason == eReflowReason_Incremental || -#ifdef IBMBIDI - aReflowState.reason == eReflowReason_StyleChange || -#endif - aReflowState.reason == eReflowReason_Dirty) { - aBailOnHeight = PR_FALSE; - aBailOnWidth = PR_FALSE; - - } else if (eReflowReason_Initial == aReflowState.reason) { - aBailOnHeight = PR_FALSE; - aBailOnWidth = PR_FALSE; - - } else { - - nscoord width; - if (NS_UNCONSTRAINEDSIZE == aReflowState.mComputedWidth) { - if (aReflowState.availableWidth == NS_UNCONSTRAINEDSIZE) { - width = NS_UNCONSTRAINEDSIZE; - aBailOnWidth = aCacheSize.width != kSizeNotSet; -#ifdef FCF_NOISY - if (aBailOnWidth) { - printf("-------------- #1 Bailing on aCachedAvailableSize.width %d != kSizeNotSet\n", aCachedAvailableSize.width); - } -#endif - } else { - width = aReflowState.availableWidth - aReflowState.mComputedBorderPadding.left - - aReflowState.mComputedBorderPadding.right; - aBailOnWidth = aCachedAvailableSize.width <= width && aCachedAvailableSize.width != kSizeNotSet; -#ifdef FCF_NOISY - if (aBailOnWidth) { - printf("-------------- #2 Bailing on aCachedAvailableSize.width %d <= width %d\n", aCachedAvailableSize.width, width ); - } else { - aBailOnWidth = width <= (aCacheSize.width - aReflowState.mComputedBorderPadding.left - aReflowState.mComputedBorderPadding.right) && - aCachedAvailableSize.width == kSizeNotSet; - if (aBailOnWidth) { - printf("-------------- #2.2 Bailing on width %d <= aCachedAvailableSize.width %d\n",(aCacheSize.width - aReflowState.mComputedBorderPadding.left - aReflowState.mComputedBorderPadding.right), width ); - } - } -#endif - } - } else { - width = aReflowState.mComputedWidth; - //if (aCachedAvailableSize.width == kSizeNotSet) { - // //aBailOnWidth = aCachedAvailableSize.width == aCacheSize.width; - aBailOnWidth = PR_FALSE; - //} else { - aBailOnWidth = width == (aCacheSize.width - aReflowState.mComputedBorderPadding.left - aReflowState.mComputedBorderPadding.right); - //} -#ifdef FCF_NOISY - if (aBailOnWidth) { - printf("-------------- #3 Bailing on aCachedAvailableSize.width %d == aReflowState.mComputedWidth %d\n", aCachedAvailableSize.width, width ); - } -#endif - } - - nscoord height; - if (NS_UNCONSTRAINEDSIZE == aReflowState.mComputedHeight) { - if (aReflowState.availableHeight == NS_UNCONSTRAINEDSIZE) { - height = NS_UNCONSTRAINEDSIZE; - aBailOnHeight = aCacheSize.height != kSizeNotSet; -#ifdef FCF_NOISY - if (aBailOnHeight) { - printf("-------------- #1 Bailing on aCachedAvailableSize.height %d != kSizeNotSet\n", aCachedAvailableSize.height); - } -#endif - } else { - height = aReflowState.availableHeight - aReflowState.mComputedBorderPadding.left - - aReflowState.mComputedBorderPadding.right; - aBailOnHeight = aCachedAvailableSize.height <= height && aCachedAvailableSize.height != kSizeNotSet; -#ifdef FCF_NOISY - if (aBailOnHeight) { - printf("-------------- #2 Bailing on aCachedAvailableSize.height %d <= height %d\n", aCachedAvailableSize.height, height ); - } else { - aBailOnHeight = height <= (aCacheSize.height - aReflowState.mComputedBorderPadding.left - aReflowState.mComputedBorderPadding.right) && - aCachedAvailableSize.height == kSizeNotSet; - if (aBailOnHeight) { - printf("-------------- #2.2 Bailing on height %d <= aCachedAvailableSize.height %d\n",(aCacheSize.height - aReflowState.mComputedBorderPadding.left - aReflowState.mComputedBorderPadding.right), height ); - } - } -#endif - } - } else { - height = aReflowState.mComputedHeight; - //if (aCachedAvailableSize.height == kSizeNotSet) { - // //aBailOnHeight = aCachedAvailableSize.height == aCacheSize.height; - aBailOnHeight = PR_FALSE; - //} else { - aBailOnHeight = height == (aCacheSize.height - aReflowState.mComputedBorderPadding.left - aReflowState.mComputedBorderPadding.right); - //} -#ifdef FCF_NOISY - if (aBailOnHeight) { - printf("-------------- #3 Bailing on aCachedAvailableSize.height %d == aReflowState.mComputedHeight %d\n", aCachedAvailableSize.height, height ); - } -#endif - } - - if (aBailOnWidth || aBailOnHeight) { - aDesiredSize.width = aCacheSize.width; - aDesiredSize.height = aCacheSize.height; - aDesiredSize.ascent = aCachedAscent; - aDesiredSize.descent = aDesiredSize.height - aDesiredSize.ascent; - - if (aDesiredSize.mComputeMEW) { - aDesiredSize.mMaxElementWidth = aCachedMaxElementWidth; - } - } - } -} - -void -nsFormControlFrame::GetDesiredSize(nsPresContext* aPresContext, - const nsHTMLReflowState& aReflowState, - nsHTMLReflowMetrics& aDesiredSize) -{ - // get the css size and let the frame use or override it - nsSize styleSize; - GetStyleSize(aPresContext, aReflowState, styleSize); - - // subclasses should always override this method, but if not and no css, make it small - aDesiredSize.width = (styleSize.width > CSS_NOTSET) ? styleSize.width : 144; - aDesiredSize.height = (styleSize.height > CSS_NOTSET) ? styleSize.height : 144; - aDesiredSize.ascent = aDesiredSize.height; - aDesiredSize.descent = 0; - if (aDesiredSize.mComputeMEW) { - aDesiredSize.SetMEWToActualWidth(aReflowState.mStylePosition->mWidth.GetUnit()); - } + // Intrinsic height is 144 twips. Why? I have no idea; that's what + // it was before I touched this code, and the original checkin + // comment is not so helpful. + return 144; } NS_IMETHODIMP @@ -275,21 +140,15 @@ nsFormControlFrame::Reflow(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { - DO_GLOBAL_REFLOW_COUNT("nsFormControlFrame", aReflowState.reason); + DO_GLOBAL_REFLOW_COUNT("nsFormControlFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); - if (!mDidInit) { + if (mState & NS_FRAME_FIRST_REFLOW) { RegUnRegAccessKey(NS_STATIC_CAST(nsIFrame*, this), PR_TRUE); - mDidInit = PR_TRUE; } - nsresult rv = nsLeafFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); - - aStatus = NS_FRAME_COMPLETE; - NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); - aDesiredSize.mOverflowArea = nsRect(0, 0, aDesiredSize.width, aDesiredSize.height); - FinishAndStoreOverflow(&aDesiredSize); - return rv; + return nsLeafFrame::Reflow(aPresContext, aDesiredSize, aReflowState, + aStatus); } nsresult @@ -335,25 +194,6 @@ nsFormControlFrame::HandleEvent(nsPresContext* aPresContext, return NS_OK; } -void -nsFormControlFrame::GetStyleSize(nsPresContext* aPresContext, - const nsHTMLReflowState& aReflowState, - nsSize& aSize) -{ - if (aReflowState.mComputedWidth != NS_INTRINSICSIZE) { - aSize.width = aReflowState.mComputedWidth; - } - else { - aSize.width = CSS_NOTSET; - } - if (aReflowState.mComputedHeight != NS_INTRINSICSIZE) { - aSize.height = aReflowState.mComputedHeight; - } - else { - aSize.height = CSS_NOTSET; - } -} - void nsFormControlFrame::GetCurrentCheckState(PRBool *aState) { diff --git a/mozilla/layout/forms/nsFormControlFrame.h b/mozilla/layout/forms/nsFormControlFrame.h index d1d8190e2fd..4041b054627 100644 --- a/mozilla/layout/forms/nsFormControlFrame.h +++ b/mozilla/layout/forms/nsFormControlFrame.h @@ -41,7 +41,6 @@ #include "nsIFormControlFrame.h" #include "nsLeafFrame.h" -#define CSS_NOTSET -1 #define ATTR_NOTSET -1 #ifdef DEBUG_rods @@ -114,36 +113,11 @@ public: virtual void SetFocus(PRBool aOn = PR_TRUE, PRBool aRepaint = PR_FALSE); - /** - * Get the width and height of this control based on CSS - * @param aPresContext the presentation context - * @param aSize the size that this frame wants, set by this method. values of -1 - * for aSize.width or aSize.height indicate unset values. - */ - static void GetStyleSize(nsPresContext* aContext, - const nsHTMLReflowState& aReflowState, - nsSize& aSize); - // nsIFormControlFrame virtual nsresult SetFormProperty(nsIAtom* aName, const nsAString& aValue); virtual nsresult GetFormProperty(nsIAtom* aName, nsAString& aValue) const; - // Resize Reflow Optimization Methods - static void SetupCachedSizes(nsSize& aCacheSize, - nscoord& aCachedAscent, - nscoord& aCachedMaxElementWidth, - nsHTMLReflowMetrics& aDesiredSize); - - static void SkipResizeReflow(nsSize& aCacheSize, - nscoord& aCachedAscent, - nscoord& aCachedMaxElementWidth, - nsSize& aCachedAvailableSize, - nsHTMLReflowMetrics& aDesiredSize, - const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus, - PRBool& aBailOnWidth, - PRBool& aBailOnHeight); // AccessKey Helper function static nsresult RegUnRegAccessKey(nsIFrame * aFrame, PRBool aDoReg); @@ -165,15 +139,8 @@ protected: virtual ~nsFormControlFrame(); - /** - * Get the size that this frame would occupy without any constraints - * @param aPresContext the presentation context - * @param aDesiredSize the size desired by this frame, to be set by this method - * @param aMaxSize the maximum size available for this frame - */ - virtual void GetDesiredSize(nsPresContext* aPresContext, - const nsHTMLReflowState& aReflowState, - nsHTMLReflowMetrics& aDesiredSize); + virtual nscoord GetIntrinsicWidth(); + virtual nscoord GetIntrinsicHeight(); // //------------------------------------------------------------------------------------- diff --git a/mozilla/layout/forms/nsGfxButtonControlFrame.cpp b/mozilla/layout/forms/nsGfxButtonControlFrame.cpp index bb6fa15718c..3f325a43f74 100644 --- a/mozilla/layout/forms/nsGfxButtonControlFrame.cpp +++ b/mozilla/layout/forms/nsGfxButtonControlFrame.cpp @@ -48,7 +48,6 @@ #include "nsIServiceManager.h" #include "nsIDOMNode.h" #include "nsLayoutAtoms.h" -#include "nsReflowPath.h" #include "nsAutoPtr.h" #include "nsStyleSet.h" #include "nsContentUtils.h" @@ -63,8 +62,7 @@ const nscoord kSuggestedNotSet = -1; nsGfxButtonControlFrame::nsGfxButtonControlFrame(nsStyleContext* aContext): - nsHTMLButtonControlFrame(aContext), - mSuggestedSize(kSuggestedNotSet, kSuggestedNotSet) + nsHTMLButtonControlFrame(aContext) { } @@ -308,44 +306,6 @@ nsGfxButtonControlFrame::IsLeaf() const return PR_TRUE; } -NS_IMETHODIMP -nsGfxButtonControlFrame::Reflow(nsPresContext* aPresContext, - nsHTMLReflowMetrics& aDesiredSize, - const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus) -{ - DO_GLOBAL_REFLOW_COUNT("nsGfxButtonControlFrame", aReflowState.reason); - DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); - - if ((kSuggestedNotSet != mSuggestedSize.width) || - (kSuggestedNotSet != mSuggestedSize.height)) { - nsHTMLReflowState suggestedReflowState(aReflowState); - - // Honor the suggested width and/or height. - if (kSuggestedNotSet != mSuggestedSize.width) { - suggestedReflowState.mComputedWidth = mSuggestedSize.width; - } - - if (kSuggestedNotSet != mSuggestedSize.height) { - suggestedReflowState.mComputedHeight = mSuggestedSize.height; - } - - return nsHTMLButtonControlFrame::Reflow(aPresContext, aDesiredSize, suggestedReflowState, aStatus); - - } - - // Normal reflow. - - return nsHTMLButtonControlFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); -} - -void -nsGfxButtonControlFrame::SetSuggestedSize(const nsSize& aSize) -{ - mSuggestedSize = aSize; - //mState |= NS_FRAME_IS_DIRTY; -} - NS_IMETHODIMP nsGfxButtonControlFrame::HandleEvent(nsPresContext* aPresContext, nsGUIEvent* aEvent, diff --git a/mozilla/layout/forms/nsGfxButtonControlFrame.h b/mozilla/layout/forms/nsGfxButtonControlFrame.h index 58d53328370..2ef536fa00e 100644 --- a/mozilla/layout/forms/nsGfxButtonControlFrame.h +++ b/mozilla/layout/forms/nsGfxButtonControlFrame.h @@ -58,12 +58,6 @@ class nsGfxButtonControlFrame : public nsHTMLButtonControlFrame, public: nsGfxButtonControlFrame(nsStyleContext* aContext); - //nsIFrame - NS_IMETHOD Reflow(nsPresContext* aCX, - nsHTMLReflowMetrics& aDesiredSize, - const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus); - NS_IMETHOD HandleEvent(nsPresContext* aPresContext, nsGUIEvent* aEvent, nsEventStatus* aEventStatus); @@ -94,14 +88,6 @@ public: virtual PRBool IsLeaf() const; - /** - * Set the suggested size of the button. - * @note This is NOT a virtual function, it will be called - * directly on an instance of this class. - * @param aSize The suggested size. - */ - void SetSuggestedSize(const nsSize& aSize); - protected: nsresult GetDefaultLabel(nsXPIDLString& aLabel); diff --git a/mozilla/layout/forms/nsGfxCheckboxControlFrame.cpp b/mozilla/layout/forms/nsGfxCheckboxControlFrame.cpp index 815aa70c0eb..b8dea7ab9c5 100644 --- a/mozilla/layout/forms/nsGfxCheckboxControlFrame.cpp +++ b/mozilla/layout/forms/nsGfxCheckboxControlFrame.cpp @@ -112,6 +112,12 @@ nsGfxCheckboxControlFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr return nsFormControlFrame::QueryInterface(aIID, aInstancePtr); } +PRBool +nsGfxCheckboxControlFrame::IsFrameOfType(PRUint32 aFlags) const +{ + return !(aFlags & ~(eReplaced | eReplacedContainsBlock)); +} + #ifdef ACCESSIBILITY NS_IMETHODIMP nsGfxCheckboxControlFrame::GetAccessible(nsIAccessible** aAccessible) { @@ -283,4 +289,3 @@ nsGfxCheckboxControlFrame::GetCheckboxState ( ) elem->GetChecked(&retval); return retval; } - diff --git a/mozilla/layout/forms/nsGfxCheckboxControlFrame.h b/mozilla/layout/forms/nsGfxCheckboxControlFrame.h index 19d6a3d03f3..31cc259dd87 100644 --- a/mozilla/layout/forms/nsGfxCheckboxControlFrame.h +++ b/mozilla/layout/forms/nsGfxCheckboxControlFrame.h @@ -55,6 +55,8 @@ public: nsGfxCheckboxControlFrame(nsStyleContext* aContext); virtual ~nsGfxCheckboxControlFrame(); + virtual PRBool IsFrameOfType(PRUint32 aFlags) const; + #ifdef DEBUG NS_IMETHOD GetFrameName(nsAString& aResult) const { return MakeFrameName(NS_LITERAL_STRING("CheckboxControl"), aResult); diff --git a/mozilla/layout/forms/nsGfxRadioControlFrame.cpp b/mozilla/layout/forms/nsGfxRadioControlFrame.cpp index 4619937dfcf..73718a3e58e 100644 --- a/mozilla/layout/forms/nsGfxRadioControlFrame.cpp +++ b/mozilla/layout/forms/nsGfxRadioControlFrame.cpp @@ -77,6 +77,12 @@ nsGfxRadioControlFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) return nsFormControlFrame::QueryInterface(aIID, aInstancePtr); } +PRBool +nsGfxRadioControlFrame::IsFrameOfType(PRUint32 aFlags) const +{ + return !(aFlags & ~(eReplaced | eReplacedContainsBlock)); +} + #ifdef ACCESSIBILITY NS_IMETHODIMP nsGfxRadioControlFrame::GetAccessible(nsIAccessible** aAccessible) { diff --git a/mozilla/layout/forms/nsGfxRadioControlFrame.h b/mozilla/layout/forms/nsGfxRadioControlFrame.h index e4d90027b58..429bd5bebae 100644 --- a/mozilla/layout/forms/nsGfxRadioControlFrame.h +++ b/mozilla/layout/forms/nsGfxRadioControlFrame.h @@ -60,6 +60,8 @@ public: nsGfxRadioControlFrame(nsStyleContext* aContext); ~nsGfxRadioControlFrame(); + virtual PRBool IsFrameOfType(PRUint32 aFlags) const; + //nsIRadioControlFrame methods NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); NS_IMETHOD SetRadioButtonFaceStyleContext(nsStyleContext *aRadioButtonFaceStyleContext); diff --git a/mozilla/layout/forms/nsHTMLButtonControlFrame.cpp b/mozilla/layout/forms/nsHTMLButtonControlFrame.cpp index 398d568e158..f5123fa062d 100644 --- a/mozilla/layout/forms/nsHTMLButtonControlFrame.cpp +++ b/mozilla/layout/forms/nsHTMLButtonControlFrame.cpp @@ -61,7 +61,6 @@ #include "nsFormControlFrame.h" #include "nsFrameManager.h" #include "nsINameSpaceManager.h" -#include "nsReflowPath.h" #include "nsIServiceManager.h" #include "nsIDOMHTMLButtonElement.h" #include "nsIDOMHTMLInputElement.h" @@ -81,9 +80,6 @@ NS_NewHTMLButtonControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) nsHTMLButtonControlFrame::nsHTMLButtonControlFrame(nsStyleContext* aContext) : nsHTMLContainerFrame(aContext) { - mCacheSize.width = -1; - mCacheSize.height = -1; - mCachedMaxElementWidth = -1; } nsHTMLButtonControlFrame::~nsHTMLButtonControlFrame() @@ -104,7 +100,9 @@ nsHTMLButtonControlFrame::Init( nsIFrame* aPrevInFlow) { nsresult rv = nsHTMLContainerFrame::Init(aContent, aParent, aPrevInFlow); - mRenderer.SetFrame(this, GetPresContext()); + if (NS_SUCCEEDED(rv)) { + mRenderer.SetFrame(this, GetPresContext()); + } return rv; } @@ -248,6 +246,51 @@ nsHTMLButtonControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, return DisplaySelectionOverlay(aBuilder, aLists); } +nscoord +nsHTMLButtonControlFrame::GetMinWidth(nsIRenderingContext* aRenderingContext) +{ + return DoGetMinWidth(aRenderingContext, PR_TRUE); +} + +nscoord +nsHTMLButtonControlFrame::DoGetMinWidth(nsIRenderingContext* aRenderingContext, + PRBool aZeroIfWidthSpecified) +{ + nscoord result; + DISPLAY_MIN_WIDTH(this, result); + + // Note: to fix the button equivalent of bug 40596 while still working + // correctly in general, we want to return our actual min width as our min + // width if our style width is auto. Otherwise, we're ok with shrinking as + // small as needed. + if (!aZeroIfWidthSpecified || + GetStylePosition()->mWidth.GetUnit() == eStyleUnit_Auto) { + nsIFrame* kid = mFrames.FirstChild(); + result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, + kid, + nsLayoutUtils::MIN_WIDTH); + + result += mRenderer.GetAddedButtonBorderAndPadding().LeftRight(); + } else { + result = 0; + } + + return result; +} + +nscoord +nsHTMLButtonControlFrame::GetPrefWidth(nsIRenderingContext* aRenderingContext) +{ + nscoord result; + DISPLAY_PREF_WIDTH(this, result); + + nsIFrame* kid = mFrames.FirstChild(); + result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, + kid, + nsLayoutUtils::PREF_WIDTH); + result += mRenderer.GetAddedButtonBorderAndPadding().LeftRight(); + return result; +} NS_IMETHODIMP nsHTMLButtonControlFrame::Reflow(nsPresContext* aPresContext, @@ -255,120 +298,62 @@ nsHTMLButtonControlFrame::Reflow(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { - DO_GLOBAL_REFLOW_COUNT("nsHTMLButtonControlFrame", aReflowState.reason); + DO_GLOBAL_REFLOW_COUNT("nsHTMLButtonControlFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); - if (eReflowReason_Initial == aReflowState.reason) { + NS_PRECONDITION(aReflowState.mComputedWidth != NS_INTRINSICSIZE, + "Should have real computed width by now"); + + if (mState & NS_FRAME_FIRST_REFLOW) { nsFormControlFrame::RegUnRegAccessKey(NS_STATIC_CAST(nsIFrame*, this), PR_TRUE); } -#if 0 - nsresult skiprv = nsFormControlFrame::SkipResizeReflow(mCacheSize, mCachedMaxElementWidth, aPresContext, - aDesiredSize, aReflowState, aStatus); - if (NS_SUCCEEDED(skiprv)) { - return skiprv; - } -#endif - // Reflow the child nsIFrame* firstKid = mFrames.FirstChild(); - nsSize availSize(aReflowState.mComputedWidth, NS_INTRINSICSIZE); - // Indent the child inside us by the focus border. We must do this separate from the - // regular border. + // XXXbz Eventually we may want to check-and-bail if + // !aReflowState.ShouldReflowAllKids() && + // !(firstKid->GetStateBits() & + // (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)). + // We'd need to cache our ascent for that, of course. + nsMargin focusPadding = mRenderer.GetAddedButtonBorderAndPadding(); - - if (NS_INTRINSICSIZE != availSize.width) { - availSize.width -= focusPadding.left + focusPadding.right; - availSize.width = PR_MAX(availSize.width,0); - } - if (NS_AUTOHEIGHT != availSize.height) { - availSize.height -= focusPadding.top + focusPadding.bottom; - availSize.height = PR_MAX(availSize.height,0); - } - - // XXX Proper handling of incremental reflow... - nsReflowReason reason = aReflowState.reason; - if (eReflowReason_Incremental == reason) { - // See if it's targeted at us - nsHTMLReflowCommand *command = aReflowState.path->mReflowCommand; - if (command) { - // I'm not sure what exactly this Invalidate is for - Invalidate(nsRect(0,0,mRect.width,mRect.height), PR_FALSE); - - nsReflowType reflowType; - command->GetType(reflowType); - if (eReflowType_StyleChanged == reflowType) { - reason = eReflowReason_StyleChange; - } - else { - reason = eReflowReason_Resize; - } - } - } - - // Reflow the contents of the button a first time. + // Reflow the contents of the button. ReflowButtonContents(aPresContext, aDesiredSize, aReflowState, firstKid, - availSize, reason, focusPadding, aStatus); + focusPadding, aStatus); - // If we just performed the first pass of a shrink-wrap reflow (which will have - // found the requested size of the children, but not placed them), perform - // the second pass of a shrink-wrap reflow (which places the children - // within this parent button which has now shrink-wrapped around them). - if (availSize.width == NS_SHRINKWRAPWIDTH) { - nsSize newAvailSize(aDesiredSize.width, NS_INTRINSICSIZE); - - ReflowButtonContents(aPresContext, aDesiredSize, aReflowState, firstKid, - newAvailSize, eReflowReason_Resize, focusPadding, aStatus); - } - - // If computed use the computed values. - if (aReflowState.mComputedWidth != NS_INTRINSICSIZE) - aDesiredSize.width = aReflowState.mComputedWidth; - else - aDesiredSize.width += focusPadding.left + focusPadding.right; + aDesiredSize.width = aReflowState.mComputedWidth; + // If computed use the computed value. if (aReflowState.mComputedHeight != NS_INTRINSICSIZE) aDesiredSize.height = aReflowState.mComputedHeight; else - aDesiredSize.height += focusPadding.top + focusPadding.bottom; + aDesiredSize.height += focusPadding.TopBottom(); + + aDesiredSize.width += aReflowState.mComputedBorderPadding.LeftRight(); + aDesiredSize.height += aReflowState.mComputedBorderPadding.TopBottom(); - + // Make sure we obey min/max-height. Note that we do this after adjusting + // for borderpadding, since buttons have border-box sizing... - aDesiredSize.width += aReflowState.mComputedBorderPadding.left + aReflowState.mComputedBorderPadding.right; - aDesiredSize.height += aReflowState.mComputedBorderPadding.top + aReflowState.mComputedBorderPadding.bottom; + // XXXbz unless someone overrides that, of course! We should really consider + // exposing nsHTMLReflowState::AdjustComputed* or something. + aDesiredSize.height = NS_CSS_MINMAX(aDesiredSize.height, + aReflowState.mComputedMinHeight, + aReflowState.mComputedMaxHeight); - if (aDesiredSize.mComputeMEW) { - aDesiredSize.SetMEWToActualWidth(aReflowState.mStylePosition->mWidth.GetUnit()); - } - - // Make sure we obey min/max-width and min/max-height - if (aDesiredSize.width > aReflowState.mComputedMaxWidth) { - aDesiredSize.width = aReflowState.mComputedMaxWidth; - } - if (aDesiredSize.width < aReflowState.mComputedMinWidth) { - aDesiredSize.width = aReflowState.mComputedMinWidth; - } - - if (aDesiredSize.height > aReflowState.mComputedMaxHeight) { - aDesiredSize.height = aReflowState.mComputedMaxHeight; - } - if (aDesiredSize.height < aReflowState.mComputedMinHeight) { - aDesiredSize.height = aReflowState.mComputedMinHeight; - } - - aDesiredSize.ascent += aReflowState.mComputedBorderPadding.top + focusPadding.top; + aDesiredSize.ascent += + aReflowState.mComputedBorderPadding.top + focusPadding.top; aDesiredSize.descent = aDesiredSize.height - aDesiredSize.ascent; - aDesiredSize.mOverflowArea = nsRect(0, 0, aDesiredSize.width, aDesiredSize.height); + aDesiredSize.mOverflowArea = + nsRect(0, 0, aDesiredSize.width, aDesiredSize.height); ConsiderChildOverflow(aDesiredSize.mOverflowArea, firstKid); FinishAndStoreOverflow(&aDesiredSize); aStatus = NS_FRAME_COMPLETE; - nsFormControlFrame::SetupCachedSizes(mCacheSize, mCachedAscent, - mCachedMaxElementWidth, aDesiredSize); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); return NS_OK; } @@ -378,25 +363,48 @@ nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsIFrame* aFirstKid, - const nsSize& aAvailSize, - nsReflowReason aReason, nsMargin aFocusPadding, nsReflowStatus& aStatus) { - nsHTMLReflowState reflowState(aPresContext, aReflowState, aFirstKid, aAvailSize, aReason); + nsSize availSize(aReflowState.mComputedWidth, NS_INTRINSICSIZE); + + // Indent the child inside us by the focus border. We must do this separate + // from the regular border. + availSize.width -= aFocusPadding.LeftRight(); + availSize.width = PR_MAX(availSize.width,0); + + // See whether out availSize's width is big enough. If it's smaller than our + // intrinsic min width, that means that the kid wouldn't really fit; for a + // better look in such cases we adjust the available width and our left + // offset to allow the kid to spill left into our padding. + nscoord xoffset = aFocusPadding.left + aReflowState.mComputedBorderPadding.left; + nscoord extrawidth = DoGetMinWidth(aReflowState.rendContext, PR_FALSE) - + aReflowState.mComputedWidth; + if (extrawidth > 0) { + nscoord extraleft = extrawidth / 2; + nscoord extraright = extrawidth - extraleft; + NS_ASSERTION(extraright >=0, "How'd that happen?"); + + // Do not allow the extras to be bigger than the relevant padding + extraleft = PR_MIN(extraleft, aReflowState.mComputedPadding.left); + extraright = PR_MIN(extraright, aReflowState.mComputedPadding.right); + xoffset -= extraleft; + availSize.width += extraleft + extraright; + } + + nsHTMLReflowState reflowState(aPresContext, aReflowState, aFirstKid, + availSize); ReflowChild(aFirstKid, aPresContext, aDesiredSize, reflowState, - aFocusPadding.left + aReflowState.mComputedBorderPadding.left, + xoffset, aFocusPadding.top + aReflowState.mComputedBorderPadding.top, 0, aStatus); - // calculate the min internal size so the contents gets centered correctly - // minInternalWidth is not being used at all and causes a warning--commenting - // out until someone wants it. - // nscoord minInternalWidth = aReflowState.mComputedMinWidth == 0?0:aReflowState.mComputedMinWidth - - // (aReflowState.mComputedBorderPadding.left + aReflowState.mComputedBorderPadding.right); - nscoord minInternalHeight = aReflowState.mComputedMinHeight == 0?0:aReflowState.mComputedMinHeight - - (aReflowState.mComputedBorderPadding.top + aReflowState.mComputedBorderPadding.bottom); + // calculate the min internal height so the contents gets centered correctly. + // XXXbz this assumes border-box sizing. + nscoord minInternalHeight = aReflowState.mComputedMinHeight - + aReflowState.mComputedBorderPadding.TopBottom(); + minInternalHeight = PR_MAX(minInternalHeight, 0); // center child vertically nscoord yoff = 0; @@ -413,27 +421,6 @@ nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext, // baseline by that much). aDesiredSize.ascent += yoff; - // Place the child. If we have a non-intrinsic width, we want to - // reduce the left padding as needed to try and fit the text in the - // button - nscoord xoffset = aFocusPadding.left + aReflowState.mComputedBorderPadding.left; - if (aReflowState.mComputedWidth != NS_INTRINSICSIZE) { - // First, how much did we "overflow"? This is the width of our - // kid plus our special focus stuff (which did not get accounted - // for in calculating aReflowState.mComputedWidth minus the width - // we're forced to be. - nscoord extrawidth = - aDesiredSize.width + aFocusPadding.left + aFocusPadding.right - - aReflowState.mComputedWidth; - if (extrawidth > 0) { - // Split it evenly between right and left - extrawidth /= 2; - // But do not shoot out the left side of the button, please - extrawidth = PR_MIN(extrawidth, aReflowState.mComputedPadding.left); - xoffset -= extrawidth; - } - } - // Place the child FinishReflowChild(aFirstKid, aPresContext, &reflowState, aDesiredSize, xoffset, @@ -506,3 +493,9 @@ nsHTMLButtonControlFrame::RemoveFrame(nsIAtom* aListName, NS_NOTREACHED("unsupported operation"); return NS_ERROR_UNEXPECTED; } + +PRBool +nsHTMLButtonControlFrame::IsFrameOfType(PRUint32 aFlags) const +{ + return !(aFlags & ~(eReplaced | eReplacedContainsBlock)); +} diff --git a/mozilla/layout/forms/nsHTMLButtonControlFrame.h b/mozilla/layout/forms/nsHTMLButtonControlFrame.h index ac10dc01156..24076b0e4fd 100644 --- a/mozilla/layout/forms/nsHTMLButtonControlFrame.h +++ b/mozilla/layout/forms/nsHTMLButtonControlFrame.h @@ -71,6 +71,15 @@ public: const nsRect& aDirtyRect, const nsDisplayListSet& aLists); + virtual nscoord GetMinWidth(nsIRenderingContext *aRenderingContext); + + virtual nscoord GetPrefWidth(nsIRenderingContext *aRenderingContext); + + // Differs from GetMinWidth in that it allows one to specify whether + // to fall back on 0 for the min width if we have a specified width. + nscoord DoGetMinWidth(nsIRenderingContext *aRenderingContext, + PRBool aZeroIfWidthSpecified); + NS_IMETHOD Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, @@ -122,6 +131,8 @@ public: return GetFirstChild(nsnull)->GetContentInsertionFrame(); } + virtual PRBool IsFrameOfType(PRUint32 aFlags) const; + protected: virtual PRBool IsReset(PRInt32 type); virtual PRBool IsSubmit(PRInt32 type); @@ -129,8 +140,6 @@ protected: nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsIFrame* aFirstKid, - const nsSize& aAvailSize, - nsReflowReason aReason, nsMargin aFocusPadding, nsReflowStatus& aStatus); @@ -139,11 +148,6 @@ protected: PRIntn GetSkipSides() const; nsButtonFrameRenderer mRenderer; - - //Resize Reflow OpitmizationSize; - nsSize mCacheSize; - nscoord mCachedAscent; - nscoord mCachedMaxElementWidth; }; #endif diff --git a/mozilla/layout/forms/nsIListControlFrame.h b/mozilla/layout/forms/nsIListControlFrame.h index 09e3a065a6b..80d815d0c00 100644 --- a/mozilla/layout/forms/nsIListControlFrame.h +++ b/mozilla/layout/forms/nsIListControlFrame.h @@ -46,8 +46,8 @@ class nsIContent; // IID for the nsIListControlFrame class #define NS_ILISTCONTROLFRAME_IID \ - { 0xa28ca6f, 0x6850, 0x11da, \ - { 0x95, 0x2c, 0x0, 0xe0, 0x81, 0x61, 0x16, 0x5f } } +{ 0x4de9ab73, 0x31b5, 0x4d92, \ + { 0xb7, 0xe4, 0x73, 0xb4, 0x4d, 0xcb, 0xfc, 0xda } } /** * nsIListControlFrame is the interface for frame-based listboxes. @@ -81,10 +81,10 @@ public: virtual void CaptureMouseEvents(PRBool aGrabMouseEvents) = 0; /** - * Returns the maximum width and height of an item in the listbox + * Returns the height of a single row in the list. This is the + * maximum of the heights of all the options/optgroups. */ - - virtual nsSize GetMaximumSize() = 0; + virtual nscoord GetHeightOfARow() = 0; /** * Returns the number of options in the listbox @@ -112,11 +112,6 @@ public: */ virtual void FireOnChange() = 0; - /** - * - */ - virtual void SetOverrideReflowOptimization(PRBool aValue) = 0; - /** * Tell the selected list to roll up and ensure that the proper index is * selected, possibly firing onChange if the index has changed diff --git a/mozilla/layout/forms/nsImageControlFrame.cpp b/mozilla/layout/forms/nsImageControlFrame.cpp index 25acb4f3208..8ede9abdece 100644 --- a/mozilla/layout/forms/nsImageControlFrame.cpp +++ b/mozilla/layout/forms/nsImageControlFrame.cpp @@ -84,6 +84,7 @@ public: nsEventStatus* aEventStatus); virtual nsIAtom* GetType() const; + virtual PRBool IsFrameOfType(PRUint32 aFlags) const; #ifdef ACCESSIBILITY NS_IMETHOD GetAccessible(nsIAccessible** aAccessible); @@ -193,15 +194,21 @@ nsImageControlFrame::GetType() const return nsLayoutAtoms::imageControlFrame; } +PRBool +nsImageControlFrame::IsFrameOfType(PRUint32 aFlags) const +{ + return !(aFlags & ~(eReplaced)); +} + NS_METHOD nsImageControlFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { - DO_GLOBAL_REFLOW_COUNT("nsImageControlFrame", aReflowState.reason); + DO_GLOBAL_REFLOW_COUNT("nsImageControlFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); - if (aReflowState.reason == eReflowReason_Initial) { + if (mState & NS_FRAME_FIRST_REFLOW) { nsFormControlFrame::RegUnRegAccessKey(NS_STATIC_CAST(nsIFrame*, this), PR_TRUE); } return nsImageControlFrameSuper::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); diff --git a/mozilla/layout/forms/nsIsIndexFrame.cpp b/mozilla/layout/forms/nsIsIndexFrame.cpp index dc7c94cf25d..b495ffbe5cf 100644 --- a/mozilla/layout/forms/nsIsIndexFrame.cpp +++ b/mozilla/layout/forms/nsIsIndexFrame.cpp @@ -267,23 +267,16 @@ nsIsIndexFrame::ScrollIntoView(nsPresContext* aPresContext) } } - -NS_IMETHODIMP nsIsIndexFrame::Reflow(nsPresContext* aPresContext, - nsHTMLReflowMetrics& aDesiredSize, - const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus) +nscoord +nsIsIndexFrame::GetMinWidth(nsIRenderingContext *aRenderingContext) { - DO_GLOBAL_REFLOW_COUNT("nsIsIndexFrame", aReflowState.reason); - DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); + nscoord result; + DISPLAY_MIN_WIDTH(this, result); - // The Areaframe takes care of all our reflow - // (except for when style is used to change its size?) - nsresult rv = nsAreaFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); - if (aDesiredSize.mComputeMEW) { - aDesiredSize.SetMEWToActualWidth(aReflowState.mStylePosition->mWidth.GetUnit()); - } - NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); - return rv; + // Our min width is our pref width; the rest of our reflow is + // happily handled by nsAreaFrame + result = GetPrefWidth(aRenderingContext); + return result; } PRBool diff --git a/mozilla/layout/forms/nsIsIndexFrame.h b/mozilla/layout/forms/nsIsIndexFrame.h index 477207701e9..3a4aaf6ec57 100644 --- a/mozilla/layout/forms/nsIsIndexFrame.h +++ b/mozilla/layout/forms/nsIsIndexFrame.h @@ -84,11 +84,8 @@ public: // nsIFormControlFrame NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); - NS_IMETHOD Reflow(nsPresContext* aCX, - nsHTMLReflowMetrics& aDesiredSize, - const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus); - + virtual nscoord GetMinWidth(nsIRenderingContext *aRenderingContext); + virtual PRBool IsLeaf() const; #ifdef NS_DEBUG diff --git a/mozilla/layout/forms/nsLegendFrame.cpp b/mozilla/layout/forms/nsLegendFrame.cpp index aadbca2a562..68d9035427c 100644 --- a/mozilla/layout/forms/nsLegendFrame.cpp +++ b/mozilla/layout/forms/nsLegendFrame.cpp @@ -99,11 +99,11 @@ nsLegendFrame::Reflow(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { - DO_GLOBAL_REFLOW_COUNT("nsLegendFrame", aReflowState.reason); + DO_GLOBAL_REFLOW_COUNT("nsLegendFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); - if (eReflowReason_Initial == aReflowState.reason) { + if (mState & NS_FRAME_FIRST_REFLOW) { nsFormControlFrame::RegUnRegAccessKey(NS_STATIC_CAST(nsIFrame*, this), PR_TRUE); - } + } return nsAreaFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); } diff --git a/mozilla/layout/forms/nsListControlFrame.cpp b/mozilla/layout/forms/nsListControlFrame.cpp index d62e4a48015..1027defb549 100644 --- a/mozilla/layout/forms/nsListControlFrame.cpp +++ b/mozilla/layout/forms/nsListControlFrame.cpp @@ -58,7 +58,6 @@ #include "nsIScrollableView.h" #include "nsIDOMHTMLOptGroupElement.h" #include "nsWidgetsCID.h" -#include "nsHTMLReflowCommand.h" #include "nsIPresShell.h" #include "nsHTMLParts.h" #include "nsIDOMEventReceiver.h" @@ -86,7 +85,6 @@ #include "nsISelectElement.h" #include "nsIPrivateDOMEvent.h" #include "nsCSSRendering.h" -#include "nsReflowPath.h" #include "nsITheme.h" #include "nsIDOMMouseListener.h" #include "nsIDOMMouseMotionListener.h" @@ -166,123 +164,16 @@ NS_NewListControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) return it; } -//----------------------------------------------------------- -// Reflow Debugging Macros -// These let us "see" how many reflow counts are happening -//----------------------------------------------------------- -#ifdef DO_REFLOW_COUNTER - -#define MAX_REFLOW_CNT 1024 -static PRInt32 gTotalReqs = 0;; -static PRInt32 gTotalReflows = 0;; -static PRInt32 gReflowControlCntRQ[MAX_REFLOW_CNT]; -static PRInt32 gReflowControlCnt[MAX_REFLOW_CNT]; -static PRInt32 gReflowInx = -1; - -#define REFLOW_COUNTER() \ - if (mReflowId > -1) \ - gReflowControlCnt[mReflowId]++; - -#define REFLOW_COUNTER_REQUEST() \ - if (mReflowId > -1) \ - gReflowControlCntRQ[mReflowId]++; - -#define REFLOW_COUNTER_DUMP(__desc) \ - if (mReflowId > -1) {\ - gTotalReqs += gReflowControlCntRQ[mReflowId];\ - gTotalReflows += gReflowControlCnt[mReflowId];\ - printf("** Id:%5d %s RF: %d RQ: %d %d/%d %5.2f\n", \ - mReflowId, (__desc), \ - gReflowControlCnt[mReflowId], \ - gReflowControlCntRQ[mReflowId],\ - gTotalReflows, gTotalReqs, float(gTotalReflows)/float(gTotalReqs)*100.0f);\ - } - -#define REFLOW_COUNTER_INIT() \ - if (gReflowInx < MAX_REFLOW_CNT) { \ - gReflowInx++; \ - mReflowId = gReflowInx; \ - gReflowControlCnt[mReflowId] = 0; \ - gReflowControlCntRQ[mReflowId] = 0; \ - } else { \ - mReflowId = -1; \ - } - -// reflow messages -#define REFLOW_DEBUG_MSG(_msg1) printf((_msg1)) -#define REFLOW_DEBUG_MSG2(_msg1, _msg2) printf((_msg1), (_msg2)) -#define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3) printf((_msg1), (_msg2), (_msg3)) -#define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4) printf((_msg1), (_msg2), (_msg3), (_msg4)) - -#else //------------- - -#define REFLOW_COUNTER_REQUEST() -#define REFLOW_COUNTER() -#define REFLOW_COUNTER_DUMP(__desc) -#define REFLOW_COUNTER_INIT() - -#define REFLOW_DEBUG_MSG(_msg) -#define REFLOW_DEBUG_MSG2(_msg1, _msg2) -#define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3) -#define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4) - - -#endif - -//------------------------------------------ -// This is for being VERY noisy -//------------------------------------------ -#ifdef DO_VERY_NOISY -#define REFLOW_NOISY_MSG(_msg1) printf((_msg1)) -#define REFLOW_NOISY_MSG2(_msg1, _msg2) printf((_msg1), (_msg2)) -#define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3) printf((_msg1), (_msg2), (_msg3)) -#define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4) printf((_msg1), (_msg2), (_msg3), (_msg4)) -#else -#define REFLOW_NOISY_MSG(_msg) -#define REFLOW_NOISY_MSG2(_msg1, _msg2) -#define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3) -#define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4) -#endif - -//------------------------------------------ -// Displays value in pixels or twips -//------------------------------------------ -#ifdef DO_PIXELS -#define PX(__v) __v / 15 -#else -#define PX(__v) __v -#endif - -//------------------------------------------ -// Asserts if we return a desired size that -// doesn't correctly match the mComputedWidth -//------------------------------------------ -#ifdef DO_UNCONSTRAINED_CHECK -#define UNCONSTRAINED_CHECK() \ -if (aReflowState.mComputedWidth != NS_UNCONSTRAINEDSIZE) { \ - nscoord width = aDesiredSize.width - borderPadding.left - borderPadding.right; \ - if (width != aReflowState.mComputedWidth) { \ - printf("aDesiredSize.width %d %d != aReflowState.mComputedWidth %d\n", aDesiredSize.width, width, aReflowState.mComputedWidth); \ - } \ - NS_ASSERTION(width == aReflowState.mComputedWidth, "Returning bad value when constrained!"); \ -} -#else -#define UNCONSTRAINED_CHECK() -#endif -//------------------------------------------------------ -//-- Done with macros -//------------------------------------------------------ - //--------------------------------------------------------- nsListControlFrame::nsListControlFrame( nsIPresShell* aShell, nsIDocument* aDocument, nsStyleContext* aContext) - : nsHTMLScrollFrame(aShell, aContext, PR_FALSE) + : nsHTMLScrollFrame(aShell, aContext, PR_FALSE), + mMightNeedSecondPass(PR_FALSE), + mLastDropdownComputedHeight(NS_UNCONSTRAINEDSIZE) { mComboboxFrame = nsnull; mChangesSinceDragStart = PR_FALSE; mButtonDown = PR_FALSE; - mMaxWidth = 0; - mMaxHeight = 0; mIsAllContentHere = PR_FALSE; mIsAllFramesHere = PR_FALSE; @@ -290,26 +181,12 @@ nsListControlFrame::nsListControlFrame( mNeedToReset = PR_TRUE; mPostChildrenLoadedReset = PR_FALSE; - mCacheSize.width = -1; - mCacheSize.height = -1; - mCachedMaxElementWidth = -1; - mCachedAvailableSize.width = -1; - mCachedAvailableSize.height = -1; - mCachedUnconstrainedSize.width = -1; - mCachedUnconstrainedSize.height = -1; - - mOverrideReflowOpt = PR_FALSE; - mPassId = 0; - mControlSelectMode = PR_FALSE; - REFLOW_COUNTER_INIT() } //--------------------------------------------------------- nsListControlFrame::~nsListControlFrame() { - REFLOW_COUNTER_DUMP("nsLCF"); - mComboboxFrame = nsnull; } @@ -538,23 +415,6 @@ NS_IMETHODIMP nsListControlFrame::GetAccessible(nsIAccessible** aAccessible) } #endif -//--------------------------------------------------------- -// Reflow is overriden to constrain the listbox height to the number of rows and columns -// specified. -#ifdef DO_REFLOW_DEBUG -static int myCounter = 0; - -static void printSize(char * aDesc, nscoord aSize) -{ - printf(" %s: ", aDesc); - if (aSize == NS_UNCONSTRAINEDSIZE) { - printf("UNC"); - } else { - printf("%d", aSize); - } -} -#endif - static nscoord GetMaxOptionHeight(nsIFrame* aContainer) { @@ -630,456 +490,285 @@ GetOptGroupLabelsHeight(nsPresContext* aPresContext, //----------------------------------------------------------------- // Main Reflow for ListBox/Dropdown //----------------------------------------------------------------- + +nscoord +nsListControlFrame::CalcHeightOfARow() +{ + // Calculate the height of a single row in the listbox or dropdown list by + // using the tallest thing in the subtree, since there may be option groups + // in addition to option elements, either of which may be visible or + // invisible, may use different fonts, etc. + PRInt32 heightOfARow = GetMaxOptionHeight(GetOptionsContainer()); + + // Check to see if we have zero items + if (heightOfARow == 0) { + heightOfARow = CalcFallbackRowHeight(GetNumberOfOptions()); + } + + return heightOfARow; +} + +nscoord +nsListControlFrame::GetMinWidth(nsIRenderingContext *aRenderingContext) +{ + // We don't want to have options wrapping unless they absolutely + // have to, so our min width is our pref width. + nscoord result; + DISPLAY_MIN_WIDTH(this, result); + + result = GetPrefWidth(aRenderingContext); + + return result; +} + NS_IMETHODIMP nsListControlFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { - DO_GLOBAL_REFLOW_COUNT("nsListControlFrame", aReflowState.reason); - DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); - REFLOW_COUNTER_REQUEST(); - - aStatus = NS_FRAME_COMPLETE; - -#ifdef DO_REFLOW_DEBUG - printf("%p ** Id: %d nsLCF::Reflow %d R: ", this, mReflowId, myCounter++); - switch (aReflowState.reason) { - case eReflowReason_Initial: - printf("Initia");break; - case eReflowReason_Incremental: - printf("Increm");break; - case eReflowReason_Resize: - printf("Resize");break; - case eReflowReason_StyleChange: - printf("StyleC");break; - case eReflowReason_Dirty: - printf("Dirty ");break; - default:printf("%d", aReflowState.reason);break; - } - - printSize("AW", aReflowState.availableWidth); - printSize("AH", aReflowState.availableHeight); - printSize("CW", aReflowState.mComputedWidth); - printSize("CH", aReflowState.mComputedHeight); - printf("\n"); -#if 0 - { - const nsStyleDisplay* display = GetStyleDisplay(); - printf("+++++++++++++++++++++++++++++++++ "); - switch (display->mVisible) { - case NS_STYLE_VISIBILITY_COLLAPSE: printf("NS_STYLE_VISIBILITY_COLLAPSE\n");break; - case NS_STYLE_VISIBILITY_HIDDEN: printf("NS_STYLE_VISIBILITY_HIDDEN\n");break; - case NS_STYLE_VISIBILITY_VISIBLE: printf("NS_STYLE_VISIBILITY_VISIBLE\n");break; - } - } -#endif -#endif // DEBUG_rodsXXX - - PRBool bailOnWidth; - PRBool bailOnHeight; - // This ifdef is for turning off the optimization - // so we can check timings against the old version -#if 1 - - nsFormControlFrame::SkipResizeReflow(mCacheSize, - mCachedAscent, - mCachedMaxElementWidth, - mCachedAvailableSize, - aDesiredSize, aReflowState, - aStatus, - bailOnWidth, bailOnHeight); - - // Here we bail if both the height and the width haven't changed - // also we see if we should override the optimization - // - // The optimization can get overridden by the combobox - // sometime the combobox knows that the list MUST do a full reflow - // no matter what - if (!mOverrideReflowOpt && bailOnWidth && bailOnHeight) { - REFLOW_DEBUG_MSG3("*** Done nsLCF - Bailing on DW: %d DH: %d ", PX(aDesiredSize.width), PX(aDesiredSize.height)); - REFLOW_DEBUG_MSG3("bailOnWidth %d bailOnHeight %d\n", PX(bailOnWidth), PX(bailOnHeight)); - NS_ASSERTION(aDesiredSize.width < 100000, "Width is still NS_UNCONSTRAINEDSIZE"); - NS_ASSERTION(aDesiredSize.height < 100000, "Height is still NS_UNCONSTRAINEDSIZE"); - return NS_OK; - } else if (mOverrideReflowOpt) { - mOverrideReflowOpt = PR_FALSE; - } - -#else - bailOnWidth = PR_FALSE; - bailOnHeight = PR_FALSE; -#endif + NS_PRECONDITION(aReflowState.mComputedWidth != NS_UNCONSTRAINEDSIZE, + "Must have a computed width"); // If all the content and frames are here // then initialize it before reflow - if (mIsAllContentHere && !mHasBeenInitialized) { - if (PR_FALSE == mIsAllFramesHere) { - CheckIfAllFramesHere(); - } - if (mIsAllFramesHere && !mHasBeenInitialized) { - mHasBeenInitialized = PR_TRUE; - } + if (mIsAllContentHere && !mHasBeenInitialized) { + if (PR_FALSE == mIsAllFramesHere) { + CheckIfAllFramesHere(); } - - - if (eReflowReason_Incremental == aReflowState.reason) { - nsHTMLReflowCommand *command = aReflowState.path->mReflowCommand; - if (command) { - // XXX So this may do it too often - // the side effect of this is if the user has scrolled to some other place in the list and - // an incremental reflow comes through the list gets scrolled to the first selected item - // I haven't been able to make it do it, but it will do it - // basically the real solution is to know when all the reframes are there. - PRInt32 selectedIndex = mEndSelectionIndex; - if (selectedIndex == kNothingSelected) { - selectedIndex = GetSelectedIndex(); - } - ScrollToIndex(selectedIndex); - } + if (mIsAllFramesHere && !mHasBeenInitialized) { + mHasBeenInitialized = PR_TRUE; } - - // Strategy: Let the inherited reflow happen as though the width and height of the - // ScrollFrame are big enough to allow the listbox to - // shrink to fit the longest option element line in the list. - // The desired width and height returned by the inherited reflow is returned, - // unless one of the following has been specified. - // 1. A css width has been specified. - // 2. The size has been specified. - // 3. The css height has been specified, but the number of rows has not. - // The size attribute overrides the height setting but the height setting - // should be honored if there isn't a size specified. - - // Determine the desired width + height for the listbox + - aDesiredSize.width = 0; - aDesiredSize.height = 0; - - // Add the list frame as a child of the form - if (eReflowReason_Initial == aReflowState.reason) { - nsFormControlFrame::RegUnRegAccessKey(NS_STATIC_CAST(nsIFrame*, this), PR_TRUE); } - //--Calculate a width just big enough for the scrollframe to shrink around the - //longest element in the list - nsHTMLReflowState secondPassState(aReflowState); - nsHTMLReflowState firstPassState(aReflowState); + if (GetStateBits() & NS_FRAME_FIRST_REFLOW) { + nsFormControlFrame::RegUnRegAccessKey(this, PR_TRUE); + } - //nsHTMLReflowState firstPassState(aPresContext, nsnull, - // this, aDesiredSize); + if (IsInDropDownMode()) { + return ReflowAsDropdown(aPresContext, aDesiredSize, aReflowState, aStatus); + } - // Get the size of option elements inside the listbox - // Compute the width based on the longest line in the listbox. + /* + * Due to the fact that our intrinsic height depends on the heights of our + * kids, we end up having to do two-pass reflow, in general -- the first pass + * to find the intrinsic height and a second pass to reflow the scrollframe + * at that height (which will size the scrollbars correctly, etc). + * + * Naturaly, we want to avoid doing the second reflow as much as possible. + * We can skip it in the following cases (in all of which the first reflow is + * already happening at the right height): + * + * - We're reflowing with a constrained computed height -- just use that + * height. + * - We're not dirty and have no dirty kids. In this case, our cached max + * height of a child is not going to change. + * - We do our first reflow using our cached max height of a child, then + * compute the new max height and it's the same as the old one. + */ + + PRBool autoHeight = (aReflowState.mComputedHeight == NS_UNCONSTRAINEDSIZE); + + mMightNeedSecondPass = autoHeight && + (GetStateBits() & (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)); - firstPassState.mComputedWidth = NS_UNCONSTRAINEDSIZE; - firstPassState.mComputedHeight = NS_UNCONSTRAINEDSIZE; - firstPassState.availableWidth = NS_UNCONSTRAINEDSIZE; - firstPassState.availableHeight = NS_UNCONSTRAINEDSIZE; - - nsHTMLReflowMetrics scrolledAreaDesiredSize(PR_TRUE); + nsHTMLReflowState state(aReflowState); + PRInt32 length = GetNumberOfOptions(); - - if (eReflowReason_Incremental == aReflowState.reason) { - nsHTMLReflowCommand *command = firstPassState.path->mReflowCommand; - if (command) { - nsReflowType type; - command->GetType(type); - firstPassState.reason = eReflowReason_StyleChange; - firstPassState.path = nsnull; - } else { - nsresult res = nsHTMLScrollFrame::Reflow(aPresContext, - scrolledAreaDesiredSize, - aReflowState, - aStatus); - if (NS_FAILED(res)) { - NS_ASSERTION(aDesiredSize.width < 100000, "Width is still NS_UNCONSTRAINEDSIZE"); - NS_ASSERTION(aDesiredSize.height < 100000, "Height is still NS_UNCONSTRAINEDSIZE"); - return res; - } - - firstPassState.reason = eReflowReason_StyleChange; - firstPassState.path = nsnull; - } + nscoord oldHeightOfARow = HeightOfARow(); + + if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW) && autoHeight) { + // When not doing an initial reflow, and when the height is auto, start off + // with our computed height set to what we'd expect our height to be. + state.mComputedHeight = CalcIntrinsicHeight(oldHeightOfARow, length); + state.ApplyMinMaxConstraints(nsnull, &state.mComputedHeight); } - if (mPassId == 0 || mPassId == 1) { - if (mPassId == 0) { - // We will reflow again, so tell the scrollframe not to scroll-to-restored-position - // yet - SetSuppressScrollbarUpdate(PR_TRUE); - } - nsresult res = nsHTMLScrollFrame::Reflow(aPresContext, - scrolledAreaDesiredSize, - firstPassState, - aStatus); - if (mPassId == 0) { - SetSuppressScrollbarUpdate(PR_FALSE); - } - - if (NS_FAILED(res)) { - NS_ASSERTION(aDesiredSize.width < 100000, "Width is still NS_UNCONSTRAINEDSIZE"); - NS_ASSERTION(aDesiredSize.height < 100000, "Height is still NS_UNCONSTRAINEDSIZE"); - return res; - } - mCachedUnconstrainedSize.width = scrolledAreaDesiredSize.width; - mCachedUnconstrainedSize.height = scrolledAreaDesiredSize.height; - mCachedAscent = scrolledAreaDesiredSize.ascent; - mCachedDesiredMEW = scrolledAreaDesiredSize.mMaxElementWidth; + nsresult rv = nsHTMLScrollFrame::Reflow(aPresContext, aDesiredSize, + state, aStatus); + NS_ENSURE_SUCCESS(rv, rv); + + if (!mMightNeedSecondPass) { + NS_ASSERTION(!autoHeight || HeightOfARow() == oldHeightOfARow, + "How did our height of a row change if nothing was dirty?"); + NS_ASSERTION(!IsScrollbarUpdateSuppressed(), + "Shouldn't be suppressing if we don't need a second pass!"); + return rv; + } + + mMightNeedSecondPass = PR_FALSE; + + nscoord heightOfARow = HeightOfARow(); + + // Now see whether we need a second pass. If the height of a row has not + // changed, we don't. See similar logic in nsSelectsAreaFrame::Reflow. We + // need to match it here. + if (heightOfARow == oldHeightOfARow) { + // All done. No need to do more reflow. + NS_ASSERTION(!IsScrollbarUpdateSuppressed(), + "Shouldn't be suppressing if the height of a row has not " + "changed!"); + return rv; + } + + NS_ASSERTION(IsScrollbarUpdateSuppressed(), + "Why didn't we suppress for our second pass?"); + + SetSuppressScrollbarUpdate(PR_FALSE); + + // Gotta reflow again. + // XXXbz We're just changing the height here; do we need to dirty ourselves + // or anything like that? We might need to, per the letter of the reflow + // protocol, but things seem to work fine without it... Is that just an + // implementation detail of nsHTMLScrollFrame that we're depending on? + nsHTMLScrollFrame::DidReflow(aPresContext, &state, aStatus); + + // Now compute the height we want to have + state.mComputedHeight = CalcIntrinsicHeight(heightOfARow, length); + state.ApplyMinMaxConstraints(nsnull, &state.mComputedHeight); + + nsHTMLScrollFrame::WillReflow(aPresContext); + + // XXXbz to make the ascent really correct, we should add our + // mComputedPadding.top to it (and subtract it from descent). Need that + // because nsGfxScrollFrame just adds in the border.... + return nsHTMLScrollFrame::Reflow(aPresContext, aDesiredSize, state, aStatus); +} + +nsresult +nsListControlFrame::ReflowAsDropdown(nsPresContext* aPresContext, + nsHTMLReflowMetrics& aDesiredSize, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus) +{ + NS_PRECONDITION(aReflowState.mComputedHeight == NS_UNCONSTRAINEDSIZE, + "We should not have a computed height here!"); + + mMightNeedSecondPass = + (GetStateBits() & (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) != 0; + + nscoord oldHeightOfARow = HeightOfARow(); + + nsHTMLReflowState state(aReflowState); + + nscoord oldVisibleHeight; + if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { + // When not doing an initial reflow, and when the height is auto, start off + // with our computed height set to what we'd expect our height to be. + // Note: At this point, mLastDropdownComputedHeight can be + // NS_UNCONSTRAINEDSIZE in cases when last time we didn't have to constrain + // the height. That's fine; just do the same thing as last time. + state.mComputedHeight = mLastDropdownComputedHeight; + oldVisibleHeight = GetScrolledFrame()->GetSize().height; } else { - scrolledAreaDesiredSize.width = mCachedUnconstrainedSize.width; - scrolledAreaDesiredSize.height = mCachedUnconstrainedSize.height; - scrolledAreaDesiredSize.mMaxElementWidth = mCachedDesiredMEW; + // Set oldVisibleHeight to something that will never test true against a + // real height. + oldVisibleHeight = NS_UNCONSTRAINEDSIZE; } - // Compute the bounding box of the contents of the list using the area - // calculated by the first reflow as a starting point. - // - // The nsHTMLScrollFrame::Reflow adds border and padding to the - // maxElementWidth, so these need to be subtracted - nscoord scrolledAreaWidth = scrolledAreaDesiredSize.width - - (aReflowState.mComputedBorderPadding.left + aReflowState.mComputedBorderPadding.right); - nscoord scrolledAreaHeight = scrolledAreaDesiredSize.height - - (aReflowState.mComputedBorderPadding.top + aReflowState.mComputedBorderPadding.bottom); + nsresult rv = nsHTMLScrollFrame::Reflow(aPresContext, aDesiredSize, + state, aStatus); + NS_ENSURE_SUCCESS(rv, rv); - // Set up max values - mMaxWidth = scrolledAreaWidth; - - // Now the scrolledAreaWidth and scrolledAreaHeight are exactly - // wide and high enough to enclose their contents - PRBool isInDropDownMode = IsInDropDownMode(); - - nscoord visibleWidth = 0; - if (NS_UNCONSTRAINEDSIZE == aReflowState.mComputedWidth) { - visibleWidth = scrolledAreaWidth; - } else { - visibleWidth = aReflowState.mComputedWidth; + if (!mMightNeedSecondPass) { + NS_ASSERTION(oldVisibleHeight == GetScrolledFrame()->GetSize().height, + "How did our kid's height change if nothing was dirty?"); + NS_ASSERTION(HeightOfARow() == oldHeightOfARow, + "How did our height of a row change if nothing was dirty?"); + NS_ASSERTION(!IsScrollbarUpdateSuppressed(), + "Shouldn't be suppressing if we don't need a second pass!"); + NS_ASSERTION(!(GetStateBits() & NS_FRAME_FIRST_REFLOW), + "How can we avoid a second pass during first reflow?"); + return rv; } - // Determine if a scrollbar will be needed, If so we need to add - // enough the width to allow for the scrollbar. - // The scrollbar will be needed under two conditions: - // (size * heightOfaRow) < scrolledAreaHeight or - // the height set through Style < scrolledAreaHeight. + mMightNeedSecondPass = PR_FALSE; - // Calculate the height of a single row in the listbox or dropdown - // list by using the tallest of the grandchildren, since there may be - // option groups in addition to option elements, either of which may - // be visible or invisible. - PRInt32 heightOfARow = GetMaxOptionHeight(GetOptionsContainer()); + nscoord visibleHeight = GetScrolledFrame()->GetSize().height; + nscoord heightOfARow = HeightOfARow(); - // Check to see if we have zero items - PRInt32 length = GetNumberOfOptions(); - if (heightOfARow == 0) { - heightOfARow = CalcFallbackRowHeight(length); + // Now see whether we need a second pass. If the height of a row has not + // changed and neither has the height of our scrolled frame, we don't. See + // similar logic in nsSelectsAreaFrame::Reflow. We need to match it here. + if (visibleHeight == oldVisibleHeight && + heightOfARow == oldHeightOfARow) { + // All done. No need to do more reflow. + NS_ASSERTION(!IsScrollbarUpdateSuppressed(), + "Shouldn't be suppressing if total height has not changed!"); + NS_ASSERTION(!(GetStateBits() & NS_FRAME_FIRST_REFLOW), + "How is the visible height unconstrained?"); + NS_ASSERTION(!(GetStateBits() & NS_FRAME_FIRST_REFLOW), + "How can we avoid a second pass during first reflow?"); + return rv; } - mMaxHeight = heightOfARow; - // Check to see if we have no width and height - // The following code measures the width and height - // of a bogus string so the list actually displays - nscoord visibleHeight = 0; - if (isInDropDownMode) { - // Compute the visible height of the drop-down list - // The dropdown list height is the smaller of its height setting or the height - // of the smallest box that can drawn around its contents. - visibleHeight = scrolledAreaHeight; + NS_ASSERTION(IsScrollbarUpdateSuppressed(), + "Why didn't we suppress for our second pass?"); - mNumDisplayRows = kMaxDropDownRows; - if (visibleHeight > (mNumDisplayRows * heightOfARow)) { - visibleHeight = (mNumDisplayRows * heightOfARow); - // This is an adaptive algorithm for figuring out how many rows - // should be displayed in the drop down. The standard size is 20 rows, - // but on 640x480 it is typically too big. - // This takes the height of the screen divides it by two and then subtracts off - // an estimated height of the combobox. I estimate it by taking the max element size - // of the drop down and multiplying it by 2 (this is arbitrary) then subtract off - // the border and padding of the drop down (again rather arbitrary) - // This all breaks down if the font of the combobox is a lot larger then the option items - // or CSS style has set the height of the combobox to be rather large. - // We can fix these cases later if they actually happen. - if (isInDropDownMode) { - nscoord screenHeightInPixels = 0; - if (NS_SUCCEEDED(nsFormControlFrame::GetScreenHeight(aPresContext, screenHeightInPixels))) { - float p2t; - p2t = aPresContext->PixelsToTwips(); - nscoord screenHeight = NSIntPixelsToTwips(screenHeightInPixels, p2t); + SetSuppressScrollbarUpdate(PR_FALSE); - nscoord availDropHgt = (screenHeight / 2) - (heightOfARow*2); // approx half screen minus combo size - availDropHgt -= aReflowState.mComputedBorderPadding.top + aReflowState.mComputedBorderPadding.bottom; + // Gotta reflow again. + // XXXbz We're just changing the height here; do we need to dirty ourselves + // or anything like that? We might need to, per the letter of the reflow + // protocol, but things seem to work fine without it... Is that just an + // implementation detail of nsHTMLScrollFrame that we're depending on? + nsHTMLScrollFrame::DidReflow(aPresContext, &state, aStatus); - nscoord hgt = visibleHeight + aReflowState.mComputedBorderPadding.top + aReflowState.mComputedBorderPadding.bottom; - if (heightOfARow > 0) { - if (hgt > availDropHgt) { - visibleHeight = (availDropHgt / heightOfARow) * heightOfARow; - } - mNumDisplayRows = visibleHeight / heightOfARow; - } else { - // Hmmm, not sure what to do here. Punt, and make both of them one - visibleHeight = 1; - mNumDisplayRows = 1; - } + // Now compute the height we want to have + mNumDisplayRows = kMaxDropDownRows; + if (visibleHeight > mNumDisplayRows * heightOfARow) { + visibleHeight = mNumDisplayRows * heightOfARow; + // This is an adaptive algorithm for figuring out how many rows + // should be displayed in the drop down. The standard size is 20 rows, + // but on 640x480 it is typically too big. + // This takes the height of the screen divides it by two and then subtracts off + // an estimated height of the combobox. I estimate it by taking the max element size + // of the drop down and multiplying it by 2 (this is arbitrary) then subtract off + // the border and padding of the drop down (again rather arbitrary) + // This all breaks down if the font of the combobox is a lot larger then the option items + // or CSS style has set the height of the combobox to be rather large. + // We can fix these cases later if they actually happen. + nscoord screenHeightInPixels = 0; + if (NS_SUCCEEDED(nsFormControlFrame::GetScreenHeight(aPresContext, screenHeightInPixels))) { + float p2t; + p2t = aPresContext->PixelsToTwips(); + nscoord screenHeight = NSIntPixelsToTwips(screenHeightInPixels, p2t); + + nscoord availDropHgt = (screenHeight / 2) - (heightOfARow*2); // approx half screen minus combo size + availDropHgt -= aReflowState.mComputedBorderPadding.top + aReflowState.mComputedBorderPadding.bottom; + + nscoord hgt = visibleHeight + aReflowState.mComputedBorderPadding.top + aReflowState.mComputedBorderPadding.bottom; + if (heightOfARow > 0) { + if (hgt > availDropHgt) { + visibleHeight = (availDropHgt / heightOfARow) * heightOfARow; } - } - } - - } else { - // Calculate the visible height of the listbox - if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedHeight) { - visibleHeight = aReflowState.mComputedHeight; - } else { - mNumDisplayRows = 1; - GetSizeAttribute(&mNumDisplayRows); - if (mNumDisplayRows >= 1) { - visibleHeight = mNumDisplayRows * heightOfARow; + mNumDisplayRows = visibleHeight / heightOfARow; } else { - // When SIZE=0 or unspecified we constrain the height to [2..kMaxDropDownRows] rows. - // We add in the height of optgroup labels (within the constraint above), bug 300474. - visibleHeight = ::GetOptGroupLabelsHeight(GetPresContext(), mContent, heightOfARow); - - if (GetMultiple()) { - if (length < 2) { - // Add in 1 heightOfARow also when length==0 to match how we calculate the desired size. - visibleHeight = heightOfARow + PR_MAX(heightOfARow, visibleHeight); - } else { - visibleHeight = PR_MIN(kMaxDropDownRows * heightOfARow, length * heightOfARow + visibleHeight); - } - } else { - visibleHeight += 2 * heightOfARow; - } + // Hmmm, not sure what to do here. Punt, and make both of them one + visibleHeight = 1; + mNumDisplayRows = 1; } } - } - // There are no items in the list - // but we want to include space for the scrollbars - // So fake like we will need scrollbars also - if (!isInDropDownMode && 0 == length) { - if (aReflowState.mComputedWidth != 0) { - scrolledAreaHeight = visibleHeight+1; - } - } - - // The visible height is zero, this could be a select with no options - // or a select with a single option that has no content or no label - // - // So this may not be the best solution, but we get the height of the font - // for the list frame and use that as the max/minimum size for the contents - if (visibleHeight == 0) { - if (aReflowState.mComputedHeight != 0) { - nsCOMPtr fontMet; - nsresult rvv = - nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet)); - if (NS_SUCCEEDED(rvv) && fontMet) { - aReflowState.rendContext->SetFont(fontMet); - fontMet->GetHeight(visibleHeight); - mMaxHeight = visibleHeight; - } - } - } - - // When in dropdown mode make sure we obey min/max-width and min/max-height - if (!isInDropDownMode) { - nscoord fullWidth = visibleWidth + aReflowState.mComputedBorderPadding.left + aReflowState.mComputedBorderPadding.right; - if (fullWidth > aReflowState.mComputedMaxWidth) { - visibleWidth = aReflowState.mComputedMaxWidth - aReflowState.mComputedBorderPadding.left - aReflowState.mComputedBorderPadding.right; - } - if (fullWidth < aReflowState.mComputedMinWidth) { - visibleWidth = aReflowState.mComputedMinWidth - aReflowState.mComputedBorderPadding.left - aReflowState.mComputedBorderPadding.right; - } - - // calculate full height for comparison - // must add in Border & Padding twice because the scrolled area also inherits Border & Padding - nscoord fullHeight = 0; - if (aReflowState.mComputedHeight != 0) { - fullHeight = visibleHeight + aReflowState.mComputedBorderPadding.top + aReflowState.mComputedBorderPadding.bottom; - // + aReflowState.mComputedBorderPadding.top + aReflowState.mComputedBorderPadding.bottom; - } - if (fullHeight > aReflowState.mComputedMaxHeight) { - visibleHeight = aReflowState.mComputedMaxHeight - aReflowState.mComputedBorderPadding.top - aReflowState.mComputedBorderPadding.bottom; - } - if (fullHeight < aReflowState.mComputedMinHeight) { - visibleHeight = aReflowState.mComputedMinHeight - aReflowState.mComputedBorderPadding.top - aReflowState.mComputedBorderPadding.bottom; - } - } - - // Do a second reflow with the adjusted width and height settings - // This sets up all of the frames with the correct width and height. - secondPassState.mComputedWidth = visibleWidth; - secondPassState.mComputedHeight = visibleHeight; - secondPassState.reason = eReflowReason_Resize; - - if (mPassId == 0 || mPassId == 2 || visibleHeight != scrolledAreaHeight || - visibleWidth != scrolledAreaWidth) { - nsHTMLScrollFrame::Reflow(aPresContext, aDesiredSize, secondPassState, aStatus); - if (aReflowState.mComputedHeight == 0) { - aDesiredSize.ascent = 0; - aDesiredSize.descent = 0; - aDesiredSize.height = 0; - } - - // Set the max element size to be the same as the desired element size. + state.mComputedHeight = mNumDisplayRows * heightOfARow; + // Note: no need to apply min/max constraints, since we have no such + // rules applied to the combobox dropdown. + // XXXbz this is ending up too big!! Figure out why. + } else if (visibleHeight == 0) { + // Looks like we have no options. Just size us to a single row height. + state.mComputedHeight = heightOfARow; } else { - // aDesiredSize is the desired frame size, so includes border and padding - aDesiredSize.width = visibleWidth + - (aReflowState.mComputedBorderPadding.left + aReflowState.mComputedBorderPadding.right); - aDesiredSize.height = visibleHeight + - (aReflowState.mComputedBorderPadding.top + aReflowState.mComputedBorderPadding.bottom); - aDesiredSize.ascent = - scrolledAreaDesiredSize.ascent + aReflowState.mComputedBorderPadding.top; - aDesiredSize.descent = aDesiredSize.height - aDesiredSize.ascent; + // Not too big, not too small. Just use it! + state.mComputedHeight = NS_UNCONSTRAINEDSIZE; } - if (aDesiredSize.mComputeMEW) { - aDesiredSize.mMaxElementWidth = aDesiredSize.width; - } + // Note: At this point, state.mComputedHeight can be NS_UNCONSTRAINEDSIZE in + // cases when there were some options, but not too many (so no scrollbar was + // needed). That's fine; just store that. + mLastDropdownComputedHeight = state.mComputedHeight; - aStatus = NS_FRAME_COMPLETE; - -#ifdef DEBUG_rodsX - if (!isInDropDownMode) { - PRInt32 numRows = 1; - GetSizeAttribute(&numRows); - printf("%d ", numRows); - if (numRows == 2) { - COMPARE_QUIRK_SIZE("nsListControlFrame", 56, 38) - } else if (numRows == 3) { - COMPARE_QUIRK_SIZE("nsListControlFrame", 56, 54) - } else if (numRows == 4) { - COMPARE_QUIRK_SIZE("nsListControlFrame", 56, 70) - } else { - COMPARE_QUIRK_SIZE("nsListControlFrame", 127, 118) - } - } -#endif - - if (aReflowState.availableWidth != NS_UNCONSTRAINEDSIZE) { - mCachedAvailableSize.width = aDesiredSize.width - (aReflowState.mComputedBorderPadding.left + aReflowState.mComputedBorderPadding.right); - REFLOW_DEBUG_MSG2("** nsLCF Caching AW: %d\n", PX(mCachedAvailableSize.width)); - } - if (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE) { - mCachedAvailableSize.height = aDesiredSize.height - (aReflowState.mComputedBorderPadding.top + aReflowState.mComputedBorderPadding.bottom); - REFLOW_DEBUG_MSG2("** nsLCF Caching AH: %d\n", PX(mCachedAvailableSize.height)); - } - - //REFLOW_DEBUG_MSG3("** nsLCF Caching AW: %d AH: %d\n", PX(mCachedAvailableSize.width), PX(mCachedAvailableSize.height)); - - nsFormControlFrame::SetupCachedSizes(mCacheSize, mCachedAscent, - mCachedMaxElementWidth, aDesiredSize); - - REFLOW_DEBUG_MSG3("** Done nsLCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height)); - - REFLOW_COUNTER(); - -#ifdef DO_REFLOW_COUNTER - if (gReflowControlCnt[mReflowId] > 50) { - REFLOW_DEBUG_MSG3("** Id: %d Cnt: %d ", mReflowId, gReflowControlCnt[mReflowId]); - REFLOW_DEBUG_MSG3("Done nsLCF DW: %d DH: %d\n", PX(aDesiredSize.width), PX(aDesiredSize.height)); - } -#endif - - NS_ASSERTION(aDesiredSize.width < 100000, "Width is still NS_UNCONSTRAINEDSIZE"); - NS_ASSERTION(aDesiredSize.height < 100000, "Height is still NS_UNCONSTRAINEDSIZE"); - NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); - return NS_OK; + nsHTMLScrollFrame::WillReflow(aPresContext); + return nsHTMLScrollFrame::Reflow(aPresContext, aDesiredSize, state, aStatus); } nsGfxScrollFrameInner::ScrollbarStyles @@ -1314,7 +1003,7 @@ nsListControlFrame::CaptureMouseEvents(PRBool aGrabMouseEvents) // code paths, if any exist). if (aGrabMouseEvents && IsInDropDownMode() && nsComboboxControlFrame::ToolkitHasNativePopup()) return; - + nsIView* view = GetScrolledFrame()->GetView(); NS_ASSERTION(view, "no view???"); @@ -1643,8 +1332,6 @@ nsListControlFrame::OnContentReset() void nsListControlFrame::ResetList(PRBool aAllowScrolling) { - REFLOW_DEBUG_MSG("LBX::ResetList\n"); - // if all the frames aren't here // don't bother reseting if (!mIsAllFramesHere) { @@ -1768,7 +1455,7 @@ nsListControlFrame::IsInDropDownMode() const //--------------------------------------------------------- PRInt32 -nsListControlFrame::GetNumberOfOptions() +nsListControlFrame::GetNumberOfOptions() { if (mContent != nsnull) { nsCOMPtr options = GetOptions(mContent); @@ -2156,6 +1843,21 @@ nsListControlFrame::GetType() const return nsLayoutAtoms::listControlFrame; } +PRBool +nsListControlFrame::IsFrameOfType(PRUint32 aFlags) const +{ + return !(aFlags & ~(eReplaced | eReplacedContainsBlock)); +} + +PRBool +nsListControlFrame::IsContainingBlock() const +{ + // We are in fact the containing block for our options. They should + // certainly not use our parent block (or worse yet our parent combobox) for + // their sizing. + return PR_TRUE; +} + void nsListControlFrame::InvalidateInternal(const nsRect& aDamageRect, nscoord aX, nscoord aY, nsIFrame* aForChild, @@ -2175,13 +1877,10 @@ nsListControlFrame::GetFrameName(nsAString& aResult) const #endif //--------------------------------------------------------- -nsSize -nsListControlFrame::GetMaximumSize() +nscoord +nsListControlFrame::GetHeightOfARow() { - nsSize aSize; - aSize.width = mMaxWidth; - aSize.height = mMaxHeight; - return aSize; + return HeightOfARow(); } //---------------------------------------------------------------------- @@ -2251,6 +1950,49 @@ nsListControlFrame::CalcFallbackRowHeight(PRInt32 aNumOptions) return rowHeight; } +nscoord +nsListControlFrame::CalcIntrinsicHeight(nscoord aHeightOfARow, + PRInt32 aNumberOfOptions) +{ + NS_PRECONDITION(!IsInDropDownMode(), + "Shouldn't be in dropdown mode when we call this"); + + mNumDisplayRows = 1; + GetSizeAttribute(&mNumDisplayRows); + + // Extra height to tack on to aHeightOfARow * mNumDisplayRows + nscoord extraHeight = 0; + + if (mNumDisplayRows < 1) { + // When SIZE=0 or unspecified we constrain the height to + // [2..kMaxDropDownRows] rows. We add in the height of optgroup labels + // (within the constraint above), bug 300474. + nscoord labelHeight = + ::GetOptGroupLabelsHeight(GetPresContext(), mContent, aHeightOfARow); + + if (GetMultiple()) { + if (aNumberOfOptions < 2) { + // Add in 1 aHeightOfARow also when aNumberOfOptions == 0 + mNumDisplayRows = 1; + extraHeight = PR_MAX(aHeightOfARow, labelHeight); + } + else if (aNumberOfOptions * aHeightOfARow + labelHeight > + kMaxDropDownRows * aHeightOfARow) { + mNumDisplayRows = kMaxDropDownRows; + } else { + mNumDisplayRows = aNumberOfOptions; + extraHeight = labelHeight; + } + } + else { + NS_NOTREACHED("Shouldn't hit this case -- we should a be a combobox if " + "we have no size set and no multiple set!"); + } + } + + return mNumDisplayRows * aHeightOfARow + extraHeight; +} + //---------------------------------------------------------------------- // nsIDOMMouseListener //---------------------------------------------------------------------- @@ -2261,8 +2003,6 @@ nsListControlFrame::MouseUp(nsIDOMEvent* aMouseEvent) UpdateInListState(aMouseEvent); - REFLOW_DEBUG_MSG("--------------------------- MouseUp ----------------------------\n"); - mButtonDown = PR_FALSE; if (mContent->HasAttr(kNameSpaceID_None, nsHTMLAtoms::disabled)) { @@ -2292,7 +2032,6 @@ nsListControlFrame::MouseUp(nsIDOMEvent* aMouseEvent) const nsStyleVisibility* vis = GetStyleVisibility(); if (!vis->IsVisible()) { - REFLOW_DEBUG_MSG(">>>>>> Select is NOT visible"); return NS_OK; } @@ -2313,8 +2052,6 @@ nsListControlFrame::MouseUp(nsIDOMEvent* aMouseEvent) PRInt32 selectedIndex; if (NS_SUCCEEDED(GetIndexFromDOMEvent(aMouseEvent, selectedIndex))) { - REFLOW_DEBUG_MSG2(">>>>>> Found Index: %d", selectedIndex); - // If it's disabled, disallow the click and leave. PRBool isDisabled = PR_FALSE; IsOptionDisabled(selectedIndex, isDisabled); @@ -2336,7 +2073,6 @@ nsListControlFrame::MouseUp(nsIDOMEvent* aMouseEvent) mouseEvent->clickCount = IgnoreMouseEventForSelection(aMouseEvent) ? 1 : 0; } } else { - REFLOW_DEBUG_MSG(">>>>>> Didn't find"); CaptureMouseEvents(PR_FALSE); // Notify if (mChangesSinceDragStart) { @@ -2510,8 +2246,6 @@ nsListControlFrame::MouseDown(nsIDOMEvent* aMouseEvent) UpdateInListState(aMouseEvent); - REFLOW_DEBUG_MSG("--------------------------- MouseDown ----------------------------\n"); - mButtonDown = PR_TRUE; if (mContent->HasAttr(kNameSpaceID_None, nsHTMLAtoms::disabled)) { @@ -2573,7 +2307,6 @@ nsresult nsListControlFrame::MouseMove(nsIDOMEvent* aMouseEvent) { NS_ASSERTION(aMouseEvent, "aMouseEvent is null."); - //REFLOW_DEBUG_MSG("MouseMove\n"); UpdateInListState(aMouseEvent); @@ -2602,7 +2335,6 @@ nsresult nsListControlFrame::DragMove(nsIDOMEvent* aMouseEvent) { NS_ASSERTION(aMouseEvent, "aMouseEvent is null."); - //REFLOW_DEBUG_MSG("DragMove\n"); UpdateInListState(aMouseEvent); @@ -2855,11 +2587,6 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent) PRUint32 charcode = 0; keyEvent->GetKeyCode(&keycode); keyEvent->GetCharCode(&charcode); -#ifdef DO_REFLOW_DEBUG - if (charcode >= 32) { - REFLOW_DEBUG_MSG3("KeyCode: %c %d\n", charcode, charcode); - } -#endif PRBool isAlt = PR_FALSE; @@ -2913,23 +2640,16 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent) case nsIDOMKeyEvent::DOM_VK_UP: case nsIDOMKeyEvent::DOM_VK_LEFT: { - REFLOW_DEBUG_MSG2("DOM_VK_UP mEndSelectionIndex: %d ", - mEndSelectionIndex); AdjustIndexForDisabledOpt(mEndSelectionIndex, newIndex, (PRInt32)numOptions, -1, -1); - REFLOW_DEBUG_MSG2(" After: %d\n", newIndex); } break; case nsIDOMKeyEvent::DOM_VK_DOWN: case nsIDOMKeyEvent::DOM_VK_RIGHT: { - REFLOW_DEBUG_MSG2("DOM_VK_DOWN mEndSelectionIndex: %d ", - mEndSelectionIndex); - AdjustIndexForDisabledOpt(mEndSelectionIndex, newIndex, (PRInt32)numOptions, 1, 1); - REFLOW_DEBUG_MSG2(" After: %d\n", newIndex); } break; case nsIDOMKeyEvent::DOM_VK_RETURN: { @@ -3115,13 +2835,10 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent) GetPresContext()->PresShell()-> GetDocument()->FlushPendingNotifications(Flush_OnlyReflow); } - REFLOW_DEBUG_MSG2(" After: %d\n", newIndex); // Make sure the SelectArea frame gets painted Invalidate(nsRect(0,0,mRect.width,mRect.height), PR_TRUE); - } else { - REFLOW_DEBUG_MSG(" After: SKIPPED it\n"); } return NS_OK; diff --git a/mozilla/layout/forms/nsListControlFrame.h b/mozilla/layout/forms/nsListControlFrame.h index 432e98fc976..607b2caac6b 100644 --- a/mozilla/layout/forms/nsListControlFrame.h +++ b/mozilla/layout/forms/nsListControlFrame.h @@ -56,6 +56,7 @@ #include "nsIDOMEventListener.h" #include "nsIContent.h" #include "nsAutoPtr.h" +#include "nsSelectsAreaFrame.h" class nsIDOMHTMLSelectElement; class nsIDOMHTMLOptionsCollection; @@ -92,6 +93,9 @@ public: NS_IMETHOD SetInitialChildList(nsIAtom* aListName, nsIFrame* aChildList); + // Our min width is our pref width + virtual nscoord GetMinWidth(nsIRenderingContext *aRenderingContext); + NS_IMETHOD Reflow(nsPresContext* aCX, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, @@ -119,6 +123,10 @@ public: */ virtual nsIAtom* GetType() const; + virtual PRBool IsFrameOfType(PRUint32 aFlags) const; + + virtual PRBool IsContainingBlock() const; + virtual void InvalidateInternal(const nsRect& aDamageRect, nscoord aX, nscoord aY, nsIFrame* aForChild, PRBool aImmediate); @@ -148,12 +156,11 @@ public: virtual PRInt32 GetSelectedIndex(); virtual void GetOptionText(PRInt32 aIndex, nsAString & aStr); virtual void CaptureMouseEvents(PRBool aGrabMouseEvents); - virtual nsSize GetMaximumSize(); + virtual nscoord GetHeightOfARow(); virtual PRInt32 GetNumberOfOptions(); virtual void SyncViewWithFrame(); virtual void AboutToDropDown(); virtual void AboutToRollup(); - virtual void SetOverrideReflowOptimization(PRBool aValue) { mOverrideReflowOpt = aValue; } virtual void FireOnChange(); virtual void ComboboxFinish(PRInt32 aIndex); virtual void OnContentReset(); @@ -190,11 +197,38 @@ public: static void ComboboxFocusSet(); // Helper - void SetPassId(PRInt16 aId) { mPassId = aId; } - PRBool IsFocused() { return this == mFocused; } + + /** + * Function to paint the focus rect when our nsSelectsAreaFrame is painting. + * @param aPt the offset of this frame, relative to the rendering reference + * frame + */ void PaintFocus(nsIRenderingContext& aRC, nsPoint aPt); + /** + * Function to calculate the height a row, for use with the "size" attribute. + * Can't be const because GetNumberOfOptions() isn't const. + */ + nscoord CalcHeightOfARow(); + + /** + * Function to ask whether we're currently in what might be the + * first pass of a two-pass reflow. + */ + PRBool MightNeedSecondPass() const { + return mMightNeedSecondPass; + } + + void SetSuppressScrollbarUpdate(PRBool aSuppress) { + nsHTMLScrollFrame::SetSuppressScrollbarUpdate(aSuppress); + } + + /** + * Return whether the list is in dropdown mode. + */ + PRBool IsInDropDownMode() const; + #ifdef ACCESSIBILITY void FireMenuItemActiveEvent(); // Inform assistive tech what got focused #endif @@ -232,9 +266,22 @@ protected: // we'll just guess at a row height based on our own style. nscoord CalcFallbackRowHeight(PRInt32 aNumOptions); + // CalcIntrinsicHeight computes our intrinsic height (taking the "size" + // attribute into account). This should only be called in non-dropdown mode. + nscoord CalcIntrinsicHeight(nscoord aHeightOfARow, PRInt32 aNumberOfOptions); + // Dropped down stuff void SetComboboxItem(PRInt32 aIndex); - PRBool IsInDropDownMode() const; + + /** + * Method to reflow ourselves as a dropdown list. This differs from + * reflow as a listbox because the criteria for needing a second + * pass are different. This will be called from Reflow() as needed. + */ + nsresult ReflowAsDropdown(nsPresContext* aPresContext, + nsHTMLReflowMetrics& aDesiredSize, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus); // Selection PRBool SetOptionsSelectedFromFrame(PRInt32 aStartIndex, @@ -250,8 +297,12 @@ protected: PRBool HandleListSelection(nsIDOMEvent * aDOMEvent, PRInt32 selectedIndex); void InitSelectionRange(PRInt32 aClickedIndex); - nsIFrame* GetOptionsContainer() { - return GetScrolledFrame(); + nsSelectsAreaFrame* GetOptionsContainer() const { + return NS_STATIC_CAST(nsSelectsAreaFrame*, GetScrolledFrame()); + } + + nscoord HeightOfARow() { + return GetOptionsContainer()->HeightOfARow(); } // Data Members @@ -259,8 +310,6 @@ protected: PRInt32 mEndSelectionIndex; nsIComboboxControlFrame *mComboboxFrame; - nscoord mMaxWidth; - nscoord mMaxHeight; PRInt32 mNumDisplayRows; PRPackedBool mChangesSinceDragStart:1; PRPackedBool mButtonDown:1; @@ -274,23 +323,20 @@ protected: PRPackedBool mNeedToReset:1; PRPackedBool mPostChildrenLoadedReset:1; - PRPackedBool mOverrideReflowOpt:1; - //bool value for multiple discontiguous selection PRPackedBool mControlSelectMode:1; - PRInt16 mPassId; - nscoord mCachedDesiredMEW; + // True if we're in the middle of a reflow and might need a second + // pass. This only happens for auto heights. + PRPackedBool mMightNeedSecondPass:1; + + // The last computed height we reflowed at if we're a combobox dropdown. + // XXXbz should we be using a subclass here? Or just not worry + // about the extra member on listboxes? + nscoord mLastDropdownComputedHeight; nsRefPtr mEventListener; - //Resize Reflow OpitmizationSize; - nsSize mCacheSize; - nscoord mCachedAscent; - nscoord mCachedMaxElementWidth; - nsSize mCachedUnconstrainedSize; - nsSize mCachedAvailableSize; - static nsListControlFrame * mFocused; #ifdef DO_REFLOW_COUNTER diff --git a/mozilla/layout/forms/nsSelectsAreaFrame.cpp b/mozilla/layout/forms/nsSelectsAreaFrame.cpp index b9280be0b04..d31a8c02333 100644 --- a/mozilla/layout/forms/nsSelectsAreaFrame.cpp +++ b/mozilla/layout/forms/nsSelectsAreaFrame.cpp @@ -229,3 +229,51 @@ nsSelectsAreaFrame::BuildDisplayListInternal(nsDisplayListBuilder* aBuilder, return NS_OK; } + +NS_IMETHODIMP +nsSelectsAreaFrame::Reflow(nsPresContext* aPresContext, + nsHTMLReflowMetrics& aDesiredSize, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus) +{ + nsListControlFrame* list = GetEnclosingListFrame(this); + NS_ASSERTION(list, + "Must have an nsListControlFrame! Frame constructor is " + "broken"); + + PRBool isInDropdownMode = list->IsInDropDownMode(); + + // See similar logic in nsListControlFrame::Reflow and + // nsListControlFrame::ReflowAsDropdown. We need to match it here. + nscoord oldHeight; + if (isInDropdownMode) { + // Store the height now in case it changes during + // nsAreaFrame::Reflow for some odd reason. + if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { + oldHeight = GetSize().height; + } else { + oldHeight = NS_UNCONSTRAINEDSIZE; + } + } + + nsresult rv = nsAreaFrame::Reflow(aPresContext, aDesiredSize, + aReflowState, aStatus); + NS_ENSURE_SUCCESS(rv, rv); + + // Check whether we need to suppress scrolbar updates. We want to do that if + // we're in a possible first pass and our height of a row has changed. + if (list->MightNeedSecondPass()) { + nscoord newHeightOfARow = list->CalcHeightOfARow(); + // We'll need a second pass if our height of a row changed. For + // comboboxes, we'll also need it if our height changed. If we're going + // to do a second pass, suppress scrollbar updates for this pass. + if (newHeightOfARow != mHeightOfARow || + (isInDropdownMode && (oldHeight != aDesiredSize.height || + oldHeight != GetSize().height))) { + mHeightOfARow = newHeightOfARow; + list->SetSuppressScrollbarUpdate(PR_TRUE); + } + } + + return rv; +} diff --git a/mozilla/layout/forms/nsSelectsAreaFrame.h b/mozilla/layout/forms/nsSelectsAreaFrame.h index 3e14e89a4b2..87730b3bab9 100644 --- a/mozilla/layout/forms/nsSelectsAreaFrame.h +++ b/mozilla/layout/forms/nsSelectsAreaFrame.h @@ -62,11 +62,27 @@ public: const nsRect& aDirtyRect, const nsDisplayListSet& aLists); + NS_IMETHOD Reflow(nsPresContext* aCX, + nsHTMLReflowMetrics& aDesiredSize, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus); + static PRBool IsOptionElement(nsIContent* aContent); static PRBool IsOptionElementFrame(nsIFrame *aFrame); + nscoord HeightOfARow() const { return mHeightOfARow; } + protected: - nsSelectsAreaFrame(nsStyleContext* aContext) : nsAreaFrame(aContext) {} + nsSelectsAreaFrame(nsStyleContext* aContext) : + nsAreaFrame(aContext), + mHeightOfARow(0) + {} + + // We cache the height of a single row so that changes to the "size" + // attribute, padding, etc. can all be handled with only one reflow. We'll + // have to reflow twice if someone changes our font size or something like + // that, so that the heights of our options will change. + nscoord mHeightOfARow; }; #endif /* nsSelectsAreaFrame_h___ */ diff --git a/mozilla/layout/forms/nsTextControlFrame.cpp b/mozilla/layout/forms/nsTextControlFrame.cpp index 427f3c93762..b6d47d7b4fe 100644 --- a/mozilla/layout/forms/nsTextControlFrame.cpp +++ b/mozilla/layout/forms/nsTextControlFrame.cpp @@ -1292,23 +1292,78 @@ nsTextControlFrame::GetRows() nsresult -nsTextControlFrame::ReflowStandard(nsPresContext* aPresContext, - nsSize& aDesiredSize, - const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus) +nsTextControlFrame::CalcIntrinsicSize(nsIRenderingContext* aRenderingContext, + nsSize& aIntrinsicSize) { - // get the css size and let the frame use or override it - nsSize minSize; - nsresult rv = CalculateSizeStandard(aPresContext, aReflowState, - aDesiredSize, minSize); + // Get leading and the Average/MaxAdvance char width + nscoord lineHeight = 0; + nscoord charWidth = 0; + nscoord charMaxAdvance = 0; + + nsCOMPtr fontMet; + nsresult rv = + nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet)); NS_ENSURE_SUCCESS(rv, rv); + aRenderingContext->SetFont(fontMet); + + nsPresContext* presContext = GetPresContext(); + lineHeight = nsHTMLReflowState::CalcLineHeight(presContext, + aRenderingContext, + this); + fontMet->GetAveCharWidth(charWidth); + fontMet->GetMaxAdvance(charMaxAdvance); + + // Set the width equal to the width in characters + PRInt32 cols = GetCols(); + aIntrinsicSize.width = cols * charWidth; + + // To better match IE, take the maximum character width(in twips) and remove + // 4 pixels add this on as additional padding(internalPadding). But only do + // this if charMaxAdvance != charWidth; if they are equal, this is almost + // certainly a fixed-width font. + if (charWidth != charMaxAdvance) { + float p2t; + p2t = presContext->PixelsToTwips(); + nscoord internalPadding = PR_MAX(charMaxAdvance - NSToCoordRound(4 * p2t), 0); + // round to a multiple of p2t + nscoord t = NSToCoordRound(p2t); + nscoord rest = internalPadding % t; + if (rest < t - rest) { + internalPadding -= rest; + } else { + internalPadding += t - rest; + } + // Now add the extra padding on (so that small input sizes work well) + aIntrinsicSize.width += internalPadding; + } else { + // This is to account for the anonymous
having a 1 twip width + // in Full Standards mode, see BRFrame::Reflow and bug 228752. + if (presContext->CompatibilityMode() == eCompatibility_FullStandards) { + aIntrinsicSize.width += 1; + } + } + + // Increment width with cols * letter-spacing. + { + const nsStyleCoord& lsCoord = GetStyleText()->mLetterSpacing; + if (eStyleUnit_Coord == lsCoord.GetUnit()) { + nscoord letterSpacing = lsCoord.GetCoordValue(); + if (letterSpacing != 0) { + aIntrinsicSize.width += cols * letterSpacing; + } + } + } + + // Set the height equal to total number of rows (times the height of each + // line, of course) + aIntrinsicSize.height = lineHeight * GetRows(); // Add in the size of the scrollbars for textarea if (IsTextArea()) { float p2t; - p2t = aPresContext->PixelsToTwips(); + p2t = presContext->PixelsToTwips(); - nsIDeviceContext *dx = aPresContext->DeviceContext(); + nsIDeviceContext *dx = presContext->DeviceContext(); float scale; dx->GetCanonicalPixelScale(scale); @@ -1320,96 +1375,10 @@ nsTextControlFrame::ReflowStandard(nsPresContext* aPresContext, nscoord scrollbarWidth = PRInt32(sbWidth * scale); nscoord scrollbarHeight = PRInt32(sbHeight * scale); - aDesiredSize.height += scrollbarHeight; - minSize.height += scrollbarHeight; + aIntrinsicSize.height += scrollbarHeight; - aDesiredSize.width += scrollbarWidth; - minSize.width += scrollbarWidth; + aIntrinsicSize.width += scrollbarWidth; } - aDesiredSize.width += aReflowState.mComputedBorderPadding.left + aReflowState.mComputedBorderPadding.right; - aDesiredSize.height += aReflowState.mComputedBorderPadding.top + aReflowState.mComputedBorderPadding.bottom; - - NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); - - return NS_OK; -} - - - -nsresult -nsTextControlFrame::CalculateSizeStandard(nsPresContext* aPresContext, - const nsHTMLReflowState& aReflowState, - nsSize& aDesiredSize, - nsSize& aMinSize) -{ - aDesiredSize.width = CSS_NOTSET; - aDesiredSize.height = CSS_NOTSET; - - // Get leading and the Average/MaxAdvance char width - nscoord lineHeight = 0; - nscoord charWidth = 0; - nscoord charMaxAdvance = 0; - - nsCOMPtr fontMet; - nsresult rv = - nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet)); - NS_ENSURE_SUCCESS(rv, rv); - nsIRenderingContext* rendContext = aReflowState.rendContext; - rendContext->SetFont(fontMet); - lineHeight = aReflowState.CalcLineHeight(aPresContext, rendContext, this); - fontMet->GetAveCharWidth(charWidth); - fontMet->GetMaxAdvance(charMaxAdvance); - - // Set the width equal to the width in characters - PRInt32 cols = GetCols(); - aDesiredSize.width = cols * charWidth; - - // To better match IE, take the maximum character width(in twips) and remove - // 4 pixels add this on as additional padding(internalPadding). But only do - // this if charMaxAdvance != charWidth; if they are equal, this is almost - // certainly a fixed-width font. - if (charWidth != charMaxAdvance) { - float p2t; - p2t = aPresContext->PixelsToTwips(); - nscoord internalPadding = PR_MAX(charMaxAdvance - NSToCoordRound(4 * p2t), 0); - // round to a multiple of p2t - nscoord t = NSToCoordRound(p2t); - nscoord rest = internalPadding % t; - if (rest < t - rest) { - internalPadding -= rest; - } else { - internalPadding += t - rest; - } - // Now add the extra padding on (so that small input sizes work well) - aDesiredSize.width += internalPadding; - } else { - // This is to account for the anonymous
having a 1 twip width - // in Full Standards mode, see BRFrame::Reflow and bug 228752. - if (aPresContext->CompatibilityMode() == eCompatibility_FullStandards) { - aDesiredSize.width += 1; - } - } - - // Increment width with cols * letter-spacing. - { - const nsStyleCoord& lsCoord = GetStyleText()->mLetterSpacing; - if (eStyleUnit_Coord == lsCoord.GetUnit()) { - nscoord letterSpacing = lsCoord.GetCoordValue(); - if (letterSpacing != 0) { - aDesiredSize.width += cols * letterSpacing; - } - } - } - - // Set the height equal to total number of rows (times the height of each - // line, of course) - aDesiredSize.height = lineHeight * GetRows(); - - // Set minimum size equal to desired size. We are form controls. We are Gods - // among elements. We do not yield for anybody, not even a table cell. None - // shall pass. - aMinSize.width = aDesiredSize.width; - aMinSize.height = aDesiredSize.height; return NS_OK; } @@ -1783,13 +1752,33 @@ nsTextControlFrame::CreateAnonymousContent(nsPresContext* aPresContext, return aChildList.AppendElement(divContent); } +nscoord +nsTextControlFrame::GetMinWidth(nsIRenderingContext* aRenderingContext) +{ + // Note: to fix bug 40596 while still working correctly in general, we want + // to return our preferred width as our min width if our style width is auto. + // Otherwise, we're ok with shrinking as small as needed. + nscoord result; + DISPLAY_MIN_WIDTH(this, result); + + if (GetStylePosition()->mWidth.GetUnit() == eStyleUnit_Auto) { + result = GetPrefWidth(aRenderingContext); + } else { + result = 0; + } + + return result; +} + +// We inherit our GetPrefWidth from nsBoxFrame + NS_IMETHODIMP nsTextControlFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { - DO_GLOBAL_REFLOW_COUNT("nsTextControlFrame", aReflowState.reason); + DO_GLOBAL_REFLOW_COUNT("nsTextControlFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); // make sure the the form registers itself on the initial/first reflow @@ -1797,19 +1786,8 @@ nsTextControlFrame::Reflow(nsPresContext* aPresContext, nsFormControlFrame::RegUnRegAccessKey(this, PR_TRUE); } - nsresult rv = nsStackFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); - if (NS_SUCCEEDED(rv)) - { // fix for bug 40596, width:auto means the control sets it's mMaxElementWidth to it's default width - if (aDesiredSize.mComputeMEW) - { - const nsStylePosition* stylePosition = GetStylePosition(); - nsStyleUnit widthUnit = stylePosition->mWidth.GetUnit(); - if (eStyleUnit_Auto == widthUnit) { - aDesiredSize.mMaxElementWidth = aDesiredSize.width; - } - } - } - return rv; + return nsStackFrame::Reflow(aPresContext, aDesiredSize, aReflowState, + aStatus); } NS_IMETHODIMP @@ -1827,24 +1805,15 @@ nsTextControlFrame::GetPrefSize(nsBoxLayoutState& aState, nsSize& aSize) aSize.width = 0; aSize.height = 0; + // XXXbz this is almost certainly wrong. PRBool collapsed = PR_FALSE; IsCollapsed(aState, collapsed); if (collapsed) return NS_OK; - nsPresContext* presContext = aState.PresContext(); - const nsHTMLReflowState* reflowState = aState.GetReflowState(); - // XXXldb Is there a good reason to think this is both non-null and the - // correct reflow state? - if (!reflowState) - return NS_OK; - - nsSize styleSize(CSS_NOTSET,CSS_NOTSET); - nsFormControlFrame::GetStyleSize(presContext, *reflowState, styleSize); - - nsReflowStatus status; - nsresult rv = ReflowStandard(presContext, aSize, *reflowState, status); + nsresult rv = CalcIntrinsicSize(aState.GetRenderingContext(), aSize); NS_ENSURE_SUCCESS(rv, rv); + AddBorderAndPadding(aSize); AddInset(aSize); mPrefSize = aSize; @@ -1869,18 +1838,14 @@ nsTextControlFrame::GetPrefSize(nsBoxLayoutState& aState, nsSize& aSize) NS_IMETHODIMP nsTextControlFrame::GetMinSize(nsBoxLayoutState& aState, nsSize& aSize) { -#define FIX_FOR_BUG_40596 -#ifdef FIX_FOR_BUG_40596 - aSize = mMinSize; - return NS_OK; -#else + // XXXbz why? Why not the nsBoxFrame sizes? return nsBox::GetMinSize(aState, aSize); -#endif } NS_IMETHODIMP nsTextControlFrame::GetMaxSize(nsBoxLayoutState& aState, nsSize& aSize) { + // XXXbz why? Why not the nsBoxFrame sizes? return nsBox::GetMaxSize(aState, aSize); } @@ -1892,7 +1857,9 @@ nsTextControlFrame::GetAscent(nsBoxLayoutState& aState, nscoord& aAscent) NS_ENSURE_SUCCESS(rv, rv); // Now adjust the ascent for our borders and padding - aAscent += aState.GetReflowState()->mComputedBorderPadding.top; + nsMargin borderPadding; + GetBorderAndPadding(borderPadding); + aAscent += borderPadding.top; return NS_OK; } @@ -1903,7 +1870,11 @@ nsTextControlFrame::IsLeaf() const return PR_TRUE; } - +PRBool +nsTextControlFrame::IsFrameOfType(PRUint32 aFlags) const +{ + return !(aFlags & ~(eReplaced | eReplacedContainsBlock)); +} static PRBool IsFocusedContent(nsPresContext* aPresContext, nsIContent* aContent) diff --git a/mozilla/layout/forms/nsTextControlFrame.h b/mozilla/layout/forms/nsTextControlFrame.h index ba612532954..ec7bd6f5b07 100644 --- a/mozilla/layout/forms/nsTextControlFrame.h +++ b/mozilla/layout/forms/nsTextControlFrame.h @@ -79,6 +79,8 @@ public: virtual void Destroy(); + virtual nscoord GetMinWidth(nsIRenderingContext* aRenderingContext); + NS_IMETHOD Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, @@ -105,6 +107,8 @@ public: } #endif + virtual PRBool IsFrameOfType(PRUint32 aFlags) const; + // from nsIAnonymousContentCreator NS_IMETHOD CreateAnonymousContent(nsPresContext* aPresContext, nsISupportsArray& aChildList); @@ -252,15 +256,11 @@ protected: */ PRInt32 GetRows(); - nsresult ReflowStandard(nsPresContext* aPresContext, - nsSize& aDesiredSize, - const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus); - - nsresult CalculateSizeStandard(nsPresContext* aPresContext, - const nsHTMLReflowState& aReflowState, - nsSize& aDesiredSize, - nsSize& aMinSize); + // Compute our intrinsic size. This does not include any borders, paddings, + // etc. Just the size of our actual area for the text (and the scrollbars, + // for