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::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: 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: 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            .send(SystemFontServiceMessage::Exit(response_chan))
72            .expect("Couldn't send SystemFontService exit message");
73        response_port
74            .recv()
75            .expect("Couldn't receive SystemFontService reply");
76    }
77
78    pub fn to_sender(&self) -> SystemFontServiceProxySender {
79        SystemFontServiceProxySender(self.sender.clone())
80    }
81
82    pub fn get_system_font_instance(
83        &self,
84        identifier: FontIdentifier,
85        size: Au,
86        flags: FontInstanceFlags,
87        variations: Vec<FontVariation>,
88    ) -> FontInstanceKey {
89        let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel");
90        self.sender
91            .send(SystemFontServiceMessage::GetFontInstance(
92                identifier,
93                size,
94                flags,
95                variations,
96                response_chan,
97            ))
98            .expect("failed to send message to system font service");
99
100        let instance_key = response_port.recv();
101        if instance_key.is_err() {
102            let font_thread_has_closed = self.sender.send(SystemFontServiceMessage::Ping).is_err();
103            assert!(
104                font_thread_has_closed,
105                "Failed to receive a response from live font cache"
106            );
107            panic!("SystemFontService has already exited.");
108        }
109        instance_key.unwrap()
110    }
111
112    pub fn find_matching_font_templates(
113        &self,
114        descriptor_to_match: Option<&FontDescriptor>,
115        family_descriptor: &SingleFontFamily,
116    ) -> Vec<FontTemplateRef> {
117        let cache_key = FontTemplateCacheKey {
118            font_descriptor: descriptor_to_match.cloned(),
119            family_descriptor: family_descriptor.clone(),
120        };
121        if let Some(templates) = self.templates.read().get(&cache_key).cloned() {
122            return templates;
123        }
124
125        debug!(
126            "SystemFontServiceProxy: cache miss for template_descriptor={:?} family_descriptor={:?}",
127            descriptor_to_match, family_descriptor
128        );
129
130        let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel");
131        self.sender
132            .send(SystemFontServiceMessage::GetFontTemplates(
133                descriptor_to_match.cloned(),
134                family_descriptor.clone(),
135                response_chan,
136            ))
137            .expect("failed to send message to system font service");
138
139        let Ok(templates) = response_port.recv() else {
140            let font_thread_has_closed = self.sender.send(SystemFontServiceMessage::Ping).is_err();
141            assert!(
142                font_thread_has_closed,
143                "Failed to receive a response from live font cache"
144            );
145            panic!("SystemFontService has already exited.");
146        };
147
148        let templates: Vec<_> = templates.into_iter().map(FontTemplateRef::new).collect();
149        self.templates.write().insert(cache_key, templates.clone());
150
151        templates
152    }
153
154    pub fn generate_font_key(&self) -> FontKey {
155        let (result_sender, result_receiver) =
156            ipc::channel().expect("failed to create IPC channel");
157        self.sender
158            .send(SystemFontServiceMessage::GetFontKey(result_sender))
159            .expect("failed to send message to system font service");
160        result_receiver
161            .recv()
162            .expect("Failed to communicate with system font service.")
163    }
164
165    pub fn generate_font_instance_key(&self) -> FontInstanceKey {
166        let (result_sender, result_receiver) =
167            ipc::channel().expect("failed to create IPC channel");
168        self.sender
169            .send(SystemFontServiceMessage::GetFontInstanceKey(result_sender))
170            .expect("failed to send message to system font service");
171        result_receiver
172            .recv()
173            .expect("Failed to communicate with system font service.")
174    }
175}