diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index ac91e1757dd49c..d16ce775e2ec13 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -84,6 +84,7 @@ set(SOURCES DOM/ParentNode.cpp DOM/Position.cpp DOM/ProcessingInstruction.cpp + DOM/QualifiedName.cpp DOM/Range.cpp DOM/ShadowRoot.cpp DOM/StaticNodeList.cpp diff --git a/Userland/Libraries/LibWeb/DOM/QualifiedName.cpp b/Userland/Libraries/LibWeb/DOM/QualifiedName.cpp new file mode 100644 index 00000000000000..28fa677fd75dd7 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/QualifiedName.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Web::DOM { + +struct ImplTraits : public Traits { + static unsigned hash(QualifiedName::Impl* impl) + { + return pair_int_hash(impl->local_name.hash(), pair_int_hash(impl->prefix.hash(), impl->namespace_.hash())); + } + + static bool equals(QualifiedName::Impl* a, QualifiedName::Impl* b) + { + return a->local_name == b->local_name + && a->prefix == b->prefix + && a->namespace_ == b->namespace_; + } +}; + +static HashTable impls; + +static NonnullRefPtr ensure_impl(FlyString const& local_name, FlyString const& prefix, FlyString const& namespace_) +{ + auto hash = pair_int_hash(local_name.hash(), pair_int_hash(prefix.hash(), namespace_.hash())); + auto it = impls.find(hash, [&](QualifiedName::Impl* entry) { + return entry->local_name == local_name + && entry->prefix == prefix + && entry->namespace_ == namespace_; + }); + if (it != impls.end()) + return *(*it); + return adopt_ref(*new QualifiedName::Impl(local_name, prefix, namespace_)); +} + +QualifiedName::QualifiedName(FlyString const& local_name, FlyString const& prefix, FlyString const& namespace_) + : m_impl(ensure_impl(local_name, prefix, namespace_)) +{ +} + +QualifiedName::Impl::Impl(FlyString const& a_local_name, FlyString const& a_prefix, FlyString const& a_namespace) + : local_name(a_local_name) + , prefix(a_prefix) + , namespace_(a_namespace) +{ + impls.set(this); + make_internal_string(); +} + +QualifiedName::Impl::~Impl() +{ + impls.remove(this); +} + +// https://dom.spec.whatwg.org/#concept-attribute-qualified-name +// https://dom.spec.whatwg.org/#concept-element-qualified-name +void QualifiedName::Impl::make_internal_string() +{ + // This is possible to do according to the spec: "User agents could have this as an internal slot as an optimization." + if (prefix.is_null()) { + as_string = local_name; + return; + } + + as_string = String::formatted("{}:{}", prefix, local_name); +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/QualifiedName.h b/Userland/Libraries/LibWeb/DOM/QualifiedName.h index 8a63e943e7efdc..39659949289c2e 100644 --- a/Userland/Libraries/LibWeb/DOM/QualifiedName.h +++ b/Userland/Libraries/LibWeb/DOM/QualifiedName.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, the SerenityOS developers. + * Copyright (c) 2022, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ @@ -12,38 +13,27 @@ namespace Web::DOM { class QualifiedName { public: - QualifiedName(const FlyString& local_name, const FlyString& prefix, const FlyString& namespace_) - : m_local_name(local_name) - , m_prefix(prefix) - , m_namespace(namespace_) - { - make_internal_string(); - } + QualifiedName(FlyString const& local_name, FlyString const& prefix, FlyString const& namespace_); - const FlyString& local_name() const { return m_local_name; } - const FlyString& prefix() const { return m_prefix; } - const FlyString& namespace_() const { return m_namespace; } + FlyString const& local_name() const { return m_impl->local_name; } + FlyString const& prefix() const { return m_impl->prefix; } + FlyString const& namespace_() const { return m_impl->namespace_; } - const String& as_string() const { return m_as_string; } + String const& as_string() const { return m_impl->as_string; } + + struct Impl : public RefCounted { + Impl(FlyString const& local_name, FlyString const& prefix, FlyString const& namespace_); + ~Impl(); + + void make_internal_string(); + FlyString local_name; + FlyString prefix; + FlyString namespace_; + String as_string; + }; private: - FlyString m_local_name; - FlyString m_prefix; - FlyString m_namespace; - String m_as_string; - - // https://dom.spec.whatwg.org/#concept-attribute-qualified-name - // https://dom.spec.whatwg.org/#concept-element-qualified-name - void make_internal_string() - { - // This is possible to do according to the spec: "User agents could have this as an internal slot as an optimization." - if (m_prefix.is_null()) { - m_as_string = m_local_name; - return; - } - - m_as_string = String::formatted("{}:{}", m_prefix, m_local_name); - } + NonnullRefPtr m_impl; }; }