From d490f00b7b8662f2e2dac1aa047fdaecb5941b6b Mon Sep 17 00:00:00 2001 From: "bzbarsky%mit.edu" Date: Fri, 4 Nov 2005 23:36:27 +0000 Subject: [PATCH] Remove the dummy frame stuff from selects. Replace it with smarter focus rect painting and sizing for empty selects. Bug 314879, r+sr=dbaron git-svn-id: svn://10.0.0.236/trunk@184162 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/layout/base/nsCSSFrameConstructor.cpp | 128 ------------------ mozilla/layout/base/nsCSSFrameConstructor.h | 11 -- .../layout/forms/nsComboboxControlFrame.cpp | 26 ---- mozilla/layout/forms/nsComboboxControlFrame.h | 2 - mozilla/layout/forms/nsISelectControlFrame.h | 12 +- mozilla/layout/forms/nsListControlFrame.cpp | 101 +++++++------- mozilla/layout/forms/nsListControlFrame.h | 8 +- mozilla/layout/style/forms.css | 5 +- mozilla/layout/style/nsCSSAnonBoxList.h | 1 - 9 files changed, 61 insertions(+), 233 deletions(-) diff --git a/mozilla/layout/base/nsCSSFrameConstructor.cpp b/mozilla/layout/base/nsCSSFrameConstructor.cpp index 97bd9c81c36..8ea354bfd51 100644 --- a/mozilla/layout/base/nsCSSFrameConstructor.cpp +++ b/mozilla/layout/base/nsCSSFrameConstructor.cpp @@ -5308,16 +5308,6 @@ nsCSSFrameConstructor::InitializeSelectFrame(nsFrameConstructorState& aState, ProcessChildren(aState, aContent, scrolledFrame, PR_FALSE, childItems, PR_TRUE); - // if a select is being created with zero options we need to create - // a special pseudo frame so it can be sized as best it can - nsCOMPtr selectElement(do_QueryInterface(aContent)); - if (selectElement) { - AddDummyFrameToSelect(aState, scrollFrame, scrolledFrame, &childItems, - aContent, selectElement); - } - ////////////////////////////////////////////////// - ////////////////////////////////////////////////// - // Set the scrolled frame's initial child lists scrolledFrame->SetInitialChildList(aState.mPresContext, nsnull, childItems.childList); @@ -8972,17 +8962,6 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, } } - // Here we have been notified that content has been appended so if - // the select now has a single item we need to go in and removed - // the dummy frame. - nsCOMPtr sel(do_QueryInterface(aContainer)); - if (sel) { - nsIContent *childContent = aContainer->GetChildAt(aNewIndexInContainer); - if (childContent) { - RemoveDummyFrameFromSelect(aContainer, childContent, sel); - } - } - #ifdef DEBUG if (gReallyNoisyContentUpdates) { nsIFrameDebug* fdbg = nsnull; @@ -8997,49 +8976,6 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, return NS_OK; } - -nsresult -nsCSSFrameConstructor::AddDummyFrameToSelect(nsFrameConstructorState& aState, - nsIFrame* aListFrame, - nsIFrame* aParentFrame, - nsFrameItems* aChildItems, - nsIContent* aContainer, - nsIDOMHTMLSelectElement* aSelectElement) -{ - PRUint32 numOptions = 0; - nsresult rv = aSelectElement->GetLength(&numOptions); - if (NS_SUCCEEDED(rv) && 0 == numOptions) { - nsISelectControlFrame* listFrame = nsnull; - CallQueryInterface(aListFrame, &listFrame); - if (listFrame) { - nsIFrame* dummyFrame; - listFrame->GetDummyFrame(&dummyFrame); - - if (!dummyFrame) { - nsStyleContext* styleContext = aParentFrame->GetStyleContext(); - nsIFrame* generatedFrame = nsnull; - if (CreateGeneratedContentFrame(aState, aParentFrame, aContainer, - styleContext, - nsCSSAnonBoxes::dummyOption, - &generatedFrame)) { - // Add the generated frame to the child list - if (aChildItems) { - aChildItems->AddChild(generatedFrame); - } else { - aState.mFrameManager->AppendFrames(aParentFrame, nsnull, - generatedFrame); - } - - listFrame->SetDummyFrame(generatedFrame); - return NS_OK; - } - } - } - } - - return NS_ERROR_FAILURE; -} - // defined below static nsresult DeletingFrameSubtree(nsPresContext* aPresContext, @@ -9047,44 +8983,6 @@ DeletingFrameSubtree(nsPresContext* aPresContext, nsFrameManager* aFrameManager, nsIFrame* aFrame); -nsresult -nsCSSFrameConstructor::RemoveDummyFrameFromSelect(nsIContent* aContainer, - nsIContent* aChild, - nsIDOMHTMLSelectElement * aSelectElement) -{ - // Check to see if this is the first thing we have added to this frame. - - PRUint32 numOptions = 0; - nsresult rv = aSelectElement->GetLength(&numOptions); - if (NS_SUCCEEDED(rv) && numOptions > 0) { - nsIFrame* frame = mPresShell->GetPrimaryFrameFor(aContainer); - if (frame) { - nsISelectControlFrame* listFrame = nsnull; - CallQueryInterface(frame, &listFrame); - - if (listFrame) { - nsIFrame* dummyFrame; - listFrame->GetDummyFrame(&dummyFrame); - - if (dummyFrame) { - listFrame->SetDummyFrame(nsnull); - - // get the child's parent frame (which ought to be the list frame) - nsIFrame* parentFrame = dummyFrame->GetParent(); - - nsFrameManager *frameManager = mPresShell->FrameManager(); - DeletingFrameSubtree(mPresShell->GetPresContext(), mPresShell, - frameManager, dummyFrame); - frameManager->RemoveFrame(parentFrame, nsnull, dummyFrame); - return NS_OK; - } - } - } - } - - return NS_ERROR_FAILURE; -} - // Return TRUE if the insertion of aChild into aParent1,2 should force a reframe. aParent1 is // the special inline container which contains a block. aParentFrame is approximately aParent1's // primary frame and will be set to the correct parent of aChild if a reframe is not necessary. @@ -9646,12 +9544,6 @@ nsCSSFrameConstructor::ContentInserted(nsIContent* aContainer, } } } - // Here we have been notified that content has been insert - // so if the select now has a single item - // we need to go in and removed the dummy frame - nsCOMPtr selectElement = do_QueryInterface(aContainer); - if (selectElement) - RemoveDummyFrameFromSelect(aContainer, aChild, selectElement); #ifdef DEBUG if (gReallyNoisyContentUpdates && parentFrame) { @@ -9908,26 +9800,6 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, frameManager->ClearUndisplayedContentIn(aChild, aContainer); } - // When the last item is removed from a select, - // we need to add a pseudo frame so select gets sized as the best it can - // so here we see if it is a select and then we get the number of options - if (aContainer && childFrame) { - nsCOMPtr selectElement = do_QueryInterface(aContainer); - if (selectElement) { - // XXX temp needed only native controls - nsIFrame* selectFrame = mPresShell->GetPrimaryFrameFor(aContainer); - - // For "select" add the pseudo frame after the last item is deleted - nsIFrame* parentFrame = childFrame->GetParent(); - if (parentFrame && parentFrame != selectFrame) { - nsFrameConstructorState state(mPresShell, - nsnull, nsnull, nsnull); - AddDummyFrameToSelect(state, selectFrame, parentFrame, nsnull, - aContainer, selectElement); - } - } - } - #ifdef MOZ_XUL if (NotifyListBoxBody(presContext, aContainer, aChild, aIndexInContainer, mDocument, childFrame, gUseXBLForms, CONTENT_REMOVED)) diff --git a/mozilla/layout/base/nsCSSFrameConstructor.h b/mozilla/layout/base/nsCSSFrameConstructor.h index c2c38d30863..18b1c14dbff 100644 --- a/mozilla/layout/base/nsCSSFrameConstructor.h +++ b/mozilla/layout/base/nsCSSFrameConstructor.h @@ -666,17 +666,6 @@ private: ImageFrameCreatorFunc aFunc, nsIFrame** aFrame); - nsresult AddDummyFrameToSelect(nsFrameConstructorState& aState, - nsIFrame* aListFrame, - nsIFrame* aParentFrame, - nsFrameItems* aChildItems, - nsIContent* aContainer, - nsIDOMHTMLSelectElement* aSelectElement); - - nsresult RemoveDummyFrameFromSelect(nsIContent* aContainer, - nsIContent* aChild, - nsIDOMHTMLSelectElement* aSelectElement); - nsIFrame* GetFrameFor(nsIContent* aContent); nsIFrame* GetAbsoluteContainingBlock(nsIFrame* aFrame); diff --git a/mozilla/layout/forms/nsComboboxControlFrame.cpp b/mozilla/layout/forms/nsComboboxControlFrame.cpp index 33ed8f6778c..f285cbd9635 100644 --- a/mozilla/layout/forms/nsComboboxControlFrame.cpp +++ b/mozilla/layout/forms/nsComboboxControlFrame.cpp @@ -1917,32 +1917,6 @@ nsComboboxControlFrame::GetOptionSelected(PRInt32 aIndex, PRBool* aValue) return listFrame->GetOptionSelected(aIndex, aValue); } -//--------------------------------------------------------- -// Used by layout to determine if we have a fake option -NS_IMETHODIMP -nsComboboxControlFrame::GetDummyFrame(nsIFrame** aFrame) -{ - nsISelectControlFrame* listFrame = nsnull; - NS_ASSERTION(mDropdownFrame, "No dropdown frame!"); - - CallQueryInterface(mDropdownFrame, &listFrame); - NS_ASSERTION(listFrame, "No list frame!"); - - return listFrame->GetDummyFrame(aFrame); -} - -NS_IMETHODIMP -nsComboboxControlFrame::SetDummyFrame(nsIFrame* aFrame) -{ - nsISelectControlFrame* listFrame = nsnull; - NS_ASSERTION(mDropdownFrame, "No dropdown frame!"); - - CallQueryInterface(mDropdownFrame, &listFrame); - NS_ASSERTION(listFrame, "No list frame!"); - - return listFrame->SetDummyFrame(aFrame); -} - NS_IMETHODIMP nsComboboxControlFrame::OnSetSelectedIndex(PRInt32 aOldIndex, PRInt32 aNewIndex) { diff --git a/mozilla/layout/forms/nsComboboxControlFrame.h b/mozilla/layout/forms/nsComboboxControlFrame.h index 3fbc9219d2c..d4539607829 100644 --- a/mozilla/layout/forms/nsComboboxControlFrame.h +++ b/mozilla/layout/forms/nsComboboxControlFrame.h @@ -184,8 +184,6 @@ public: NS_IMETHOD OnOptionSelected(nsPresContext* aPresContext, PRInt32 aIndex, PRBool aSelected); - NS_IMETHOD GetDummyFrame(nsIFrame** aFrame); - NS_IMETHOD SetDummyFrame(nsIFrame* aFrame); NS_IMETHOD OnSetSelectedIndex(PRInt32 aOldIndex, PRInt32 aNewIndex); //nsIRollupListener diff --git a/mozilla/layout/forms/nsISelectControlFrame.h b/mozilla/layout/forms/nsISelectControlFrame.h index e231c92062b..551ddd77b60 100644 --- a/mozilla/layout/forms/nsISelectControlFrame.h +++ b/mozilla/layout/forms/nsISelectControlFrame.h @@ -42,10 +42,10 @@ #include "nsISupports.h" // IID for the nsISelectControlFrame class -// 264dc2f5-1cca-47dd-9ebc-699c430be00a +// f8a1b329-d0d8-4bd5-a9ab-08c3c0f2f166 #define NS_ISELECTCONTROLFRAME_IID \ -{ 0x264dc2f5, 0x1cca, 0x47dd, \ - { 0x9e, 0xbc, 0x69, 0x9c, 0x43, 0x0b, 0xe0, 0x0a } } +{ 0xf8a1b329, 0xd0d8, 0x4bd5, \ + { 0xa9, 0xab, 0x08, 0xc3, 0xc0, 0xf2, 0xf1, 0x66 } } class nsIDOMHTMLOptionElement; @@ -87,12 +87,6 @@ public: PRInt32 aIndex, PRBool aSelected) = 0; - /** - * For the content model to tell if there's a dummy frame or not - */ - NS_IMETHOD GetDummyFrame(nsIFrame** aFrame) = 0; - NS_IMETHOD SetDummyFrame(nsIFrame* aFrame) = 0; - /** * Notify the frame when selectedIndex was changed */ diff --git a/mozilla/layout/forms/nsListControlFrame.cpp b/mozilla/layout/forms/nsListControlFrame.cpp index 1934f3235db..d4103ddec3a 100644 --- a/mozilla/layout/forms/nsListControlFrame.cpp +++ b/mozilla/layout/forms/nsListControlFrame.cpp @@ -301,8 +301,6 @@ nsListControlFrame::nsListControlFrame(nsIPresShell* aShell, mOverrideReflowOpt = PR_FALSE; mPassId = 0; - mDummyFrame = nsnull; - mControlSelectMode = PR_FALSE; REFLOW_COUNTER_INIT() } @@ -486,20 +484,25 @@ void nsListControlFrame::PaintFocus(nsIRenderingContext& aRC, nsFramePaintLayer childframe = presShell->GetPrimaryFrameFor(focusedContent); } if (!childframe) { - // The only way we can get right here is that there are no options - // and we need to get the dummy frame so it has the focus ring + // Failing all else, try the first thing we have. childframe = containerFrame->GetFirstChild(nsnull); result = NS_OK; } } - if (!childframe) return; + nsRect fRect; + if (childframe) { + // get the child rect + fRect = childframe->GetRect(); - // get the child rect - nsRect fRect = childframe->GetRect(); + // get it into the coordinates of containerFrame + fRect.MoveBy(childframe->GetParent()->GetOffsetTo(containerFrame)); + } else { + fRect.x = fRect.y = 0; + fRect.width = mRect.width; + fRect.height = CalcFallbackRowHeight(0); + } - // get it into the coordinates of containerFrame - fRect.MoveBy(childframe->GetParent()->GetOffsetTo(containerFrame)); PRBool lastItemIsSelected = PR_FALSE; if (focusedIndex != kNothingSelected) { nsCOMPtr node; @@ -898,30 +901,8 @@ nsListControlFrame::Reflow(nsPresContext* aPresContext, PRInt32 length = 0; GetNumberOfOptions(&length); - // If there is only one option and that option's content is empty - // then heightOfARow is zero, so we need to go measure - // the height of the option as if it had some text. - if (heightOfARow == 0 && length > 0) { - nsCOMPtr option = GetOptionContent(0); - if (option) { - nsIFrame * optFrame = GetPresContext()->PresShell()-> - GetPrimaryFrameFor(option); - if (optFrame) { - nsStyleContext* optStyle = optFrame->GetStyleContext(); - if (optStyle) { - const nsStyleFont* styleFont = optStyle->GetStyleFont(); - nsCOMPtr fontMet; - nsresult result = aPresContext->DeviceContext()-> - GetMetricsFor(styleFont->mFont, *getter_AddRefs(fontMet)); - if (NS_SUCCEEDED(result) && fontMet) { - if (fontMet) { - fontMet->GetHeight(heightOfARow); - mMaxHeight = heightOfARow; - } - } - } - } - } + if (heightOfARow == 0) { + heightOfARow = CalcFallbackRowHeight(length); } mMaxHeight = heightOfARow; @@ -2167,22 +2148,6 @@ nsListControlFrame::GetOptionSelected(PRInt32 aIndex, PRBool* aValue) return NS_OK; } -//--------------------------------------------------------- -// Used by layout to determine if we have a fake option -NS_IMETHODIMP -nsListControlFrame::GetDummyFrame(nsIFrame** aFrame) -{ - (*aFrame) = mDummyFrame; - return NS_OK; -} - -NS_IMETHODIMP -nsListControlFrame::SetDummyFrame(nsIFrame* aFrame) -{ - mDummyFrame = aFrame; - return NS_OK; -} - NS_IMETHODIMP nsListControlFrame::OnSetSelectedIndex(PRInt32 aOldIndex, PRInt32 aNewIndex) { @@ -2397,6 +2362,44 @@ nsListControlFrame::IsLeftButton(nsIDOMEvent* aMouseEvent) return PR_FALSE; } +nscoord +nsListControlFrame::CalcFallbackRowHeight(PRInt32 aNumOptions) +{ + const nsStyleFont* styleFont = nsnull; + + if (aNumOptions > 0) { + // Try the first option + nsCOMPtr option = GetOptionContent(0); + if (option) { + nsIFrame * optFrame = GetPresContext()->PresShell()-> + GetPrimaryFrameFor(option); + if (optFrame) { + styleFont = optFrame->GetStyleFont(); + } + } + } + + if (!styleFont) { + // Fall back to our own font + styleFont = GetStyleFont(); + } + + NS_ASSERTION(styleFont, "Must have font style by now!"); + + nscoord rowHeight = 0; + + nsCOMPtr fontMet; + nsresult result = GetPresContext()->DeviceContext()-> + GetMetricsFor(styleFont->mFont, *getter_AddRefs(fontMet)); + if (NS_SUCCEEDED(result) && fontMet) { + if (fontMet) { + fontMet->GetHeight(rowHeight); + } + } + + return rowHeight; +} + //---------------------------------------------------------------------- // nsIDOMMouseListener //---------------------------------------------------------------------- diff --git a/mozilla/layout/forms/nsListControlFrame.h b/mozilla/layout/forms/nsListControlFrame.h index 726a2794326..f7bf8f2a830 100644 --- a/mozilla/layout/forms/nsListControlFrame.h +++ b/mozilla/layout/forms/nsListControlFrame.h @@ -182,8 +182,6 @@ public: NS_IMETHOD OnOptionSelected(nsPresContext* aPresContext, PRInt32 aIndex, PRBool aSelected); - NS_IMETHOD GetDummyFrame(nsIFrame** aFrame); - NS_IMETHOD SetDummyFrame(nsIFrame* aFrame); NS_IMETHOD OnSetSelectedIndex(PRInt32 aOldIndex, PRInt32 aNewIndex); // mouse event listeners @@ -241,6 +239,10 @@ protected: PRInt32 GetIndexFromContent(nsIContent *aContent); PRBool IsLeftButton(nsIDOMEvent* aMouseEvent); + // aNumOptions is the number of options we have; if we have none, + // we'll just guess at a row height based on our own style. + nscoord CalcFallbackRowHeight(PRInt32 aNumOptions); + // Dropped down stuff void SetComboboxItem(PRInt32 aIndex); PRBool IsInDropDownMode() const; @@ -293,8 +295,6 @@ protected: nsRefPtr mEventListener; - nsIFrame* mDummyFrame; - //Resize Reflow OpitmizationSize; nsSize mCacheSize; nscoord mCachedAscent; diff --git a/mozilla/layout/style/forms.css b/mozilla/layout/style/forms.css index ed4ca55d501..c4609c0f062 100644 --- a/mozilla/layout/style/forms.css +++ b/mozilla/layout/style/forms.css @@ -185,9 +185,8 @@ select > input[type="button"]:active { background-image: url("arrowd.gif") !important; } -select::-moz-dummy-option { - visibility: hidden; - content: "XX"; /* demo 8, edge case test 1 */ +select:empty { + width: 2.5em; } *|*::-moz-display-comboboxcontrol-frame { diff --git a/mozilla/layout/style/nsCSSAnonBoxList.h b/mozilla/layout/style/nsCSSAnonBoxList.h index ea8bb1f1e9f..7699e74f46e 100644 --- a/mozilla/layout/style/nsCSSAnonBoxList.h +++ b/mozilla/layout/style/nsCSSAnonBoxList.h @@ -67,7 +67,6 @@ CSS_ANON_BOX(framesetBlank, ":-moz-frameset-blank") CSS_ANON_BOX(radio, ":-moz-radio") CSS_ANON_BOX(check, ":-moz-checkbox") CSS_ANON_BOX(mozDisplayComboboxControlFrame, ":-moz-display-comboboxcontrol-frame") -CSS_ANON_BOX(dummyOption, ":-moz-dummy-option") CSS_ANON_BOX(table, ":-moz-table") CSS_ANON_BOX(tableCell, ":-moz-table-cell")