script: Reduce ScriptThread TLS usage (#38875)

We store a pointer to the ScriptThread singleton for a thread in
thread-local storage. While we don't have yet have profiles pointing to
this TLS access as a hot spot, we can remove a potential performance
footgun without a lot of effort by passing around small pieces of data
that we otherwise need to fetch from the ScriptThread.

Testing: Existing WPT is sufficient
Fixes: part of #37969

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
Josh Matthews 2025-08-30 12:51:40 -04:00 committed by GitHub
parent d1da1a995c
commit c97ec1b2fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 129 additions and 68 deletions

View file

@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::collections::HashSet;
use crossbeam_channel::{Receiver, select};
use devtools_traits::DevtoolScriptControlMsg;
@ -53,7 +55,7 @@ pub(crate) fn run_worker_event_loop<T, WorkerMsg, Event>(
let event = select! {
recv(worker_scope.control_receiver()) -> msg => T::from_control_msg(msg.unwrap()),
recv(task_queue.select()) -> msg => {
task_queue.take_tasks(msg.unwrap());
task_queue.take_tasks(msg.unwrap(), &HashSet::new());
T::from_worker_msg(task_queue.recv().unwrap())
},
recv(devtools_receiver) -> msg => T::from_devtools_msg(msg.unwrap()),
@ -72,7 +74,7 @@ pub(crate) fn run_worker_event_loop<T, WorkerMsg, Event>(
while !scope.is_closing() {
// Batch all events that are ready.
// The task queue will throttle non-priority tasks if necessary.
match task_queue.take_tasks_and_recv() {
match task_queue.take_tasks_and_recv(&HashSet::new()) {
Err(_) => match devtools_receiver.try_recv() {
Ok(message) => sequential.push(T::from_devtools_msg(message)),
Err(_) => break,

View file

@ -52,7 +52,6 @@ use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::window::Window;
use crate::script_runtime::{CanGc, JSContext, JSContext as SafeJSContext};
use crate::script_thread::ScriptThread;
/// <https://html.spec.whatwg.org/multipage/#htmlconstructor>
fn html_constructor(
@ -391,14 +390,6 @@ fn get_constructor_object_from_local_name(
true
}
pub(crate) fn pop_current_element_queue(can_gc: CanGc) {
ScriptThread::pop_current_element_queue(can_gc);
}
pub(crate) fn push_new_element_queue() {
ScriptThread::push_new_element_queue();
}
pub(crate) fn call_html_constructor<T: DerivedFrom<Element> + DomObject>(
cx: JSContext,
args: &CallArgs,

View file

@ -19,9 +19,7 @@ use script_bindings::settings_stack::StackEntry;
use crate::DomTypes;
use crate::dom::bindings::codegen::{InterfaceObjectMap, PrototypeList};
use crate::dom::bindings::constructor::{
call_html_constructor, pop_current_element_queue, push_new_element_queue,
};
use crate::dom::bindings::constructor::call_html_constructor;
use crate::dom::bindings::conversions::DerivedFrom;
use crate::dom::bindings::error::{Error, report_pending_exception, throw_dom_exception};
use crate::dom::bindings::principals::PRINCIPALS_CALLBACKS;
@ -33,6 +31,7 @@ use crate::dom::globalscope::GlobalScope;
use crate::dom::windowproxy::WindowProxyHandler;
use crate::realms::InRealm;
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
use crate::script_thread::ScriptThread;
#[derive(JSTraceable, MallocSizeOf)]
/// Static data associated with a global object.
@ -185,10 +184,10 @@ impl DomHelpers<crate::DomTypeHolder> for crate::DomTypeHolder {
}
fn push_new_element_queue() {
push_new_element_queue()
ScriptThread::custom_element_reaction_stack().push_new_element_queue()
}
fn pop_current_element_queue(can_gc: CanGc) {
pop_current_element_queue(can_gc)
ScriptThread::custom_element_reaction_stack().pop_current_element_queue(can_gc)
}
fn reflect_dom_object<T, U>(obj: Box<T>, global: &U, can_gc: CanGc) -> DomRoot<T>

View file

@ -1044,8 +1044,12 @@ enum BackupElementQueueFlag {
}
/// <https://html.spec.whatwg.org/multipage/#custom-element-reactions-stack>
/// # Safety
/// This can be shared inside an Rc because one of those Rc copies lives
/// inside ScriptThread, so the GC can always reach this structure.
#[derive(JSTraceable, MallocSizeOf)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_in_rc)]
pub(crate) struct CustomElementReactionStack {
stack: DomRefCell<Vec<ElementQueue>>,
backup_queue: ElementQueue,

View file

@ -124,7 +124,7 @@ use crate::dom::cdatasection::CDATASection;
use crate::dom::comment::Comment;
use crate::dom::compositionevent::CompositionEvent;
use crate::dom::cssstylesheet::CSSStyleSheet;
use crate::dom::customelementregistry::CustomElementDefinition;
use crate::dom::customelementregistry::{CustomElementDefinition, CustomElementReactionStack};
use crate::dom::customevent::CustomEvent;
use crate::dom::document_event_handler::DocumentEventHandler;
use crate::dom::documentfragment::DocumentFragment;
@ -565,6 +565,10 @@ pub(crate) struct Document {
/// this `Document` will not perform any more rendering updates.
#[no_trace]
current_canvas_epoch: RefCell<Epoch>,
/// The global custom element reaction stack for this script thread.
#[conditional_malloc_size_of]
custom_element_reaction_stack: Rc<CustomElementReactionStack>,
}
#[allow(non_snake_case)]
@ -3261,6 +3265,7 @@ impl Document {
allow_declarative_shadow_roots: bool,
inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
has_trustworthy_ancestor_origin: bool,
custom_element_reaction_stack: Rc<CustomElementReactionStack>,
) -> Document {
let url = url.unwrap_or_else(|| ServoUrl::parse("about:blank").unwrap());
@ -3434,6 +3439,7 @@ impl Document {
resize_observer_started_observing_target: Cell::new(false),
waiting_on_canvas_image_updates: Cell::new(false),
current_canvas_epoch: RefCell::new(Epoch(0)),
custom_element_reaction_stack,
}
}
@ -3536,6 +3542,7 @@ impl Document {
allow_declarative_shadow_roots: bool,
inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
has_trustworthy_ancestor_origin: bool,
custom_element_reaction_stack: Rc<CustomElementReactionStack>,
can_gc: CanGc,
) -> DomRoot<Document> {
Self::new_with_proto(
@ -3557,6 +3564,7 @@ impl Document {
allow_declarative_shadow_roots,
inherited_insecure_requests_policy,
has_trustworthy_ancestor_origin,
custom_element_reaction_stack,
can_gc,
)
}
@ -3581,6 +3589,7 @@ impl Document {
allow_declarative_shadow_roots: bool,
inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
has_trustworthy_ancestor_origin: bool,
custom_element_reaction_stack: Rc<CustomElementReactionStack>,
can_gc: CanGc,
) -> DomRoot<Document> {
let document = reflect_dom_object_with_proto(
@ -3602,6 +3611,7 @@ impl Document {
allow_declarative_shadow_roots,
inherited_insecure_requests_policy,
has_trustworthy_ancestor_origin,
custom_element_reaction_stack,
)),
window,
proto,
@ -3736,6 +3746,7 @@ impl Document {
self.allow_declarative_shadow_roots(),
Some(self.insecure_requests_policy()),
self.has_trustworthy_ancestor_or_current_origin(),
self.custom_element_reaction_stack.clone(),
can_gc,
);
new_doc
@ -4413,6 +4424,10 @@ impl Document {
pub(crate) fn highlighted_dom_node(&self) -> Option<DomRoot<Node>> {
self.highlighted_dom_node.get()
}
pub(crate) fn custom_element_reaction_stack(&self) -> Rc<CustomElementReactionStack> {
self.custom_element_reaction_stack.clone()
}
}
#[allow(non_snake_case)]
@ -4444,6 +4459,7 @@ impl DocumentMethods<crate::DomTypeHolder> for Document {
doc.allow_declarative_shadow_roots(),
Some(doc.insecure_requests_policy()),
doc.has_trustworthy_ancestor_or_current_origin(),
doc.custom_element_reaction_stack(),
can_gc,
))
}

View file

@ -117,6 +117,7 @@ impl DOMImplementationMethods<crate::DomTypeHolder> for DOMImplementation {
loader,
Some(self.document.insecure_requests_policy()),
self.document.has_trustworthy_ancestor_or_current_origin(),
self.document.custom_element_reaction_stack(),
can_gc,
);
@ -184,6 +185,7 @@ impl DOMImplementationMethods<crate::DomTypeHolder> for DOMImplementation {
self.document.allow_declarative_shadow_roots(),
Some(self.document.insecure_requests_policy()),
self.document.has_trustworthy_ancestor_or_current_origin(),
self.document.custom_element_reaction_stack(),
can_gc,
);

View file

@ -104,6 +104,7 @@ impl DOMParserMethods<crate::DomTypeHolder> for DOMParser {
false,
Some(doc.insecure_requests_policy()),
doc.has_trustworthy_ancestor_or_current_origin(),
doc.custom_element_reaction_stack(),
can_gc,
);
// Step switch-1. Parse HTML from a string given document and compliantString.
@ -131,6 +132,7 @@ impl DOMParserMethods<crate::DomTypeHolder> for DOMParser {
false,
Some(doc.insecure_requests_policy()),
doc.has_trustworthy_ancestor_or_current_origin(),
doc.custom_element_reaction_stack(),
can_gc,
);
// Step switch-1. Create an XML parser parser, associated with document,

View file

@ -326,7 +326,7 @@ impl Node {
/// Implements the "unsafely set HTML" algorithm as specified in:
/// <https://html.spec.whatwg.org/multipage/#concept-unsafely-set-html>
pub fn unsafely_set_html(
pub(crate) fn unsafely_set_html(
target: &Node,
context_element: &Element,
html: DOMString,
@ -2926,6 +2926,7 @@ impl Node {
document.allow_declarative_shadow_roots(),
Some(document.insecure_requests_policy()),
document.has_trustworthy_ancestor_or_current_origin(),
document.custom_element_reaction_stack(),
can_gc,
);
DomRoot::upcast::<Node>(document)

View file

@ -8,6 +8,7 @@ use std::borrow::Cow;
use std::cell::{Cell, Ref, RefCell, RefMut};
use std::collections::HashMap;
use std::collections::vec_deque::VecDeque;
use std::rc::Rc;
use std::thread;
use crossbeam_channel::{Receiver, Sender, unbounded};
@ -29,6 +30,7 @@ use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString;
use crate::dom::comment::Comment;
use crate::dom::customelementregistry::CustomElementReactionStack;
use crate::dom::document::Document;
use crate::dom::documenttype::DocumentType;
use crate::dom::element::{Element, ElementCreator};
@ -228,6 +230,8 @@ pub(crate) struct Tokenizer {
#[no_trace]
url: ServoUrl,
parsing_algorithm: ParsingAlgorithm,
#[conditional_malloc_size_of]
custom_element_reaction_stack: Rc<CustomElementReactionStack>,
}
impl Tokenizer {
@ -246,6 +250,7 @@ impl Tokenizer {
None => ParsingAlgorithm::Normal,
};
let custom_element_reaction_stack = document.custom_element_reaction_stack();
let tokenizer = Tokenizer {
document: Dom::from_ref(document),
receiver: tokenizer_receiver,
@ -253,6 +258,7 @@ impl Tokenizer {
nodes: RefCell::new(HashMap::new()),
url,
parsing_algorithm: algorithm,
custom_element_reaction_stack,
};
tokenizer.insert_node(0, Dom::from_ref(document.upcast()));
@ -397,7 +403,14 @@ impl Tokenizer {
.GetParentNode()
.expect("append_before_sibling called on node without parent");
super::insert(parent, Some(sibling), node, self.parsing_algorithm, can_gc);
super::insert(
parent,
Some(sibling),
node,
self.parsing_algorithm,
&self.custom_element_reaction_stack,
can_gc,
);
}
fn append(&self, parent: ParseNodeId, node: NodeOrText, can_gc: CanGc) {
@ -409,7 +422,14 @@ impl Tokenizer {
};
let parent = &**self.get_node(&parent);
super::insert(parent, None, node, self.parsing_algorithm, can_gc);
super::insert(
parent,
None,
node,
self.parsing_algorithm,
&self.custom_element_reaction_stack,
can_gc,
);
}
fn has_parent_node(&self, node: ParseNodeId) -> bool {
@ -454,6 +474,7 @@ impl Tokenizer {
&self.document,
ElementCreator::ParserCreated(current_line),
ParsingAlgorithm::Normal,
&self.custom_element_reaction_stack,
can_gc,
);
self.insert_node(node, Dom::from_ref(element.upcast()));

View file

@ -52,12 +52,14 @@ impl Tokenizer {
fragment_context: Option<super::FragmentContext>,
parsing_algorithm: ParsingAlgorithm,
) -> Self {
let custom_element_reaction_stack = document.custom_element_reaction_stack();
let sink = Sink {
base_url: url,
document: Dom::from_ref(document),
current_line: Cell::new(1),
script: Default::default(),
parsing_algorithm,
custom_element_reaction_stack,
};
let quirks_mode = match document.quirks_mode() {

View file

@ -4,6 +4,7 @@
use std::borrow::Cow;
use std::cell::Cell;
use std::rc::Rc;
use base::cross_process_instant::CrossProcessInstant;
use base::id::PipelineId;
@ -57,6 +58,7 @@ use crate::dom::bindings::str::{DOMString, USVString};
use crate::dom::characterdata::CharacterData;
use crate::dom::comment::Comment;
use crate::dom::csp::{CspReporting, GlobalCspReporting, Violation, parse_csp_list_from_metadata};
use crate::dom::customelementregistry::CustomElementReactionStack;
use crate::dom::document::{Document, DocumentSource, HasBrowsingContext, IsHTMLDocument};
use crate::dom::documentfragment::DocumentFragment;
use crate::dom::documenttype::DocumentType;
@ -244,6 +246,7 @@ impl ServoParser {
allow_declarative_shadow_roots,
Some(context_document.insecure_requests_policy()),
context_document.has_trustworthy_ancestor_or_current_origin(),
context_document.custom_element_reaction_stack(),
can_gc,
);
@ -1156,6 +1159,7 @@ fn insert(
reference_child: Option<&Node>,
child: NodeOrText<Dom<Node>>,
parsing_algorithm: ParsingAlgorithm,
custom_element_reaction_stack: &CustomElementReactionStack,
can_gc: CanGc,
) {
match child {
@ -1166,11 +1170,11 @@ fn insert(
let element_in_non_fragment =
parsing_algorithm != ParsingAlgorithm::Fragment && n.is::<Element>();
if element_in_non_fragment {
ScriptThread::push_new_element_queue();
custom_element_reaction_stack.push_new_element_queue();
}
parent.InsertBefore(&n, reference_child, can_gc).unwrap();
if element_in_non_fragment {
ScriptThread::pop_current_element_queue(can_gc);
custom_element_reaction_stack.pop_current_element_queue(can_gc);
}
},
NodeOrText::AppendText(t) => {
@ -1201,6 +1205,8 @@ pub(crate) struct Sink {
current_line: Cell<u64>,
script: MutNullableDom<HTMLScriptElement>,
parsing_algorithm: ParsingAlgorithm,
#[conditional_malloc_size_of]
custom_element_reaction_stack: Rc<CustomElementReactionStack>,
}
impl Sink {
@ -1278,6 +1284,7 @@ impl TreeSink for Sink {
&self.document,
ElementCreator::ParserCreated(self.current_line.get()),
parsing_algorithm,
&self.custom_element_reaction_stack,
CanGc::note(),
);
Dom::from_ref(element.upcast())
@ -1347,6 +1354,7 @@ impl TreeSink for Sink {
Some(sibling),
new_node,
self.parsing_algorithm,
&self.custom_element_reaction_stack,
CanGc::note(),
);
}
@ -1366,7 +1374,14 @@ impl TreeSink for Sink {
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
fn append(&self, parent: &Dom<Node>, child: NodeOrText<Dom<Node>>) {
insert(parent, None, child, self.parsing_algorithm, CanGc::note());
insert(
parent,
None,
child,
self.parsing_algorithm,
&self.custom_element_reaction_stack,
CanGc::note(),
);
}
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
@ -1479,6 +1494,7 @@ fn create_element_for_token(
document: &Document,
creator: ElementCreator,
parsing_algorithm: ParsingAlgorithm,
custom_element_reaction_stack: &CustomElementReactionStack,
can_gc: CanGc,
) -> DomRoot<Element> {
// Step 3.
@ -1506,7 +1522,7 @@ fn create_element_for_token(
.perform_a_microtask_checkpoint(can_gc);
}
// Step 6.3
ScriptThread::push_new_element_queue()
custom_element_reaction_stack.push_new_element_queue()
}
// Step 7.
@ -1544,7 +1560,7 @@ fn create_element_for_token(
// Step 9.
if will_execute_script {
// Steps 9.1 - 9.2.
ScriptThread::pop_current_element_queue(can_gc);
custom_element_reaction_stack.pop_current_element_queue(can_gc);
// Step 9.3.
document.decrement_throw_on_dynamic_markup_insertion_counter();
}

View file

@ -35,6 +35,7 @@ impl Tokenizer {
current_line: Cell::new(1),
script: Default::default(),
parsing_algorithm: ParsingAlgorithm::Normal,
custom_element_reaction_stack: document.custom_element_reaction_stack(),
};
let tb = XmlTreeBuilder::new(sink, Default::default());

View file

@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::rc::Rc;
use data_url::mime::Mime;
use dom_struct::dom_struct;
use net_traits::request::InsecureRequestsPolicy;
@ -17,6 +19,7 @@ use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::reflect_dom_object;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::customelementregistry::CustomElementReactionStack;
use crate::dom::document::{Document, DocumentSource, HasBrowsingContext, IsHTMLDocument};
use crate::dom::location::Location;
use crate::dom::node::Node;
@ -44,6 +47,7 @@ impl XMLDocument {
doc_loader: DocumentLoader,
inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
has_trustworthy_ancestor_origin: bool,
custom_element_reaction_stack: Rc<CustomElementReactionStack>,
) -> XMLDocument {
XMLDocument {
document: Document::new_inherited(
@ -64,6 +68,7 @@ impl XMLDocument {
false,
inherited_insecure_requests_policy,
has_trustworthy_ancestor_origin,
custom_element_reaction_stack,
),
}
}
@ -82,6 +87,7 @@ impl XMLDocument {
doc_loader: DocumentLoader,
inherited_insecure_requests_policy: Option<InsecureRequestsPolicy>,
has_trustworthy_ancestor_origin: bool,
custom_element_reaction_stack: Rc<CustomElementReactionStack>,
can_gc: CanGc,
) -> DomRoot<XMLDocument> {
let doc = reflect_dom_object(
@ -98,6 +104,7 @@ impl XMLDocument {
doc_loader,
inherited_insecure_requests_policy,
has_trustworthy_ancestor_origin,
custom_element_reaction_stack,
)),
window,
can_gc,

View file

@ -1535,6 +1535,7 @@ impl XMLHttpRequest {
false,
Some(doc.insecure_requests_policy()),
doc.has_trustworthy_ancestor_origin(),
doc.custom_element_reaction_stack(),
can_gc,
)
}

View file

@ -5,6 +5,7 @@
use core::fmt;
#[cfg(feature = "webgpu")]
use std::cell::RefCell;
use std::collections::HashSet;
use std::option::Option;
use std::result::Result;
@ -397,10 +398,11 @@ impl ScriptThreadReceivers {
&self,
task_queue: &TaskQueue<MainThreadScriptMsg>,
timer_scheduler: &TimerScheduler,
fully_active: &HashSet<PipelineId>,
) -> MixedMessage {
select! {
recv(task_queue.select()) -> msg => {
task_queue.take_tasks(msg.unwrap());
task_queue.take_tasks(msg.unwrap(), fully_active);
let event = task_queue
.recv()
.expect("Spurious wake-up of the event-loop, task-queue has no tasks available");
@ -437,6 +439,7 @@ impl ScriptThreadReceivers {
pub(crate) fn try_recv(
&self,
task_queue: &TaskQueue<MainThreadScriptMsg>,
fully_active: &HashSet<PipelineId>,
) -> Option<MixedMessage> {
if let Ok(message) = self.constellation_receiver.try_recv() {
let message = message
@ -449,7 +452,7 @@ impl ScriptThreadReceivers {
.ok()?;
return MixedMessage::FromConstellation(message).into();
}
if let Ok(message) = task_queue.take_tasks_and_recv() {
if let Ok(message) = task_queue.take_tasks_and_recv(fully_active) {
return MixedMessage::FromScript(message).into();
}
if let Ok(message) = self.devtools_server_receiver.try_recv() {

View file

@ -279,7 +279,7 @@ pub struct ScriptThread {
docs_with_no_blocking_loads: DomRefCell<HashSet<Dom<Document>>>,
/// <https://html.spec.whatwg.org/multipage/#custom-element-reactions-stack>
custom_element_reaction_stack: CustomElementReactionStack,
custom_element_reaction_stack: Rc<CustomElementReactionStack>,
/// Cross-process access to the compositor's API.
#[no_trace]
@ -723,24 +723,21 @@ impl ScriptThread {
with_script_thread(|script_thread| script_thread.is_user_interacting.get())
}
pub(crate) fn get_fully_active_document_ids() -> HashSet<PipelineId> {
with_script_thread(|script_thread| {
script_thread
.documents
.borrow()
.iter()
.filter_map(|(id, document)| {
if document.is_fully_active() {
Some(id)
} else {
None
}
})
.fold(HashSet::new(), |mut set, id| {
let _ = set.insert(id);
set
})
})
pub(crate) fn get_fully_active_document_ids(&self) -> HashSet<PipelineId> {
self.documents
.borrow()
.iter()
.filter_map(|(id, document)| {
if document.is_fully_active() {
Some(id)
} else {
None
}
})
.fold(HashSet::new(), |mut set, id| {
let _ = set.insert(id);
set
})
}
pub(crate) fn find_window_proxy(id: BrowsingContextId) -> Option<DomRoot<WindowProxy>> {
@ -810,19 +807,13 @@ impl ScriptThread {
.register_paint_worklet_modules(name, properties, painter);
}
pub(crate) fn push_new_element_queue() {
with_script_thread(|script_thread| {
pub(crate) fn custom_element_reaction_stack() -> Rc<CustomElementReactionStack> {
with_optional_script_thread(|script_thread| {
script_thread
.as_ref()
.unwrap()
.custom_element_reaction_stack
.push_new_element_queue();
})
}
pub(crate) fn pop_current_element_queue(can_gc: CanGc) {
with_script_thread(|script_thread| {
script_thread
.custom_element_reaction_stack
.pop_current_element_queue(can_gc);
.clone()
})
}
@ -1006,7 +997,7 @@ impl ScriptThread {
webxr_registry: state.webxr_registry,
worklet_thread_pool: Default::default(),
docs_with_no_blocking_loads: Default::default(),
custom_element_reaction_stack: CustomElementReactionStack::new(),
custom_element_reaction_stack: Rc::new(CustomElementReactionStack::new()),
compositor_api: state.compositor_api,
profile_script_events: opts.debug.profile_script_events,
print_pwm: opts.print_pwm,
@ -1352,9 +1343,12 @@ impl ScriptThread {
// Receive at least one message so we don't spinloop.
debug!("Waiting for event.");
let mut event = self
.receivers
.recv(&self.task_queue, &self.timer_scheduler.borrow());
let fully_active = self.get_fully_active_document_ids();
let mut event = self.receivers.recv(
&self.task_queue,
&self.timer_scheduler.borrow(),
&fully_active,
);
loop {
debug!("Handling event: {event:?}");
@ -1464,7 +1458,7 @@ impl ScriptThread {
// If any of our input sources has an event pending, we'll perform another iteration
// and check for more resize events. If there are no events pending, we'll move
// on and execute the sequential non-resize events we've seen.
match self.receivers.try_recv(&self.task_queue) {
match self.receivers.try_recv(&self.task_queue, &fully_active) {
Some(new_event) => event = new_event,
None => break,
}
@ -3422,6 +3416,7 @@ impl ScriptThread {
true,
incomplete.load_data.inherited_insecure_requests_policy,
incomplete.load_data.has_trustworthy_ancestor_origin,
self.custom_element_reaction_stack.clone(),
can_gc,
);

View file

@ -15,7 +15,6 @@ use strum::VariantArray;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::worker::TrustedWorkerAddress;
use crate::script_runtime::ScriptThreadEventCategory;
use crate::script_thread::ScriptThread;
use crate::task::TaskBox;
use crate::task_source::TaskSourceName;
@ -197,19 +196,18 @@ impl<T: QueuedTaskConversion> TaskQueue<T> {
}
/// Take all tasks again and then run `recv()`.
pub(crate) fn take_tasks_and_recv(&self) -> Result<T, ()> {
self.take_tasks(T::wake_up_msg());
pub(crate) fn take_tasks_and_recv(&self, fully_active: &HashSet<PipelineId>) -> Result<T, ()> {
self.take_tasks(T::wake_up_msg(), fully_active);
self.recv()
}
/// Drain the queue for the current iteration of the event-loop.
/// Holding-back throttles above a given high-water mark.
pub(crate) fn take_tasks(&self, first_msg: T) {
pub(crate) fn take_tasks(&self, first_msg: T, fully_active: &HashSet<PipelineId>) {
// High-watermark: once reached, throttled tasks will be held-back.
const PER_ITERATION_MAX: u64 = 5;
let fully_active = ScriptThread::get_fully_active_document_ids();
// Always first check for new tasks, but don't reset 'taken_task_counter'.
self.process_incoming_tasks(first_msg, &fully_active);
self.process_incoming_tasks(first_msg, fully_active);
let mut throttled = self.throttled.borrow_mut();
let mut throttled_length: usize = throttled.values().map(|queue| queue.len()).sum();
let mut task_source_cycler = TaskSourceName::VARIANTS.iter().cycle();