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 base::generic_channel::{self, GenericSender};
8use base::id::PainterId;
9use log::debug;
10use malloc_size_of_derive::MallocSizeOf;
11use parking_lot::RwLock;
12use profile_traits::mem::ReportsChan;
13use serde::{Deserialize, Serialize};
14use style::values::computed::font::SingleFontFamily;
15use webrender_api::units::Au;
16use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, FontVariation};
17
18use crate::{FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef};
19
20/// Commands that the `FontContext` sends to the `SystemFontService`.
21#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
22pub enum SystemFontServiceMessage {
23    GetFontTemplates(
24        Option<FontDescriptor>,
25        SingleFontFamily,
26        GenericSender<Vec<FontTemplate>>,
27    ),
28    GetFontInstance(
29        PainterId,
30        FontIdentifier,
31        Au,
32        FontInstanceFlags,
33        Vec<FontVariation>,
34        GenericSender<FontInstanceKey>,
35    ),
36    PrefetchFontKeys(PainterId),
37    GetFontKey(PainterId, GenericSender<FontKey>),
38    GetFontInstanceKey(PainterId, GenericSender<FontInstanceKey>),
39    CollectMemoryReport(ReportsChan),
40    Exit(GenericSender<()>),
41    Ping,
42}
43
44#[derive(Clone, Deserialize, Serialize)]
45pub struct SystemFontServiceProxySender(pub GenericSender<SystemFontServiceMessage>);
46
47impl SystemFontServiceProxySender {
48    pub fn to_proxy(&self) -> SystemFontServiceProxy {
49        SystemFontServiceProxy {
50            sender: self.0.clone(),
51            templates: Default::default(),
52        }
53    }
54}
55
56#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)]
57struct FontTemplateCacheKey {
58    font_descriptor: Option<FontDescriptor>,
59    family_descriptor: SingleFontFamily,
60}
61
62/// The public interface to the [`SystemFontService`], used by per-Document
63/// `FontContext` instances.
64#[derive(Debug, MallocSizeOf)]
65pub struct SystemFontServiceProxy {
66    sender: GenericSender<SystemFontServiceMessage>,
67    templates: RwLock<HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>>,
68}
69
70impl SystemFontServiceProxy {
71    pub fn exit(&self) {
72        let (response_chan, response_port) = generic_channel::channel().unwrap();
73        self.sender
74            .send(SystemFontServiceMessage::Exit(response_chan))
75            .expect("Couldn't send SystemFontService exit message");
76        response_port
77            .recv()
78            .expect("Couldn't receive SystemFontService reply");
79    }
80
81    pub fn to_sender(&self) -> SystemFontServiceProxySender {
82        SystemFontServiceProxySender(self.sender.clone())
83    }
84
85    pub fn get_system_font_instance(
86        &self,
87        identifier: FontIdentifier,
88        size: Au,
89        flags: FontInstanceFlags,
90        variations: Vec<FontVariation>,
91        painter_id: PainterId,
92    ) -> FontInstanceKey {
93        let (response_chan, response_port) =
94            generic_channel::channel().expect("failed to create IPC channel");
95        self.sender
96            .send(SystemFontServiceMessage::GetFontInstance(
97                painter_id,
98                identifier,
99                size,
100                flags,
101                variations,
102                response_chan,
103            ))
104            .expect("failed to send message to system font service");
105
106        let instance_key = response_port.recv();
107        if instance_key.is_err() {
108            let font_thread_has_closed = self.sender.send(SystemFontServiceMessage::Ping).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) =
137            generic_channel::channel().expect("failed to create IPC channel");
138        self.sender
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.sender.send(SystemFontServiceMessage::Ping).is_err();
148            assert!(
149                font_thread_has_closed,
150                "Failed to receive a response from live font cache"
151            );
152            panic!("SystemFontService has already exited.");
153        };
154
155        let templates: Vec<_> = templates.into_iter().map(FontTemplateRef::new).collect();
156        self.templates.write().insert(cache_key, templates.clone());
157
158        templates
159    }
160
161    pub fn generate_font_key(&self, painter_id: PainterId) -> FontKey {
162        let (result_sender, result_receiver) =
163            generic_channel::channel().expect("failed to create IPC channel");
164        self.sender
165            .send(SystemFontServiceMessage::GetFontKey(
166                painter_id,
167                result_sender,
168            ))
169            .expect("failed to send message to system font service");
170        result_receiver
171            .recv()
172            .expect("Failed to communicate with system font service.")
173    }
174
175    pub fn generate_font_instance_key(&self, painter_id: PainterId) -> FontInstanceKey {
176        let (result_sender, result_receiver) =
177            generic_channel::channel().expect("failed to create IPC channel");
178        self.sender
179            .send(SystemFontServiceMessage::GetFontInstanceKey(
180                painter_id,
181                result_sender,
182            ))
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
189    pub fn prefetch_font_keys_for_painter(&self, painter_id: PainterId) {
190        let _ = self
191            .sender
192            .send(SystemFontServiceMessage::PrefetchFontKeys(painter_id));
193    }
194}