/* * Copyright (C) Research In Motion Limited 2010. All rights reserved. * Copyright (C) 2018-2019 Apple Inc. All rights reserved. * * 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. */ #pragma once #include "Path.h" #include "SVGPathByteStream.h" #include "SVGPathSeg.h" #include "SVGPropertyList.h" namespace WebCore { class SVGPathSegList final : public SVGPropertyList { friend class SVGAnimatedPathSegListAnimator; friend class SVGPathSegListBuilder; friend class SVGPathSegListSource; using Base = SVGPropertyList; using Base::Base; public: static Ref create(SVGPropertyOwner* owner, SVGPropertyAccess access) { return adoptRef(*new SVGPathSegList(owner, access)); } static Ref create(const SVGPathSegList& other, SVGPropertyAccess access) { return adoptRef(*new SVGPathSegList(other, access)); } static Ref create(Ref&& newItem) { return adoptRef(*new SVGPathSegList(WTFMove(newItem))); } SVGPathSegList& operator=(const SVGPathSegList& other) { pathByteStreamWillChange(); m_pathByteStream = other.pathByteStream(); return *this; } // Override SVGList::length() because numberOfItems() isn't virtual. unsigned length() const { return numberOfItems(); } unsigned numberOfItems() const { const_cast(this)->ensureItems(); return Base::numberOfItems(); } ExceptionOr clear() { itemsWillChange(); return Base::clear(); } ExceptionOr> getItem(unsigned index) { ensureItems(); return Base::getItem(index); } ExceptionOr> initialize(Ref&& newItem) { itemsWillChange(); return Base::initialize(WTFMove(newItem)); } ExceptionOr> insertItemBefore(Ref&& newItem, unsigned index) { ensureItems(); itemsWillChange(); return Base::insertItemBefore(WTFMove(newItem), index); } ExceptionOr> replaceItem(Ref&& newItem, unsigned index) { ensureItems(); itemsWillChange(); return Base::replaceItem(WTFMove(newItem), index); } ExceptionOr> removeItem(unsigned index) { ensureItems(); itemsWillChange(); return Base::removeItem(index); } ExceptionOr> appendItem(Ref&& newItem) { ensureItems(); appendPathSegToPathByteStream(newItem); clearPath(); return Base::appendItem(WTFMove(newItem)); } // Override SVGList::setItem() because replaceItem() isn't virtual. ExceptionOr setItem(unsigned index, Ref&& newItem) { auto result = replaceItem(WTFMove(newItem), index); if (result.hasException()) return result.releaseException(); return { }; } const SVGPathByteStream& pathByteStream() const { return const_cast(this)->pathByteStream(); } SVGPathByteStream& pathByteStream() { ensurePathByteStream(); return m_pathByteStream; } bool parse(const String& value) { pathByteStreamWillChange(); return buildSVGPathByteStreamFromString(value, m_pathByteStream, UnalteredParsing); } Path path() const { if (!m_path) m_path = buildPathFromByteStream(pathByteStream()); return *m_path; } size_t approximateMemoryCost() const { // This is an approximation for path memory cost since the path is parsed on demand. size_t pathMemoryCost = (m_pathByteStream.size() / 10) * sizeof(FloatPoint); // We need to account for the memory which is allocated by the m_path. return m_path ? pathMemoryCost + sizeof(*m_path) : pathMemoryCost; } String valueAsString() const override { String value; buildStringFromByteStream(pathByteStream(), value, UnalteredParsing); return value; } private: SVGPathSegList(const SVGPathSegList& other, SVGPropertyAccess access) : Base(other.owner(), access) , m_pathByteStream(other.pathByteStream()) { } // Used by appendPathSegToPathByteStream() to create a temporary SVGPathSegList with one item. SVGPathSegList(Ref&& newItem) { append(WTFMove(newItem)); } // Called when changing an item in the list. void commitPropertyChange(SVGProperty* property) override { itemsWillChange(); Base::commitPropertyChange(property); } void ensureItems() { if (!m_items.isEmpty() || m_pathByteStream.isEmpty()) return; buildSVGPathSegListFromByteStream(m_pathByteStream, *this, UnalteredParsing); } void ensurePathByteStream() { if (!m_pathByteStream.isEmpty() || m_items.isEmpty()) return; buildSVGPathByteStreamFromSVGPathSegList(*this, m_pathByteStream, UnalteredParsing); } // Optimize appending an SVGPathSeg to the list. Instead of creating the whole // byte stream, a temporary byte stream will be creating just for the new item // and this temporary byte stream will be appended to m_pathByteStream. void appendPathSegToPathByteStream(const Ref& item) { if (m_pathByteStream.isEmpty()) return; Ref pathSegList = SVGPathSegList::create(item.copyRef()); SVGPathByteStream pathSegStream; if (!buildSVGPathByteStreamFromSVGPathSegList(pathSegList, pathSegStream, UnalteredParsing, false)) return; m_pathByteStream.append(pathSegStream); } void clearPathByteStream() { m_pathByteStream.clear(); } void clearPath() { m_path = std::nullopt; } void pathByteStreamWillChange() { clearItems(); clearPath(); } void itemsWillChange() { clearPathByteStream(); clearPath(); } SVGPathByteStream m_pathByteStream; mutable std::optional m_path; }; }