mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
11d4b8640e
Without this patch, when changing the x/y attributes of svg:use, innerSVG and foreignObject, we were relying on the transform changes of the children to trigger the right invalidations. However, changes to those attributes can also change the filter region. And there's a difference between moving children in a fixed filter region and moving the filter region along with the children: In the first case, we wouldn't need to invalidate anything outside the old filter region, because those parts of the children would be clipped away anyway. But when the filter region changes, we need to invalidate both the old and the new filter region. Also, when the filter has primitives without inputs, e.g. flood or turbulence, the filtered frame needs to be invalidate even if it has no children.
253 lines
7.9 KiB
C++
253 lines
7.9 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/. */
|
|
|
|
// Keep in (case-insensitive) order:
|
|
#include "nsIAnonymousContentCreator.h"
|
|
#include "nsSVGEffects.h"
|
|
#include "nsSVGGFrame.h"
|
|
#include "mozilla/dom/SVGUseElement.h"
|
|
#include "nsContentList.h"
|
|
|
|
typedef nsSVGGFrame nsSVGUseFrameBase;
|
|
|
|
using namespace mozilla::dom;
|
|
|
|
class nsSVGUseFrame : public nsSVGUseFrameBase,
|
|
public nsIAnonymousContentCreator
|
|
{
|
|
friend nsIFrame*
|
|
NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
|
|
|
|
protected:
|
|
nsSVGUseFrame(nsStyleContext* aContext) :
|
|
nsSVGUseFrameBase(aContext),
|
|
mHasValidDimensions(true)
|
|
{}
|
|
|
|
public:
|
|
NS_DECL_QUERYFRAME
|
|
NS_DECL_FRAMEARENA_HELPERS
|
|
|
|
|
|
// nsIFrame interface:
|
|
virtual void Init(nsIContent* aContent,
|
|
nsIFrame* aParent,
|
|
nsIFrame* aPrevInFlow) MOZ_OVERRIDE;
|
|
|
|
virtual nsresult AttributeChanged(int32_t aNameSpaceID,
|
|
nsIAtom* aAttribute,
|
|
int32_t aModType) MOZ_OVERRIDE;
|
|
|
|
virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE;
|
|
|
|
/**
|
|
* Get the "type" of the frame
|
|
*
|
|
* @see nsGkAtoms::svgUseFrame
|
|
*/
|
|
virtual nsIAtom* GetType() const MOZ_OVERRIDE;
|
|
|
|
virtual bool IsLeaf() const MOZ_OVERRIDE;
|
|
|
|
#ifdef DEBUG_FRAME_DUMP
|
|
virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE
|
|
{
|
|
return MakeFrameName(NS_LITERAL_STRING("SVGUse"), aResult);
|
|
}
|
|
#endif
|
|
|
|
// nsISVGChildFrame interface:
|
|
virtual void ReflowSVG() MOZ_OVERRIDE;
|
|
virtual void NotifySVGChanged(uint32_t aFlags) MOZ_OVERRIDE;
|
|
|
|
// nsIAnonymousContentCreator
|
|
virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) MOZ_OVERRIDE;
|
|
virtual void AppendAnonymousContentTo(nsBaseContentList& aElements,
|
|
uint32_t aFilter) MOZ_OVERRIDE;
|
|
|
|
private:
|
|
bool mHasValidDimensions;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Implementation
|
|
|
|
nsIFrame*
|
|
NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
|
{
|
|
return new (aPresShell) nsSVGUseFrame(aContext);
|
|
}
|
|
|
|
NS_IMPL_FRAMEARENA_HELPERS(nsSVGUseFrame)
|
|
|
|
nsIAtom *
|
|
nsSVGUseFrame::GetType() const
|
|
{
|
|
return nsGkAtoms::svgUseFrame;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsQueryFrame methods
|
|
|
|
NS_QUERYFRAME_HEAD(nsSVGUseFrame)
|
|
NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
|
|
NS_QUERYFRAME_TAIL_INHERITING(nsSVGUseFrameBase)
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsIFrame methods:
|
|
|
|
void
|
|
nsSVGUseFrame::Init(nsIContent* aContent,
|
|
nsIFrame* aParent,
|
|
nsIFrame* aPrevInFlow)
|
|
{
|
|
NS_ASSERTION(aContent->IsSVG(nsGkAtoms::use),
|
|
"Content is not an SVG use!");
|
|
|
|
mHasValidDimensions =
|
|
static_cast<SVGUseElement*>(aContent)->HasValidDimensions();
|
|
|
|
nsSVGUseFrameBase::Init(aContent, aParent, aPrevInFlow);
|
|
}
|
|
|
|
nsresult
|
|
nsSVGUseFrame::AttributeChanged(int32_t aNameSpaceID,
|
|
nsIAtom* aAttribute,
|
|
int32_t aModType)
|
|
{
|
|
SVGUseElement *useElement = static_cast<SVGUseElement*>(mContent);
|
|
|
|
if (aNameSpaceID == kNameSpaceID_None) {
|
|
if (aAttribute == nsGkAtoms::x ||
|
|
aAttribute == nsGkAtoms::y) {
|
|
// make sure our cached transform matrix gets (lazily) updated
|
|
mCanvasTM = nullptr;
|
|
nsSVGEffects::InvalidateRenderingObservers(this);
|
|
nsSVGUtils::ScheduleReflowSVG(this);
|
|
nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED);
|
|
} else if (aAttribute == nsGkAtoms::width ||
|
|
aAttribute == nsGkAtoms::height) {
|
|
bool invalidate = false;
|
|
if (mHasValidDimensions != useElement->HasValidDimensions()) {
|
|
mHasValidDimensions = !mHasValidDimensions;
|
|
invalidate = true;
|
|
}
|
|
if (useElement->OurWidthAndHeightAreUsed()) {
|
|
invalidate = true;
|
|
useElement->SyncWidthOrHeight(aAttribute);
|
|
}
|
|
if (invalidate) {
|
|
nsSVGEffects::InvalidateRenderingObservers(this);
|
|
nsSVGUtils::ScheduleReflowSVG(this);
|
|
}
|
|
}
|
|
} else if (aNameSpaceID == kNameSpaceID_XLink &&
|
|
aAttribute == nsGkAtoms::href) {
|
|
// we're changing our nature, clear out the clone information
|
|
nsSVGEffects::InvalidateRenderingObservers(this);
|
|
nsSVGUtils::ScheduleReflowSVG(this);
|
|
useElement->mOriginal = nullptr;
|
|
useElement->UnlinkSource();
|
|
useElement->TriggerReclone();
|
|
}
|
|
|
|
return nsSVGUseFrameBase::AttributeChanged(aNameSpaceID,
|
|
aAttribute, aModType);
|
|
}
|
|
|
|
void
|
|
nsSVGUseFrame::DestroyFrom(nsIFrame* aDestructRoot)
|
|
{
|
|
nsRefPtr<SVGUseElement> use = static_cast<SVGUseElement*>(mContent);
|
|
nsSVGUseFrameBase::DestroyFrom(aDestructRoot);
|
|
use->DestroyAnonymousContent();
|
|
}
|
|
|
|
bool
|
|
nsSVGUseFrame::IsLeaf() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsISVGChildFrame methods
|
|
|
|
void
|
|
nsSVGUseFrame::ReflowSVG()
|
|
{
|
|
// We only handle x/y offset here, since any width/height that is in force is
|
|
// handled by the nsSVGOuterSVGFrame for the anonymous <svg> that will be
|
|
// created for that purpose.
|
|
float x, y;
|
|
static_cast<SVGUseElement*>(mContent)->
|
|
GetAnimatedLengthValues(&x, &y, nullptr);
|
|
mRect.MoveTo(nsLayoutUtils::RoundGfxRectToAppRect(
|
|
gfxRect(x, y, 0.0, 0.0),
|
|
PresContext()->AppUnitsPerCSSPixel()).TopLeft());
|
|
|
|
// If we have a filter, we need to invalidate ourselves because filter
|
|
// output can change even if none of our descendants need repainting.
|
|
if (StyleSVGReset()->HasFilters()) {
|
|
InvalidateFrame();
|
|
}
|
|
|
|
nsSVGUseFrameBase::ReflowSVG();
|
|
}
|
|
|
|
void
|
|
nsSVGUseFrame::NotifySVGChanged(uint32_t aFlags)
|
|
{
|
|
if (aFlags & COORD_CONTEXT_CHANGED &&
|
|
!(aFlags & TRANSFORM_CHANGED)) {
|
|
// Coordinate context changes affect mCanvasTM if we have a
|
|
// percentage 'x' or 'y'
|
|
SVGUseElement *use = static_cast<SVGUseElement*>(mContent);
|
|
if (use->mLengthAttributes[SVGUseElement::ATTR_X].IsPercentage() ||
|
|
use->mLengthAttributes[SVGUseElement::ATTR_Y].IsPercentage()) {
|
|
aFlags |= TRANSFORM_CHANGED;
|
|
// Ancestor changes can't affect how we render from the perspective of
|
|
// any rendering observers that we may have, so we don't need to
|
|
// invalidate them. We also don't need to invalidate ourself, since our
|
|
// changed ancestor will have invalidated its entire area, which includes
|
|
// our area.
|
|
// For perf reasons we call this before calling NotifySVGChanged() below.
|
|
nsSVGUtils::ScheduleReflowSVG(this);
|
|
}
|
|
}
|
|
|
|
// We don't remove the TRANSFORM_CHANGED flag here if we have a viewBox or
|
|
// non-percentage width/height, since if they're set then they are cloned to
|
|
// an anonymous child <svg>, and its nsSVGInnerSVGFrame will do that.
|
|
|
|
nsSVGUseFrameBase::NotifySVGChanged(aFlags);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsIAnonymousContentCreator methods:
|
|
|
|
nsresult
|
|
nsSVGUseFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
|
|
{
|
|
SVGUseElement *use = static_cast<SVGUseElement*>(mContent);
|
|
|
|
nsIContent* clone = use->CreateAnonymousContent();
|
|
nsSVGEffects::InvalidateRenderingObservers(this);
|
|
if (!clone)
|
|
return NS_ERROR_FAILURE;
|
|
if (!aElements.AppendElement(clone))
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsSVGUseFrame::AppendAnonymousContentTo(nsBaseContentList& aElements,
|
|
uint32_t aFilter)
|
|
{
|
|
SVGUseElement *use = static_cast<SVGUseElement*>(mContent);
|
|
nsIContent* clone = use->GetAnonymousContent();
|
|
aElements.MaybeAppendElement(clone);
|
|
}
|