Browse Source

LibWeb: Wait for initial navigation to complete before modifying iframe

If initial src of an iframe is "about:blank", it does synchronous
navigation that is not supposed to be interleaved by other navigation
or usage of Document.open().

Fixes crashing in navigation on https://twinings.co.uk/
Aliaksandr Kalenik 1 năm trước cách đây
mục cha
commit
a3149c1ce9

+ 1 - 0
Tests/LibWeb/Text/expected/navigation/populate-iframe-using-document-write.txt

@@ -0,0 +1 @@
+  PASS (didn't crash)

+ 22 - 0
Tests/LibWeb/Text/input/navigation/populate-iframe-using-document-write.html

@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<script src="../include.js"></script>
+<body></body>
+<script>
+    const iframe = document.createElement("iframe");
+    document.body.appendChild(iframe);
+
+    const iframeDocument = iframe.contentDocument;
+
+    iframeDocument.open();
+    iframeDocument.write(
+        "<!DOCTYPE html><html><head><title>Iframe Content</title></head><body>"
+    );
+    iframeDocument.write("<h1>Hello</h1>");
+    iframeDocument.write("<p>from iframe</p>");
+    iframeDocument.write("</body></html>");
+    iframeDocument.close();
+
+    test(() => {
+        println("PASS (didn't crash)");
+    })
+</script>

+ 9 - 0
Userland/Libraries/LibWeb/DOM/Document.cpp

@@ -567,6 +567,15 @@ WebIDL::ExceptionOr<void> Document::run_the_document_write_steps(StringView inpu
 // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-document-open
 WebIDL::ExceptionOr<Document*> Document::open(Optional<String> const&, Optional<String> const&)
 {
+    // If document belongs to a child navigable, we need to make sure its initial navigation is done,
+    // because subsequent steps will modify "initial about:blank" to false, which would cause
+    // initial navigation to fail in case it was "about:blank".
+    if (auto navigable = this->navigable(); navigable->container() && !navigable->container()->content_navigable_initialized()) {
+        HTML::main_thread_event_loop().spin_processing_tasks_with_source_until(HTML::Task::Source::NavigationAndTraversal, [navigable_container = navigable->container()] {
+            return navigable_container->content_navigable_initialized();
+        });
+    }
+
     // 1. If document is an XML document, then throw an "InvalidStateError" DOMException exception.
     if (m_type == Type::XML)
         return WebIDL::InvalidStateError::create(realm(), "open() called on XML document."_fly_string);

+ 6 - 0
Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.cpp

@@ -76,6 +76,12 @@ void HTMLIFrameElement::process_the_iframe_attributes(bool initial_insertion)
     if (!content_navigable())
         return;
 
+    // Make sure applying of history step caused by potential sync navigation to "about:blank"
+    // is finished. Otherwise, it might interrupt navigation caused by changing src or srcdoc.
+    if (!initial_insertion && !content_navigable_initialized()) {
+        main_thread_event_loop().spin_processing_tasks_with_source_until(Task::Source::NavigationAndTraversal, [this] { return content_navigable_initialized(); });
+    }
+
     // 1. If element's srcdoc attribute is specified, then:
     if (has_attribute(HTML::AttributeNames::srcdoc)) {
         // 1. Set element's current navigation was lazy loaded boolean to false.

+ 2 - 0
Userland/Libraries/LibWeb/HTML/NavigableContainer.h

@@ -51,6 +51,8 @@ public:
     // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#potentially-delays-the-load-event
     bool currently_delays_the_load_event() const;
 
+    bool content_navigable_initialized() const { return m_content_navigable_initialized; }
+
 protected:
     NavigableContainer(DOM::Document&, DOM::QualifiedName);