layout/forms/nsSelectsAreaFrame.cpp

changeset 0
6474c204b198
     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 +}

mercurial