/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 "nsComboboxControlFrame.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/Event.h" #include "mozilla/dom/HTMLOptGroupElement.h" #include "mozilla/dom/HTMLOptionsCollection.h" #include "mozilla/dom/HTMLSelectElement.h" #include "mozilla/dom/MouseEvent.h" #include "mozilla/dom/MouseEventBinding.h" #include "mozilla/EventStateManager.h" #include "mozilla/EventStates.h" #include "mozilla/LookAndFeel.h" #include "mozilla/MouseEvents.h" #include "mozilla/Preferences.h" #include "mozilla/PresShell.h" #include "mozilla/StaticPrefs_browser.h" #include "mozilla/StaticPrefs_ui.h" #include "mozilla/TextEvents.h" #include using namespace mozilla; using namespace mozilla::dom; // Constants const uint32_t kMaxDropDownRows = 20; // matches the setting for 4.x browsers const int32_t kNothingSelected = -1; // Static members nsListControlFrame* nsListControlFrame::mFocused = nullptr; nsString* nsListControlFrame::sIncrementalString = nullptr; 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 // nsIDOMEventListener MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD HandleEvent(Event* aEvent) override; private: ~nsListEventListener() = default; nsListControlFrame* mFrame; }; //--------------------------------------------------------- nsContainerFrame* NS_NewListControlFrame(PresShell* aPresShell, ComputedStyle* aStyle) { nsListControlFrame* it = new (aPresShell) nsListControlFrame(aStyle, aPresShell->GetPresContext()); it->AddStateBits(NS_FRAME_INDEPENDENT_SELECTION); return it; } NS_IMPL_FRAMEARENA_HELPERS(nsListControlFrame) //--------------------------------------------------------- nsListControlFrame::nsListControlFrame(ComputedStyle* aStyle, nsPresContext* aPresContext) : nsHTMLScrollFrame(aStyle, aPresContext, 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() && StaticPrefs::browser_tabs_remote_desktopbehavior()) || Preferences::GetBool("dom.select_popup_in_parent.enabled", false); } // for Bug 47302 (remove this comment later) void nsListControlFrame::DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData) { // get the receiver interface from the browser button's content node NS_ENSURE_TRUE_VOID(mContent); // Clear the frame pointer on our event listener, just in case the // event listener can outlive the frame. mEventListener->SetFrame(nullptr); mContent->RemoveSystemEventListener(u"keydown"_ns, mEventListener, false); mContent->RemoveSystemEventListener(u"keypress"_ns, mEventListener, false); mContent->RemoveSystemEventListener(u"mousedown"_ns, mEventListener, false); mContent->RemoveSystemEventListener(u"mouseup"_ns, mEventListener, false); mContent->RemoveSystemEventListener(u"mousemove"_ns, mEventListener, false); if (ShouldFireDropDownEvent()) { nsContentUtils::AddScriptRunner( new AsyncEventDispatcher(mContent, u"mozhidedropdown"_ns, CanBubble::eYes, ChromeOnlyDispatch::eYes)); } nsCheckboxRadioFrame::RegUnRegAccessKey(static_cast(this), false); nsHTMLScrollFrame::DestroyFrom(aDestructRoot, aPostDestroyData); } 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