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::id::PainterId;
8use ipc_channel::ipc::{self, IpcSender};
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, Serialize)]
22pub enum SystemFontServiceMessage {
23    GetFontTemplates(
24        Option<FontDescriptor>,
25        SingleFontFamily,
26        IpcSender<Vec<FontTemplate>>,
27    ),
28    GetFontInstance(
29        PainterId,
30        FontIdentifier,
31        Au,
32        FontInstanceFlags,
33        Vec<FontVariation>,
34        IpcSender<FontInstanceKey>,
35    ),
36    PrefetchFontKeys(PainterId),
37    GetFontKey(PainterId, IpcSender<FontKey>),
38    GetFontInstanceKey(PainterId, IpcSender<FontInstanceKey>),
39    CollectMemoryReport(ReportsChan),
40    Exit(IpcSender<()>),
41    Ping,
42}
43
44#[derive(Clone, Deserialize, Serialize)]
45pub struct SystemFontServiceProxySender(pub IpcSender<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: IpcSender<SystemFontServiceMessage>,
67    templates: RwLock<HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>>,
68}
69
70impl SystemFontServiceProxy {
71    pub fn exit(&self) {
72        let (response_chan, response_port) = ipc::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) = ipc::channel().expect("failed to create IPC channel");
94        self.sender
95            .send(SystemFontServiceMessage::GetFontInstance(
96                painter_id,
97                identifier,
98                size,
99                flags,
100                variations,
101                response_chan,
102            ))
103            .expect("failed to send message to system font service");
104
105        let instance_key = response_port.recv();
106        if instance_key.is_err() {
107            let font_thread_has_closed = self.sender.send(SystemFontServiceMessage::Ping).is_err();
108            assert!(
109                font_thread_has_closed,
110                "Failed to receive a response from live font cache"
111            );
112            panic!("SystemFontService has already exited.");
113        }
114        instance_key.unwrap()
115    }
116
117    pub fn find_matching_font_templates(
118        &self,
119        descriptor_to_match: Option<&FontDescriptor>,
120        family_descriptor: &SingleFontFamily,
121    ) -> Vec<FontTemplateRef> {
122        let cache_key = FontTemplateCacheKey {
123            font_descriptor: descriptor_to_match.cloned(),
124            family_descriptor: family_descriptor.clone(),
125        };
126        if let Some(templates) = self.templates.read().get(&cache_key).cloned() {
127            return templates;
128        }
129
130        debug!(
131            "SystemFontServiceProxy: cache miss for template_descriptor={:?} family_descriptor={:?}",
132            descriptor_to_match, family_descriptor
133        );
134
135        let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel");
136        self.sender
137            .send(SystemFontServiceMessage::GetFontTemplates(
138                descriptor_to_match.cloned(),
139                family_descriptor.clone(),
140                response_chan,
141            ))
142            .expect("failed to send message to system font service");
143
144        let Ok(templates) = response_port.recv() else {
145            let font_thread_has_closed = self.sender.send(SystemFontServiceMessage::Ping).is_err();
146            assert!(
147                font_thread_has_closed,
148                "Failed to receive a response from live font cache"
149            );
150            panic!("SystemFontService has already exited.");
151        };
152
153        let templates: Vec<_> = templates.into_iter().map(FontTemplateRef::new).collect();
154        self.templates.write().insert(cache_key, templates.clone());
155
156        templates
157    }
158
159    pub fn generate_font_key(&self, painter_id: PainterId) -> FontKey {
160        let (result_sender, result_receiver) =
161            ipc::channel().expect("failed to create IPC channel");
162        self.sender
163            .send(SystemFontServiceMessage::GetFontKey(
164                painter_id,
165                result_sender,
166            ))
167            .expect("failed to send message to system font service");
168        result_receiver
169            .recv()
170            .expect("Failed to communicate with system font service.")
171    }
172
173    pub fn generate_font_instance_key(&self, painter_id: PainterId) -> FontInstanceKey {
174        let (result_sender, result_receiver) =
175            ipc::channel().expect("failed to create IPC channel");
176        self.sender
177            .send(SystemFontServiceMessage::GetFontInstanceKey(
178                painter_id,
179                result_sender,
180            ))
181            .expect("failed to send message to system font service");
182        result_receiver
183            .recv()
184            .expect("Failed to communicate with system font service.")
185    }
186
187    pub fn prefetch_font_keys_for_painter(&self, painter_id: PainterId) {
188        let _ = self
189            .sender
190            .send(SystemFontServiceMessage::PrefetchFontKeys(painter_id));
191    }
192}