LCOV - app.info - layout/forms/nsListControlFrame.cpp

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