script/
network_listener.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::sync::{Arc, Mutex};
6
7use net_traits::{
8    Action, BoxedFetchCallback, FetchResponseListener, FetchResponseMsg, ResourceFetchTiming,
9    ResourceTimingType,
10};
11use servo_url::ServoUrl;
12
13use crate::dom::bindings::inheritance::Castable;
14use crate::dom::bindings::root::DomRoot;
15use crate::dom::globalscope::GlobalScope;
16use crate::dom::performance::performanceentry::PerformanceEntry;
17use crate::dom::performance::performanceresourcetiming::{
18    InitiatorType, PerformanceResourceTiming,
19};
20use crate::script_runtime::CanGc;
21use crate::task::TaskOnce;
22use crate::task_source::SendableTaskSource;
23
24/// An off-thread sink for async network event tasks. All such events are forwarded to
25/// a target thread, where they are invoked on the provided context object.
26pub(crate) struct NetworkListener<Listener: PreInvoke + Send + 'static> {
27    pub(crate) context: Arc<Mutex<Listener>>,
28    pub(crate) task_source: SendableTaskSource,
29}
30
31pub(crate) trait ResourceTimingListener {
32    fn resource_timing_information(&self) -> (InitiatorType, ServoUrl);
33    fn resource_timing_global(&self) -> DomRoot<GlobalScope>;
34}
35
36pub(crate) fn submit_timing<T: ResourceTimingListener + FetchResponseListener>(
37    listener: &T,
38    can_gc: CanGc,
39) {
40    if listener.resource_timing().timing_type != ResourceTimingType::Resource {
41        warn!(
42            "Submitting non-resource ({:?}) timing as resource",
43            listener.resource_timing().timing_type
44        );
45        return;
46    }
47
48    let (initiator_type, url) = listener.resource_timing_information();
49    if initiator_type == InitiatorType::Other {
50        warn!("Ignoring InitiatorType::Other resource {:?}", url);
51        return;
52    }
53
54    submit_timing_data(
55        &listener.resource_timing_global(),
56        url,
57        initiator_type,
58        listener.resource_timing(),
59        can_gc,
60    );
61}
62
63pub(crate) fn submit_timing_data(
64    global: &GlobalScope,
65    url: ServoUrl,
66    initiator_type: InitiatorType,
67    resource_timing: &ResourceFetchTiming,
68    can_gc: CanGc,
69) {
70    let performance_entry =
71        PerformanceResourceTiming::new(global, url, initiator_type, None, resource_timing, can_gc);
72    global
73        .performance()
74        .queue_entry(performance_entry.upcast::<PerformanceEntry>(), can_gc);
75}
76
77impl<Listener: PreInvoke + Send + 'static> NetworkListener<Listener> {
78    pub(crate) fn notify<A: Action<Listener> + Send + 'static>(&mut self, action: A) {
79        self.task_source.queue(ListenerTask {
80            context: self.context.clone(),
81            action,
82        });
83    }
84}
85
86// helps type inference
87impl<Listener: FetchResponseListener + PreInvoke + Send + 'static> NetworkListener<Listener> {
88    pub(crate) fn notify_fetch(&mut self, action: FetchResponseMsg) {
89        self.notify(action);
90    }
91
92    pub(crate) fn into_callback(mut self) -> BoxedFetchCallback {
93        Box::new(move |response_msg| self.notify_fetch(response_msg))
94    }
95}
96
97/// A gating mechanism that runs before invoking the task on the target thread.
98/// If the `should_invoke` method returns false, the task is discarded without
99/// being invoked.
100pub(crate) trait PreInvoke {
101    fn should_invoke(&self) -> bool {
102        true
103    }
104}
105
106/// A task for moving the async network events between threads.
107struct ListenerTask<A: Action<Listener> + Send + 'static, Listener: PreInvoke + Send> {
108    context: Arc<Mutex<Listener>>,
109    action: A,
110}
111
112impl<A, Listener> TaskOnce for ListenerTask<A, Listener>
113where
114    A: Action<Listener> + Send + 'static,
115    Listener: PreInvoke + Send,
116{
117    fn run_once(self) {
118        let mut context = self.context.lock().unwrap();
119        if context.should_invoke() {
120            self.action.process(&mut *context);
121        }
122    }
123}