mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
04e9887192
This patch does several things. Sorry. In BuildDisplayList implementations, instead of wrapping display items in nsDisplayClip, we push clip state onto the nsDisplayListBuilder and give the display items an explicit clip when they're created. In FrameLayerBuilder, we use the explicit clips we find on display items instead of computing our own. We remove nsDisplayClip and everything that depends on it. We remove ExplodeAnonymousChildLists. With nsDisplayClip gone, and nsDisplayOptionEventGrabber removed in a previous patch, there are no anonymous child lists. nsDisplayItem::TryMerge implementations need to make sure they have the same clip before being merged. I ripped out the part of PruneDisplayListForExtraPage that adjusts clip rects. As far as I can tell, it isn't actually necessary. --HG-- extra : rebase_source : 6f3988b385d0ac54ab26fad10b12173884441f48
369 lines
12 KiB
C++
369 lines
12 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "nsHTMLButtonControlFrame.h"
|
|
|
|
#include "nsCOMPtr.h"
|
|
#include "nsContainerFrame.h"
|
|
#include "nsIFormControlFrame.h"
|
|
#include "nsHTMLParts.h"
|
|
#include "nsIFormControl.h"
|
|
|
|
#include "nsPresContext.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsStyleContext.h"
|
|
#include "nsLeafFrame.h"
|
|
#include "nsCSSRendering.h"
|
|
#include "nsISupports.h"
|
|
#include "nsGkAtoms.h"
|
|
#include "nsCSSAnonBoxes.h"
|
|
#include "nsStyleConsts.h"
|
|
#include "nsIComponentManager.h"
|
|
#include "nsButtonFrameRenderer.h"
|
|
#include "nsFormControlFrame.h"
|
|
#include "nsFrameManager.h"
|
|
#include "nsINameSpaceManager.h"
|
|
#include "nsIServiceManager.h"
|
|
#include "nsIDOMHTMLButtonElement.h"
|
|
#include "nsIDOMHTMLInputElement.h"
|
|
#include "nsStyleSet.h"
|
|
#include "nsDisplayList.h"
|
|
#include <algorithm>
|
|
|
|
using namespace mozilla;
|
|
|
|
nsIFrame*
|
|
NS_NewHTMLButtonControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
|
{
|
|
return new (aPresShell) nsHTMLButtonControlFrame(aContext);
|
|
}
|
|
|
|
NS_IMPL_FRAMEARENA_HELPERS(nsHTMLButtonControlFrame)
|
|
|
|
nsHTMLButtonControlFrame::nsHTMLButtonControlFrame(nsStyleContext* aContext)
|
|
: nsContainerFrame(aContext)
|
|
{
|
|
}
|
|
|
|
nsHTMLButtonControlFrame::~nsHTMLButtonControlFrame()
|
|
{
|
|
}
|
|
|
|
void
|
|
nsHTMLButtonControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
|
|
{
|
|
nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false);
|
|
nsContainerFrame::DestroyFrom(aDestructRoot);
|
|
}
|
|
|
|
void
|
|
nsHTMLButtonControlFrame::Init(
|
|
nsIContent* aContent,
|
|
nsIFrame* aParent,
|
|
nsIFrame* aPrevInFlow)
|
|
{
|
|
nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
|
|
mRenderer.SetFrame(this, PresContext());
|
|
}
|
|
|
|
NS_QUERYFRAME_HEAD(nsHTMLButtonControlFrame)
|
|
NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
|
|
NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
|
|
|
|
#ifdef ACCESSIBILITY
|
|
a11y::AccType
|
|
nsHTMLButtonControlFrame::AccessibleType()
|
|
{
|
|
return a11y::eHTMLButtonType;
|
|
}
|
|
#endif
|
|
|
|
nsIAtom*
|
|
nsHTMLButtonControlFrame::GetType() const
|
|
{
|
|
return nsGkAtoms::HTMLButtonControlFrame;
|
|
}
|
|
|
|
void
|
|
nsHTMLButtonControlFrame::SetFocus(bool aOn, bool aRepaint)
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsHTMLButtonControlFrame::HandleEvent(nsPresContext* aPresContext,
|
|
nsGUIEvent* aEvent,
|
|
nsEventStatus* aEventStatus)
|
|
{
|
|
// if disabled do nothing
|
|
if (mRenderer.isDisabled()) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// mouse clicks are handled by content
|
|
// we don't want our children to get any events. So just pass it to frame.
|
|
return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
|
|
}
|
|
|
|
|
|
void
|
|
nsHTMLButtonControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|
const nsRect& aDirtyRect,
|
|
const nsDisplayListSet& aLists)
|
|
{
|
|
nsDisplayList onTop;
|
|
if (IsVisibleForPainting(aBuilder)) {
|
|
mRenderer.DisplayButton(aBuilder, aLists.BorderBackground(), &onTop);
|
|
}
|
|
|
|
bool overflowClip =
|
|
IsInput() || StyleDisplay()->mOverflowX != NS_STYLE_OVERFLOW_VISIBLE;
|
|
nsRect rect;
|
|
nscoord radii[8];
|
|
nsDisplayListCollection set;
|
|
|
|
{
|
|
DisplayListClipState::AutoSaveRestore saveClipState(aBuilder->ClipState());
|
|
DisplayItemClip overflowClipOnStack;
|
|
|
|
if (overflowClip) {
|
|
nsMargin border = StyleBorder()->GetComputedBorder();
|
|
rect = nsRect(aBuilder->ToReferenceFrame(this), GetSize());
|
|
rect.Deflate(border);
|
|
bool hasRadii = GetPaddingBoxBorderRadii(radii);
|
|
aBuilder->ClipState().ClipContainingBlockDescendants(rect,
|
|
hasRadii ? radii : nullptr, overflowClipOnStack);
|
|
}
|
|
|
|
// Do not allow the child subtree to receive events.
|
|
if (!aBuilder->IsForEventDelivery()) {
|
|
BuildDisplayListForChild(aBuilder, mFrames.FirstChild(), aDirtyRect, set,
|
|
DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT);
|
|
// That should put the display items in set.Content()
|
|
}
|
|
}
|
|
|
|
// Put the foreground outline and focus rects on top of the children
|
|
set.Content()->AppendToTop(&onTop);
|
|
set.MoveTo(aLists);
|
|
|
|
DisplayOutline(aBuilder, aLists);
|
|
|
|
// to draw border when selected in editor
|
|
DisplaySelectionOverlay(aBuilder, aLists.Content());
|
|
}
|
|
|
|
nscoord
|
|
nsHTMLButtonControlFrame::GetMinWidth(nsRenderingContext* aRenderingContext)
|
|
{
|
|
nscoord result;
|
|
DISPLAY_MIN_WIDTH(this, result);
|
|
|
|
nsIFrame* kid = mFrames.FirstChild();
|
|
result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
|
|
kid,
|
|
nsLayoutUtils::MIN_WIDTH);
|
|
|
|
result += mRenderer.GetAddedButtonBorderAndPadding().LeftRight();
|
|
|
|
return result;
|
|
}
|
|
|
|
nscoord
|
|
nsHTMLButtonControlFrame::GetPrefWidth(nsRenderingContext* aRenderingContext)
|
|
{
|
|
nscoord result;
|
|
DISPLAY_PREF_WIDTH(this, result);
|
|
|
|
nsIFrame* kid = mFrames.FirstChild();
|
|
result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
|
|
kid,
|
|
nsLayoutUtils::PREF_WIDTH);
|
|
result += mRenderer.GetAddedButtonBorderAndPadding().LeftRight();
|
|
return result;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsHTMLButtonControlFrame::Reflow(nsPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
DO_GLOBAL_REFLOW_COUNT("nsHTMLButtonControlFrame");
|
|
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
|
|
|
NS_PRECONDITION(aReflowState.ComputedWidth() != NS_INTRINSICSIZE,
|
|
"Should have real computed width by now");
|
|
|
|
if (mState & NS_FRAME_FIRST_REFLOW) {
|
|
nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), true);
|
|
}
|
|
|
|
// Reflow the child
|
|
nsIFrame* firstKid = mFrames.FirstChild();
|
|
|
|
// XXXbz Eventually we may want to check-and-bail if
|
|
// !aReflowState.ShouldReflowAllKids() &&
|
|
// !NS_SUBTREE_DIRTY(firstKid).
|
|
// We'd need to cache our ascent for that, of course.
|
|
|
|
nsMargin focusPadding = mRenderer.GetAddedButtonBorderAndPadding();
|
|
|
|
// Reflow the contents of the button.
|
|
ReflowButtonContents(aPresContext, aDesiredSize, aReflowState, firstKid,
|
|
focusPadding, aStatus);
|
|
|
|
aDesiredSize.width = aReflowState.ComputedWidth();
|
|
|
|
// If computed use the computed value.
|
|
if (aReflowState.ComputedHeight() != NS_INTRINSICSIZE) {
|
|
aDesiredSize.height = aReflowState.ComputedHeight();
|
|
} else {
|
|
aDesiredSize.height += focusPadding.TopBottom();
|
|
|
|
// Make sure we obey min/max-height in the case when we're doing intrinsic
|
|
// sizing (we get it for free when we have a non-intrinsic
|
|
// aReflowState.ComputedHeight()). Note that we do this before adjusting
|
|
// for borderpadding, since mComputedMaxHeight and mComputedMinHeight are
|
|
// content heights.
|
|
aDesiredSize.height = NS_CSS_MINMAX(aDesiredSize.height,
|
|
aReflowState.mComputedMinHeight,
|
|
aReflowState.mComputedMaxHeight);
|
|
}
|
|
|
|
aDesiredSize.width += aReflowState.mComputedBorderPadding.LeftRight();
|
|
aDesiredSize.height += aReflowState.mComputedBorderPadding.TopBottom();
|
|
|
|
aDesiredSize.ascent +=
|
|
aReflowState.mComputedBorderPadding.top + focusPadding.top;
|
|
|
|
aDesiredSize.SetOverflowAreasToDesiredBounds();
|
|
ConsiderChildOverflow(aDesiredSize.mOverflowAreas, firstKid);
|
|
FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus);
|
|
|
|
aStatus = NS_FRAME_COMPLETE;
|
|
|
|
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsIFrame* aFirstKid,
|
|
nsMargin aFocusPadding,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
nsSize availSize(aReflowState.ComputedWidth(), NS_INTRINSICSIZE);
|
|
|
|
// Indent the child inside us by the focus border. We must do this separate
|
|
// from the regular border.
|
|
availSize.width -= aFocusPadding.LeftRight();
|
|
|
|
// See whether out availSize's width is big enough. If it's smaller than our
|
|
// intrinsic min width, that means that the kid wouldn't really fit; for a
|
|
// better look in such cases we adjust the available width and our left
|
|
// offset to allow the kid to spill left into our padding.
|
|
nscoord xoffset = aFocusPadding.left + aReflowState.mComputedBorderPadding.left;
|
|
nscoord extrawidth = GetMinWidth(aReflowState.rendContext) -
|
|
aReflowState.ComputedWidth();
|
|
if (extrawidth > 0) {
|
|
nscoord extraleft = extrawidth / 2;
|
|
nscoord extraright = extrawidth - extraleft;
|
|
NS_ASSERTION(extraright >=0, "How'd that happen?");
|
|
|
|
// Do not allow the extras to be bigger than the relevant padding
|
|
extraleft = std::min(extraleft, aReflowState.mComputedPadding.left);
|
|
extraright = std::min(extraright, aReflowState.mComputedPadding.right);
|
|
xoffset -= extraleft;
|
|
availSize.width += extraleft + extraright;
|
|
}
|
|
availSize.width = std::max(availSize.width,0);
|
|
|
|
nsHTMLReflowState reflowState(aPresContext, aReflowState, aFirstKid,
|
|
availSize);
|
|
|
|
ReflowChild(aFirstKid, aPresContext, aDesiredSize, reflowState,
|
|
xoffset,
|
|
aFocusPadding.top + aReflowState.mComputedBorderPadding.top,
|
|
0, aStatus);
|
|
|
|
// calculate the min internal height so the contents gets centered correctly.
|
|
// XXXbz this assumes border-box sizing.
|
|
nscoord minInternalHeight = aReflowState.mComputedMinHeight -
|
|
aReflowState.mComputedBorderPadding.TopBottom();
|
|
minInternalHeight = std::max(minInternalHeight, 0);
|
|
|
|
// center child vertically
|
|
nscoord yoff = 0;
|
|
if (aReflowState.ComputedHeight() != NS_INTRINSICSIZE) {
|
|
yoff = (aReflowState.ComputedHeight() - aDesiredSize.height)/2;
|
|
if (yoff < 0) {
|
|
yoff = 0;
|
|
}
|
|
} else if (aDesiredSize.height < minInternalHeight) {
|
|
yoff = (minInternalHeight - aDesiredSize.height) / 2;
|
|
}
|
|
|
|
// Place the child
|
|
FinishReflowChild(aFirstKid, aPresContext, &reflowState, aDesiredSize,
|
|
xoffset,
|
|
yoff + aFocusPadding.top + aReflowState.mComputedBorderPadding.top, 0);
|
|
|
|
if (aDesiredSize.ascent == nsHTMLReflowMetrics::ASK_FOR_BASELINE)
|
|
aDesiredSize.ascent = aFirstKid->GetBaseline();
|
|
|
|
// Adjust the baseline by our offset (since we moved the child's
|
|
// baseline by that much).
|
|
aDesiredSize.ascent += yoff;
|
|
}
|
|
|
|
nsresult nsHTMLButtonControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aValue)
|
|
{
|
|
if (nsGkAtoms::value == aName) {
|
|
return mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::value,
|
|
aValue, true);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsStyleContext*
|
|
nsHTMLButtonControlFrame::GetAdditionalStyleContext(int32_t aIndex) const
|
|
{
|
|
return mRenderer.GetStyleContext(aIndex);
|
|
}
|
|
|
|
void
|
|
nsHTMLButtonControlFrame::SetAdditionalStyleContext(int32_t aIndex,
|
|
nsStyleContext* aStyleContext)
|
|
{
|
|
mRenderer.SetStyleContext(aIndex, aStyleContext);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsHTMLButtonControlFrame::AppendFrames(ChildListID aListID,
|
|
nsFrameList& aFrameList)
|
|
{
|
|
NS_NOTREACHED("unsupported operation");
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsHTMLButtonControlFrame::InsertFrames(ChildListID aListID,
|
|
nsIFrame* aPrevFrame,
|
|
nsFrameList& aFrameList)
|
|
{
|
|
NS_NOTREACHED("unsupported operation");
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsHTMLButtonControlFrame::RemoveFrame(ChildListID aListID,
|
|
nsIFrame* aOldFrame)
|
|
{
|
|
NS_NOTREACHED("unsupported operation");
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|