fonts_traits/
system_font_service_proxy.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::collections::HashMap;
6
7use ipc_channel::ipc::{self, IpcSender};
8use log::debug;
9use malloc_size_of_derive::MallocSizeOf;
10use parking_lot::{Mutex, RwLock};
11use profile_traits::mem::ReportsChan;
12use serde::{Deserialize, Serialize};
13use style::values::computed::font::SingleFontFamily;
14use webrender_api::units::Au;
15use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, FontVariation};
16
17use crate::{FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef};
18
19/// Commands that the `FontContext` sends to the `SystemFontService`.
20#[derive(Debug, Deserialize, Serialize)]
21pub enum SystemFontServiceMessage {
22    GetFontTemplates(
23        Option<FontDescriptor>,
24        SingleFontFamily,
25        IpcSender<Vec<FontTemplate>>,
26    ),
27    GetFontInstance(
28        FontIdentifier,
29        Au,
30        FontInstanceFlags,
31        Vec<FontVariation>,
32        IpcSender<FontInstanceKey>,
33    ),
34    GetFontKey(IpcSender<FontKey>),
35    GetFontInstanceKey(IpcSender<FontInstanceKey>),
36    CollectMemoryReport(ReportsChan),
37    Exit(IpcSender<()>),
38    Ping,
39}
40
41#[derive(Clone, Deserialize, Serialize)]
42pub struct SystemFontServiceProxySender(pub IpcSender<SystemFontServiceMessage>);
43
44impl SystemFontServiceProxySender {
45    pub fn to_proxy(&self) -> SystemFontServiceProxy {
46        SystemFontServiceProxy {
47            sender: Mutex::new(self.0.clone()),
48            templates: Default::default(),
49        }
50    }
51}
52
53#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)]
54struct FontTemplateCacheKey {
55    font_descriptor: Option<FontDescriptor>,
56    family_descriptor: SingleFontFamily,
57}
58
59/// The public interface to the [`SystemFontService`], used by per-Document
60/// `FontContext` instances.
61#[derive(Debug, MallocSizeOf)]
62pub struct SystemFontServiceProxy {
63    sender: Mutex<IpcSender<SystemFontServiceMessage>>,
64    templates: RwLock<HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>>,
65}
66
67impl SystemFontServiceProxy {
68    pub fn exit(&self) {
69        let (response_chan, response_port) = ipc::channel().unwrap();
70        self.sender
71            .lock()
72            .send(SystemFontServiceMessage::Exit(response_chan))
73            .expect("Couldn't send SystemFontService exit message");
74        response_port
75            .recv()
76            .expect("Couldn't receive SystemFontService reply");
77    }
78
79    pub fn to_sender(&self) -> SystemFontServiceProxySender {
80        SystemFontServiceProxySender(self.sender.lock().clone())
81    }
82
83    pub fn get_system_font_instance(
84        &self,
85        identifier: FontIdentifier,
86        size: Au,
87        flags: FontInstanceFlags,
88        variations: Vec<FontVariation>,
89    ) -> FontInstanceKey {
90        let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel");
91        self.sender
92            .lock()
93            .send(SystemFontServiceMessage::GetFontInstance(
94                identifier,
95                size,
96                flags,
97                variations,
98                response_chan,
99            ))
100            .expect("failed to send message to system font service");
101
102        let instance_key = response_port.recv();
103        if instance_key.is_err() {
104            let font_thread_has_closed = self
105                .sender
106                .lock()
107                .send(SystemFontServiceMessage::Ping)
108                .is_err();
109            assert!(
110                font_thread_has_closed,
111                "Failed to receive a response from live font cache"
112            );
113            panic!("SystemFontService has already exited.");
114        }
115        instance_key.unwrap()
116    }
117
118    pub fn find_matching_font_templates(
119        &self,
120        descriptor_to_match: Option<&FontDescriptor>,
121        family_descriptor: &SingleFontFamily,
122    ) -> Vec<FontTemplateRef> {
123        let cache_key = FontTemplateCacheKey {
124            font_descriptor: descriptor_to_match.cloned(),
125            family_descriptor: family_descriptor.clone(),
126        };
127        if let Some(templates) = self.templates.read().get(&cache_key).cloned() {
128            return templates;
129        }
130
131        debug!(
132            "SystemFontServiceProxy: cache miss for template_descriptor={:?} family_descriptor={:?}",
133            descriptor_to_match, family_descriptor
134        );
135
136        let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel");
137        self.sender
138            .lock()
139            .send(SystemFontServiceMessage::GetFontTemplates(
140                descriptor_to_match.cloned(),
141                family_descriptor.clone(),
142                response_chan,
143            ))
144            .expect("failed to send message to system font service");
145
146        let Ok(templates) = response_port.recv() else {
147            let font_thread_has_closed = self
148                .sender
149                .lock()
150                .send(SystemFontServiceMessage::Ping)
151                .is_err();
152            assert!(
153                font_thread_has_closed,
154                "Failed to receive a response from live font cache"
155            );
156            panic!("SystemFontService has already exited.");
157        };
158
159        let templates: Vec<_> = templates.into_iter().map(FontTemplateRef::new).collect();
160        self.templates.write().insert(cache_key, templates.clone());
161
162        templates
163    }
164
165    pub fn generate_font_key(&self) -> FontKey {
166        let (result_sender, result_receiver) =
167            ipc::channel().expect("failed to create IPC channel");
168        self.sender
169            .lock()
170            .send(SystemFontServiceMessage::GetFontKey(result_sender))
171            .expect("failed to send message to system font service");
172        result_receiver
173            .recv()
174            .expect("Failed to communicate with system font service.")
175    }
176
177    pub fn generate_font_instance_key(&self) -> FontInstanceKey {
178        let (result_sender, result_receiver) =
179            ipc::channel().expect("failed to create IPC channel");
180        self.sender
181            .lock()
182            .send(SystemFontServiceMessage::GetFontInstanceKey(result_sender))
183            .expect("failed to send message to system font service");
184        result_receiver
185            .recv()
186            .expect("Failed to communicate with system font service.")
187    }
188}