void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode) { bool hadUseCurrentView = m_useCurrentView; if (fragmentIdentifier.startsWith("xpointer(")) { // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491) setUseCurrentView(false); } else if (fragmentIdentifier.startsWith("svgView(")) { if (currentView()->parseViewSpec(fragmentIdentifier)) setUseCurrentView(true); } else if (anchorNode && anchorNode->hasTagName(SVGNames::viewTag)) { if (SVGViewElement* viewElement = anchorNode->hasTagName(SVGNames::viewTag) ? static_cast<SVGViewElement*>(anchorNode) : 0) { SVGElement* element = SVGLocatable::nearestViewportElement(viewElement); if (element->hasTagName(SVGNames::svgTag)) { SVGSVGElement* svg = static_cast<SVGSVGElement*>(element); svg->inheritViewAttributes(viewElement); setUseCurrentView(true); } } } if (!hadUseCurrentView) { if (!m_useCurrentView) return; } else if (!m_useCurrentView) currentView()->setTransform(emptyString()); // Force a layout, otherwise RenderSVGRoots localToBorderBoxTransform won't be rebuild. if (RenderObject* object = renderer()) RenderSVGResource::markForLayoutAndParentResourceInvalidation(object); // FIXME: We need to decide which <svg> to focus on, and zoom to it. // FIXME: We need to actually "highlight" the viewTarget(s). }
void SVGElement::sendSVGLoadEventIfPossible(bool sendParentLoadEvents) { RefPtr<SVGElement> currentTarget = this; while (currentTarget && currentTarget->haveLoadedRequiredResources()) { RefPtr<Element> parent; if (sendParentLoadEvents) parent = currentTarget->parentOrShadowHostElement(); // save the next parent to dispatch too incase dispatching the event changes the tree if (hasLoadListener(currentTarget.get())) currentTarget->dispatchEvent(Event::create(eventNames().loadEvent, false, false)); currentTarget = (parent && parent->isSVGElement()) ? static_pointer_cast<SVGElement>(parent) : RefPtr<SVGElement>(); SVGElement* element = currentTarget.get(); if (!element || !element->isOutermostSVGSVGElement()) continue; // Consider <svg onload="foo()"><image xlink:href="foo.png" externalResourcesRequired="true"/></svg>. // If foo.png is not yet loaded, the first SVGLoad event will go to the <svg> element, sent through // Document::implicitClose(). Then the SVGLoad event will fire for <image>, once its loaded. ASSERT(sendParentLoadEvents); // If the load event was not sent yet by Document::implicitClose(), but the <image> from the example // above, just appeared, don't send the SVGLoad event to the outermost <svg>, but wait for the document // to be "ready to render", first. if (!document().loadEventFinished()) break; } }
PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(Filter* filter) { SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node()); bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX; // Add effects to the builder RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(filter); for (Node* node = filterElement->firstChild(); node; node = node->nextSibling()) { if (!node->isSVGElement()) continue; SVGElement* element = static_cast<SVGElement*>(node); if (!element->isFilterEffect()) continue; SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element); RefPtr<FilterEffect> effect = effectElement->build(builder.get(), filter); if (!effect) { builder->clearEffects(); return 0; } builder->appendEffectToEffectReferences(effect, effectElement->renderer()); effectElement->setStandardAttributes(primitiveBoundingBoxMode, effect.get()); builder->add(effectElement->result(), effect); } return builder.release(); }
void RenderSVGViewportContainer::calcViewport() { SVGSVGElement& svg = svgSVGElement(); FloatRect oldViewport = m_viewport; SVGLengthContext lengthContext(&svg); m_viewport = FloatRect(svg.x().value(lengthContext), svg.y().value(lengthContext), svg.width().value(lengthContext), svg.height().value(lengthContext)); SVGElement* correspondingElement = svg.correspondingElement(); if (correspondingElement && svg.isInShadowTree()) { const HashSet<SVGElementInstance*>& instances = correspondingElement->instancesForElement(); ASSERT(!instances.isEmpty()); SVGUseElement* useElement = 0; const HashSet<SVGElementInstance*>::const_iterator end = instances.end(); for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) { const SVGElementInstance* instance = (*it); ASSERT(instance->correspondingElement()->hasTagName(SVGNames::svgTag) || instance->correspondingElement()->hasTagName(SVGNames::symbolTag)); if (instance->shadowTreeElement() == &svg) { ASSERT(correspondingElement == instance->correspondingElement()); useElement = instance->directUseElement(); if (!useElement) useElement = instance->correspondingUseElement(); break; } } ASSERT(useElement); bool isSymbolElement = correspondingElement->hasTagName(SVGNames::symbolTag); // Spec (<use> on <symbol>): This generated 'svg' will always have explicit values for attributes width and height. // If attributes width and/or height are provided on the 'use' element, then these attributes // will be transferred to the generated 'svg'. If attributes width and/or height are not specified, // the generated 'svg' element will use values of 100% for these attributes. // Spec (<use> on <svg>): If attributes width and/or height are provided on the 'use' element, then these // values will override the corresponding attributes on the 'svg' in the generated tree. SVGLengthContext lengthContext(&svg); if (useElement->hasAttribute(SVGNames::widthAttr)) m_viewport.setWidth(useElement->width().value(lengthContext)); else if (isSymbolElement && svg.hasAttribute(SVGNames::widthAttr)) { SVGLength containerWidth(LengthModeWidth, "100%"); m_viewport.setWidth(containerWidth.value(lengthContext)); } if (useElement->hasAttribute(SVGNames::heightAttr)) m_viewport.setHeight(useElement->height().value(lengthContext)); else if (isSymbolElement && svg.hasAttribute(SVGNames::heightAttr)) { SVGLength containerHeight(LengthModeHeight, "100%"); m_viewport.setHeight(containerHeight.value(lengthContext)); } } if (oldViewport != m_viewport) { setNeedsBoundariesUpdate(); setNeedsTransformUpdate(); } }
bool SVGElementInstance::dispatchEvent(PassRefPtr<Event> event) { SVGElement* element = shadowTreeElement(); if (!element) return false; return element->dispatchEvent(event); }
void SVGUseElement::expandUseElementsInShadowTree(Node* element) { // Why expand the <use> elements in the shadow tree here, and not just // do this directly in buildShadowTree, if we encounter a <use> element? // // Short answer: Because we may miss to expand some elements. Ie. if a <symbol> // contains <use> tags, we'd miss them. So once we're done with settin' up the // actual shadow tree (after the special case modification for svg/symbol) we have // to walk it completely and expand all <use> elements. if (element->hasTagName(SVGNames::useTag)) { SVGUseElement* use = toSVGUseElement(element); ASSERT(!use->cachedDocumentIsStillLoading()); ASSERT(referencedDocument()); Element* targetElement = SVGURIReference::targetElementFromIRIString(use->href(), *referencedDocument()); SVGElement* target = 0; if (targetElement && targetElement->isSVGElement()) target = toSVGElement(targetElement); // Don't ASSERT(target) here, it may be "pending", too. // Setup sub-shadow tree root node RefPtr<SVGGElement> cloneParent = SVGGElement::create(SVGNames::gTag, *referencedDocument()); use->cloneChildNodes(cloneParent.get()); // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element. transferUseAttributesToReplacedElement(use, cloneParent.get()); if (target && !isDisallowedElement(*target)) { RefPtr<Element> newChild = target->cloneElementWithChildren(); ASSERT(newChild->isSVGElement()); cloneParent->appendChild(newChild.release()); } // We don't walk the target tree element-by-element, and clone each element, // but instead use cloneElementWithChildren(). This is an optimization for the common // case where <use> doesn't contain disallowed elements (ie. <foreignObject>). // Though if there are disallowed elements in the subtree, we have to remove them. // For instance: <use> on <g> containing <foreignObject> (indirect case). if (subtreeContainsDisallowedElement(*cloneParent)) removeDisallowedElementsFromSubtree(*cloneParent); RefPtr<Node> replacingElement(cloneParent.get()); // Replace <use> with referenced content. ASSERT(use->parentNode()); use->parentNode()->replaceChild(cloneParent.release(), use); // Expand the siblings because the *element* is replaced and we will // lose the sibling chain when we are back from recursion. element = replacingElement.get(); for (RefPtr<Node> sibling = element->nextSibling(); sibling; sibling = sibling->nextSibling()) expandUseElementsInShadowTree(sibling.get()); } for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling()) expandUseElementsInShadowTree(child.get()); }
PassRefPtr<FilterEffect> FilterEffectRenderer::buildReferenceFilter(RenderObject* renderer, PassRefPtr<FilterEffect> previousEffect, ReferenceFilterOperation* filterOperation) { #if ENABLE(SVG) if (!renderer) return 0; Document* document = &renderer->document(); CachedSVGDocumentReference* cachedSVGDocumentReference = filterOperation->cachedSVGDocumentReference(); CachedSVGDocument* cachedSVGDocument = cachedSVGDocumentReference ? cachedSVGDocumentReference->document() : 0; // If we have an SVG document, this is an external reference. Otherwise // we look up the referenced node in the current document. if (cachedSVGDocument) document = cachedSVGDocument->document(); if (!document) return 0; Element* filter = document->getElementById(filterOperation->fragment()); if (!filter) { // Although we did not find the referenced filter, it might exist later // in the document document->accessSVGExtensions()->addPendingResource(filterOperation->fragment(), toElement(renderer->node())); return 0; } RefPtr<FilterEffect> effect; // FIXME: Figure out what to do with SourceAlpha. Right now, we're // using the alpha of the original input layer, which is obviously // wrong. We should probably be extracting the alpha from the // previousEffect, but this requires some more processing. // This may need a spec clarification. RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(previousEffect, SourceAlpha::create(this)); for (SVGElement* svgElement = Traversal<SVGElement>::firstChild(filter); svgElement; svgElement = Traversal<SVGElement>::nextSibling(svgElement)) { if (!svgElement->isFilterEffect()) continue; SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(svgElement); effect = effectElement->build(builder.get(), this); if (!effect) continue; effectElement->setStandardAttributes(effect.get()); builder->add(effectElement->result(), effect); m_effects.append(effect); } return effect; #else UNUSED_PARAM(renderer); UNUSED_PARAM(previousEffect); UNUSED_PARAM(filterOperation); return 0; #endif }
bool SVGAnimateTransformElement::hasValidTarget() const { SVGElement* targetElement = this->targetElement(); return SVGAnimationElement::hasValidTarget() && (targetElement->isStyledTransformable() || targetElement->hasTagName(SVGNames::textTag) || targetElement->hasTagName(SVGNames::linearGradientTag) || targetElement->hasTagName(SVGNames::radialGradientTag)); }
void SVGTRefElement::updateReferencedText() { Element* targetElement = ownerDocument()->getElementById(SVGURIReference::getTarget(href())); SVGElement* target = svg_dynamic_cast(targetElement); if (target) { ExceptionCode ignore = 0; setTextContent(target->textContent(), ignore); } }
void RenderSVGResourceClipper::createDisplayList(GraphicsContext* context, const AffineTransform& contentTransformation) { ASSERT(context); ASSERT(frame()); // Using strokeBoundingBox (instead of paintInvalidationRectInLocalCoordinates) to avoid the intersection // with local clips/mask, which may yield incorrect results when mixing objectBoundingBox and // userSpaceOnUse units (http://crbug.com/294900). FloatRect bounds = strokeBoundingBox(); context->beginRecording(bounds); // Switch to a paint behavior where all children of this <clipPath> will be rendered using special constraints: // - fill-opacity/stroke-opacity/opacity set to 1 // - masker/filter not applied when rendering the children // - fill is set to the initial fill paint server (solid, black) // - stroke is set to the initial stroke paint server (none) PaintBehavior oldBehavior = frame()->view()->paintBehavior(); frame()->view()->setPaintBehavior(oldBehavior | PaintBehaviorRenderingSVGMask); for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement)) { RenderObject* renderer = childElement->renderer(); if (!renderer) continue; RenderStyle* style = renderer->style(); if (!style || style->display() == NONE || style->visibility() != VISIBLE) continue; WindRule newClipRule = style->svgStyle().clipRule(); bool isUseElement = isSVGUseElement(*childElement); if (isUseElement) { SVGUseElement& useElement = toSVGUseElement(*childElement); renderer = useElement.rendererClipChild(); if (!renderer) continue; if (!useElement.hasAttribute(SVGNames::clip_ruleAttr)) newClipRule = renderer->style()->svgStyle().clipRule(); } // Only shapes, paths and texts are allowed for clipping. if (!renderer->isSVGShape() && !renderer->isSVGText()) continue; context->setFillRule(newClipRule); if (isUseElement) renderer = childElement->renderer(); SVGRenderingContext::renderSubtree(context, renderer, contentTransformation); } frame()->view()->setPaintBehavior(oldBehavior); m_clipContentDisplayList = context->endRecording(); }
void SVGTransformable::dscale(double dsx, double dsy) { SVGScaleTransform * scale = getScale(true); scale->sx *= dsx; scale->sy *= dsy; scale->updateMatrix(); SVGElement* meAsSVGElement = dynamic_cast<SVGElement*>(this); if(meAsSVGElement) meAsSVGElement->setDamaged(true); }
static bool isDirectReference(const SVGElement& element) { return element.hasTagName(SVGNames::pathTag) || element.hasTagName(SVGNames::rectTag) || element.hasTagName(SVGNames::circleTag) || element.hasTagName(SVGNames::ellipseTag) || element.hasTagName(SVGNames::polygonTag) || element.hasTagName(SVGNames::polylineTag) || element.hasTagName(SVGNames::textTag); }
void SVGAnimateMotionElement::resetAnimatedType() { if (!hasValidAttributeType()) return; SVGElement* targetElement = this->targetElement(); if (!targetElement) return; if (AffineTransform* transform = targetElement->supplementalTransform()) transform->makeIdentity(); }
static void xmlbaseAttrSetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info) { INC_STATS("DOM.SVGElement.xmlbase._set"); SVGElement* imp = V8SVGElement::toNative(info.Holder()); V8Parameter<WithNullCheck> v = value; ExceptionCode ec = 0; imp->setXmlbase(v, ec); if (UNLIKELY(ec)) V8Proxy::setDOMException(ec); return; }
void SVGTransformable::dtranslate(double dtx, double dty) { SVGTranslateTransform * translate = getTranslate(true); translate->tx += dtx; translate->ty += dty; translate->updateMatrix(); SVGElement* meAsSVGElement = dynamic_cast<SVGElement*>(this); if(meAsSVGElement) meAsSVGElement->setDamaged(true); }
bool SVGSwitchElement::childShouldCreateRenderer(Node* child) const { for (Node* n = firstChild(); n != 0; n = n->nextSibling()) { SVGElement* element = svg_dynamic_cast(n); if (element && element->isValid()) return (n == child); // Only allow this child if it's the first valid child } return false; }
void SVGAnimateTransformElement::applyResultsToTarget() { if (!hasValidTarget()) return; // We accumulate to the target element transform list so there is not much to do here. SVGElement* targetElement = this->targetElement(); if (!targetElement) return; if (RenderObject* renderer = targetElement->renderer()) { renderer->setNeedsTransformUpdate(); RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); } // ...except in case where we have additional instances in <use> trees. const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement(); RefPtr<SVGTransformList> transformList = transformListFor(targetElement); const HashSet<SVGElementInstance*>::const_iterator end = instances.end(); for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) { SVGElement* shadowTreeElement = (*it)->shadowTreeElement(); ASSERT(shadowTreeElement); if (shadowTreeElement->isStyledTransformable()) static_cast<SVGStyledTransformableElement*>(shadowTreeElement)->setTransformBaseValue(transformList.get()); else if (shadowTreeElement->hasTagName(SVGNames::textTag)) static_cast<SVGTextElement*>(shadowTreeElement)->setTransformBaseValue(transformList.get()); else if (shadowTreeElement->hasTagName(SVGNames::linearGradientTag) || shadowTreeElement->hasTagName(SVGNames::radialGradientTag)) static_cast<SVGGradientElement*>(shadowTreeElement)->setGradientTransformBaseValue(transformList.get()); if (RenderObject* renderer = shadowTreeElement->renderer()) { renderer->setNeedsTransformUpdate(); RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); } } }
static inline void registerPendingResource(const AtomicString& id, const SVGPaint::SVGPaintType& paintType, const RenderObject* object) { if (paintType != SVGPaint::SVG_PAINTTYPE_URI) return; SVGElement* svgElement = static_cast<SVGElement*>(object->node()); ASSERT(svgElement); ASSERT(svgElement->isStyled()); object->document()->accessSVGExtensions()->addPendingResource(id, static_cast<SVGStyledElement*>(svgElement)); }
void SVGStyledElement::rebuildRenderer() const { if (!renderer() || !renderer()->isRenderPath()) return; RenderPath* renderPath = static_cast<RenderPath*>(renderer()); SVGElement* parentElement = svg_dynamic_cast(parentNode()); if (parentElement && parentElement->renderer() && parentElement->isStyled() && parentElement->childShouldCreateRenderer(const_cast<SVGStyledElement*>(this))) renderPath->setNeedsLayout(true); }
void SVGTransformable::drotate(double dangle=0) { SVGRotateTransform * rotate = getRotate(true); rotate->angle += dangle; rotate->cx = 0; rotate->cy = 0; rotate->updateMatrix(); SVGElement* meAsSVGElement = dynamic_cast<SVGElement*>(this); if(meAsSVGElement) meAsSVGElement->setDamaged(true); }
void RenderSVGViewportContainer::calcViewport() { SVGElement* element = static_cast<SVGElement*>(node()); if (element->hasTagName(SVGNames::svgTag)) { SVGSVGElement* svg = static_cast<SVGSVGElement*>(element); m_viewport = FloatRect(svg->x().value(svg) , svg->y().value(svg) , svg->width().value(svg) , svg->height().value(svg)); } }
static void cloneDataAndChildren(SVGElement& replacementClone, SVGElement& originalClone) { // This assertion checks that we don't call this with the arguments backwards. // The replacement clone is new and so it's not installed in a parent yet. ASSERT(!replacementClone.parentNode()); replacementClone.cloneDataFromElement(originalClone); originalClone.cloneChildNodes(&replacementClone); associateReplacementClonesWithOriginals(replacementClone, originalClone); removeDisallowedElementsFromSubtree(replacementClone); }
void SVGElement::synchronizeAnimatedSVGAttribute(const QualifiedName& name) const { if (!elementData() || !elementData()->animatedSVGAttributesAreDirty()) return; SVGElement* nonConstThis = const_cast<SVGElement*>(this); if (name == anyQName()) synchronizeAllAnimatedSVGAttribute(nonConstThis); else nonConstThis->localAttributeToPropertyMap().synchronizeProperty(*nonConstThis, name); }
void RenderSVGResourceMasker::calculateMaskContentPaintInvalidationRect() { for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement)) { RenderObject* renderer = childElement->renderer(); if (!renderer) continue; RenderStyle* style = renderer->style(); if (!style || style->display() == NONE || style->visibility() != VISIBLE) continue; m_maskContentBoundaries.unite(renderer->localToParentTransform().mapRect(renderer->paintInvalidationRectInLocalCoordinates())); } }
static void associateClonesWithOriginals(SVGElement& clone, SVGElement& original) { // This assertion checks that we don't call this with the arguments backwards. // The clone is new and so it's not installed in a parent yet. ASSERT(!clone.parentNode()); // The loop below works because we are associating these clones immediately, before // doing transformations like removing disallowed elements or expanding elements. clone.setCorrespondingElement(&original); for (auto pair : descendantsOfType<SVGElement>(clone, original)) pair.first.setCorrespondingElement(&pair.second); }
bool RenderSVGModelObject::checkIntersection(RenderObject* renderer, const SVGRect& rect) { if (!renderer || renderer->style()->pointerEvents() == PE_NONE) return false; if (!isGraphicsElement(renderer)) return false; AffineTransform ctm; SVGElement* svgElement = toSVGElement(renderer->node()); getElementCTM(svgElement, ctm); ASSERT(svgElement->renderer()); return intersectsAllowingEmpty(rect, ctm.mapRect(svgElement->renderer()->repaintRectInLocalCoordinates())); }
bool RenderSVGModelObject::checkEnclosure(RenderObject* renderer, const FloatRect& rect) { if (!renderer || renderer->style()->pointerEvents() == PE_NONE) return false; if (!isGraphicsElement(renderer)) return false; AffineTransform ctm; SVGElement* svgElement = toSVGElement(renderer->node()); getElementCTM(svgElement, ctm); ASSERT(svgElement->renderer()); return rect.contains(ctm.mapRect(svgElement->renderer()->repaintRectInLocalCoordinates())); }
PassRefPtr<FilterEffect> FilterEffectRenderer::buildReferenceFilter(Document* document, PassRefPtr<FilterEffect> previousEffect, ReferenceFilterOperation* filterOperation) { #if ENABLE(SVG) CachedSVGDocumentReference* cachedSVGDocumentReference = filterOperation->cachedSVGDocumentReference(); CachedSVGDocument* cachedSVGDocument = cachedSVGDocumentReference ? cachedSVGDocumentReference->document() : 0; // If we have an SVG document, this is an external reference. Otherwise // we look up the referenced node in the current document. if (cachedSVGDocument) document = cachedSVGDocument->document(); if (!document) return 0; Element* filter = document->getElementById(filterOperation->fragment()); if (!filter) return 0; RefPtr<FilterEffect> effect; // FIXME: Figure out what to do with SourceAlpha. Right now, we're // using the alpha of the original input layer, which is obviously // wrong. We should probably be extracting the alpha from the // previousEffect, but this requires some more processing. // This may need a spec clarification. RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(previousEffect, SourceAlpha::create(this)); for (Node* node = filter->firstChild(); node; node = node->nextSibling()) { if (!node->isSVGElement()) continue; SVGElement* element = static_cast<SVGElement*>(node); if (!element->isFilterEffect()) continue; SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element); effect = effectElement->build(builder.get(), this); if (!effect) continue; effectElement->setStandardAttributes(effect.get()); builder->add(effectElement->result(), effect); m_effects.append(effect); } return effect; #else UNUSED_PARAM(document); UNUSED_PARAM(previousEffect); UNUSED_PARAM(filterOperation); return 0; #endif }
void LayoutSVGResourceMasker::calculateMaskContentPaintInvalidationRect() { for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement)) { LayoutObject* layoutObject = childElement->layoutObject(); if (!layoutObject) continue; const ComputedStyle* style = layoutObject->style(); if (!style || style->display() == NONE || style->visibility() != VISIBLE) continue; m_maskContentBoundaries.unite(layoutObject->localToParentTransform().mapRect(layoutObject->paintInvalidationRectInLocalCoordinates())); } }
void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode) { RenderObject* renderer = this->renderer(); SVGViewSpec* view = m_viewSpec.get(); if (view) view->reset(); bool hadUseCurrentView = m_useCurrentView; m_useCurrentView = false; if (fragmentIdentifier.startsWith("xpointer(")) { // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491) if (renderer && hadUseCurrentView) RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); return; } if (fragmentIdentifier.startsWith("svgView(")) { if (!view) view = currentView(); // Create the SVGViewSpec. if (view->parseViewSpec(fragmentIdentifier)) m_useCurrentView = true; else view->reset(); if (renderer && (hadUseCurrentView || m_useCurrentView)) RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); return; } // Spec: If the SVG fragment identifier addresses a ‘view’ element within an SVG document (e.g., MyDrawing.svg#MyView // or MyDrawing.svg#xpointer(id('MyView'))) then the closest ancestor ‘svg’ element is displayed in the viewport. // Any view specification attributes included on the given ‘view’ element override the corresponding view specification // attributes on the closest ancestor ‘svg’ element. if (anchorNode && anchorNode->hasTagName(SVGNames::viewTag)) { if (SVGViewElement* viewElement = anchorNode->hasTagName(SVGNames::viewTag) ? static_cast<SVGViewElement*>(anchorNode) : 0) { SVGElement* element = SVGLocatable::nearestViewportElement(viewElement); if (element->hasTagName(SVGNames::svgTag)) { SVGSVGElement* svg = static_cast<SVGSVGElement*>(element); svg->inheritViewAttributes(viewElement); if (RenderObject* renderer = svg->renderer()) RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); } } return; } // FIXME: We need to decide which <svg> to focus on, and zoom to it. // FIXME: We need to actually "highlight" the viewTarget(s). }