mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 22:32:46 +00:00
Bug 1362907 - Make select use nsHTMLButtonControlFrame for layout. r=jfkthame,dholbert
This simplifies our combobox code a bit more: * Reflow() is only needed to compute the label isize. * Frame construction uses a setup more similar to <input type=file> to get the right frame tree, removing a bunch of special code. * Lots of special code removed all over the place. Differential Revision: https://phabricator.services.mozilla.com/D203010
This commit is contained in:
parent
506151ac79
commit
fb89acd3b5
@ -70,10 +70,6 @@ label {
|
||||
user-select: all !important;
|
||||
}
|
||||
|
||||
::-moz-display-comboboxcontrol-frame {
|
||||
user-select: text !important;
|
||||
}
|
||||
|
||||
option {
|
||||
user-select: text !important;
|
||||
}
|
||||
|
@ -170,6 +170,7 @@ nsIFrame* NS_NewSVGFEImageFrame(PresShell* aPresShell, ComputedStyle* aStyle);
|
||||
nsIFrame* NS_NewSVGFEUnstyledLeafFrame(PresShell* aPresShell,
|
||||
ComputedStyle* aStyle);
|
||||
nsIFrame* NS_NewFileControlLabelFrame(PresShell*, ComputedStyle*);
|
||||
nsIFrame* NS_NewComboboxLabelFrame(PresShell*, ComputedStyle*);
|
||||
nsIFrame* NS_NewMiddleCroppingLabelFrame(PresShell*, ComputedStyle*);
|
||||
|
||||
#include "mozilla/dom/NodeInfo.h"
|
||||
@ -3012,91 +3013,33 @@ static inline void ClearLazyBits(nsIContent* aStartContent,
|
||||
}
|
||||
}
|
||||
|
||||
nsIFrame* nsCSSFrameConstructor::ConstructSelectFrame(
|
||||
/* static */
|
||||
const nsCSSFrameConstructor::FrameConstructionData*
|
||||
nsCSSFrameConstructor::FindSelectData(const Element& aElement,
|
||||
ComputedStyle& aStyle) {
|
||||
// Construct a frame-based listbox or combobox
|
||||
const auto* sel = dom::HTMLSelectElement::FromNode(aElement);
|
||||
MOZ_ASSERT(sel);
|
||||
if (sel->IsCombobox()) {
|
||||
static constexpr FrameConstructionData sComboboxData{
|
||||
ToCreationFunc(NS_NewComboboxControlFrame), 0,
|
||||
PseudoStyleType::buttonContent};
|
||||
return &sComboboxData;
|
||||
}
|
||||
// FIXME: Can we simplify this to avoid needing ConstructListboxSelectFrame,
|
||||
// and reuse ConstructScrollableBlock or so?
|
||||
static constexpr FrameConstructionData sListBoxData{
|
||||
&nsCSSFrameConstructor::ConstructListBoxSelectFrame};
|
||||
return &sListBoxData;
|
||||
}
|
||||
|
||||
nsIFrame* nsCSSFrameConstructor::ConstructListBoxSelectFrame(
|
||||
nsFrameConstructorState& aState, FrameConstructionItem& aItem,
|
||||
nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
|
||||
nsFrameList& aFrameList) {
|
||||
nsIContent* const content = aItem.mContent;
|
||||
ComputedStyle* const computedStyle = aItem.mComputedStyle;
|
||||
|
||||
// Construct a frame-based listbox or combobox
|
||||
dom::HTMLSelectElement* sel = dom::HTMLSelectElement::FromNode(content);
|
||||
MOZ_ASSERT(sel);
|
||||
if (sel->IsCombobox()) {
|
||||
// Construct a frame-based combo box.
|
||||
// The frame-based combo box is built out of three parts. A display area, a
|
||||
// button and a dropdown list. The display area and button are created
|
||||
// through anonymous content. The drop-down list's frame is created
|
||||
// explicitly. The combobox frame shares its content with the drop-down
|
||||
// list.
|
||||
nsComboboxControlFrame* comboboxFrame =
|
||||
NS_NewComboboxControlFrame(mPresShell, computedStyle);
|
||||
|
||||
// Save the history state so we don't restore during construction
|
||||
// since the complete tree is required before we restore.
|
||||
nsILayoutHistoryState* historyState = aState.mFrameState;
|
||||
aState.mFrameState = nullptr;
|
||||
// Initialize the combobox frame
|
||||
InitAndRestoreFrame(aState, content,
|
||||
aState.GetGeometricParent(*aStyleDisplay, aParentFrame),
|
||||
comboboxFrame);
|
||||
|
||||
comboboxFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
|
||||
|
||||
aState.AddChild(comboboxFrame, aFrameList, content, aParentFrame);
|
||||
|
||||
// Resolve pseudo element style for the dropdown list
|
||||
RefPtr<ComputedStyle> listStyle =
|
||||
mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
|
||||
PseudoStyleType::dropDownList, computedStyle);
|
||||
|
||||
// child frames of combobox frame
|
||||
nsFrameList childList;
|
||||
|
||||
// Create display and button frames from the combobox's anonymous content.
|
||||
// The anonymous content is appended to existing anonymous content for this
|
||||
// element (the scrollbars).
|
||||
//
|
||||
// nsComboboxControlFrame needs special frame creation behavior for its
|
||||
// first piece of anonymous content, which means that we can't take the
|
||||
// normal ProcessChildren path.
|
||||
AutoTArray<nsIAnonymousContentCreator::ContentInfo, 2> newAnonymousItems;
|
||||
DebugOnly<nsresult> rv =
|
||||
GetAnonymousContent(content, comboboxFrame, newAnonymousItems);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
MOZ_ASSERT(!newAnonymousItems.IsEmpty());
|
||||
|
||||
// Manually create a frame for the special NAC.
|
||||
MOZ_ASSERT(newAnonymousItems[0].mContent ==
|
||||
comboboxFrame->GetDisplayNode());
|
||||
newAnonymousItems.RemoveElementAt(0);
|
||||
nsIFrame* customFrame = comboboxFrame->CreateFrameForDisplayNode();
|
||||
MOZ_ASSERT(customFrame);
|
||||
childList.AppendFrame(nullptr, customFrame);
|
||||
|
||||
nsFrameConstructorSaveState floatSaveState;
|
||||
aState.MaybePushFloatContainingBlock(comboboxFrame, floatSaveState);
|
||||
|
||||
// The other piece of NAC can take the normal path.
|
||||
AutoFrameConstructionItemList fcItems(this);
|
||||
AutoFrameConstructionPageName pageNameTracker(aState, comboboxFrame);
|
||||
AddFCItemsForAnonymousContent(aState, comboboxFrame, newAnonymousItems,
|
||||
fcItems, pageNameTracker);
|
||||
ConstructFramesFromItemList(aState, fcItems, comboboxFrame,
|
||||
/* aParentIsWrapperAnonBox = */ false,
|
||||
childList);
|
||||
|
||||
comboboxFrame->SetInitialChildList(FrameChildListID::Principal,
|
||||
std::move(childList));
|
||||
|
||||
aState.mFrameState = historyState;
|
||||
if (aState.mFrameState) {
|
||||
// Restore frame state for the entire subtree of |comboboxFrame|.
|
||||
RestoreFrameState(comboboxFrame, aState.mFrameState);
|
||||
}
|
||||
return comboboxFrame;
|
||||
}
|
||||
|
||||
// Listbox, not combobox
|
||||
nsContainerFrame* listFrame =
|
||||
NS_NewListControlFrame(mPresShell, computedStyle);
|
||||
@ -3175,7 +3118,7 @@ nsIFrame* nsCSSFrameConstructor::ConstructFieldSetFrame(
|
||||
|
||||
const nsStyleDisplay* fieldsetContentDisplay =
|
||||
fieldsetContentStyle->StyleDisplay();
|
||||
bool isScrollable = fieldsetContentDisplay->IsScrollableOverflow();
|
||||
const bool isScrollable = fieldsetContentDisplay->IsScrollableOverflow();
|
||||
nsContainerFrame* scrollFrame = nullptr;
|
||||
if (isScrollable) {
|
||||
fieldsetContentStyle = BeginBuildingScrollFrame(
|
||||
@ -3499,11 +3442,18 @@ nsCSSFrameConstructor::FindHTMLData(const Element& aElement,
|
||||
"Unexpected parent for fieldset content anon box");
|
||||
|
||||
if (aElement.IsInNativeAnonymousSubtree() &&
|
||||
aElement.NodeInfo()->NameAtom() == nsGkAtoms::label &&
|
||||
static_cast<nsFileControlFrame*>(do_QueryFrame(aParentFrame))) {
|
||||
static constexpr FrameConstructionData sFileLabelData(
|
||||
NS_NewFileControlLabelFrame);
|
||||
return &sFileLabelData;
|
||||
aElement.NodeInfo()->NameAtom() == nsGkAtoms::label && aParentFrame) {
|
||||
if (static_cast<nsFileControlFrame*>(do_QueryFrame(aParentFrame))) {
|
||||
static constexpr FrameConstructionData sFileLabelData(
|
||||
NS_NewFileControlLabelFrame);
|
||||
return &sFileLabelData;
|
||||
}
|
||||
if (aParentFrame->GetParent() &&
|
||||
aParentFrame->GetParent()->IsComboboxControlFrame()) {
|
||||
static constexpr FrameConstructionData sComboboxLabelData(
|
||||
NS_NewComboboxLabelFrame);
|
||||
return &sComboboxLabelData;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr FrameConstructionDataByTag sHTMLData[] = {
|
||||
@ -3515,7 +3465,7 @@ nsCSSFrameConstructor::FindHTMLData(const Element& aElement,
|
||||
SIMPLE_TAG_CREATE(wbr, NS_NewWBRFrame),
|
||||
SIMPLE_TAG_CHAIN(input, nsCSSFrameConstructor::FindInputData),
|
||||
SIMPLE_TAG_CREATE(textarea, NS_NewTextControlFrame),
|
||||
COMPLEX_TAG_CREATE(select, &nsCSSFrameConstructor::ConstructSelectFrame),
|
||||
SIMPLE_TAG_CHAIN(select, nsCSSFrameConstructor::FindSelectData),
|
||||
SIMPLE_TAG_CHAIN(object, nsCSSFrameConstructor::FindObjectData),
|
||||
SIMPLE_TAG_CHAIN(embed, nsCSSFrameConstructor::FindObjectData),
|
||||
COMPLEX_TAG_CREATE(fieldset,
|
||||
@ -5165,10 +5115,14 @@ static bool ShouldSuppressFrameInSelect(const nsIContent* aParent,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allow native anonymous content no matter what.
|
||||
if (aChild.IsRootOfNativeAnonymousSubtree()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Options with labels have their label text added in ::before by forms.css.
|
||||
// Suppress frames for their child text.
|
||||
if (aParent->IsHTMLElement(nsGkAtoms::option) &&
|
||||
!aChild.IsRootOfNativeAnonymousSubtree()) {
|
||||
if (aParent->IsHTMLElement(nsGkAtoms::option)) {
|
||||
return aParent->AsElement()->HasNonEmptyAttr(nsGkAtoms::label);
|
||||
}
|
||||
|
||||
@ -5191,11 +5145,7 @@ static bool ShouldSuppressFrameInSelect(const nsIContent* aParent,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allow native anonymous content no matter what.
|
||||
if (aChild.IsRootOfNativeAnonymousSubtree()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Anything else is not ok.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1388,14 +1388,6 @@ class nsCSSFrameConstructor final : public nsFrameManager {
|
||||
nsFrameState aTypeBit);
|
||||
|
||||
private:
|
||||
// ConstructSelectFrame puts the new frame in aFrameList and
|
||||
// handles the kids of the select.
|
||||
nsIFrame* ConstructSelectFrame(nsFrameConstructorState& aState,
|
||||
FrameConstructionItem& aItem,
|
||||
nsContainerFrame* aParentFrame,
|
||||
const nsStyleDisplay* aStyleDisplay,
|
||||
nsFrameList& aFrameList);
|
||||
|
||||
// ConstructFieldSetFrame puts the new frame in aFrameList and
|
||||
// handles the kids of the fieldset
|
||||
nsIFrame* ConstructFieldSetFrame(nsFrameConstructorState& aState,
|
||||
@ -1404,6 +1396,12 @@ class nsCSSFrameConstructor final : public nsFrameManager {
|
||||
const nsStyleDisplay* aStyleDisplay,
|
||||
nsFrameList& aFrameList);
|
||||
|
||||
nsIFrame* ConstructListBoxSelectFrame(nsFrameConstructorState& aState,
|
||||
FrameConstructionItem& aItem,
|
||||
nsContainerFrame* aParentFrame,
|
||||
const nsStyleDisplay* aStyleDisplay,
|
||||
nsFrameList& aFrameList);
|
||||
|
||||
// Creates a block frame wrapping an anonymous ruby frame.
|
||||
nsIFrame* ConstructBlockRubyFrame(nsFrameConstructorState& aState,
|
||||
FrameConstructionItem& aItem,
|
||||
@ -1450,6 +1448,8 @@ class nsCSSFrameConstructor final : public nsFrameManager {
|
||||
nsIFrame* aParentFrame,
|
||||
ComputedStyle&);
|
||||
// HTML data-finding helper functions
|
||||
static const FrameConstructionData* FindSelectData(const Element&,
|
||||
ComputedStyle&);
|
||||
static const FrameConstructionData* FindImgData(const Element&,
|
||||
ComputedStyle&);
|
||||
static const FrameConstructionData* FindGeneratedImageData(const Element&,
|
||||
|
@ -7,9 +7,6 @@
|
||||
#include "nsCSSRendering.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsPresContextInlines.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsCSSPseudoElements.h"
|
||||
#include "nsNameSpaceManager.h"
|
||||
#include "mozilla/ServoStyleSet.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsDisplayList.h"
|
||||
@ -46,18 +43,6 @@ void nsButtonFrameRenderer::SetFrame(nsIFrame* aFrame,
|
||||
|
||||
nsIFrame* nsButtonFrameRenderer::GetFrame() { return mFrame; }
|
||||
|
||||
void nsButtonFrameRenderer::SetDisabled(bool aDisabled, bool aNotify) {
|
||||
dom::Element* element = mFrame->GetContent()->AsElement();
|
||||
if (aDisabled)
|
||||
element->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, u""_ns, aNotify);
|
||||
else
|
||||
element->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, aNotify);
|
||||
}
|
||||
|
||||
bool nsButtonFrameRenderer::isDisabled() {
|
||||
return mFrame->GetContent()->AsElement()->IsDisabled();
|
||||
}
|
||||
|
||||
nsresult nsButtonFrameRenderer::DisplayButton(nsDisplayListBuilder* aBuilder,
|
||||
nsDisplayList* aBackground,
|
||||
nsDisplayList* aForeground) {
|
||||
|
@ -28,8 +28,8 @@ class nsButtonFrameRenderer {
|
||||
using nsDisplayList = mozilla::nsDisplayList;
|
||||
using nsDisplayListBuilder = mozilla::nsDisplayListBuilder;
|
||||
|
||||
typedef mozilla::image::ImgDrawResult ImgDrawResult;
|
||||
typedef mozilla::ComputedStyle ComputedStyle;
|
||||
using ImgDrawResult = mozilla::image::ImgDrawResult;
|
||||
using ComputedStyle = mozilla::ComputedStyle;
|
||||
|
||||
public:
|
||||
nsButtonFrameRenderer();
|
||||
@ -60,11 +60,6 @@ class nsButtonFrameRenderer {
|
||||
|
||||
void SetFrame(nsIFrame* aFrame, nsPresContext* aPresContext);
|
||||
|
||||
void SetDisabled(bool aDisabled, bool notify);
|
||||
|
||||
bool isActive();
|
||||
bool isDisabled();
|
||||
|
||||
void GetButtonInnerFocusRect(const nsRect& aRect, nsRect& aResult);
|
||||
|
||||
ComputedStyle* GetComputedStyle(int32_t aIndex) const;
|
||||
|
@ -8,25 +8,17 @@
|
||||
|
||||
#include "gfxContext.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/PathHelpers.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsDeviceContext.h"
|
||||
#include "nsFocusManager.h"
|
||||
#include "nsCheckboxRadioFrame.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsCSSAnonBoxes.h"
|
||||
#include "nsHTMLParts.h"
|
||||
#include "nsIFormControl.h"
|
||||
#include "nsILayoutHistoryState.h"
|
||||
#include "nsNameSpaceManager.h"
|
||||
#include "nsListControlFrame.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "mozilla/PresState.h"
|
||||
#include "nsView.h"
|
||||
#include "nsViewManager.h"
|
||||
#include "nsIContentInlines.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsISelectControlFrame.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "mozilla/dom/Event.h"
|
||||
@ -48,12 +40,8 @@
|
||||
#include "nsTextNode.h"
|
||||
#include "mozilla/AsyncEventDispatcher.h"
|
||||
#include "mozilla/LookAndFeel.h"
|
||||
#include "mozilla/MouseEvents.h"
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/PresShellInlines.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "mozilla/widget/nsAutoRollup.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::gfx;
|
||||
@ -85,124 +73,17 @@ nsComboboxControlFrame* NS_NewComboboxControlFrame(PresShell* aPresShell,
|
||||
|
||||
NS_IMPL_FRAMEARENA_HELPERS(nsComboboxControlFrame)
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// Reflow Debugging Macros
|
||||
// These let us "see" how many reflow counts are happening
|
||||
//-----------------------------------------------------------
|
||||
#ifdef DO_REFLOW_COUNTER
|
||||
|
||||
# define MAX_REFLOW_CNT 1024
|
||||
static int32_t gTotalReqs = 0;
|
||||
;
|
||||
static int32_t gTotalReflows = 0;
|
||||
;
|
||||
static int32_t gReflowControlCntRQ[MAX_REFLOW_CNT];
|
||||
static int32_t gReflowControlCnt[MAX_REFLOW_CNT];
|
||||
static int32_t gReflowInx = -1;
|
||||
|
||||
# define REFLOW_COUNTER() \
|
||||
if (mReflowId > -1) gReflowControlCnt[mReflowId]++;
|
||||
|
||||
# define REFLOW_COUNTER_REQUEST() \
|
||||
if (mReflowId > -1) gReflowControlCntRQ[mReflowId]++;
|
||||
|
||||
# define REFLOW_COUNTER_DUMP(__desc) \
|
||||
if (mReflowId > -1) { \
|
||||
gTotalReqs += gReflowControlCntRQ[mReflowId]; \
|
||||
gTotalReflows += gReflowControlCnt[mReflowId]; \
|
||||
printf("** Id:%5d %s RF: %d RQ: %d %d/%d %5.2f\n", mReflowId, \
|
||||
(__desc), gReflowControlCnt[mReflowId], \
|
||||
gReflowControlCntRQ[mReflowId], gTotalReflows, gTotalReqs, \
|
||||
float(gTotalReflows) / float(gTotalReqs) * 100.0f); \
|
||||
}
|
||||
|
||||
# define REFLOW_COUNTER_INIT() \
|
||||
if (gReflowInx < MAX_REFLOW_CNT) { \
|
||||
gReflowInx++; \
|
||||
mReflowId = gReflowInx; \
|
||||
gReflowControlCnt[mReflowId] = 0; \
|
||||
gReflowControlCntRQ[mReflowId] = 0; \
|
||||
} else { \
|
||||
mReflowId = -1; \
|
||||
}
|
||||
|
||||
// reflow messages
|
||||
# define REFLOW_DEBUG_MSG(_msg1) printf((_msg1))
|
||||
# define REFLOW_DEBUG_MSG2(_msg1, _msg2) printf((_msg1), (_msg2))
|
||||
# define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3) \
|
||||
printf((_msg1), (_msg2), (_msg3))
|
||||
# define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4) \
|
||||
printf((_msg1), (_msg2), (_msg3), (_msg4))
|
||||
|
||||
#else //-------------
|
||||
|
||||
# define REFLOW_COUNTER_REQUEST()
|
||||
# define REFLOW_COUNTER()
|
||||
# define REFLOW_COUNTER_DUMP(__desc)
|
||||
# define REFLOW_COUNTER_INIT()
|
||||
|
||||
# define REFLOW_DEBUG_MSG(_msg)
|
||||
# define REFLOW_DEBUG_MSG2(_msg1, _msg2)
|
||||
# define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3)
|
||||
# define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4)
|
||||
|
||||
#endif
|
||||
|
||||
//------------------------------------------
|
||||
// This is for being VERY noisy
|
||||
//------------------------------------------
|
||||
#ifdef DO_VERY_NOISY
|
||||
# define REFLOW_NOISY_MSG(_msg1) printf((_msg1))
|
||||
# define REFLOW_NOISY_MSG2(_msg1, _msg2) printf((_msg1), (_msg2))
|
||||
# define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3) \
|
||||
printf((_msg1), (_msg2), (_msg3))
|
||||
# define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4) \
|
||||
printf((_msg1), (_msg2), (_msg3), (_msg4))
|
||||
#else
|
||||
# define REFLOW_NOISY_MSG(_msg)
|
||||
# define REFLOW_NOISY_MSG2(_msg1, _msg2)
|
||||
# define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3)
|
||||
# define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4)
|
||||
#endif
|
||||
|
||||
//------------------------------------------
|
||||
// Displays value in pixels or twips
|
||||
//------------------------------------------
|
||||
#ifdef DO_PIXELS
|
||||
# define PX(__v) __v / 15
|
||||
#else
|
||||
# define PX(__v) __v
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------
|
||||
//-- Done with macros
|
||||
//------------------------------------------------------
|
||||
|
||||
nsComboboxControlFrame::nsComboboxControlFrame(ComputedStyle* aStyle,
|
||||
nsPresContext* aPresContext)
|
||||
: nsBlockFrame(aStyle, aPresContext, kClassID),
|
||||
mDisplayFrame(nullptr),
|
||||
mButtonFrame(nullptr),
|
||||
mDisplayISize(0),
|
||||
mMaxDisplayISize(0),
|
||||
mRecentSelectedIndex(NS_SKIP_NOTIFY_INDEX),
|
||||
mDisplayedIndex(-1),
|
||||
mInRedisplayText(false),
|
||||
mIsOpenInParentProcess(false){REFLOW_COUNTER_INIT()}
|
||||
: nsHTMLButtonControlFrame(aStyle, aPresContext, kClassID) {}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
nsComboboxControlFrame::~nsComboboxControlFrame() {
|
||||
REFLOW_COUNTER_DUMP("nsCCF");
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
nsComboboxControlFrame::~nsComboboxControlFrame() = default;
|
||||
|
||||
NS_QUERYFRAME_HEAD(nsComboboxControlFrame)
|
||||
NS_QUERYFRAME_ENTRY(nsComboboxControlFrame)
|
||||
NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
|
||||
NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
|
||||
NS_QUERYFRAME_ENTRY(nsISelectControlFrame)
|
||||
NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
|
||||
NS_QUERYFRAME_TAIL_INHERITING(nsHTMLButtonControlFrame)
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
a11y::AccType nsComboboxControlFrame::AccessibleType() {
|
||||
@ -210,66 +91,6 @@ a11y::AccType nsComboboxControlFrame::AccessibleType() {
|
||||
}
|
||||
#endif
|
||||
|
||||
void nsComboboxControlFrame::SetFocus(bool aOn, bool aRepaint) {
|
||||
// This is needed on a temporary basis. It causes the focus
|
||||
// rect to be drawn. This is much faster than ReResolvingStyle
|
||||
// Bug 32920
|
||||
InvalidateFrame();
|
||||
}
|
||||
|
||||
nsPoint nsComboboxControlFrame::GetCSSTransformTranslation() {
|
||||
nsIFrame* frame = this;
|
||||
bool is3DTransform = false;
|
||||
Matrix transform;
|
||||
while (frame) {
|
||||
nsIFrame* parent;
|
||||
Matrix4x4Flagged ctm = frame->GetTransformMatrix(
|
||||
ViewportType::Layout, RelativeTo{nullptr}, &parent);
|
||||
Matrix matrix;
|
||||
if (ctm.Is2D(&matrix)) {
|
||||
transform = transform * matrix;
|
||||
} else {
|
||||
is3DTransform = true;
|
||||
break;
|
||||
}
|
||||
frame = parent;
|
||||
}
|
||||
nsPoint translation;
|
||||
if (!is3DTransform && !transform.HasNonTranslation()) {
|
||||
nsPresContext* pc = PresContext();
|
||||
// To get the translation introduced only by transforms we subtract the
|
||||
// regular non-transform translation.
|
||||
nsRootPresContext* rootPC = pc->GetRootPresContext();
|
||||
if (rootPC) {
|
||||
int32_t apd = pc->AppUnitsPerDevPixel();
|
||||
translation.x = NSFloatPixelsToAppUnits(transform._31, apd);
|
||||
translation.y = NSFloatPixelsToAppUnits(transform._32, apd);
|
||||
translation -= GetOffsetToCrossDoc(rootPC->PresShell()->GetRootFrame());
|
||||
}
|
||||
}
|
||||
return translation;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//
|
||||
//----------------------------------------------------------
|
||||
#ifdef DO_REFLOW_DEBUG
|
||||
static int myCounter = 0;
|
||||
|
||||
static void printSize(char* aDesc, nscoord aSize) {
|
||||
printf(" %s: ", aDesc);
|
||||
if (aSize == NS_UNCONSTRAINEDSIZE) {
|
||||
printf("UC");
|
||||
} else {
|
||||
printf("%d", PX(aSize));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
//-- Main Reflow for the Combobox
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
bool nsComboboxControlFrame::HasDropDownButton() const {
|
||||
const nsStyleDisplay* disp = StyleDisplay();
|
||||
// FIXME(emilio): Blink also shows this for menulist-button and such... Seems
|
||||
@ -357,7 +178,7 @@ nscoord nsComboboxControlFrame::GetIntrinsicISize(gfxContext* aRenderingContext,
|
||||
return *containISize;
|
||||
}
|
||||
|
||||
nscoord displayISize = mDisplayFrame->IntrinsicISizeOffsets().padding;
|
||||
nscoord displayISize = 0;
|
||||
if (!containISize && !StyleContent()->mContent.IsNone()) {
|
||||
displayISize += GetLongestOptionISize(aRenderingContext);
|
||||
}
|
||||
@ -408,12 +229,6 @@ void nsComboboxControlFrame::Reflow(nsPresContext* aPresContext,
|
||||
// 3) Default block size of button is block size of display area
|
||||
// 4) Inline size of display area is whatever is left over from our
|
||||
// inline size after allocating inline size for the button.
|
||||
|
||||
if (!mDisplayFrame) {
|
||||
NS_ERROR("Why did the frame constructor allow this to happen? Fix it!!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the displayed text is the same as the selected option,
|
||||
// bug 297389.
|
||||
mDisplayedIndex = Select().SelectedIndex();
|
||||
@ -427,53 +242,29 @@ void nsComboboxControlFrame::Reflow(nsPresContext* aPresContext,
|
||||
// Check if the theme specifies a minimum size for the dropdown button
|
||||
// first.
|
||||
const nscoord buttonISize = DropDownButtonISize();
|
||||
const auto borderPadding = aReflowInput.ComputedLogicalBorderPadding(wm);
|
||||
const auto padding = aReflowInput.ComputedLogicalPadding(wm);
|
||||
const auto border = borderPadding - padding;
|
||||
|
||||
// We ignore inline-end-padding (by adding it to our label box size) if we
|
||||
// have a dropdown button, so that the button aligns with the end of the
|
||||
// padding box.
|
||||
mDisplayISize = aReflowInput.ComputedISize() - buttonISize;
|
||||
mMaxDisplayISize = mDisplayISize + padding.IEnd(wm);
|
||||
|
||||
nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
|
||||
|
||||
// The button should occupy the same space as a scrollbar, and its position
|
||||
// starts from the border edge.
|
||||
if (mButtonFrame) {
|
||||
LogicalRect buttonRect(wm);
|
||||
buttonRect.IStart(wm) = borderPadding.IStart(wm) + mMaxDisplayISize;
|
||||
buttonRect.BStart(wm) = border.BStart(wm);
|
||||
|
||||
buttonRect.ISize(wm) = buttonISize;
|
||||
buttonRect.BSize(wm) = mDisplayFrame->BSize(wm) + padding.BStartEnd(wm);
|
||||
|
||||
const nsSize containerSize = aDesiredSize.PhysicalSize();
|
||||
mButtonFrame->SetRect(buttonRect, containerSize);
|
||||
if (buttonISize) {
|
||||
mDisplayISize += padding.IEnd(wm);
|
||||
}
|
||||
|
||||
if (!aStatus.IsInlineBreakBefore() && !aStatus.IsFullyComplete()) {
|
||||
// This frame didn't fit inside a fragmentation container. Splitting
|
||||
// a nsComboboxControlFrame makes no sense, so we override the status here.
|
||||
aStatus.Reset();
|
||||
}
|
||||
nsHTMLButtonControlFrame::Reflow(aPresContext, aDesiredSize, aReflowInput,
|
||||
aStatus);
|
||||
}
|
||||
|
||||
void nsComboboxControlFrame::Init(nsIContent* aContent,
|
||||
nsContainerFrame* aParent,
|
||||
nsIFrame* aPrevInFlow) {
|
||||
nsBlockFrame::Init(aContent, aParent, aPrevInFlow);
|
||||
nsHTMLButtonControlFrame::Init(aContent, aParent, aPrevInFlow);
|
||||
|
||||
mEventListener = new HTMLSelectEventListener(
|
||||
Select(), HTMLSelectEventListener::SelectType::Combobox);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_FRAME_DUMP
|
||||
nsresult nsComboboxControlFrame::GetFrameName(nsAString& aResult) const {
|
||||
return MakeFrameName(u"ComboboxControl"_ns, aResult);
|
||||
}
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
|
||||
nsresult nsComboboxControlFrame::RedisplaySelectedText() {
|
||||
nsAutoScriptBlocker scriptBlocker;
|
||||
mDisplayedIndex = Select().SelectedIndex();
|
||||
@ -494,16 +285,12 @@ nsresult nsComboboxControlFrame::RedisplayText() {
|
||||
mDisplayedOptionTextOrPreview.Truncate();
|
||||
}
|
||||
|
||||
REFLOW_DEBUG_MSG2(
|
||||
"RedisplayText \"%s\"\n",
|
||||
NS_LossyConvertUTF16toASCII(mDisplayedOptionTextOrPreview).get());
|
||||
|
||||
// Send reflow command because the new text maybe larger
|
||||
nsresult rv = NS_OK;
|
||||
if (mDisplayContent && !previousText.Equals(mDisplayedOptionTextOrPreview)) {
|
||||
// Don't call ActuallyDisplayText(true) directly here since that
|
||||
// could cause recursive frame construction. See bug 283117 and the comment
|
||||
// in HandleRedisplayTextEvent() below.
|
||||
if (!previousText.Equals(mDisplayedOptionTextOrPreview)) {
|
||||
// Don't call ActuallyDisplayText(true) directly here since that could cause
|
||||
// recursive frame construction. See bug 283117 and the comment in
|
||||
// HandleRedisplayTextEvent() below.
|
||||
|
||||
// Revoke outstanding events to avoid out-of-order events which could mean
|
||||
// displaying the wrong text.
|
||||
@ -512,7 +299,6 @@ nsresult nsComboboxControlFrame::RedisplayText() {
|
||||
NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
|
||||
"If we happen to run our redisplay event now, we might kill "
|
||||
"ourselves!");
|
||||
|
||||
mRedisplayTextEvent = new RedisplayTextEvent(this);
|
||||
nsContentUtils::AddScriptRunner(mRedisplayTextEvent.get());
|
||||
}
|
||||
@ -520,59 +306,37 @@ nsresult nsComboboxControlFrame::RedisplayText() {
|
||||
}
|
||||
|
||||
void nsComboboxControlFrame::HandleRedisplayTextEvent() {
|
||||
// First, make sure that the content model is up to date and we've
|
||||
// constructed the frames for all our content in the right places.
|
||||
// Otherwise they'll end up under the wrong insertion frame when we
|
||||
// ActuallyDisplayText, since that flushes out the content sink by
|
||||
// calling SetText on a DOM node with aNotify set to true. See bug
|
||||
// 289730.
|
||||
// First, make sure that the content model is up to date and we've constructed
|
||||
// the frames for all our content in the right places. Otherwise they'll end
|
||||
// up under the wrong insertion frame when we ActuallyDisplayText, since that
|
||||
// flushes out the content sink by calling SetText on a DOM node with aNotify
|
||||
// set to true. See bug 289730.
|
||||
AutoWeakFrame weakThis(this);
|
||||
PresContext()->Document()->FlushPendingNotifications(
|
||||
FlushType::ContentAndNotify);
|
||||
if (!weakThis.IsAlive()) return;
|
||||
|
||||
// Redirect frame insertions during this method (see
|
||||
// GetContentInsertionFrame()) so that any reframing that the frame
|
||||
// constructor forces upon us is inserted into the correct parent
|
||||
// (mDisplayFrame). See bug 282607.
|
||||
MOZ_ASSERT(!mInRedisplayText, "Nested RedisplayText");
|
||||
mInRedisplayText = true;
|
||||
mRedisplayTextEvent.Forget();
|
||||
|
||||
ActuallyDisplayText(true);
|
||||
if (!weakThis.IsAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// XXXbz This should perhaps be IntrinsicDirty::None. Check.
|
||||
PresShell()->FrameNeedsReflow(mDisplayFrame,
|
||||
IntrinsicDirty::FrameAncestorsAndDescendants,
|
||||
NS_FRAME_IS_DIRTY);
|
||||
|
||||
mInRedisplayText = false;
|
||||
mRedisplayTextEvent.Forget();
|
||||
ActuallyDisplayText(true);
|
||||
// Note: `this` might be dead here.
|
||||
}
|
||||
|
||||
void nsComboboxControlFrame::ActuallyDisplayText(bool aNotify) {
|
||||
RefPtr<nsTextNode> displayContent = mDisplayContent;
|
||||
if (mDisplayedOptionTextOrPreview.IsEmpty()) {
|
||||
// Have to use a space character of some sort for line-block-size
|
||||
// calculations to be right. Also, the space character must be zero-width
|
||||
// in order for the the inline-size calculations to be consistent between
|
||||
// size-contained comboboxes vs. empty comboboxes.
|
||||
//
|
||||
// XXXdholbert Does this space need to be "non-breaking"? I'm not sure
|
||||
// if it matters, but we previously had a comment here (added in 2002)
|
||||
// saying "Have to use a non-breaking space for line-height calculations
|
||||
// to be right". So I'll stick with a non-breaking space for now...
|
||||
static const char16_t space = 0xFEFF;
|
||||
displayContent->SetText(&space, 1, aNotify);
|
||||
} else {
|
||||
displayContent->SetText(mDisplayedOptionTextOrPreview, aNotify);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t nsComboboxControlFrame::GetIndexOfDisplayArea() {
|
||||
return mDisplayedIndex;
|
||||
RefPtr<dom::Text> displayContent = mDisplayLabel->GetFirstChild()->AsText();
|
||||
// Have to use a space character of some sort for line-block-size calculations
|
||||
// to be right. Also, the space character must be zero-width in order for the
|
||||
// inline-size calculations to be consistent between size-contained comboboxes
|
||||
// vs. empty comboboxes.
|
||||
//
|
||||
// XXXdholbert Does this space need to be "non-breaking"? I'm not sure if it
|
||||
// matters, but we previously had a comment here (added in 2002) saying "Have
|
||||
// to use a non-breaking space for line-height calculations to be right". So
|
||||
// I'll stick with a non-breaking space for now...
|
||||
displayContent->SetText(mDisplayedOptionTextOrPreview.IsEmpty()
|
||||
? u"\ufeff"_ns
|
||||
: mDisplayedOptionTextOrPreview,
|
||||
aNotify);
|
||||
}
|
||||
|
||||
bool nsComboboxControlFrame::IsDroppedDown() const {
|
||||
@ -631,53 +395,19 @@ nsresult nsComboboxControlFrame::HandleEvent(nsPresContext* aPresContext,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mContent->AsElement()->State().HasState(dom::ElementState::DISABLED)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If we have style that affects how we are selected, feed event down to
|
||||
// nsIFrame::HandleEvent so that selection takes place when appropriate.
|
||||
if (IsContentDisabled()) {
|
||||
return nsBlockFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsContainerFrame* nsComboboxControlFrame::GetContentInsertionFrame() {
|
||||
return mInRedisplayText ? mDisplayFrame : nullptr;
|
||||
}
|
||||
|
||||
void nsComboboxControlFrame::AppendDirectlyOwnedAnonBoxes(
|
||||
nsTArray<OwnedAnonBox>& aResult) {
|
||||
aResult.AppendElement(OwnedAnonBox(mDisplayFrame));
|
||||
return nsHTMLButtonControlFrame::HandleEvent(aPresContext, aEvent,
|
||||
aEventStatus);
|
||||
}
|
||||
|
||||
nsresult nsComboboxControlFrame::CreateAnonymousContent(
|
||||
nsTArray<ContentInfo>& aElements) {
|
||||
// The frames used to display the combo box and the button used to popup the
|
||||
// dropdown list are created through anonymous content. The dropdown list is
|
||||
// not created through anonymous content because its frame is initialized
|
||||
// specifically for the drop-down case and it is placed a special list
|
||||
// referenced through NS_COMBO_FRAME_POPUP_LIST_INDEX to keep separate from
|
||||
// the layout of the display and button.
|
||||
//
|
||||
// Note: The value attribute of the display content is set when an item is
|
||||
// selected in the dropdown list. If the content specified below does not
|
||||
// honor the value attribute than nothing will be displayed.
|
||||
dom::Document* doc = mContent->OwnerDoc();
|
||||
mDisplayLabel = doc->CreateHTMLElement(nsGkAtoms::label);
|
||||
|
||||
// For now the content that is created corresponds to two input buttons. It
|
||||
// would be better to create the tag as something other than input, but then
|
||||
// there isn't any way to create a button frame since it isn't possible to set
|
||||
// the display type in CSS2 to create a button frame.
|
||||
|
||||
// create content used for display
|
||||
// nsAtom* tag = NS_Atomize("mozcombodisplay");
|
||||
|
||||
// Add a child text content node for the label
|
||||
|
||||
nsNodeInfoManager* nimgr = mContent->NodeInfo()->NodeInfoManager();
|
||||
|
||||
mDisplayContent = new (nimgr) nsTextNode(nimgr);
|
||||
{
|
||||
RefPtr<nsTextNode> text = doc->CreateEmptyTextNode();
|
||||
mDisplayLabel->AppendChildTo(text, false, IgnoreErrors());
|
||||
}
|
||||
|
||||
// set the value of the text node
|
||||
mDisplayedIndex = Select().SelectedIndex();
|
||||
@ -686,14 +416,17 @@ nsresult nsComboboxControlFrame::CreateAnonymousContent(
|
||||
}
|
||||
ActuallyDisplayText(false);
|
||||
|
||||
aElements.AppendElement(mDisplayContent);
|
||||
aElements.AppendElement(mDisplayLabel);
|
||||
if (HasDropDownButton()) {
|
||||
mButtonContent = mContent->OwnerDoc()->CreateHTMLElement(nsGkAtoms::button);
|
||||
if (!mButtonContent) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
{
|
||||
// This gives the button a reasonable height. This could be done via CSS
|
||||
// instead, but relative font units like 1lh don't play very well with our
|
||||
// font inflation implementation, so we do it this way instead.
|
||||
RefPtr<nsTextNode> text = doc->CreateTextNode(u"\ufeff"_ns);
|
||||
mButtonContent->AppendChildTo(text, false, IgnoreErrors());
|
||||
}
|
||||
|
||||
// make someone to listen to the button.
|
||||
// Make someone to listen to the button.
|
||||
mButtonContent->SetAttr(kNameSpaceID_None, nsGkAtoms::type, u"button"_ns,
|
||||
false);
|
||||
// Set tabindex="-1" so that the button is not tabbable
|
||||
@ -707,8 +440,8 @@ nsresult nsComboboxControlFrame::CreateAnonymousContent(
|
||||
|
||||
void nsComboboxControlFrame::AppendAnonymousContentTo(
|
||||
nsTArray<nsIContent*>& aElements, uint32_t aFilter) {
|
||||
if (mDisplayContent) {
|
||||
aElements.AppendElement(mDisplayContent);
|
||||
if (mDisplayLabel) {
|
||||
aElements.AppendElement(mDisplayLabel);
|
||||
}
|
||||
|
||||
if (mButtonContent) {
|
||||
@ -716,226 +449,66 @@ void nsComboboxControlFrame::AppendAnonymousContentTo(
|
||||
}
|
||||
}
|
||||
|
||||
nsIContent* nsComboboxControlFrame::GetDisplayNode() const {
|
||||
return mDisplayContent;
|
||||
}
|
||||
namespace mozilla {
|
||||
|
||||
// XXXbz this is a for-now hack. Now that display:inline-block works,
|
||||
// need to revisit this.
|
||||
class nsComboboxDisplayFrame final : public nsBlockFrame {
|
||||
class ComboboxLabelFrame final : public nsBlockFrame {
|
||||
public:
|
||||
NS_DECL_FRAMEARENA_HELPERS(nsComboboxDisplayFrame)
|
||||
|
||||
nsComboboxDisplayFrame(ComputedStyle* aStyle,
|
||||
nsComboboxControlFrame* aComboBox)
|
||||
: nsBlockFrame(aStyle, aComboBox->PresContext(), kClassID),
|
||||
mComboBox(aComboBox) {}
|
||||
NS_DECL_QUERYFRAME
|
||||
NS_DECL_FRAMEARENA_HELPERS(ComboboxLabelFrame)
|
||||
|
||||
#ifdef DEBUG_FRAME_DUMP
|
||||
nsresult GetFrameName(nsAString& aResult) const final {
|
||||
return MakeFrameName(u"ComboboxDisplay"_ns, aResult);
|
||||
return MakeFrameName(u"ComboboxLabel"_ns, aResult);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
|
||||
const ReflowInput& aReflowInput, nsReflowStatus& aStatus) final;
|
||||
|
||||
void BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
const nsDisplayListSet& aLists) final;
|
||||
|
||||
protected:
|
||||
nsComboboxControlFrame* mComboBox;
|
||||
public:
|
||||
ComboboxLabelFrame(ComputedStyle* aStyle, nsPresContext* aPresContext)
|
||||
: nsBlockFrame(aStyle, aPresContext, kClassID) {}
|
||||
};
|
||||
|
||||
NS_IMPL_FRAMEARENA_HELPERS(nsComboboxDisplayFrame)
|
||||
NS_QUERYFRAME_HEAD(ComboboxLabelFrame)
|
||||
NS_QUERYFRAME_ENTRY(ComboboxLabelFrame)
|
||||
NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
|
||||
NS_IMPL_FRAMEARENA_HELPERS(ComboboxLabelFrame)
|
||||
|
||||
void nsComboboxDisplayFrame::Reflow(nsPresContext* aPresContext,
|
||||
ReflowOutput& aDesiredSize,
|
||||
const ReflowInput& aReflowInput,
|
||||
nsReflowStatus& aStatus) {
|
||||
void ComboboxLabelFrame::Reflow(nsPresContext* aPresContext,
|
||||
ReflowOutput& aDesiredSize,
|
||||
const ReflowInput& aReflowInput,
|
||||
nsReflowStatus& aStatus) {
|
||||
MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
|
||||
MOZ_ASSERT(aReflowInput.mParentReflowInput &&
|
||||
aReflowInput.mParentReflowInput->mFrame == mComboBox,
|
||||
"Combobox's frame tree is wrong!");
|
||||
|
||||
const nsComboboxControlFrame* combobox =
|
||||
do_QueryFrame(GetParent()->GetParent());
|
||||
MOZ_ASSERT(combobox, "Combobox's frame tree is wrong!");
|
||||
MOZ_ASSERT(aReflowInput.ComputedPhysicalBorderPadding() == nsMargin(),
|
||||
"We shouldn't have border and padding in UA!");
|
||||
|
||||
ReflowInput state(aReflowInput);
|
||||
if (state.ComputedBSize() == NS_UNCONSTRAINEDSIZE) {
|
||||
state.SetLineHeight(state.mParentReflowInput->GetLineHeight());
|
||||
}
|
||||
const WritingMode wm = aReflowInput.GetWritingMode();
|
||||
const LogicalMargin bp = state.ComputedLogicalBorderPadding(wm);
|
||||
MOZ_ASSERT(bp.BStartEnd(wm) == 0,
|
||||
"We shouldn't have border and padding in the block axis in UA!");
|
||||
nscoord inlineBp = bp.IStartEnd(wm);
|
||||
nscoord computedISize = mComboBox->mDisplayISize - inlineBp;
|
||||
|
||||
// Other UAs ignore padding in some (but not all) platforms for (themed only)
|
||||
// comboboxes. Instead of doing that, we prevent that padding if present from
|
||||
// clipping the display text, by enforcing the display text minimum size in
|
||||
// that situation.
|
||||
const bool shouldHonorMinISize =
|
||||
mComboBox->StyleDisplay()->EffectiveAppearance() ==
|
||||
StyleAppearance::Menulist;
|
||||
if (shouldHonorMinISize) {
|
||||
computedISize = std::max(state.ComputedMinISize(), computedISize);
|
||||
// Don't let this size go over mMaxDisplayISize, since that'd be
|
||||
// observable via clientWidth / scrollWidth.
|
||||
computedISize =
|
||||
std::min(computedISize, mComboBox->mMaxDisplayISize - inlineBp);
|
||||
}
|
||||
|
||||
state.SetComputedISize(std::max(0, computedISize));
|
||||
state.SetComputedISize(combobox->mDisplayISize);
|
||||
nsBlockFrame::Reflow(aPresContext, aDesiredSize, state, aStatus);
|
||||
aStatus.Reset(); // this type of frame can't be split
|
||||
}
|
||||
|
||||
void nsComboboxDisplayFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
const nsDisplayListSet& aLists) {
|
||||
nsDisplayListCollection set(aBuilder);
|
||||
nsBlockFrame::BuildDisplayList(aBuilder, set);
|
||||
} // namespace mozilla
|
||||
|
||||
// remove background items if parent frame is themed
|
||||
if (mComboBox->IsThemed()) {
|
||||
set.BorderBackground()->DeleteAll(aBuilder);
|
||||
}
|
||||
|
||||
set.MoveTo(aLists);
|
||||
}
|
||||
|
||||
nsIFrame* nsComboboxControlFrame::CreateFrameForDisplayNode() {
|
||||
MOZ_ASSERT(mDisplayContent);
|
||||
|
||||
// Get PresShell
|
||||
mozilla::PresShell* ps = PresShell();
|
||||
ServoStyleSet* styleSet = ps->StyleSet();
|
||||
|
||||
// create the ComputedStyle for the anonymous block frame and text frame
|
||||
RefPtr<ComputedStyle> computedStyle =
|
||||
styleSet->ResolveInheritingAnonymousBoxStyle(
|
||||
PseudoStyleType::mozDisplayComboboxControlFrame, mComputedStyle);
|
||||
|
||||
RefPtr<ComputedStyle> textComputedStyle =
|
||||
styleSet->ResolveStyleForText(mDisplayContent, mComputedStyle);
|
||||
|
||||
// Start by creating our anonymous block frame
|
||||
mDisplayFrame = new (ps) nsComboboxDisplayFrame(computedStyle, this);
|
||||
mDisplayFrame->Init(mContent, this, nullptr);
|
||||
|
||||
// Create a text frame and put it inside the block frame
|
||||
nsIFrame* textFrame = NS_NewTextFrame(ps, textComputedStyle);
|
||||
|
||||
// initialize the text frame
|
||||
textFrame->Init(mDisplayContent, mDisplayFrame, nullptr);
|
||||
mDisplayContent->SetPrimaryFrame(textFrame);
|
||||
|
||||
mDisplayFrame->SetInitialChildList(FrameChildListID::Principal,
|
||||
nsFrameList(textFrame, textFrame));
|
||||
return mDisplayFrame;
|
||||
nsIFrame* NS_NewComboboxLabelFrame(PresShell* aPresShell,
|
||||
ComputedStyle* aStyle) {
|
||||
return new (aPresShell)
|
||||
ComboboxLabelFrame(aStyle, aPresShell->GetPresContext());
|
||||
}
|
||||
|
||||
void nsComboboxControlFrame::Destroy(DestroyContext& aContext) {
|
||||
// Revoke any pending RedisplayTextEvent
|
||||
mRedisplayTextEvent.Revoke();
|
||||
|
||||
mEventListener->Detach();
|
||||
|
||||
// Cleanup frames in popup child list
|
||||
aContext.AddAnonymousContent(mDisplayContent.forget());
|
||||
aContext.AddAnonymousContent(mDisplayLabel.forget());
|
||||
aContext.AddAnonymousContent(mButtonContent.forget());
|
||||
nsBlockFrame::Destroy(aContext);
|
||||
}
|
||||
|
||||
const nsFrameList& nsComboboxControlFrame::GetChildList(
|
||||
ChildListID aListID) const {
|
||||
return nsBlockFrame::GetChildList(aListID);
|
||||
}
|
||||
|
||||
void nsComboboxControlFrame::GetChildLists(nsTArray<ChildList>* aLists) const {
|
||||
nsBlockFrame::GetChildLists(aLists);
|
||||
}
|
||||
|
||||
void nsComboboxControlFrame::SetInitialChildList(ChildListID aListID,
|
||||
nsFrameList&& aChildList) {
|
||||
for (nsIFrame* f : aChildList) {
|
||||
MOZ_ASSERT(f->GetParent() == this, "Unexpected parent");
|
||||
nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(f->GetContent());
|
||||
if (formControl &&
|
||||
formControl->ControlType() == FormControlType::ButtonButton) {
|
||||
mButtonFrame = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
nsBlockFrame::SetInitialChildList(aListID, std::move(aChildList));
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class nsDisplayComboboxFocus : public nsPaintedDisplayItem {
|
||||
public:
|
||||
nsDisplayComboboxFocus(nsDisplayListBuilder* aBuilder,
|
||||
nsComboboxControlFrame* aFrame)
|
||||
: nsPaintedDisplayItem(aBuilder, aFrame) {
|
||||
MOZ_COUNT_CTOR(nsDisplayComboboxFocus);
|
||||
}
|
||||
MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayComboboxFocus)
|
||||
|
||||
void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
|
||||
NS_DISPLAY_DECL_NAME("ComboboxFocus", TYPE_COMBOBOX_FOCUS)
|
||||
};
|
||||
|
||||
void nsDisplayComboboxFocus::Paint(nsDisplayListBuilder* aBuilder,
|
||||
gfxContext* aCtx) {
|
||||
static_cast<nsComboboxControlFrame*>(mFrame)->PaintFocus(
|
||||
*aCtx->GetDrawTarget(), ToReferenceFrame());
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
void nsComboboxControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
const nsDisplayListSet& aLists) {
|
||||
if (aBuilder->IsForEventDelivery()) {
|
||||
// Don't allow children to receive events.
|
||||
// REVIEW: following old GetFrameForPoint
|
||||
DisplayBorderBackgroundOutline(aBuilder, aLists);
|
||||
} else {
|
||||
// REVIEW: Our in-flow child frames are inline-level so they will paint in
|
||||
// our content list, so we don't need to mess with layers.
|
||||
nsBlockFrame::BuildDisplayList(aBuilder, aLists);
|
||||
}
|
||||
|
||||
// draw a focus indicator only when focus rings should be drawn
|
||||
if (Select().State().HasState(dom::ElementState::FOCUSRING) && IsThemed() &&
|
||||
PresContext()->Theme()->ThemeWantsButtonInnerFocusRing()) {
|
||||
aLists.Content()->AppendNewToTop<nsDisplayComboboxFocus>(aBuilder, this);
|
||||
}
|
||||
|
||||
DisplaySelectionOverlay(aBuilder, aLists.Content());
|
||||
}
|
||||
|
||||
void nsComboboxControlFrame::PaintFocus(DrawTarget& aDrawTarget, nsPoint aPt) {
|
||||
/* Do we need to do anything? */
|
||||
dom::ElementState state = mContent->AsElement()->State();
|
||||
if (state.HasState(dom::ElementState::DISABLED) ||
|
||||
!state.HasState(dom::ElementState::FOCUS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
|
||||
|
||||
nsRect clipRect = mDisplayFrame->GetRect() + aPt;
|
||||
aDrawTarget.PushClipRect(
|
||||
NSRectToSnappedRect(clipRect, appUnitsPerDevPixel, aDrawTarget));
|
||||
|
||||
StrokeOptions strokeOptions;
|
||||
nsLayoutUtils::InitDashPattern(strokeOptions, StyleBorderStyle::Dotted);
|
||||
ColorPattern color(ToDeviceColor(StyleText()->mColor));
|
||||
nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
|
||||
clipRect.width -= onePixel;
|
||||
clipRect.height -= onePixel;
|
||||
Rect r = ToRect(nsLayoutUtils::RectToGfxRect(clipRect, appUnitsPerDevPixel));
|
||||
StrokeSnappedEdgesOfRect(r, aDrawTarget, color, strokeOptions);
|
||||
|
||||
aDrawTarget.PopClip();
|
||||
nsHTMLButtonControlFrame::Destroy(aContext);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
@ -960,6 +533,7 @@ nsComboboxControlFrame::OnOptionSelected(int32_t aIndex, bool aSelected) {
|
||||
|
||||
void nsComboboxControlFrame::FireValueChangeEvent() {
|
||||
// Fire ValueChange event to indicate data value of combo box has changed
|
||||
// FIXME(emilio): This shouldn't be exposed to content.
|
||||
nsContentUtils::AddScriptRunner(new AsyncEventDispatcher(
|
||||
mContent, u"ValueChange"_ns, CanBubble::eYes, ChromeOnlyDispatch::eNo));
|
||||
}
|
||||
|
@ -7,56 +7,30 @@
|
||||
#ifndef nsComboboxControlFrame_h___
|
||||
#define nsComboboxControlFrame_h___
|
||||
|
||||
#ifdef DEBUG_evaughan
|
||||
// #define DEBUG_rods
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_rods
|
||||
// #define DO_REFLOW_DEBUG
|
||||
// #define DO_REFLOW_COUNTER
|
||||
// #define DO_UNCONSTRAINED_CHECK
|
||||
// #define DO_PIXELS
|
||||
// #define DO_NEW_REFLOW
|
||||
#endif
|
||||
|
||||
// Mark used to indicate when onchange has been fired for current combobox item
|
||||
#define NS_SKIP_NOTIFY_INDEX -2
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsBlockFrame.h"
|
||||
#include "nsIFormControlFrame.h"
|
||||
#include "nsIAnonymousContentCreator.h"
|
||||
#include "nsISelectControlFrame.h"
|
||||
#include "nsIRollupListener.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
class nsComboboxDisplayFrame;
|
||||
class nsTextNode;
|
||||
#include "nsHTMLButtonControlFrame.h"
|
||||
|
||||
namespace mozilla {
|
||||
class PresShell;
|
||||
class HTMLSelectEventListener;
|
||||
class ComboboxLabelFrame;
|
||||
namespace dom {
|
||||
class HTMLSelectElement;
|
||||
}
|
||||
|
||||
namespace gfx {
|
||||
class DrawTarget;
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
class nsComboboxControlFrame final : public nsBlockFrame,
|
||||
public nsIFormControlFrame,
|
||||
class nsComboboxControlFrame final : public nsHTMLButtonControlFrame,
|
||||
public nsIAnonymousContentCreator,
|
||||
public nsISelectControlFrame {
|
||||
using DrawTarget = mozilla::gfx::DrawTarget;
|
||||
using Element = mozilla::dom::Element;
|
||||
|
||||
public:
|
||||
friend nsComboboxControlFrame* NS_NewComboboxControlFrame(
|
||||
mozilla::PresShell* aPresShell, ComputedStyle* aStyle);
|
||||
friend class nsComboboxDisplayFrame;
|
||||
|
||||
friend class mozilla::ComboboxLabelFrame;
|
||||
explicit nsComboboxControlFrame(ComputedStyle* aStyle,
|
||||
nsPresContext* aPresContext);
|
||||
~nsComboboxControlFrame();
|
||||
@ -69,17 +43,17 @@ class nsComboboxControlFrame final : public nsBlockFrame,
|
||||
void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
|
||||
uint32_t aFilter) final;
|
||||
|
||||
nsIContent* GetDisplayNode() const;
|
||||
nsIFrame* CreateFrameForDisplayNode();
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
mozilla::a11y::AccType AccessibleType() final;
|
||||
#endif
|
||||
|
||||
nscoord GetMinISize(gfxContext* aRenderingContext) final;
|
||||
|
||||
nscoord GetPrefISize(gfxContext* aRenderingContext) final;
|
||||
|
||||
// We're a leaf, so we need to report ourselves as the content insertion
|
||||
// frame.
|
||||
nsContainerFrame* GetContentInsertionFrame() override { return this; }
|
||||
|
||||
void Reflow(nsPresContext* aCX, ReflowOutput& aDesiredSize,
|
||||
const ReflowInput& aReflowInput, nsReflowStatus& aStatus) final;
|
||||
|
||||
@ -88,60 +62,26 @@ class nsComboboxControlFrame final : public nsBlockFrame,
|
||||
mozilla::WidgetGUIEvent* aEvent,
|
||||
nsEventStatus* aEventStatus) final;
|
||||
|
||||
void BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
const nsDisplayListSet& aLists) final;
|
||||
|
||||
void PaintFocus(DrawTarget& aDrawTarget, nsPoint aPt);
|
||||
|
||||
void Init(nsIContent* aContent, nsContainerFrame* aParent,
|
||||
nsIFrame* aPrevInFlow) final;
|
||||
|
||||
#ifdef DEBUG_FRAME_DUMP
|
||||
nsresult GetFrameName(nsAString& aResult) const final;
|
||||
#endif
|
||||
void Destroy(DestroyContext&) final;
|
||||
|
||||
void SetInitialChildList(ChildListID aListID, nsFrameList&& aChildList) final;
|
||||
const nsFrameList& GetChildList(ChildListID aListID) const final;
|
||||
void GetChildLists(nsTArray<ChildList>* aLists) const final;
|
||||
|
||||
nsContainerFrame* GetContentInsertionFrame() final;
|
||||
|
||||
// Return the dropdown and display frame.
|
||||
void AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) final;
|
||||
#ifdef DEBUG_FRAME_DUMP
|
||||
nsresult GetFrameName(nsAString& aResult) const final {
|
||||
return MakeFrameName(u"ComboboxControl"_ns, aResult);
|
||||
}
|
||||
#endif
|
||||
|
||||
// nsIFormControlFrame
|
||||
nsresult SetFormProperty(nsAtom* aName, const nsAString& aValue) final {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inform the control that it got (or lost) focus.
|
||||
* If it lost focus, the dropdown menu will be rolled up if needed,
|
||||
* and FireOnChange() will be called.
|
||||
* @param aOn true if got focus, false if lost focus.
|
||||
* @param aRepaint if true then force repaint (NOTE: we always force repaint
|
||||
* currently)
|
||||
* @note This method might destroy |this|.
|
||||
*/
|
||||
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
||||
void SetFocus(bool aOn, bool aRepaint) final;
|
||||
|
||||
/**
|
||||
* Return the available space before and after this frame for
|
||||
* placing the drop-down list, and the current 2D translation.
|
||||
* Note that either or both can be less than or equal to zero,
|
||||
* if both are then the drop-down should be closed.
|
||||
*/
|
||||
void GetAvailableDropdownSpace(mozilla::WritingMode aWM, nscoord* aBefore,
|
||||
nscoord* aAfter,
|
||||
mozilla::LogicalPoint* aTranslation);
|
||||
int32_t GetIndexOfDisplayArea();
|
||||
/**
|
||||
* @note This method might destroy |this|.
|
||||
*/
|
||||
void FireValueChangeEvent();
|
||||
nsresult RedisplaySelectedText();
|
||||
int32_t UpdateRecentIndex(int32_t aIndex);
|
||||
|
||||
bool IsDroppedDown() const;
|
||||
|
||||
@ -192,53 +132,23 @@ class nsComboboxControlFrame final : public nsBlockFrame,
|
||||
nsComboboxControlFrame* mControlFrame;
|
||||
};
|
||||
|
||||
void CheckFireOnChange();
|
||||
void FireValueChangeEvent();
|
||||
nsresult RedisplayText();
|
||||
void HandleRedisplayTextEvent();
|
||||
void ActuallyDisplayText(bool aNotify);
|
||||
|
||||
// If our total transform to the root frame of the root document is only a 2d
|
||||
// translation then return that translation, otherwise returns (0,0).
|
||||
nsPoint GetCSSTransformTranslation();
|
||||
|
||||
mozilla::dom::HTMLSelectElement& Select() const;
|
||||
void GetOptionText(uint32_t aIndex, nsAString& aText) const;
|
||||
|
||||
RefPtr<nsTextNode> mDisplayContent; // Anonymous content used to display the
|
||||
// current selection
|
||||
RefPtr<Element> mButtonContent; // Anonymous content for the button
|
||||
nsContainerFrame* mDisplayFrame; // frame to display selection
|
||||
nsIFrame* mButtonFrame; // button frame
|
||||
|
||||
// The inline size of our display area. Used by that frame's reflow
|
||||
// to size to the full inline size except the drop-marker.
|
||||
nscoord mDisplayISize;
|
||||
// The maximum inline size of our display area, which is the
|
||||
// nsComoboxControlFrame's border-box.
|
||||
//
|
||||
// Going over this would be observable via DOM APIs like client / scrollWidth.
|
||||
nscoord mMaxDisplayISize;
|
||||
|
||||
RefPtr<Element> mDisplayLabel; // Anonymous content for the label
|
||||
RefPtr<Element> mButtonContent; // Anonymous content for the button
|
||||
nsRevocableEventPtr<RedisplayTextEvent> mRedisplayTextEvent;
|
||||
|
||||
int32_t mRecentSelectedIndex;
|
||||
int32_t mDisplayedIndex;
|
||||
// The inline size of our display area. Used by that frame's reflow to size to
|
||||
// the full inline size except the drop-marker.
|
||||
nscoord mDisplayISize = 0;
|
||||
int32_t mDisplayedIndex = -1;
|
||||
nsString mDisplayedOptionTextOrPreview;
|
||||
|
||||
RefPtr<mozilla::HTMLSelectEventListener> mEventListener;
|
||||
|
||||
// See comment in HandleRedisplayTextEvent().
|
||||
bool mInRedisplayText;
|
||||
bool mIsOpenInParentProcess;
|
||||
|
||||
// static class data member for Bug 32920
|
||||
// only one control can be focused at a time
|
||||
static nsComboboxControlFrame* sFocused;
|
||||
|
||||
#ifdef DO_REFLOW_COUNTER
|
||||
int32_t mReflowId;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -160,10 +160,6 @@ nsresult nsGfxButtonControlFrame::AttributeChanged(int32_t aNameSpaceID,
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsContainerFrame* nsGfxButtonControlFrame::GetContentInsertionFrame() {
|
||||
return this;
|
||||
}
|
||||
|
||||
nsresult nsGfxButtonControlFrame::HandleEvent(nsPresContext* aPresContext,
|
||||
WidgetGUIEvent* aEvent,
|
||||
nsEventStatus* aEventStatus) {
|
||||
|
@ -7,9 +7,7 @@
|
||||
#ifndef nsGfxButtonControlFrame_h___
|
||||
#define nsGfxButtonControlFrame_h___
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsHTMLButtonControlFrame.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIAnonymousContentCreator.h"
|
||||
|
||||
class nsTextNode;
|
||||
@ -29,26 +27,25 @@ class nsGfxButtonControlFrame final : public nsHTMLButtonControlFrame,
|
||||
|
||||
void Destroy(DestroyContext&) override;
|
||||
|
||||
virtual nsresult HandleEvent(nsPresContext* aPresContext,
|
||||
mozilla::WidgetGUIEvent* aEvent,
|
||||
nsEventStatus* aEventStatus) override;
|
||||
nsresult HandleEvent(nsPresContext* aPresContext,
|
||||
mozilla::WidgetGUIEvent* aEvent,
|
||||
nsEventStatus* aEventStatus) override;
|
||||
|
||||
#ifdef DEBUG_FRAME_DUMP
|
||||
virtual nsresult GetFrameName(nsAString& aResult) const override;
|
||||
nsresult GetFrameName(nsAString& aResult) const override;
|
||||
#endif
|
||||
|
||||
NS_DECL_QUERYFRAME
|
||||
|
||||
// nsIAnonymousContentCreator
|
||||
virtual nsresult CreateAnonymousContent(
|
||||
nsTArray<ContentInfo>& aElements) override;
|
||||
virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
|
||||
uint32_t aFilter) override;
|
||||
nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override;
|
||||
void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
|
||||
uint32_t aFilter) override;
|
||||
|
||||
virtual nsresult AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute,
|
||||
int32_t aModType) override;
|
||||
nsresult AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute,
|
||||
int32_t aModType) override;
|
||||
|
||||
virtual nsContainerFrame* GetContentInsertionFrame() override;
|
||||
nsContainerFrame* GetContentInsertionFrame() override { return this; }
|
||||
|
||||
protected:
|
||||
nsresult GetDefaultLabel(nsAString& aLabel) const;
|
||||
|
@ -57,8 +57,8 @@ void nsHTMLButtonControlFrame::SetFocus(bool aOn, bool aRepaint) {}
|
||||
nsresult nsHTMLButtonControlFrame::HandleEvent(nsPresContext* aPresContext,
|
||||
WidgetGUIEvent* aEvent,
|
||||
nsEventStatus* aEventStatus) {
|
||||
// if disabled do nothing
|
||||
if (mRenderer.isDisabled()) {
|
||||
if (mContent->AsElement()->IsDisabled()) {
|
||||
// If disabled do nothing
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -237,12 +237,14 @@ void nsHTMLButtonControlFrame::ReflowButtonContents(
|
||||
// Button has a fixed block-size -- that's its content-box bSize.
|
||||
buttonContentBox.BSize(wm) = aButtonReflowInput.ComputedBSize();
|
||||
} else {
|
||||
// Button is intrinsically sized -- it should shrinkwrap the
|
||||
// button-contents' bSize. But if it has size containment in block axis,
|
||||
// ignore the contents and use contain-intrinsic-block-size.
|
||||
nscoord bSize = aButtonReflowInput.mFrame->ContainIntrinsicBSize().valueOr(
|
||||
contentsDesiredSize.BSize(wm));
|
||||
|
||||
// Button is intrinsically sized -- it should shrinkwrap the contents'
|
||||
// bSize.
|
||||
// If we have size containment in block axis, ignore the contents and use
|
||||
// contain-intrinsic-block-size. The combobox content size with no content
|
||||
// is one line-height, not zero.
|
||||
const Maybe<nscoord> containBSize = ContainIntrinsicBSize(
|
||||
IsComboboxControlFrame() ? aButtonReflowInput.GetLineHeight() : 0);
|
||||
const nscoord bSize = containBSize.valueOr(contentsDesiredSize.BSize(wm));
|
||||
// Make sure we obey min/max-bSize in the case when we're doing intrinsic
|
||||
// sizing (we get it for free when we have a non-intrinsic
|
||||
// aButtonReflowInput.ComputedBSize()). Note that we do this before
|
||||
|
@ -55,13 +55,8 @@ FRAME_CLASSES = [
|
||||
Frame("nsColorControlFrame", "ColorControl", REPLACED_WITH_BLOCK | LEAF),
|
||||
Frame("nsColumnSetFrame", "ColumnSet", COMMON),
|
||||
Frame("ColumnSetWrapperFrame", "ColumnSetWrapper", BLOCK),
|
||||
Frame("nsComboboxControlFrame", "ComboboxControl", BLOCK | REPLACED_WITH_BLOCK),
|
||||
# FIXME(emilio, bug 1362907): Revisit these after that bug, this is the
|
||||
# only frame that has ReplacedContainsBlock but not Replaced, which is
|
||||
# sketchy.
|
||||
Frame(
|
||||
"nsComboboxDisplayFrame", "ComboboxDisplay", REPLACED_WITH_BLOCK - {"Replaced"}
|
||||
),
|
||||
Frame("nsComboboxControlFrame", "ComboboxControl", REPLACED_WITH_BLOCK | LEAF),
|
||||
Frame("ComboboxLabelFrame", "Block", BLOCK),
|
||||
Frame("nsContinuingTextFrame", "Text", TEXT),
|
||||
Frame("nsDateTimeControlFrame", "DateTimeControl", REPLACED_WITH_BLOCK),
|
||||
Frame("nsFieldSetFrame", "FieldSet", BLOCK),
|
||||
|
@ -29,8 +29,7 @@
|
||||
|
||||
using mozilla::layout::TextDrawTarget;
|
||||
|
||||
namespace mozilla {
|
||||
namespace css {
|
||||
namespace mozilla::css {
|
||||
|
||||
class LazyReferenceRenderingDrawTargetGetterFromFrame final
|
||||
: public gfxFontGroup::LazyReferenceDrawTargetGetter {
|
||||
@ -834,9 +833,10 @@ bool TextOverflow::CanHaveOverflowMarkers(nsBlockFrame* aBlockFrame,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip ComboboxControlFrame because it would clip the drop-down arrow.
|
||||
// Its anon block inherits 'text-overflow' and does what is expected.
|
||||
if (aBlockFrame->IsComboboxControlFrame()) {
|
||||
// Skip the combobox anonymous block because it would clip the drop-down
|
||||
// arrow. The inner label inherits 'text-overflow' and does the right thing.
|
||||
if (aBlockFrame->GetParent() &&
|
||||
aBlockFrame->GetParent()->IsComboboxControlFrame()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -931,5 +931,4 @@ void TextOverflow::Marker::SetupString(nsIFrame* aFrame) {
|
||||
mInitialized = true;
|
||||
}
|
||||
|
||||
} // namespace css
|
||||
} // namespace mozilla
|
||||
} // namespace mozilla::css
|
||||
|
@ -2229,97 +2229,83 @@ nscoord nsBlockFrame::ComputeFinalSize(const ReflowInput& aReflowInput,
|
||||
// calculated from aspect-ratio. i.e. Don't carry out block margin-end if it
|
||||
// is replaced by the block size from aspect-ratio and inline size.
|
||||
aMetrics.mCarriedOutBEndMargin.Zero();
|
||||
} else {
|
||||
Maybe<nscoord> containBSize = ContainIntrinsicBSize(
|
||||
IsComboboxControlFrame() ? NS_UNCONSTRAINEDSIZE : 0);
|
||||
if (containBSize && *containBSize != NS_UNCONSTRAINEDSIZE) {
|
||||
// If we're size-containing in block axis and we don't have a specified
|
||||
// block size, then our final size should actually be computed from only
|
||||
// our border, padding and contain-intrinsic-block-size, ignoring the
|
||||
// actual contents. Hence this case is a simplified version of the case
|
||||
// below.
|
||||
//
|
||||
// NOTE: We exempt the nsComboboxControlFrame subclass from taking this
|
||||
// special case when it has 'contain-intrinsic-block-size: none', because
|
||||
// comboboxes implicitly honors the size-containment behavior on its
|
||||
// nsComboboxDisplayFrame child (which it shrinkwraps) rather than on the
|
||||
// nsComboboxControlFrame. (Moreover, the DisplayFrame child doesn't even
|
||||
// need any special content-size-ignoring behavior in its reflow method,
|
||||
// because that method just resolves "auto" BSize values to one
|
||||
// line-height rather than by measuring its contents' BSize.)
|
||||
nscoord contentBSize = *containBSize;
|
||||
nscoord autoBSize =
|
||||
aReflowInput.ApplyMinMaxBSize(contentBSize, aState.mConsumedBSize);
|
||||
} else if (Maybe<nscoord> containBSize = ContainIntrinsicBSize()) {
|
||||
// If we're size-containing in block axis and we don't have a specified
|
||||
// block size, then our final size should actually be computed from only
|
||||
// our border, padding and contain-intrinsic-block-size, ignoring the
|
||||
// actual contents. Hence this case is a simplified version of the case
|
||||
// below.
|
||||
nscoord contentBSize = *containBSize;
|
||||
nscoord autoBSize =
|
||||
aReflowInput.ApplyMinMaxBSize(contentBSize, aState.mConsumedBSize);
|
||||
aMetrics.mCarriedOutBEndMargin.Zero();
|
||||
autoBSize += borderPadding.BStartEnd(wm);
|
||||
finalSize.BSize(wm) = autoBSize;
|
||||
} else if (aState.mReflowStatus.IsInlineBreakBefore()) {
|
||||
// Our parent is expected to push this frame to the next page/column so
|
||||
// what size we set here doesn't really matter.
|
||||
finalSize.BSize(wm) = aReflowInput.AvailableBSize();
|
||||
} else if (aState.mReflowStatus.IsComplete()) {
|
||||
const nscoord lineClampedContentBlockEndEdge =
|
||||
ApplyLineClamp(aReflowInput, this, blockEndEdgeOfChildren);
|
||||
|
||||
const nscoord bpBStart = borderPadding.BStart(wm);
|
||||
const nscoord contentBSize = blockEndEdgeOfChildren - bpBStart;
|
||||
const nscoord lineClampedContentBSize =
|
||||
lineClampedContentBlockEndEdge - bpBStart;
|
||||
|
||||
const nscoord autoBSize = aReflowInput.ApplyMinMaxBSize(
|
||||
lineClampedContentBSize, aState.mConsumedBSize);
|
||||
if (autoBSize != contentBSize) {
|
||||
// Our min-block-size, max-block-size, or -webkit-line-clamp value made
|
||||
// our bsize change. Don't carry out our kids' block-end margins.
|
||||
aMetrics.mCarriedOutBEndMargin.Zero();
|
||||
autoBSize += borderPadding.BStartEnd(wm);
|
||||
finalSize.BSize(wm) = autoBSize;
|
||||
} else if (aState.mReflowStatus.IsInlineBreakBefore()) {
|
||||
// Our parent is expected to push this frame to the next page/column so
|
||||
// what size we set here doesn't really matter.
|
||||
finalSize.BSize(wm) = aReflowInput.AvailableBSize();
|
||||
} else if (aState.mReflowStatus.IsComplete()) {
|
||||
const nscoord lineClampedContentBlockEndEdge =
|
||||
ApplyLineClamp(aReflowInput, this, blockEndEdgeOfChildren);
|
||||
|
||||
const nscoord bpBStart = borderPadding.BStart(wm);
|
||||
const nscoord contentBSize = blockEndEdgeOfChildren - bpBStart;
|
||||
const nscoord lineClampedContentBSize =
|
||||
lineClampedContentBlockEndEdge - bpBStart;
|
||||
|
||||
const nscoord autoBSize = aReflowInput.ApplyMinMaxBSize(
|
||||
lineClampedContentBSize, aState.mConsumedBSize);
|
||||
if (autoBSize != contentBSize) {
|
||||
// Our min-block-size, max-block-size, or -webkit-line-clamp value made
|
||||
// our bsize change. Don't carry out our kids' block-end margins.
|
||||
aMetrics.mCarriedOutBEndMargin.Zero();
|
||||
}
|
||||
nscoord bSize = autoBSize + borderPadding.BStartEnd(wm);
|
||||
if (MOZ_UNLIKELY(autoBSize > contentBSize &&
|
||||
bSize > aReflowInput.AvailableBSize() &&
|
||||
aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE)) {
|
||||
// Applying `min-size` made us overflow our available size.
|
||||
// Clamp it and report that we're Incomplete, or BreakBefore if we have
|
||||
// 'break-inside: avoid' that is applicable.
|
||||
bSize = aReflowInput.AvailableBSize();
|
||||
if (ShouldAvoidBreakInside(aReflowInput)) {
|
||||
aState.mReflowStatus.SetInlineLineBreakBeforeAndReset();
|
||||
} else {
|
||||
aState.mReflowStatus.SetIncomplete();
|
||||
}
|
||||
}
|
||||
finalSize.BSize(wm) = bSize;
|
||||
} else {
|
||||
NS_ASSERTION(
|
||||
aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE,
|
||||
"Shouldn't be incomplete if availableBSize is UNCONSTRAINED.");
|
||||
nscoord bSize = std::max(aState.mBCoord, aReflowInput.AvailableBSize());
|
||||
if (aReflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE) {
|
||||
// This should never happen, but it does. See bug 414255
|
||||
bSize = aState.mBCoord;
|
||||
}
|
||||
const nscoord maxBSize = aReflowInput.ComputedMaxBSize();
|
||||
if (maxBSize != NS_UNCONSTRAINEDSIZE &&
|
||||
aState.mConsumedBSize + bSize - borderPadding.BStart(wm) > maxBSize) {
|
||||
// Compute this fragment's block-size, with the max-block-size
|
||||
// constraint taken into consideration.
|
||||
const nscoord clampedBSizeWithoutEndBP =
|
||||
std::max(0, maxBSize - aState.mConsumedBSize) +
|
||||
borderPadding.BStart(wm);
|
||||
const nscoord clampedBSize =
|
||||
clampedBSizeWithoutEndBP + borderPadding.BEnd(wm);
|
||||
if (clampedBSize <= aReflowInput.AvailableBSize()) {
|
||||
// We actually fit after applying `max-size` so we should be
|
||||
// Overflow-Incomplete instead.
|
||||
bSize = clampedBSize;
|
||||
aState.mReflowStatus.SetOverflowIncomplete();
|
||||
} else {
|
||||
// We cannot fit after applying `max-size` with our block-end BP, so
|
||||
// we should draw it in our next continuation.
|
||||
bSize = clampedBSizeWithoutEndBP;
|
||||
}
|
||||
}
|
||||
finalSize.BSize(wm) = bSize;
|
||||
}
|
||||
nscoord bSize = autoBSize + borderPadding.BStartEnd(wm);
|
||||
if (MOZ_UNLIKELY(autoBSize > contentBSize &&
|
||||
bSize > aReflowInput.AvailableBSize() &&
|
||||
aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE)) {
|
||||
// Applying `min-size` made us overflow our available size.
|
||||
// Clamp it and report that we're Incomplete, or BreakBefore if we have
|
||||
// 'break-inside: avoid' that is applicable.
|
||||
bSize = aReflowInput.AvailableBSize();
|
||||
if (ShouldAvoidBreakInside(aReflowInput)) {
|
||||
aState.mReflowStatus.SetInlineLineBreakBeforeAndReset();
|
||||
} else {
|
||||
aState.mReflowStatus.SetIncomplete();
|
||||
}
|
||||
}
|
||||
finalSize.BSize(wm) = bSize;
|
||||
} else {
|
||||
NS_ASSERTION(aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE,
|
||||
"Shouldn't be incomplete if availableBSize is UNCONSTRAINED.");
|
||||
nscoord bSize = std::max(aState.mBCoord, aReflowInput.AvailableBSize());
|
||||
if (aReflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE) {
|
||||
// This should never happen, but it does. See bug 414255
|
||||
bSize = aState.mBCoord;
|
||||
}
|
||||
const nscoord maxBSize = aReflowInput.ComputedMaxBSize();
|
||||
if (maxBSize != NS_UNCONSTRAINEDSIZE &&
|
||||
aState.mConsumedBSize + bSize - borderPadding.BStart(wm) > maxBSize) {
|
||||
// Compute this fragment's block-size, with the max-block-size
|
||||
// constraint taken into consideration.
|
||||
const nscoord clampedBSizeWithoutEndBP =
|
||||
std::max(0, maxBSize - aState.mConsumedBSize) +
|
||||
borderPadding.BStart(wm);
|
||||
const nscoord clampedBSize =
|
||||
clampedBSizeWithoutEndBP + borderPadding.BEnd(wm);
|
||||
if (clampedBSize <= aReflowInput.AvailableBSize()) {
|
||||
// We actually fit after applying `max-size` so we should be
|
||||
// Overflow-Incomplete instead.
|
||||
bSize = clampedBSize;
|
||||
aState.mReflowStatus.SetOverflowIncomplete();
|
||||
} else {
|
||||
// We cannot fit after applying `max-size` with our block-end BP, so
|
||||
// we should draw it in our next continuation.
|
||||
bSize = clampedBSizeWithoutEndBP;
|
||||
}
|
||||
}
|
||||
finalSize.BSize(wm) = bSize;
|
||||
}
|
||||
|
||||
if (IsTrueOverflowContainer()) {
|
||||
@ -7572,7 +7558,7 @@ void nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
// (B) we are not honoring the document colors
|
||||
// (C) the force color adjust property is set to auto
|
||||
if (StaticPrefs::browser_display_permit_backplate() &&
|
||||
PresContext()->ForcingColors() && !IsComboboxControlFrame() &&
|
||||
PresContext()->ForcingColors() &&
|
||||
StyleText()->mForcedColorAdjust != StyleForcedColorAdjust::None) {
|
||||
backplateColor.emplace(GetBackplateColor(this));
|
||||
}
|
||||
@ -7970,8 +7956,7 @@ void nsBlockFrame::SetInitialChildList(ChildListID aListID,
|
||||
(pseudo == PseudoStyleType::scrolledContent &&
|
||||
!GetParent()->IsListControlFrame()) ||
|
||||
pseudo == PseudoStyleType::mozSVGText) &&
|
||||
!IsComboboxControlFrame() && !IsMathMLFrame() &&
|
||||
!IsColumnSetWrapperFrame() &&
|
||||
!IsMathMLFrame() && !IsColumnSetWrapperFrame() &&
|
||||
RefPtr<ComputedStyle>(GetFirstLetterStyle(PresContext())) != nullptr;
|
||||
NS_ASSERTION(haveFirstLetterStyle ==
|
||||
HasAnyStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE),
|
||||
|
@ -735,8 +735,7 @@ static bool IsPercentageAware(const nsIFrame* aFrame, WritingMode aWM) {
|
||||
disp->DisplayInside() == StyleDisplayInside::Table)) ||
|
||||
fType == LayoutFrameType::HTMLButtonControl ||
|
||||
fType == LayoutFrameType::GfxButtonControl ||
|
||||
fType == LayoutFrameType::FieldSet ||
|
||||
fType == LayoutFrameType::ComboboxDisplay) {
|
||||
fType == LayoutFrameType::FieldSet) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -77,10 +77,6 @@ input[contenteditable="true"][type="hidden"] {
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
||||
*|*::-moz-display-comboboxcontrol-frame {
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
option:read-write {
|
||||
user-select: text;
|
||||
}
|
||||
|
@ -99,7 +99,6 @@ CSS_ANON_BOX(buttonContent, ":-moz-button-content")
|
||||
CSS_ANON_BOX(cellContent, ":-moz-cell-content")
|
||||
CSS_ANON_BOX(dropDownList, ":-moz-dropdown-list")
|
||||
CSS_ANON_BOX(fieldsetContent, ":-moz-fieldset-content")
|
||||
CSS_ANON_BOX(mozDisplayComboboxControlFrame, ":-moz-display-comboboxcontrol-frame")
|
||||
CSS_ANON_BOX(htmlCanvasContent, ":-moz-html-canvas-content")
|
||||
|
||||
CSS_WRAPPER_ANON_BOX(inlineTable, ":-moz-inline-table")
|
||||
|
@ -259,40 +259,27 @@ select:-moz-select-list-box {
|
||||
}
|
||||
|
||||
select > button {
|
||||
inline-size: 12px;
|
||||
white-space: nowrap;
|
||||
position: static;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
appearance: auto;
|
||||
-moz-default-appearance: -moz-menulist-arrow-button;
|
||||
|
||||
/* Make sure to size correctly if the combobox has a non-auto height. */
|
||||
block-size: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
/* Draw the arrow in the select's color */
|
||||
color: inherit;
|
||||
|
||||
/*
|
||||
Make sure to align properly with the display frame. Note that we
|
||||
want the baseline of the combobox to match the baseline of the
|
||||
display frame, so the dropmarker is what gets the vertical-align.
|
||||
*/
|
||||
/* We don't want the button to grow the line-height */
|
||||
font: inherit;
|
||||
max-block-size: 100%;
|
||||
|
||||
/* Make sure to align properly with the display frame. Note that we want the
|
||||
* baseline of the combobox to match the baseline of the label, so the
|
||||
* dropmarker is what gets the vertical-align. */
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
*|*::-moz-display-comboboxcontrol-frame {
|
||||
content: inherit;
|
||||
select > label {
|
||||
display: inline-block;
|
||||
overflow: clip;
|
||||
color: unset;
|
||||
white-space: nowrap;
|
||||
text-align: unset;
|
||||
user-select: none;
|
||||
/* Make sure to size correctly if the combobox has a non-auto block-size. */
|
||||
block-size: 100%;
|
||||
/* Try to always display our own text */
|
||||
min-inline-size: max-content;
|
||||
box-sizing: border-box;
|
||||
line-height: -moz-block-height;
|
||||
}
|
||||
|
||||
option[label]::before {
|
||||
@ -668,16 +655,15 @@ input[type=file] > label {
|
||||
* inherit into the ':-moz-button-content' pseudo-element.
|
||||
*
|
||||
* <select>:
|
||||
* inherit into the ':-moz-display-comboboxcontrol-frame' pseudo-element and
|
||||
* the <optgroup>'s ':before' pseudo-element, which is where the label of
|
||||
* the <optgroup> gets displayed. The <option>s don't use anonymous boxes,
|
||||
* so they need no special rules.
|
||||
* inherit into the label and the <optgroup>'s ':before' pseudo-element,
|
||||
* which is where the label of the <optgroup> gets displayed. The <option>s
|
||||
* don't use anonymous boxes, so they need no special rules.
|
||||
*/
|
||||
::placeholder,
|
||||
::-moz-text-control-editing-root,
|
||||
*|*::-moz-button-content,
|
||||
*|*::-moz-display-comboboxcontrol-frame,
|
||||
optgroup:before {
|
||||
select > label,
|
||||
optgroup::before {
|
||||
unicode-bidi: inherit;
|
||||
text-overflow: inherit;
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>CSS Test Reference</title>
|
||||
<style>
|
||||
select, input, button { height: 100px; }
|
||||
</style>
|
||||
<div>
|
||||
<select><option>Select</option></select>
|
||||
<button>Button</button>
|
||||
<input type="button" value="Input">
|
||||
</div>
|
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>min-height and height both trigger same rendering for select and buttons by default</title>
|
||||
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
|
||||
<link rel="author" title="Mozilla" href="https://mozilla.org">
|
||||
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1362907">
|
||||
<link rel="match" href="select-button-min-height-001-ref.html">
|
||||
<style>
|
||||
select, input, button { min-height: 100px; }
|
||||
</style>
|
||||
<div>
|
||||
<select><option>Select</option></select>
|
||||
<button>Button</button>
|
||||
<input type="button" value="Input">
|
||||
</div>
|
@ -4,27 +4,12 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ThemeCocoa.h"
|
||||
|
||||
#include "cocoa/MacThemeGeometryType.h"
|
||||
#include "gfxPlatform.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/gfx/Helpers.h"
|
||||
#include "mozilla/LookAndFeel.h"
|
||||
#include "mozilla/ServoStyleConsts.h"
|
||||
|
||||
namespace mozilla::widget {
|
||||
|
||||
LayoutDeviceIntSize ThemeCocoa::GetMinimumWidgetSize(
|
||||
nsPresContext* aPresContext, nsIFrame* aFrame,
|
||||
StyleAppearance aAppearance) {
|
||||
if (aAppearance == StyleAppearance::MozMenulistArrowButton) {
|
||||
auto size =
|
||||
GetScrollbarSize(aPresContext, StyleScrollbarWidth::Auto, Overlay::No);
|
||||
return {size, size};
|
||||
}
|
||||
return Theme::GetMinimumWidgetSize(aPresContext, aFrame, aAppearance);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ThemeCocoa::DrawWidgetBackground(gfxContext* aContext, nsIFrame* aFrame,
|
||||
StyleAppearance aAppearance,
|
||||
|
@ -9,8 +9,6 @@
|
||||
|
||||
#include "Theme.h"
|
||||
|
||||
#include "ScrollbarDrawingCocoa.h"
|
||||
|
||||
namespace mozilla::widget {
|
||||
|
||||
class ThemeCocoa : public Theme {
|
||||
@ -18,10 +16,6 @@ class ThemeCocoa : public Theme {
|
||||
explicit ThemeCocoa(UniquePtr<ScrollbarDrawing>&& aScrollbarDrawing)
|
||||
: Theme(std::move(aScrollbarDrawing)) {}
|
||||
|
||||
LayoutDeviceIntSize GetMinimumWidgetSize(
|
||||
nsPresContext* aPresContext, nsIFrame* aFrame,
|
||||
StyleAppearance aAppearance) override;
|
||||
|
||||
NS_IMETHOD DrawWidgetBackground(gfxContext* aContext, nsIFrame*,
|
||||
StyleAppearance, const nsRect& aRect,
|
||||
const nsRect& aDirtyRect,
|
||||
|
@ -2529,7 +2529,6 @@ STATIC_ATOMS = [
|
||||
InheritingAnonBoxAtom("AnonBox_cellContent", ":-moz-cell-content"),
|
||||
InheritingAnonBoxAtom("AnonBox_dropDownList", ":-moz-dropdown-list"),
|
||||
InheritingAnonBoxAtom("AnonBox_fieldsetContent", ":-moz-fieldset-content"),
|
||||
InheritingAnonBoxAtom("AnonBox_mozDisplayComboboxControlFrame", ":-moz-display-comboboxcontrol-frame"),
|
||||
InheritingAnonBoxAtom("AnonBox_htmlCanvasContent", ":-moz-html-canvas-content"),
|
||||
InheritingAnonBoxAtom("AnonBox_inlineTable", ":-moz-inline-table"),
|
||||
InheritingAnonBoxAtom("AnonBox_table", ":-moz-table"),
|
||||
|
Loading…
Reference in New Issue
Block a user