LibWeb: Change SessionHistoryTraversalQueue to use Promises

If multiple cross-document navigations are queued on
SessionHistoryTraversalQueue, running the next entry before the current
document load is finished may result in a deadlock. If the new document
has a navigable element of its own, it will append steps to SHTQ and
hang in nested spin_until.
This change uses promises to ensure that the current document loads
before the next entry is executed.

Fixes timeouts in the imported tests.

Co-authored-by: Sam Atkins <sam@ladybird.org>
This commit is contained in:
Prajjwal 2025-07-04 10:53:28 +05:30 committed by Alexander Kalenik
parent eed4dd3745
commit 50a79c6af8
Notes: github-actions[bot] 2025-11-26 11:28:29 +00:00
20 changed files with 781 additions and 82 deletions

View file

@ -61,7 +61,7 @@ bool build_xml_document(DOM::Document& document, ByteBuffer const& data, Optiona
}
// https://html.spec.whatwg.org/multipage/document-lifecycle.html#navigate-html
static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_html_document(HTML::NavigationParams const& navigation_params)
static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_html_document(HTML::NavigationParams const& navigation_params, NonnullRefPtr<Core::Promise<Empty>> signal_to_continue_session_history_processing)
{
// To load an HTML document, given navigation params navigationParams:
@ -74,7 +74,8 @@ static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_html_document(HTML::Navi
if (document->url_string() == "about:blank"_string
&& navigation_params.response->body()->length().value_or(0) == 0) {
TRY(document->populate_with_html_head_and_body());
// Nothing else is added to the document, so mark it as loaded.
// NB: Nothing else is added to the document, so mark it as loaded and resolve the signal_to_continue_session_history_processing.
signal_to_continue_session_history_processing->resolve({});
HTML::HTMLParser::the_end(document);
}
@ -92,8 +93,10 @@ static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_html_document(HTML::Navi
// causes a load event to be fired.
else {
// FIXME: Parse as we receive the document data, instead of waiting for the whole document to be fetched first.
auto process_body = GC::create_function(document->heap(), [document, url = navigation_params.response->url().value(), mime_type = navigation_params.response->header_list()->extract_mime_type()](ByteBuffer data) {
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(document->heap(), [document = document, data = move(data), url = url, mime_type] {
auto process_body = GC::create_function(document->heap(), [document, signal_to_continue_session_history_processing, url = navigation_params.response->url().value(), mime_type = navigation_params.response->header_list()->extract_mime_type()](ByteBuffer data) {
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(document->heap(), [signal_to_continue_session_history_processing, document = document, data = move(data), url = url, mime_type] {
// NB: If document is part of a session history entry's traversal, resolve the signal_to_continue_session_history_processing.
signal_to_continue_session_history_processing->resolve({});
auto parser = HTML::HTMLParser::create_with_uncertain_encoding(document, data, mime_type);
parser->run(url);
}));
@ -112,7 +115,7 @@ static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_html_document(HTML::Navi
}
// https://html.spec.whatwg.org/multipage/document-lifecycle.html#read-xml
static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_xml_document(HTML::NavigationParams const& navigation_params, MimeSniff::MimeType type)
static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_xml_document(HTML::NavigationParams const& navigation_params, MimeSniff::MimeType type, NonnullRefPtr<Core::Promise<Empty>> signal_to_continue_session_history_processing)
{
// When faced with displaying an XML file inline, provided navigation params navigationParams and a string type, user agents
// must follow the requirements defined in XML and Namespaces in XML, XML Media Types, DOM, and other relevant specifications
@ -147,7 +150,7 @@ static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_xml_document(HTML::Navig
if (auto maybe_encoding = type.parameters().get("charset"sv); maybe_encoding.has_value())
content_encoding = maybe_encoding.value();
auto process_body = GC::create_function(document->heap(), [document, url = navigation_params.response->url().value(), content_encoding = move(content_encoding), mime = type](ByteBuffer data) {
auto process_body = GC::create_function(document->heap(), [document, signal_to_continue_session_history_processing, url = navigation_params.response->url().value(), content_encoding = move(content_encoding), mime = type](ByteBuffer data) {
Optional<TextCodec::Decoder&> decoder;
// The actual HTTP headers and other metadata, not the headers as mutated or implied by the algorithms given in this specification,
// are the ones that must be used when determining the character encoding according to the rules given in the above specifications.
@ -164,8 +167,10 @@ static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_xml_document(HTML::Navig
dbgln("XML Document contains improperly-encoded characters");
convert_to_xml_error_document(document, "XML Document contains improperly-encoded characters"_utf16);
// NOTE: This ensures that the `load` event gets fired for the frame loading this document.
// NB: This ensures that the `load` event gets fired for the frame loading this document.
document->completely_finish_loading();
// NB: If document is part of a session history entry's traversal, resolve the signal_to_continue_session_history_processing.
signal_to_continue_session_history_processing->resolve({});
return;
}
auto source = decoder->to_utf8(data);
@ -174,10 +179,14 @@ static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_xml_document(HTML::Navig
dbgln("Failed to decode XML document: {}", source.error());
convert_to_xml_error_document(document, Utf16String::formatted("Failed to decode XML document: {}", source.error()));
// NOTE: This ensures that the `load` event gets fired for the frame loading this document.
// NB: This ensures that the `load` event gets fired for the frame loading this document.
document->completely_finish_loading();
// NB: If document is part of session history traversal, resolve the signal_to_continue_session_history_processing.
signal_to_continue_session_history_processing->resolve({});
return;
}
// NB: If document is part of session history traversal, resolve the signal_to_continue_session_history_processing.
signal_to_continue_session_history_processing->resolve({});
XML::Parser parser(source.value(), { .preserve_cdata = true, .preserve_comments = true, .resolve_external_resource = resolve_xml_resource });
XMLDocumentBuilder builder { document };
auto result = parser.parse_with_listener(builder);
@ -186,7 +195,7 @@ static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_xml_document(HTML::Navig
dbgln("Failed to parse XML document: {}", result.error());
convert_to_xml_error_document(document, Utf16String::formatted("Failed to parse XML document: {}", result.error()));
// NOTE: XMLDocumentBuilder ensures that the `load` event gets fired. We don't need to do anything else here.
// NB: XMLDocumentBuilder ensures that the `load` event gets fired. We don't need to do anything else here.
}
});
@ -201,7 +210,7 @@ static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_xml_document(HTML::Navig
}
// https://html.spec.whatwg.org/multipage/document-lifecycle.html#navigate-text
static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_text_document(HTML::NavigationParams const& navigation_params, MimeSniff::MimeType type)
static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_text_document(HTML::NavigationParams const& navigation_params, MimeSniff::MimeType type, NonnullRefPtr<Core::Promise<Empty>> signal_to_continue_session_history_processing)
{
// To load a text document, given a navigation params navigationParams and a string type:
@ -228,10 +237,12 @@ static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_text_document(HTML::Navi
// document's relevant global object to have the parser to process the implied EOF character, which eventually causes a
// load event to be fired.
// FIXME: Parse as we receive the document data, instead of waiting for the whole document to be fetched first.
auto process_body = GC::create_function(document->heap(), [document, url = navigation_params.response->url().value(), mime = type](ByteBuffer data) {
auto process_body = GC::create_function(document->heap(), [document, signal_to_continue_session_history_processing, url = navigation_params.response->url().value(), mime = type](ByteBuffer data) {
auto encoding = run_encoding_sniffing_algorithm(document, data, mime);
dbgln_if(HTML_PARSER_DEBUG, "The encoding sniffing algorithm returned encoding '{}'", encoding);
// NB: If document is part of session history traversal, resolve the signal_to_continue_session_history_processing.
signal_to_continue_session_history_processing->resolve({});
auto parser = HTML::HTMLParser::create_for_scripting(document);
parser->tokenizer().update_insertion_point();
@ -266,7 +277,7 @@ static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_text_document(HTML::Navi
}
// https://html.spec.whatwg.org/multipage/document-lifecycle.html#navigate-media
static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_media_document(HTML::NavigationParams const& navigation_params, MimeSniff::MimeType type)
static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_media_document(HTML::NavigationParams const& navigation_params, MimeSniff::MimeType type, NonnullRefPtr<Core::Promise<Empty>> signal_to_continue_session_history_processing)
{
// To load a media document, given navigationParams and a string type:
@ -351,7 +362,10 @@ static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_media_document(HTML::Nav
auto& realm = document->realm();
navigation_params.response->body()->fully_read(
realm,
GC::create_function(document->heap(), [document](ByteBuffer) { HTML::HTMLParser::the_end(document); }),
GC::create_function(document->heap(), [document, signal_to_continue_session_history_processing](ByteBuffer) {
// NB: If document is part of session history traversal, resolve the signal_to_continue_session_history_processing.
signal_to_continue_session_history_processing->resolve({});
HTML::HTMLParser::the_end(document); }),
GC::create_function(document->heap(), [](JS::Value) {}),
GC::Ref { realm.global_object() });
@ -396,11 +410,13 @@ bool can_load_document_with_type(MimeSniff::MimeType const& type)
}
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#loading-a-document
GC::Ptr<DOM::Document> load_document(HTML::NavigationParams const& navigation_params)
GC::Ptr<DOM::Document> load_document(HTML::NavigationParams const& navigation_params, NonnullRefPtr<Core::Promise<Empty>> signal_to_continue_session_history_processing)
{
// To load a document given navigation params navigationParams, source snapshot params sourceSnapshotParams,
// and origin initiatorOrigin, perform the following steps. They return a Document or null.
// NB: Use Core::Promise to signal SessionHistoryTraversalQueue that it can continue to execute next entry.
// 1. Let type be the computed type of navigationParams's response.
auto supplied_type = navigation_params.response->header_list()->extract_mime_type();
auto type = MimeSniff::Resource::sniff(
@ -421,14 +437,14 @@ GC::Ptr<DOM::Document> load_document(HTML::NavigationParams const& navigation_pa
// -> an HTML MIME type
if (type.is_html()) {
// Return the result of loading an HTML document, given navigationParams.
return load_html_document(navigation_params).release_value_but_fixme_should_propagate_errors();
return load_html_document(navigation_params, signal_to_continue_session_history_processing).release_value_but_fixme_should_propagate_errors();
}
// -> an XML MIME type that is not an explicitly supported XML MIME type
// FIXME: that is not an explicitly supported XML MIME type
if (type.is_xml()) {
// Return the result of loading an XML document given navigationParams and type.
return load_xml_document(navigation_params, type).release_value_but_fixme_should_propagate_errors();
return load_xml_document(navigation_params, type, signal_to_continue_session_history_processing).release_value_but_fixme_should_propagate_errors();
}
// -> a JavaScript MIME type
@ -442,7 +458,7 @@ GC::Ptr<DOM::Document> load_document(HTML::NavigationParams const& navigation_pa
|| type.essence() == "text/plain"_string
|| type.essence() == "text/vtt"_string) {
// Return the result of loading a text document given navigationParams and type.
return load_text_document(navigation_params, type).release_value_but_fixme_should_propagate_errors();
return load_text_document(navigation_params, type, signal_to_continue_session_history_processing).release_value_but_fixme_should_propagate_errors();
}
// -> "multipart/x-mixed-replace"
@ -455,7 +471,7 @@ GC::Ptr<DOM::Document> load_document(HTML::NavigationParams const& navigation_pa
if (type.is_image()
|| type.is_audio_or_video()) {
// Return the result of loading a media document given navigationParams and type.
return load_media_document(navigation_params, type).release_value_but_fixme_should_propagate_errors();
return load_media_document(navigation_params, type, signal_to_continue_session_history_processing).release_value_but_fixme_should_propagate_errors();
}
// -> "application/pdf"

View file

@ -15,7 +15,7 @@
namespace Web {
bool build_xml_document(DOM::Document& document, ByteBuffer const& data, Optional<String> content_encoding);
GC::Ptr<DOM::Document> load_document(HTML::NavigationParams const& navigation_params);
GC::Ptr<DOM::Document> load_document(HTML::NavigationParams const& navigation_params, NonnullRefPtr<Core::Promise<Empty>> signal_to_continue_session_history_processing);
bool can_load_document_with_type(MimeSniff::MimeType const&);
// https://html.spec.whatwg.org/multipage/document-lifecycle.html#read-ua-inline

View file

@ -125,7 +125,11 @@ void HTMLIFrameElement::post_connection()
if (auto navigable = content_navigable()) {
auto traversable = navigable->traversable_navigable();
traversable->append_session_history_traversal_steps(GC::create_function(heap(), [this] {
// NB: Use Core::Promise to signal SessionHistoryTraversalQueue that it can continue to execute next entry.
auto signal_to_continue_session_history_processing = Core::Promise<Empty>::construct();
set_content_navigable_has_session_history_entry_and_ready_for_navigation();
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}));
}
})));

View file

@ -1325,8 +1325,9 @@ void Navigable::populate_session_history_entry_document(
SourceSnapshotParams const& source_snapshot_params,
TargetSnapshotParams const& target_snapshot_params,
UserNavigationInvolvement user_involvement,
NonnullRefPtr<Core::Promise<Empty>> signal_to_continue_session_history_processing,
Optional<String> navigation_id,
Navigable::NavigationParamsVariant navigation_params,
NavigationParamsVariant navigation_params,
ContentSecurityPolicy::Directives::Directive::NavigationType csp_navigation_type,
bool allow_POST,
GC::Ptr<GC::Function<void()>> completion_steps)
@ -1344,21 +1345,24 @@ void Navigable::populate_session_history_entry_document(
// 3. Let documentResource be entry's document state's resource.
auto document_resource = entry->document_state()->resource();
auto received_navigation_params = GC::create_function(heap(), [this, entry, navigation_id, user_involvement, completion_steps, csp_navigation_type](NavigationParamsVariant received_navigation_params) {
auto received_navigation_params = GC::create_function(heap(), [this, entry, navigation_id, user_involvement, completion_steps, csp_navigation_type, signal_to_continue_session_history_processing](NavigationParamsVariant received_navigation_params) {
// AD-HOC: Not in the spec but subsequent steps will fail if the navigable doesn't have an active window.
if (!active_window())
return;
// 5. Queue a global task on the navigation and traversal task source, given navigable's active window, to run these steps:
queue_global_task(Task::Source::NavigationAndTraversal, *active_window(), GC::create_function(heap(), [this, entry, received_navigation_params = move(received_navigation_params), navigation_id, user_involvement, completion_steps, csp_navigation_type]() mutable {
queue_global_task(Task::Source::NavigationAndTraversal, *active_window(), GC::create_function(heap(), [this, entry, received_navigation_params = move(received_navigation_params), navigation_id, user_involvement, completion_steps, csp_navigation_type, signal_to_continue_session_history_processing]() mutable {
// NOTE: This check is not in the spec but we should not continue navigation if navigable has been destroyed.
if (has_been_destroyed())
return;
// 1. If navigable's ongoing navigation no longer equals navigationId, then run completionSteps and abort these steps.
if (navigation_id.has_value() && (!ongoing_navigation().has<String>() || ongoing_navigation().get<String>() != *navigation_id)) {
if (completion_steps)
if (completion_steps) {
// NB: Use Core::Promise to signal SessionHistoryTraversalQueue that it can continue to execute next entry.
signal_to_continue_session_history_processing->resolve({});
completion_steps->function()();
}
return;
}
@ -1442,7 +1446,7 @@ void Navigable::populate_session_history_entry_document(
// 6. Otherwise, if navigationParams's response's status is not 204 and is not 205, then set entry's document state's document to the result of
// loading a document given navigationParams, sourceSnapshotParams, and entry's document state's initiator origin.
else if (auto const& response = received_navigation_params.get<GC::Ref<NavigationParams>>()->response; response->status() != 204 && response->status() != 205) {
auto document = load_document(received_navigation_params.get<GC::Ref<NavigationParams>>());
auto document = load_document(received_navigation_params.get<GC::Ref<NavigationParams>>(), signal_to_continue_session_history_processing);
entry->document_state()->set_document(document);
}
@ -1873,20 +1877,32 @@ void Navigable::begin_navigation(NavigateParams params)
// 9. Attempt to populate the history entry's document for historyEntry, given navigable, "navigate",
// sourceSnapshotParams, targetSnapshotParams, userInvolvement, navigationId, navigationParams,
// cspNavigationType, with allowPOST set to true and completionSteps set to the following step:
populate_session_history_entry_document(history_entry, source_snapshot_params, target_snapshot_params, user_involvement, navigation_id, navigation_params, csp_navigation_type, true, GC::create_function(heap(), [this, history_entry, history_handling, navigation_id, user_involvement] {
// NB: Use Core::Promise to signal SessionHistoryTraversalQueue that it can continue to execute next entry.
auto signal_to_continue_session_history_processing = Core::Promise<Empty>::construct();
populate_session_history_entry_document(history_entry, source_snapshot_params, target_snapshot_params, user_involvement, signal_to_continue_session_history_processing, navigation_id, navigation_params, csp_navigation_type, true, GC::create_function(heap(), [this, signal_to_continue_session_history_processing, history_entry, history_handling, navigation_id, user_involvement] {
// 1. Append session history traversal steps to navigable's traversable to finalize a cross-document navigation given navigable, historyHandling, userInvolvement, and historyEntry.
traversable_navigable()->append_session_history_traversal_steps(GC::create_function(heap(), [this, history_entry, history_handling, navigation_id, user_involvement] {
traversable_navigable()->append_session_history_traversal_steps(GC::create_function(heap(), [this, history_entry, history_handling, navigation_id, user_involvement, signal_to_continue_session_history_processing] {
if (this->has_been_destroyed()) {
// NOTE: This check is not in the spec but we should not continue navigation if navigable has been destroyed.
// AD-HOC: This check is not in the spec but we should not continue navigation if navigable has been destroyed.
set_delaying_load_events(false);
return;
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}
if (this->ongoing_navigation() != navigation_id) {
// NOTE: This check is not in the spec but we should not continue navigation if ongoing navigation id has changed.
// AD-HOC: This check is not in the spec but we should not continue navigation if ongoing navigation id has changed.
set_delaying_load_events(false);
return;
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}
finalize_a_cross_document_navigation(*this, to_history_handling_behavior(history_handling), user_involvement, history_entry);
// AD-HOC: If the document isn't active or is still loading session history traversal queue will wait
// for it to load else resolve the signal_to_continue_session_history_processing.
if (history_entry->document() && (!history_entry->document()->is_active() || history_entry->document()->ready_state() != "loading")) {
signal_to_continue_session_history_processing->resolve({});
}
return signal_to_continue_session_history_processing;
}));
}));
}));
@ -1968,12 +1984,16 @@ void Navigable::navigate_to_a_fragment(URL::URL const& url, HistoryHandlingBehav
// 17. Append the following session history synchronous navigation steps involving navigable to traversable:
traversable->append_session_history_synchronous_navigation_steps(*this, GC::create_function(heap(), [this, traversable, history_entry, entry_to_replace, navigation_id, history_handling, user_involvement] {
// NB: Use Core::Promise to signal SessionHistoryTraversalQueue that it can continue to execute next entry.
auto signal_to_continue_session_history_processing = Core::Promise<Empty>::construct();
// 1. Finalize a same-document navigation given traversable, navigable, historyEntry, entryToReplace, historyHandling, and userInvolvement.
finalize_a_same_document_navigation(*traversable, *this, history_entry, entry_to_replace, history_handling, user_involvement);
signal_to_continue_session_history_processing->resolve({});
// FIXME: 2. Invoke WebDriver BiDi fragment navigated with navigable and a new WebDriver BiDi
// navigation status whose id is navigationId, url is url, and status is "complete".
(void)navigation_id;
return signal_to_continue_session_history_processing;
}));
}
@ -2081,7 +2101,7 @@ GC::Ptr<DOM::Document> Navigable::evaluate_javascript_url(URL::URL const& url, U
user_involvement);
// 17. Return the result of loading an HTML document given navigationParams.
return load_document(navigation_params);
return load_document(navigation_params, Core::Promise<Empty>::construct());
}
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigate-to-a-javascript:-url
@ -2167,7 +2187,11 @@ void Navigable::navigate_to_a_javascript_url(URL::URL const& url, HistoryHandlin
// 13. Append session history traversal steps to targetNavigable's traversable to finalize a cross-document navigation with targetNavigable, historyHandling, userInvolvement, and historyEntry.
traversable_navigable()->append_session_history_traversal_steps(GC::create_function(heap(), [this, history_entry, history_handling, user_involvement] {
// NB: Use Core::Promise to signal SessionHistoryTraversalQueue that it can continue to execute next entry.
auto signal_to_continue_session_history_processing = Core::Promise<Empty>::construct();
finalize_a_cross_document_navigation(*this, history_handling, user_involvement, history_entry);
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}));
}
@ -2182,8 +2206,12 @@ void Navigable::reload(UserNavigationInvolvement user_involvement)
// 3. Append the following session history traversal steps to traversable:
traversable->append_session_history_traversal_steps(GC::create_function(heap(), [traversable, user_involvement] {
// NB: Use Core::Promise to signal SessionHistoryTraversalQueue that it can continue to execute next entry.
auto signal_to_continue_session_history_processing = Core::Promise<Empty>::construct();
// 1. Apply the reload history step to traversable given userInvolvement.
traversable->apply_the_reload_history_step(user_involvement);
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}));
}
@ -2406,10 +2434,13 @@ void perform_url_and_history_update_steps(DOM::Document& document, URL::URL new_
// 13. Append the following session history synchronous navigation steps involving navigable to traversable:
traversable->append_session_history_synchronous_navigation_steps(*navigable, GC::create_function(document.realm().heap(), [traversable, navigable, new_entry, entry_to_replace, history_handling] {
// NB: Use Core::Promise to signal SessionHistoryTraversalQueue that it can continue to execute next entry.
auto signal_to_continue_session_history_processing = Core::Promise<Empty>::construct();
// 1. Finalize a same-document navigation given traversable, navigable, newEntry, entryToReplace, historyHandling, and "none".
finalize_a_same_document_navigation(*traversable, *navigable, new_entry, entry_to_replace, history_handling, UserNavigationInvolvement::None);
signal_to_continue_session_history_processing->resolve({});
// 2. FIXME: Invoke WebDriver BiDi history updated with navigable.
return signal_to_continue_session_history_processing;
}));
}

View file

@ -124,6 +124,7 @@ public:
SourceSnapshotParams const& source_snapshot_params,
TargetSnapshotParams const& target_snapshot_params,
UserNavigationInvolvement user_involvement,
NonnullRefPtr<Core::Promise<Empty>> signal_to_continue_session_history_processing,
Optional<String> navigation_id = {},
NavigationParamsVariant navigation_params = Navigable::NullOrError {},
ContentSecurityPolicy::Directives::Directive::NavigationType csp_navigation_type = ContentSecurityPolicy::Directives::Directive::NavigationType::Other,

View file

@ -114,6 +114,8 @@ WebIDL::ExceptionOr<void> NavigableContainer::create_new_child_navigable(GC::Ptr
// 12. Append the following session history traversal steps to traversable:
traversable->append_session_history_traversal_steps(GC::create_function(heap(), [traversable, navigable, parent_navigable, history_entry, after_session_history_update] {
// NB: Use Core::Promise to signal SessionHistoryTraversalQueue that it can continue to execute next entry.
auto signal_to_continue_session_history_processing = Core::Promise<Empty>::construct();
// 1. Let parentDocState be parentNavigable's active session history entry's document state.
auto parent_doc_state = parent_navigable->active_session_history_entry()->document_state();
@ -143,6 +145,8 @@ WebIDL::ExceptionOr<void> NavigableContainer::create_new_child_navigable(GC::Ptr
if (after_session_history_update) {
after_session_history_update->function()();
}
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}));
return {};
@ -315,8 +319,12 @@ void NavigableContainer::destroy_the_child_navigable()
// 9. Append the following session history traversal steps to traversable:
traversable->append_session_history_traversal_steps(GC::create_function(heap(), [traversable] {
// NB: Use Core::Promise to signal SessionHistoryTraversalQueue that it can continue to execute next entry.
auto signal_to_continue_session_history_processing = Core::Promise<Empty>::construct();
// 1. Update for navigable creation/destruction given traversable.
traversable->update_for_navigable_creation_or_destruction();
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}));
}));
}

View file

@ -664,6 +664,8 @@ WebIDL::ExceptionOr<NavigationResult> Navigation::perform_a_navigation_api_trave
// 12. Append the following session history traversal steps to traversable:
traversable->append_session_history_traversal_steps(GC::create_function(heap(), [key, api_method_tracker, navigable, source_snapshot_params, traversable, this] {
// NB: Use Core::Promise to signal SessionHistoryTraversalQueue that it can continue to execute next entry.
auto signal_to_continue_session_history_processing = Core::Promise<Empty>::construct();
// 1. Let navigableSHEs be the result of getting session history entries given navigable.
auto navigable_shes = navigable->get_session_history_entries();
@ -685,15 +687,18 @@ WebIDL::ExceptionOr<NavigationResult> Navigation::perform_a_navigation_api_trave
}));
// 2. Abort these steps.
return;
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}
auto target_she = *it;
// 3. If targetSHE is navigable's active session history entry, then abort these steps.
// NOTE: This can occur if a previously queued traversal already took us to this session history entry.
// In that case the previous traversal will have dealt with apiMethodTracker already.
if (target_she == navigable->active_session_history_entry())
return;
if (target_she == navigable->active_session_history_entry()) {
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}
// 4. Let result be the result of applying the traverse history step given by targetSHE's step to traversable,
// given sourceSnapshotParams, navigable, and "none".
@ -725,6 +730,8 @@ WebIDL::ExceptionOr<NavigationResult> Navigation::perform_a_navigation_api_trave
reject_the_finished_promise(api_method_tracker, WebIDL::SecurityError::create(realm, "Navigation disallowed from this origin"_utf16));
}));
}
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}));
// 13. Return a navigation API method tracker-derived result for apiMethodTracker.

View file

@ -12,7 +12,7 @@ namespace Web::HTML {
GC_DEFINE_ALLOCATOR(SessionHistoryTraversalQueue);
GC_DEFINE_ALLOCATOR(SessionHistoryTraversalQueueEntry);
GC::Ref<SessionHistoryTraversalQueueEntry> SessionHistoryTraversalQueueEntry::create(JS::VM& vm, GC::Ref<GC::Function<void()>> steps, GC::Ptr<HTML::Navigable> target_navigable)
GC::Ref<SessionHistoryTraversalQueueEntry> SessionHistoryTraversalQueueEntry::create(JS::VM& vm, GC::Ref<GC::Function<NonnullRefPtr<Core::Promise<Empty>>()>> steps, GC::Ptr<HTML::Navigable> target_navigable)
{
return vm.heap().allocate<SessionHistoryTraversalQueueEntry>(steps, target_navigable);
}
@ -31,12 +31,20 @@ SessionHistoryTraversalQueue::SessionHistoryTraversalQueue()
m_timer->start();
return;
}
while (m_queue.size() > 0) {
if (m_current_promise && !m_current_promise->is_resolved() && !m_current_promise->is_rejected()) {
m_timer->start();
return;
}
m_is_task_running = true;
auto entry = m_queue.take_first();
entry->execute_steps();
m_current_promise = entry->execute_steps();
m_is_task_running = false;
}
m_current_promise = {};
});
}
@ -46,7 +54,7 @@ void SessionHistoryTraversalQueue::visit_edges(JS::Cell::Visitor& visitor)
visitor.visit(m_queue);
}
void SessionHistoryTraversalQueue::append(GC::Ref<GC::Function<void()>> steps)
void SessionHistoryTraversalQueue::append(GC::Ref<GC::Function<NonnullRefPtr<Core::Promise<Empty>>()>> steps)
{
m_queue.append(SessionHistoryTraversalQueueEntry::create(vm(), steps, nullptr));
if (!m_timer->is_active()) {
@ -54,7 +62,7 @@ void SessionHistoryTraversalQueue::append(GC::Ref<GC::Function<void()>> steps)
}
}
void SessionHistoryTraversalQueue::append_sync(GC::Ref<GC::Function<void()>> steps, GC::Ptr<Navigable> target_navigable)
void SessionHistoryTraversalQueue::append_sync(GC::Ref<GC::Function<NonnullRefPtr<Core::Promise<Empty>>()>> steps, GC::Ptr<Navigable> target_navigable)
{
m_queue.append(SessionHistoryTraversalQueueEntry::create(vm(), steps, target_navigable));
if (!m_timer->is_active()) {

View file

@ -7,6 +7,7 @@
#pragma once
#include <AK/Vector.h>
#include <LibCore/Promise.h>
#include <LibCore/Timer.h>
#include <LibGC/CellAllocator.h>
#include <LibGC/Function.h>
@ -23,13 +24,13 @@ struct SessionHistoryTraversalQueueEntry : public JS::Cell {
GC_DECLARE_ALLOCATOR(SessionHistoryTraversalQueueEntry);
public:
static GC::Ref<SessionHistoryTraversalQueueEntry> create(JS::VM& vm, GC::Ref<GC::Function<void()>> steps, GC::Ptr<HTML::Navigable> target_navigable);
static GC::Ref<SessionHistoryTraversalQueueEntry> create(JS::VM& vm, GC::Ref<GC::Function<NonnullRefPtr<Core::Promise<Empty>>()>> steps, GC::Ptr<HTML::Navigable> target_navigable);
GC::Ptr<HTML::Navigable> target_navigable() const { return m_target_navigable; }
void execute_steps() const { m_steps->function()(); }
NonnullRefPtr<Core::Promise<Empty>> execute_steps() const { return m_steps->function()(); }
private:
SessionHistoryTraversalQueueEntry(GC::Ref<GC::Function<void()>> steps, GC::Ptr<HTML::Navigable> target_navigable)
SessionHistoryTraversalQueueEntry(GC::Ref<GC::Function<NonnullRefPtr<Core::Promise<Empty>>()>> steps, GC::Ptr<HTML::Navigable> target_navigable)
: m_steps(steps)
, m_target_navigable(target_navigable)
{
@ -37,7 +38,7 @@ private:
virtual void visit_edges(Cell::Visitor&) override;
GC::Ref<GC::Function<void()>> m_steps;
GC::Ref<GC::Function<NonnullRefPtr<Core::Promise<Empty>>()>> m_steps;
GC::Ptr<HTML::Navigable> m_target_navigable;
};
@ -49,8 +50,8 @@ class WEB_API SessionHistoryTraversalQueue : public JS::Cell {
public:
SessionHistoryTraversalQueue();
void append(GC::Ref<GC::Function<void()>> steps);
void append_sync(GC::Ref<GC::Function<void()>> steps, GC::Ptr<Navigable> target_navigable);
void append(GC::Ref<GC::Function<NonnullRefPtr<Core::Promise<Empty>>()>> steps);
void append_sync(GC::Ref<GC::Function<NonnullRefPtr<Core::Promise<Empty>>()>> steps, GC::Ptr<Navigable> target_navigable);
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#sync-navigations-jump-queue
GC::Ptr<SessionHistoryTraversalQueueEntry> first_synchronous_navigation_steps_with_target_navigable_not_contained_in(HashTable<GC::Ref<Navigable>> const&);
@ -61,6 +62,7 @@ private:
Vector<GC::Ref<SessionHistoryTraversalQueueEntry>> m_queue;
RefPtr<Core::Timer> m_timer;
bool m_is_task_running { false };
WeakPtr<Core::Promise<Empty>> m_current_promise;
};
}

View file

@ -273,12 +273,15 @@ Vector<GC::Root<Navigable>> TraversableNavigable::get_all_navigables_whose_curre
// 1. Let targetEntry be the result of getting the target history entry given navigable and targetStep.
auto target_entry = navigable->get_the_target_history_entry(target_step);
// 2. If targetEntry is not navigable's current session history entry or targetEntry's document state's reload pending is true, then append navigable to results.
if (target_entry != navigable->current_session_history_entry() || target_entry->document_state()->reload_pending()) {
// 2. If targetEntry is not navigable's current session history entry or targetEntry's document state's reload
// pending is true, then append navigable to results.
// AD-HOC: We don't want to choose a navigable that has ongoing traversal.
if ((target_entry != navigable->current_session_history_entry() || target_entry->document_state()->reload_pending()) && !navigable->ongoing_navigation().has<Traversal>()) {
results.append(*navigable);
}
// 3. If targetEntry's document is navigable's document, and targetEntry's document state's reload pending is false, then extend navigablesToCheck with the child navigables of navigable.
// 3. If targetEntry's document is navigable's document, and targetEntry's document state's reload pending is
// false, then extend navigablesToCheck with the child navigables of navigable.
if (target_entry->document() == navigable->active_document() && !target_entry->document_state()->reload_pending()) {
navigables_to_check.extend(navigable->child_navigables());
}
@ -651,12 +654,23 @@ TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_history_
// queue a global task on the navigation and traversal task source given navigable's active window to
// run afterDocumentPopulated.
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(this->heap(), [populated_target_entry, potentially_target_specific_source_snapshot_params, target_snapshot_params, this, allow_POST, navigable, after_document_populated = GC::create_function(this->heap(), move(after_document_populated)), user_involvement] {
navigable->populate_session_history_entry_document(populated_target_entry, *potentially_target_specific_source_snapshot_params, target_snapshot_params, user_involvement, {}, Navigable::NullOrError {}, ContentSecurityPolicy::Directives::Directive::NavigationType::Other, allow_POST, GC::create_function(this->heap(), [this, after_document_populated, populated_target_entry]() mutable {
VERIFY(active_window());
queue_global_task(Task::Source::NavigationAndTraversal, *active_window(), GC::create_function(this->heap(), [after_document_populated, populated_target_entry]() mutable {
after_document_populated->function()(true, populated_target_entry);
auto signal_to_continue_session_history_processing = Core::Promise<Empty>::construct();
navigable->populate_session_history_entry_document(
populated_target_entry,
*potentially_target_specific_source_snapshot_params,
target_snapshot_params,
user_involvement,
signal_to_continue_session_history_processing,
{},
Navigable::NullOrError {},
ContentSecurityPolicy::Directives::Directive::NavigationType::Other,
allow_POST,
GC::create_function(this->heap(), [this, after_document_populated, populated_target_entry]() mutable {
VERIFY(active_window());
queue_global_task(Task::Source::NavigationAndTraversal, *active_window(), GC::create_function(this->heap(), [after_document_populated, populated_target_entry]() mutable {
after_document_populated->function()(true, populated_target_entry);
}));
}));
}));
}));
}
// Otherwise, run afterDocumentPopulated immediately.
@ -701,7 +715,7 @@ TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_history_
m_running_nested_apply_history_step = true;
// 4. Run steps.
entry->execute_steps();
entry->execute_steps()->await().release_value_but_fixme_should_propagate_errors();
// 5. Set traversable's running nested apply history step to false.
m_running_nested_apply_history_step = false;
@ -1153,6 +1167,8 @@ void TraversableNavigable::traverse_the_history_by_delta(int delta, GC::Ptr<DOM:
// 4. Append the following session history traversal steps to traversable:
append_session_history_traversal_steps(GC::create_function(heap(), [this, delta, source_snapshot_params, initiator_to_check, user_involvement] {
// NB: Use Core::Promise to signal SessionHistoryTraversalQueue that it can continue to execute next entry.
auto signal_to_continue_session_history_processing = Core::Promise<Empty>::construct();
// 1. Let allSteps be the result of getting all used history steps for traversable.
auto all_steps = get_all_used_history_steps();
@ -1164,12 +1180,15 @@ void TraversableNavigable::traverse_the_history_by_delta(int delta, GC::Ptr<DOM:
// 4. If allSteps[targetStepIndex] does not exist, then abort these steps.
if (target_step_index >= all_steps.size()) {
return;
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}
// 5. Apply the traverse history step allSteps[targetStepIndex] to traversable, given sourceSnapshotParams,
// initiatorToCheck, and userInvolvement.
apply_the_traverse_history_step(all_steps[target_step_index], source_snapshot_params, initiator_to_check, user_involvement);
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}));
}
@ -1232,6 +1251,8 @@ void TraversableNavigable::definitely_close_top_level_traversable()
// 3. Append the following session history traversal steps to traversable:
append_session_history_traversal_steps(GC::create_function(heap(), [this] {
// NB: Use Core::Promise to signal SessionHistoryTraversalQueue that it can continue to execute next entry.
auto signal_to_continue_session_history_processing = Core::Promise<Empty>::construct();
// 1. Let afterAllUnloads be an algorithm step which destroys traversable.
auto after_all_unloads = GC::create_function(heap(), [this] {
destroy_top_level_traversable();
@ -1239,6 +1260,8 @@ void TraversableNavigable::definitely_close_top_level_traversable()
// 2. Unload a document and its descendants given traversable's active document, null, and afterAllUnloads.
active_document()->unload_a_document_and_its_descendants({}, after_all_unloads);
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}));
}

View file

@ -87,12 +87,12 @@ public:
void definitely_close_top_level_traversable();
void destroy_top_level_traversable();
void append_session_history_traversal_steps(GC::Ref<GC::Function<void()>> steps)
void append_session_history_traversal_steps(GC::Ref<GC::Function<NonnullRefPtr<Core::Promise<Empty>>()>> steps)
{
m_session_history_traversal_queue->append(steps);
}
void append_session_history_synchronous_navigation_steps(GC::Ref<Navigable> target_navigable, GC::Ref<GC::Function<void()>> steps)
void append_session_history_synchronous_navigation_steps(GC::Ref<Navigable> target_navigable, GC::Ref<GC::Function<NonnullRefPtr<Core::Promise<Empty>>()>> steps)
{
m_session_history_traversal_queue->append_sync(steps, target_navigable);
}

View file

@ -403,8 +403,12 @@ Messages::WebDriverClient::BackResponse WebDriverConnection::back()
// 7. If the previous step completed results in a pageHide event firing, wait until pageShow event fires or
// timer' timeout fired flag to be set, whichever occurs first.
current_top_level_browsing_context()->top_level_traversable()->append_session_history_traversal_steps(GC::create_function(realm.heap(), [this, timer, on_complete]() {
if (timer->is_timed_out())
return;
// NB: Use Core::Promise to signal SessionHistoryTraversalQueue that it can continue to execute next entry.
auto signal_to_continue_session_history_processing = Core::Promise<Empty>::construct();
if (timer->is_timed_out()) {
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}
if (auto* document = current_top_level_browsing_context()->active_document(); document->page_showing()) {
on_complete->function()();
@ -416,6 +420,9 @@ Messages::WebDriverClient::BackResponse WebDriverConnection::back()
on_complete->function()();
});
}
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}));
});
@ -473,8 +480,12 @@ Messages::WebDriverClient::ForwardResponse WebDriverConnection::forward()
// 7. If the previous step completed results in a pageHide event firing, wait until pageShow event fires or
// timer' timeout fired flag to be set, whichever occurs first.
current_top_level_browsing_context()->top_level_traversable()->append_session_history_traversal_steps(GC::create_function(realm.heap(), [this, timer, on_complete]() {
if (timer->is_timed_out())
return;
// NB: Use Core::Promise to signal SessionHistoryTraversalQueue that it can continue to execute next entry.
auto signal_to_continue_session_history_processing = Core::Promise<Empty>::construct();
if (timer->is_timed_out()) {
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}
if (auto* document = current_top_level_browsing_context()->active_document(); document->page_showing()) {
on_complete->function()();
@ -486,6 +497,9 @@ Messages::WebDriverClient::ForwardResponse WebDriverConnection::forward()
on_complete->function()();
});
}
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}));
});

View file

@ -0,0 +1,116 @@
Harness status: OK
Found 111 tests
111 Pass
Pass html5lib_template.html 010950d55f4eccf16e9c4af1d263bb747294c646
Pass html5lib_template.html a838bd54410cef059a42eea9606356488e16535b
Pass html5lib_template.html 27fb9111f6675a7e033b867480c0afddcda161a6
Pass html5lib_template.html aee883a65775489399a003b2371d58248a6aff6f
Pass html5lib_template.html 89b17b54ab343191bf74ef5434f4d2cfac40ea97
Pass html5lib_template.html c4433556c7414cfd71f27b420f1ffc4348774f5e
Pass html5lib_template.html 3dcce7d97108b3e9ea7fa96f240ac62bf280e74b
Pass html5lib_template.html a1f587f7ea85ccfe294bd45bfb501e850cb979e0
Pass html5lib_template.html cd26a7832f13bdc135697321ca6c2fecdca6ef5d
Pass html5lib_template.html e30571d90b0e56864499961eb7be955994cf72e2
Pass html5lib_template.html 01cbe9f6a25f286b08d8dc4f7b65421e8eb3500c
Pass html5lib_template.html 96cbbcdffe02c86a8b929604c2fd5f3571a18dbe
Pass html5lib_template.html d51676f55550e960dd0f5fa7fd0bdfa20bdde046
Pass html5lib_template.html f9dfd9acfd494489c899604649a01d864741f50f
Pass html5lib_template.html ea00361c265d3ffb47ce636d919c94ca10d58911
Pass html5lib_template.html d8ebfcf7694c9d04457e796ac73049210313602e
Pass html5lib_template.html b4d5e6fe9b92e2c8f54199d7cab3da383c42add0
Pass html5lib_template.html 07724ef8f7a4fa61c77ffcd5180d3101c4781502
Pass html5lib_template.html e90f8aae8fc690540b42b3ffa3e741e7c1dfbf43
Pass html5lib_template.html 687bdf4adda88a316ec69fe20e84720acc5d1fe6
Pass html5lib_template.html 5b232642f472c2b4c0c7511fed464eebe686b427
Pass html5lib_template.html dc1ac1830a881d1532a1e6fd6d0cfa56d6571da2
Pass html5lib_template.html c58747a85e8b4f44d7ae63c04cdad783a903c25e
Pass html5lib_template.html ca59bfdaec7451f704973176fab46e582bd691b2
Pass html5lib_template.html cf807d6391a58c172b6c15c3b01d2a99ec0e6cf8
Pass html5lib_template.html 350b7ac850e46de79615308fc923649264406104
Pass html5lib_template.html a31ff44edf7f377543dabdda8141cda9bb6de134
Pass html5lib_template.html 533c5c1b5f0d0cbb1ede2cc5ae927095c5b21f0b
Pass html5lib_template.html 61f79e083005007853c4f8e431559ac8d3845cfd
Pass html5lib_template.html e802e85f36792b176b73c102c0e8761d9478621d
Pass html5lib_template.html 51d0797ff7653cd7be34458d689146e08a666c7f
Pass html5lib_template.html d60e4079a18bd6266740cc61d1ca736e9d5098ce
Pass html5lib_template.html 308709292677b4d74c108a811ad7b7acd0bdfc9c
Pass html5lib_template.html 8965cdf9c4e9936262e25c90c7a7f8673840a445
Pass html5lib_template.html 7dccda789764beb489e09be10188af9347335d05
Pass html5lib_template.html e15be51c77e1a6de35568a099ed339440ce9426d
Pass html5lib_template.html 503d3782e45940c19f096f360a092282b46ab1ea
Pass html5lib_template.html b4ab56fd9e9cebf479d14adfa523c06d16483a5e
Pass html5lib_template.html cd8bc9521f9683086a9e8529dd97314a6869daeb
Pass html5lib_template.html f915e7b3407c24b28c3aad318e5693cc774020f4
Pass html5lib_template.html 3c5eb261787b3d15aff86fa61de773fd7e439b0e
Pass html5lib_template.html 2b57775750c198d4b98b23aed74ff80a866a01f5
Pass html5lib_template.html dc3d016610f3ab532525a6c2871f03d6b62b0168
Pass html5lib_template.html 6a184d71d00580a26a8b6bd97aafe5503339f3f6
Pass html5lib_template.html ce570a6c4bcee8b72a03e25508c6dd72e3cc6c35
Pass html5lib_template.html e0c3d922f7b1f1654f02f716c3d9b31198ce3385
Pass html5lib_template.html 87e67242bf6debcf3b7dca852d10aa0f7b625b28
Pass html5lib_template.html 35ac4d4c972a01d368ed0cacb41370efef0a644d
Pass html5lib_template.html 5226c39dfc2d624ad4191b4eacb7e40c7ae528eb
Pass html5lib_template.html aa90cd4db6b12e0a47341914a90cc536eec32d64
Pass html5lib_template.html 48af1faf5fcf48a0854af5a5c33656d9ccf6736b
Pass html5lib_template.html ed3a029ba5e7f59969d65a4fc490a8f13b098cb9
Pass html5lib_template.html 6c8880d54475ad9574e203dcf2e55820b123cc64
Pass html5lib_template.html 275060925a844cb51b29bae660301de9780d68c8
Pass html5lib_template.html 9f82f6ec4c0a48c1d4dfbe6803b94abd553aea88
Pass html5lib_template.html f094bf7e94a88b86c80a0643e70c8e5ff3354698
Pass html5lib_template.html 35a07ec3b4bf26ea407dc1ddf52f14195a714059
Pass html5lib_template.html 24faa53b271f994a4ff31d5796c8ff47d6f2c3e6
Pass html5lib_template.html 0f1c491b58c2dd3c402a62e37f833bc1f1db8d21
Pass html5lib_template.html 868d918a7b5d8b5c065c15229492bc2022bfbcba
Pass html5lib_template.html 0538efa44e857596c556033a3821d424378aea3f
Pass html5lib_template.html e7d7bf3973c70d3cf9b0adad2ebed9f25be48d66
Pass html5lib_template.html c69d0ac542d477b7312bb24981127b8aa8fdb1df
Pass html5lib_template.html b496a8c13a7bd75b778bb0de489726aee952ae0c
Pass html5lib_template.html 5d6ee61de40274c9626ca78ee208d51276d5662d
Pass html5lib_template.html 9bd9687a65f258adc24450fc5cbd781fff6c038a
Pass html5lib_template.html db1baeb846d718c773324746524fbd68f2e9436e
Pass html5lib_template.html 4b0ce46c611dbcc016db272ef007f302bee0c897
Pass html5lib_template.html 1a735e1c7f28f8701f3c7fd5e9404b8911916086
Pass html5lib_template.html 0686eedec06b2db1dc283fac92c1ef1a33114c71
Pass html5lib_template.html d4dfb87ce626f12923056a6cd77448eaf4660ac2
Pass html5lib_template.html 1f295920f2937b2c8023b3761c43a0d4d9e5353c
Pass html5lib_template.html 3b91fa08fad923d387d924cff37fbf6b4c3a5712
Pass html5lib_template.html 45a1c1ad5d99ad67c573096a79253996a664e01b
Pass html5lib_template.html 0fe3a66773c6048c8f6f2c92f2611f65be972ec1
Pass html5lib_template.html be40897ca411e1507197c31ab2a9f9752a05f769
Pass html5lib_template.html dcfb1048ed5c40e406b4fbf0cde24c826713907f
Pass html5lib_template.html 78263aeea68ac97903598682013bae9c0c21d547
Pass html5lib_template.html 5aa177ef1a35bf4502dcb867d8e666288982ba99
Pass html5lib_template.html 5d303375907dc4d4380b477e0317c17b660613e9
Pass html5lib_template.html d822f726927c34b92fe102b13e63920850878f6a
Pass html5lib_template.html 07acdcaeb4fa639296d46673cf28823ddf2a6ca7
Pass html5lib_template.html 58bd846ce1be0caf7560fba2ef19e2c2070ab123
Pass html5lib_template.html 8eeee377e5ab324731cc592f1fa8abe1045ad610
Pass html5lib_template.html b30690019090149132fc228a7261c5cf2fd149fc
Pass html5lib_template.html 67a209d928804f90fdb66d070201b23f3d0c8a42
Pass html5lib_template.html 12104886b8f87daa937eac30b5ff0e1e074eaa6f
Pass html5lib_template.html 483cc9957a7225fe435112642be59abb4c459a1e
Pass html5lib_template.html 72d8ac431a154c40ab75d53a258d9d80d47689eb
Pass html5lib_template.html 1125967cbbcd404f4cb14d48270b8ec778970d77
Pass html5lib_template.html 32c963e164b9ec82c60e490bb141c1ccc70b992f
Pass html5lib_template.html 574a95fc9c9f2de3aeaa0c9ee1e6967fc3d4770d
Pass html5lib_template.html 332863a7f9e61bff32bd3427ede7a088b790d453
Pass html5lib_template.html 2121db07146781773df9e53b94fa921a805175ce
Pass html5lib_template.html 8675de267cd7e34f2febdee3feb665614d1562fe
Pass html5lib_template.html c5d26ad923a2b1e988ddd378ca4fb26eb48353e1
Pass html5lib_template.html eec1542e2fa0e9eafb7f8d4a51eae56b5a31b3c8
Pass html5lib_template.html b79387a54c3b136db0f28ed96555ff683b3947fe
Pass html5lib_template.html c477a29a4deb32d072a415fa809a84a4f2beee0c
Pass html5lib_template.html 26e4480c08e1f5f7b6ac8b8c1832ab0312e3b7c5
Pass html5lib_template.html 24b3b50fdd0bf8d5cf2ebaa6bf502d7bcfde1da4
Pass html5lib_template.html d3704c68528357189eb5826ab66eea071d6137a5
Pass html5lib_template.html d958f7d44faf772d1fb60f1a8f186f837ca735d9
Pass html5lib_template.html 3fc4d97fa68fc2658356bdbd4e051c867de8de53
Pass html5lib_template.html 94820107bbf3fab3f82de1f717e8413aead7d3a6
Pass html5lib_template.html ed920bca1fe1f5ad471bbd81adf8a41f3e2d9b06
Pass html5lib_template.html 657c00ebdda37ae060cc69633ed98482ccc29e18
Pass html5lib_template.html 649fc955a4b60ab2a5b881d94c9493eb4a545002
Pass html5lib_template.html 977041956eb9c7b9db73935168aba92f77c079f6
Pass html5lib_template.html fafee395fea124791df59bafeb1136342b64d3c6
Pass html5lib_template.html d5a8beecf5d3c53e947772ad887808d132334aa1

View file

@ -0,0 +1,117 @@
Harness status: OK
Found 112 tests
112 Pass
Pass html5lib_tests1.html 4235382bf15f93f7dd1096832ae74cc71edef4d7
Pass html5lib_tests1.html ad8515e9db0abd26469d0d2e46b42cebf606d4f3
Pass html5lib_tests1.html 2433aa5c088d78da9e7824e499f639177f56625d
Pass html5lib_tests1.html c99d322c3502e38e9d18ac6c0180fa5462ce612e
Pass html5lib_tests1.html d8473f7b5cec9d99526179f980ebf55a0beccbd3
Pass html5lib_tests1.html ac7703fbb5c62cadb25024aed762c206c187a919
Pass html5lib_tests1.html a00121213e2eb2c846a575f662e8c69389bfc44d
Pass html5lib_tests1.html 447f22e6a43ddbbc308afbc78b64b16452bc7bbb
Pass html5lib_tests1.html cdac424e0f2fb979f21a64f50793d529375c01b3
Pass html5lib_tests1.html 63587d177231d2478a6ffd25f3c830fed7cc2efe
Pass html5lib_tests1.html a3e13da13681f9f16c65334099136f3b5c235e6d
Pass html5lib_tests1.html b7a06a5aa0c19d7914b853f5ed497743bb269e56
Pass html5lib_tests1.html 3d28b753b97f460868ca65ed8fc153021a815b8c
Pass html5lib_tests1.html 83cce5b1e1e49c92c618aabf1ed60926a8736456
Pass html5lib_tests1.html b9e809bc4521004440bf558c7dc5d7dc1ae3dd40
Pass html5lib_tests1.html 60302916ab9a2128104dbf72629875ad19b5cb16
Pass html5lib_tests1.html f4ad8e574fcac3bb08070eeb345855ea7081ea1d
Pass html5lib_tests1.html d38fe13d87344a20bdf6e111988b5a09ed247913
Pass html5lib_tests1.html e5f2e91cbff6a4bc56149b889f4f9396e455c5ad
Pass html5lib_tests1.html 18b58d1de184b6866963c902ff8451dd3522f540
Pass html5lib_tests1.html 88eb93691065e212a4323f55ec326585c7c44262
Pass html5lib_tests1.html 260db4c31fb80894beb825c4793e6142f178497d
Pass html5lib_tests1.html 59d9c5e2941952f7af99ed3e21d66e4fdb1df07c
Pass html5lib_tests1.html 1e788677da8eb26f560409392ff674744c6f8b64
Pass html5lib_tests1.html 240ee32b47e30bcf34483c7a7530cfeb99a6d1f1
Pass html5lib_tests1.html e85dc012a49630206e6163cd1e0fd6c706865106
Pass html5lib_tests1.html c440d1f0223d6b3ce85955d8246fdd47ecdfb034
Pass html5lib_tests1.html 9a2ce05a0b342b377ef054385131ad4d1b5ae84b
Pass html5lib_tests1.html daa9d8440e2bc4d560536b52123b01e52aa81692
Pass html5lib_tests1.html 0b27e026dd03a356bbd78690fff7fb40cd63b606
Pass html5lib_tests1.html a2d3321d1ea23b55d9117e5c2014e4ab0fa5e224
Pass html5lib_tests1.html cc9c12dd44b43fa78ad340a91d20432e3f119c19
Pass html5lib_tests1.html 3d0cb9632b521a3fb334f567b3cdec985e85abd7
Pass html5lib_tests1.html f158c8b44e4cdf872cc5bb4d148fad3ff27ea03f
Pass html5lib_tests1.html 1dfb5ce6c1a10d870a35b24314976d887c700c42
Pass html5lib_tests1.html 1fc4fad5eead893c51be2b6aa1d705cd58ebcfe9
Pass html5lib_tests1.html f85417e345053cf627abf572911c0f7ffefe16c8
Pass html5lib_tests1.html 277ea1a5aade6c61a8386ff73086a91160caf5a2
Pass html5lib_tests1.html 32714c0ca1bae661ba9342ef275b2e6e5e025c34
Pass html5lib_tests1.html 619aa593419925064f51b0602000a2b9d13a8bc3
Pass html5lib_tests1.html 40fd9f6e4a08a69596f0dc0846d44ebd39e4913d
Pass html5lib_tests1.html 82120b3520ad73b9e11a413631e6f015ed0cf265
Pass html5lib_tests1.html a9f265e67b901f8d41ece9bb631696795327ed50
Pass html5lib_tests1.html 6325e1d53c83784c1e5092861c8b0138fb4871ad
Pass html5lib_tests1.html 451c02faba02d2768e3497fdcc8ffb0dec41640d
Pass html5lib_tests1.html fefda3429288aa79b4c9e8e9e3ba97897d0783c8
Pass html5lib_tests1.html 6328f70bb445e1dd692c36d6c92e2a2a7ed6ee0f
Pass html5lib_tests1.html 287320444eec6e3b4481d33738f971b696a2d6f0
Pass html5lib_tests1.html 65ea3a80efc973b5cd92c1db5a4520365bbb5478
Pass html5lib_tests1.html d60de29dd2f3e8a080882986d6689d74fb981619
Pass html5lib_tests1.html 343048017f1928db8ba4c0b45a4f1dd3dadf3063
Pass html5lib_tests1.html cf14f5563275bac3fe2c77f8973e882e51965b5b
Pass html5lib_tests1.html 2d8f9308951237fd0dcba3ff7709369cfd7563fd
Pass html5lib_tests1.html 87421a25ca75e2d6a165eb981921235f4de9210a
Pass html5lib_tests1.html 30d87693aead287d5a63310d2f819623455f4133
Pass html5lib_tests1.html 20d7a996a47fab66aead30ed7012d50b990dc65d
Pass html5lib_tests1.html 258c0966cc576ec8a563e41e783fa34f6b5c8baf
Pass html5lib_tests1.html f7ee5b858e93b22e1594d6d8cd0bc0def665ac02
Pass html5lib_tests1.html 6d9127103f8733d168e69cf04b576a7b0bea3d5c
Pass html5lib_tests1.html 03db7399a2d674955930611fdbcaad9f4064243a
Pass html5lib_tests1.html a0a1dcb330314ce12af02d136319a1be6a1ffa53
Pass html5lib_tests1.html 09b3483ef5f7a83aa9e3224d0335b6f9aa78ac73
Pass html5lib_tests1.html 34d56f09a1a9c0f51a8abc41be2f157faf0e8d15
Pass html5lib_tests1.html dffa0785e6c80af52950a64d8612633de58bbb29
Pass html5lib_tests1.html 076e6ac3cb344d60f6ce9a5188cf103ff053830c
Pass html5lib_tests1.html 5f14cb9c8502f09105ad83e27842625c70f3857d
Pass html5lib_tests1.html 25172f395b855b6eeb61ff95a8162a34ca92195e
Pass html5lib_tests1.html 053beabf01d70d03a0ca61b809d5d986ddf2ea3d
Pass html5lib_tests1.html bd385dbe93b192c2182e09e75d3664b96845ea0a
Pass html5lib_tests1.html 2ddaacb4f4e566ae79af5259f57f5b4dd166c19f
Pass html5lib_tests1.html d430f2fc3bd20e658ed00b55e6c27f14204a7b6f
Pass html5lib_tests1.html bbdb0a08b66b0789068ddac96a05ac39e104553d
Pass html5lib_tests1.html 37ed2a3a96026e7a78f6675154d11746aa9484e0
Pass html5lib_tests1.html 4177a74406cf1c048d2d6d6bcf774445f55ba517
Pass html5lib_tests1.html fc188652d9f486174b21d1919f35ea8c13636ca1
Pass html5lib_tests1.html c10f83cc69763e42964e08ddb976a1bd27b51e2f
Pass html5lib_tests1.html 64452c62ced87bd3cd8fd88df33a33d5531818d6
Pass html5lib_tests1.html 96dc04673021ad113df40397113df972f090c1f6
Pass html5lib_tests1.html 26b7eb0b18d9cd0a69d19c7f6ee9f12c2d0b2783
Pass html5lib_tests1.html f8d500cd7089942814fa0751c75bd37e63790685
Pass html5lib_tests1.html 3b386c205ec767f842e63491d14ec90192f562dd
Pass html5lib_tests1.html ca8ecc666a82d1f2f48a095d42d9f95700e015bd
Pass html5lib_tests1.html 6727ab7c4240bf05f0a5d9ca4b384ca8d61e2d4f
Pass html5lib_tests1.html 3ad08edf5b9690be261dc375da3b1d9ec82e499a
Pass html5lib_tests1.html 06ed0f32cfd261010c9d810ff8317ef96b47c04c
Pass html5lib_tests1.html 44ea84c7e4e401c9d3f96d7cc39709e4be81edc8
Pass html5lib_tests1.html 67af290f1b04c4b1a67131edba1ee832c690432c
Pass html5lib_tests1.html 2f1899f72fafcb062418e8ce892188040de4708c
Pass html5lib_tests1.html ed2a4958c832ef6cec993cb52afc808132714d0a
Pass html5lib_tests1.html c7943ccd9d880664b0894a2035e1f2a837f37c7a
Pass html5lib_tests1.html bbc836b1f494223d4eb8982930d693489d135740
Pass html5lib_tests1.html 617fdf08035740698b2f0f4c3874dbb469fd1848
Pass html5lib_tests1.html e901fe2093e51eccb4d9d23103214bc527af265c
Pass html5lib_tests1.html 63970995ab6b8aee63de9e7a7667178d4fc86820
Pass html5lib_tests1.html d7607fdd41625431bcbee319a86db1b73fc49edd
Pass html5lib_tests1.html ce31a583a3921e306aaa558b1ea798b34d2bb0dc
Pass html5lib_tests1.html 04ba864e740f7ef104570ddc6af834e6336031ed
Pass html5lib_tests1.html ea6ab4a56efd19cc04b5656583ea6d5c9cfd2752
Pass html5lib_tests1.html 1bb5dfba014f63c0a18b12097ed9a28d64552e07
Pass html5lib_tests1.html a2dd4d5a28a61ec99ce9dae35e9d4ffe92812e2f
Pass html5lib_tests1.html e26f001557952154c308e3e6f6d1789cf711ef27
Pass html5lib_tests1.html 34c1f9c212198edd7baf56e658db21d98c59f74c
Pass html5lib_tests1.html e1c47adbde197e3eaa9504f3582fd9deac1cbbff
Pass html5lib_tests1.html 0f5aba4c1ed57ac3c6a2774f3bf43ef598bd9915
Pass html5lib_tests1.html d11ff75681c4df2ff24ad731ff244afc3e1c87af
Pass html5lib_tests1.html fa392f5dd6f2f6a73635c06208c8989caa874588
Pass html5lib_tests1.html 5815b2afbb0a7f4756af7914407f71f469658f38
Pass html5lib_tests1.html a100bb6a2a80bec65c418d672144b6f647fbd46b
Pass html5lib_tests1.html f66797fd63c8b0254b4ef28e9c38c3d4e512c93c
Pass html5lib_tests1.html f10a41faca4cf01e39ed92c4eefff0631d1195dc
Pass html5lib_tests1.html 5d14e20ae19e0b3e14dcb997e3ccad5f0e1956e1
Pass html5lib_tests1.html 1949f8c612e660985fb5eb28235b6f9386ed4ffc

View file

@ -0,0 +1,19 @@
Harness status: OK
Found 14 tests
14 Pass
Pass html5lib_tests15.html 6b53427ced5c2da3830c5053af874df81b59f4dc
Pass html5lib_tests15.html e4719f96139431e67dc783631eb1b3b25e0e62e8
Pass html5lib_tests15.html 6c880904728529240130ab72c30a1f800502e1fc
Pass html5lib_tests15.html 59cae979c2d8110a1015fd401afb42d1015e2c05
Pass html5lib_tests15.html fdac591df8c2ff9db7bdd63adee17e002a620ea1
Pass html5lib_tests15.html 72352f74535e62a2b0623446073f3edb7698bcf3
Pass html5lib_tests15.html 93c2841d64ce8d4745de6a0fbd28ddba273fd3d0
Pass html5lib_tests15.html e83c7f01a04d3ff180019d1578cc666f67ea71eb
Pass html5lib_tests15.html 8ed7e05d49dfc6701ef8325a5d9504d596d8d083
Pass html5lib_tests15.html 938af694979b4eae59e7bd3ab71d76e5254192a0
Pass html5lib_tests15.html e7bba1876ae72e36eb68142d21f64fee1043cff6
Pass html5lib_tests15.html fc3373f9cb00e32fc98435157d1d74a6a27a7fb9
Pass html5lib_tests15.html f4c1486ad877d96aeb9a31fde16d2a93ada53b35
Pass html5lib_tests15.html c1176256398117ceca6c3878106893855c39aa5a

View file

@ -0,0 +1,196 @@
Harness status: OK
Found 191 tests
191 Pass
Pass html5lib_tests16.html 6d8b9d29f1890d59ef2453cff3f6d57b7e398c5c
Pass html5lib_tests16.html 5d4ac4961f9d52a42f309886d16fbe9c55c198bb
Pass html5lib_tests16.html 132c6e3cd2659e15b69904c67981a04e81fabe78
Pass html5lib_tests16.html bf0b3062e7cbe684380581919947333beef23a8c
Pass html5lib_tests16.html cd76a82b9a4bde442e4f8819b37e6308e3eef8f5
Pass html5lib_tests16.html 747087d5bf2fe9a8a6c0ecf822211d93a4e0cc2a
Pass html5lib_tests16.html 413482922b0185970bfdd6008e6a0e70ad1b554f
Pass html5lib_tests16.html 0e3cc8b1f36a34fb3048bb4f01e0e7fec678ceef
Pass html5lib_tests16.html 3d7a659a8880588e831c7198867b65ac2a974353
Pass html5lib_tests16.html 46914793d44763c3cd37c3aef0c3689d826602d1
Pass html5lib_tests16.html bc8ed1aea5ac5d7eac284386a5defe779eeab3d7
Pass html5lib_tests16.html 48e7e206aade47bdd9ad3a7cce2c9c86fb4227f6
Pass html5lib_tests16.html cc10f706cec3e9356f8c42f77b970f669f74be03
Pass html5lib_tests16.html 9a6506b01fabf7ba73bb5c90f11b3898c2f119fa
Pass html5lib_tests16.html f840264cf775999580e621a83d34af302e139632
Pass html5lib_tests16.html efe27c508629d48cf36861e680918f11f48aad15
Pass html5lib_tests16.html c51f18f140335e61f0158fadd282fb0f0c75bee6
Pass html5lib_tests16.html 76d621ce4bd9e462bacaa40ebf43e1ccb569bd21
Pass html5lib_tests16.html 70e4352779315880955134dfe67c53acb76c4850
Pass html5lib_tests16.html b05be3f93446c26026591cbfee84b9603cd6f151
Pass html5lib_tests16.html 3f08f2e2326b621f819b73336f502610dd94d54f
Pass html5lib_tests16.html 4932d705ce9c31d4141a630d305e16ae130982e7
Pass html5lib_tests16.html accb817d72edbb0d9f72e2c44f47055fb1719d0a
Pass html5lib_tests16.html b7b2e78af3f5846dc7f67246c92d95aacc2bd996
Pass html5lib_tests16.html 52e03d2903a9556823275541c58c173ac077a2a9
Pass html5lib_tests16.html 1daec6e34a3b4b4ea28f3e90595052090e67cbf7
Pass html5lib_tests16.html 539e26d76efe146f95bd7b6bfa88ae2d29afd35a
Pass html5lib_tests16.html ab43ce067468a33bb658e0d6cc542b9dbcd0c80f
Pass html5lib_tests16.html 3f93565e7a692675cc519326cc4122b5ea44b533
Pass html5lib_tests16.html e579c03d00de7a95bad40602af783b4d7775ab78
Pass html5lib_tests16.html 1541ebd513bc357af538635050f1b3ec854648e5
Pass html5lib_tests16.html ae91f664e0c85f63d21dddaa03a9104d27a9d5ce
Pass html5lib_tests16.html 17b1bf0912a302c2bed5358791fae3ea6d3efa7d
Pass html5lib_tests16.html beac9d7ec99b6317c8504e80118dd0f2d5fad573
Pass html5lib_tests16.html d5ead5851ba4d1cdac136d97a449e6b48b0c2cb4
Pass html5lib_tests16.html 17fe63597371d22c41ccbc4abcd8f468373559d7
Pass html5lib_tests16.html 26a585731ba7caa063ad5c87a09748a223e56639
Pass html5lib_tests16.html 91dc36fa03b62334c115db6d4b4a420ef1081753
Pass html5lib_tests16.html b0ee0820468ee622b802020e20be120d3c534af2
Pass html5lib_tests16.html 65907331b39d13cc2128eb57afb7185f0173a953
Pass html5lib_tests16.html c284310f4becf9d64252eaec25fe46de8c6e4f2c
Pass html5lib_tests16.html 63bbe135b3dbb75f2262ce1cc5e9259634596a55
Pass html5lib_tests16.html c83e5a48a9409482b62903960b0a04a932832668
Pass html5lib_tests16.html 193aa9aa570a39d74e340e8d6ddd015ee0a7477c
Pass html5lib_tests16.html 0ce3d12fa6ac40027b789b09fd987194426e09e6
Pass html5lib_tests16.html fd613c2eb713e158fae7a54c45a3d952efa3d5a7
Pass html5lib_tests16.html 14220896fd483fbeae58f2d69975acc99682be0c
Pass html5lib_tests16.html 11ca3aa17a2c0b1b6cfc0fb842105247272fbc8d
Pass html5lib_tests16.html b524c040a6ab671b006713c8f117d494b11e92ce
Pass html5lib_tests16.html 9032e7dd5c12caaaf2baf259cee186aaaa67e0e7
Pass html5lib_tests16.html aea756cf197079dc506242b729a1d16231644e31
Pass html5lib_tests16.html 7446d6b66ccedb48db489df92102a0cf439004ce
Pass html5lib_tests16.html 935849b22e851061994b7da0a7abb626cfe27cab
Pass html5lib_tests16.html 075cdcf1f8b05fe1a1306ae9702fb7442a76754c
Pass html5lib_tests16.html c7d08dec7a358d06f64235f1f6189c0c53e7e75e
Pass html5lib_tests16.html c0c7b3e8f7109cf2fef447c2ac28e1566a246a74
Pass html5lib_tests16.html d95e3c1ce7b2b79e43654cc23a4a1fc65c084d82
Pass html5lib_tests16.html 49e2f750500035dfc265752923b58a26d743bc60
Pass html5lib_tests16.html c9a6e8a5f0da04035a690465b85c49e1c7259390
Pass html5lib_tests16.html 7e0c780436a6c11fcdc39dfa30c7e40542bb4745
Pass html5lib_tests16.html 10b54008a6e2f12bbfcaa0d9e19c0f98d67dfb53
Pass html5lib_tests16.html 6cdd162096c7fb581b808d491baff3c1234b02e1
Pass html5lib_tests16.html 243a2da6a25d3d7641fac624e712f4c96376d23c
Pass html5lib_tests16.html 86ff3afe4315b87db9a5d1d566b029c775e62b94
Pass html5lib_tests16.html e20f08402b6afc6d237e8261e512f89ce5299881
Pass html5lib_tests16.html c48c5eae7882c00df9026ba16f890266291635f2
Pass html5lib_tests16.html d055df57faa87a91d463956c4816bb9c67384c73
Pass html5lib_tests16.html 33cc450505dd8b55c690589d441a793bb8985f11
Pass html5lib_tests16.html 40077f2a5b88cf53f3a53485194fc29e39feb39b
Pass html5lib_tests16.html 10bd03da7b29a7ebe5e18e2163849c2521ae4555
Pass html5lib_tests16.html 39e7696382843bda945f5717030388257f54dad0
Pass html5lib_tests16.html 6d0edc9ca958384e4c608386588400b63f8cbc1a
Pass html5lib_tests16.html 0ec48786ebc1bf532930e5f442c83fc05d5ab873
Pass html5lib_tests16.html 763803287fa8300b3fd5a0285ce1ab6520640245
Pass html5lib_tests16.html f9b350e5c8304caf954b333f54060cd1ab377b47
Pass html5lib_tests16.html c82aa963e4443053afe065da586dc6f5df062f9f
Pass html5lib_tests16.html a97946be8e03c386e23023e6b6184d11517fc4f5
Pass html5lib_tests16.html 5b1e9bf7ee6e6222b78d38d99ffd3d0281b930a3
Pass html5lib_tests16.html 252c0a3870902b1fdf15224188ffd5abf56ced5f
Pass html5lib_tests16.html 10f2c0e9041fe19c9aea3c9f6a61842372242691
Pass html5lib_tests16.html a68fa9f51d285e08b95b34a1ad7ae303d1180cda
Pass html5lib_tests16.html 1553cebdf01dc953ed7983d39a18752a4fbb24d7
Pass html5lib_tests16.html 802e7c9b307082d9f15835722eb9ef2dee60ea5f
Pass html5lib_tests16.html c7f41e79f00db5b41872c0ef1443094e7ad5bc22
Pass html5lib_tests16.html ae3967a139a3ecf61ecbc59c8c769a2731626fac
Pass html5lib_tests16.html 3586a5a4a1d1d69b139d139b0823af4753bc3e8d
Pass html5lib_tests16.html 0e99e2603bc91553c252713108e30495d71c3f37
Pass html5lib_tests16.html f9858d096fa1e68cce0742d125c551878d2d7020
Pass html5lib_tests16.html 225e87bce5a4518c3e5cd248ef93ebc39dba14e0
Pass html5lib_tests16.html bb08b00b361470ce18b435c97aff4449dc98cc51
Pass html5lib_tests16.html cd74b727c1c8233f98e325293a2307e882e10f41
Pass html5lib_tests16.html 367bf426c092467300f78e5d7526b5a95b490871
Pass html5lib_tests16.html 96630ff73c222ae5aa31c5d8d32391c00e01d4ae
Pass html5lib_tests16.html efd159c8bb96c72857a1b23247240fef25c4bc16
Pass html5lib_tests16.html 302c14341a82cd1ed9c77beb7ad60ce574f764ff
Pass html5lib_tests16.html 40369d98631eb17e8ae0cad61d9b7d6dbcddf424
Pass html5lib_tests16.html 4285eba81853a6d9ea3121ffa93f1b68bb33c157
Pass html5lib_tests16.html 824bd03d81ed9d5c1d3effe1ea45db39c27d520e
Pass html5lib_tests16.html 5593651c759624a4b7d91f3e07fc3e02c9bc6642
Pass html5lib_tests16.html 8862cf5a3972eec607fabbac4bd1dcf5eb50c3f2
Pass html5lib_tests16.html bdaf31925507cd81bace2411601cb50be9ed3339
Pass html5lib_tests16.html 7df307e7cede64fda865c44c0897fc6191ce788c
Pass html5lib_tests16.html 8f275021b4d57f5235abc4efa2f53d0c633a22d6
Pass html5lib_tests16.html b3b17f7b0a43afc62fcc24602488f7c1535eafb1
Pass html5lib_tests16.html 4383e943d6be1525114a923c5c5c7875d3506f43
Pass html5lib_tests16.html c47db9cd301d75cc4ff1f4f66a70efaf04d88d34
Pass html5lib_tests16.html c02e6a5ec0971e7e860b61f3638baecfe8d12edc
Pass html5lib_tests16.html b3e566664033b9b6c550d44627807a9c17ac0fec
Pass html5lib_tests16.html e1b7c9e452fbd47b9f62901e2802d4880c9798d5
Pass html5lib_tests16.html 20b70b5e4c22aae23ed2f06f957d611b60a16e69
Pass html5lib_tests16.html 80275b0c1f6bd16a37660822a9accc973b9ad6f6
Pass html5lib_tests16.html 0707f977884170b8e752fb7956e658e60b96a39c
Pass html5lib_tests16.html 3634374d8c7b8c049cfeb6cc9cab3284103d7745
Pass html5lib_tests16.html 1cef2f6f416ac760843e475fdc3aca248fde2a64
Pass html5lib_tests16.html bcc75b33353806f86f5ecb7137309fe33fbc39cf
Pass html5lib_tests16.html 2b2b5615880e8fdd80ec772540344d3bf9f6cf98
Pass html5lib_tests16.html 87f5714929355b5a84c5bd86ade881e98135bbb8
Pass html5lib_tests16.html bc926c61b947962e23f1424d178e8b6caec63984
Pass html5lib_tests16.html 0f8650ed2fd554c65428eed896e5a6d0276ffe43
Pass html5lib_tests16.html e6c000aaa91a5cf04f15a4b6775e17d1bd9143b7
Pass html5lib_tests16.html 6783594161f7848e42e7c89a32da546163892d75
Pass html5lib_tests16.html 9c23cc23237032d8decf39d3d886a300c9304707
Pass html5lib_tests16.html 985c2415bc39a322004bbba6817df37dcdf3f10b
Pass html5lib_tests16.html 649e75657bd308f81f93189a02efa6b9a7702902
Pass html5lib_tests16.html d42bb3557b0fbbe59cf5f606445ba973d4a3d720
Pass html5lib_tests16.html d0178734ef1a063624bfd6a737dba933a54bf63d
Pass html5lib_tests16.html f6ac88b3fe1743a446da7e0ad895b8f46ea31b23
Pass html5lib_tests16.html fb513439a4a4683ec8fc60dc5f2d5588bd656910
Pass html5lib_tests16.html 4ed828c62d9dcc18ecf8608c9c38708b0b89b83d
Pass html5lib_tests16.html 51bbdaeed5d24b370a3456040561f8cad226b1a7
Pass html5lib_tests16.html 45b8f97a3a29d8b1d5e7b7f2586189ead21873b7
Pass html5lib_tests16.html 1ca26210654f4c95e3f5a337922cd5e8b0694789
Pass html5lib_tests16.html a2cd3e4d9dfccfe71bbf5780b537396b5f76c0d6
Pass html5lib_tests16.html bb1450fbedebc1605f79eaa4dc501f47d1d7feb6
Pass html5lib_tests16.html a9d29f0909132226fd57de4a55282f95682778fd
Pass html5lib_tests16.html 1fdf0dda892b230252ce864b5c7073ee09aa9165
Pass html5lib_tests16.html af1c863ec4e65c29006a0c98ef0872950618a05e
Pass html5lib_tests16.html 18763f4dd0f9681f043e0124ac26b76795f8afad
Pass html5lib_tests16.html 499e8bdd759619c5c2248e854e7cd9148ff5fe6d
Pass html5lib_tests16.html b442057918b0f6954cf88be6da17b0c5ace03382
Pass html5lib_tests16.html 8a4c1d1a49ab635498f581f8341f0f037178d01d
Pass html5lib_tests16.html c4d806ce1a7cb0abe0cb26e6950839b47134dc68
Pass html5lib_tests16.html 189378cd03b029adb6e679b6a349124155c697b7
Pass html5lib_tests16.html 6c9b32168850736c788f14208a39e70272ccc54e
Pass html5lib_tests16.html 2e9cd7a39540b3bd1df32320556cec71585fb2df
Pass html5lib_tests16.html 15fee76589fa386fa841a369bf84eb5c75ec131a
Pass html5lib_tests16.html 689bc409ce3f93662f1223f16271f8ca9883c647
Pass html5lib_tests16.html 47097173fe23e65b63647777b53b0bdde6ce0f18
Pass html5lib_tests16.html fcaa6c59f13ef6a3d6cafeaffaad71b017c34c3c
Pass html5lib_tests16.html dd0a2f0c0e6fdcf01c7a917ea662a7ef1e953f11
Pass html5lib_tests16.html b29fb15f9b7a448bb70ca27a4b1aac48c1d4f51f
Pass html5lib_tests16.html 0d91bcb1f42c1a7214168ee61e875166bd75d547
Pass html5lib_tests16.html d561d1634333a05fe3428e355f6ab3d67b69ed44
Pass html5lib_tests16.html 6a13a34b36f6ea738d10b2a2da4958045c243fb7
Pass html5lib_tests16.html ee49c31a8c40676f366959341cd98aecdcf9c14f
Pass html5lib_tests16.html c541aa24beb8107a3726bd8b9e655ca8f95d7b48
Pass html5lib_tests16.html 7ae06c19dd24dea99940ceaed8dffdd0e24cd5f9
Pass html5lib_tests16.html 6d2029f0e2404dcc7f836481cef6bf56f060b248
Pass html5lib_tests16.html 5c5a26c8bcb37a214c47f4b6d9843de20a8fb7e1
Pass html5lib_tests16.html 3ee633bf4e4eadc2b70c6eebfa14c600435604c9
Pass html5lib_tests16.html ad5d10a0d7d8fc98040ff33f1db197735d6d3d52
Pass html5lib_tests16.html 2bde3ac14192be5f4e664c2b01b5aaa57c14e347
Pass html5lib_tests16.html a87a03c8c3a06269e6ff8d11e142f6ebb05e9306
Pass html5lib_tests16.html 190325aed218711647eae5581a1f629c76da297c
Pass html5lib_tests16.html e244d9f1edb6275b69c4a832620b9439d39bfb22
Pass html5lib_tests16.html 751f7c9538b9fd00f5c7dd0ed59df07f4d51fdac
Pass html5lib_tests16.html 762904de405fa26afdd39e024395a441bc6a0f8a
Pass html5lib_tests16.html 4df21df646897816e166b3dd829a8a3e21157f22
Pass html5lib_tests16.html 9c241f721c1f6677d736b37ecd86bb230df329df
Pass html5lib_tests16.html 96ebedcc684f27f984cc7be467c908bdab2470a9
Pass html5lib_tests16.html 419cfbd87ad35c1d43214b122630cfde1c3ccf1b
Pass html5lib_tests16.html cf75ace0d7531b8daa271001dbf92b93b0b4490c
Pass html5lib_tests16.html a058b4bb03a689ce8528ed412d62cdd5bb879571
Pass html5lib_tests16.html d7c871c41c9db40312ffc5af996ede62ecdfc579
Pass html5lib_tests16.html 6c353ee8f48c1227eeab81279dc3eb3890c9c3bd
Pass html5lib_tests16.html fea28aab54637701c5dfaef4f3fe64c72b272e1c
Pass html5lib_tests16.html ecd79b7eb7af2bb4dadf710f70e0d78f62adc40e
Pass html5lib_tests16.html 43917824cc1d9b5a65601f46e13a0779c3dcff4e
Pass html5lib_tests16.html 5423bb28649f37e70a0559cba78c3b253a60c277
Pass html5lib_tests16.html 2c091a50dfd31e766a5a629c0b7c21973e33319d
Pass html5lib_tests16.html 920feb4f9d1032dcec2abc5c526e4996f642968b
Pass html5lib_tests16.html 31b9b446263cd5b7a844d43f2a235ed9b0c53efb
Pass html5lib_tests16.html dbb5127246fee18718bcffc6cf0730674d12b98a
Pass html5lib_tests16.html 3872a4cfeba7651f0671de7e5f3922fd5053837b
Pass html5lib_tests16.html 2fbcb2db61b6416cdf46e0526e1929d146ab3da7
Pass html5lib_tests16.html 999da234e770bbf681a819423d04ea57415d9bbc
Pass html5lib_tests16.html 6fc6be53a87bbb2d2a51a5617009063002321d09
Pass html5lib_tests16.html b0de72088fa3e543572329eda36fa4bd16e29fa3
Pass html5lib_tests16.html 2a6d8110b8148a9aa83db81dc38d544becea2fa9
Pass html5lib_tests16.html 6556aaaca3956eafbc1660bce50a2f3568f1bff6
Pass html5lib_tests16.html 8ba3d2c7712e9c0484a4cafc8b0ca6f35d8f11ed

View file

@ -0,0 +1,109 @@
Harness status: OK
Found 103 tests
97 Pass
6 Fail
Pass html5lib_tests19.html 6135e0cbdbb22a97e8a13c2442c3e9a9e0a53298
Pass html5lib_tests19.html 6b46dba2f4d7d1a08359ab21fe5e011463dd8746
Pass html5lib_tests19.html bd558a6d89fae63fed9c0801e6fd8e8737bc8dc1
Pass html5lib_tests19.html a7955e9f06178980cbc13fc4d548f196fef42b13
Pass html5lib_tests19.html 2fda53e44aa91cb475f8b1aa57e938adcce60d4d
Pass html5lib_tests19.html 9f55c21807de5c769197a9a2f29f836f08af050b
Pass html5lib_tests19.html 197605d0b406dbb3de884de6949237dd33669997
Pass html5lib_tests19.html 3b1730b917da1c33da80ee08d41573c44404c663
Pass html5lib_tests19.html ac6b608079815ad00c84b291ad6715eec523ccf4
Pass html5lib_tests19.html a68acf7673e0c886fcf8cf609f3a39fb36362de5
Pass html5lib_tests19.html 266157f051148b068dad52e9786a0bda96e851db
Pass html5lib_tests19.html 2fd40bb6048be379000d73bc52a50405ca99b356
Pass html5lib_tests19.html 19652e3b1ec783f279f527ddfb07073684520ab8
Pass html5lib_tests19.html e5676878cff9332b572ebfd327e426f87e32ab4d
Pass html5lib_tests19.html 03bbba49b30ec908e06e3c84e1fcede1ac7508ff
Pass html5lib_tests19.html 5f9f25a089e72b4a4e5d7ebbacb440349c53f52f
Pass html5lib_tests19.html d26f2542fc6fcee4e737b578c8db716a96a22ade
Pass html5lib_tests19.html 0e8c4b181993b477f4f215d5724b99ad04f18b2d
Pass html5lib_tests19.html e3f1de111562f25106efad76583a3b6c0e3516ba
Pass html5lib_tests19.html 84db6521d1350830f3e46b7d9676527e1b4ee9e2
Pass html5lib_tests19.html 033d9b356b2a646471de009fa8a75e40500d2dd2
Pass html5lib_tests19.html 0971186f913b3c7f915817d2b82c73ea6487e932
Pass html5lib_tests19.html 5257f17c3ecab5b65d74d7c27f0fd492f82ef9b9
Pass html5lib_tests19.html b8c3379e60829428188fd57e974ea4d8a7e7ec45
Pass html5lib_tests19.html f9ab61cdf08301bcf02208165cc94e6bdbad746c
Pass html5lib_tests19.html c6aa9952dcccdde3fc6d4ba104647f049d038e66
Pass html5lib_tests19.html 3984cbe166b42d77ff6dadc8e1687075db7a8e65
Pass html5lib_tests19.html 2d78ba34eecedf8261e9a9e3f8858baf00a0d960
Pass html5lib_tests19.html 8d45a57b7093df38c88dccb1f3fba6a62c810445
Pass html5lib_tests19.html 871917ed049ff21b35e74e39de2ca1eef6863562
Fail html5lib_tests19.html 74ad5d3026f1b3eb6553086eb2811eed4418e334
Fail html5lib_tests19.html 0b1e97138c4d8e8d770f107202e46a18362fd587
Fail html5lib_tests19.html 02624fcb7ddb77b15e9bc553d8392fc8cd031f68
Fail html5lib_tests19.html a2579d287fd8111306e6dcd7248971a6baa4488d
Fail html5lib_tests19.html acd1c9218828ea4367920c7a499c54de7f9a618f
Pass html5lib_tests19.html 36a6346b03982942b4427d5089271a101ebf1605
Pass html5lib_tests19.html e58ed5b56f81d057c23be31e068a10e694f52adf
Pass html5lib_tests19.html d422166b880468765f34d89075d4db9c4fcbfee9
Pass html5lib_tests19.html fc673f5874c0cf1477554dfb733d6292cc01558b
Pass html5lib_tests19.html 0d2192c16cc224e770abff9e9e89e860aff9da78
Pass html5lib_tests19.html d5941f6b68ef817d062636d0d61c24c68d0d86a2
Pass html5lib_tests19.html 259a085138224e3dfe0dceded2a043e72a8b6d76
Pass html5lib_tests19.html 869fff76cfe00ff33987de007ce98dc949bf8cbf
Pass html5lib_tests19.html c06518cdbf3816faf265644d28d93b7aee9258f7
Pass html5lib_tests19.html 2fb0cdb3a9f69ca6c3d564e01f89d7173cd51295
Pass html5lib_tests19.html 138623cd07ccff1ce13bb1aa5dbaaadc962c414f
Pass html5lib_tests19.html 52e4a8422d6ef591f33f8686842d974dbaa33302
Pass html5lib_tests19.html 5ba1d0e97f121810025c1d6447a426c669661637
Pass html5lib_tests19.html 709ec07924db4c9fd66d48eac08575dfdef01d8f
Pass html5lib_tests19.html f72edb80bc3be091e4b448cd1de8fe851d623e05
Pass html5lib_tests19.html cb7de0dc7e17c1454ceaa7eb49cb9f9476a1f510
Pass html5lib_tests19.html 511bf4bae15c8119042e5e80b5358b70e7da26c1
Pass html5lib_tests19.html c51ee24ef9d92206318cc5bbba784630cd10e531
Pass html5lib_tests19.html 819d39ae49fd04f0dfce90ac8ed2f8e423bb9329
Pass html5lib_tests19.html ef6bdf5b3ba9d41d6c1c08ede60c2e7fb21c6d71
Pass html5lib_tests19.html 6bf9c038255c31f20d8fbeaebdf3609eb3c9ed75
Pass html5lib_tests19.html 08983ab2a54f61b09a1df134cdaf51d321f3ef32
Pass html5lib_tests19.html 2ef94ed028fa71b70540cf41f0884ae0c1cf8077
Pass html5lib_tests19.html 5c9ed202789fb29566e0d146d155e0d672e8a037
Pass html5lib_tests19.html 7d766253cbf500df03506dfb7f2cbdd0f0f533c6
Pass html5lib_tests19.html 4d5629c61993ef0bb83fcbd80ccc6ead7cd776ee
Pass html5lib_tests19.html 6320d9fa3295984d3da7e4831ced75dfb97d4848
Pass html5lib_tests19.html bdb97868e951b7a719fa8b1afc79650bf9aae7a5
Pass html5lib_tests19.html 81868cde252694a252b6b0c8d366850f8a54736c
Pass html5lib_tests19.html 20651db48147f8dda189c563f5fa60bfd0913ac0
Pass html5lib_tests19.html d098e8a39897cd8453fc18399622e413872b054c
Pass html5lib_tests19.html 0beb7903dcc0dbee206d8b6e729963a0461eedb3
Pass html5lib_tests19.html 78e5cdb86bf2aecef4697d70d50499b50de5c25b
Pass html5lib_tests19.html cfb1d482e5971a6915c90434f580721b772fc09b
Pass html5lib_tests19.html 99d8bff8bbcca1c7faa7b6e10b9ff305b9f99594
Pass html5lib_tests19.html daaa43db05ffe9bdd8d734a4389fb3c459caddf1
Pass html5lib_tests19.html b3d421e57a4135731309152d7f47e26a4866f3d6
Pass html5lib_tests19.html aa5eac4b7a0bafdafcd52cb190a8e181ff74144c
Pass html5lib_tests19.html 91d56a57f4c8af2bce03e9974df0a80f62686fb4
Pass html5lib_tests19.html 9cbad5cad719a63d2eb7721de6737a346ad42da1
Pass html5lib_tests19.html 057c2e91a7e6053d39436efe2bc29049f6fa8e82
Pass html5lib_tests19.html 61e6517e5e82cdaed41ab3e1f6fa927731aa0c82
Pass html5lib_tests19.html 9274ab4f08baa52c5d7016f6062f186730cfbe6c
Pass html5lib_tests19.html fc2fa41ebadc21653b38d70c1097bd4ac5979948
Pass html5lib_tests19.html 473303e65f26fafacbaf01c11b04d745ff293963
Pass html5lib_tests19.html 7854276e1637619f693cd87f64542c08c35d40bd
Pass html5lib_tests19.html a2d262d392e8d10645ce559edf401df3f3872eb3
Fail html5lib_tests19.html d5e50987bf495e285e279ff8670255d9b1314f5d
Pass html5lib_tests19.html be54d7506e54a127a05b115a8659a5b52fa57f6c
Pass html5lib_tests19.html af389f1a8ef93c457560fd2445df80a6789dab0d
Pass html5lib_tests19.html 601dfa9502942deb8bf46a91c64f10f649bc1875
Pass html5lib_tests19.html 4b01442faf29563d3b736d235515f77c20c81863
Pass html5lib_tests19.html 696a2b60681b0b6758f165a67e29a84b0f75a153
Pass html5lib_tests19.html 7a48b98cbae5cd7cbbb90f138c0d12da6c4448b3
Pass html5lib_tests19.html d7693a0f2be68925250d3aa2cf295b1d1a60bf94
Pass html5lib_tests19.html 9e4d91f02184de1b1e5d927144bb06d3bc78bb09
Pass html5lib_tests19.html 9655591702381a52fe0eb3224e63e2d8bfd735e8
Pass html5lib_tests19.html fe9aa1c8ec32796e26f3e58022f0e42dc365b5c7
Pass html5lib_tests19.html 4c9ec04359c3e94d4a56d6932d289f0c4246d1ef
Pass html5lib_tests19.html 3fcc2f15951b3c3375c3e359cf7888c71187994a
Pass html5lib_tests19.html a94b35c317763de75150df6b436c7c153aeb8c51
Pass html5lib_tests19.html 173b990198dd8fd9534d7808817c19440b75d406
Pass html5lib_tests19.html 48ed5945491d4ad0c00acfd01c2060a459436d34
Pass html5lib_tests19.html 392b8d4d25351467da961e4ab011a1d4e970b97a
Pass html5lib_tests19.html bd14373173ea3617610fc8154237f59f1a810733
Pass html5lib_tests19.html db3c4ddb389a2b8f42ca6b3719007d4de2eccc41
Pass html5lib_tests19.html c1741a62627b2e5bb1cfa2b2cd667f3f9f76157e
Pass html5lib_tests19.html 0d6cf626790e9f06e30ceb6baf3004663b0247e7

View file

@ -1,26 +1,21 @@
Summary
Harness status: OK
Rerun
Found 16 tests
16 Pass
Details
Result Test Name MessagePass html5lib_tests5.html c482a88c4feb445945f19c77eda5e460cd6db344
Pass html5lib_tests5.html b28eaef63aeeb165eceb56152d50767327f975fa
Pass html5lib_tests5.html 20c1b55aabcd426fa5975648f21cff40fa3fc2e3
Pass html5lib_tests5.html cc87be99b2531e4c0c1fd95f81cd4dd989f699d3
Pass html5lib_tests5.html 283a0f4eb33a3ee80f718020268bf1794a758ec9
Pass html5lib_tests5.html bde8b7a035edd6f123f45708ac10b4f60e81edf4
Pass html5lib_tests5.html 6ace30add7690cda74de9830481c95bef1f5976d
Pass html5lib_tests5.html 3c5f82c8db30cc1cce4c7fa8a5d18cf13ce8d007
Pass html5lib_tests5.html 9cac6179dc295f43afd5a41ed98aef3a9d5a08de
Pass html5lib_tests5.html 021a5fbf8c725781d08dce099d21f7023c9bb26d
Pass html5lib_tests5.html 412eae0c0e6e5da254550debd587ff86cff55c0c
Pass html5lib_tests5.html 410a64500216425d811748b0258c92a49fbad0ff
Pass html5lib_tests5.html bd7dfd1a0f74731c22b3e2d331f7c14ba7c9a4e8
Pass html5lib_tests5.html 5f847a390a413a42fcef3d4510ddc56815c7d722
Pass html5lib_tests5.html 6d5b2f84df760f8995146c406c2dd07ba5510f7f
Pass html5lib_tests5.html eded02e700d7329f650a9a38ef7ea6c0e453766b
Pass html5lib_tests5.html c482a88c4feb445945f19c77eda5e460cd6db344
Pass html5lib_tests5.html b28eaef63aeeb165eceb56152d50767327f975fa
Pass html5lib_tests5.html 20c1b55aabcd426fa5975648f21cff40fa3fc2e3
Pass html5lib_tests5.html cc87be99b2531e4c0c1fd95f81cd4dd989f699d3
Pass html5lib_tests5.html 283a0f4eb33a3ee80f718020268bf1794a758ec9
Pass html5lib_tests5.html bde8b7a035edd6f123f45708ac10b4f60e81edf4
Pass html5lib_tests5.html 6ace30add7690cda74de9830481c95bef1f5976d
Pass html5lib_tests5.html 3c5f82c8db30cc1cce4c7fa8a5d18cf13ce8d007
Pass html5lib_tests5.html 9cac6179dc295f43afd5a41ed98aef3a9d5a08de
Pass html5lib_tests5.html 021a5fbf8c725781d08dce099d21f7023c9bb26d
Pass html5lib_tests5.html 412eae0c0e6e5da254550debd587ff86cff55c0c
Pass html5lib_tests5.html 410a64500216425d811748b0258c92a49fbad0ff
Pass html5lib_tests5.html bd7dfd1a0f74731c22b3e2d331f7c14ba7c9a4e8
Pass html5lib_tests5.html 5f847a390a413a42fcef3d4510ddc56815c7d722
Pass html5lib_tests5.html 6d5b2f84df760f8995146c406c2dd07ba5510f7f
Pass html5lib_tests5.html eded02e700d7329f650a9a38ef7ea6c0e453766b

View file

@ -0,0 +1,31 @@
<!DOCTYPE html>
<script src="../include.js"></script>
<iframe id="a"></iframe>
<iframe id="b"></iframe>
<script>
asyncTest(done => {
let doneA = false, doneB = false;
function check() {if (doneA && doneB) done();}
function makeContent(id, n) {
let html = `<h3>${id} ${n}</h3>`;
if (n % 3 === 0) html += `<iframe id="nest1+${id}" srcdoc="${id} ${n}"></iframe>`;
if (n % 5 === 0) html += `<frame id="nest2${id} srcdoc="${id} ${n}"/>`;
return html;
}
function run(iframe, id, n, max, finish) {
if (n >= max) {println('PASS'); finish(); return;}
const blob = new Blob([makeContent(id, n)], {type: 'text/html'});
const url = URL.createObjectURL(blob);
iframe.onload = () => {
try {
iframe.contentDocument;
URL.revokeObjectURL(url);
} catch {println('FAIL'); finish(); return;}
run(iframe, id, n + 1, max, finish);
};
iframe.src = url;
}
run(document.getElementById('a'), 'a', 0, 101, () => {doneA = true; check();});
run(document.getElementById('b'), 'b', 0, 101, () => {doneB = true; check();});
});
</script>