michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nscore.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsUnicharUtils.h" michael@0: #include "nsListControlFrame.h" michael@0: #include "nsFormControlFrame.h" // for COMPARE macro michael@0: #include "nsGkAtoms.h" michael@0: #include "nsIDOMHTMLSelectElement.h" michael@0: #include "nsIDOMHTMLOptionElement.h" michael@0: #include "nsComboboxControlFrame.h" michael@0: #include "nsIDOMHTMLOptGroupElement.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsIDOMMouseEvent.h" michael@0: #include "nsIXULRuntime.h" michael@0: #include "nsFontMetrics.h" michael@0: #include "nsIScrollableFrame.h" michael@0: #include "nsCSSRendering.h" michael@0: #include "nsIDOMEventListener.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include "nsDisplayList.h" michael@0: #include "nsContentUtils.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/dom/HTMLOptionsCollection.h" michael@0: #include "mozilla/dom/HTMLSelectElement.h" michael@0: #include "mozilla/EventStateManager.h" michael@0: #include "mozilla/EventStates.h" michael@0: #include "mozilla/LookAndFeel.h" michael@0: #include "mozilla/MouseEvents.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/TextEvents.h" michael@0: #include michael@0: michael@0: using namespace mozilla; michael@0: michael@0: // Constants michael@0: const uint32_t kMaxDropDownRows = 20; // This matches the setting for 4.x browsers michael@0: const int32_t kNothingSelected = -1; michael@0: michael@0: // Static members michael@0: nsListControlFrame * nsListControlFrame::mFocused = nullptr; michael@0: nsString * nsListControlFrame::sIncrementalString = nullptr; michael@0: michael@0: // Using for incremental typing navigation michael@0: #define INCREMENTAL_SEARCH_KEYPRESS_TIME 1000 michael@0: // XXX, kyle.yuan@sun.com, there are 4 definitions for the same purpose: michael@0: // nsMenuPopupFrame.h, nsListControlFrame.cpp, listbox.xml, tree.xml michael@0: // need to find a good place to put them together. michael@0: // if someone changes one, please also change the other. michael@0: michael@0: DOMTimeStamp nsListControlFrame::gLastKeyTime = 0; michael@0: michael@0: /****************************************************************************** michael@0: * nsListEventListener michael@0: * This class is responsible for propagating events to the nsListControlFrame. michael@0: * Frames are not refcounted so they can't be used as event listeners. michael@0: *****************************************************************************/ michael@0: michael@0: class nsListEventListener MOZ_FINAL : public nsIDOMEventListener michael@0: { michael@0: public: michael@0: nsListEventListener(nsListControlFrame *aFrame) michael@0: : mFrame(aFrame) { } michael@0: michael@0: void SetFrame(nsListControlFrame *aFrame) { mFrame = aFrame; } michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIDOMEVENTLISTENER michael@0: michael@0: private: michael@0: nsListControlFrame *mFrame; michael@0: }; michael@0: michael@0: //--------------------------------------------------------- michael@0: nsIFrame* michael@0: NS_NewListControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: nsListControlFrame* it = michael@0: new (aPresShell) nsListControlFrame(aPresShell, aPresShell->GetDocument(), aContext); michael@0: michael@0: it->AddStateBits(NS_FRAME_INDEPENDENT_SELECTION); michael@0: michael@0: return it; michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsListControlFrame) michael@0: michael@0: //--------------------------------------------------------- michael@0: nsListControlFrame::nsListControlFrame( michael@0: nsIPresShell* aShell, nsIDocument* aDocument, nsStyleContext* aContext) michael@0: : nsHTMLScrollFrame(aShell, aContext, false), michael@0: mMightNeedSecondPass(false), michael@0: mHasPendingInterruptAtStartOfReflow(false), michael@0: mDropdownCanGrow(false), michael@0: mLastDropdownComputedHeight(NS_UNCONSTRAINEDSIZE) michael@0: { michael@0: mComboboxFrame = nullptr; michael@0: mChangesSinceDragStart = false; michael@0: mButtonDown = false; michael@0: michael@0: mIsAllContentHere = false; michael@0: mIsAllFramesHere = false; michael@0: mHasBeenInitialized = false; michael@0: mNeedToReset = true; michael@0: mPostChildrenLoadedReset = false; michael@0: michael@0: mControlSelectMode = false; michael@0: } michael@0: michael@0: //--------------------------------------------------------- michael@0: nsListControlFrame::~nsListControlFrame() michael@0: { michael@0: mComboboxFrame = nullptr; michael@0: } michael@0: michael@0: // for Bug 47302 (remove this comment later) michael@0: void michael@0: nsListControlFrame::DestroyFrom(nsIFrame* aDestructRoot) michael@0: { michael@0: // get the receiver interface from the browser button's content node michael@0: ENSURE_TRUE(mContent); michael@0: michael@0: // Clear the frame pointer on our event listener, just in case the michael@0: // event listener can outlive the frame. michael@0: michael@0: mEventListener->SetFrame(nullptr); michael@0: michael@0: mContent->RemoveSystemEventListener(NS_LITERAL_STRING("keydown"), michael@0: mEventListener, false); michael@0: mContent->RemoveSystemEventListener(NS_LITERAL_STRING("keypress"), michael@0: mEventListener, false); michael@0: mContent->RemoveSystemEventListener(NS_LITERAL_STRING("mousedown"), michael@0: mEventListener, false); michael@0: mContent->RemoveSystemEventListener(NS_LITERAL_STRING("mouseup"), michael@0: mEventListener, false); michael@0: mContent->RemoveSystemEventListener(NS_LITERAL_STRING("mousemove"), michael@0: mEventListener, false); michael@0: michael@0: nsFormControlFrame::RegUnRegAccessKey(static_cast(this), false); michael@0: nsHTMLScrollFrame::DestroyFrom(aDestructRoot); michael@0: } michael@0: michael@0: void michael@0: nsListControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: // We allow visibility:hidden . So if the mouse goes over an option just before michael@0: // he leaves the box and clicks, that's what the