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