Add font-fallback on OpenHarmony and fix several compilation issues (#32141)

* Add OpenHarmony support for allocator / profile

Signed-off-by: Jonathan Schwender <jonathan.schwender@huawei.com>

* gfx: Build harfbuzz from source on OHOS

Updates `freetype-sys` to v0.20.1, which includes a build
fix for OpenHarmony.

Signed-off-by: Jonathan Schwender <jonathan.schwender@huawei.com>

* gfx: Don't depend on fontconfig on OpenHarmony

Signed-off-by: Jonathan Schwender <jonathan.schwender@huawei.com>

* gfx: Add ohos font fallback

Hardcode HarmonyOS_Sans_SC_Regular for Chinese

Signed-off-by: Jonathan Schwender <jonathan.schwender@huawei.com>

* libservo: OHOS useragent, and explicitly opt out of sandboxing

Signed-off-by: Jonathan Schwender <jonathan.schwender@huawei.com>

* libservo: Disable get_native_media_display_and_gl_context on ohos

Signed-off-by: Jonathan Schwender <jonathan.schwender@huawei.com>

---------

Signed-off-by: Jonathan Schwender <jonathan.schwender@huawei.com>
This commit is contained in:
Jonathan Schwender 2024-05-02 20:32:51 +02:00 committed by GitHub
parent 9acf2182cd
commit ca064eaa51
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 307 additions and 27 deletions

View file

@ -12,7 +12,7 @@ path = "lib.rs"
[features]
use-system-allocator = ["libc"]
[target.'cfg(not(any(windows, target_os = "android")))'.dependencies]
[target.'cfg(not(any(windows, target_os = "android", target_env = "ohos")))'.dependencies]
jemallocator = { workspace = true }
jemalloc-sys = { workspace = true }
libc = { workspace = true, optional = true }
@ -20,5 +20,5 @@ libc = { workspace = true, optional = true }
[target.'cfg(windows)'.dependencies]
winapi = { workspace = true, features = ["heapapi"] }
[target.'cfg(target_os = "android")'.dependencies]
[target.'cfg(any(target_os = "android", target_env = "ohos"))'.dependencies]
libc = { workspace = true }

View file

@ -9,7 +9,12 @@ static ALLOC: Allocator = Allocator;
pub use crate::platform::*;
#[cfg(not(any(windows, target_os = "android", feature = "use-system-allocator")))]
#[cfg(not(any(
windows,
target_os = "android",
feature = "use-system-allocator",
target_env = "ohos"
)))]
mod platform {
use std::os::raw::c_void;
@ -32,7 +37,11 @@ mod platform {
#[cfg(all(
not(windows),
any(target_os = "android", feature = "use-system-allocator")
any(
target_os = "android",
feature = "use-system-allocator",
target_env = "ohos"
)
))]
mod platform {
pub use std::alloc::System as Allocator;

View file

@ -22,7 +22,7 @@ euclid = { workspace = true }
fnv = { workspace = true }
fontsan = { git = "https://github.com/servo/fontsan" }
gfx_traits = { workspace = true }
harfbuzz-sys = "0.6"
harfbuzz-sys = "0.6.1"
ipc-channel = { workspace = true }
lazy_static = { workspace = true }
libc = { workspace = true }
@ -55,12 +55,16 @@ harfbuzz-sys = { version = "0.6", features = ["bundled"] }
freetype = "0.7"
servo_allocator = { path = "../allocator" }
[target.'cfg(target_os = "linux")'.dependencies]
[target.'cfg(all(target_os = "linux", not(target_env = "ohos")))'.dependencies]
fontconfig_sys = { package = "yeslogic-fontconfig-sys", version = "5" }
[target.'cfg(target_os = "android")'.dependencies]
xml-rs = "0.8"
[target.'cfg(target_env = "ohos")'.dependencies]
harfbuzz-sys = { version = "0.6.1", features = ["bundled"] }
[target.'cfg(target_os = "windows")'.dependencies]
harfbuzz-sys = { version = "0.6", features = ["bundled"] }
dwrote = "0.11"

View file

@ -0,0 +1,241 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use log::warn;
use serde::{Deserialize, Serialize};
use style::values::computed::{
FontStretch as StyleFontStretch, FontStyle as StyleFontStyle, FontWeight as StyleFontWeight,
};
use style::Atom;
use ucd::{Codepoint, UnicodeBlock};
use webrender_api::NativeFontHandle;
use crate::font_template::{FontTemplate, FontTemplateDescriptor};
use crate::text::util::is_cjk;
lazy_static::lazy_static! {
static ref FONT_LIST: FontList = FontList::new();
}
/// An identifier for a local font on OpenHarmony systems.
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct LocalFontIdentifier {
/// The path to the font.
pub path: Atom,
}
impl LocalFontIdentifier {
pub(crate) fn index(&self) -> u32 {
0
}
pub(crate) fn read_data_from_file(&self) -> Vec<u8> {
let mut bytes = Vec::new();
File::open(Path::new(&*self.path))
.expect("Couldn't open font file!")
.read_to_end(&mut bytes)
.unwrap();
bytes
}
}
struct Font {
filename: String,
weight: Option<i32>,
style: Option<String>,
}
struct FontFamily {
name: String,
fonts: Vec<Font>,
}
struct FontAlias {
from: String,
to: String,
weight: Option<i32>,
}
struct FontList {
families: Vec<FontFamily>,
aliases: Vec<FontAlias>,
}
impl FontList {
fn new() -> FontList {
// We don't support parsing `/system/etc/fontconfig.json` yet.
FontList {
families: Self::fallback_font_families(),
aliases: Vec::new(),
}
}
// Fonts expected to exist in OpenHarmony devices.
// Used until parsing of the fontconfig.json file is implemented.
fn fallback_font_families() -> Vec<FontFamily> {
let alternatives = [
("HarmonyOS Sans", "HarmonyOS_Sans_SC_Regular.ttf"),
("sans-serif", "HarmonyOS_Sans_SC_Regular.ttf"),
];
alternatives
.iter()
.filter(|item| Path::new(&Self::font_absolute_path(item.1)).exists())
.map(|item| FontFamily {
name: item.0.into(),
fonts: vec![Font {
filename: item.1.into(),
weight: None,
style: None,
}],
})
.collect()
}
// OHOS fonts are located in /system/fonts
fn font_absolute_path(filename: &str) -> String {
if filename.starts_with("/") {
String::from(filename)
} else {
format!("/system/fonts/{}", filename)
}
}
fn find_family(&self, name: &str) -> Option<&FontFamily> {
self.families.iter().find(|f| f.name == name)
}
fn find_alias(&self, name: &str) -> Option<&FontAlias> {
self.aliases.iter().find(|f| f.from == name)
}
}
// Functions used by FontCacheThread
pub fn for_each_available_family<F>(mut callback: F)
where
F: FnMut(String),
{
for family in &FONT_LIST.families {
callback(family.name.clone());
}
for alias in &FONT_LIST.aliases {
callback(alias.from.clone());
}
}
pub fn for_each_variation<F>(family_name: &str, mut callback: F)
where
F: FnMut(FontTemplate),
{
let mut produce_font = |font: &Font| {
let local_font_identifier = LocalFontIdentifier {
path: Atom::from(FontList::font_absolute_path(&font.filename)),
};
let stretch = StyleFontStretch::NORMAL;
let weight = font
.weight
.map(|w| StyleFontWeight::from_float(w as f32))
.unwrap_or(StyleFontWeight::NORMAL);
let style = match font.style.as_deref() {
Some("italic") => StyleFontStyle::ITALIC,
Some("normal") => StyleFontStyle::NORMAL,
Some(value) => {
warn!(
"unknown value \"{value}\" for \"style\" attribute in the font {}",
font.filename
);
StyleFontStyle::NORMAL
},
None => StyleFontStyle::NORMAL,
};
let descriptor = FontTemplateDescriptor {
weight,
stretch,
style,
};
callback(FontTemplate::new_local(local_font_identifier, descriptor));
};
if let Some(family) = FONT_LIST.find_family(family_name) {
for font in &family.fonts {
produce_font(font);
}
return;
}
if let Some(alias) = FONT_LIST.find_alias(family_name) {
if let Some(family) = FONT_LIST.find_family(&alias.to) {
for font in &family.fonts {
match (alias.weight, font.weight) {
(None, _) => produce_font(font),
(Some(w1), Some(w2)) if w1 == w2 => produce_font(font),
_ => {},
}
}
}
}
}
pub fn system_default_family(generic_name: &str) -> Option<String> {
if let Some(family) = FONT_LIST.find_family(&generic_name) {
Some(family.name.clone())
} else if let Some(alias) = FONT_LIST.find_alias(&generic_name) {
Some(alias.from.clone())
} else {
FONT_LIST.families.get(0).map(|family| family.name.clone())
}
}
// Based on fonts present in OpenHarmony.
pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
let mut families = vec![];
if let Some(block) = codepoint.and_then(|c| c.block()) {
match block {
UnicodeBlock::Hebrew => {
families.push("Noto Sans Hebrew");
},
UnicodeBlock::Arabic => {
families.push("HarmonyOS Sans Naskh Arabic");
},
UnicodeBlock::Devanagari => {
families.push("Noto Sans Devanagari");
},
UnicodeBlock::Tamil => {
families.push("Noto Sans Tamil");
},
UnicodeBlock::Thai => {
families.push("Noto Sans Thai");
},
UnicodeBlock::Georgian | UnicodeBlock::GeorgianSupplement => {
families.push("Noto Sans Georgian");
},
UnicodeBlock::Ethiopic | UnicodeBlock::EthiopicSupplement => {
families.push("Noto Sans Ethiopic");
},
_ => {
if is_cjk(codepoint.unwrap()) {
families.push("Noto Sans JP");
families.push("Noto Sans KR");
}
},
}
}
families.push("HarmonyOS Sans");
families
}
pub static SANS_SERIF_FONT_FAMILY: &'static str = "HarmonyOS Sans";

