devtools/actors/
worker.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 atomic_refcell::AtomicRefCell;
6use devtools_traits::DevtoolScriptControlMsg::WantsLiveNotifications;
7use devtools_traits::{DevtoolScriptControlMsg, WorkerId};
8use malloc_size_of_derive::MallocSizeOf;
9use rustc_hash::FxHashSet;
10use serde::Serialize;
11use serde_json::{Map, Value};
12use servo_base::generic_channel::GenericSender;
13use servo_base::id::TEST_PIPELINE_ID;
14use servo_url::ServoUrl;
15
16use crate::StreamId;
17use crate::actor::{Actor, ActorEncode, ActorError, ActorRegistry};
18use crate::protocol::{ClientRequest, JsonPacketStream};
19use crate::resource::ResourceAvailable;
20
21#[derive(Clone, Copy, MallocSizeOf)]
22#[expect(dead_code)]
23pub enum WorkerType {
24    Dedicated = 0,
25    Shared = 1,
26    Service = 2,
27}
28
29#[derive(MallocSizeOf)]
30pub(crate) struct WorkerActor {
31    pub name: String,
32    pub console_name: String,
33    pub thread_name: String,
34    pub worker_id: WorkerId,
35    pub url: ServoUrl,
36    pub type_: WorkerType,
37    pub script_chan: GenericSender<DevtoolScriptControlMsg>,
38    pub streams: AtomicRefCell<FxHashSet<StreamId>>,
39}
40
41impl ResourceAvailable for WorkerActor {
42    fn actor_name(&self) -> String {
43        self.name.clone()
44    }
45}
46
47impl Actor for WorkerActor {
48    fn name(&self) -> String {
49        self.name.clone()
50    }
51    fn handle_message(
52        &self,
53        mut request: ClientRequest,
54        _registry: &ActorRegistry,
55        msg_type: &str,
56        _msg: &Map<String, Value>,
57        stream_id: StreamId,
58    ) -> Result<(), ActorError> {
59        match msg_type {
60            "attach" => {
61                let msg = AttachedReply {
62                    from: self.name(),
63                    type_: "attached".to_owned(),
64                    url: self.url.as_str().to_owned(),
65                };
66                // FIXME: we don’t send an actual reply (message without type), which seems to be a bug?
67                request.write_json_packet(&msg)?;
68                self.streams.borrow_mut().insert(stream_id);
69                // FIXME: fix messages to not require forging a pipeline for worker messages
70                self.script_chan
71                    .send(WantsLiveNotifications(TEST_PIPELINE_ID, true))
72                    .unwrap();
73            },
74
75            "connect" => {
76                let msg = ConnectReply {
77                    from: self.name(),
78                    type_: "connected".to_owned(),
79                    thread_actor: self.thread_name.clone(),
80                    console_actor: self.console_name.clone(),
81                };
82                // FIXME: we don’t send an actual reply (message without type), which seems to be a bug?
83                request.write_json_packet(&msg)?;
84            },
85
86            "detach" => {
87                let msg = DetachedReply {
88                    from: self.name(),
89                    type_: "detached".to_string(),
90                };
91                self.cleanup(stream_id);
92                // FIXME: we don’t send an actual reply (message without type), which seems to be a bug?
93                request.write_json_packet(&msg)?;
94            },
95
96            "getPushSubscription" => {
97                let msg = GetPushSubscriptionReply {
98                    from: self.name(),
99                    subscription: None,
100                };
101                request.reply_final(&msg)?
102            },
103
104            _ => return Err(ActorError::UnrecognizedPacketType),
105        };
106        Ok(())
107    }
108
109    fn cleanup(&self, stream_id: StreamId) {
110        self.streams.borrow_mut().remove(&stream_id);
111        if self.streams.borrow().is_empty() {
112            self.script_chan
113                .send(WantsLiveNotifications(TEST_PIPELINE_ID, false))
114                .unwrap();
115        }
116    }
117}
118
119#[derive(Serialize)]
120struct GetPushSubscriptionReply {
121    from: String,
122    subscription: Option<()>,
123}
124
125#[derive(Serialize)]
126struct DetachedReply {
127    from: String,
128    #[serde(rename = "type")]
129    type_: String,
130}
131
132#[derive(Serialize)]
133struct AttachedReply {
134    from: String,
135    #[serde(rename = "type")]
136    type_: String,
137    url: String,
138}
139
140#[derive(Serialize)]
141#[serde(rename_all = "camelCase")]
142struct ConnectReply {
143    from: String,
144    #[serde(rename = "type")]
145    type_: String,
146    thread_actor: String,
147    console_actor: String,
148}
149
150#[derive(Serialize)]
151#[serde(rename_all = "camelCase")]
152struct WorkerTraits {
153    is_parent_intercept_enabled: bool,
154    supports_top_level_target_flag: bool,
155}
156
157#[derive(Serialize)]
158#[serde(rename_all = "camelCase")]
159pub(crate) struct WorkerActorMsg {
160    actor: String,
161    console_actor: String,
162    thread_actor: String,
163    id: String,
164    url: String,
165    traits: WorkerTraits,
166    #[serde(rename = "type")]
167    type_: u32,
168    #[serde(rename = "targetType")]
169    target_type: String,
170}
171
172impl ActorEncode<WorkerActorMsg> for WorkerActor {
173    fn encode(&self, _: &ActorRegistry) -> WorkerActorMsg {
174        WorkerActorMsg {
175            actor: self.name(),
176            console_actor: self.console_name.clone(),
177            thread_actor: self.thread_name.clone(),
178            id: self.worker_id.0.to_string(),
179            url: self.url.to_string(),
180            traits: WorkerTraits {
181                is_parent_intercept_enabled: false,
182                supports_top_level_target_flag: false,
183            },
184            type_: self.type_ as u32,
185            target_type: match self.type_ {
186                WorkerType::Service => "service_worker",
187                _ => "worker",
188            }
189            .to_string(),
190        }
191    }
192}