From fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 03:14:29 +0200 Subject: Merging upstream version 125.0.1. Signed-off-by: Daniel Baumann --- layout/forms/HTMLSelectEventListener.cpp | 2 +- layout/forms/crashtests/crashtests.list | 2 +- layout/forms/moz.build | 1 - layout/forms/nsButtonFrameRenderer.cpp | 471 ----------------- layout/forms/nsButtonFrameRenderer.h | 83 --- layout/forms/nsComboboxControlFrame.cpp | 600 ++++------------------ layout/forms/nsComboboxControlFrame.h | 128 +---- layout/forms/nsFieldSetFrame.cpp | 10 - layout/forms/nsGfxButtonControlFrame.cpp | 4 - layout/forms/nsGfxButtonControlFrame.h | 23 +- layout/forms/nsHTMLButtonControlFrame.cpp | 175 ++++++- layout/forms/nsHTMLButtonControlFrame.h | 15 +- layout/forms/nsNumberControlFrame.cpp | 25 +- layout/forms/test/test_bug536567_perwindowpb.html | 2 +- layout/forms/test/test_bug935876.html | 14 +- 15 files changed, 302 insertions(+), 1253 deletions(-) delete mode 100644 layout/forms/nsButtonFrameRenderer.cpp delete mode 100644 layout/forms/nsButtonFrameRenderer.h (limited to 'layout/forms') diff --git a/layout/forms/HTMLSelectEventListener.cpp b/layout/forms/HTMLSelectEventListener.cpp index 4f6b1e3561..8c74c57cc0 100644 --- a/layout/forms/HTMLSelectEventListener.cpp +++ b/layout/forms/HTMLSelectEventListener.cpp @@ -40,7 +40,7 @@ static bool IsOptionInteractivelySelectable(HTMLSelectElement& aSelect, // options in a display: contents subtree interactively. // test_select_key_navigation_bug1498769.html tests for this and should // probably be changed (and this loop removed) or alternatively - // SelectChild.jsm should be changed to match it. + // SelectChild.sys.mjs should be changed to match it. for (Element* el = &aOption; el && el != &aSelect; el = el->GetParentElement()) { if (Servo_Element_IsDisplayContents(el)) { diff --git a/layout/forms/crashtests/crashtests.list b/layout/forms/crashtests/crashtests.list index da43270ba6..5bc0fa7746 100644 --- a/layout/forms/crashtests/crashtests.list +++ b/layout/forms/crashtests/crashtests.list @@ -68,7 +68,7 @@ load 1405830.html load 1418477.html load 1432853.html asserts(1-4) load 1460787-1.html -load 1464165-1.html +asserts(1-4) load 1464165-1.html # Big sizes. load 1471157.html load 1488219.html load 1600207.html diff --git a/layout/forms/moz.build b/layout/forms/moz.build index 838a17bf92..93ec0a430c 100644 --- a/layout/forms/moz.build +++ b/layout/forms/moz.build @@ -19,7 +19,6 @@ EXPORTS += [ UNIFIED_SOURCES += [ "HTMLSelectEventListener.cpp", "ListMutationObserver.cpp", - "nsButtonFrameRenderer.cpp", "nsCheckboxRadioFrame.cpp", "nsColorControlFrame.cpp", "nsComboboxControlFrame.cpp", diff --git a/layout/forms/nsButtonFrameRenderer.cpp b/layout/forms/nsButtonFrameRenderer.cpp deleted file mode 100644 index 598610cf72..0000000000 --- a/layout/forms/nsButtonFrameRenderer.cpp +++ /dev/null @@ -1,471 +0,0 @@ -/* -*- 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 "nsButtonFrameRenderer.h" -#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" -#include "nsITheme.h" -#include "nsIFrame.h" -#include "mozilla/dom/Element.h" - -#include "gfxUtils.h" -#include "mozilla/layers/RenderRootStateManager.h" - -using namespace mozilla; -using namespace mozilla::image; -using namespace mozilla::layers; - -namespace mozilla { -class nsDisplayButtonBoxShadowOuter; -class nsDisplayButtonBorder; -class nsDisplayButtonForeground; -} // namespace mozilla - -nsButtonFrameRenderer::nsButtonFrameRenderer() : mFrame(nullptr) { - MOZ_COUNT_CTOR(nsButtonFrameRenderer); -} - -nsButtonFrameRenderer::~nsButtonFrameRenderer() { - MOZ_COUNT_DTOR(nsButtonFrameRenderer); -} - -void nsButtonFrameRenderer::SetFrame(nsIFrame* aFrame, - nsPresContext* aPresContext) { - mFrame = aFrame; - ReResolveStyles(aPresContext); -} - -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) { - if (!mFrame->StyleEffects()->mBoxShadow.IsEmpty()) { - aBackground->AppendNewToTop(aBuilder, - GetFrame()); - } - - nsRect buttonRect = - mFrame->GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(mFrame); - - const AppendedBackgroundType result = - nsDisplayBackgroundImage::AppendBackgroundItemsToTop( - aBuilder, mFrame, buttonRect, aBackground); - if (result == AppendedBackgroundType::None) { - aBuilder->BuildCompositorHitTestInfoIfNeeded(GetFrame(), aBackground); - } - - aBackground->AppendNewToTop(aBuilder, GetFrame(), - this); - - // Only display focus rings if we actually have them. Since at most one - // button would normally display a focus ring, most buttons won't have them. - if (mInnerFocusStyle && mInnerFocusStyle->StyleBorder()->HasBorder() && - mFrame->IsThemed() && - mFrame->PresContext()->Theme()->ThemeWantsButtonInnerFocusRing()) { - aForeground->AppendNewToTop(aBuilder, GetFrame(), - this); - } - return NS_OK; -} - -void nsButtonFrameRenderer::GetButtonInnerFocusRect(const nsRect& aRect, - nsRect& aResult) { - aResult = aRect; - aResult.Deflate(mFrame->GetUsedBorderAndPadding()); - - if (mInnerFocusStyle) { - nsMargin innerFocusPadding(0, 0, 0, 0); - mInnerFocusStyle->StylePadding()->GetPadding(innerFocusPadding); - - nsMargin framePadding = mFrame->GetUsedPadding(); - - innerFocusPadding.top = std::min(innerFocusPadding.top, framePadding.top); - innerFocusPadding.right = - std::min(innerFocusPadding.right, framePadding.right); - innerFocusPadding.bottom = - std::min(innerFocusPadding.bottom, framePadding.bottom); - innerFocusPadding.left = - std::min(innerFocusPadding.left, framePadding.left); - - aResult.Inflate(innerFocusPadding); - } -} - -ImgDrawResult nsButtonFrameRenderer::PaintInnerFocusBorder( - nsDisplayListBuilder* aBuilder, nsPresContext* aPresContext, - gfxContext& aRenderingContext, const nsRect& aDirtyRect, - const nsRect& aRect) { - // we draw the -moz-focus-inner border just inside the button's - // normal border and padding, to match Windows themes. - - nsRect rect; - - PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages() - ? PaintBorderFlags::SyncDecodeImages - : PaintBorderFlags(); - - ImgDrawResult result = ImgDrawResult::SUCCESS; - - if (mInnerFocusStyle) { - GetButtonInnerFocusRect(aRect, rect); - - result &= - nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame, - aDirtyRect, rect, mInnerFocusStyle, flags); - } - - return result; -} - -Maybe -nsButtonFrameRenderer::CreateInnerFocusBorderRenderer( - nsDisplayListBuilder* aBuilder, nsPresContext* aPresContext, - gfxContext* aRenderingContext, const nsRect& aDirtyRect, - const nsRect& aRect, bool* aBorderIsEmpty) { - if (mInnerFocusStyle) { - nsRect rect; - GetButtonInnerFocusRect(aRect, rect); - - gfx::DrawTarget* dt = - aRenderingContext ? aRenderingContext->GetDrawTarget() : nullptr; - return nsCSSRendering::CreateBorderRenderer( - aPresContext, dt, mFrame, aDirtyRect, rect, mInnerFocusStyle, - aBorderIsEmpty); - } - - return Nothing(); -} - -ImgDrawResult nsButtonFrameRenderer::PaintBorder(nsDisplayListBuilder* aBuilder, - nsPresContext* aPresContext, - gfxContext& aRenderingContext, - const nsRect& aDirtyRect, - const nsRect& aRect) { - // get the button rect this is inside the focus and outline rects - nsRect buttonRect = aRect; - ComputedStyle* context = mFrame->Style(); - - PaintBorderFlags borderFlags = aBuilder->ShouldSyncDecodeImages() - ? PaintBorderFlags::SyncDecodeImages - : PaintBorderFlags(); - - nsCSSRendering::PaintBoxShadowInner(aPresContext, aRenderingContext, mFrame, - buttonRect); - - ImgDrawResult result = - nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame, - aDirtyRect, buttonRect, context, borderFlags); - - return result; -} - -/** - * Call this when styles change - */ -void nsButtonFrameRenderer::ReResolveStyles(nsPresContext* aPresContext) { - // get all the styles - ServoStyleSet* styleSet = aPresContext->StyleSet(); - - // get styles assigned to -moz-focus-inner (ie dotted border on Windows) - mInnerFocusStyle = styleSet->ProbePseudoElementStyle( - *mFrame->GetContent()->AsElement(), PseudoStyleType::mozFocusInner, - nullptr, mFrame->Style()); -} - -ComputedStyle* nsButtonFrameRenderer::GetComputedStyle(int32_t aIndex) const { - switch (aIndex) { - case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX: - return mInnerFocusStyle; - default: - return nullptr; - } -} - -void nsButtonFrameRenderer::SetComputedStyle(int32_t aIndex, - ComputedStyle* aComputedStyle) { - switch (aIndex) { - case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX: - mInnerFocusStyle = aComputedStyle; - break; - } -} - -namespace mozilla { - -class nsDisplayButtonBoxShadowOuter : public nsPaintedDisplayItem { - public: - nsDisplayButtonBoxShadowOuter(nsDisplayListBuilder* aBuilder, - nsIFrame* aFrame) - : nsPaintedDisplayItem(aBuilder, aFrame) { - MOZ_COUNT_CTOR(nsDisplayButtonBoxShadowOuter); - } - MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayButtonBoxShadowOuter) - - virtual bool CreateWebRenderCommands( - mozilla::wr::DisplayListBuilder& aBuilder, - mozilla::wr::IpcResourceUpdateQueue& aResources, - const StackingContextHelper& aSc, - mozilla::layers::RenderRootStateManager* aManager, - nsDisplayListBuilder* aDisplayListBuilder) override; - - bool CanBuildWebRenderDisplayItems(); - - virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override; - virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, - bool* aSnap) const override; - NS_DISPLAY_DECL_NAME("ButtonBoxShadowOuter", TYPE_BUTTON_BOX_SHADOW_OUTER) -}; - -nsRect nsDisplayButtonBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder, - bool* aSnap) const { - *aSnap = false; - return mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame(); -} - -void nsDisplayButtonBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder, - gfxContext* aCtx) { - nsRect frameRect = nsRect(ToReferenceFrame(), mFrame->GetSize()); - - nsCSSRendering::PaintBoxShadowOuter(mFrame->PresContext(), *aCtx, mFrame, - frameRect, GetPaintRect(aBuilder, aCtx)); -} - -bool nsDisplayButtonBoxShadowOuter::CanBuildWebRenderDisplayItems() { - // FIXME(emilio): Is this right? That doesn't make much sense. - if (mFrame->StyleEffects()->mBoxShadow.IsEmpty()) { - return false; - } - - bool hasBorderRadius; - bool nativeTheme = - nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius); - - // We don't support native themed things yet like box shadows around - // input buttons. - return !nativeTheme; -} - -bool nsDisplayButtonBoxShadowOuter::CreateWebRenderCommands( - mozilla::wr::DisplayListBuilder& aBuilder, - mozilla::wr::IpcResourceUpdateQueue& aResources, - const StackingContextHelper& aSc, - mozilla::layers::RenderRootStateManager* aManager, - nsDisplayListBuilder* aDisplayListBuilder) { - if (!CanBuildWebRenderDisplayItems()) { - return false; - } - int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel(); - nsRect shadowRect = nsRect(ToReferenceFrame(), mFrame->GetSize()); - LayoutDeviceRect deviceBox = - LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel); - wr::LayoutRect deviceBoxRect = wr::ToLayoutRect(deviceBox); - - bool dummy; - LayoutDeviceRect clipRect = LayoutDeviceRect::FromAppUnits( - GetBounds(aDisplayListBuilder, &dummy), appUnitsPerDevPixel); - wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect); - - bool hasBorderRadius; - Unused << nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius); - - LayoutDeviceSize zeroSize; - wr::BorderRadius borderRadius = - wr::ToBorderRadius(zeroSize, zeroSize, zeroSize, zeroSize); - if (hasBorderRadius) { - gfx::RectCornerRadii borderRadii; - hasBorderRadius = nsCSSRendering::GetBorderRadii(shadowRect, shadowRect, - mFrame, borderRadii); - if (hasBorderRadius) { - borderRadius = wr::ToBorderRadius(borderRadii); - } - } - - const Span shadows = - mFrame->StyleEffects()->mBoxShadow.AsSpan(); - MOZ_ASSERT(!shadows.IsEmpty()); - - for (const StyleBoxShadow& shadow : Reversed(shadows)) { - if (shadow.inset) { - continue; - } - float blurRadius = - float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel); - gfx::DeviceColor shadowColor = - ToDeviceColor(nsCSSRendering::GetShadowColor(shadow.base, mFrame, 1.0)); - - LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits( - nsPoint(shadow.base.horizontal.ToAppUnits(), - shadow.base.vertical.ToAppUnits()), - appUnitsPerDevPixel); - - float spreadRadius = - float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel); - - aBuilder.PushBoxShadow(deviceBoxRect, deviceClipRect, !BackfaceIsHidden(), - deviceBoxRect, wr::ToLayoutVector2D(shadowOffset), - wr::ToColorF(shadowColor), blurRadius, spreadRadius, - borderRadius, wr::BoxShadowClipMode::Outset); - } - return true; -} - -class nsDisplayButtonBorder final : public nsPaintedDisplayItem { - public: - nsDisplayButtonBorder(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, - nsButtonFrameRenderer* aRenderer) - : nsPaintedDisplayItem(aBuilder, aFrame), mBFR(aRenderer) { - MOZ_COUNT_CTOR(nsDisplayButtonBorder); - } - MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayButtonBorder) - - virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, - HitTestState* aState, - nsTArray* aOutFrames) override { - aOutFrames->AppendElement(mFrame); - } - virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override; - virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, - bool* aSnap) const override; - virtual bool CreateWebRenderCommands( - mozilla::wr::DisplayListBuilder& aBuilder, - mozilla::wr::IpcResourceUpdateQueue& aResources, - const StackingContextHelper& aSc, - mozilla::layers::RenderRootStateManager* aManager, - nsDisplayListBuilder* aDisplayListBuilder) override; - NS_DISPLAY_DECL_NAME("ButtonBorderBackground", TYPE_BUTTON_BORDER_BACKGROUND) - private: - nsButtonFrameRenderer* mBFR; -}; - -bool nsDisplayButtonBorder::CreateWebRenderCommands( - mozilla::wr::DisplayListBuilder& aBuilder, - mozilla::wr::IpcResourceUpdateQueue& aResources, - const StackingContextHelper& aSc, - mozilla::layers::RenderRootStateManager* aManager, - nsDisplayListBuilder* aDisplayListBuilder) { - // This is really a combination of paint box shadow inner + - // paint border. - aBuilder.StartGroup(this); - const nsRect buttonRect = nsRect(ToReferenceFrame(), mFrame->GetSize()); - bool snap; - nsRect visible = GetBounds(aDisplayListBuilder, &snap); - nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands( - aBuilder, aSc, visible, mFrame, buttonRect); - - bool borderIsEmpty = false; - Maybe br = nsCSSRendering::CreateBorderRenderer( - mFrame->PresContext(), nullptr, mFrame, nsRect(), - nsRect(ToReferenceFrame(), mFrame->GetSize()), mFrame->Style(), - &borderIsEmpty, mFrame->GetSkipSides()); - if (!br) { - if (borderIsEmpty) { - aBuilder.FinishGroup(); - } else { - aBuilder.CancelGroup(true); - } - - return borderIsEmpty; - } - - br->CreateWebRenderCommands(this, aBuilder, aResources, aSc); - aBuilder.FinishGroup(); - return true; -} - -void nsDisplayButtonBorder::Paint(nsDisplayListBuilder* aBuilder, - gfxContext* aCtx) { - NS_ASSERTION(mFrame, "No frame?"); - nsPresContext* pc = mFrame->PresContext(); - nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize()); - - // draw the border and background inside the focus and outline borders - Unused << mBFR->PaintBorder(aBuilder, pc, *aCtx, GetPaintRect(aBuilder, aCtx), - r); -} - -nsRect nsDisplayButtonBorder::GetBounds(nsDisplayListBuilder* aBuilder, - bool* aSnap) const { - *aSnap = false; - return aBuilder->IsForEventDelivery() - ? nsRect(ToReferenceFrame(), mFrame->GetSize()) - : mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame(); -} - -class nsDisplayButtonForeground final : public nsPaintedDisplayItem { - public: - nsDisplayButtonForeground(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, - nsButtonFrameRenderer* aRenderer) - : nsPaintedDisplayItem(aBuilder, aFrame), mBFR(aRenderer) { - MOZ_COUNT_CTOR(nsDisplayButtonForeground); - } - MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayButtonForeground) - - void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override; - bool CreateWebRenderCommands( - mozilla::wr::DisplayListBuilder& aBuilder, - mozilla::wr::IpcResourceUpdateQueue& aResources, - const StackingContextHelper& aSc, - mozilla::layers::RenderRootStateManager* aManager, - nsDisplayListBuilder* aDisplayListBuilder) override; - NS_DISPLAY_DECL_NAME("ButtonForeground", TYPE_BUTTON_FOREGROUND) - private: - nsButtonFrameRenderer* mBFR; -}; - -void nsDisplayButtonForeground::Paint(nsDisplayListBuilder* aBuilder, - gfxContext* aCtx) { - nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize()); - - // Draw the -moz-focus-inner border - Unused << mBFR->PaintInnerFocusBorder(aBuilder, mFrame->PresContext(), *aCtx, - GetPaintRect(aBuilder, aCtx), r); -} - -bool nsDisplayButtonForeground::CreateWebRenderCommands( - mozilla::wr::DisplayListBuilder& aBuilder, - mozilla::wr::IpcResourceUpdateQueue& aResources, - const StackingContextHelper& aSc, - mozilla::layers::RenderRootStateManager* aManager, - nsDisplayListBuilder* aDisplayListBuilder) { - Maybe br; - bool borderIsEmpty = false; - bool dummy; - nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize()); - br = mBFR->CreateInnerFocusBorderRenderer( - aDisplayListBuilder, mFrame->PresContext(), nullptr, - GetBounds(aDisplayListBuilder, &dummy), r, &borderIsEmpty); - - if (!br) { - return borderIsEmpty; - } - - aBuilder.StartGroup(this); - br->CreateWebRenderCommands(this, aBuilder, aResources, aSc); - aBuilder.FinishGroup(); - - return true; -} - -} // namespace mozilla diff --git a/layout/forms/nsButtonFrameRenderer.h b/layout/forms/nsButtonFrameRenderer.h deleted file mode 100644 index ea5a9cdea4..0000000000 --- a/layout/forms/nsButtonFrameRenderer.h +++ /dev/null @@ -1,83 +0,0 @@ -/* -*- 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/. */ - -#ifndef nsButtonFrameRenderer_h___ -#define nsButtonFrameRenderer_h___ - -#include "nsMargin.h" -#include "nsCSSRenderingBorders.h" - -class gfxContext; -class nsIFrame; -class nsPresContext; -struct nsRect; - -namespace mozilla { -class nsDisplayList; -class nsDisplayListBuilder; -} // namespace mozilla - -#define NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX 0 -#define NS_BUTTON_RENDERER_LAST_CONTEXT_INDEX \ - NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX - -class nsButtonFrameRenderer { - using nsDisplayList = mozilla::nsDisplayList; - using nsDisplayListBuilder = mozilla::nsDisplayListBuilder; - - typedef mozilla::image::ImgDrawResult ImgDrawResult; - typedef mozilla::ComputedStyle ComputedStyle; - - public: - nsButtonFrameRenderer(); - ~nsButtonFrameRenderer(); - - /** - * Create display list items for the button - */ - nsresult DisplayButton(nsDisplayListBuilder* aBuilder, - nsDisplayList* aBackground, - nsDisplayList* aForeground); - - ImgDrawResult PaintInnerFocusBorder(nsDisplayListBuilder* aBuilder, - nsPresContext* aPresContext, - gfxContext& aRenderingContext, - const nsRect& aDirtyRect, - const nsRect& aRect); - - mozilla::Maybe CreateInnerFocusBorderRenderer( - nsDisplayListBuilder* aBuilder, nsPresContext* aPresContext, - gfxContext* aRenderingContext, const nsRect& aDirtyRect, - const nsRect& aRect, bool* aBorderIsEmpty); - - ImgDrawResult PaintBorder(nsDisplayListBuilder* aBuilder, - nsPresContext* aPresContext, - gfxContext& aRenderingContext, - const nsRect& aDirtyRect, const nsRect& aRect); - - 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; - void SetComputedStyle(int32_t aIndex, ComputedStyle* aComputedStyle); - void ReResolveStyles(nsPresContext* aPresContext); - - nsIFrame* GetFrame(); - - private: - // cached style for optional inner focus outline (used on Windows). - RefPtr mInnerFocusStyle; - - nsIFrame* mFrame; -}; - -#endif diff --git a/layout/forms/nsComboboxControlFrame.cpp b/layout/forms/nsComboboxControlFrame.cpp index a935e3c761..1d4ff15b4f 100644 --- a/layout/forms/nsComboboxControlFrame.cpp +++ b/layout/forms/nsComboboxControlFrame.cpp @@ -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()} - - //-------------------------------------------------------------- - nsComboboxControlFrame::~nsComboboxControlFrame() { - REFLOW_COUNTER_DUMP("nsCCF"); -} + : nsHTMLButtonControlFrame(aStyle, aPresContext, kClassID) {} -//-------------------------------------------------------------- +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 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 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& aResult) { - aResult.AppendElement(OwnedAnonBox(mDisplayFrame)); + return nsHTMLButtonControlFrame::HandleEvent(aPresContext, aEvent, + aEventStatus); } nsresult nsComboboxControlFrame::CreateAnonymousContent( nsTArray& 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. - - // 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 + dom::Document* doc = mContent->OwnerDoc(); + mDisplayLabel = doc->CreateHTMLElement(nsGkAtoms::label); - nsNodeInfoManager* nimgr = mContent->NodeInfo()->NodeInfoManager(); - - mDisplayContent = new (nimgr) nsTextNode(nimgr); + { + RefPtr 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 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& 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!"); - 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); - } + 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!"); - state.SetComputedISize(std::max(0, computedISize)); + ReflowInput state(aReflowInput); + 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); - - // 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 = - styleSet->ResolveInheritingAnonymousBoxStyle( - PseudoStyleType::mozDisplayComboboxControlFrame, mComputedStyle); - - RefPtr 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); +} // namespace mozilla - 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* aLists) const { - nsBlockFrame::GetChildLists(aLists); -} - -void nsComboboxControlFrame::SetInitialChildList(ChildListID aListID, - nsFrameList&& aChildList) { - for (nsIFrame* f : aChildList) { - MOZ_ASSERT(f->GetParent() == this, "Unexpected parent"); - nsCOMPtr 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(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(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)); } diff --git a/layout/forms/nsComboboxControlFrame.h b/layout/forms/nsComboboxControlFrame.h index e878c0ebe3..4daa636f1a 100644 --- a/layout/forms/nsComboboxControlFrame.h +++ b/layout/forms/nsComboboxControlFrame.h @@ -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& 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; + void Destroy(DestroyContext&) final; #ifdef DEBUG_FRAME_DUMP - nsresult GetFrameName(nsAString& aResult) const final; + nsresult GetFrameName(nsAString& aResult) const final { + return MakeFrameName(u"ComboboxControl"_ns, aResult); + } #endif - void Destroy(DestroyContext&) final; - - void SetInitialChildList(ChildListID aListID, nsFrameList&& aChildList) final; - const nsFrameList& GetChildList(ChildListID aListID) const final; - void GetChildLists(nsTArray* aLists) const final; - - nsContainerFrame* GetContentInsertionFrame() final; - - // Return the dropdown and display frame. - void AppendDirectlyOwnedAnonBoxes(nsTArray& aResult) final; // 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 mDisplayContent; // Anonymous content used to display the - // current selection - RefPtr 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 mDisplayLabel; // Anonymous content for the label + RefPtr mButtonContent; // Anonymous content for the button nsRevocableEventPtr 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 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 diff --git a/layout/forms/nsFieldSetFrame.cpp b/layout/forms/nsFieldSetFrame.cpp index 03781da8bd..c96b293e82 100644 --- a/layout/forms/nsFieldSetFrame.cpp +++ b/layout/forms/nsFieldSetFrame.cpp @@ -785,11 +785,6 @@ void nsFieldSetFrame::Reflow(nsPresContext* aPresContext, void nsFieldSetFrame::SetInitialChildList(ChildListID aListID, nsFrameList&& aChildList) { nsContainerFrame::SetInitialChildList(aListID, std::move(aChildList)); - if (nsBlockFrame* legend = do_QueryFrame(GetLegend())) { - // A rendered legend always establish a new formatting context. - // https://html.spec.whatwg.org/multipage/rendering.html#rendered-legend - legend->AddStateBits(NS_BLOCK_STATIC_BFC); - } MOZ_ASSERT( aListID != FrameChildListID::Principal || GetInner() || GetLegend(), "Setting principal child list should populate our inner frame " @@ -816,11 +811,6 @@ void nsFieldSetFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, nsContainerFrame::InsertFrames(aListID, aPrevFrame, aPrevFrameLine, std::move(aFrameList)); MOZ_ASSERT(GetLegend()); - if (nsBlockFrame* legend = do_QueryFrame(GetLegend())) { - // A rendered legend always establish a new formatting context. - // https://html.spec.whatwg.org/multipage/rendering.html#rendered-legend - legend->AddStateBits(NS_BLOCK_STATIC_BFC); - } } #ifdef DEBUG diff --git a/layout/forms/nsGfxButtonControlFrame.cpp b/layout/forms/nsGfxButtonControlFrame.cpp index 37aa996c27..b250030e13 100644 --- a/layout/forms/nsGfxButtonControlFrame.cpp +++ b/layout/forms/nsGfxButtonControlFrame.cpp @@ -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) { diff --git a/layout/forms/nsGfxButtonControlFrame.h b/layout/forms/nsGfxButtonControlFrame.h index 32d4689559..38c30590ab 100644 --- a/layout/forms/nsGfxButtonControlFrame.h +++ b/layout/forms/nsGfxButtonControlFrame.h @@ -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& aElements) override; - virtual void AppendAnonymousContentTo(nsTArray& aElements, - uint32_t aFilter) override; + nsresult CreateAnonymousContent(nsTArray& aElements) override; + void AppendAnonymousContentTo(nsTArray& 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; diff --git a/layout/forms/nsHTMLButtonControlFrame.cpp b/layout/forms/nsHTMLButtonControlFrame.cpp index 7a599f093b..cb24ccbfb9 100644 --- a/layout/forms/nsHTMLButtonControlFrame.cpp +++ b/layout/forms/nsHTMLButtonControlFrame.cpp @@ -10,15 +10,17 @@ #include "mozilla/PresShell.h" #include "nsIFrameInlines.h" #include "nsContainerFrame.h" +#include "nsPresContextInlines.h" #include "nsIFormControlFrame.h" #include "nsPresContext.h" #include "nsLayoutUtils.h" #include "nsGkAtoms.h" -#include "nsButtonFrameRenderer.h" #include "nsDisplayList.h" +#include "nsCSSRendering.h" #include using namespace mozilla; +using namespace mozilla::image; nsContainerFrame* NS_NewHTMLButtonControlFrame(PresShell* aPresShell, ComputedStyle* aStyle) { @@ -39,7 +41,13 @@ void nsHTMLButtonControlFrame::Init(nsIContent* aContent, nsContainerFrame* aParent, nsIFrame* aPrevInFlow) { nsContainerFrame::Init(aContent, aParent, aPrevInFlow); - mRenderer.SetFrame(this, PresContext()); + + // get all the styles + ServoStyleSet* styleSet = PresContext()->StyleSet(); + + // Get styles assigned to -moz-focus-inner (i.e. dotted border on Windows) + mInnerFocusStyle = styleSet->ProbePseudoElementStyle( + *mContent->AsElement(), PseudoStyleType::mozFocusInner, nullptr, Style()); } NS_QUERYFRAME_HEAD(nsHTMLButtonControlFrame) @@ -57,8 +65,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; } @@ -67,14 +75,125 @@ nsresult nsHTMLButtonControlFrame::HandleEvent(nsPresContext* aPresContext, return nsIFrame::HandleEvent(aPresContext, aEvent, aEventStatus); } -bool nsHTMLButtonControlFrame::ShouldClipPaintingToBorderBox() { +bool nsHTMLButtonControlFrame::ShouldClipPaintingToBorderBox() const { // FIXME(emilio): probably should account for per-axis clipping... return StyleDisplay()->mOverflowX != StyleOverflow::Visible; } +namespace mozilla { + +class nsDisplayButtonForeground final : public nsPaintedDisplayItem { + public: + nsDisplayButtonForeground(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) + : nsPaintedDisplayItem(aBuilder, aFrame) { + MOZ_COUNT_CTOR(nsDisplayButtonForeground); + } + MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayButtonForeground) + + void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override; + bool CreateWebRenderCommands( + mozilla::wr::DisplayListBuilder& aBuilder, + mozilla::wr::IpcResourceUpdateQueue& aResources, + const StackingContextHelper& aSc, + mozilla::layers::RenderRootStateManager* aManager, + nsDisplayListBuilder* aDisplayListBuilder) override; + NS_DISPLAY_DECL_NAME("ButtonForeground", TYPE_BUTTON_FOREGROUND) +}; + +void nsDisplayButtonForeground::Paint(nsDisplayListBuilder* aBuilder, + gfxContext* aCtx) { + static_cast(mFrame)->PaintInnerFocusBorder( + aBuilder, *aCtx, GetPaintRect(aBuilder, aCtx), + nsRect(ToReferenceFrame(), mFrame->GetSize())); +} + +bool nsDisplayButtonForeground::CreateWebRenderCommands( + mozilla::wr::DisplayListBuilder& aBuilder, + mozilla::wr::IpcResourceUpdateQueue& aResources, + const StackingContextHelper& aSc, + mozilla::layers::RenderRootStateManager* aManager, + nsDisplayListBuilder* aDisplayListBuilder) { + bool borderIsEmpty = false; + bool dummy; + Maybe br = + static_cast(mFrame) + ->CreateInnerFocusBorderRenderer( + aDisplayListBuilder, nullptr, + GetBounds(aDisplayListBuilder, &dummy), + nsRect(ToReferenceFrame(), mFrame->GetSize()), &borderIsEmpty); + + if (!br) { + return borderIsEmpty; + } + + aBuilder.StartGroup(this); + br->CreateWebRenderCommands(this, aBuilder, aResources, aSc); + aBuilder.FinishGroup(); + + return true; +} + +} // namespace mozilla + +static nsRect GetButtonInnerFocusRect(const nsIFrame* aFrame, + const ComputedStyle& aFocusStyle, + const nsRect& aRect) { + nsRect result = aRect; + result.Deflate(aFrame->GetUsedBorderAndPadding()); + + nsMargin innerFocusPadding; + aFocusStyle.StylePadding()->GetPadding(innerFocusPadding); + + nsMargin framePadding = aFrame->GetUsedPadding(); + innerFocusPadding.top = std::min(innerFocusPadding.top, framePadding.top); + innerFocusPadding.right = + std::min(innerFocusPadding.right, framePadding.right); + innerFocusPadding.bottom = + std::min(innerFocusPadding.bottom, framePadding.bottom); + innerFocusPadding.left = std::min(innerFocusPadding.left, framePadding.left); + + result.Inflate(innerFocusPadding); + return result; +} + +void nsHTMLButtonControlFrame::PaintInnerFocusBorder( + nsDisplayListBuilder* aBuilder, gfxContext& aRenderingContext, + const nsRect& aDirtyRect, const nsRect& aRect) { + if (!mInnerFocusStyle) { + return; + } + + // we draw the -moz-focus-inner border just inside the button's + // normal border and padding, to match Windows themes. + PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages() + ? PaintBorderFlags::SyncDecodeImages + : PaintBorderFlags(); + + nsRect rect = GetButtonInnerFocusRect(this, *mInnerFocusStyle, aRect); + // We don't paint border images here, so the ImgDrawResult is useless. + Unused << nsCSSRendering::PaintBorder(PresContext(), aRenderingContext, this, + aDirtyRect, rect, mInnerFocusStyle, + flags); +} + +Maybe +nsHTMLButtonControlFrame::CreateInnerFocusBorderRenderer( + nsDisplayListBuilder* aBuilder, gfxContext* aRenderingContext, + const nsRect& aDirtyRect, const nsRect& aRect, bool* aBorderIsEmpty) { + if (!mInnerFocusStyle) { + return Nothing(); + } + + nsRect rect = GetButtonInnerFocusRect(this, *mInnerFocusStyle, aRect); + gfx::DrawTarget* dt = + aRenderingContext ? aRenderingContext->GetDrawTarget() : nullptr; + return nsCSSRendering::CreateBorderRenderer(PresContext(), dt, this, + aDirtyRect, rect, + mInnerFocusStyle, aBorderIsEmpty); +} + void nsHTMLButtonControlFrame::BuildDisplayList( nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) { - nsDisplayList onTop(aBuilder); if (IsVisibleForPainting()) { // Clip the button itself to its border area for event hit testing. Maybe eventClipState; @@ -87,11 +206,9 @@ void nsHTMLButtonControlFrame::BuildDisplayList( rect, hasRadii ? radii : nullptr); } - mRenderer.DisplayButton(aBuilder, aLists.BorderBackground(), &onTop); + DisplayBorderBackgroundOutline(aBuilder, aLists); } - nsDisplayListCollection set(aBuilder); - { DisplayListClipState::AutoSaveRestore clipState(aBuilder); @@ -100,22 +217,23 @@ void nsHTMLButtonControlFrame::BuildDisplayList( nsRect rect(aBuilder->ToReferenceFrame(this), GetSize()); rect.Deflate(border); nscoord radii[8]; - bool hasRadii = GetPaddingBoxBorderRadii(radii); + const bool hasRadii = GetPaddingBoxBorderRadii(radii); clipState.ClipContainingBlockDescendants(rect, hasRadii ? radii : nullptr); } - BuildDisplayListForChild(aBuilder, mFrames.FirstChild(), set, + BuildDisplayListForChild(aBuilder, mFrames.FirstChild(), aLists, DisplayChildFlag::ForcePseudoStackingContext); } - // Put the foreground outline and focus rects on top of the children - set.Content()->AppendToTop(&onTop); - set.MoveTo(aLists); - - DisplayOutline(aBuilder, aLists); + // Put the foreground outline on top of the children. + if (IsVisibleForPainting() && mInnerFocusStyle && + mInnerFocusStyle->StyleBorder()->HasBorder() && IsThemed() && + PresContext()->Theme()->ThemeWantsButtonInnerFocusRing()) { + aLists.Content()->AppendNewToTop(aBuilder, this); + } - // to draw border when selected in editor + // To draw border when selected in editor DisplaySelectionOverlay(aBuilder, aLists.Content()); } @@ -237,12 +355,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 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 @@ -359,12 +479,17 @@ nsresult nsHTMLButtonControlFrame::SetFormProperty(nsAtom* aName, ComputedStyle* nsHTMLButtonControlFrame::GetAdditionalComputedStyle( int32_t aIndex) const { - return mRenderer.GetComputedStyle(aIndex); + if (aIndex == 0) { + return mInnerFocusStyle; + } + return nullptr; } void nsHTMLButtonControlFrame::SetAdditionalComputedStyle( int32_t aIndex, ComputedStyle* aComputedStyle) { - mRenderer.SetComputedStyle(aIndex, aComputedStyle); + if (aIndex == 0) { + mInnerFocusStyle = aComputedStyle; + } } void nsHTMLButtonControlFrame::AppendDirectlyOwnedAnonBoxes( diff --git a/layout/forms/nsHTMLButtonControlFrame.h b/layout/forms/nsHTMLButtonControlFrame.h index b4409d66a7..760418f954 100644 --- a/layout/forms/nsHTMLButtonControlFrame.h +++ b/layout/forms/nsHTMLButtonControlFrame.h @@ -7,10 +7,9 @@ #ifndef nsHTMLButtonControlFrame_h___ #define nsHTMLButtonControlFrame_h___ -#include "mozilla/Attributes.h" #include "nsContainerFrame.h" +#include "nsCSSRenderingBorders.h" #include "nsIFormControlFrame.h" -#include "nsButtonFrameRenderer.h" class gfxContext; class nsPresContext; @@ -83,6 +82,14 @@ class nsHTMLButtonControlFrame : public nsContainerFrame, // Return the ::-moz-button-content anonymous box. void AppendDirectlyOwnedAnonBoxes(nsTArray& aResult) override; + mozilla::Maybe CreateInnerFocusBorderRenderer( + nsDisplayListBuilder* aBuilder, gfxContext* aRenderingContext, + const nsRect& aDirtyRect, const nsRect& aRect, bool* aBorderIsEmpty); + + void PaintInnerFocusBorder(nsDisplayListBuilder* aBuilder, + gfxContext& aRenderingContext, + const nsRect& aDirtyRect, const nsRect& aRect); + protected: nsHTMLButtonControlFrame(ComputedStyle* aStyle, nsPresContext* aPresContext, nsIFrame::ClassID aID); @@ -90,7 +97,7 @@ class nsHTMLButtonControlFrame : public nsContainerFrame, // Indicates whether we should clip our children's painting to our // border-box (either because of "overflow" or because of legacy reasons // about how -flavored buttons work). - bool ShouldClipPaintingToBorderBox(); + bool ShouldClipPaintingToBorderBox() const; // Reflows the button's sole child frame, and computes the desired size // of the button itself from the results. @@ -104,7 +111,7 @@ class nsHTMLButtonControlFrame : public nsContainerFrame, mozilla::WritingMode aWM, BaselineSharingGroup aBaselineGroup) const override; - nsButtonFrameRenderer mRenderer; + RefPtr mInnerFocusStyle; }; #endif diff --git a/layout/forms/nsNumberControlFrame.cpp b/layout/forms/nsNumberControlFrame.cpp index e8ed87c8f3..518ba91e24 100644 --- a/layout/forms/nsNumberControlFrame.cpp +++ b/layout/forms/nsNumberControlFrame.cpp @@ -64,22 +64,29 @@ nsresult nsNumberControlFrame::CreateAnonymousContent( nsTextControlFrame::CreateAnonymousContent(aElements); +#if defined(MOZ_WIDGET_ANDROID) + // We don't want spin buttons on Android + return NS_OK; +#else // The author has elected to hide the spinner by setting this // -moz-appearance. We will reframe if it changes. - if (StyleDisplay()->EffectiveAppearance() != StyleAppearance::Textfield) { - // Create the ::-moz-number-spin-box pseudo-element: - mSpinBox = MakeAnonElement(PseudoStyleType::mozNumberSpinBox); + if (StyleDisplay()->EffectiveAppearance() == StyleAppearance::Textfield) { + return NS_OK; + } - // Create the ::-moz-number-spin-up pseudo-element: - mSpinUp = MakeAnonElement(PseudoStyleType::mozNumberSpinUp, mSpinBox); + // Create the ::-moz-number-spin-box pseudo-element: + mSpinBox = MakeAnonElement(PseudoStyleType::mozNumberSpinBox); - // Create the ::-moz-number-spin-down pseudo-element: - mSpinDown = MakeAnonElement(PseudoStyleType::mozNumberSpinDown, mSpinBox); + // Create the ::-moz-number-spin-up pseudo-element: + mSpinUp = MakeAnonElement(PseudoStyleType::mozNumberSpinUp, mSpinBox); - aElements.AppendElement(mSpinBox); - } + // Create the ::-moz-number-spin-down pseudo-element: + mSpinDown = MakeAnonElement(PseudoStyleType::mozNumberSpinDown, mSpinBox); + + aElements.AppendElement(mSpinBox); return NS_OK; +#endif } /* static */ diff --git a/layout/forms/test/test_bug536567_perwindowpb.html b/layout/forms/test/test_bug536567_perwindowpb.html index 224b2c74a4..e27803157e 100644 --- a/layout/forms/test/test_bug536567_perwindowpb.html +++ b/layout/forms/test/test_bug536567_perwindowpb.html @@ -19,7 +19,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=536567 const Cm = Components.manager; var MockFilePicker = SpecialPowers.MockFilePicker; -MockFilePicker.init(window); +MockFilePicker.init(SpecialPowers.wrap(window).browsingContext); var tmpDir = Services.dirsvc.get("TmpD", Ci.nsIFile); var homeDir = Services.dirsvc.get("Desk", Ci.nsIFile); diff --git a/layout/forms/test/test_bug935876.html b/layout/forms/test/test_bug935876.html index 4488fdf962..465fcac732 100644 --- a/layout/forms/test/test_bug935876.html +++ b/layout/forms/test/test_bug935876.html @@ -112,7 +112,7 @@ function runTests() var listbox = document.getElementById("listbox"); listbox.addEventListener("keydown", onKeydown); listbox.addEventListener("keypress", onKeypress); - SpecialPowers.addSystemEventListener(listbox, "keydown", onkeydownInSystemEventGroup, false); + SpecialPowers.wrap(listbox).addEventListener("keydown", onkeydownInSystemEventGroup, { mozSystemGroup: true }); listbox.focus(); @@ -185,14 +185,13 @@ function runTests() listbox.removeEventListener("keydown", onKeydown); listbox.removeEventListener("keypress", onKeypress); - SpecialPowers.removeSystemEventListener(listbox, "keydown", onkeydownInSystemEventGroup, false); - + SpecialPowers.wrap(listbox).removeEventListener("keydown", onkeydownInSystemEventGroup, { mozSystemGroup: true }); var multipleListbox = document.getElementById("multipleListbox"); multipleListbox.addEventListener("keydown", onKeydown); multipleListbox.addEventListener("keypress", onKeypress); - SpecialPowers.addSystemEventListener(multipleListbox, "keydown", onkeydownInSystemEventGroup, false); + SpecialPowers.wrap(multipleListbox).addEventListener("keydown", onkeydownInSystemEventGroup, { mozSystemGroup: true }); multipleListbox.focus(); @@ -265,14 +264,13 @@ function runTests() multipleListbox.removeEventListener("keydown", onKeydown); multipleListbox.removeEventListener("keypress", onKeypress); - SpecialPowers.removeSystemEventListener(multipleListbox, "keydown", onkeydownInSystemEventGroup, false); - + SpecialPowers.wrap(multipleListbox).removeEventListener("keydown", onkeydownInSystemEventGroup, { mozSystemGroup: true }); var combobox = document.getElementById("combobox"); combobox.addEventListener("keydown", onKeydown); combobox.addEventListener("keypress", onKeypress); - SpecialPowers.addSystemEventListener(combobox, "keydown", onkeydownInSystemEventGroup, false); + SpecialPowers.wrap(combobox).addEventListener("keydown", onkeydownInSystemEventGroup, { mozSystemGroup: true }); combobox.focus(); @@ -353,7 +351,7 @@ function runTests() { combobox.removeEventListener("keydown", onKeydown); combobox.removeEventListener("keypress", onKeypress); - SpecialPowers.removeSystemEventListener(combobox, "keydown", onkeydownInSystemEventGroup, false); + SpecialPowers.wrap(combobox).removeEventListener("keydown", onkeydownInSystemEventGroup, { mozSystemGroup: true }); SimpleTest.finish(); } -- cgit v1.2.3