LibWeb: Add Rust HTML parser host plumbing
Add the C++ and Rust scaffolding that lets the tree builder live in Rust while the DOM remains owned by LibWeb. Keep the exported surface small: Rust stores parser state, and C++ provides node creation, insertion, script, template, and GC hooks. Route dump-html-tree through the selectable parser backend so the new implementation can be exercised beside the existing parser while it is being brought up.
This commit is contained in:
parent
5b63cb5f37
commit
09296315c2
7 changed files with 321 additions and 19 deletions
|
|
@ -1255,6 +1255,7 @@ import_rust_crate(MANIFEST_PATH Rust/Cargo.toml CRATE_NAME libweb_rust FFI_HEADE
|
|||
target_link_libraries(LibWeb PRIVATE libweb_rust)
|
||||
get_target_property(_libweb_rust_lib libweb_rust IMPORTED_LOCATION)
|
||||
set_property(SOURCE HTML/Parser/HTMLTokenizer.cpp APPEND PROPERTY OBJECT_DEPENDS ${_libweb_rust_lib})
|
||||
set_property(SOURCE HTML/Parser/HTMLParser.cpp APPEND PROPERTY OBJECT_DEPENDS ${_libweb_rust_lib})
|
||||
if ((LINUX OR BSD) AND NOT BUILD_SHARED_LIBS)
|
||||
target_link_options(LibWeb INTERFACE LINKER:--allow-multiple-definition)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@
|
|||
#include <LibWeb/HTML/Scripting/ExceptionReporter.h>
|
||||
#include <LibWeb/HTML/Scripting/SimilarOriginWindowAgent.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
#include <LibWeb/HTMLTokenizerRustFFI.h>
|
||||
#include <LibWeb/HighResolutionTime/TimeOrigin.h>
|
||||
#include <LibWeb/Infra/CharacterTypes.h>
|
||||
#include <LibWeb/Infra/Strings.h>
|
||||
|
|
@ -54,6 +55,8 @@
|
|||
#include <LibWeb/Platform/EventLoopPlugin.h>
|
||||
#include <LibWeb/SVG/SVGScriptElement.h>
|
||||
#include <LibWeb/SVG/TagNames.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
|
|
@ -65,6 +68,40 @@ static inline void log_parse_error(SourceLocation const& location = SourceLocati
|
|||
dbgln_if(HTML_PARSER_DEBUG, "Parse error! {}", location);
|
||||
}
|
||||
|
||||
Optional<HTMLParserBackend> html_parser_backend_from_string(StringView backend)
|
||||
{
|
||||
if (backend == "cpp"sv)
|
||||
return HTMLParserBackend::Cpp;
|
||||
if (backend == "rust"sv)
|
||||
return HTMLParserBackend::Rust;
|
||||
return {};
|
||||
}
|
||||
|
||||
StringView html_parser_backend_name(HTMLParserBackend backend)
|
||||
{
|
||||
switch (backend) {
|
||||
case HTMLParserBackend::Cpp:
|
||||
return "cpp"sv;
|
||||
case HTMLParserBackend::Rust:
|
||||
return "rust"sv;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
HTMLParserBackend default_html_parser_backend()
|
||||
{
|
||||
static HTMLParserBackend s_backend = [] {
|
||||
auto* backend = getenv("LIBWEB_HTML_PARSER");
|
||||
if (!backend)
|
||||
return HTMLParserBackend::Cpp;
|
||||
if (auto parsed_backend = html_parser_backend_from_string(StringView { backend, strlen(backend) }); parsed_backend.has_value())
|
||||
return parsed_backend.value();
|
||||
dbgln("Unknown LIBWEB_HTML_PARSER value '{}'; using cpp", backend);
|
||||
return HTMLParserBackend::Cpp;
|
||||
}();
|
||||
return s_backend;
|
||||
}
|
||||
|
||||
static Vector<StringView> const s_quirks_public_ids = {
|
||||
"+//Silmaril//dtd html Pro v0r11 19970101//"sv,
|
||||
"-//AS//DTD HTML 3.0 asWedit + extensions//"sv,
|
||||
|
|
@ -159,11 +196,14 @@ static bool is_html_integration_point(DOM::Element const& element)
|
|||
return false;
|
||||
}
|
||||
|
||||
HTMLParser::HTMLParser(DOM::Document& document, ParserScriptingMode scripting_mode, StringView input, StringView encoding)
|
||||
HTMLParser::HTMLParser(DOM::Document& document, ParserScriptingMode scripting_mode, StringView input, StringView encoding, HTMLParserBackend backend)
|
||||
: m_tokenizer(input, encoding)
|
||||
, m_backend(backend)
|
||||
, m_scripting_mode(scripting_mode)
|
||||
, m_document(document)
|
||||
{
|
||||
if (m_backend == HTMLParserBackend::Rust)
|
||||
m_rust_parser = rust_html_parser_create();
|
||||
m_tokenizer.set_parser({}, *this);
|
||||
m_document->set_parser({}, *this);
|
||||
m_stack_of_open_elements.set_on_element_popped([this](DOM::Element& element) {
|
||||
|
|
@ -174,11 +214,14 @@ HTMLParser::HTMLParser(DOM::Document& document, ParserScriptingMode scripting_mo
|
|||
m_document->set_encoding(MUST(String::from_utf8(standardized_encoding.value())));
|
||||
}
|
||||
|
||||
HTMLParser::HTMLParser(DOM::Document& document, ParserScriptingMode scripting_mode, ScriptCreatedParser script_created)
|
||||
: m_scripting_mode(scripting_mode)
|
||||
HTMLParser::HTMLParser(DOM::Document& document, ParserScriptingMode scripting_mode, ScriptCreatedParser script_created, HTMLParserBackend backend)
|
||||
: m_backend(backend)
|
||||
, m_scripting_mode(scripting_mode)
|
||||
, m_script_created(script_created == ScriptCreatedParser::Yes)
|
||||
, m_document(document)
|
||||
{
|
||||
if (m_backend == HTMLParserBackend::Rust)
|
||||
m_rust_parser = rust_html_parser_create();
|
||||
m_document->set_parser({}, *this);
|
||||
m_tokenizer.set_parser({}, *this);
|
||||
m_stack_of_open_elements.set_on_element_popped([this](DOM::Element& element) {
|
||||
|
|
@ -186,8 +229,15 @@ HTMLParser::HTMLParser(DOM::Document& document, ParserScriptingMode scripting_mo
|
|||
});
|
||||
}
|
||||
|
||||
HTMLParser::~HTMLParser()
|
||||
HTMLParser::~HTMLParser() = default;
|
||||
|
||||
void HTMLParser::finalize()
|
||||
{
|
||||
Base::finalize();
|
||||
if (m_rust_parser) {
|
||||
rust_html_parser_destroy(m_rust_parser);
|
||||
m_rust_parser = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void HTMLParser::visit_edges(Cell::Visitor& visitor)
|
||||
|
|
@ -212,6 +262,11 @@ void HTMLParser::initialize(JS::Realm& realm)
|
|||
|
||||
void HTMLParser::run(HTMLTokenizer::StopAtInsertionPoint stop_at_insertion_point)
|
||||
{
|
||||
if (m_backend == HTMLParserBackend::Rust) {
|
||||
auto result = rust_html_parser_run_document(m_rust_parser);
|
||||
VERIFY(result == RustFfiHtmlParserRunResult::Unsupported);
|
||||
}
|
||||
|
||||
m_stop_parsing = false;
|
||||
|
||||
for (;;) {
|
||||
|
|
@ -5189,28 +5244,28 @@ WebIDL::ExceptionOr<Vector<GC::Root<DOM::Node>>> HTMLParser::parse_html_fragment
|
|||
GC::Ref<HTMLParser> HTMLParser::create_for_scripting(DOM::Document& document)
|
||||
{
|
||||
auto scripting_mode = document.is_scripting_enabled() ? ParserScriptingMode::Normal : ParserScriptingMode::Disabled;
|
||||
return document.realm().create<HTMLParser>(document, scripting_mode, ScriptCreatedParser::Yes);
|
||||
return document.realm().create<HTMLParser>(document, scripting_mode, ScriptCreatedParser::Yes, default_html_parser_backend());
|
||||
}
|
||||
|
||||
GC::Ref<HTMLParser> HTMLParser::create_with_open_input_stream(DOM::Document& document)
|
||||
{
|
||||
auto scripting_mode = document.is_scripting_enabled() ? ParserScriptingMode::Normal : ParserScriptingMode::Disabled;
|
||||
return document.realm().create<HTMLParser>(document, scripting_mode, ScriptCreatedParser::No);
|
||||
return document.realm().create<HTMLParser>(document, scripting_mode, ScriptCreatedParser::No, default_html_parser_backend());
|
||||
}
|
||||
|
||||
GC::Ref<HTMLParser> HTMLParser::create_with_uncertain_encoding(DOM::Document& document, ByteBuffer const& input, Optional<MimeSniff::MimeType> maybe_mime_type)
|
||||
{
|
||||
auto scripting_mode = document.is_scripting_enabled() ? ParserScriptingMode::Normal : ParserScriptingMode::Disabled;
|
||||
if (document.has_encoding())
|
||||
return document.realm().create<HTMLParser>(document, scripting_mode, input, document.encoding().value().to_byte_string());
|
||||
return document.realm().create<HTMLParser>(document, scripting_mode, input, document.encoding().value().to_byte_string(), default_html_parser_backend());
|
||||
auto encoding = run_encoding_sniffing_algorithm(document, input, maybe_mime_type);
|
||||
dbgln_if(HTML_PARSER_DEBUG, "The encoding sniffing algorithm returned encoding '{}'", encoding);
|
||||
return document.realm().create<HTMLParser>(document, scripting_mode, input, encoding);
|
||||
return document.realm().create<HTMLParser>(document, scripting_mode, input, encoding, default_html_parser_backend());
|
||||
}
|
||||
|
||||
GC::Ref<HTMLParser> HTMLParser::create(DOM::Document& document, StringView input, ParserScriptingMode scripting_mode, StringView encoding)
|
||||
GC::Ref<HTMLParser> HTMLParser::create(DOM::Document& document, StringView input, ParserScriptingMode scripting_mode, StringView encoding, HTMLParserBackend backend)
|
||||
{
|
||||
return document.realm().create<HTMLParser>(document, scripting_mode, input, encoding);
|
||||
return document.realm().create<HTMLParser>(document, scripting_mode, input, encoding, backend);
|
||||
}
|
||||
|
||||
enum class AttributeMode {
|
||||
|
|
@ -5902,4 +5957,141 @@ void HTMLParser::insert_an_element_at_the_adjusted_insertion_location(GC::Ref<DO
|
|||
}
|
||||
}
|
||||
|
||||
static StringView html_parser_ffi_string_view(u8 const* ptr, size_t len)
|
||||
{
|
||||
if (ptr == nullptr || len == 0)
|
||||
return {};
|
||||
return { ptr, len };
|
||||
}
|
||||
|
||||
static FlyString fly_string_from_html_parser_ffi(u8 const* ptr, size_t len)
|
||||
{
|
||||
return MUST(FlyString::from_utf8(html_parser_ffi_string_view(ptr, len)));
|
||||
}
|
||||
|
||||
static String string_from_html_parser_ffi(u8 const* ptr, size_t len)
|
||||
{
|
||||
return MUST(String::from_utf8(html_parser_ffi_string_view(ptr, len)));
|
||||
}
|
||||
|
||||
enum class LadybirdHtmlParserNamespace {
|
||||
HTML,
|
||||
MathML,
|
||||
SVG,
|
||||
};
|
||||
|
||||
enum class LadybirdHtmlParserQuirksMode {
|
||||
No,
|
||||
Limited,
|
||||
Yes,
|
||||
};
|
||||
|
||||
struct LadybirdHtmlParserAttribute {
|
||||
u8 const* local_name_ptr;
|
||||
size_t local_name_len;
|
||||
u8 const* value_ptr;
|
||||
size_t value_len;
|
||||
};
|
||||
|
||||
extern "C" uintptr_t ladybird_html_parser_document_node(void*);
|
||||
extern "C" void ladybird_html_parser_set_document_quirks_mode(void*, LadybirdHtmlParserQuirksMode);
|
||||
extern "C" uintptr_t ladybird_html_parser_create_document_type(void*, u8 const*, size_t, u8 const*, size_t, u8 const*, size_t);
|
||||
extern "C" uintptr_t ladybird_html_parser_create_comment(void*, u8 const*, size_t);
|
||||
extern "C" uintptr_t ladybird_html_parser_create_text_node(void*, u8 const*, size_t);
|
||||
extern "C" uintptr_t ladybird_html_parser_create_element(void*, LadybirdHtmlParserNamespace, u8 const*, size_t, LadybirdHtmlParserAttribute const*, size_t);
|
||||
extern "C" void ladybird_html_parser_append_child(uintptr_t, uintptr_t);
|
||||
|
||||
static Optional<FlyString> namespace_from_html_parser_ffi(LadybirdHtmlParserNamespace namespace_)
|
||||
{
|
||||
switch (namespace_) {
|
||||
case LadybirdHtmlParserNamespace::HTML:
|
||||
return Namespace::HTML;
|
||||
case LadybirdHtmlParserNamespace::MathML:
|
||||
return Namespace::MathML;
|
||||
case LadybirdHtmlParserNamespace::SVG:
|
||||
return Namespace::SVG;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
static DOM::QuirksMode quirks_mode_from_html_parser_ffi(LadybirdHtmlParserQuirksMode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case LadybirdHtmlParserQuirksMode::No:
|
||||
return DOM::QuirksMode::No;
|
||||
case LadybirdHtmlParserQuirksMode::Limited:
|
||||
return DOM::QuirksMode::Limited;
|
||||
case LadybirdHtmlParserQuirksMode::Yes:
|
||||
return DOM::QuirksMode::Yes;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
static HTMLParser& parser_from_html_parser_ffi(void* parser)
|
||||
{
|
||||
VERIFY(parser);
|
||||
return *reinterpret_cast<HTMLParser*>(parser);
|
||||
}
|
||||
|
||||
static DOM::Node& node_from_html_parser_ffi(uintptr_t node)
|
||||
{
|
||||
VERIFY(node);
|
||||
return *reinterpret_cast<DOM::Node*>(node);
|
||||
}
|
||||
|
||||
extern "C" uintptr_t ladybird_html_parser_document_node(void* parser)
|
||||
{
|
||||
return reinterpret_cast<uintptr_t>(&parser_from_html_parser_ffi(parser).document());
|
||||
}
|
||||
|
||||
extern "C" void ladybird_html_parser_set_document_quirks_mode(void* parser, LadybirdHtmlParserQuirksMode mode)
|
||||
{
|
||||
parser_from_html_parser_ffi(parser).document().set_quirks_mode(quirks_mode_from_html_parser_ffi(mode));
|
||||
}
|
||||
|
||||
extern "C" uintptr_t ladybird_html_parser_create_document_type(void* parser, u8 const* name_ptr, size_t name_len, u8 const* public_id_ptr, size_t public_id_len, u8 const* system_id_ptr, size_t system_id_len)
|
||||
{
|
||||
auto& html_parser = parser_from_html_parser_ffi(parser);
|
||||
auto document_type = html_parser.document().realm().create<DOM::DocumentType>(html_parser.document());
|
||||
document_type->set_name(string_from_html_parser_ffi(name_ptr, name_len));
|
||||
document_type->set_public_id(string_from_html_parser_ffi(public_id_ptr, public_id_len));
|
||||
document_type->set_system_id(string_from_html_parser_ffi(system_id_ptr, system_id_len));
|
||||
return reinterpret_cast<uintptr_t>(document_type.ptr());
|
||||
}
|
||||
|
||||
extern "C" uintptr_t ladybird_html_parser_create_comment(void* parser, u8 const* data_ptr, size_t data_len)
|
||||
{
|
||||
auto& html_parser = parser_from_html_parser_ffi(parser);
|
||||
auto comment = html_parser.document().realm().create<DOM::Comment>(html_parser.document(), Utf16String::from_utf8(string_from_html_parser_ffi(data_ptr, data_len)));
|
||||
return reinterpret_cast<uintptr_t>(comment.ptr());
|
||||
}
|
||||
|
||||
extern "C" uintptr_t ladybird_html_parser_create_text_node(void* parser, u8 const* data_ptr, size_t data_len)
|
||||
{
|
||||
auto& html_parser = parser_from_html_parser_ffi(parser);
|
||||
auto text = html_parser.document().realm().create<DOM::Text>(html_parser.document(), Utf16String::from_utf8(string_from_html_parser_ffi(data_ptr, data_len)));
|
||||
return reinterpret_cast<uintptr_t>(text.ptr());
|
||||
}
|
||||
|
||||
extern "C" uintptr_t ladybird_html_parser_create_element(void* parser, LadybirdHtmlParserNamespace namespace_, u8 const* local_name_ptr, size_t local_name_len, LadybirdHtmlParserAttribute const* attributes, size_t attribute_count)
|
||||
{
|
||||
auto& html_parser = parser_from_html_parser_ffi(parser);
|
||||
auto local_name = fly_string_from_html_parser_ffi(local_name_ptr, local_name_len);
|
||||
auto element = DOM::create_element(html_parser.document(), local_name, namespace_from_html_parser_ffi(namespace_)).release_value_but_fixme_should_propagate_errors();
|
||||
|
||||
for (size_t i = 0; i < attribute_count; ++i) {
|
||||
auto const& attribute = attributes[i];
|
||||
DOM::QualifiedName qualified_name { fly_string_from_html_parser_ffi(attribute.local_name_ptr, attribute.local_name_len), {}, {} };
|
||||
auto dom_attribute = html_parser.document().realm().create<DOM::Attr>(html_parser.document(), move(qualified_name), string_from_html_parser_ffi(attribute.value_ptr, attribute.value_len), element);
|
||||
element->append_attribute(dom_attribute);
|
||||
}
|
||||
|
||||
return reinterpret_cast<uintptr_t>(element.ptr());
|
||||
}
|
||||
|
||||
extern "C" void ladybird_html_parser_append_child(uintptr_t parent, uintptr_t child)
|
||||
{
|
||||
MUST(node_from_html_parser_ffi(parent).append_child(node_from_html_parser_ffi(child)));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,15 @@
|
|||
|
||||
namespace Web::HTML {
|
||||
|
||||
enum class HTMLParserBackend : u8 {
|
||||
Cpp,
|
||||
Rust,
|
||||
};
|
||||
|
||||
WEB_API Optional<HTMLParserBackend> html_parser_backend_from_string(StringView);
|
||||
WEB_API StringView html_parser_backend_name(HTMLParserBackend);
|
||||
WEB_API HTMLParserBackend default_html_parser_backend();
|
||||
|
||||
#define ENUMERATE_INSERTION_MODES \
|
||||
__ENUMERATE_INSERTION_MODE(Initial) \
|
||||
__ENUMERATE_INSERTION_MODE(BeforeHTML) \
|
||||
|
|
@ -49,12 +58,14 @@ class WEB_API HTMLParser final : public JS::Cell {
|
|||
friend class HTMLTokenizer;
|
||||
|
||||
public:
|
||||
~HTMLParser();
|
||||
static constexpr bool OVERRIDES_FINALIZE = true;
|
||||
|
||||
virtual ~HTMLParser() override;
|
||||
|
||||
static GC::Ref<HTMLParser> create_for_scripting(DOM::Document&);
|
||||
static GC::Ref<HTMLParser> create_with_open_input_stream(DOM::Document&);
|
||||
static GC::Ref<HTMLParser> create_with_uncertain_encoding(DOM::Document&, ByteBuffer const& input, Optional<MimeSniff::MimeType> maybe_mime_type = {});
|
||||
static GC::Ref<HTMLParser> create(DOM::Document&, StringView input, ParserScriptingMode, StringView encoding);
|
||||
static GC::Ref<HTMLParser> create(DOM::Document&, StringView input, ParserScriptingMode, StringView encoding, HTMLParserBackend = default_html_parser_backend());
|
||||
|
||||
void run(HTMLTokenizer::StopAtInsertionPoint = HTMLTokenizer::StopAtInsertionPoint::No);
|
||||
void run(URL::URL const&, HTMLTokenizer::StopAtInsertionPoint = HTMLTokenizer::StopAtInsertionPoint::No);
|
||||
|
|
@ -106,11 +117,12 @@ private:
|
|||
Yes,
|
||||
};
|
||||
|
||||
HTMLParser(DOM::Document&, ParserScriptingMode, StringView input, StringView encoding);
|
||||
HTMLParser(DOM::Document&, ParserScriptingMode, ScriptCreatedParser);
|
||||
HTMLParser(DOM::Document&, ParserScriptingMode, StringView input, StringView encoding, HTMLParserBackend);
|
||||
HTMLParser(DOM::Document&, ParserScriptingMode, ScriptCreatedParser, HTMLParserBackend);
|
||||
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void finalize() override;
|
||||
|
||||
char const* insertion_mode_name() const;
|
||||
|
||||
|
|
@ -209,6 +221,8 @@ private:
|
|||
ListOfActiveFormattingElements m_list_of_active_formatting_elements;
|
||||
|
||||
HTMLTokenizer m_tokenizer;
|
||||
HTMLParserBackend m_backend { HTMLParserBackend::Cpp };
|
||||
RustFfiHtmlParserHandle* m_rust_parser { nullptr };
|
||||
|
||||
bool m_next_line_feed_can_be_ignored { false };
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <LibWeb/HTML/Parser/HTMLToken.h>
|
||||
|
||||
struct RustFfiTokenizerHandle;
|
||||
struct RustFfiHtmlParserHandle;
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
pub mod entities;
|
||||
pub mod interned_names;
|
||||
pub mod parser;
|
||||
pub mod token;
|
||||
pub mod tokenizer;
|
||||
|
||||
|
|
|
|||
92
Libraries/LibWeb/HTML/Parser/Rust/src/parser.rs
Normal file
92
Libraries/LibWeb/HTML/Parser/Rust/src/parser.rs
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright (c) 2026-present, the Ladybird developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum RustFfiHtmlParserRunResult {
|
||||
Ok = 0,
|
||||
Unsupported = 1,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum RustFfiHtmlNamespace {
|
||||
Html = 0,
|
||||
MathMl = 1,
|
||||
Svg = 2,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum RustFfiHtmlQuirksMode {
|
||||
No = 0,
|
||||
Limited = 1,
|
||||
Yes = 2,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct RustFfiHtmlParserAttribute {
|
||||
pub local_name_ptr: *const u8,
|
||||
pub local_name_len: usize,
|
||||
pub value_ptr: *const u8,
|
||||
pub value_len: usize,
|
||||
}
|
||||
|
||||
/// Opaque handle for the Rust HTML parser, passed across the FFI boundary.
|
||||
pub struct RustFfiHtmlParserHandle {
|
||||
run_count: u64,
|
||||
}
|
||||
|
||||
/// Create a new Rust HTML parser.
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn rust_html_parser_create() -> *mut RustFfiHtmlParserHandle {
|
||||
Box::into_raw(Box::new(RustFfiHtmlParserHandle { run_count: 0 }))
|
||||
}
|
||||
|
||||
/// Run the Rust HTML parser.
|
||||
///
|
||||
/// This is intentionally small while the C++ host side is being carved out:
|
||||
/// it proves the selectable Rust parser object is linked and reachable, and
|
||||
/// gives the next step a stable ABI to extend with DOM host callbacks.
|
||||
///
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer from `rust_html_parser_create`.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn rust_html_parser_run_document(
|
||||
handle: *mut RustFfiHtmlParserHandle,
|
||||
) -> RustFfiHtmlParserRunResult {
|
||||
if handle.is_null() {
|
||||
return RustFfiHtmlParserRunResult::Unsupported;
|
||||
}
|
||||
let handle = unsafe { &mut *handle };
|
||||
handle.run_count = handle.run_count.wrapping_add(1);
|
||||
RustFfiHtmlParserRunResult::Unsupported
|
||||
}
|
||||
|
||||
/// Return how many times this parser handle has been asked to run.
|
||||
///
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer from `rust_html_parser_create`.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn rust_html_parser_run_count(handle: *const RustFfiHtmlParserHandle) -> u64 {
|
||||
if handle.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let handle = unsafe { &*handle };
|
||||
handle.run_count
|
||||
}
|
||||
|
||||
/// Destroy a Rust HTML parser.
|
||||
///
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer from `rust_html_parser_create`,
|
||||
/// and must not be used after this call.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn rust_html_parser_destroy(handle: *mut RustFfiHtmlParserHandle) {
|
||||
if !handle.is_null() {
|
||||
drop(unsafe { Box::from_raw(handle) });
|
||||
}
|
||||
}
|
||||
|
|
@ -280,7 +280,7 @@ static void dump_tree(Web::DOM::Node const& node, size_t indent)
|
|||
}
|
||||
}
|
||||
|
||||
static GC::Ref<Web::DOM::Document> parse_html(JS::Realm& realm, Web::DOM::Document const& origin_document, StringView input)
|
||||
static GC::Ref<Web::DOM::Document> parse_html(JS::Realm& realm, Web::DOM::Document const& origin_document, StringView input, Web::HTML::HTMLParserBackend backend)
|
||||
{
|
||||
auto document = Web::DOM::Document::create(realm, URL::about_blank());
|
||||
document->set_document_type(Web::DOM::Document::Type::HTML);
|
||||
|
|
@ -290,7 +290,7 @@ static GC::Ref<Web::DOM::Document> parse_html(JS::Realm& realm, Web::DOM::Docume
|
|||
document->set_allow_declarative_shadow_roots(true);
|
||||
document->set_custom_element_registry(realm.create<Web::HTML::CustomElementRegistry>(realm));
|
||||
|
||||
auto parser = Web::HTML::HTMLParser::create(document, input, Web::HTML::ParserScriptingMode::Disabled, "UTF-8"sv);
|
||||
auto parser = Web::HTML::HTMLParser::create(document, input, Web::HTML::ParserScriptingMode::Disabled, "UTF-8"sv, backend);
|
||||
parser->run(URL::about_blank());
|
||||
return document;
|
||||
}
|
||||
|
|
@ -312,7 +312,8 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
|
|||
args_parser.add_option(iterations, "Run the parser N times, reporting timing unless --silent is used", "iterations", 'n', "count");
|
||||
args_parser.parse(arguments);
|
||||
|
||||
if (parser_name != "cpp"sv) {
|
||||
auto backend = Web::HTML::html_parser_backend_from_string(parser_name);
|
||||
if (!backend.has_value()) {
|
||||
warnln("Unknown or unavailable parser backend: '{}'", parser_name);
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -353,7 +354,7 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
|
|||
auto timer = Core::ElapsedTimer::start_new();
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
auto document = parse_html(realm, origin_document, input);
|
||||
auto document = parse_html(realm, origin_document, input, backend.value());
|
||||
if (!silent && i == 0)
|
||||
dump_tree(document);
|
||||
}
|
||||
|
|
@ -362,7 +363,7 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
|
|||
auto elapsed_ms = timer.elapsed_milliseconds();
|
||||
warnln("input={}B parser={} iterations={} total={}ms ({:.3f}ms/iter)",
|
||||
input_data.size(),
|
||||
parser_name,
|
||||
Web::HTML::html_parser_backend_name(backend.value()),
|
||||
iterations,
|
||||
elapsed_ms,
|
||||
static_cast<double>(elapsed_ms) / iterations);
|
||||
|
|
|
|||
Loading…
Reference in a new issue