1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/forms/nsSelectsAreaFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,200 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 +#include "nsSelectsAreaFrame.h" 1.9 +#include "nsIContent.h" 1.10 +#include "nsListControlFrame.h" 1.11 +#include "nsDisplayList.h" 1.12 + 1.13 +nsIFrame* 1.14 +NS_NewSelectsAreaFrame(nsIPresShell* aShell, nsStyleContext* aContext, nsFrameState aFlags) 1.15 +{ 1.16 + nsSelectsAreaFrame* it = new (aShell) nsSelectsAreaFrame(aContext); 1.17 + 1.18 + // We need NS_BLOCK_FLOAT_MGR to ensure that the options inside the select 1.19 + // aren't expanded by right floats outside the select. 1.20 + it->SetFlags(aFlags | NS_BLOCK_FLOAT_MGR); 1.21 + 1.22 + return it; 1.23 +} 1.24 + 1.25 +NS_IMPL_FRAMEARENA_HELPERS(nsSelectsAreaFrame) 1.26 + 1.27 +//--------------------------------------------------------- 1.28 +/** 1.29 + * This wrapper class lets us redirect mouse hits from the child frame of 1.30 + * an option element to the element's own frame. 1.31 + * REVIEW: This is what nsSelectsAreaFrame::GetFrameForPoint used to do 1.32 + */ 1.33 +class nsDisplayOptionEventGrabber : public nsDisplayWrapList { 1.34 +public: 1.35 + nsDisplayOptionEventGrabber(nsDisplayListBuilder* aBuilder, 1.36 + nsIFrame* aFrame, nsDisplayItem* aItem) 1.37 + : nsDisplayWrapList(aBuilder, aFrame, aItem) {} 1.38 + nsDisplayOptionEventGrabber(nsDisplayListBuilder* aBuilder, 1.39 + nsIFrame* aFrame, nsDisplayList* aList) 1.40 + : nsDisplayWrapList(aBuilder, aFrame, aList) {} 1.41 + virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, 1.42 + HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames); 1.43 + NS_DISPLAY_DECL_NAME("OptionEventGrabber", TYPE_OPTION_EVENT_GRABBER) 1.44 +}; 1.45 + 1.46 +void nsDisplayOptionEventGrabber::HitTest(nsDisplayListBuilder* aBuilder, 1.47 + const nsRect& aRect, HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) 1.48 +{ 1.49 + nsTArray<nsIFrame*> outFrames; 1.50 + mList.HitTest(aBuilder, aRect, aState, &outFrames); 1.51 + 1.52 + for (uint32_t i = 0; i < outFrames.Length(); i++) { 1.53 + nsIFrame* selectedFrame = outFrames.ElementAt(i); 1.54 + while (selectedFrame && 1.55 + !(selectedFrame->GetContent() && 1.56 + selectedFrame->GetContent()->IsHTML(nsGkAtoms::option))) { 1.57 + selectedFrame = selectedFrame->GetParent(); 1.58 + } 1.59 + if (selectedFrame) { 1.60 + aOutFrames->AppendElement(selectedFrame); 1.61 + } else { 1.62 + // keep the original result, which could be this frame 1.63 + aOutFrames->AppendElement(outFrames.ElementAt(i)); 1.64 + } 1.65 + } 1.66 +} 1.67 + 1.68 +class nsOptionEventGrabberWrapper : public nsDisplayWrapper 1.69 +{ 1.70 +public: 1.71 + nsOptionEventGrabberWrapper() {} 1.72 + virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder, 1.73 + nsIFrame* aFrame, nsDisplayList* aList) { 1.74 + return new (aBuilder) nsDisplayOptionEventGrabber(aBuilder, aFrame, aList); 1.75 + } 1.76 + virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder, 1.77 + nsDisplayItem* aItem) { 1.78 + return new (aBuilder) nsDisplayOptionEventGrabber(aBuilder, aItem->Frame(), aItem); 1.79 + } 1.80 +}; 1.81 + 1.82 +static nsListControlFrame* GetEnclosingListFrame(nsIFrame* aSelectsAreaFrame) 1.83 +{ 1.84 + nsIFrame* frame = aSelectsAreaFrame->GetParent(); 1.85 + while (frame) { 1.86 + if (frame->GetType() == nsGkAtoms::listControlFrame) 1.87 + return static_cast<nsListControlFrame*>(frame); 1.88 + frame = frame->GetParent(); 1.89 + } 1.90 + return nullptr; 1.91 +} 1.92 + 1.93 +class nsDisplayListFocus : public nsDisplayItem { 1.94 +public: 1.95 + nsDisplayListFocus(nsDisplayListBuilder* aBuilder, 1.96 + nsSelectsAreaFrame* aFrame) : 1.97 + nsDisplayItem(aBuilder, aFrame) { 1.98 + MOZ_COUNT_CTOR(nsDisplayListFocus); 1.99 + } 1.100 +#ifdef NS_BUILD_REFCNT_LOGGING 1.101 + virtual ~nsDisplayListFocus() { 1.102 + MOZ_COUNT_DTOR(nsDisplayListFocus); 1.103 + } 1.104 +#endif 1.105 + 1.106 + virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { 1.107 + *aSnap = false; 1.108 + // override bounds because the list item focus ring may extend outside 1.109 + // the nsSelectsAreaFrame 1.110 + nsListControlFrame* listFrame = GetEnclosingListFrame(Frame()); 1.111 + return listFrame->GetVisualOverflowRectRelativeToSelf() + 1.112 + listFrame->GetOffsetToCrossDoc(ReferenceFrame()); 1.113 + } 1.114 + virtual void Paint(nsDisplayListBuilder* aBuilder, 1.115 + nsRenderingContext* aCtx) { 1.116 + nsListControlFrame* listFrame = GetEnclosingListFrame(Frame()); 1.117 + // listFrame must be non-null or we wouldn't get called. 1.118 + listFrame->PaintFocus(*aCtx, aBuilder->ToReferenceFrame(listFrame)); 1.119 + } 1.120 + NS_DISPLAY_DECL_NAME("ListFocus", TYPE_LIST_FOCUS) 1.121 +}; 1.122 + 1.123 +void 1.124 +nsSelectsAreaFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.125 + const nsRect& aDirtyRect, 1.126 + const nsDisplayListSet& aLists) 1.127 +{ 1.128 + if (!aBuilder->IsForEventDelivery()) { 1.129 + BuildDisplayListInternal(aBuilder, aDirtyRect, aLists); 1.130 + return; 1.131 + } 1.132 + 1.133 + nsDisplayListCollection set; 1.134 + BuildDisplayListInternal(aBuilder, aDirtyRect, set); 1.135 + 1.136 + nsOptionEventGrabberWrapper wrapper; 1.137 + wrapper.WrapLists(aBuilder, this, set, aLists); 1.138 +} 1.139 + 1.140 +void 1.141 +nsSelectsAreaFrame::BuildDisplayListInternal(nsDisplayListBuilder* aBuilder, 1.142 + const nsRect& aDirtyRect, 1.143 + const nsDisplayListSet& aLists) 1.144 +{ 1.145 + nsBlockFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists); 1.146 + 1.147 + nsListControlFrame* listFrame = GetEnclosingListFrame(this); 1.148 + if (listFrame && listFrame->IsFocused()) { 1.149 + // we can't just associate the display item with the list frame, 1.150 + // because then the list's scrollframe won't clip it (the scrollframe 1.151 + // only clips contained descendants). 1.152 + aLists.Outlines()->AppendNewToTop(new (aBuilder) 1.153 + nsDisplayListFocus(aBuilder, this)); 1.154 + } 1.155 +} 1.156 + 1.157 +nsresult 1.158 +nsSelectsAreaFrame::Reflow(nsPresContext* aPresContext, 1.159 + nsHTMLReflowMetrics& aDesiredSize, 1.160 + const nsHTMLReflowState& aReflowState, 1.161 + nsReflowStatus& aStatus) 1.162 +{ 1.163 + nsListControlFrame* list = GetEnclosingListFrame(this); 1.164 + NS_ASSERTION(list, 1.165 + "Must have an nsListControlFrame! Frame constructor is " 1.166 + "broken"); 1.167 + 1.168 + bool isInDropdownMode = list->IsInDropDownMode(); 1.169 + 1.170 + // See similar logic in nsListControlFrame::Reflow and 1.171 + // nsListControlFrame::ReflowAsDropdown. We need to match it here. 1.172 + nscoord oldHeight; 1.173 + if (isInDropdownMode) { 1.174 + // Store the height now in case it changes during 1.175 + // nsBlockFrame::Reflow for some odd reason. 1.176 + if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { 1.177 + oldHeight = GetSize().height; 1.178 + } else { 1.179 + oldHeight = NS_UNCONSTRAINEDSIZE; 1.180 + } 1.181 + } 1.182 + 1.183 + nsresult rv = nsBlockFrame::Reflow(aPresContext, aDesiredSize, 1.184 + aReflowState, aStatus); 1.185 + NS_ENSURE_SUCCESS(rv, rv); 1.186 + 1.187 + // Check whether we need to suppress scrollbar updates. We want to do that if 1.188 + // we're in a possible first pass and our height of a row has changed. 1.189 + if (list->MightNeedSecondPass()) { 1.190 + nscoord newHeightOfARow = list->CalcHeightOfARow(); 1.191 + // We'll need a second pass if our height of a row changed. For 1.192 + // comboboxes, we'll also need it if our height changed. If we're going 1.193 + // to do a second pass, suppress scrollbar updates for this pass. 1.194 + if (newHeightOfARow != mHeightOfARow || 1.195 + (isInDropdownMode && (oldHeight != aDesiredSize.Height() || 1.196 + oldHeight != GetSize().height))) { 1.197 + mHeightOfARow = newHeightOfARow; 1.198 + list->SetSuppressScrollbarUpdate(true); 1.199 + } 1.200 + } 1.201 + 1.202 + return rv; 1.203 +}