LCOV - code coverage report
Current view: directory - layout/forms - nsListControlFrame.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1063 4 0.4 %
Date: 2012-06-02 Functions: 98 1 1.0 %

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

Generated by: LCOV version 1.7