blob: 9859027a6faf8b3d0e2c4f03d3e3c207e5c0bc7a [file] [log] [blame]
/*
* Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann@kde.org>
* Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org>
* Copyright (C) 2009 Google, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "third_party/blink/renderer/core/layout/svg/layout_svg_transformable_container.h"
#include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h"
#include "third_party/blink/renderer/core/svg/svg_g_element.h"
#include "third_party/blink/renderer/core/svg/svg_graphics_element.h"
#include "third_party/blink/renderer/core/svg/svg_use_element.h"
namespace blink {
LayoutSVGTransformableContainer::LayoutSVGTransformableContainer(
SVGGraphicsElement* node)
: LayoutSVGContainer(node), needs_transform_update_(true) {}
static bool HasValidPredecessor(const Node* node) {
DCHECK(node);
for (node = node->previousSibling(); node; node = node->previousSibling()) {
if (node->IsSVGElement() && ToSVGElement(node)->IsValid())
return true;
}
return false;
}
bool LayoutSVGTransformableContainer::IsChildAllowed(
LayoutObject* child,
const ComputedStyle& style) const {
DCHECK(GetElement());
Node* child_node = child->GetNode();
if (IsSVGSwitchElement(*GetElement())) {
// Reject non-SVG/non-valid elements.
if (!child_node || !child_node->IsSVGElement() ||
!ToSVGElement(child_node)->IsValid()) {
return false;
}
// Reject this child if it isn't the first valid node.
if (HasValidPredecessor(child_node))
return false;
} else if (IsSVGAElement(*GetElement())) {
// http://www.w3.org/2003/01/REC-SVG11-20030114-errata#linking-text-environment
// The 'a' element may contain any element that its parent may contain,
// except itself.
if (child_node && IsSVGAElement(*child_node))
return false;
if (Parent() && Parent()->IsSVG())
return Parent()->IsChildAllowed(child, style);
}
return LayoutSVGContainer::IsChildAllowed(child, style);
}
void LayoutSVGTransformableContainer::SetNeedsTransformUpdate() {
// The transform paint property relies on the SVG transform being up-to-date
// (see: PaintPropertyTreeBuilder::updateTransformForNonRootSVG).
SetNeedsPaintPropertyUpdate();
needs_transform_update_ = true;
}
bool LayoutSVGTransformableContainer::IsUseElement() const {
const SVGElement& element = *GetElement();
if (IsSVGUseElement(element))
return true;
// Nested <use> are replaced by <g> during shadow tree expansion.
if (IsSVGGElement(element) && ToSVGGElement(element).InUseShadowTree())
return IsSVGUseElement(element.CorrespondingElement());
return false;
}
SVGTransformChange LayoutSVGTransformableContainer::CalculateLocalTransform() {
SVGElement* element = GetElement();
DCHECK(element);
// If we're either the LayoutObject for a <use> element, or for any <g>
// element inside the shadow tree, that was created during the use/symbol/svg
// expansion in SVGUseElement. These containers need to respect the
// translations induced by their corresponding use elements x/y attributes.
if (IsUseElement()) {
const ComputedStyle& style = StyleRef();
const SVGComputedStyle& svg_style = style.SvgStyle();
SVGLengthContext length_context(element);
FloatSize translation(ToFloatSize(
length_context.ResolveLengthPair(svg_style.X(), svg_style.Y(), style)));
// TODO(fs): Signal this on style update instead.
if (translation != additional_translation_)
SetNeedsTransformUpdate();
additional_translation_ = translation;
}
if (!needs_transform_update_)
return SVGTransformChange::kNone;
SVGTransformChangeDetector change_detector(local_transform_);
local_transform_ =
element->CalculateTransform(SVGElement::kIncludeMotionTransform);
local_transform_.Translate(additional_translation_.Width(),
additional_translation_.Height());
needs_transform_update_ = false;
return change_detector.ComputeChange(local_transform_);
}
} // namespace blink