Skip to main content

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