SVGTRefElement.cpp [plain text]
#include "config.h"
#if ENABLE(SVG)
#include "SVGTRefElement.h"
#include "EventListener.h"
#include "EventNames.h"
#include "MutationEvent.h"
#include "NodeRenderingContext.h"
#include "RenderSVGInline.h"
#include "RenderSVGInlineText.h"
#include "RenderSVGResource.h"
#include "ShadowRoot.h"
#include "ShadowTree.h"
#include "SVGDocument.h"
#include "SVGElementInstance.h"
#include "SVGNames.h"
#include "Text.h"
#include "XLinkNames.h"
namespace WebCore {
DEFINE_ANIMATED_STRING(SVGTRefElement, XLinkNames::hrefAttr, Href, href)
BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGTRefElement)
REGISTER_LOCAL_ANIMATED_PROPERTY(href)
REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTextPositioningElement)
END_REGISTER_ANIMATED_PROPERTIES
PassRefPtr<SVGTRefElement> SVGTRefElement::create(const QualifiedName& tagName, Document* document)
{
RefPtr<SVGTRefElement> element = adoptRef(new SVGTRefElement(tagName, document));
element->createShadowSubtree();
return element.release();
}
class SVGTRefTargetEventListener : public EventListener {
public:
static PassRefPtr<SVGTRefTargetEventListener> create(SVGTRefElement* trefElement)
{
return adoptRef(new SVGTRefTargetEventListener(trefElement));
}
static const SVGTRefTargetEventListener* cast(const EventListener* listener)
{
return listener->type() == SVGTRefTargetEventListenerType
? static_cast<const SVGTRefTargetEventListener*>(listener) : 0;
}
void attach(PassRefPtr<Element> target);
void detach();
bool isAttached() const { return m_target.get(); }
private:
SVGTRefTargetEventListener(SVGTRefElement* trefElement);
virtual void handleEvent(ScriptExecutionContext*, Event*) OVERRIDE;
virtual bool operator==(const EventListener&) OVERRIDE;
SVGTRefElement* m_trefElement;
RefPtr<Element> m_target;
};
SVGTRefTargetEventListener::SVGTRefTargetEventListener(SVGTRefElement* trefElement)
: EventListener(SVGTRefTargetEventListenerType)
, m_trefElement(trefElement)
, m_target(0)
{
ASSERT(m_trefElement);
}
void SVGTRefTargetEventListener::attach(PassRefPtr<Element> target)
{
ASSERT(!isAttached());
ASSERT(target.get());
ASSERT(target->inDocument());
target->addEventListener(eventNames().DOMSubtreeModifiedEvent, this, false);
target->addEventListener(eventNames().DOMNodeRemovedFromDocumentEvent, this, false);
m_target = target;
}
void SVGTRefTargetEventListener::detach()
{
if (!isAttached())
return;
m_target->removeEventListener(eventNames().DOMSubtreeModifiedEvent, this, false);
m_target->removeEventListener(eventNames().DOMNodeRemovedFromDocumentEvent, this, false);
m_target.clear();
}
bool SVGTRefTargetEventListener::operator==(const EventListener& listener)
{
if (const SVGTRefTargetEventListener* targetListener = SVGTRefTargetEventListener::cast(&listener))
return m_trefElement == targetListener->m_trefElement;
return false;
}
void SVGTRefTargetEventListener::handleEvent(ScriptExecutionContext*, Event* event)
{
ASSERT(isAttached());
if (event->type() == eventNames().DOMSubtreeModifiedEvent && m_trefElement != event->target())
m_trefElement->updateReferencedText(m_target.get());
else if (event->type() == eventNames().DOMNodeRemovedFromDocumentEvent)
m_trefElement->detachTarget();
}
class SVGShadowText : public Text {
public:
static PassRefPtr<SVGShadowText> create(Document* document, const String& data)
{
return adoptRef(new SVGShadowText(document, data));
}
private:
SVGShadowText(Document* document, const String& data)
: Text(document, data)
{
setHasCustomWillOrDidRecalcStyle();
}
virtual RenderObject* createRenderer(RenderArena*, RenderStyle*);
virtual void willRecalcTextStyle(StyleChange);
};
RenderObject* SVGShadowText::createRenderer(RenderArena* arena, RenderStyle*)
{
return new (arena) RenderSVGInlineText(this, dataImpl());
}
void SVGShadowText::willRecalcTextStyle(StyleChange change)
{
if (change != NoChange && parentNode()->shadowHost()) {
if (renderer())
renderer()->setStyle(parentNode()->shadowHost()->renderer()->style());
}
}
inline SVGTRefElement::SVGTRefElement(const QualifiedName& tagName, Document* document)
: SVGTextPositioningElement(tagName, document)
, m_targetListener(SVGTRefTargetEventListener::create(this))
{
ASSERT(hasTagName(SVGNames::trefTag));
registerAnimatedPropertiesForSVGTRefElement();
}
SVGTRefElement::~SVGTRefElement()
{
m_targetListener->detach();
}
void SVGTRefElement::createShadowSubtree()
{
ShadowRoot::create(this, ShadowRoot::CreatingUserAgentShadowRoot, ASSERT_NO_EXCEPTION);
}
void SVGTRefElement::updateReferencedText(Element* target)
{
String textContent;
if (target)
textContent = target->textContent();
ASSERT(hasShadowRoot());
ShadowRoot* root = shadowTree()->oldestShadowRoot();
if (!root->firstChild())
root->appendChild(SVGShadowText::create(document(), textContent), ASSERT_NO_EXCEPTION);
else
root->firstChild()->setTextContent(textContent, ASSERT_NO_EXCEPTION);
}
void SVGTRefElement::detachTarget()
{
m_targetListener->detach();
String emptyContent;
ExceptionCode ignore = 0;
ASSERT(hasShadowRoot());
Node* container = shadowTree()->oldestShadowRoot()->firstChild();
if (container)
container->setTextContent(emptyContent, ignore);
if (!inDocument())
return;
String id;
SVGURIReference::targetElementFromIRIString(href(), document(), &id);
if (!hasPendingResources() && !id.isEmpty())
document()->accessSVGExtensions()->addPendingResource(id, this);
}
bool SVGTRefElement::isSupportedAttribute(const QualifiedName& attrName)
{
DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
if (supportedAttributes.isEmpty())
SVGURIReference::addSupportedAttributes(supportedAttributes);
return supportedAttributes.contains<QualifiedName, SVGAttributeHashTranslator>(attrName);
}
void SVGTRefElement::parseAttribute(Attribute* attr)
{
if (!isSupportedAttribute(attr->name())) {
SVGTextPositioningElement::parseAttribute(attr);
return;
}
if (SVGURIReference::parseAttribute(attr)) {
return;
}
ASSERT_NOT_REACHED();
}
void SVGTRefElement::svgAttributeChanged(const QualifiedName& attrName)
{
if (!isSupportedAttribute(attrName)) {
SVGTextPositioningElement::svgAttributeChanged(attrName);
return;
}
SVGElementInstance::InvalidationGuard invalidationGuard(this);
if (SVGURIReference::isKnownAttribute(attrName)) {
buildPendingResource();
if (RenderObject* renderer = this->renderer())
RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
return;
}
ASSERT_NOT_REACHED();
}
RenderObject* SVGTRefElement::createRenderer(RenderArena* arena, RenderStyle*)
{
return new (arena) RenderSVGInline(this);
}
bool SVGTRefElement::childShouldCreateRenderer(const NodeRenderingContext& childContext) const
{
return childContext.node()->isInShadowTree();
}
bool SVGTRefElement::rendererIsNeeded(const NodeRenderingContext& context)
{
if (parentNode()
&& (parentNode()->hasTagName(SVGNames::aTag)
#if ENABLE(SVG_FONTS)
|| parentNode()->hasTagName(SVGNames::altGlyphTag)
#endif
|| parentNode()->hasTagName(SVGNames::textTag)
|| parentNode()->hasTagName(SVGNames::textPathTag)
|| parentNode()->hasTagName(SVGNames::tspanTag)))
return StyledElement::rendererIsNeeded(context);
return false;
}
void SVGTRefElement::buildPendingResource()
{
m_targetListener->detach();
if (!inDocument())
return;
String id;
RefPtr<Element> target = SVGURIReference::targetElementFromIRIString(href(), document(), &id);
if (!target.get()) {
if (hasPendingResources() || id.isEmpty())
return;
ASSERT(!hasPendingResources());
document()->accessSVGExtensions()->addPendingResource(id, this);
ASSERT(hasPendingResources());
return;
}
if (!isInShadowTree())
m_targetListener->attach(target);
updateReferencedText(target.get());
}
Node::InsertionNotificationRequest SVGTRefElement::insertedInto(Node* rootParent)
{
SVGStyledElement::insertedInto(rootParent);
if (rootParent->inDocument())
buildPendingResource();
return InsertionDone;
}
void SVGTRefElement::removedFrom(Node* rootParent)
{
SVGStyledElement::removedFrom(rootParent);
if (rootParent->inDocument())
m_targetListener->detach();
}
}
#endif // ENABLE(SVG)