script: Move TaskManager to GlobalScope (#34827)

This is a simplification of the internal `TaskQueue` API that moves the
`TaskManager` to the `GlobalScope` itself. In addition, the handling of
cancellers is moved to the `TaskManager` as well. This means that no
arguments other than the `task` are necessary for queueing tasks, which
makes the API a lot easier to use and cleaner.

`TaskSource` now also keeps a copy of the canceller with it, so that
they always know the proper way to cancel any tasks queued on them.

There is one complication here. The event loop `sender` for dedicated
workers is constantly changing as it is set to `None` when not handling
messages. This is because this sender keeps a handle to the main
thread's `Worker` object, preventing garbage collection while any
messages are still in flight or being handled. This change allows
setting the `sender` on the `TaskManager` to `None` to allow proper
garbabge collection.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2025-01-04 09:41:50 +01:00 committed by GitHub
parent 75a22cfe2e
commit b2eda71952
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
54 changed files with 1060 additions and 1516 deletions

View file

@ -696,37 +696,34 @@ impl Document {
// But it's now Step 4 in https://html.spec.whatwg.org/multipage/#reactivate-a-document
// TODO: See #32687 for more information.
let document = Trusted::new(self);
self.window
self.window()
.task_manager()
.dom_manipulation_task_source()
.queue(
task!(fire_pageshow_event: move || {
let document = document.root();
let window = document.window();
// Step 4.6.1
if document.page_showing.get() {
return;
}
// Step 4.6.2 Set document's page showing flag to true.
document.page_showing.set(true);
// Step 4.6.3 Update the visibility state of document to "visible".
document.update_visibility_state(DocumentVisibilityState::Visible, CanGc::note());
// Step 4.6.4 Fire a page transition event named pageshow at document's relevant
// global object with true.
let event = PageTransitionEvent::new(
window,
atom!("pageshow"),
false, // bubbles
false, // cancelable
true, // persisted
CanGc::note(),
);
let event = event.upcast::<Event>();
event.set_trusted(true);
window.dispatch_event_with_target_override(event, CanGc::note());
}),
self.window.upcast(),
)
.queue(task!(fire_pageshow_event: move || {
let document = document.root();
let window = document.window();
// Step 4.6.1
if document.page_showing.get() {
return;
}
// Step 4.6.2 Set document's page showing flag to true.
document.page_showing.set(true);
// Step 4.6.3 Update the visibility state of document to "visible".
document.update_visibility_state(DocumentVisibilityState::Visible, CanGc::note());
// Step 4.6.4 Fire a page transition event named pageshow at document's relevant
// global object with true.
let event = PageTransitionEvent::new(
window,
atom!("pageshow"),
false, // bubbles
false, // cancelable
true, // persisted
CanGc::note(),
);
let event = event.upcast::<Event>();
event.set_trusted(true);
window.dispatch_event_with_target_override(event, CanGc::note());
}))
.unwrap();
}
@ -2129,14 +2126,9 @@ impl Document {
request: RequestBuilder,
listener: Listener,
) {
let (task_source, canceller) = self
.window()
.task_manager()
.networking_task_source_with_canceller();
let callback = NetworkListener {
context: std::sync::Arc::new(Mutex::new(listener)),
task_source,
canceller: Some(canceller),
task_source: self.window().task_manager().networking_task_source(),
}
.into_callback();
self.loader_mut()
@ -2149,14 +2141,9 @@ impl Document {
listener: Listener,
cancel_override: Option<ipc::IpcReceiver<()>>,
) {
let (task_source, canceller) = self
.window()
.task_manager()
.networking_task_source_with_canceller();
let callback = NetworkListener {
context: std::sync::Arc::new(Mutex::new(listener)),
task_source,
canceller: Some(canceller),
task_source: self.window().task_manager().networking_task_source(),
}
.into_callback();
self.loader_mut()
@ -2384,81 +2371,75 @@ impl Document {
// Step 7.
debug!("Document loads are complete.");
let document = Trusted::new(self);
self.window
self.window()
.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;
}
.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.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(),
atom!("load"),
EventBubbles::DoesNotBubble,
EventCancelable::NotCancelable,
CanGc::note(),
);
event.set_trusted(true);
// Step 7.2.
if document.browsing_context().is_none() {
return;
}
let event = Event::new(
window.upcast(),
atom!("load"),
EventBubbles::DoesNotBubble,
EventCancelable::NotCancelable,
CanGc::note(),
);
event.set_trusted(true);
// http://w3c.github.io/navigation-timing/#widl-PerformanceNavigationTiming-loadEventStart
update_with_current_instant(&document.load_event_start);
// http://w3c.github.io/navigation-timing/#widl-PerformanceNavigationTiming-loadEventStart
update_with_current_instant(&document.load_event_start);
debug!("About to dispatch load for {:?}", document.url());
window.dispatch_event_with_target_override(&event, CanGc::note());
debug!("About to dispatch load for {:?}", document.url());
window.dispatch_event_with_target_override(&event, CanGc::note());
// http://w3c.github.io/navigation-timing/#widl-PerformanceNavigationTiming-loadEventEnd
update_with_current_instant(&document.load_event_end);
// http://w3c.github.io/navigation-timing/#widl-PerformanceNavigationTiming-loadEventEnd
update_with_current_instant(&document.load_event_end);
if let Some(fragment) = document.url().fragment() {
document.check_and_scroll_fragment(fragment, CanGc::note());
}
}),
self.window.upcast(),
)
if let Some(fragment) = document.url().fragment() {
document.check_and_scroll_fragment(fragment, CanGc::note());
}
}))
.unwrap();
// Step 8.
let document = Trusted::new(self);
if document.root().browsing_context().is_some() {
self.window
self.window()
.task_manager()
.dom_manipulation_task_source()
.queue(
task!(fire_pageshow_event: move || {
let document = document.root();
let window = document.window();
if document.page_showing.get() || !window.is_alive() {
return;
}
.queue(task!(fire_pageshow_event: move || {
let document = document.root();
let window = document.window();
if document.page_showing.get() || !window.is_alive() {
return;
}
document.page_showing.set(true);
document.page_showing.set(true);
let event = PageTransitionEvent::new(
window,
atom!("pageshow"),
false, // bubbles
false, // cancelable
false, // persisted
CanGc::note(),
);
let event = event.upcast::<Event>();
event.set_trusted(true);
let event = PageTransitionEvent::new(
window,
atom!("pageshow"),
false, // bubbles
false, // cancelable
false, // persisted
CanGc::note(),
);
let event = event.upcast::<Event>();
event.set_trusted(true);
window.dispatch_event_with_target_override(event, CanGc::note());
}),
self.window.upcast(),
)
window.dispatch_event_with_target_override(event, CanGc::note());
}))
.unwrap();
}
@ -2486,32 +2467,29 @@ impl Document {
// TODO: fully implement "completely loaded".
let document = Trusted::new(self);
if document.root().browsing_context().is_some() {
self.window
self.window()
.task_manager()
.dom_manipulation_task_source()
.queue(
task!(completely_loaded: move || {
let document = document.root();
document.completely_loaded.set(true);
if let Some(DeclarativeRefresh::PendingLoad {
url,
time
}) = &*document.declarative_refresh.borrow() {
// https://html.spec.whatwg.org/multipage/#shared-declarative-refresh-steps
document.window.upcast::<GlobalScope>().schedule_callback(
OneshotTimerCallback::RefreshRedirectDue(RefreshRedirectDue {
window: DomRoot::from_ref(document.window()),
url: url.clone(),
}),
Duration::from_secs(*time),
);
}
// Note: this will, among others, result in the "iframe-load-event-steps" being run.
// https://html.spec.whatwg.org/multipage/#iframe-load-event-steps
document.notify_constellation_load();
}),
self.window.upcast(),
)
.queue(task!(completely_loaded: move || {
let document = document.root();
document.completely_loaded.set(true);
if let Some(DeclarativeRefresh::PendingLoad {
url,
time
}) = &*document.declarative_refresh.borrow() {
// https://html.spec.whatwg.org/multipage/#shared-declarative-refresh-steps
document.window.upcast::<GlobalScope>().schedule_callback(
OneshotTimerCallback::RefreshRedirectDue(RefreshRedirectDue {
window: DomRoot::from_ref(document.window()),
url: url.clone(),
}),
Duration::from_secs(*time),
);
}
// Note: this will, among others, result in the "iframe-load-event-steps" being run.
// https://html.spec.whatwg.org/multipage/#iframe-load-event-steps
document.notify_constellation_load();
}))
.unwrap();
}
}
@ -2657,9 +2635,8 @@ impl Document {
update_with_current_instant(&self.dom_content_loaded_event_start);
// Step 4.1.
let window = self.window();
let document = Trusted::new(self);
window
self.window()
.task_manager()
.dom_manipulation_task_source()
.queue(
@ -2667,8 +2644,7 @@ impl Document {
let document = document.root();
document.upcast::<EventTarget>().fire_bubbling_event(atom!("DOMContentLoaded"), CanGc::note());
update_with_current_instant(&document.dom_content_loaded_event_end);
}),
window.upcast(),
})
)
.unwrap();
@ -2713,8 +2689,9 @@ impl Document {
// Note: the spec says to discard any tasks queued for fetch.
// This cancels all tasks on the networking task source, which might be too broad.
// See https://github.com/whatwg/html/issues/3837
self.window
.cancel_all_tasks_from_source(TaskSourceName::Networking);
self.window()
.task_manager()
.cancel_pending_tasks_for_source(TaskSourceName::Networking);
// Step 3.
if let Some(parser) = self.get_current_parser() {