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