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