LCOV - code coverage report
Current view: top level - layout/forms - nsListControlFrame.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 1103 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 92 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "nscore.h"
       7             : #include "nsCOMPtr.h"
       8             : #include "nsUnicharUtils.h"
       9             : #include "nsListControlFrame.h"
      10             : #include "nsFormControlFrame.h" // for COMPARE macro
      11             : #include "nsGkAtoms.h"
      12             : #include "nsIDOMHTMLSelectElement.h"
      13             : #include "nsIDOMHTMLOptionElement.h"
      14             : #include "nsComboboxControlFrame.h"
      15             : #include "nsIDOMHTMLOptGroupElement.h"
      16             : #include "nsIPresShell.h"
      17             : #include "nsIDOMMouseEvent.h"
      18             : #include "nsIXULRuntime.h"
      19             : #include "nsFontMetrics.h"
      20             : #include "nsIScrollableFrame.h"
      21             : #include "nsCSSRendering.h"
      22             : #include "nsIDOMEventListener.h"
      23             : #include "nsLayoutUtils.h"
      24             : #include "nsDisplayList.h"
      25             : #include "nsContentUtils.h"
      26             : #include "mozilla/Attributes.h"
      27             : #include "mozilla/dom/HTMLOptionsCollection.h"
      28             : #include "mozilla/dom/HTMLSelectElement.h"
      29             : #include "mozilla/EventStateManager.h"
      30             : #include "mozilla/EventStates.h"
      31             : #include "mozilla/LookAndFeel.h"
      32             : #include "mozilla/MouseEvents.h"
      33             : #include "mozilla/Preferences.h"
      34             : #include "mozilla/TextEvents.h"
      35             : #include <algorithm>
      36             : 
      37             : using namespace mozilla;
      38             : 
      39             : // Constants
      40             : const uint32_t kMaxDropDownRows         = 20; // This matches the setting for 4.x browsers
      41             : const int32_t kNothingSelected          = -1;
      42             : 
      43             : // Static members
      44             : nsListControlFrame * nsListControlFrame::mFocused = nullptr;
      45             : nsString * nsListControlFrame::sIncrementalString = nullptr;
      46             : 
      47             : // Using for incremental typing navigation
      48             : #define INCREMENTAL_SEARCH_KEYPRESS_TIME 1000
      49             : // XXX, kyle.yuan@sun.com, there are 4 definitions for the same purpose:
      50             : //  nsMenuPopupFrame.h, nsListControlFrame.cpp, listbox.xml, tree.xml
      51             : //  need to find a good place to put them together.
      52             : //  if someone changes one, please also change the other.
      53             : 
      54             : DOMTimeStamp nsListControlFrame::gLastKeyTime = 0;
      55             : 
      56             : /******************************************************************************
      57             :  * nsListEventListener
      58             :  * This class is responsible for propagating events to the nsListControlFrame.
      59             :  * Frames are not refcounted so they can't be used as event listeners.
      60             :  *****************************************************************************/
      61             : 
      62             : class nsListEventListener final : public nsIDOMEventListener
      63             : {
      64             : public:
      65           0 :   explicit nsListEventListener(nsListControlFrame *aFrame)
      66           0 :     : mFrame(aFrame) { }
      67             : 
      68           0 :   void SetFrame(nsListControlFrame *aFrame) { mFrame = aFrame; }
      69             : 
      70             :   NS_DECL_ISUPPORTS
      71             :   NS_DECL_NSIDOMEVENTLISTENER
      72             : 
      73             : private:
      74           0 :   ~nsListEventListener() {}
      75             : 
      76             :   nsListControlFrame  *mFrame;
      77             : };
      78             : 
      79             : //---------------------------------------------------------
      80             : nsContainerFrame*
      81           0 : NS_NewListControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
      82             : {
      83             :   nsListControlFrame* it =
      84           0 :     new (aPresShell) nsListControlFrame(aContext);
      85             : 
      86           0 :   it->AddStateBits(NS_FRAME_INDEPENDENT_SELECTION);
      87             : 
      88           0 :   return it;
      89             : }
      90             : 
      91           0 : NS_IMPL_FRAMEARENA_HELPERS(nsListControlFrame)
      92             : 
      93             : //---------------------------------------------------------
      94           0 : nsListControlFrame::nsListControlFrame(nsStyleContext* aContext)
      95             :   : nsHTMLScrollFrame(aContext, kClassID, false)
      96             :   , mView(nullptr)
      97             :   , mMightNeedSecondPass(false)
      98             :   , mHasPendingInterruptAtStartOfReflow(false)
      99             :   , mDropdownCanGrow(false)
     100             :   , mForceSelection(false)
     101           0 :   , mLastDropdownComputedBSize(NS_UNCONSTRAINEDSIZE)
     102             : {
     103           0 :   mComboboxFrame      = nullptr;
     104           0 :   mChangesSinceDragStart = false;
     105           0 :   mButtonDown         = false;
     106             : 
     107           0 :   mIsAllContentHere   = false;
     108           0 :   mIsAllFramesHere    = false;
     109           0 :   mHasBeenInitialized = false;
     110           0 :   mNeedToReset        = true;
     111           0 :   mPostChildrenLoadedReset = false;
     112             : 
     113           0 :   mControlSelectMode           = false;
     114           0 : }
     115             : 
     116             : //---------------------------------------------------------
     117           0 : nsListControlFrame::~nsListControlFrame()
     118             : {
     119           0 :   mComboboxFrame = nullptr;
     120           0 : }
     121             : 
     122           0 : static bool ShouldFireDropDownEvent() {
     123           0 :   return (XRE_IsContentProcess() &&
     124           0 :           Preferences::GetBool("browser.tabs.remote.desktopbehavior", false)) ||
     125           0 :          Preferences::GetBool("dom.select_popup_in_parent.enabled", false);
     126             : }
     127             : 
     128             : // for Bug 47302 (remove this comment later)
     129             : void
     130           0 : nsListControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
     131             : {
     132             :   // get the receiver interface from the browser button's content node
     133           0 :   ENSURE_TRUE(mContent);
     134             : 
     135             :   // Clear the frame pointer on our event listener, just in case the
     136             :   // event listener can outlive the frame.
     137             : 
     138           0 :   mEventListener->SetFrame(nullptr);
     139             : 
     140           0 :   mContent->RemoveSystemEventListener(NS_LITERAL_STRING("keydown"),
     141           0 :                                       mEventListener, false);
     142           0 :   mContent->RemoveSystemEventListener(NS_LITERAL_STRING("keypress"),
     143           0 :                                       mEventListener, false);
     144           0 :   mContent->RemoveSystemEventListener(NS_LITERAL_STRING("mousedown"),
     145           0 :                                       mEventListener, false);
     146           0 :   mContent->RemoveSystemEventListener(NS_LITERAL_STRING("mouseup"),
     147           0 :                                       mEventListener, false);
     148           0 :   mContent->RemoveSystemEventListener(NS_LITERAL_STRING("mousemove"),
     149           0 :                                       mEventListener, false);
     150             : 
     151           0 :   if (ShouldFireDropDownEvent()) {
     152           0 :     nsContentUtils::AddScriptRunner(
     153           0 :       new AsyncEventDispatcher(mContent,
     154           0 :                                NS_LITERAL_STRING("mozhidedropdown"), true,
     155           0 :                                true));
     156             :   }
     157             : 
     158           0 :   nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false);
     159           0 :   nsHTMLScrollFrame::DestroyFrom(aDestructRoot);
     160             : }
     161             : 
     162             : void
     163           0 : nsListControlFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
     164             :                                      const nsRect&           aDirtyRect,
     165             :                                      const nsDisplayListSet& aLists)
     166             : {
     167             :   // We allow visibility:hidden <select>s to contain visible options.
     168             : 
     169             :   // Don't allow painting of list controls when painting is suppressed.
     170             :   // XXX why do we need this here? we should never reach this. Maybe
     171             :   // because these can have widgets? Hmm
     172           0 :   if (aBuilder->IsBackgroundOnly())
     173           0 :     return;
     174             : 
     175           0 :   DO_GLOBAL_REFLOW_COUNT_DSP("nsListControlFrame");
     176             : 
     177           0 :   if (IsInDropDownMode()) {
     178           0 :     NS_ASSERTION(NS_GET_A(mLastDropdownBackstopColor) == 255,
     179             :                  "need an opaque backstop color");
     180             :     // XXX Because we have an opaque widget and we get called to paint with
     181             :     // this frame as the root of a stacking context we need make sure to draw
     182             :     // some opaque color over the whole widget. (Bug 511323)
     183           0 :     aLists.BorderBackground()->AppendNewToBottom(
     184             :       new (aBuilder) nsDisplaySolidColor(aBuilder,
     185           0 :         this, nsRect(aBuilder->ToReferenceFrame(this), GetSize()),
     186           0 :         mLastDropdownBackstopColor));
     187             :   }
     188             : 
     189           0 :   nsHTMLScrollFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
     190             : }
     191             : 
     192             : /**
     193             :  * This is called by the SelectsAreaFrame, which is the same
     194             :  * as the frame returned by GetOptionsContainer. It's the frame which is
     195             :  * scrolled by us.
     196             :  * @param aPt the offset of this frame, relative to the rendering reference
     197             :  * frame
     198             :  */
     199           0 : void nsListControlFrame::PaintFocus(DrawTarget* aDrawTarget, nsPoint aPt)
     200             : {
     201           0 :   if (mFocused != this) return;
     202             : 
     203           0 :   nsPresContext* presContext = PresContext();
     204             : 
     205           0 :   nsIFrame* containerFrame = GetOptionsContainer();
     206           0 :   if (!containerFrame) return;
     207             : 
     208           0 :   nsIFrame* childframe = nullptr;
     209           0 :   nsCOMPtr<nsIContent> focusedContent = GetCurrentOption();
     210           0 :   if (focusedContent) {
     211           0 :     childframe = focusedContent->GetPrimaryFrame();
     212             :   }
     213             : 
     214           0 :   nsRect fRect;
     215           0 :   if (childframe) {
     216             :     // get the child rect
     217           0 :     fRect = childframe->GetRect();
     218             :     // get it into our coordinates
     219           0 :     fRect.MoveBy(childframe->GetParent()->GetOffsetTo(this));
     220             :   } else {
     221           0 :     float inflation = nsLayoutUtils::FontSizeInflationFor(this);
     222           0 :     fRect.x = fRect.y = 0;
     223           0 :     if (GetWritingMode().IsVertical()) {
     224           0 :       fRect.width = GetScrollPortRect().width;
     225           0 :       fRect.height = CalcFallbackRowBSize(inflation);
     226             :     } else {
     227           0 :       fRect.width = CalcFallbackRowBSize(inflation);
     228           0 :       fRect.height = GetScrollPortRect().height;
     229             :     }
     230           0 :     fRect.MoveBy(containerFrame->GetOffsetTo(this));
     231             :   }
     232           0 :   fRect += aPt;
     233             : 
     234           0 :   bool lastItemIsSelected = false;
     235           0 :   if (focusedContent) {
     236             :     nsCOMPtr<nsIDOMHTMLOptionElement> domOpt =
     237           0 :       do_QueryInterface(focusedContent);
     238           0 :     if (domOpt) {
     239           0 :       domOpt->GetSelected(&lastItemIsSelected);
     240             :     }
     241             :   }
     242             : 
     243             :   // set up back stop colors and then ask L&F service for the real colors
     244             :   nscolor color =
     245           0 :     LookAndFeel::GetColor(lastItemIsSelected ?
     246             :                             LookAndFeel::eColorID_WidgetSelectForeground :
     247           0 :                             LookAndFeel::eColorID_WidgetSelectBackground);
     248             : 
     249           0 :   nsCSSRendering::PaintFocus(presContext, aDrawTarget, fRect, color);
     250             : }
     251             : 
     252             : void
     253           0 : nsListControlFrame::InvalidateFocus()
     254             : {
     255           0 :   if (mFocused != this)
     256           0 :     return;
     257             : 
     258           0 :   nsIFrame* containerFrame = GetOptionsContainer();
     259           0 :   if (containerFrame) {
     260           0 :     containerFrame->InvalidateFrame();
     261             :   }
     262             : }
     263             : 
     264           0 : NS_QUERYFRAME_HEAD(nsListControlFrame)
     265           0 :   NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
     266           0 :   NS_QUERYFRAME_ENTRY(nsIListControlFrame)
     267           0 :   NS_QUERYFRAME_ENTRY(nsISelectControlFrame)
     268           0 : NS_QUERYFRAME_TAIL_INHERITING(nsHTMLScrollFrame)
     269             : 
     270             : #ifdef ACCESSIBILITY
     271             : a11y::AccType
     272           0 : nsListControlFrame::AccessibleType()
     273             : {
     274           0 :   return a11y::eHTMLSelectListType;
     275             : }
     276             : #endif
     277             : 
     278             : static nscoord
     279           0 : GetMaxOptionBSize(nsIFrame* aContainer, WritingMode aWM)
     280             : {
     281           0 :   nscoord result = 0;
     282           0 :   for (nsIFrame* option : aContainer->PrincipalChildList()) {
     283             :     nscoord optionBSize;
     284           0 :     if (nsCOMPtr<nsIDOMHTMLOptGroupElement>
     285           0 :         (do_QueryInterface(option->GetContent()))) {
     286             :       // An optgroup; drill through any scroll frame and recurse.  |frame| might
     287             :       // be null here though if |option| is an anonymous leaf frame of some sort.
     288           0 :       auto frame = option->GetContentInsertionFrame();
     289           0 :       optionBSize = frame ? GetMaxOptionBSize(frame, aWM) : 0;
     290             :     } else {
     291             :       // an option
     292           0 :       optionBSize = option->BSize(aWM);
     293             :     }
     294           0 :     if (result < optionBSize)
     295           0 :       result = optionBSize;
     296             :   }
     297           0 :   return result;
     298             : }
     299             : 
     300             : //-----------------------------------------------------------------
     301             : // Main Reflow for ListBox/Dropdown
     302             : //-----------------------------------------------------------------
     303             : 
     304             : nscoord
     305           0 : nsListControlFrame::CalcBSizeOfARow()
     306             : {
     307             :   // Calculate the block size in our writing mode of a single row in the
     308             :   // listbox or dropdown list by using the tallest thing in the subtree,
     309             :   // since there may be option groups in addition to option elements,
     310             :   // either of which may be visible or invisible, may use different
     311             :   // fonts, etc.
     312           0 :   int32_t blockSizeOfARow = GetMaxOptionBSize(GetOptionsContainer(),
     313           0 :                                               GetWritingMode());
     314             : 
     315             :   // Check to see if we have zero items (and optimize by checking
     316             :   // blockSizeOfARow first)
     317           0 :   if (blockSizeOfARow == 0 && GetNumberOfOptions() == 0) {
     318           0 :     float inflation = nsLayoutUtils::FontSizeInflationFor(this);
     319           0 :     blockSizeOfARow = CalcFallbackRowBSize(inflation);
     320             :   }
     321             : 
     322           0 :   return blockSizeOfARow;
     323             : }
     324             : 
     325             : nscoord
     326           0 : nsListControlFrame::GetPrefISize(gfxContext *aRenderingContext)
     327             : {
     328             :   nscoord result;
     329           0 :   DISPLAY_PREF_WIDTH(this, result);
     330             : 
     331             :   // Always add scrollbar inline sizes to the pref-inline-size of the
     332             :   // scrolled content. Combobox frames depend on this happening in the
     333             :   // dropdown, and standalone listboxes are overflow:scroll so they need
     334             :   // it too.
     335           0 :   WritingMode wm = GetWritingMode();
     336           0 :   result = GetScrolledFrame()->GetPrefISize(aRenderingContext);
     337           0 :   LogicalMargin scrollbarSize(wm, GetDesiredScrollbarSizes(PresContext(),
     338           0 :                                                            aRenderingContext));
     339           0 :   result = NSCoordSaturatingAdd(result, scrollbarSize.IStartEnd(wm));
     340           0 :   return result;
     341             : }
     342             : 
     343             : nscoord
     344           0 : nsListControlFrame::GetMinISize(gfxContext *aRenderingContext)
     345             : {
     346             :   nscoord result;
     347           0 :   DISPLAY_MIN_WIDTH(this, result);
     348             : 
     349             :   // Always add scrollbar inline sizes to the min-inline-size of the
     350             :   // scrolled content. Combobox frames depend on this happening in the
     351             :   // dropdown, and standalone listboxes are overflow:scroll so they need
     352             :   // it too.
     353           0 :   WritingMode wm = GetWritingMode();
     354           0 :   result = GetScrolledFrame()->GetMinISize(aRenderingContext);
     355           0 :   LogicalMargin scrollbarSize(wm, GetDesiredScrollbarSizes(PresContext(),
     356           0 :                                                            aRenderingContext));
     357           0 :   result += scrollbarSize.IStartEnd(wm);
     358             : 
     359           0 :   return result;
     360             : }
     361             : 
     362             : void
     363           0 : nsListControlFrame::Reflow(nsPresContext*           aPresContext,
     364             :                            ReflowOutput&     aDesiredSize,
     365             :                            const ReflowInput& aReflowInput,
     366             :                            nsReflowStatus&          aStatus)
     367             : {
     368           0 :   NS_PRECONDITION(aReflowInput.ComputedISize() != NS_UNCONSTRAINEDSIZE,
     369             :                   "Must have a computed inline size");
     370             : 
     371           0 :   SchedulePaint();
     372             : 
     373           0 :   mHasPendingInterruptAtStartOfReflow = aPresContext->HasPendingInterrupt();
     374             : 
     375             :   // If all the content and frames are here
     376             :   // then initialize it before reflow
     377           0 :   if (mIsAllContentHere && !mHasBeenInitialized) {
     378           0 :     if (false == mIsAllFramesHere) {
     379           0 :       CheckIfAllFramesHere();
     380             :     }
     381           0 :     if (mIsAllFramesHere && !mHasBeenInitialized) {
     382           0 :       mHasBeenInitialized = true;
     383             :     }
     384             :   }
     385             : 
     386           0 :   if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
     387           0 :     nsFormControlFrame::RegUnRegAccessKey(this, true);
     388             :   }
     389             : 
     390           0 :   if (IsInDropDownMode()) {
     391           0 :     ReflowAsDropdown(aPresContext, aDesiredSize, aReflowInput, aStatus);
     392           0 :     return;
     393             :   }
     394             : 
     395           0 :   MarkInReflow();
     396             :   /*
     397             :    * Due to the fact that our intrinsic block size depends on the block
     398             :    * sizes of our kids, we end up having to do two-pass reflow, in
     399             :    * general -- the first pass to find the intrinsic block size and a
     400             :    * second pass to reflow the scrollframe at that block size (which
     401             :    * will size the scrollbars correctly, etc).
     402             :    *
     403             :    * Naturally, we want to avoid doing the second reflow as much as
     404             :    * possible.
     405             :    * We can skip it in the following cases (in all of which the first
     406             :    * reflow is already happening at the right block size):
     407             :    *
     408             :    * - We're reflowing with a constrained computed block size -- just
     409             :    *   use that block size.
     410             :    * - We're not dirty and have no dirty kids and shouldn't be reflowing
     411             :    *   all kids.  In this case, our cached max block size of a child is
     412             :    *   not going to change.
     413             :    * - We do our first reflow using our cached max block size of a
     414             :    *   child, then compute the new max block size and it's the same as
     415             :    *   the old one.
     416             :    */
     417             : 
     418           0 :   bool autoBSize = (aReflowInput.ComputedBSize() == NS_UNCONSTRAINEDSIZE);
     419             : 
     420           0 :   mMightNeedSecondPass = autoBSize &&
     421           0 :     (NS_SUBTREE_DIRTY(this) || aReflowInput.ShouldReflowAllKids());
     422             : 
     423           0 :   ReflowInput state(aReflowInput);
     424           0 :   int32_t length = GetNumberOfRows();
     425             : 
     426           0 :   nscoord oldBSizeOfARow = BSizeOfARow();
     427             : 
     428           0 :   if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW) && autoBSize) {
     429             :     // When not doing an initial reflow, and when the block size is
     430             :     // auto, start off with our computed block size set to what we'd
     431             :     // expect our block size to be.
     432           0 :     nscoord computedBSize = CalcIntrinsicBSize(oldBSizeOfARow, length);
     433           0 :     computedBSize = state.ApplyMinMaxBSize(computedBSize);
     434           0 :     state.SetComputedBSize(computedBSize);
     435             :   }
     436             : 
     437           0 :   nsHTMLScrollFrame::Reflow(aPresContext, aDesiredSize, state, aStatus);
     438             : 
     439           0 :   if (!mMightNeedSecondPass) {
     440           0 :     NS_ASSERTION(!autoBSize || BSizeOfARow() == oldBSizeOfARow,
     441             :                  "How did our BSize of a row change if nothing was dirty?");
     442           0 :     NS_ASSERTION(!autoBSize ||
     443             :                  !(GetStateBits() & NS_FRAME_FIRST_REFLOW),
     444             :                  "How do we not need a second pass during initial reflow at "
     445             :                  "auto BSize?");
     446           0 :     NS_ASSERTION(!IsScrollbarUpdateSuppressed(),
     447             :                  "Shouldn't be suppressing if we don't need a second pass!");
     448           0 :     if (!autoBSize) {
     449             :       // Update our mNumDisplayRows based on our new row block size now
     450             :       // that we know it.  Note that if autoBSize and we landed in this
     451             :       // code then we already set mNumDisplayRows in CalcIntrinsicBSize.
     452             :       //  Also note that we can't use BSizeOfARow() here because that
     453             :       // just uses a cached value that we didn't compute.
     454           0 :       nscoord rowBSize = CalcBSizeOfARow();
     455           0 :       if (rowBSize == 0) {
     456             :         // Just pick something
     457           0 :         mNumDisplayRows = 1;
     458             :       } else {
     459           0 :         mNumDisplayRows = std::max(1, state.ComputedBSize() / rowBSize);
     460             :       }
     461             :     }
     462             : 
     463           0 :     return;
     464             :   }
     465             : 
     466           0 :   mMightNeedSecondPass = false;
     467             : 
     468             :   // Now see whether we need a second pass.  If we do, our
     469             :   // nsSelectsAreaFrame will have suppressed the scrollbar update.
     470           0 :   if (!IsScrollbarUpdateSuppressed()) {
     471             :     // All done.  No need to do more reflow.
     472           0 :     return;
     473             :   }
     474             : 
     475           0 :   SetSuppressScrollbarUpdate(false);
     476             : 
     477             :   // Gotta reflow again.
     478             :   // XXXbz We're just changing the block size here; do we need to dirty
     479             :   // ourselves or anything like that?  We might need to, per the letter
     480             :   // of the reflow protocol, but things seem to work fine without it...
     481             :   // Is that just an implementation detail of nsHTMLScrollFrame that
     482             :   // we're depending on?
     483           0 :   nsHTMLScrollFrame::DidReflow(aPresContext, &state,
     484           0 :                                nsDidReflowStatus::FINISHED);
     485             : 
     486             :   // Now compute the block size we want to have
     487           0 :   nscoord computedBSize = CalcIntrinsicBSize(BSizeOfARow(), length);
     488           0 :   computedBSize = state.ApplyMinMaxBSize(computedBSize);
     489           0 :   state.SetComputedBSize(computedBSize);
     490             : 
     491             :   // XXXbz to make the ascent really correct, we should add our
     492             :   // mComputedPadding.top to it (and subtract it from descent).  Need that
     493             :   // because nsGfxScrollFrame just adds in the border....
     494           0 :   nsHTMLScrollFrame::Reflow(aPresContext, aDesiredSize, state, aStatus);
     495             : }
     496             : 
     497             : void
     498           0 : nsListControlFrame::ReflowAsDropdown(nsPresContext*           aPresContext,
     499             :                                      ReflowOutput&     aDesiredSize,
     500             :                                      const ReflowInput& aReflowInput,
     501             :                                      nsReflowStatus&          aStatus)
     502             : {
     503           0 :   NS_PRECONDITION(aReflowInput.ComputedBSize() == NS_UNCONSTRAINEDSIZE,
     504             :                   "We should not have a computed block size here!");
     505             : 
     506           0 :   mMightNeedSecondPass = NS_SUBTREE_DIRTY(this) ||
     507           0 :     aReflowInput.ShouldReflowAllKids();
     508             : 
     509           0 :   WritingMode wm = aReflowInput.GetWritingMode();
     510             : #ifdef DEBUG
     511           0 :   nscoord oldBSizeOfARow = BSizeOfARow();
     512           0 :   nscoord oldVisibleBSize = (GetStateBits() & NS_FRAME_FIRST_REFLOW) ?
     513           0 :     NS_UNCONSTRAINEDSIZE : GetScrolledFrame()->BSize(wm);
     514             : #endif
     515             : 
     516           0 :   ReflowInput state(aReflowInput);
     517             : 
     518           0 :   if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
     519             :     // When not doing an initial reflow, and when the block size is
     520             :     // auto, start off with our computed block size set to what we'd
     521             :     // expect our block size to be.
     522             :     // Note: At this point, mLastDropdownComputedBSize can be
     523             :     // NS_UNCONSTRAINEDSIZE in cases when last time we didn't have to
     524             :     // constrain the block size.  That's fine; just do the same thing as
     525             :     // last time.
     526           0 :     state.SetComputedBSize(mLastDropdownComputedBSize);
     527             :   }
     528             : 
     529           0 :   nsHTMLScrollFrame::Reflow(aPresContext, aDesiredSize, state, aStatus);
     530             : 
     531           0 :   if (!mMightNeedSecondPass) {
     532           0 :     NS_ASSERTION(oldVisibleBSize == GetScrolledFrame()->BSize(wm),
     533             :                  "How did our kid's BSize change if nothing was dirty?");
     534           0 :     NS_ASSERTION(BSizeOfARow() == oldBSizeOfARow,
     535             :                  "How did our BSize of a row change if nothing was dirty?");
     536           0 :     NS_ASSERTION(!IsScrollbarUpdateSuppressed(),
     537             :                  "Shouldn't be suppressing if we don't need a second pass!");
     538           0 :     NS_ASSERTION(!(GetStateBits() & NS_FRAME_FIRST_REFLOW),
     539             :                  "How can we avoid a second pass during first reflow?");
     540           0 :     return;
     541             :   }
     542             : 
     543           0 :   mMightNeedSecondPass = false;
     544             : 
     545             :   // Now see whether we need a second pass.  If we do, our nsSelectsAreaFrame
     546             :   // will have suppressed the scrollbar update.
     547           0 :   if (!IsScrollbarUpdateSuppressed()) {
     548             :     // All done.  No need to do more reflow.
     549           0 :     NS_ASSERTION(!(GetStateBits() & NS_FRAME_FIRST_REFLOW),
     550             :                  "How can we avoid a second pass during first reflow?");
     551           0 :     return;
     552             :   }
     553             : 
     554           0 :   SetSuppressScrollbarUpdate(false);
     555             : 
     556           0 :   nscoord visibleBSize = GetScrolledFrame()->BSize(wm);
     557           0 :   nscoord blockSizeOfARow = BSizeOfARow();
     558             : 
     559             :   // Gotta reflow again.
     560             :   // XXXbz We're just changing the block size here; do we need to dirty
     561             :   // ourselves or anything like that?  We might need to, per the letter
     562             :   // of the reflow protocol, but things seem to work fine without it...
     563             :   // Is that just an implementation detail of nsHTMLScrollFrame that
     564             :   // we're depending on?
     565           0 :   nsHTMLScrollFrame::DidReflow(aPresContext, &state,
     566           0 :                                nsDidReflowStatus::FINISHED);
     567             : 
     568             :   // Now compute the block size we want to have.
     569             :   // Note: no need to apply min/max constraints, since we have no such
     570             :   // rules applied to the combobox dropdown.
     571             : 
     572           0 :   mDropdownCanGrow = false;
     573           0 :   if (visibleBSize <= 0 || blockSizeOfARow <= 0 || XRE_IsContentProcess()) {
     574             :     // Looks like we have no options.  Just size us to a single row
     575             :     // block size.
     576           0 :     state.SetComputedBSize(blockSizeOfARow);
     577           0 :     mNumDisplayRows = 1;
     578             :   } else {
     579             :     nsComboboxControlFrame* combobox =
     580           0 :       static_cast<nsComboboxControlFrame*>(mComboboxFrame);
     581           0 :     LogicalPoint translation(wm);
     582             :     nscoord before, after;
     583           0 :     combobox->GetAvailableDropdownSpace(wm, &before, &after, &translation);
     584           0 :     if (before <= 0 && after <= 0) {
     585           0 :       state.SetComputedBSize(blockSizeOfARow);
     586           0 :       mNumDisplayRows = 1;
     587           0 :       mDropdownCanGrow = GetNumberOfRows() > 1;
     588             :     } else {
     589           0 :       nscoord bp = aReflowInput.ComputedLogicalBorderPadding().BStartEnd(wm);
     590           0 :       nscoord availableBSize = std::max(before, after) - bp;
     591             :       nscoord newBSize;
     592             :       uint32_t rows;
     593           0 :       if (visibleBSize <= availableBSize) {
     594             :         // The dropdown fits in the available block size.
     595           0 :         rows = GetNumberOfRows();
     596           0 :         mNumDisplayRows = clamped<uint32_t>(rows, 1, kMaxDropDownRows);
     597           0 :         if (mNumDisplayRows == rows) {
     598           0 :           newBSize = visibleBSize;  // use the exact block size
     599             :         } else {
     600           0 :           newBSize = mNumDisplayRows * blockSizeOfARow; // approximate
     601             :           // The approximation here might actually be too big (bug 1208978);
     602             :           // don't let it exceed the actual block-size of the list.
     603           0 :           newBSize = std::min(newBSize, visibleBSize);
     604             :         }
     605             :       } else {
     606           0 :         rows = availableBSize / blockSizeOfARow;
     607           0 :         mNumDisplayRows = clamped<uint32_t>(rows, 1, kMaxDropDownRows);
     608           0 :         newBSize = mNumDisplayRows * blockSizeOfARow; // approximate
     609             :       }
     610           0 :       state.SetComputedBSize(newBSize);
     611           0 :       mDropdownCanGrow = visibleBSize - newBSize >= blockSizeOfARow &&
     612           0 :                          mNumDisplayRows != kMaxDropDownRows;
     613             :     }
     614             :   }
     615             : 
     616           0 :   mLastDropdownComputedBSize = state.ComputedBSize();
     617             : 
     618           0 :   nsHTMLScrollFrame::Reflow(aPresContext, aDesiredSize, state, aStatus);
     619             : }
     620             : 
     621             : ScrollbarStyles
     622           0 : nsListControlFrame::GetScrollbarStyles() const
     623             : {
     624             :   // We can't express this in the style system yet; when we can, this can go away
     625             :   // and GetScrollbarStyles can be devirtualized
     626           0 :   int32_t style = IsInDropDownMode() ? NS_STYLE_OVERFLOW_AUTO
     627           0 :                                      : NS_STYLE_OVERFLOW_SCROLL;
     628           0 :   if (GetWritingMode().IsVertical()) {
     629           0 :     return ScrollbarStyles(style, NS_STYLE_OVERFLOW_HIDDEN);
     630             :   } else {
     631           0 :     return ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, style);
     632             :   }
     633             : }
     634             : 
     635             : bool
     636           0 : nsListControlFrame::ShouldPropagateComputedBSizeToScrolledContent() const
     637             : {
     638           0 :   return !IsInDropDownMode();
     639             : }
     640             : 
     641             : //---------------------------------------------------------
     642             : nsContainerFrame*
     643           0 : nsListControlFrame::GetContentInsertionFrame() {
     644           0 :   return GetOptionsContainer()->GetContentInsertionFrame();
     645             : }
     646             : 
     647             : //---------------------------------------------------------
     648             : bool
     649           0 : nsListControlFrame::ExtendedSelection(int32_t aStartIndex,
     650             :                                       int32_t aEndIndex,
     651             :                                       bool aClearAll)
     652             : {
     653           0 :   return SetOptionsSelectedFromFrame(aStartIndex, aEndIndex,
     654           0 :                                      true, aClearAll);
     655             : }
     656             : 
     657             : //---------------------------------------------------------
     658             : bool
     659           0 : nsListControlFrame::SingleSelection(int32_t aClickedIndex, bool aDoToggle)
     660             : {
     661           0 :   if (mComboboxFrame) {
     662           0 :     mComboboxFrame->UpdateRecentIndex(GetSelectedIndex());
     663             :   }
     664             : 
     665           0 :   bool wasChanged = false;
     666             :   // Get Current selection
     667           0 :   if (aDoToggle) {
     668           0 :     wasChanged = ToggleOptionSelectedFromFrame(aClickedIndex);
     669             :   } else {
     670             :     wasChanged = SetOptionsSelectedFromFrame(aClickedIndex, aClickedIndex,
     671           0 :                                 true, true);
     672             :   }
     673           0 :   AutoWeakFrame weakFrame(this);
     674           0 :   ScrollToIndex(aClickedIndex);
     675           0 :   if (!weakFrame.IsAlive()) {
     676           0 :     return wasChanged;
     677             :   }
     678             : 
     679             : #ifdef ACCESSIBILITY
     680           0 :   bool isCurrentOptionChanged = mEndSelectionIndex != aClickedIndex;
     681             : #endif
     682           0 :   mStartSelectionIndex = aClickedIndex;
     683           0 :   mEndSelectionIndex = aClickedIndex;
     684           0 :   InvalidateFocus();
     685             : 
     686             : #ifdef ACCESSIBILITY
     687           0 :   if (isCurrentOptionChanged) {
     688           0 :     FireMenuItemActiveEvent();
     689             :   }
     690             : #endif
     691             : 
     692           0 :   return wasChanged;
     693             : }
     694             : 
     695             : void
     696           0 : nsListControlFrame::InitSelectionRange(int32_t aClickedIndex)
     697             : {
     698             :   //
     699             :   // If nothing is selected, set the start selection depending on where
     700             :   // the user clicked and what the initial selection is:
     701             :   // - if the user clicked *before* selectedIndex, set the start index to
     702             :   //   the end of the first contiguous selection.
     703             :   // - if the user clicked *after* the end of the first contiguous
     704             :   //   selection, set the start index to selectedIndex.
     705             :   // - if the user clicked *within* the first contiguous selection, set the
     706             :   //   start index to selectedIndex.
     707             :   // The last two rules, of course, boil down to the same thing: if the user
     708             :   // clicked >= selectedIndex, return selectedIndex.
     709             :   //
     710             :   // This makes it so that shift click works properly when you first click
     711             :   // in a multiple select.
     712             :   //
     713           0 :   int32_t selectedIndex = GetSelectedIndex();
     714           0 :   if (selectedIndex >= 0) {
     715             :     // Get the end of the contiguous selection
     716           0 :     RefPtr<dom::HTMLOptionsCollection> options = GetOptions();
     717           0 :     NS_ASSERTION(options, "Collection of options is null!");
     718           0 :     uint32_t numOptions = options->Length();
     719             :     // Push i to one past the last selected index in the group.
     720             :     uint32_t i;
     721           0 :     for (i = selectedIndex + 1; i < numOptions; i++) {
     722           0 :       if (!options->ItemAsOption(i)->Selected()) {
     723           0 :         break;
     724             :       }
     725             :     }
     726             : 
     727           0 :     if (aClickedIndex < selectedIndex) {
     728             :       // User clicked before selection, so start selection at end of
     729             :       // contiguous selection
     730           0 :       mStartSelectionIndex = i-1;
     731           0 :       mEndSelectionIndex = selectedIndex;
     732             :     } else {
     733             :       // User clicked after selection, so start selection at start of
     734             :       // contiguous selection
     735           0 :       mStartSelectionIndex = selectedIndex;
     736           0 :       mEndSelectionIndex = i-1;
     737             :     }
     738             :   }
     739           0 : }
     740             : 
     741             : static uint32_t
     742           0 : CountOptionsAndOptgroups(nsIFrame* aFrame)
     743             : {
     744           0 :   uint32_t count = 0;
     745           0 :   nsFrameList::Enumerator e(aFrame->PrincipalChildList());
     746           0 :   for (; !e.AtEnd(); e.Next()) {
     747           0 :     nsIFrame* child = e.get();
     748           0 :     nsIContent* content = child->GetContent();
     749           0 :     if (content) {
     750           0 :       if (content->IsHTMLElement(nsGkAtoms::option)) {
     751           0 :         ++count;
     752             :       } else {
     753           0 :         nsCOMPtr<nsIDOMHTMLOptGroupElement> optgroup = do_QueryInterface(content);
     754           0 :         if (optgroup) {
     755           0 :           nsAutoString label;
     756           0 :           optgroup->GetLabel(label);
     757           0 :           if (label.Length() > 0) {
     758           0 :             ++count;
     759             :           }
     760           0 :           count += CountOptionsAndOptgroups(child);
     761             :         }
     762             :       }
     763             :     }
     764             :   }
     765           0 :   return count;
     766             : }
     767             : 
     768             : uint32_t
     769           0 : nsListControlFrame::GetNumberOfRows()
     770             : {
     771           0 :   return ::CountOptionsAndOptgroups(GetContentInsertionFrame());
     772             : }
     773             : 
     774             : //---------------------------------------------------------
     775             : bool
     776           0 : nsListControlFrame::PerformSelection(int32_t aClickedIndex,
     777             :                                      bool aIsShift,
     778             :                                      bool aIsControl)
     779             : {
     780           0 :   bool wasChanged = false;
     781             : 
     782           0 :   if (aClickedIndex == kNothingSelected && !mForceSelection) {
     783             :     // Ignore kNothingSelected unless the selection is forced
     784           0 :   } else if (GetMultiple()) {
     785           0 :     if (aIsShift) {
     786             :       // Make sure shift+click actually does something expected when
     787             :       // the user has never clicked on the select
     788           0 :       if (mStartSelectionIndex == kNothingSelected) {
     789           0 :         InitSelectionRange(aClickedIndex);
     790             :       }
     791             : 
     792             :       // Get the range from beginning (low) to end (high)
     793             :       // Shift *always* works, even if the current option is disabled
     794             :       int32_t startIndex;
     795             :       int32_t endIndex;
     796           0 :       if (mStartSelectionIndex == kNothingSelected) {
     797           0 :         startIndex = aClickedIndex;
     798           0 :         endIndex   = aClickedIndex;
     799           0 :       } else if (mStartSelectionIndex <= aClickedIndex) {
     800           0 :         startIndex = mStartSelectionIndex;
     801           0 :         endIndex   = aClickedIndex;
     802             :       } else {
     803           0 :         startIndex = aClickedIndex;
     804           0 :         endIndex   = mStartSelectionIndex;
     805             :       }
     806             : 
     807             :       // Clear only if control was not pressed
     808           0 :       wasChanged = ExtendedSelection(startIndex, endIndex, !aIsControl);
     809           0 :       AutoWeakFrame weakFrame(this);
     810           0 :       ScrollToIndex(aClickedIndex);
     811           0 :       if (!weakFrame.IsAlive()) {
     812           0 :         return wasChanged;
     813             :       }
     814             : 
     815           0 :       if (mStartSelectionIndex == kNothingSelected) {
     816           0 :         mStartSelectionIndex = aClickedIndex;
     817             :       }
     818             : #ifdef ACCESSIBILITY
     819           0 :       bool isCurrentOptionChanged = mEndSelectionIndex != aClickedIndex;
     820             : #endif
     821           0 :       mEndSelectionIndex = aClickedIndex;
     822           0 :       InvalidateFocus();
     823             : 
     824             : #ifdef ACCESSIBILITY
     825           0 :       if (isCurrentOptionChanged) {
     826           0 :         FireMenuItemActiveEvent();
     827             :       }
     828             : #endif
     829           0 :     } else if (aIsControl) {
     830           0 :       wasChanged = SingleSelection(aClickedIndex, true); // might destroy us
     831             :     } else {
     832           0 :       wasChanged = SingleSelection(aClickedIndex, false); // might destroy us
     833             :     }
     834             :   } else {
     835           0 :     wasChanged = SingleSelection(aClickedIndex, false); // might destroy us
     836             :   }
     837             : 
     838           0 :   return wasChanged;
     839             : }
     840             : 
     841             : //---------------------------------------------------------
     842             : bool
     843           0 : nsListControlFrame::HandleListSelection(nsIDOMEvent* aEvent,
     844             :                                         int32_t aClickedIndex)
     845             : {
     846           0 :   nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent);
     847             :   bool isShift;
     848             :   bool isControl;
     849             : #ifdef XP_MACOSX
     850             :   mouseEvent->GetMetaKey(&isControl);
     851             : #else
     852           0 :   mouseEvent->GetCtrlKey(&isControl);
     853             : #endif
     854           0 :   mouseEvent->GetShiftKey(&isShift);
     855           0 :   return PerformSelection(aClickedIndex, isShift, isControl); // might destroy us
     856             : }
     857             : 
     858             : //---------------------------------------------------------
     859             : void
     860           0 : nsListControlFrame::CaptureMouseEvents(bool aGrabMouseEvents)
     861             : {
     862             :   // Currently cocoa widgets use a native popup widget which tracks clicks synchronously,
     863             :   // so we never want to do mouse capturing. Note that we only bail if the list
     864             :   // is in drop-down mode, and the caller is requesting capture (we let release capture
     865             :   // requests go through to ensure that we can release capture requested via other
     866             :   // code paths, if any exist).
     867           0 :   if (aGrabMouseEvents && IsInDropDownMode() && nsComboboxControlFrame::ToolkitHasNativePopup())
     868           0 :     return;
     869             : 
     870           0 :   if (aGrabMouseEvents) {
     871           0 :     nsIPresShell::SetCapturingContent(mContent, CAPTURE_IGNOREALLOWED);
     872             :   } else {
     873           0 :     nsIContent* capturingContent = nsIPresShell::GetCapturingContent();
     874             : 
     875           0 :     bool dropDownIsHidden = false;
     876           0 :     if (IsInDropDownMode()) {
     877           0 :       dropDownIsHidden = !mComboboxFrame->IsDroppedDown();
     878             :     }
     879           0 :     if (capturingContent == mContent || dropDownIsHidden) {
     880             :       // only clear the capturing content if *we* are the ones doing the
     881             :       // capturing (or if the dropdown is hidden, in which case NO-ONE should
     882             :       // be capturing anything - it could be a scrollbar inside this listbox
     883             :       // which is actually grabbing
     884             :       // This shouldn't be necessary. We should simply ensure that events targeting
     885             :       // scrollbars are never visible to DOM consumers.
     886           0 :       nsIPresShell::SetCapturingContent(nullptr, 0);
     887             :     }
     888             :   }
     889             : }
     890             : 
     891             : //---------------------------------------------------------
     892             : nsresult
     893           0 : nsListControlFrame::HandleEvent(nsPresContext* aPresContext,
     894             :                                 WidgetGUIEvent* aEvent,
     895             :                                 nsEventStatus* aEventStatus)
     896             : {
     897           0 :   NS_ENSURE_ARG_POINTER(aEventStatus);
     898             : 
     899             :   /*const char * desc[] = {"eMouseMove",
     900             :                           "NS_MOUSE_LEFT_BUTTON_UP",
     901             :                           "NS_MOUSE_LEFT_BUTTON_DOWN",
     902             :                           "<NA>","<NA>","<NA>","<NA>","<NA>","<NA>","<NA>",
     903             :                           "NS_MOUSE_MIDDLE_BUTTON_UP",
     904             :                           "NS_MOUSE_MIDDLE_BUTTON_DOWN",
     905             :                           "<NA>","<NA>","<NA>","<NA>","<NA>","<NA>","<NA>","<NA>",
     906             :                           "NS_MOUSE_RIGHT_BUTTON_UP",
     907             :                           "NS_MOUSE_RIGHT_BUTTON_DOWN",
     908             :                           "eMouseOver",
     909             :                           "eMouseOut",
     910             :                           "NS_MOUSE_LEFT_DOUBLECLICK",
     911             :                           "NS_MOUSE_MIDDLE_DOUBLECLICK",
     912             :                           "NS_MOUSE_RIGHT_DOUBLECLICK",
     913             :                           "NS_MOUSE_LEFT_CLICK",
     914             :                           "NS_MOUSE_MIDDLE_CLICK",
     915             :                           "NS_MOUSE_RIGHT_CLICK"};
     916             :   int inx = aEvent->mMessage - eMouseEventFirst;
     917             :   if (inx >= 0 && inx <= (NS_MOUSE_RIGHT_CLICK - eMouseEventFirst)) {
     918             :     printf("Mouse in ListFrame %s [%d]\n", desc[inx], aEvent->mMessage);
     919             :   } else {
     920             :     printf("Mouse in ListFrame <UNKNOWN> [%d]\n", aEvent->mMessage);
     921             :   }*/
     922             : 
     923           0 :   if (nsEventStatus_eConsumeNoDefault == *aEventStatus)
     924           0 :     return NS_OK;
     925             : 
     926             :   // do we have style that affects how we are selected?
     927             :   // do we have user-input style?
     928           0 :   const nsStyleUserInterface* uiStyle = StyleUserInterface();
     929           0 :   if (uiStyle->mUserInput == StyleUserInput::None ||
     930           0 :       uiStyle->mUserInput == StyleUserInput::Disabled) {
     931           0 :     return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
     932             :   }
     933           0 :   EventStates eventStates = mContent->AsElement()->State();
     934           0 :   if (eventStates.HasState(NS_EVENT_STATE_DISABLED))
     935           0 :     return NS_OK;
     936             : 
     937           0 :   return nsHTMLScrollFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
     938             : }
     939             : 
     940             : 
     941             : //---------------------------------------------------------
     942             : void
     943           0 : nsListControlFrame::SetInitialChildList(ChildListID    aListID,
     944             :                                         nsFrameList&   aChildList)
     945             : {
     946           0 :   if (aListID == kPrincipalList) {
     947             :     // First check to see if all the content has been added
     948           0 :     mIsAllContentHere = mContent->IsDoneAddingChildren();
     949           0 :     if (!mIsAllContentHere) {
     950           0 :       mIsAllFramesHere    = false;
     951           0 :       mHasBeenInitialized = false;
     952             :     }
     953             :   }
     954           0 :   nsHTMLScrollFrame::SetInitialChildList(aListID, aChildList);
     955             : 
     956             :   // If all the content is here now check
     957             :   // to see if all the frames have been created
     958             :   /*if (mIsAllContentHere) {
     959             :     // If all content and frames are here
     960             :     // the reset/initialize
     961             :     if (CheckIfAllFramesHere()) {
     962             :       ResetList(aPresContext);
     963             :       mHasBeenInitialized = true;
     964             :     }
     965             :   }*/
     966           0 : }
     967             : 
     968             : //---------------------------------------------------------
     969             : void
     970           0 : nsListControlFrame::Init(nsIContent*       aContent,
     971             :                          nsContainerFrame* aParent,
     972             :                          nsIFrame*         aPrevInFlow)
     973             : {
     974           0 :   nsHTMLScrollFrame::Init(aContent, aParent, aPrevInFlow);
     975             : 
     976           0 :   if (IsInDropDownMode()) {
     977           0 :     AddStateBits(NS_FRAME_IN_POPUP);
     978           0 :     CreateView();
     979             :   }
     980             : 
     981             :   // we shouldn't have to unregister this listener because when
     982             :   // our frame goes away all these content node go away as well
     983             :   // because our frame is the only one who references them.
     984             :   // we need to hook up our listeners before the editor is initialized
     985           0 :   mEventListener = new nsListEventListener(this);
     986             : 
     987           0 :   mContent->AddSystemEventListener(NS_LITERAL_STRING("keydown"),
     988           0 :                                    mEventListener, false, false);
     989           0 :   mContent->AddSystemEventListener(NS_LITERAL_STRING("keypress"),
     990           0 :                                    mEventListener, false, false);
     991           0 :   mContent->AddSystemEventListener(NS_LITERAL_STRING("mousedown"),
     992           0 :                                    mEventListener, false, false);
     993           0 :   mContent->AddSystemEventListener(NS_LITERAL_STRING("mouseup"),
     994           0 :                                    mEventListener, false, false);
     995           0 :   mContent->AddSystemEventListener(NS_LITERAL_STRING("mousemove"),
     996           0 :                                    mEventListener, false, false);
     997             : 
     998           0 :   mStartSelectionIndex = kNothingSelected;
     999           0 :   mEndSelectionIndex = kNothingSelected;
    1000             : 
    1001           0 :   mLastDropdownBackstopColor = PresContext()->DefaultBackgroundColor();
    1002           0 : }
    1003             : 
    1004             : dom::HTMLOptionsCollection*
    1005           0 : nsListControlFrame::GetOptions() const
    1006             : {
    1007             :   dom::HTMLSelectElement* select =
    1008           0 :     dom::HTMLSelectElement::FromContentOrNull(mContent);
    1009           0 :   NS_ENSURE_TRUE(select, nullptr);
    1010             : 
    1011           0 :   return select->Options();
    1012             : }
    1013             : 
    1014             : dom::HTMLOptionElement*
    1015           0 : nsListControlFrame::GetOption(uint32_t aIndex) const
    1016             : {
    1017             :   dom::HTMLSelectElement* select =
    1018           0 :     dom::HTMLSelectElement::FromContentOrNull(mContent);
    1019           0 :   NS_ENSURE_TRUE(select, nullptr);
    1020             : 
    1021           0 :   return select->Item(aIndex);
    1022             : }
    1023             : 
    1024             : NS_IMETHODIMP
    1025           0 : nsListControlFrame::OnOptionSelected(int32_t aIndex, bool aSelected)
    1026             : {
    1027           0 :   if (aSelected) {
    1028           0 :     ScrollToIndex(aIndex);
    1029             :   }
    1030           0 :   return NS_OK;
    1031             : }
    1032             : 
    1033             : void
    1034           0 : nsListControlFrame::OnContentReset()
    1035             : {
    1036           0 :   ResetList(true);
    1037           0 : }
    1038             : 
    1039             : void
    1040           0 : nsListControlFrame::ResetList(bool aAllowScrolling)
    1041             : {
    1042             :   // if all the frames aren't here
    1043             :   // don't bother reseting
    1044           0 :   if (!mIsAllFramesHere) {
    1045           0 :     return;
    1046             :   }
    1047             : 
    1048           0 :   if (aAllowScrolling) {
    1049           0 :     mPostChildrenLoadedReset = true;
    1050             : 
    1051             :     // Scroll to the selected index
    1052           0 :     int32_t indexToSelect = kNothingSelected;
    1053             : 
    1054           0 :     nsCOMPtr<nsIDOMHTMLSelectElement> selectElement(do_QueryInterface(mContent));
    1055           0 :     NS_ASSERTION(selectElement, "No select element!");
    1056           0 :     if (selectElement) {
    1057           0 :       selectElement->GetSelectedIndex(&indexToSelect);
    1058           0 :       AutoWeakFrame weakFrame(this);
    1059           0 :       ScrollToIndex(indexToSelect);
    1060           0 :       if (!weakFrame.IsAlive()) {
    1061           0 :         return;
    1062             :       }
    1063             :     }
    1064             :   }
    1065             : 
    1066           0 :   mStartSelectionIndex = kNothingSelected;
    1067           0 :   mEndSelectionIndex = kNothingSelected;
    1068           0 :   InvalidateFocus();
    1069             :   // Combobox will redisplay itself with the OnOptionSelected event
    1070             : }
    1071             : 
    1072             : void
    1073           0 : nsListControlFrame::SetFocus(bool aOn, bool aRepaint)
    1074             : {
    1075           0 :   InvalidateFocus();
    1076             : 
    1077           0 :   if (aOn) {
    1078           0 :     ComboboxFocusSet();
    1079           0 :     mFocused = this;
    1080             :   } else {
    1081           0 :     mFocused = nullptr;
    1082             :   }
    1083             : 
    1084           0 :   InvalidateFocus();
    1085           0 : }
    1086             : 
    1087           0 : void nsListControlFrame::ComboboxFocusSet()
    1088             : {
    1089           0 :   gLastKeyTime = 0;
    1090           0 : }
    1091             : 
    1092             : void
    1093           0 : nsListControlFrame::SetComboboxFrame(nsIFrame* aComboboxFrame)
    1094             : {
    1095           0 :   if (nullptr != aComboboxFrame) {
    1096           0 :     mComboboxFrame = do_QueryFrame(aComboboxFrame);
    1097             :   }
    1098           0 : }
    1099             : 
    1100             : void
    1101           0 : nsListControlFrame::GetOptionText(uint32_t aIndex, nsAString& aStr)
    1102             : {
    1103           0 :   aStr.Truncate();
    1104           0 :   if (dom::HTMLOptionElement* optionElement = GetOption(aIndex)) {
    1105           0 :     optionElement->GetText(aStr);
    1106             :   }
    1107           0 : }
    1108             : 
    1109             : int32_t
    1110           0 : nsListControlFrame::GetSelectedIndex()
    1111             : {
    1112             :   dom::HTMLSelectElement* select =
    1113           0 :     dom::HTMLSelectElement::FromContentOrNull(mContent);
    1114           0 :   return select->SelectedIndex();
    1115             : }
    1116             : 
    1117             : dom::HTMLOptionElement*
    1118           0 : nsListControlFrame::GetCurrentOption()
    1119             : {
    1120             :   // The mEndSelectionIndex is what is currently being selected. Use
    1121             :   // the selected index if this is kNothingSelected.
    1122           0 :   int32_t focusedIndex = (mEndSelectionIndex == kNothingSelected) ?
    1123           0 :     GetSelectedIndex() : mEndSelectionIndex;
    1124             : 
    1125           0 :   if (focusedIndex != kNothingSelected) {
    1126           0 :     return GetOption(AssertedCast<uint32_t>(focusedIndex));
    1127             :   }
    1128             : 
    1129             :   // There is no selected option. Return the first non-disabled option, if any.
    1130           0 :   return GetNonDisabledOptionFrom(0);
    1131             : }
    1132             : 
    1133             : HTMLOptionElement*
    1134           0 : nsListControlFrame::GetNonDisabledOptionFrom(int32_t aFromIndex,
    1135             :                                              int32_t* aFoundIndex)
    1136             : {
    1137             :   RefPtr<dom::HTMLSelectElement> selectElement =
    1138           0 :     dom::HTMLSelectElement::FromContent(mContent);
    1139             : 
    1140           0 :   const uint32_t length = selectElement->Length();
    1141           0 :   for (uint32_t i = std::max(aFromIndex, 0); i < length; ++i) {
    1142           0 :     HTMLOptionElement* node = selectElement->Item(i);
    1143           0 :     if (!node) {
    1144           0 :       break;
    1145             :     }
    1146           0 :     if (!selectElement->IsOptionDisabled(node)) {
    1147           0 :       if (aFoundIndex) {
    1148           0 :         *aFoundIndex = i;
    1149             :       }
    1150           0 :       return node;
    1151             :     }
    1152             :   }
    1153           0 :   return nullptr;
    1154             : }
    1155             : 
    1156             : bool
    1157           0 : nsListControlFrame::IsInDropDownMode() const
    1158             : {
    1159           0 :   return (mComboboxFrame != nullptr);
    1160             : }
    1161             : 
    1162             : uint32_t
    1163           0 : nsListControlFrame::GetNumberOfOptions()
    1164             : {
    1165           0 :   dom::HTMLOptionsCollection* options = GetOptions();
    1166           0 :   if (!options) {
    1167           0 :     return 0;
    1168             :   }
    1169             : 
    1170           0 :   return options->Length();
    1171             : }
    1172             : 
    1173             : //----------------------------------------------------------------------
    1174             : // nsISelectControlFrame
    1175             : //----------------------------------------------------------------------
    1176           0 : bool nsListControlFrame::CheckIfAllFramesHere()
    1177             : {
    1178             :   // Get the number of optgroups and options
    1179             :   //int32_t numContentItems = 0;
    1180           0 :   nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent));
    1181           0 :   if (node) {
    1182             :     // XXX Need to find a fail proff way to determine that
    1183             :     // all the frames are there
    1184           0 :     mIsAllFramesHere = true;//NS_OK == CountAllChild(node, numContentItems);
    1185             :   }
    1186             :   // now make sure we have a frame each piece of content
    1187             : 
    1188           0 :   return mIsAllFramesHere;
    1189             : }
    1190             : 
    1191             : NS_IMETHODIMP
    1192           0 : nsListControlFrame::DoneAddingChildren(bool aIsDone)
    1193             : {
    1194           0 :   mIsAllContentHere = aIsDone;
    1195           0 :   if (mIsAllContentHere) {
    1196             :     // Here we check to see if all the frames have been created
    1197             :     // for all the content.
    1198             :     // If so, then we can initialize;
    1199           0 :     if (!mIsAllFramesHere) {
    1200             :       // if all the frames are now present we can initialize
    1201           0 :       if (CheckIfAllFramesHere()) {
    1202           0 :         mHasBeenInitialized = true;
    1203           0 :         ResetList(true);
    1204             :       }
    1205             :     }
    1206             :   }
    1207           0 :   return NS_OK;
    1208             : }
    1209             : 
    1210             : NS_IMETHODIMP
    1211           0 : nsListControlFrame::AddOption(int32_t aIndex)
    1212             : {
    1213             : #ifdef DO_REFLOW_DEBUG
    1214             :   printf("---- Id: %d nsLCF %p Added Option %d\n", mReflowId, this, aIndex);
    1215             : #endif
    1216             : 
    1217           0 :   if (!mIsAllContentHere) {
    1218           0 :     mIsAllContentHere = mContent->IsDoneAddingChildren();
    1219           0 :     if (!mIsAllContentHere) {
    1220           0 :       mIsAllFramesHere    = false;
    1221           0 :       mHasBeenInitialized = false;
    1222             :     } else {
    1223           0 :       mIsAllFramesHere = (aIndex == static_cast<int32_t>(GetNumberOfOptions()-1));
    1224             :     }
    1225             :   }
    1226             : 
    1227             :   // Make sure we scroll to the selected option as needed
    1228           0 :   mNeedToReset = true;
    1229             : 
    1230           0 :   if (!mHasBeenInitialized) {
    1231           0 :     return NS_OK;
    1232             :   }
    1233             : 
    1234           0 :   mPostChildrenLoadedReset = mIsAllContentHere;
    1235           0 :   return NS_OK;
    1236             : }
    1237             : 
    1238             : static int32_t
    1239           0 : DecrementAndClamp(int32_t aSelectionIndex, int32_t aLength)
    1240             : {
    1241           0 :   return aLength == 0 ? kNothingSelected : std::max(0, aSelectionIndex - 1);
    1242             : }
    1243             : 
    1244             : NS_IMETHODIMP
    1245           0 : nsListControlFrame::RemoveOption(int32_t aIndex)
    1246             : {
    1247           0 :   NS_PRECONDITION(aIndex >= 0, "negative <option> index");
    1248             : 
    1249             :   // Need to reset if we're a dropdown
    1250           0 :   if (IsInDropDownMode()) {
    1251           0 :     mNeedToReset = true;
    1252           0 :     mPostChildrenLoadedReset = mIsAllContentHere;
    1253             :   }
    1254             : 
    1255           0 :   if (mStartSelectionIndex != kNothingSelected) {
    1256           0 :     NS_ASSERTION(mEndSelectionIndex != kNothingSelected, "");
    1257           0 :     int32_t numOptions = GetNumberOfOptions();
    1258             :     // NOTE: numOptions is the new number of options whereas aIndex is the
    1259             :     // unadjusted index of the removed option (hence the <= below).
    1260           0 :     NS_ASSERTION(aIndex <= numOptions, "out-of-bounds <option> index");
    1261             : 
    1262           0 :     int32_t forward = mEndSelectionIndex - mStartSelectionIndex;
    1263           0 :     int32_t* low  = forward >= 0 ? &mStartSelectionIndex : &mEndSelectionIndex;
    1264           0 :     int32_t* high = forward >= 0 ? &mEndSelectionIndex : &mStartSelectionIndex;
    1265           0 :     if (aIndex < *low)
    1266           0 :       *low = ::DecrementAndClamp(*low, numOptions);
    1267           0 :     if (aIndex <= *high)
    1268           0 :       *high = ::DecrementAndClamp(*high, numOptions);
    1269           0 :     if (forward == 0)
    1270           0 :       *low = *high;
    1271             :   }
    1272             :   else
    1273           0 :     NS_ASSERTION(mEndSelectionIndex == kNothingSelected, "");
    1274             : 
    1275           0 :   InvalidateFocus();
    1276           0 :   return NS_OK;
    1277             : }
    1278             : 
    1279             : //---------------------------------------------------------
    1280             : // Set the option selected in the DOM.  This method is named
    1281             : // as it is because it indicates that the frame is the source
    1282             : // of this event rather than the receiver.
    1283             : bool
    1284           0 : nsListControlFrame::SetOptionsSelectedFromFrame(int32_t aStartIndex,
    1285             :                                                 int32_t aEndIndex,
    1286             :                                                 bool aValue,
    1287             :                                                 bool aClearAll)
    1288             : {
    1289             :   RefPtr<dom::HTMLSelectElement> selectElement =
    1290           0 :     dom::HTMLSelectElement::FromContent(mContent);
    1291             : 
    1292           0 :   uint32_t mask = dom::HTMLSelectElement::NOTIFY;
    1293           0 :   if (mForceSelection) {
    1294           0 :     mask |= dom::HTMLSelectElement::SET_DISABLED;
    1295             :   }
    1296           0 :   if (aValue) {
    1297           0 :     mask |= dom::HTMLSelectElement::IS_SELECTED;
    1298             :   }
    1299           0 :   if (aClearAll) {
    1300           0 :     mask |= dom::HTMLSelectElement::CLEAR_ALL;
    1301             :   }
    1302             : 
    1303           0 :   return selectElement->SetOptionsSelectedByIndex(aStartIndex, aEndIndex, mask);
    1304             : }
    1305             : 
    1306             : bool
    1307           0 : nsListControlFrame::ToggleOptionSelectedFromFrame(int32_t aIndex)
    1308             : {
    1309             :   RefPtr<dom::HTMLOptionElement> option =
    1310           0 :     GetOption(static_cast<uint32_t>(aIndex));
    1311           0 :   NS_ENSURE_TRUE(option, false);
    1312             : 
    1313             :   RefPtr<dom::HTMLSelectElement> selectElement =
    1314           0 :     dom::HTMLSelectElement::FromContent(mContent);
    1315             : 
    1316           0 :   uint32_t mask = dom::HTMLSelectElement::NOTIFY;
    1317           0 :   if (!option->Selected()) {
    1318           0 :     mask |= dom::HTMLSelectElement::IS_SELECTED;
    1319             :   }
    1320             : 
    1321           0 :   return selectElement->SetOptionsSelectedByIndex(aIndex, aIndex, mask);
    1322             : }
    1323             : 
    1324             : 
    1325             : // Dispatch event and such
    1326             : bool
    1327           0 : nsListControlFrame::UpdateSelection()
    1328             : {
    1329           0 :   if (mIsAllFramesHere) {
    1330             :     // if it's a combobox, display the new text
    1331           0 :     AutoWeakFrame weakFrame(this);
    1332           0 :     if (mComboboxFrame) {
    1333           0 :       mComboboxFrame->RedisplaySelectedText();
    1334             : 
    1335             :       // When dropdown list is open, onchange event will be fired when Enter key
    1336             :       // is hit or when dropdown list is dismissed.
    1337           0 :       if (mComboboxFrame->IsDroppedDown()) {
    1338           0 :         return weakFrame.IsAlive();
    1339             :       }
    1340             :     }
    1341           0 :     if (mIsAllContentHere) {
    1342           0 :       FireOnInputAndOnChange();
    1343             :     }
    1344           0 :     return weakFrame.IsAlive();
    1345             :   }
    1346           0 :   return true;
    1347             : }
    1348             : 
    1349             : void
    1350           0 : nsListControlFrame::ComboboxFinish(int32_t aIndex)
    1351             : {
    1352           0 :   gLastKeyTime = 0;
    1353             : 
    1354           0 :   if (mComboboxFrame) {
    1355           0 :     int32_t displayIndex = mComboboxFrame->GetIndexOfDisplayArea();
    1356             :     // Make sure we can always reset to the displayed index
    1357           0 :     mForceSelection = displayIndex == aIndex;
    1358             : 
    1359           0 :     AutoWeakFrame weakFrame(this);
    1360           0 :     PerformSelection(aIndex, false, false);  // might destroy us
    1361           0 :     if (!weakFrame.IsAlive() || !mComboboxFrame) {
    1362           0 :       return;
    1363             :     }
    1364             : 
    1365           0 :     if (displayIndex != aIndex) {
    1366           0 :       mComboboxFrame->RedisplaySelectedText(); // might destroy us
    1367             :     }
    1368             : 
    1369           0 :     if (weakFrame.IsAlive() && mComboboxFrame) {
    1370           0 :       mComboboxFrame->RollupFromList(); // might destroy us
    1371             :     }
    1372             :   }
    1373             : }
    1374             : 
    1375             : // Send out an onInput and onChange notification.
    1376             : void
    1377           0 : nsListControlFrame::FireOnInputAndOnChange()
    1378             : {
    1379           0 :   if (mComboboxFrame) {
    1380             :     // Return hit without changing anything
    1381           0 :     int32_t index = mComboboxFrame->UpdateRecentIndex(NS_SKIP_NOTIFY_INDEX);
    1382           0 :     if (index == NS_SKIP_NOTIFY_INDEX) {
    1383           0 :       return;
    1384             :     }
    1385             : 
    1386             :     // See if the selection actually changed
    1387           0 :     if (index == GetSelectedIndex()) {
    1388           0 :       return;
    1389             :     }
    1390             :   }
    1391             : 
    1392           0 :   nsCOMPtr<nsIContent> content = mContent;
    1393             :   // Dispatch the input event.
    1394           0 :   nsContentUtils::DispatchTrustedEvent(content->OwnerDoc(), content,
    1395           0 :                                        NS_LITERAL_STRING("input"), true,
    1396           0 :                                        false);
    1397             :   // Dispatch the change event.
    1398           0 :   nsContentUtils::DispatchTrustedEvent(content->OwnerDoc(), content,
    1399           0 :                                        NS_LITERAL_STRING("change"), true,
    1400           0 :                                        false);
    1401             : }
    1402             : 
    1403             : NS_IMETHODIMP
    1404           0 : nsListControlFrame::OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex)
    1405             : {
    1406           0 :   if (mComboboxFrame) {
    1407             :     // UpdateRecentIndex with NS_SKIP_NOTIFY_INDEX, so that we won't fire an onchange
    1408             :     // event for this setting of selectedIndex.
    1409           0 :     mComboboxFrame->UpdateRecentIndex(NS_SKIP_NOTIFY_INDEX);
    1410             :   }
    1411             : 
    1412           0 :   AutoWeakFrame weakFrame(this);
    1413           0 :   ScrollToIndex(aNewIndex);
    1414           0 :   if (!weakFrame.IsAlive()) {
    1415           0 :     return NS_OK;
    1416             :   }
    1417           0 :   mStartSelectionIndex = aNewIndex;
    1418           0 :   mEndSelectionIndex = aNewIndex;
    1419           0 :   InvalidateFocus();
    1420             : 
    1421             : #ifdef ACCESSIBILITY
    1422           0 :   FireMenuItemActiveEvent();
    1423             : #endif
    1424             : 
    1425           0 :   return NS_OK;
    1426             : }
    1427             : 
    1428             : //----------------------------------------------------------------------
    1429             : // End nsISelectControlFrame
    1430             : //----------------------------------------------------------------------
    1431             : 
    1432             : nsresult
    1433           0 : nsListControlFrame::SetFormProperty(nsIAtom* aName,
    1434             :                                 const nsAString& aValue)
    1435             : {
    1436           0 :   if (nsGkAtoms::selected == aName) {
    1437           0 :     return NS_ERROR_INVALID_ARG; // Selected is readonly according to spec.
    1438           0 :   } else if (nsGkAtoms::selectedindex == aName) {
    1439             :     // You shouldn't be calling me for this!!!
    1440           0 :     return NS_ERROR_INVALID_ARG;
    1441             :   }
    1442             : 
    1443             :   // We should be told about selectedIndex by the DOM element through
    1444             :   // OnOptionSelected
    1445             : 
    1446           0 :   return NS_OK;
    1447             : }
    1448             : 
    1449             : void
    1450           0 : nsListControlFrame::AboutToDropDown()
    1451             : {
    1452           0 :   NS_ASSERTION(IsInDropDownMode(),
    1453             :     "AboutToDropDown called without being in dropdown mode");
    1454             : 
    1455             :   // Our widget doesn't get invalidated on changes to the rest of the document,
    1456             :   // so compute and store this color at the start of a dropdown so we don't
    1457             :   // get weird painting behaviour.
    1458             :   // We start looking for backgrounds above the combobox frame to avoid
    1459             :   // duplicating the combobox frame's background and compose each background
    1460             :   // color we find underneath until we have an opaque color, or run out of
    1461             :   // backgrounds. We compose with the PresContext default background color,
    1462             :   // which is always opaque, in case we don't end up with an opaque color.
    1463             :   // This gives us a very poor approximation of translucency.
    1464           0 :   nsIFrame* comboboxFrame = do_QueryFrame(mComboboxFrame);
    1465           0 :   nsIFrame* ancestor = comboboxFrame->GetParent();
    1466           0 :   mLastDropdownBackstopColor = NS_RGBA(0,0,0,0);
    1467           0 :   while (NS_GET_A(mLastDropdownBackstopColor) < 255 && ancestor) {
    1468           0 :     nsStyleContext* context = ancestor->StyleContext();
    1469           0 :     mLastDropdownBackstopColor =
    1470           0 :       NS_ComposeColors(context->StyleBackground()->BackgroundColor(context),
    1471             :                        mLastDropdownBackstopColor);
    1472           0 :     ancestor = ancestor->GetParent();
    1473             :   }
    1474           0 :   mLastDropdownBackstopColor =
    1475           0 :     NS_ComposeColors(PresContext()->DefaultBackgroundColor(),
    1476             :                      mLastDropdownBackstopColor);
    1477             : 
    1478           0 :   if (mIsAllContentHere && mIsAllFramesHere && mHasBeenInitialized) {
    1479           0 :     AutoWeakFrame weakFrame(this);
    1480           0 :     ScrollToIndex(GetSelectedIndex());
    1481           0 :     if (!weakFrame.IsAlive()) {
    1482           0 :       return;
    1483             :     }
    1484             : #ifdef ACCESSIBILITY
    1485           0 :     FireMenuItemActiveEvent(); // Inform assistive tech what got focus
    1486             : #endif
    1487             :   }
    1488           0 :   mItemSelectionStarted = false;
    1489           0 :   mForceSelection = false;
    1490             : }
    1491             : 
    1492             : // We are about to be rolledup from the outside (ComboboxFrame)
    1493             : void
    1494           0 : nsListControlFrame::AboutToRollup()
    1495             : {
    1496             :   // We've been updating the combobox with the keyboard up until now, but not
    1497             :   // with the mouse.  The problem is, even with mouse selection, we are
    1498             :   // updating the <select>.  So if the mouse goes over an option just before
    1499             :   // he leaves the box and clicks, that's what the <select> will show.
    1500             :   //
    1501             :   // To deal with this we say "whatever is in the combobox is canonical."
    1502             :   // - IF the combobox is different from the current selected index, we
    1503             :   //   reset the index.
    1504             : 
    1505           0 :   if (IsInDropDownMode()) {
    1506           0 :     ComboboxFinish(mComboboxFrame->GetIndexOfDisplayArea()); // might destroy us
    1507             :   }
    1508           0 : }
    1509             : 
    1510             : void
    1511           0 : nsListControlFrame::DidReflow(nsPresContext*           aPresContext,
    1512             :                               const ReflowInput* aReflowInput,
    1513             :                               nsDidReflowStatus        aStatus)
    1514             : {
    1515           0 :   bool wasInterrupted = !mHasPendingInterruptAtStartOfReflow &&
    1516           0 :                           aPresContext->HasPendingInterrupt();
    1517             : 
    1518           0 :   nsHTMLScrollFrame::DidReflow(aPresContext, aReflowInput, aStatus);
    1519             : 
    1520           0 :   if (mNeedToReset && !wasInterrupted) {
    1521           0 :     mNeedToReset = false;
    1522             :     // Suppress scrolling to the selected element if we restored
    1523             :     // scroll history state AND the list contents have not changed
    1524             :     // since we loaded all the children AND nothing else forced us
    1525             :     // to scroll by calling ResetList(true). The latter two conditions
    1526             :     // are folded into mPostChildrenLoadedReset.
    1527             :     //
    1528             :     // The idea is that we want scroll history restoration to trump ResetList
    1529             :     // scrolling to the selected element, when the ResetList was probably only
    1530             :     // caused by content loading normally.
    1531           0 :     ResetList(!DidHistoryRestore() || mPostChildrenLoadedReset);
    1532             :   }
    1533             : 
    1534           0 :   mHasPendingInterruptAtStartOfReflow = false;
    1535           0 : }
    1536             : 
    1537             : #ifdef DEBUG_FRAME_DUMP
    1538             : nsresult
    1539           0 : nsListControlFrame::GetFrameName(nsAString& aResult) const
    1540             : {
    1541           0 :   return MakeFrameName(NS_LITERAL_STRING("ListControl"), aResult);
    1542             : }
    1543             : #endif
    1544             : 
    1545             : nscoord
    1546           0 : nsListControlFrame::GetBSizeOfARow()
    1547             : {
    1548           0 :   return BSizeOfARow();
    1549             : }
    1550             : 
    1551             : nsresult
    1552           0 : nsListControlFrame::IsOptionDisabled(int32_t anIndex, bool &aIsDisabled)
    1553             : {
    1554             :   RefPtr<dom::HTMLSelectElement> sel =
    1555           0 :     dom::HTMLSelectElement::FromContent(mContent);
    1556           0 :   if (sel) {
    1557           0 :     sel->IsOptionDisabled(anIndex, &aIsDisabled);
    1558           0 :     return NS_OK;
    1559             :   }
    1560           0 :   return NS_ERROR_FAILURE;
    1561             : }
    1562             : 
    1563             : //----------------------------------------------------------------------
    1564             : // helper
    1565             : //----------------------------------------------------------------------
    1566             : bool
    1567           0 : nsListControlFrame::IsLeftButton(nsIDOMEvent* aMouseEvent)
    1568             : {
    1569             :   // only allow selection with the left button
    1570           0 :   nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aMouseEvent);
    1571           0 :   if (mouseEvent) {
    1572             :     int16_t whichButton;
    1573           0 :     if (NS_SUCCEEDED(mouseEvent->GetButton(&whichButton))) {
    1574           0 :       return whichButton != 0?false:true;
    1575             :     }
    1576             :   }
    1577           0 :   return false;
    1578             : }
    1579             : 
    1580             : nscoord
    1581           0 : nsListControlFrame::CalcFallbackRowBSize(float aFontSizeInflation)
    1582             : {
    1583             :   RefPtr<nsFontMetrics> fontMet =
    1584           0 :     nsLayoutUtils::GetFontMetricsForFrame(this, aFontSizeInflation);
    1585           0 :   return fontMet->MaxHeight();
    1586             : }
    1587             : 
    1588             : nscoord
    1589           0 : nsListControlFrame::CalcIntrinsicBSize(nscoord aBSizeOfARow,
    1590             :                                        int32_t aNumberOfOptions)
    1591             : {
    1592           0 :   NS_PRECONDITION(!IsInDropDownMode(),
    1593             :                   "Shouldn't be in dropdown mode when we call this");
    1594             : 
    1595             :   dom::HTMLSelectElement* select =
    1596           0 :     dom::HTMLSelectElement::FromContentOrNull(mContent);
    1597           0 :   if (select) {
    1598           0 :     mNumDisplayRows = select->Size();
    1599             :   } else {
    1600           0 :     mNumDisplayRows = 1;
    1601             :   }
    1602             : 
    1603           0 :   if (mNumDisplayRows < 1) {
    1604           0 :     mNumDisplayRows = 4;
    1605             :   }
    1606             : 
    1607           0 :   return mNumDisplayRows * aBSizeOfARow;
    1608             : }
    1609             : 
    1610             : //----------------------------------------------------------------------
    1611             : // nsIDOMMouseListener
    1612             : //----------------------------------------------------------------------
    1613             : nsresult
    1614           0 : nsListControlFrame::MouseUp(nsIDOMEvent* aMouseEvent)
    1615             : {
    1616           0 :   NS_ASSERTION(aMouseEvent != nullptr, "aMouseEvent is null.");
    1617             : 
    1618           0 :   nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aMouseEvent);
    1619           0 :   NS_ENSURE_TRUE(mouseEvent, NS_ERROR_FAILURE);
    1620             : 
    1621           0 :   UpdateInListState(aMouseEvent);
    1622             : 
    1623           0 :   mButtonDown = false;
    1624             : 
    1625           0 :   EventStates eventStates = mContent->AsElement()->State();
    1626           0 :   if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
    1627           0 :     return NS_OK;
    1628             :   }
    1629             : 
    1630             :   // only allow selection with the left button
    1631             :   // if a right button click is on the combobox itself
    1632             :   // or on the select when in listbox mode, then let the click through
    1633           0 :   if (!IsLeftButton(aMouseEvent)) {
    1634           0 :     if (IsInDropDownMode()) {
    1635           0 :       if (!IgnoreMouseEventForSelection(aMouseEvent)) {
    1636           0 :         aMouseEvent->PreventDefault();
    1637           0 :         aMouseEvent->StopPropagation();
    1638             :       } else {
    1639           0 :         CaptureMouseEvents(false);
    1640           0 :         return NS_OK;
    1641             :       }
    1642           0 :       CaptureMouseEvents(false);
    1643           0 :       return NS_ERROR_FAILURE; // means consume event
    1644             :     } else {
    1645           0 :       CaptureMouseEvents(false);
    1646           0 :       return NS_OK;
    1647             :     }
    1648             :   }
    1649             : 
    1650           0 :   const nsStyleVisibility* vis = StyleVisibility();
    1651             : 
    1652           0 :   if (!vis->IsVisible()) {
    1653           0 :     return NS_OK;
    1654             :   }
    1655             : 
    1656           0 :   if (IsInDropDownMode()) {
    1657             :     // XXX This is a bit of a hack, but.....
    1658             :     // But the idea here is to make sure you get an "onclick" event when you mouse
    1659             :     // down on the select and the drag over an option and let go
    1660             :     // And then NOT get an "onclick" event when when you click down on the select
    1661             :     // and then up outside of the select
    1662             :     // the EventStateManager tracks the content of the mouse down and the mouse up
    1663             :     // to make sure they are the same, and the onclick is sent in the PostHandleEvent
    1664             :     // depeneding on whether the clickCount is non-zero.
    1665             :     // So we cheat here by either setting or unsetting the clcikCount in the native event
    1666             :     // so the right thing happens for the onclick event
    1667             :     WidgetMouseEvent* mouseEvent =
    1668           0 :       aMouseEvent->WidgetEventPtr()->AsMouseEvent();
    1669             : 
    1670             :     int32_t selectedIndex;
    1671           0 :     if (NS_SUCCEEDED(GetIndexFromDOMEvent(aMouseEvent, selectedIndex))) {
    1672             :       // If it's disabled, disallow the click and leave.
    1673           0 :       bool isDisabled = false;
    1674           0 :       IsOptionDisabled(selectedIndex, isDisabled);
    1675           0 :       if (isDisabled) {
    1676           0 :         aMouseEvent->PreventDefault();
    1677           0 :         aMouseEvent->StopPropagation();
    1678           0 :         CaptureMouseEvents(false);
    1679           0 :         return NS_ERROR_FAILURE;
    1680             :       }
    1681             : 
    1682           0 :       if (kNothingSelected != selectedIndex) {
    1683           0 :         AutoWeakFrame weakFrame(this);
    1684           0 :         ComboboxFinish(selectedIndex);
    1685           0 :         if (!weakFrame.IsAlive()) {
    1686           0 :           return NS_OK;
    1687             :         }
    1688             : 
    1689           0 :         FireOnInputAndOnChange();
    1690             :       }
    1691             : 
    1692           0 :       mouseEvent->mClickCount = 1;
    1693             :     } else {
    1694             :       // the click was out side of the select or its dropdown
    1695           0 :       mouseEvent->mClickCount =
    1696           0 :         IgnoreMouseEventForSelection(aMouseEvent) ? 1 : 0;
    1697             :     }
    1698             :   } else {
    1699           0 :     CaptureMouseEvents(false);
    1700             :     // Notify
    1701           0 :     if (mChangesSinceDragStart) {
    1702             :       // reset this so that future MouseUps without a prior MouseDown
    1703             :       // won't fire onchange
    1704           0 :       mChangesSinceDragStart = false;
    1705           0 :       FireOnInputAndOnChange();
    1706             :     }
    1707             :   }
    1708             : 
    1709           0 :   return NS_OK;
    1710             : }
    1711             : 
    1712             : void
    1713           0 : nsListControlFrame::UpdateInListState(nsIDOMEvent* aEvent)
    1714             : {
    1715           0 :   if (!mComboboxFrame || !mComboboxFrame->IsDroppedDown())
    1716           0 :     return;
    1717             : 
    1718           0 :   nsPoint pt = nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(aEvent, this);
    1719           0 :   nsRect borderInnerEdge = GetScrollPortRect();
    1720           0 :   if (pt.y >= borderInnerEdge.y && pt.y < borderInnerEdge.YMost()) {
    1721           0 :     mItemSelectionStarted = true;
    1722             :   }
    1723             : }
    1724             : 
    1725           0 : bool nsListControlFrame::IgnoreMouseEventForSelection(nsIDOMEvent* aEvent)
    1726             : {
    1727           0 :   if (!mComboboxFrame)
    1728           0 :     return false;
    1729             : 
    1730             :   // Our DOM listener does get called when the dropdown is not
    1731             :   // showing, because it listens to events on the SELECT element
    1732           0 :   if (!mComboboxFrame->IsDroppedDown())
    1733           0 :     return true;
    1734             : 
    1735           0 :   return !mItemSelectionStarted;
    1736             : }
    1737             : 
    1738             : #ifdef ACCESSIBILITY
    1739             : void
    1740           0 : nsListControlFrame::FireMenuItemActiveEvent()
    1741             : {
    1742           0 :   if (mFocused != this && !IsInDropDownMode()) {
    1743           0 :     return;
    1744             :   }
    1745             : 
    1746           0 :   nsCOMPtr<nsIContent> optionContent = GetCurrentOption();
    1747           0 :   if (!optionContent) {
    1748           0 :     return;
    1749             :   }
    1750             : 
    1751           0 :   FireDOMEvent(NS_LITERAL_STRING("DOMMenuItemActive"), optionContent);
    1752             : }
    1753             : #endif
    1754             : 
    1755             : nsresult
    1756           0 : nsListControlFrame::GetIndexFromDOMEvent(nsIDOMEvent* aMouseEvent,
    1757             :                                          int32_t&     aCurIndex)
    1758             : {
    1759           0 :   if (IgnoreMouseEventForSelection(aMouseEvent))
    1760           0 :     return NS_ERROR_FAILURE;
    1761             : 
    1762           0 :   if (nsIPresShell::GetCapturingContent() != mContent) {
    1763             :     // If we're not capturing, then ignore movement in the border
    1764           0 :     nsPoint pt = nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(aMouseEvent, this);
    1765           0 :     nsRect borderInnerEdge = GetScrollPortRect();
    1766           0 :     if (!borderInnerEdge.Contains(pt)) {
    1767           0 :       return NS_ERROR_FAILURE;
    1768             :     }
    1769             :   }
    1770             : 
    1771           0 :   RefPtr<dom::HTMLOptionElement> option;
    1772           0 :   for (nsCOMPtr<nsIContent> content =
    1773           0 :          PresContext()->EventStateManager()->GetEventTargetContent(nullptr);
    1774           0 :        content && !option;
    1775           0 :        content = content->GetParent()) {
    1776           0 :     option = dom::HTMLOptionElement::FromContent(content);
    1777             :   }
    1778             : 
    1779           0 :   if (option) {
    1780           0 :     aCurIndex = option->Index();
    1781           0 :     MOZ_ASSERT(aCurIndex >= 0);
    1782           0 :     return NS_OK;
    1783             :   }
    1784             : 
    1785           0 :   return NS_ERROR_FAILURE;
    1786             : }
    1787             : 
    1788             : static bool
    1789           0 : FireShowDropDownEvent(nsIContent* aContent, bool aShow, bool aIsSourceTouchEvent)
    1790             : {
    1791           0 :   if (ShouldFireDropDownEvent()) {
    1792           0 :     nsString eventName;
    1793           0 :     if (aShow) {
    1794           0 :       eventName = aIsSourceTouchEvent ? NS_LITERAL_STRING("mozshowdropdown-sourcetouch") :
    1795           0 :                                         NS_LITERAL_STRING("mozshowdropdown");
    1796             :     } else {
    1797           0 :       eventName = NS_LITERAL_STRING("mozhidedropdown");
    1798             :     }
    1799           0 :     nsContentUtils::DispatchChromeEvent(aContent->OwnerDoc(), aContent,
    1800           0 :                                         eventName, true, false);
    1801           0 :     return true;
    1802             :   }
    1803             : 
    1804           0 :   return false;
    1805             : }
    1806             : 
    1807             : nsresult
    1808           0 : nsListControlFrame::MouseDown(nsIDOMEvent* aMouseEvent)
    1809             : {
    1810           0 :   NS_ASSERTION(aMouseEvent != nullptr, "aMouseEvent is null.");
    1811             : 
    1812           0 :   nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aMouseEvent);
    1813           0 :   NS_ENSURE_TRUE(mouseEvent, NS_ERROR_FAILURE);
    1814             : 
    1815           0 :   UpdateInListState(aMouseEvent);
    1816             : 
    1817           0 :   EventStates eventStates = mContent->AsElement()->State();
    1818           0 :   if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
    1819           0 :     return NS_OK;
    1820             :   }
    1821             : 
    1822             :   // only allow selection with the left button
    1823             :   // if a right button click is on the combobox itself
    1824             :   // or on the select when in listbox mode, then let the click through
    1825           0 :   if (!IsLeftButton(aMouseEvent)) {
    1826           0 :     if (IsInDropDownMode()) {
    1827           0 :       if (!IgnoreMouseEventForSelection(aMouseEvent)) {
    1828           0 :         aMouseEvent->PreventDefault();
    1829           0 :         aMouseEvent->StopPropagation();
    1830             :       } else {
    1831           0 :         return NS_OK;
    1832             :       }
    1833           0 :       return NS_ERROR_FAILURE; // means consume event
    1834             :     } else {
    1835           0 :       return NS_OK;
    1836             :     }
    1837             :   }
    1838             : 
    1839             :   int32_t selectedIndex;
    1840           0 :   if (NS_SUCCEEDED(GetIndexFromDOMEvent(aMouseEvent, selectedIndex))) {
    1841             :     // Handle Like List
    1842           0 :     mButtonDown = true;
    1843           0 :     CaptureMouseEvents(true);
    1844           0 :     AutoWeakFrame weakFrame(this);
    1845             :     bool change =
    1846           0 :       HandleListSelection(aMouseEvent, selectedIndex); // might destroy us
    1847           0 :     if (!weakFrame.IsAlive()) {
    1848           0 :       return NS_OK;
    1849             :     }
    1850           0 :     mChangesSinceDragStart = change;
    1851             :   } else {
    1852             :     // NOTE: the combo box is responsible for dropping it down
    1853           0 :     if (mComboboxFrame) {
    1854             :       // Ignore the click that occurs on the option element when one is
    1855             :       // selected from the parent process popup.
    1856           0 :       if (mComboboxFrame->IsOpenInParentProcess()) {
    1857           0 :         nsCOMPtr<nsIDOMEventTarget> etarget;
    1858           0 :         aMouseEvent->GetTarget(getter_AddRefs(etarget));
    1859           0 :         nsCOMPtr<nsIDOMHTMLOptionElement> option = do_QueryInterface(etarget);
    1860           0 :         if (option) {
    1861           0 :           return NS_OK;
    1862             :         }
    1863             :       }
    1864             : 
    1865           0 :       uint16_t inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
    1866           0 :       if (NS_FAILED(mouseEvent->GetMozInputSource(&inputSource))) {
    1867           0 :         return NS_ERROR_FAILURE;
    1868             :       }
    1869           0 :       bool isSourceTouchEvent = inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
    1870           0 :       if (FireShowDropDownEvent(mContent, !mComboboxFrame->IsDroppedDownOrHasParentPopup(),
    1871             :                                 isSourceTouchEvent)) {
    1872           0 :         return NS_OK;
    1873             :       }
    1874             : 
    1875           0 :       if (!IgnoreMouseEventForSelection(aMouseEvent)) {
    1876           0 :         return NS_OK;
    1877             :       }
    1878             : 
    1879           0 :       if (!nsComboboxControlFrame::ToolkitHasNativePopup())
    1880             :       {
    1881           0 :         bool isDroppedDown = mComboboxFrame->IsDroppedDown();
    1882           0 :         nsIFrame* comboFrame = do_QueryFrame(mComboboxFrame);
    1883           0 :         AutoWeakFrame weakFrame(comboFrame);
    1884           0 :         mComboboxFrame->ShowDropDown(!isDroppedDown);
    1885           0 :         if (!weakFrame.IsAlive())
    1886           0 :           return NS_OK;
    1887           0 :         if (isDroppedDown) {
    1888           0 :           CaptureMouseEvents(false);
    1889             :         }
    1890             :       }
    1891             :     }
    1892             :   }
    1893             : 
    1894           0 :   return NS_OK;
    1895             : }
    1896             : 
    1897             : //----------------------------------------------------------------------
    1898             : // nsIDOMMouseMotionListener
    1899             : //----------------------------------------------------------------------
    1900             : nsresult
    1901           0 : nsListControlFrame::MouseMove(nsIDOMEvent* aMouseEvent)
    1902             : {
    1903           0 :   NS_ASSERTION(aMouseEvent, "aMouseEvent is null.");
    1904           0 :   nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aMouseEvent);
    1905           0 :   NS_ENSURE_TRUE(mouseEvent, NS_ERROR_FAILURE);
    1906             : 
    1907           0 :   UpdateInListState(aMouseEvent);
    1908             : 
    1909           0 :   if (IsInDropDownMode()) {
    1910           0 :     if (mComboboxFrame->IsDroppedDown()) {
    1911             :       int32_t selectedIndex;
    1912           0 :       if (NS_SUCCEEDED(GetIndexFromDOMEvent(aMouseEvent, selectedIndex))) {
    1913           0 :         PerformSelection(selectedIndex, false, false); // might destroy us
    1914             :       }
    1915             :     }
    1916             :   } else {// XXX - temporary until we get drag events
    1917           0 :     if (mButtonDown) {
    1918           0 :       return DragMove(aMouseEvent); // might destroy us
    1919             :     }
    1920             :   }
    1921           0 :   return NS_OK;
    1922             : }
    1923             : 
    1924             : nsresult
    1925           0 : nsListControlFrame::DragMove(nsIDOMEvent* aMouseEvent)
    1926             : {
    1927           0 :   NS_ASSERTION(aMouseEvent, "aMouseEvent is null.");
    1928             : 
    1929           0 :   UpdateInListState(aMouseEvent);
    1930             : 
    1931           0 :   if (!IsInDropDownMode()) {
    1932             :     int32_t selectedIndex;
    1933           0 :     if (NS_SUCCEEDED(GetIndexFromDOMEvent(aMouseEvent, selectedIndex))) {
    1934             :       // Don't waste cycles if we already dragged over this item
    1935           0 :       if (selectedIndex == mEndSelectionIndex) {
    1936           0 :         return NS_OK;
    1937             :       }
    1938           0 :       nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aMouseEvent);
    1939           0 :       NS_ASSERTION(mouseEvent, "aMouseEvent is not an nsIDOMMouseEvent!");
    1940             :       bool isControl;
    1941             : #ifdef XP_MACOSX
    1942             :       mouseEvent->GetMetaKey(&isControl);
    1943             : #else
    1944           0 :       mouseEvent->GetCtrlKey(&isControl);
    1945             : #endif
    1946           0 :       AutoWeakFrame weakFrame(this);
    1947             :       // Turn SHIFT on when you are dragging, unless control is on.
    1948           0 :       bool wasChanged = PerformSelection(selectedIndex,
    1949           0 :                                            !isControl, isControl);
    1950           0 :       if (!weakFrame.IsAlive()) {
    1951           0 :         return NS_OK;
    1952             :       }
    1953           0 :       mChangesSinceDragStart = mChangesSinceDragStart || wasChanged;
    1954             :     }
    1955             :   }
    1956           0 :   return NS_OK;
    1957             : }
    1958             : 
    1959             : //----------------------------------------------------------------------
    1960             : // Scroll helpers.
    1961             : //----------------------------------------------------------------------
    1962             : void
    1963           0 : nsListControlFrame::ScrollToIndex(int32_t aIndex)
    1964             : {
    1965           0 :   if (aIndex < 0) {
    1966             :     // XXX shouldn't we just do nothing if we're asked to scroll to
    1967             :     // kNothingSelected?
    1968           0 :     ScrollTo(nsPoint(0, 0), nsIScrollableFrame::INSTANT);
    1969             :   } else {
    1970             :     RefPtr<dom::HTMLOptionElement> option =
    1971           0 :       GetOption(AssertedCast<uint32_t>(aIndex));
    1972           0 :     if (option) {
    1973           0 :       ScrollToFrame(*option);
    1974             :     }
    1975             :   }
    1976           0 : }
    1977             : 
    1978             : void
    1979           0 : nsListControlFrame::ScrollToFrame(dom::HTMLOptionElement& aOptElement)
    1980             : {
    1981             :   // otherwise we find the content's frame and scroll to it
    1982           0 :   nsIFrame* childFrame = aOptElement.GetPrimaryFrame();
    1983           0 :   if (childFrame) {
    1984           0 :     PresContext()->PresShell()->
    1985           0 :       ScrollFrameRectIntoView(childFrame,
    1986           0 :                               nsRect(nsPoint(0, 0), childFrame->GetSize()),
    1987             :                               nsIPresShell::ScrollAxis(), nsIPresShell::ScrollAxis(),
    1988             :                               nsIPresShell::SCROLL_OVERFLOW_HIDDEN |
    1989           0 :                               nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY);
    1990             :   }
    1991           0 : }
    1992             : 
    1993             : //---------------------------------------------------------------------
    1994             : // Ok, the entire idea of this routine is to move to the next item that
    1995             : // is suppose to be selected. If the item is disabled then we search in
    1996             : // the same direction looking for the next item to select. If we run off
    1997             : // the end of the list then we start at the end of the list and search
    1998             : // backwards until we get back to the original item or an enabled option
    1999             : //
    2000             : // aStartIndex - the index to start searching from
    2001             : // aNewIndex - will get set to the new index if it finds one
    2002             : // aNumOptions - the total number of options in the list
    2003             : // aDoAdjustInc - the initial increment 1-n
    2004             : // aDoAdjustIncNext - the increment used to search for the next enabled option
    2005             : //
    2006             : // the aDoAdjustInc could be a "1" for a single item or
    2007             : // any number greater representing a page of items
    2008             : //
    2009             : void
    2010           0 : nsListControlFrame::AdjustIndexForDisabledOpt(int32_t aStartIndex,
    2011             :                                               int32_t &aNewIndex,
    2012             :                                               int32_t aNumOptions,
    2013             :                                               int32_t aDoAdjustInc,
    2014             :                                               int32_t aDoAdjustIncNext)
    2015             : {
    2016             :   // Cannot select anything if there is nothing to select
    2017           0 :   if (aNumOptions == 0) {
    2018           0 :     aNewIndex = kNothingSelected;
    2019           0 :     return;
    2020             :   }
    2021             : 
    2022             :   // means we reached the end of the list and now we are searching backwards
    2023           0 :   bool doingReverse = false;
    2024             :   // lowest index in the search range
    2025           0 :   int32_t bottom      = 0;
    2026             :   // highest index in the search range
    2027           0 :   int32_t top         = aNumOptions;
    2028             : 
    2029             :   // Start off keyboard options at selectedIndex if nothing else is defaulted to
    2030             :   //
    2031             :   // XXX Perhaps this should happen for mouse too, to start off shift click
    2032             :   // automatically in multiple ... to do this, we'd need to override
    2033             :   // OnOptionSelected and set mStartSelectedIndex if nothing is selected.  Not
    2034             :   // sure of the effects, though, so I'm not doing it just yet.
    2035           0 :   int32_t startIndex = aStartIndex;
    2036           0 :   if (startIndex < bottom) {
    2037           0 :     startIndex = GetSelectedIndex();
    2038             :   }
    2039           0 :   int32_t newIndex    = startIndex + aDoAdjustInc;
    2040             : 
    2041             :   // make sure we start off in the range
    2042           0 :   if (newIndex < bottom) {
    2043           0 :     newIndex = 0;
    2044           0 :   } else if (newIndex >= top) {
    2045           0 :     newIndex = aNumOptions-1;
    2046             :   }
    2047             : 
    2048             :   while (1) {
    2049             :     // if the newIndex isn't disabled, we are golden, bail out
    2050           0 :     bool isDisabled = true;
    2051           0 :     if (NS_SUCCEEDED(IsOptionDisabled(newIndex, isDisabled)) && !isDisabled) {
    2052           0 :       break;
    2053             :     }
    2054             : 
    2055             :     // it WAS disabled, so sart looking ahead for the next enabled option
    2056           0 :     newIndex += aDoAdjustIncNext;
    2057             : 
    2058             :     // well, if we reach end reverse the search
    2059           0 :     if (newIndex < bottom) {
    2060           0 :       if (doingReverse) {
    2061           0 :         return; // if we are in reverse mode and reach the end bail out
    2062             :       } else {
    2063             :         // reset the newIndex to the end of the list we hit
    2064             :         // reverse the incrementer
    2065             :         // set the other end of the list to our original starting index
    2066           0 :         newIndex         = bottom;
    2067           0 :         aDoAdjustIncNext = 1;
    2068           0 :         doingReverse     = true;
    2069           0 :         top              = startIndex;
    2070             :       }
    2071           0 :     } else  if (newIndex >= top) {
    2072           0 :       if (doingReverse) {
    2073           0 :         return;        // if we are in reverse mode and reach the end bail out
    2074             :       } else {
    2075             :         // reset the newIndex to the end of the list we hit
    2076             :         // reverse the incrementer
    2077             :         // set the other end of the list to our original starting index
    2078           0 :         newIndex = top - 1;
    2079           0 :         aDoAdjustIncNext = -1;
    2080           0 :         doingReverse     = true;
    2081           0 :         bottom           = startIndex;
    2082             :       }
    2083             :     }
    2084           0 :   }
    2085             : 
    2086             :   // Looks like we found one
    2087           0 :   aNewIndex     = newIndex;
    2088             : }
    2089             : 
    2090             : nsAString&
    2091           0 : nsListControlFrame::GetIncrementalString()
    2092             : {
    2093           0 :   if (sIncrementalString == nullptr)
    2094           0 :     sIncrementalString = new nsString();
    2095             : 
    2096           0 :   return *sIncrementalString;
    2097             : }
    2098             : 
    2099             : void
    2100           0 : nsListControlFrame::Shutdown()
    2101             : {
    2102           0 :   delete sIncrementalString;
    2103           0 :   sIncrementalString = nullptr;
    2104           0 : }
    2105             : 
    2106             : void
    2107           0 : nsListControlFrame::DropDownToggleKey(nsIDOMEvent* aKeyEvent)
    2108             : {
    2109             :   // Cocoa widgets do native popups, so don't try to show
    2110             :   // dropdowns there.
    2111           0 :   if (IsInDropDownMode() && !nsComboboxControlFrame::ToolkitHasNativePopup()) {
    2112           0 :     aKeyEvent->PreventDefault();
    2113           0 :     if (!mComboboxFrame->IsDroppedDown()) {
    2114           0 :       if (!FireShowDropDownEvent(mContent, true, false)) {
    2115           0 :         mComboboxFrame->ShowDropDown(true);
    2116             :       }
    2117             :     } else {
    2118           0 :       AutoWeakFrame weakFrame(this);
    2119             :       // mEndSelectionIndex is the last item that got selected.
    2120           0 :       ComboboxFinish(mEndSelectionIndex);
    2121           0 :       if (weakFrame.IsAlive()) {
    2122           0 :         FireOnInputAndOnChange();
    2123             :       }
    2124             :     }
    2125             :   }
    2126           0 : }
    2127             : 
    2128             : nsresult
    2129           0 : nsListControlFrame::KeyDown(nsIDOMEvent* aKeyEvent)
    2130             : {
    2131           0 :   MOZ_ASSERT(aKeyEvent, "aKeyEvent is null.");
    2132             : 
    2133           0 :   EventStates eventStates = mContent->AsElement()->State();
    2134           0 :   if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
    2135           0 :     return NS_OK;
    2136             :   }
    2137             : 
    2138           0 :   AutoIncrementalSearchResetter incrementalSearchResetter;
    2139             : 
    2140             :   // Don't check defaultPrevented value because other browsers don't prevent
    2141             :   // the key navigation of list control even if preventDefault() is called.
    2142             :   // XXXmats 2015-04-16: the above is not true anymore, Chrome prevents all
    2143             :   // XXXmats keyboard events, even tabbing, when preventDefault() is called
    2144             :   // XXXmats in onkeydown. That seems sub-optimal though.
    2145             : 
    2146             :   const WidgetKeyboardEvent* keyEvent =
    2147           0 :     aKeyEvent->WidgetEventPtr()->AsKeyboardEvent();
    2148           0 :   MOZ_ASSERT(keyEvent,
    2149             :     "DOM event must have WidgetKeyboardEvent for its internal event");
    2150             : 
    2151             :   bool dropDownMenuOnUpDown;
    2152             :   bool dropDownMenuOnSpace;
    2153             : #ifdef XP_MACOSX
    2154             :   dropDownMenuOnUpDown = IsInDropDownMode() && !mComboboxFrame->IsDroppedDown();
    2155             :   dropDownMenuOnSpace = !keyEvent->IsAlt() && !keyEvent->IsControl() &&
    2156             :     !keyEvent->IsMeta();
    2157             : #else
    2158           0 :   dropDownMenuOnUpDown = keyEvent->IsAlt();
    2159           0 :   dropDownMenuOnSpace = IsInDropDownMode() && !mComboboxFrame->IsDroppedDown();
    2160             : #endif
    2161             :   bool withinIncrementalSearchTime =
    2162           0 :     keyEvent->mTime - gLastKeyTime <= INCREMENTAL_SEARCH_KEYPRESS_TIME;
    2163           0 :   if ((dropDownMenuOnUpDown &&
    2164           0 :        (keyEvent->mKeyCode == NS_VK_UP || keyEvent->mKeyCode == NS_VK_DOWN)) ||
    2165           0 :       (dropDownMenuOnSpace && keyEvent->mKeyCode == NS_VK_SPACE &&
    2166           0 :        !withinIncrementalSearchTime)) {
    2167           0 :     DropDownToggleKey(aKeyEvent);
    2168           0 :     if (keyEvent->DefaultPrevented()) {
    2169           0 :       return NS_OK;
    2170             :     }
    2171             :   }
    2172           0 :   if (keyEvent->IsAlt()) {
    2173           0 :     return NS_OK;
    2174             :   }
    2175             : 
    2176             :   // now make sure there are options or we are wasting our time
    2177           0 :   RefPtr<dom::HTMLOptionsCollection> options = GetOptions();
    2178           0 :   NS_ENSURE_TRUE(options, NS_ERROR_FAILURE);
    2179             : 
    2180           0 :   uint32_t numOptions = options->Length();
    2181             : 
    2182             :   // this is the new index to set
    2183           0 :   int32_t newIndex = kNothingSelected;
    2184             : 
    2185           0 :   bool isControlOrMeta = (keyEvent->IsControl() || keyEvent->IsMeta());
    2186             :   // Don't try to handle multiple-select pgUp/pgDown in single-select lists.
    2187           0 :   if (isControlOrMeta && !GetMultiple() &&
    2188           0 :       (keyEvent->mKeyCode == NS_VK_PAGE_UP ||
    2189           0 :        keyEvent->mKeyCode == NS_VK_PAGE_DOWN)) {
    2190           0 :     return NS_OK;
    2191             :   }
    2192           0 :   if (isControlOrMeta && (keyEvent->mKeyCode == NS_VK_UP ||
    2193           0 :                           keyEvent->mKeyCode == NS_VK_LEFT ||
    2194           0 :                           keyEvent->mKeyCode == NS_VK_DOWN ||
    2195           0 :                           keyEvent->mKeyCode == NS_VK_RIGHT ||
    2196           0 :                           keyEvent->mKeyCode == NS_VK_HOME ||
    2197           0 :                           keyEvent->mKeyCode == NS_VK_END)) {
    2198             :     // Don't go into multiple-select mode unless this list can handle it.
    2199           0 :     isControlOrMeta = mControlSelectMode = GetMultiple();
    2200           0 :   } else if (keyEvent->mKeyCode != NS_VK_SPACE) {
    2201           0 :     mControlSelectMode = false;
    2202             :   }
    2203             : 
    2204             :   // We should not change the selection if the popup is "opened
    2205             :   // in the parent process" (even when we're in single-process mode).
    2206           0 :   bool shouldSelectByKey = !mComboboxFrame ||
    2207           0 :                            !mComboboxFrame->IsOpenInParentProcess();
    2208             : 
    2209           0 :   switch (keyEvent->mKeyCode) {
    2210             :     case NS_VK_UP:
    2211             :     case NS_VK_LEFT:
    2212           0 :       if (shouldSelectByKey) {
    2213           0 :         AdjustIndexForDisabledOpt(mEndSelectionIndex, newIndex,
    2214             :                                   static_cast<int32_t>(numOptions),
    2215           0 :                                   -1, -1);
    2216             :       }
    2217           0 :       break;
    2218             :     case NS_VK_DOWN:
    2219             :     case NS_VK_RIGHT:
    2220           0 :       if (shouldSelectByKey) {
    2221           0 :         AdjustIndexForDisabledOpt(mEndSelectionIndex, newIndex,
    2222             :                                   static_cast<int32_t>(numOptions),
    2223           0 :                                   1, 1);
    2224             :       }
    2225           0 :       break;
    2226             :     case NS_VK_RETURN:
    2227           0 :       if (IsInDropDownMode()) {
    2228           0 :         if (mComboboxFrame->IsDroppedDown()) {
    2229             :           // If the select element is a dropdown style, Enter key should be
    2230             :           // consumed while the dropdown is open for security.
    2231           0 :           aKeyEvent->PreventDefault();
    2232             : 
    2233           0 :           AutoWeakFrame weakFrame(this);
    2234           0 :           ComboboxFinish(mEndSelectionIndex);
    2235           0 :           if (!weakFrame.IsAlive()) {
    2236           0 :             return NS_OK;
    2237             :           }
    2238             :         }
    2239           0 :         FireOnInputAndOnChange();
    2240           0 :         return NS_OK;
    2241             :       }
    2242             : 
    2243             :       // If this is single select listbox, Enter key doesn't cause anything.
    2244           0 :       if (!GetMultiple()) {
    2245           0 :         return NS_OK;
    2246             :       }
    2247             : 
    2248           0 :       newIndex = mEndSelectionIndex;
    2249           0 :       break;
    2250             :     case NS_VK_ESCAPE: {
    2251             :       // If the select element is a listbox style, Escape key causes nothing.
    2252           0 :       if (!IsInDropDownMode()) {
    2253           0 :         return NS_OK;
    2254             :       }
    2255             : 
    2256           0 :       AboutToRollup();
    2257             :       // If the select element is a dropdown style, Enter key should be
    2258             :       // consumed everytime since Escape key may be pressed accidentally after
    2259             :       // the dropdown is closed by Escepe key.
    2260           0 :       aKeyEvent->PreventDefault();
    2261           0 :       return NS_OK;
    2262             :     }
    2263             :     case NS_VK_PAGE_UP: {
    2264           0 :       if (shouldSelectByKey) {
    2265             :         int32_t itemsPerPage =
    2266           0 :           std::max(1, static_cast<int32_t>(mNumDisplayRows - 1));
    2267           0 :         AdjustIndexForDisabledOpt(mEndSelectionIndex, newIndex,
    2268             :                                   static_cast<int32_t>(numOptions),
    2269           0 :                                   -itemsPerPage, -1);
    2270             :       }
    2271           0 :       break;
    2272             :     }
    2273             :     case NS_VK_PAGE_DOWN: {
    2274           0 :       if (shouldSelectByKey) {
    2275             :         int32_t itemsPerPage =
    2276           0 :           std::max(1, static_cast<int32_t>(mNumDisplayRows - 1));
    2277           0 :         AdjustIndexForDisabledOpt(mEndSelectionIndex, newIndex,
    2278             :                                   static_cast<int32_t>(numOptions),
    2279           0 :                                   itemsPerPage, 1);
    2280             :       }
    2281           0 :       break;
    2282             :     }
    2283             :     case NS_VK_HOME:
    2284           0 :       if (shouldSelectByKey) {
    2285           0 :         AdjustIndexForDisabledOpt(0, newIndex,
    2286             :                                   static_cast<int32_t>(numOptions),
    2287           0 :                                   0, 1);
    2288             :       }
    2289           0 :       break;
    2290             :     case NS_VK_END:
    2291           0 :       if (shouldSelectByKey) {
    2292           0 :         AdjustIndexForDisabledOpt(static_cast<int32_t>(numOptions) - 1, newIndex,
    2293             :                                   static_cast<int32_t>(numOptions),
    2294           0 :                                   0, -1);
    2295             :       }
    2296           0 :       break;
    2297             : 
    2298             : #if defined(XP_WIN)
    2299             :     case NS_VK_F4:
    2300             :       if (!isControlOrMeta) {
    2301             :         DropDownToggleKey(aKeyEvent);
    2302             :       }
    2303             :       return NS_OK;
    2304             : #endif
    2305             : 
    2306             :     default: // printable key will be handled by keypress event.
    2307           0 :       incrementalSearchResetter.Cancel();
    2308           0 :       return NS_OK;
    2309             :   }
    2310             : 
    2311           0 :   aKeyEvent->PreventDefault();
    2312             : 
    2313             :   // Actually process the new index and let the selection code
    2314             :   // do the scrolling for us
    2315           0 :   PostHandleKeyEvent(newIndex, 0, keyEvent->IsShift(), isControlOrMeta);
    2316           0 :   return NS_OK;
    2317             : }
    2318             : 
    2319             : nsresult
    2320           0 : nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent)
    2321             : {
    2322           0 :   MOZ_ASSERT(aKeyEvent, "aKeyEvent is null.");
    2323             : 
    2324           0 :   EventStates eventStates = mContent->AsElement()->State();
    2325           0 :   if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
    2326           0 :     return NS_OK;
    2327             :   }
    2328             : 
    2329           0 :   AutoIncrementalSearchResetter incrementalSearchResetter;
    2330             : 
    2331             :   const WidgetKeyboardEvent* keyEvent =
    2332           0 :     aKeyEvent->WidgetEventPtr()->AsKeyboardEvent();
    2333           0 :   MOZ_ASSERT(keyEvent,
    2334             :     "DOM event must have WidgetKeyboardEvent for its internal event");
    2335             : 
    2336             :   // Select option with this as the first character
    2337             :   // XXX Not I18N compliant
    2338             : 
    2339             :   // Don't do incremental search if the key event has already consumed.
    2340           0 :   if (keyEvent->DefaultPrevented()) {
    2341           0 :     return NS_OK;
    2342             :   }
    2343             : 
    2344           0 :   if (keyEvent->IsAlt()) {
    2345           0 :     return NS_OK;
    2346             :   }
    2347             : 
    2348             :   // With some keyboard layout, space key causes non-ASCII space.
    2349             :   // So, the check in keydown event handler isn't enough, we need to check it
    2350             :   // again with keypress event.
    2351           0 :   if (keyEvent->mCharCode != ' ') {
    2352           0 :     mControlSelectMode = false;
    2353             :   }
    2354             : 
    2355           0 :   bool isControlOrMeta = (keyEvent->IsControl() || keyEvent->IsMeta());
    2356           0 :   if (isControlOrMeta && keyEvent->mCharCode != ' ') {
    2357           0 :     return NS_OK;
    2358             :   }
    2359             : 
    2360             :   // NOTE: If mKeyCode of keypress event is not 0, mCharCode is always 0.
    2361             :   //       Therefore, all non-printable keys are not handled after this block.
    2362           0 :   if (!keyEvent->mCharCode) {
    2363             :     // Backspace key will delete the last char in the string.  Otherwise,
    2364             :     // non-printable keypress should reset incremental search.
    2365           0 :     if (keyEvent->mKeyCode == NS_VK_BACK) {
    2366           0 :       incrementalSearchResetter.Cancel();
    2367           0 :       if (!GetIncrementalString().IsEmpty()) {
    2368           0 :         GetIncrementalString().Truncate(GetIncrementalString().Length() - 1);
    2369             :       }
    2370           0 :       aKeyEvent->PreventDefault();
    2371             :     } else {
    2372             :       // XXX When a select element has focus, even if the key causes nothing,
    2373             :       //     it might be better to call preventDefault() here because nobody
    2374             :       //     should expect one of other elements including chrome handles the
    2375             :       //     key event.
    2376             :     }
    2377           0 :     return NS_OK;
    2378             :   }
    2379             : 
    2380           0 :   incrementalSearchResetter.Cancel();
    2381             : 
    2382             :   // We ate the key if we got this far.
    2383           0 :   aKeyEvent->PreventDefault();
    2384             : 
    2385             :   // XXX Why don't we check/modify timestamp first?
    2386             : 
    2387             :   // Incremental Search: if time elapsed is below
    2388             :   // INCREMENTAL_SEARCH_KEYPRESS_TIME, append this keystroke to the search
    2389             :   // string we will use to find options and start searching at the current
    2390             :   // keystroke.  Otherwise, Truncate the string if it's been a long time
    2391             :   // since our last keypress.
    2392           0 :   if (keyEvent->mTime - gLastKeyTime > INCREMENTAL_SEARCH_KEYPRESS_TIME) {
    2393             :     // If this is ' ' and we are at the beginning of the string, treat it as
    2394             :     // "select this option" (bug 191543)
    2395           0 :     if (keyEvent->mCharCode == ' ') {
    2396             :       // Actually process the new index and let the selection code
    2397             :       // do the scrolling for us
    2398           0 :       PostHandleKeyEvent(mEndSelectionIndex, keyEvent->mCharCode,
    2399           0 :                          keyEvent->IsShift(), isControlOrMeta);
    2400             : 
    2401           0 :       return NS_OK;
    2402             :     }
    2403             : 
    2404           0 :     GetIncrementalString().Truncate();
    2405             :   }
    2406             : 
    2407           0 :   gLastKeyTime = keyEvent->mTime;
    2408             : 
    2409             :   // Append this keystroke to the search string.
    2410           0 :   char16_t uniChar = ToLowerCase(static_cast<char16_t>(keyEvent->mCharCode));
    2411           0 :   GetIncrementalString().Append(uniChar);
    2412             : 
    2413             :   // See bug 188199, if all letters in incremental string are same, just try to
    2414             :   // match the first one
    2415           0 :   nsAutoString incrementalString(GetIncrementalString());
    2416           0 :   uint32_t charIndex = 1, stringLength = incrementalString.Length();
    2417           0 :   while (charIndex < stringLength &&
    2418           0 :          incrementalString[charIndex] == incrementalString[charIndex - 1]) {
    2419           0 :     charIndex++;
    2420             :   }
    2421           0 :   if (charIndex == stringLength) {
    2422           0 :     incrementalString.Truncate(1);
    2423           0 :     stringLength = 1;
    2424             :   }
    2425             : 
    2426             :   // Determine where we're going to start reading the string
    2427             :   // If we have multiple characters to look for, we start looking *at* the
    2428             :   // current option.  If we have only one character to look for, we start
    2429             :   // looking *after* the current option.
    2430             :   // Exception: if there is no option selected to start at, we always start
    2431             :   // *at* 0.
    2432           0 :   int32_t startIndex = GetSelectedIndex();
    2433           0 :   if (startIndex == kNothingSelected) {
    2434           0 :     startIndex = 0;
    2435           0 :   } else if (stringLength == 1) {
    2436           0 :     startIndex++;
    2437             :   }
    2438             : 
    2439             :   // now make sure there are options or we are wasting our time
    2440           0 :   RefPtr<dom::HTMLOptionsCollection> options = GetOptions();
    2441           0 :   NS_ENSURE_TRUE(options, NS_ERROR_FAILURE);
    2442             : 
    2443           0 :   uint32_t numOptions = options->Length();
    2444             : 
    2445           0 :   AutoWeakFrame weakFrame(this);
    2446           0 :   for (uint32_t i = 0; i < numOptions; ++i) {
    2447           0 :     uint32_t index = (i + startIndex) % numOptions;
    2448             :     RefPtr<dom::HTMLOptionElement> optionElement =
    2449           0 :       options->ItemAsOption(index);
    2450           0 :     if (!optionElement || !optionElement->GetPrimaryFrame()) {
    2451           0 :       continue;
    2452             :     }
    2453             : 
    2454           0 :     nsAutoString text;
    2455           0 :     if (NS_FAILED(optionElement->GetText(text)) ||
    2456           0 :         !StringBeginsWith(
    2457             :            nsContentUtils::TrimWhitespace<
    2458           0 :              nsContentUtils::IsHTMLWhitespaceOrNBSP>(text, false),
    2459           0 :            incrementalString, nsCaseInsensitiveStringComparator())) {
    2460           0 :       continue;
    2461             :     }
    2462             : 
    2463           0 :     bool wasChanged = PerformSelection(index, keyEvent->IsShift(), isControlOrMeta);
    2464           0 :     if (!weakFrame.IsAlive()) {
    2465           0 :       return NS_OK;
    2466             :     }
    2467           0 :     if (!wasChanged) {
    2468           0 :       break;
    2469             :     }
    2470             : 
    2471             :     // If UpdateSelection() returns false, that means the frame is no longer
    2472             :     // alive. We should stop doing anything.
    2473           0 :     if (!UpdateSelection()) {
    2474           0 :       return NS_OK;
    2475             :     }
    2476           0 :     break;
    2477             :   }
    2478             : 
    2479           0 :   return NS_OK;
    2480             : }
    2481             : 
    2482             : void
    2483           0 : nsListControlFrame::PostHandleKeyEvent(int32_t aNewIndex,
    2484             :                                        uint32_t aCharCode,
    2485             :                                        bool aIsShift,
    2486             :                                        bool aIsControlOrMeta)
    2487             : {
    2488           0 :   if (aNewIndex == kNothingSelected) {
    2489           0 :     int32_t focusedIndex = mEndSelectionIndex == kNothingSelected ?
    2490           0 :       GetSelectedIndex() : mEndSelectionIndex;
    2491           0 :     if (focusedIndex != kNothingSelected) {
    2492           0 :       return;
    2493             :     }
    2494             :     // No options are selected.  In this case the focus ring is on the first
    2495             :     // non-disabled option (if any), so we should behave as if that's the option
    2496             :     // the user acted on.
    2497           0 :     if (!GetNonDisabledOptionFrom(0, &aNewIndex)) {
    2498           0 :       return;
    2499             :     }
    2500             :   }
    2501             : 
    2502             :   // If you hold control, but not shift, no key will actually do anything
    2503             :   // except space.
    2504           0 :   AutoWeakFrame weakFrame(this);
    2505           0 :   bool wasChanged = false;
    2506           0 :   if (aIsControlOrMeta && !aIsShift && aCharCode != ' ') {
    2507           0 :     mStartSelectionIndex = aNewIndex;
    2508           0 :     mEndSelectionIndex = aNewIndex;
    2509           0 :     InvalidateFocus();
    2510           0 :     ScrollToIndex(aNewIndex);
    2511           0 :     if (!weakFrame.IsAlive()) {
    2512           0 :       return;
    2513             :     }
    2514             : 
    2515             : #ifdef ACCESSIBILITY
    2516           0 :     FireMenuItemActiveEvent();
    2517             : #endif
    2518           0 :   } else if (mControlSelectMode && aCharCode == ' ') {
    2519           0 :     wasChanged = SingleSelection(aNewIndex, true);
    2520             :   } else {
    2521           0 :     wasChanged = PerformSelection(aNewIndex, aIsShift, aIsControlOrMeta);
    2522             :   }
    2523           0 :   if (wasChanged && weakFrame.IsAlive()) {
    2524             :     // dispatch event, update combobox, etc.
    2525           0 :     UpdateSelection();
    2526             :   }
    2527             : }
    2528             : 
    2529             : 
    2530             : /******************************************************************************
    2531             :  * nsListEventListener
    2532             :  *****************************************************************************/
    2533             : 
    2534           0 : NS_IMPL_ISUPPORTS(nsListEventListener, nsIDOMEventListener)
    2535             : 
    2536             : NS_IMETHODIMP
    2537           0 : nsListEventListener::HandleEvent(nsIDOMEvent* aEvent)
    2538             : {
    2539           0 :   if (!mFrame)
    2540           0 :     return NS_OK;
    2541             : 
    2542           0 :   nsAutoString eventType;
    2543           0 :   aEvent->GetType(eventType);
    2544           0 :   if (eventType.EqualsLiteral("keydown")) {
    2545           0 :     return mFrame->nsListControlFrame::KeyDown(aEvent);
    2546             :   }
    2547           0 :   if (eventType.EqualsLiteral("keypress")) {
    2548           0 :     return mFrame->nsListControlFrame::KeyPress(aEvent);
    2549             :   }
    2550           0 :   if (eventType.EqualsLiteral("mousedown")) {
    2551           0 :     bool defaultPrevented = false;
    2552           0 :     aEvent->GetDefaultPrevented(&defaultPrevented);
    2553           0 :     if (defaultPrevented) {
    2554           0 :       return NS_OK;
    2555             :     }
    2556           0 :     return mFrame->nsListControlFrame::MouseDown(aEvent);
    2557             :   }
    2558           0 :   if (eventType.EqualsLiteral("mouseup")) {
    2559             :     // Don't try to honor defaultPrevented here - it's not web compatible.
    2560             :     // (bug 1194733)
    2561           0 :     return mFrame->nsListControlFrame::MouseUp(aEvent);
    2562             :   }
    2563           0 :   if (eventType.EqualsLiteral("mousemove")) {
    2564             :     // I don't think we want to honor defaultPrevented on mousemove
    2565             :     // in general, and it would only prevent highlighting here.
    2566           0 :     return mFrame->nsListControlFrame::MouseMove(aEvent);
    2567             :   }
    2568             : 
    2569           0 :   NS_ABORT();
    2570           0 :   return NS_OK;
    2571             : }

Generated by: LCOV version 1.13