void LayoutSVGResourceContainer::markAllClientsForInvalidation(InvalidationMode mode) { if ((m_clients.isEmpty() && m_resourceClients.isEmpty()) || m_isInvalidating) return; if (m_invalidationMask & mode) return; m_invalidationMask |= mode; m_isInvalidating = true; bool needsLayout = mode == LayoutAndBoundariesInvalidation; bool markForInvalidation = mode != ParentOnlyInvalidation; for (auto* client : m_clients) { if (client->isSVGResourceContainer()) { toLayoutSVGResourceContainer(client)->removeAllClientsFromCache(markForInvalidation); continue; } if (markForInvalidation) markClientForInvalidation(client, mode); LayoutSVGResourceContainer::markForLayoutAndParentResourceInvalidation(client, needsLayout); } markAllResourceClientsForInvalidation(); m_isInvalidating = false; }
const LayoutSVGResourceContainer* LayoutSVGResourcePattern::resolveContentElement() const { ASSERT(attributes().patternContentElement()); LayoutSVGResourceContainer* expectedLayoutObject = toLayoutSVGResourceContainer( attributes().patternContentElement()->layoutObject()); // No content inheritance - avoid walking the inheritance chain. if (this == expectedLayoutObject) return this; // Walk the inheritance chain on the LayoutObject-side. If we reach the // expected LayoutObject, all is fine. If we don't, there's a cycle that // the cycle resolver did break, and the resource will be content-less. const LayoutSVGResourceContainer* contentLayoutObject = this; while (SVGResources* resources = SVGResourcesCache::cachedResourcesForLayoutObject( contentLayoutObject)) { LayoutSVGResourceContainer* linkedResource = resources->linkedResource(); if (!linkedResource) break; if (linkedResource == expectedLayoutObject) return expectedLayoutObject; contentLayoutObject = linkedResource; } // There was a cycle, just use this resource as the "content resource" even // though it will be empty (have no children). return this; }
PassRefPtr<const SkPicture> LayoutSVGResourcePattern::asPicture(const FloatRect& tileBounds, const AffineTransform& tileTransform) const { ASSERT(!m_shouldCollectPatternAttributes); AffineTransform contentTransform; if (attributes().patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) contentTransform = tileTransform; FloatRect bounds(FloatPoint(), tileBounds.size()); SkPictureBuilder pictureBuilder(bounds); ASSERT(attributes().patternContentElement()); LayoutSVGResourceContainer* patternLayoutObject = toLayoutSVGResourceContainer(attributes().patternContentElement()->layoutObject()); ASSERT(patternLayoutObject); ASSERT(!patternLayoutObject->needsLayout()); SubtreeContentTransformScope contentTransformScope(contentTransform); { TransformRecorder transformRecorder(pictureBuilder.context(), *patternLayoutObject, tileTransform); for (LayoutObject* child = patternLayoutObject->firstChild(); child; child = child->nextSibling()) SVGPaintContext::paintSubtree(&pictureBuilder.context(), child); } return pictureBuilder.endRecording(); }
void SVGMaskElement::svgAttributeChanged(const QualifiedName& attrName) { bool isLengthAttr = attrName == SVGNames::xAttr || attrName == SVGNames::yAttr || attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr; if (isLengthAttr || attrName == SVGNames::maskUnitsAttr || attrName == SVGNames::maskContentUnitsAttr || SVGTests::isKnownAttribute(attrName)) { SVGElement::InvalidationGuard invalidationGuard(this); if (isLengthAttr) { invalidateSVGPresentationAttributeStyle(); setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::fromAttribute(attrName)); updateRelativeLengthsInformation(); } LayoutSVGResourceContainer* layoutObject = toLayoutSVGResourceContainer(this->layoutObject()); if (layoutObject) layoutObject->invalidateCacheAndMarkForLayout(); return; } SVGElement::svgAttributeChanged(attrName); }
void PaintLayerFilterInfo::updateReferenceFilterClients(const FilterOperations& operations) { removeReferenceFilterClients(); for (size_t i = 0; i < operations.size(); ++i) { RefPtrWillBeRawPtr<FilterOperation> filterOperation = operations.operations().at(i); if (filterOperation->type() != FilterOperation::REFERENCE) continue; ReferenceFilterOperation* referenceFilterOperation = toReferenceFilterOperation(filterOperation.get()); DocumentResourceReference* documentReference = ReferenceFilterBuilder::documentResourceReference(referenceFilterOperation); DocumentResource* cachedSVGDocument = documentReference ? documentReference->document() : 0; if (cachedSVGDocument) { // Reference is external; wait for notifyFinished(). cachedSVGDocument->addClient(this); m_externalSVGReferences.append(cachedSVGDocument); } else { // Reference is internal; add layer as a client so we can trigger // filter paint invalidation on SVG attribute change. Element* filter = m_layer->layoutObject()->node()->document().getElementById(referenceFilterOperation->fragment()); if (!isSVGFilterElement(filter)) continue; if (filter->layoutObject()) toLayoutSVGResourceContainer(filter->layoutObject())->addClientLayer(m_layer); else toSVGFilterElement(filter)->addClient(m_layer->layoutObject()->node()); m_internalSVGReferences.append(filter); } } }
void SVGResourceClient::addFilterReference(SVGFilterElement* filter) { if (filter->layoutObject()) toLayoutSVGResourceContainer(filter->layoutObject())->addResourceClient(this); else filter->addClient(this); m_filterReferences.add(filter); }
bool LayoutSVGResourceGradient::isChildAllowed(LayoutObject* child, const ComputedStyle&) const { if (child->isSVGGradientStop()) return true; if (!child->isSVGResourceContainer()) return false; return toLayoutSVGResourceContainer(child)->isSVGPaintServer(); }
void SVGResourceClient::clearFilterReferences() { for (SVGFilterElement* filter : m_filterReferences) { if (filter->layoutObject()) toLayoutSVGResourceContainer(filter->layoutObject())->removeResourceClient(this); else filter->removeClient(this); } m_filterReferences.clear(); }
void PaintLayerFilterInfo::removeReferenceFilterClients() { for (size_t i = 0; i < m_externalSVGReferences.size(); ++i) m_externalSVGReferences.at(i)->removeClient(this); m_externalSVGReferences.clear(); for (size_t i = 0; i < m_internalSVGReferences.size(); ++i) { Element* filter = m_internalSVGReferences.at(i).get(); if (filter->layoutObject()) toLayoutSVGResourceContainer(filter->layoutObject())->removeClientLayer(m_layer); else toSVGFilterElement(filter)->removeClient(m_layer->layoutObject()->node()); } m_internalSVGReferences.clear(); }
void SVGRadialGradientElement::svgAttributeChanged(const QualifiedName& attrName) { if (!isSupportedAttribute(attrName)) { SVGGradientElement::svgAttributeChanged(attrName); return; } SVGElement::InvalidationGuard invalidationGuard(this); updateRelativeLengthsInformation(); LayoutSVGResourceContainer* renderer = toLayoutSVGResourceContainer(this->layoutObject()); if (renderer) renderer->invalidateCacheAndMarkForLayout(); }
ClipPathHelper(GraphicsContext* context, const PaintLayer& paintLayer, PaintLayerPaintingInfo& paintingInfo, LayoutRect& rootRelativeBounds, bool& rootRelativeBoundsComputed, const LayoutPoint& offsetFromRoot, PaintLayerFlags paintFlags) : m_resourceClipper(0), m_paintLayer(paintLayer), m_context(context) { const ComputedStyle& style = paintLayer.layoutObject()->styleRef(); // Clip-path, like border radius, must not be applied to the contents of a composited-scrolling container. // It must, however, still be applied to the mask layer, so that the compositor can properly mask the // scrolling contents and scrollbars. if (!paintLayer.layoutObject()->hasClipPath() || (paintLayer.needsCompositedScrolling() && !(paintFlags & PaintLayerPaintingChildClippingMaskPhase))) return; m_clipperState = SVGClipPainter::ClipperNotApplied; paintingInfo.ancestorHasClipPathClipping = true; ASSERT(style.clipPath()); if (style.clipPath()->type() == ClipPathOperation::SHAPE) { ShapeClipPathOperation* clipPath = toShapeClipPathOperation(style.clipPath()); if (clipPath->isValid()) { if (!rootRelativeBoundsComputed) { rootRelativeBounds = paintLayer.physicalBoundingBoxIncludingReflectionAndStackingChildren(offsetFromRoot); rootRelativeBoundsComputed = true; } m_clipPathRecorder.emplace(*context, *paintLayer.layoutObject(), clipPath->path(FloatRect(rootRelativeBounds))); } } else if (style.clipPath()->type() == ClipPathOperation::REFERENCE) { ReferenceClipPathOperation* referenceClipPathOperation = toReferenceClipPathOperation(style.clipPath()); Document& document = paintLayer.layoutObject()->document(); // FIXME: It doesn't work with forward or external SVG references (https://bugs.webkit.org/show_bug.cgi?id=90405) Element* element = document.getElementById(referenceClipPathOperation->fragment()); if (isSVGClipPathElement(element) && element->layoutObject()) { if (!rootRelativeBoundsComputed) { rootRelativeBounds = paintLayer.physicalBoundingBoxIncludingReflectionAndStackingChildren(offsetFromRoot); rootRelativeBoundsComputed = true; } m_resourceClipper = toLayoutSVGResourceClipper(toLayoutSVGResourceContainer(element->layoutObject())); if (!SVGClipPainter(*m_resourceClipper).prepareEffect(*paintLayer.layoutObject(), FloatRect(rootRelativeBounds), FloatRect(rootRelativeBounds), context, m_clipperState)) { // No need to post-apply the clipper if this failed. m_resourceClipper = 0; } } } }
void SVGPatternElement::svgAttributeChanged(const QualifiedName& attrName) { if (!isSupportedAttribute(attrName)) { SVGElement::svgAttributeChanged(attrName); return; } SVGElement::InvalidationGuard invalidationGuard(this); if (attrName == SVGNames::xAttr || attrName == SVGNames::yAttr || attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr) updateRelativeLengthsInformation(); LayoutSVGResourceContainer* renderer = toLayoutSVGResourceContainer(this->layoutObject()); if (renderer) renderer->invalidateCacheAndMarkForLayout(); }
void SVGElement::invalidateRelativeLengthClients(SubtreeLayoutScope* layoutScope) { if (!inDocument()) return; ASSERT(!m_inRelativeLengthClientsInvalidation); #if ENABLE(ASSERT) TemporaryChange<bool> inRelativeLengthClientsInvalidationChange(m_inRelativeLengthClientsInvalidation, true); #endif if (LayoutObject* layoutObject = this->layoutObject()) { if (hasRelativeLengths() && layoutObject->isSVGResourceContainer()) toLayoutSVGResourceContainer(layoutObject)->invalidateCacheAndMarkForLayout(layoutScope); else if (selfHasRelativeLengths()) layoutObject->setNeedsLayoutAndFullPaintInvalidation(LayoutInvalidationReason::Unknown, MarkContainerChain, layoutScope); } for (SVGElement* element : m_elementsWithRelativeLengths) { if (element != this) element->invalidateRelativeLengthClients(layoutScope); } }
void SVGResourcesCycleSolver::resolveCycles() { ASSERT(m_activeResources.isEmpty()); // If the starting LayoutObject is a resource container itself, then add it // to the active set (to break direct self-references.) if (m_layoutObject->isSVGResourceContainer()) m_activeResources.add(toLayoutSVGResourceContainer(m_layoutObject)); ResourceSet localResources; m_resources->buildSetOfResources(localResources); // This performs a depth-first search for a back-edge in all the // (potentially disjoint) graphs formed by the resources referenced by // |m_layoutObject|. for (auto* localResource : localResources) { if (m_activeResources.contains(localResource) || resourceContainsCycles(localResource)) breakCycle(localResource); } m_activeResources.clear(); }
void LayoutSVGResourceContainer::markForLayoutAndParentResourceInvalidation(LayoutObject* object, bool needsLayout) { ASSERT(object); ASSERT(object->node()); if (needsLayout && !object->documentBeingDestroyed()) object->setNeedsLayoutAndFullPaintInvalidation(LayoutInvalidationReason::SvgResourceInvalidated); removeFromCacheAndInvalidateDependencies(object, needsLayout); // Invalidate resources in ancestor chain, if needed. LayoutObject* current = object->parent(); while (current) { removeFromCacheAndInvalidateDependencies(current, needsLayout); if (current->isSVGResourceContainer()) { // This will process the rest of the ancestors. toLayoutSVGResourceContainer(current)->removeAllClientsFromCache(); break; } current = current->parent(); } }
void writeSVGResourceContainer(TextStream& ts, const LayoutObject& object, int indent) { writeStandardPrefix(ts, object, indent); Element* element = toElement(object.node()); const AtomicString& id = element->getIdAttribute(); writeNameAndQuotedValue(ts, "id", id); LayoutSVGResourceContainer* resource = toLayoutSVGResourceContainer(const_cast<LayoutObject*>(&object)); ASSERT(resource); if (resource->resourceType() == MaskerResourceType) { LayoutSVGResourceMasker* masker = toLayoutSVGResourceMasker(resource); writeNameValuePair(ts, "maskUnits", masker->maskUnits()); writeNameValuePair(ts, "maskContentUnits", masker->maskContentUnits()); ts << "\n"; } else if (resource->resourceType() == FilterResourceType) { LayoutSVGResourceFilter* filter = toLayoutSVGResourceFilter(resource); writeNameValuePair(ts, "filterUnits", filter->filterUnits()); writeNameValuePair(ts, "primitiveUnits", filter->primitiveUnits()); ts << "\n"; // Creating a placeholder filter which is passed to the builder. FloatRect dummyRect; IntRect dummyIntRect; RefPtrWillBeRawPtr<SVGFilter> dummyFilter = SVGFilter::create(dummyIntRect, dummyRect, dummyRect, true); if (RefPtrWillBeRawPtr<SVGFilterBuilder> builder = filter->buildPrimitives(dummyFilter.get())) { if (FilterEffect* lastEffect = builder->lastEffect()) lastEffect->externalRepresentation(ts, indent + 1); } } else if (resource->resourceType() == ClipperResourceType) { writeNameValuePair(ts, "clipPathUnits", toLayoutSVGResourceClipper(resource)->clipPathUnits()); ts << "\n"; } else if (resource->resourceType() == MarkerResourceType) { LayoutSVGResourceMarker* marker = toLayoutSVGResourceMarker(resource); writeNameValuePair(ts, "markerUnits", marker->markerUnits()); ts << " [ref at " << marker->referencePoint() << "]"; ts << " [angle="; if (marker->angle() == -1) ts << marker->orientType() << "]\n"; else ts << marker->angle() << "]\n"; } else if (resource->resourceType() == PatternResourceType) { LayoutSVGResourcePattern* pattern = static_cast<LayoutSVGResourcePattern*>(resource); // Dump final results that are used for layout. No use in asking SVGPatternElement for its patternUnits(), as it may // link to other patterns using xlink:href, we need to build the full inheritance chain, aka. collectPatternProperties() PatternAttributes attributes; toSVGPatternElement(pattern->element())->collectPatternAttributes(attributes); writeNameValuePair(ts, "patternUnits", attributes.patternUnits()); writeNameValuePair(ts, "patternContentUnits", attributes.patternContentUnits()); AffineTransform transform = attributes.patternTransform(); if (!transform.isIdentity()) ts << " [patternTransform=" << transform << "]"; ts << "\n"; } else if (resource->resourceType() == LinearGradientResourceType) { LayoutSVGResourceLinearGradient* gradient = static_cast<LayoutSVGResourceLinearGradient*>(resource); // Dump final results that are used for layout. No use in asking SVGGradientElement for its gradientUnits(), as it may // link to other gradients using xlink:href, we need to build the full inheritance chain, aka. collectGradientProperties() LinearGradientAttributes attributes; toSVGLinearGradientElement(gradient->element())->collectGradientAttributes(attributes); writeCommonGradientProperties(ts, attributes.spreadMethod(), attributes.gradientTransform(), attributes.gradientUnits()); ts << " [start=" << gradient->startPoint(attributes) << "] [end=" << gradient->endPoint(attributes) << "]\n"; } else if (resource->resourceType() == RadialGradientResourceType) { LayoutSVGResourceRadialGradient* gradient = toLayoutSVGResourceRadialGradient(resource); // Dump final results that are used for layout. No use in asking SVGGradientElement for its gradientUnits(), as it may // link to other gradients using xlink:href, we need to build the full inheritance chain, aka. collectGradientProperties() RadialGradientAttributes attributes; toSVGRadialGradientElement(gradient->element())->collectGradientAttributes(attributes); writeCommonGradientProperties(ts, attributes.spreadMethod(), attributes.gradientTransform(), attributes.gradientUnits()); FloatPoint focalPoint = gradient->focalPoint(attributes); FloatPoint centerPoint = gradient->centerPoint(attributes); float radius = gradient->radius(attributes); float focalRadius = gradient->focalRadius(attributes); ts << " [center=" << centerPoint << "] [focal=" << focalPoint << "] [radius=" << radius << "] [focalRadius=" << focalRadius << "]\n"; } else { ts << "\n"; } writeChildren(ts, object, indent); }