View file

@ -26,7 +26,7 @@ mod freetype {
pub mod font;
#[cfg(target_os = "linux")]
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
pub mod font_list;
#[cfg(target_os = "android")]
mod android {
@ -35,6 +35,12 @@ mod freetype {
}
#[cfg(target_os = "android")]
pub use self::android::font_list;
#[cfg(target_env = "ohos")]
mod ohos {
pub mod font_list;
}
#[cfg(target_env = "ohos")]
pub use self::ohos::font_list;
pub mod library_handle;
}

View file

@ -25,4 +25,5 @@ regex = { workspace = true }
[target.'cfg(not(any(target_os = "windows", target_os = "android")))'.dependencies]
libc = { workspace = true }
[target.'cfg(not(any(target_os = "windows", target_os = "android", target_env = "ohos")))'.dependencies]
jemalloc-sys = { workspace = true }

View file

@ -387,16 +387,16 @@ impl ReportsForest {
//---------------------------------------------------------------------------
mod system_reporter {
#[cfg(not(any(target_os = "windows", target_os = "android")))]
#[cfg(not(any(target_os = "windows", target_os = "android", target_env = "ohos")))]
use std::ffi::CString;
#[cfg(not(any(target_os = "windows", target_os = "android")))]
#[cfg(not(any(target_os = "windows", target_os = "android", target_env = "ohos")))]
use std::mem::size_of;
#[cfg(not(any(target_os = "windows", target_os = "android")))]
#[cfg(not(any(target_os = "windows", target_os = "android", target_env = "ohos")))]
use std::ptr::null_mut;
#[cfg(target_os = "linux")]
use libc::c_int;
#[cfg(not(any(target_os = "windows", target_os = "android")))]
#[cfg(not(any(target_os = "windows", target_os = "android", target_env = "ohos")))]
use libc::{c_void, size_t};
use profile_traits::mem::{Report, ReportKind, ReporterRequest};
use profile_traits::path;
@ -499,10 +499,10 @@ mod system_reporter {
None
}
#[cfg(not(any(target_os = "windows", target_os = "android")))]
#[cfg(not(any(target_os = "windows", target_os = "android", target_env = "ohos")))]
use jemalloc_sys::mallctl;
#[cfg(not(any(target_os = "windows", target_os = "android")))]
#[cfg(not(any(target_os = "windows", target_os = "android", target_env = "ohos")))]
fn jemalloc_stat(value_name: &str) -> Option<usize> {
// Before we request the measurement of interest, we first send an "epoch"
// request. Without that jemalloc gives cached statistics(!) which can be
@ -549,7 +549,7 @@ mod system_reporter {
Some(value as usize)
}
#[cfg(any(target_os = "windows", target_os = "android"))]
#[cfg(any(target_os = "windows", target_os = "android", target_env = "ohos"))]
fn jemalloc_stat(_value_name: &str) -> Option<usize> {
None
}

View file

@ -42,7 +42,8 @@ use compositing_traits::{
not(target_os = "ios"),
not(target_os = "android"),
not(target_arch = "arm"),
not(target_arch = "aarch64")
not(target_arch = "aarch64"),
not(target_env = "ohos"),
))]
use constellation::content_process_sandbox_profile;
use constellation::{
@ -58,7 +59,8 @@ use euclid::Scale;
not(target_os = "ios"),
not(target_os = "android"),
not(target_arch = "arm"),
not(target_arch = "aarch64")
not(target_arch = "aarch64"),
not(target_env = "ohos"),
))]
use gaol::sandbox::{ChildSandbox, ChildSandboxMethods};
use gfx::font_cache_thread::FontCacheThread;
@ -81,12 +83,12 @@ use script_traits::{ScriptToConstellationChan, WindowSizeData};
use servo_config::{opts, pref, prefs};
use servo_media::player::context::GlContext;
use servo_media::ServoMedia;
#[cfg(target_os = "linux")]
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
use surfman::platform::generic::multi::connection::NativeConnection as LinuxNativeConnection;
#[cfg(target_os = "linux")]
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
use surfman::platform::generic::multi::context::NativeContext as LinuxNativeContext;
use surfman::{GLApi, GLVersion};
#[cfg(target_os = "linux")]
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
use surfman::{NativeConnection, NativeContext};
use webrender::{RenderApiSender, ShaderPrecacheFlags};
use webrender_api::{
@ -253,6 +255,9 @@ where
Some(ref ua) if ua == "desktop" => {
default_user_agent_string_for(UserAgent::Desktop).into()
},
Some(ref ua) if ua == "ohos" => {
default_user_agent_string_for(UserAgent::OpenHarmony).into()
},
Some(ua) => ua.into(),
None => embedder
.get_user_agent_string()
@ -492,7 +497,7 @@ where
}
}
#[cfg(target_os = "linux")]
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
fn get_native_media_display_and_gl_context(
rendering_context: &RenderingContext,
) -> Option<(NativeDisplay, GlContext)> {
@ -529,7 +534,10 @@ where
Some((native_display, gl_context))
}
#[cfg(not(any(target_os = "windows", target_os = "linux")))]
#[cfg(not(any(
target_os = "windows",
all(target_os = "linux", not(target_env = "ohos"))
)))]
fn get_native_media_display_and_gl_context(
_rendering_context: &RenderingContext,
) -> Option<(NativeDisplay, GlContext)> {
@ -1196,7 +1204,8 @@ pub fn run_content_process(token: String) {
not(target_os = "ios"),
not(target_os = "android"),
not(target_arch = "arm"),
not(target_arch = "aarch64")
not(target_arch = "aarch64"),
not(target_env = "ohos"),
))]
fn create_sandbox() {
ChildSandbox::new(content_process_sandbox_profile())
@ -1209,7 +1218,8 @@ fn create_sandbox() {
target_os = "ios",
target_os = "android",
target_arch = "arm",
target_arch = "aarch64"
target_arch = "aarch64",
target_env = "ohos",
))]
fn create_sandbox() {
panic!("Sandboxing is not supported on Windows, iOS, ARM targets and android.");
@ -1218,15 +1228,20 @@ fn create_sandbox() {
enum UserAgent {
Desktop,
Android,
OpenHarmony,
#[allow(non_camel_case_types)]
iOS,
}
fn default_user_agent_string_for(agent: UserAgent) -> &'static str {
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[cfg(all(target_os = "linux", target_arch = "x86_64", not(target_env = "ohos")))]
const DESKTOP_UA_STRING: &'static str =
"Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Servo/1.0 Firefox/111.0";
#[cfg(all(target_os = "linux", not(target_arch = "x86_64")))]
#[cfg(all(
target_os = "linux",
not(target_arch = "x86_64"),
not(target_env = "ohos")
))]
const DESKTOP_UA_STRING: &'static str =
"Mozilla/5.0 (X11; Linux i686; rv:109.0) Servo/1.0 Firefox/111.0";
@ -1241,12 +1256,13 @@ fn default_user_agent_string_for(agent: UserAgent) -> &'static str {
const DESKTOP_UA_STRING: &'static str =
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Servo/1.0 Firefox/111.0";
#[cfg(target_os = "android")]
#[cfg(any(target_os = "android", target_env = "ohos"))]
const DESKTOP_UA_STRING: &'static str = "";
match agent {
UserAgent::Desktop => DESKTOP_UA_STRING,
UserAgent::Android => "Mozilla/5.0 (Android; Mobile; rv:109.0) Servo/1.0 Firefox/111.0",
UserAgent::OpenHarmony => "Mozilla/5.0 (OpenHarmony; Mobile; rv:109.0) Servo/1.0 Firefox/111.0",
UserAgent::iOS => {
"Mozilla/5.0 (iPhone; CPU iPhone OS 16_4 like Mac OS X; rv:109.0) Servo/1.0 Firefox/111.0"
},
@ -1256,8 +1272,11 @@ fn default_user_agent_string_for(agent: UserAgent) -> &'static str {
#[cfg(target_os = "android")]
const DEFAULT_USER_AGENT: UserAgent = UserAgent::Android;
#[cfg(target_env = "ohos")]
const DEFAULT_USER_AGENT: UserAgent = UserAgent::OpenHarmony;
#[cfg(target_os = "ios")]
const DEFAULT_USER_AGENT: UserAgent = UserAgent::iOS;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[cfg(not(any(target_os = "android", target_os = "ios", target_env = "ohos")))]
const DEFAULT_USER_AGENT: UserAgent = UserAgent::Desktop;