Repository: servo/servo
File: components/script/dom/document.rs (Rust)

Code before:
        // Step 15, End
        self.decr_ignore_opens_during_unload_counter();
    }

    // https://html.spec.whatwg.org/multipage/#the-end
    pub(crate) fn maybe_queue_document_completion(&self) {
        // https://html.spec.whatwg.org/multipage/#delaying-load-events-mode
        let is_in_delaying_load_events_mode = match self.window.undiscarded_window_proxy() {
            Some(window_proxy) => window_proxy.is_delaying_load_events_mode(),
            None => false,
        };

        // Note: if the document is not fully active, layout will have exited already,
        // and this method will panic.
        // The underlying problem might actually be that layout exits while it should be kept alive.
        // See https://github.com/servo/servo/issues/22507
        let not_ready_for_load = self.loader.borrow().is_blocked() ||
            !self.is_fully_active() ||
            is_in_delaying_load_events_mode;

        if not_ready_for_load {
            // Step 6.
            return;
        }

        assert!(!self.loader.borrow().events_inhibited());
        self.loader.borrow_mut().inhibit_events();

        // The rest will ever run only once per document.
        // Step 7.
        debug!("Document loads are complete.");
        let document = Trusted::new(self);
        self.owner_global()
            .task_manager()
            .dom_manipulation_task_source()
            .queue(task!(fire_load_event: move || {
                let document = document.root();
                let window = document.window();
                if !window.is_alive() {
                    return;
                }

                // Step 7.1.
                document.set_ready_state(DocumentReadyState::Complete, CanGc::note());

                // Step 7.2.
                if document.browsing_context().is_none() {
                    return;
                }
                let event = Event::new(
                    window.upcast(),

Review comment (bug):
I think this assertion was wrong, mostly because `abort()` also calls `self.loader.borrow_mut().inhibit_events();`. I am not sure why `abort()` does that (it's not in the spec and the commit that introduced it is 9 years old). Still, when messages are received out-of-order and we abort and then enter this method, we should guard against that. Therefore, I think it is safe to add to the if above like with the other expressions.

Code after:
        // Step 15, End
        self.decr_ignore_opens_during_unload_counter();

        // Step 20. If oldDocument's salvageable state is false, then destroy oldDocument.
        // TODO
    }

    // https://html.spec.whatwg.org/multipage/#the-end
    pub(crate) fn maybe_queue_document_completion(&self) {
        // https://html.spec.whatwg.org/multipage/#delaying-load-events-mode
        let is_in_delaying_load_events_mode = match self.window.undiscarded_window_proxy() {
            Some(window_proxy) => window_proxy.is_delaying_load_events_mode(),
            None => false,
        };

        // Note: if the document is not fully active, layout will have exited already,
        // and this method will panic.
        // The underlying problem might actually be that layout exits while it should be kept alive.
        // See https://github.com/servo/servo/issues/22507
        let not_ready_for_load = self.loader.borrow().is_blocked() ||
            !self.is_fully_active() ||
            is_in_delaying_load_events_mode ||
            // In case we have already aborted this document and receive a
            // a subsequent message to load the document
            self.loader.borrow().events_inhibited();

        if not_ready_for_load {
            // Step 6.
            return;
        }

        self.loader.borrow_mut().inhibit_events();

        // The rest will ever run only once per document.
        // Step 7.
        debug!("Document loads are complete.");
        let document = Trusted::new(self);
        self.owner_global()
            .task_manager()
            .dom_manipulation_task_source()
            .queue(task!(fire_load_event: move || {
                let document = document.root();
                let window = document.window();
                if !window.is_alive() {
                    return;
                }

                // Step 7.1.
                document.set_ready_state(DocumentReadyState::Complete, CanGc::note());

                // Step 7.2.