blob: 94f6f39e4e1f1b674433ca250153818f4f62fe42 [file] [log] [blame]
/*
* 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<SVGPathSeg> {
friend class SVGAnimatedPathSegListAnimator;
friend class SVGPathSegListBuilder;
friend class SVGPathSegListSource;
using Base = SVGPropertyList<SVGPathSeg>;
using Base::Base;
public:
static Ref<SVGPathSegList> create(SVGPropertyOwner* owner, SVGPropertyAccess access)
{
return adoptRef(*new SVGPathSegList(owner, access));
}
static Ref<SVGPathSegList> create(const SVGPathSegList& other, SVGPropertyAccess access)
{
return adoptRef(*new SVGPathSegList(other, access));
}
static Ref<SVGPathSegList> create(Ref<SVGPathSeg>&& 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<SVGPathSegList*>(this)->ensureItems();
return Base::numberOfItems();
}
ExceptionOr<void> clear()
{
itemsWillChange();
return Base::clear();
}
ExceptionOr<Ref<SVGPathSeg>> getItem(unsigned index)
{
ensureItems();
return Base::getItem(index);
}
ExceptionOr<Ref<SVGPathSeg>> initialize(Ref<SVGPathSeg>&& newItem)
{
itemsWillChange();
return Base::initialize(WTFMove(newItem));
}
ExceptionOr<Ref<SVGPathSeg>> insertItemBefore(Ref<SVGPathSeg>&& newItem, unsigned index)
{
ensureItems();
itemsWillChange();
return Base::insertItemBefore(WTFMove(newItem), index);
}
ExceptionOr<Ref<SVGPathSeg>> replaceItem(Ref<SVGPathSeg>&& newItem, unsigned index)
{
ensureItems();
itemsWillChange();
return Base::replaceItem(WTFMove(newItem), index);
}
ExceptionOr<Ref<SVGPathSeg>> removeItem(unsigned index)
{
ensureItems();
itemsWillChange();
return Base::removeItem(index);
}
ExceptionOr<Ref<SVGPathSeg>> appendItem(Ref<SVGPathSeg>&& newItem)
{
ensureItems();
appendPathSegToPathByteStream(newItem);
clearPath();
return Base::appendItem(WTFMove(newItem));
}
// Override SVGList::setItem() because replaceItem() isn't virtual.
ExceptionOr<void> setItem(unsigned index, Ref<SVGPathSeg>&& newItem)
{
auto result = replaceItem(WTFMove(newItem), index);
if (result.hasException())
return result.releaseException();
return { };
}
const SVGPathByteStream& pathByteStream() const { return const_cast<SVGPathSegList*>(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<SVGPathSeg>&& 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<SVGPathSeg>& item)
{
if (m_pathByteStream.isEmpty())
return;
Ref<SVGPathSegList> 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 = WTF::nullopt; }
void pathByteStreamWillChange()
{
clearItems();
clearPath();
}
void itemsWillChange()
{
clearPathByteStream();
clearPath();
}
SVGPathByteStream m_pathByteStream;
mutable Optional<Path> m_path;
};
}