Remove the libsimpleservo C API (#31172)

This is unused and unmaintained. It also added a bit of complication
to the build.
This commit is contained in:
Martin Robinson 2024-01-27 10:19:25 +01:00 committed by GitHub
parent b10875956a
commit bbba839278
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 159 additions and 1758 deletions

121
Cargo.lock generated
View file

@ -267,17 +267,6 @@ version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41e67cd8309bbd06cd603a9e693a784ac2e5d1e955f11286e355089fcab3047c"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi 0.1.19",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@ -679,25 +668,6 @@ dependencies = [
"webxr-api",
]
[[package]]
name = "cbindgen"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da6bc11b07529f16944307272d5bd9b22530bc7d05751717c9d416586cedab49"
dependencies = [
"clap",
"heck",
"indexmap 1.9.3",
"log",
"proc-macro2",
"quote",
"serde",
"serde_json",
"syn 1.0.103",
"tempfile",
"toml 0.5.9",
]
[[package]]
name = "cc"
version = "1.0.83"
@ -779,30 +749,6 @@ dependencies = [
"libloading 0.8.0",
]
[[package]]
name = "clap"
version = "3.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
dependencies = [
"atty",
"bitflags 1.3.2",
"clap_lex",
"indexmap 1.9.3",
"strsim",
"termcolor",
"textwrap",
]
[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "clipboard-win"
version = "4.5.0"
@ -2664,15 +2610,6 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "hermit-abi"
version = "0.3.2"
@ -2948,7 +2885,7 @@ version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455"
dependencies = [
"hermit-abi 0.3.2",
"hermit-abi",
"rustix",
"windows-sys 0.52.0",
]
@ -4041,7 +3978,7 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi 0.3.2",
"hermit-abi",
"libc",
]
@ -4179,12 +4116,6 @@ version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063"
[[package]]
name = "os_str_bytes"
version = "6.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1"
[[package]]
name = "owned_ttf_parser"
version = "0.20.0"
@ -5562,13 +5493,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "simpleservo"
name = "simpleservo_jniapi"
version = "0.0.1"
dependencies = [
"core-foundation",
"android_logger",
"cc",
"getopts",
"gl_generator",
"ipc-channel",
"jni",
"libc",
"libloading 0.8.0",
"libservo",
@ -5578,36 +5511,6 @@ dependencies = [
"surfman",
"vergen",
"webxr",
"winapi",
]
[[package]]
name = "simpleservo_capi"
version = "0.0.1"
dependencies = [
"backtrace",
"cbindgen",
"env_logger 0.10.2",
"keyboard-types",
"lazy_static",
"libc",
"log",
"simpleservo",
"surfman",
"winapi",
]
[[package]]
name = "simpleservo_jniapi"
version = "0.0.1"
dependencies = [
"android_logger",
"cc",
"jni",
"libc",
"log",
"serde_json",
"simpleservo",
]
[[package]]
@ -5779,12 +5682,6 @@ dependencies = [
"quote",
]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "style"
version = "0.0.1"
@ -6083,12 +5980,6 @@ dependencies = [
"term",
]
[[package]]
name = "textwrap"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]]
name = "thin-vec"
version = "0.2.13"

View file

@ -2,8 +2,7 @@
resolver = "2"
members = [
"ports/servoshell",
"ports/libsimpleservo/capi/",
"ports/libsimpleservo/jniapi/",
"ports/jniapi/",
"tests/unit/*",
"support/crown",
]

View file

@ -1,40 +1,43 @@
[package]
name = "simpleservo"
name = "simpleservo_jniapi"
version = "0.0.1"
authors = ["The Servo Project Developers"]
license = "MPL-2.0"
edition = "2018"
publish = false
build = "build.rs"
[lib]
name = "simpleservo"
crate-type = ["cdylib"]
test = false
bench = false
[dependencies]
android_logger = "0.13"
getopts = { workspace = true }
ipc-channel = { workspace = true }
libservo = { path = "../../../components/servo" }
jni = "0.18.0"
libc = { workspace = true }
libloading = "0.8"
libservo = { path = "../../components/servo" }
log = { workspace = true }
serde_json = { workspace = true }
servo-media = { git = "https://github.com/servo/media" }
surfman = { workspace = true, features = ["sm-angle-default"] }
webxr = { git = "https://github.com/servo/webxr" }
[target.'cfg(not(target_os = "macos"))'.dependencies]
libc = { workspace = true }
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.9"
[target.'cfg(target_os = "windows")'.dependencies]
winapi = { workspace = true }
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies]
libloading = "0.8"
[build-dependencies]
cc = "1.0"
gl_generator = "0.14"
serde_json = { workspace = true }
vergen = { version = "8.0.0", features = ["git", "gitcl"] }
[features]
debugmozjs = ["libservo/debugmozjs"]
default = ["max_log_level", "native-bluetooth", "webdriver"]
# TODO: Once the native-bluetooth feature works for
# Android, re-add it here.
default = ["max_log_level", "webdriver"]
googlevr = ["libservo/googlevr"]
jitspew = ["libservo/jitspew"]
js_backtrace = ["libservo/js_backtrace"]

43
ports/jniapi/build.rs Normal file
View file

@ -0,0 +1,43 @@
/* 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::env;
use std::fs::File;
use std::path::PathBuf;
use gl_generator::{Api, Fallbacks, Profile, Registry};
use serde_json::{self, Value};
use vergen::EmitBuilder;
fn main() {
let target = env::var("TARGET").unwrap();
let dest = PathBuf::from(&env::var("OUT_DIR").unwrap());
if let Err(error) = EmitBuilder::builder()
.fail_on_error()
.git_sha(true /* short */)
.emit()
{
println!(
"cargo:warning=Could not generate git version information: {:?}",
error
);
println!("cargo:rustc-env=VERGEN_GIT_SHA=nogit");
}
// Generate GL bindings. For now, we only support EGL.
if target.contains("android") {
let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap();
Registry::new(Api::Egl, (1, 5), Profile::Core, Fallbacks::All, [])
.write_bindings(gl_generator::StaticStructGenerator, &mut file)
.unwrap();
println!("cargo:rustc-link-lib=EGL");
}
let mut default_prefs = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
default_prefs.push("../../resources/prefs.json");
let prefs: Value = serde_json::from_reader(File::open(&default_prefs).unwrap()).unwrap();
let file = File::create(&dest.join("prefs.json")).unwrap();
serde_json::to_writer(file, &prefs).unwrap();
}

View file

@ -0,0 +1,58 @@
/* 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/. */
#![allow(bare_trait_objects)] // Until https://github.com/brendanzab/gl-rs/pull/493
//
pub type ServoGl = std::rc::Rc<dyn servo::gl::Gl>;
#[cfg(any(target_os = "android", target_os = "windows"))]
#[allow(non_camel_case_types)]
pub mod egl {
use std::ffi::CString;
use std::os::raw::c_void;
use log::info;
use servo::gl::GlesFns;
pub type EGLNativeWindowType = *const libc::c_void;
pub type khronos_utime_nanoseconds_t = khronos_uint64_t;
pub type khronos_uint64_t = u64;
pub type khronos_ssize_t = libc::c_long;
pub type EGLint = i32;
pub type EGLContext = *const libc::c_void;
pub type EGLNativeDisplayType = *const libc::c_void;
pub type EGLNativePixmapType = *const libc::c_void;
pub type NativeDisplayType = EGLNativeDisplayType;
pub type NativePixmapType = EGLNativePixmapType;
pub type NativeWindowType = EGLNativeWindowType;
include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs"));
pub struct EGLInitResult {
pub gl_wrapper: crate::gl_glue::ServoGl,
pub gl_context: EGLContext,
pub display: EGLNativeDisplayType,
}
pub fn init() -> Result<EGLInitResult, &'static str> {
info!("Loading EGL...");
unsafe {
let egl = Egl;
let display = egl.GetCurrentDisplay();
egl.SwapInterval(display, 1);
let egl = GlesFns::load_with(|addr| {
let addr = CString::new(addr.as_bytes()).unwrap();
let addr = addr.as_ptr();
let egl = Egl;
egl.GetProcAddress(addr) as *const c_void
});
info!("EGL loaded");
Ok(EGLInitResult {
gl_wrapper: egl,
gl_context: Egl.GetCurrentContext(),
display,
})
}
}
}

View file

@ -4,6 +4,9 @@
#![allow(non_snake_case)]
mod gl_glue;
mod simpleservo;
use std::os::raw::{c_char, c_int, c_void};
use std::sync::Arc;
use std::thread;
@ -15,8 +18,8 @@ use jni::{JNIEnv, JavaVM};
use libc::{dup2, pipe, read};
use log::{debug, error, info, warn};
use simpleservo::{
self, gl_glue, Coordinates, DeviceIntRect, EventLoopWaker, HostTrait, InitOptions,
InputMethodType, MediaSessionPlaybackState, PromptResult, ServoGlue, SERVO,
Coordinates, DeviceIntRect, EventLoopWaker, HostTrait, InitOptions, InputMethodType,
MediaSessionPlaybackState, PromptResult, ServoGlue, SERVO,
};
struct HostCallbacks {

View file

@ -2,8 +2,6 @@
* 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/. */
pub mod gl_glue;
use std::cell::RefCell;
use std::collections::HashMap;
use std::mem;
@ -941,28 +939,28 @@ impl ResourceReaderMethods for ResourceReaderInstance {
Vec::from(match res {
Resource::Preferences => &include_bytes!(concat!(env!("OUT_DIR"), "/prefs.json"))[..],
Resource::HstsPreloadList => {
&include_bytes!("../../../../resources/hsts_preload.json")[..]
&include_bytes!("../../../resources/hsts_preload.json")[..]
},
Resource::BadCertHTML => &include_bytes!("../../../../resources/badcert.html")[..],
Resource::NetErrorHTML => &include_bytes!("../../../../resources/neterror.html")[..],
Resource::UserAgentCSS => &include_bytes!("../../../../resources/user-agent.css")[..],
Resource::ServoCSS => &include_bytes!("../../../../resources/servo.css")[..],
Resource::BadCertHTML => &include_bytes!("../../../resources/badcert.html")[..],
Resource::NetErrorHTML => &include_bytes!("../../../resources/neterror.html")[..],
Resource::UserAgentCSS => &include_bytes!("../../../resources/user-agent.css")[..],
Resource::ServoCSS => &include_bytes!("../../../resources/servo.css")[..],
Resource::PresentationalHintsCSS => {
&include_bytes!("../../../../resources/presentational-hints.css")[..]
&include_bytes!("../../../resources/presentational-hints.css")[..]
},
Resource::QuirksModeCSS => &include_bytes!("../../../../resources/quirks-mode.css")[..],
Resource::RippyPNG => &include_bytes!("../../../../resources/rippy.png")[..],
Resource::DomainList => &include_bytes!("../../../../resources/public_domains.txt")[..],
Resource::QuirksModeCSS => &include_bytes!("../../../resources/quirks-mode.css")[..],
Resource::RippyPNG => &include_bytes!("../../../resources/rippy.png")[..],
Resource::DomainList => &include_bytes!("../../../resources/public_domains.txt")[..],
Resource::BluetoothBlocklist => {
&include_bytes!("../../../../resources/gatt_blocklist.txt")[..]
&include_bytes!("../../../resources/gatt_blocklist.txt")[..]
},
Resource::MediaControlsCSS => {
&include_bytes!("../../../../resources/media-controls.css")[..]
&include_bytes!("../../../resources/media-controls.css")[..]
},
Resource::MediaControlsJS => {
&include_bytes!("../../../../resources/media-controls.js")[..]
&include_bytes!("../../../resources/media-controls.js")[..]
},
Resource::CrashHTML => &include_bytes!("../../../../resources/crash.html")[..],
Resource::CrashHTML => &include_bytes!("../../../resources/crash.html")[..],
})
}

View file

@ -1,11 +0,0 @@
# libsimpleservo
This is a basic wrapper around Servo. While libservo itself (/components/servo/) offers a lot of flexibility,
libsimpleservo (/ports/libsimpleservo/) tries to make it easier to embed Servo, without much configuration needed.
It is limited to only one view (no tabs, no multiple rendering area).
## Building
Run the following command to generate `libsimpleservo`
```
./mach build --release --libsimpleservo
```
this will generate a shared library (`libsimpleservo.so` on linux) as well as a header file in `target/release` that you can then link to your application.

View file

@ -1,70 +0,0 @@
/* 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::env;
use std::fs::File;
use std::path::PathBuf;
use gl_generator::{Api, Fallbacks, Profile, Registry};
use serde_json::{self, Value};
use vergen::EmitBuilder;
fn main() {
let target = env::var("TARGET").unwrap();
let dest = PathBuf::from(&env::var("OUT_DIR").unwrap());
if let Err(error) = EmitBuilder::builder()
.fail_on_error()
.git_sha(true /* short */)
.emit()
{
println!(
"cargo:warning=Could not generate git version information: {:?}",
error
);
println!("cargo:rustc-env=VERGEN_GIT_SHA=nogit");
}
// On MacOS, all dylib dependencies are shipped along with the binary
// in the "/lib" directory. Setting the rpath here, allows the dynamic
// linker to locate them. See `man dyld` for more info.
#[cfg(target_os = "macos")]
println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path/lib/");
// Generate GL bindings
// For now, we only support EGL, and only on Windows and Android.
if target.contains("android") || target.contains("windows") {
let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap();
Registry::new(Api::Egl, (1, 5), Profile::Core, Fallbacks::All, [])
.write_bindings(gl_generator::StaticStructGenerator, &mut file)
.unwrap();
// Historically, Android builds have succeeded with rustc-link-lib=EGL.
// On Windows when relying on %LIBS% to contain libEGL.lib, however,
// we must explicitly use rustc-link-lib=libEGL or rustc will attempt
// to link EGL.lib instead.
if target.contains("windows") {
println!("cargo:rustc-link-lib=libEGL");
} else {
println!("cargo:rustc-link-lib=EGL");
}
}
if target.contains("linux") ||
target.contains("dragonfly") ||
target.contains("freebsd") ||
target.contains("openbsd")
{
let mut file = File::create(&dest.join("glx_bindings.rs")).unwrap();
Registry::new(Api::Glx, (1, 4), Profile::Core, Fallbacks::All, [])
.write_bindings(gl_generator::StructGenerator, &mut file)
.unwrap();
}
let mut default_prefs = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
default_prefs.push("../../../resources/prefs.json");
let prefs: Value = serde_json::from_reader(File::open(&default_prefs).unwrap()).unwrap();
let file = File::create(&dest.join("prefs.json")).unwrap();
serde_json::to_writer(file, &prefs).unwrap();
}

View file

@ -1,141 +0,0 @@
/* 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/. */
#![allow(bare_trait_objects)] // Until https://github.com/brendanzab/gl-rs/pull/493
//
pub type ServoGl = std::rc::Rc<dyn servo::gl::Gl>;
#[cfg(any(target_os = "android", target_os = "windows"))]
#[allow(non_camel_case_types)]
pub mod egl {
use std::ffi::CString;
use std::os::raw::c_void;
use log::info;
use servo::gl::GlesFns;
pub type EGLNativeWindowType = *const libc::c_void;
pub type khronos_utime_nanoseconds_t = khronos_uint64_t;
pub type khronos_uint64_t = u64;
pub type khronos_ssize_t = libc::c_long;
pub type EGLint = i32;
pub type EGLContext = *const libc::c_void;
pub type EGLNativeDisplayType = *const libc::c_void;
pub type EGLNativePixmapType = *const libc::c_void;
pub type NativeDisplayType = EGLNativeDisplayType;
pub type NativePixmapType = EGLNativePixmapType;
pub type NativeWindowType = EGLNativeWindowType;
include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs"));
pub struct EGLInitResult {
pub gl_wrapper: crate::gl_glue::ServoGl,
pub gl_context: EGLContext,
pub display: EGLNativeDisplayType,
}
pub fn init() -> Result<EGLInitResult, &'static str> {
info!("Loading EGL...");
unsafe {
let egl = Egl;
let display = egl.GetCurrentDisplay();
egl.SwapInterval(display, 1);
let egl = GlesFns::load_with(|addr| {
let addr = CString::new(addr.as_bytes()).unwrap();
let addr = addr.as_ptr();
let egl = Egl;
egl.GetProcAddress(addr) as *const c_void
});
info!("EGL loaded");
Ok(EGLInitResult {
gl_wrapper: egl,
gl_context: Egl.GetCurrentContext(),
display,
})
}
}
}
#[cfg(target_os = "windows")]
pub mod gl {
pub fn init() -> Result<crate::gl_glue::ServoGl, &'static str> {
unimplemented!();
}
}
#[cfg(target_os = "macos")]
pub mod gl {
use std::os::raw::c_void;
use std::str;
use core_foundation::base::TCFType;
use core_foundation::bundle::{
CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName,
};
use core_foundation::string::CFString;
use log::info;
use servo::gl::GlFns;
pub fn init() -> Result<crate::gl_glue::ServoGl, &'static str> {
info!("Loading OpenGL...");
let gl = unsafe {
GlFns::load_with(|addr| {
let symbol_name: CFString = str::FromStr::from_str(addr).unwrap();
let framework_name: CFString = str::FromStr::from_str("com.apple.opengl").unwrap();
let framework =
CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef());
let symbol =
CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef());
symbol as *const c_void
})
};
info!("OpenGL loaded");
Ok(gl)
}
}
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "openbsd"
))]
pub mod gl {
use std::ffi::CString;
use std::os::raw::c_void;
use libloading::{Library, Symbol};
use log::info;
use servo::gl::GlFns;
pub fn init() -> Result<crate::gl_glue::ServoGl, &'static str> {
info!("Loading OpenGL");
pub mod glx {
include!(concat!(env!("OUT_DIR"), "/glx_bindings.rs"));
}
let lib = match unsafe { Library::new("libGL.so.1").or_else(|_| Library::new("libGL.so")) }
{
Ok(lib) => lib,
Err(_) => return Err("Can't find libGL.so, OpenGL isn't configured/installed"),
};
let glx = glx::Glx::load_with(|sym| unsafe {
let symbol: Symbol<*const c_void> = lib.get(sym.as_bytes()).unwrap();
*symbol.into_raw()
});
let gl = unsafe {
GlFns::load_with(|addr| {
let addr = CString::new(addr.as_bytes()).unwrap();
glx.GetProcAddress(addr.as_ptr() as *const _) as *const _
})
};
info!("OpenGL is loaded");
Ok(gl)
}
}

View file

@ -1,45 +0,0 @@
[package]
name = "simpleservo_capi"
version = "0.0.1"
authors = ["The Servo Project Developers"]
license = "MPL-2.0"
edition = "2018"
publish = false
[lib]
name = "simpleservo"
crate-type = ["cdylib"]
test = false
bench = false
[dependencies]
backtrace = { workspace = true }
env_logger = { workspace = true }
lazy_static = { workspace = true }
log = { workspace = true }
simpleservo = { path = "../api" }
surfman = { workspace = true }
keyboard-types = { workspace = true }
[target.'cfg(target_os = "windows")'.dependencies]
libc = { workspace = true }
winapi = { workspace = true, features = ["wingdi", "winuser", "winnt", "winbase", "processenv", "namedpipeapi", "ntdef", "minwindef", "handleapi", "debugapi"] }
[build-dependencies]
cbindgen = "0.26"
[features]
debugmozjs = ["simpleservo/debugmozjs"]
default = ["webdriver", "max_log_level"]
googlevr = ["simpleservo/googlevr"]
jitspew = ["simpleservo/jitspew"]
js_backtrace = ["simpleservo/js_backtrace"]
max_log_level = ["simpleservo/max_log_level"]
media-gstreamer = ["simpleservo/media-gstreamer"]
native-bluetooth = ["simpleservo/native-bluetooth"]
no-wgl = ["simpleservo/no-wgl"]
profilemozjs = ["simpleservo/profilemozjs"]
refcell_backtrace = ["simpleservo/refcell_backtrace"]
webdriver = ["simpleservo/webdriver"]
webgl_backtrace = ["simpleservo/webgl_backtrace"]
xr-profile = ["simpleservo/xr-profile"]

View file

@ -1,23 +0,0 @@
/* 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::env;
use std::path::PathBuf;
fn main() {
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let target_dir = env::var("CARGO_TARGET_DIR").unwrap();
let mut path: PathBuf = [crate_dir.clone(), target_dir].iter().collect();
let target = env::var("TARGET").unwrap();
let host = env::var("HOST").unwrap();
if target != host {
path.push(target);
}
let profile_dir = env::var("PROFILE").unwrap();
path.push(profile_dir);
path.push("simpleservo.h");
cbindgen::generate(crate_dir)
.expect("Unable to generate C bindings")
.write_to_file(path);
}

View file

@ -1,8 +0,0 @@
language = "C"
include_guard = "simpleservo_h"
cpp_compat = true
tab_width = 4
documentation_style = "c99"
[export]
exclude = ["OutputDebugStringA"]

View file

@ -1,953 +0,0 @@
/* 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/. */
mod prefs;
#[cfg(target_os = "windows")]
mod vslogger;
use std::ffi::{CStr, CString};
#[cfg(target_os = "windows")]
use std::mem;
use std::os::raw::{c_char, c_uint, c_void};
use std::slice;
use std::str::FromStr;
use std::sync::{Mutex, RwLock};
#[cfg(not(target_os = "windows"))]
use env_logger;
use keyboard_types::Key;
use lazy_static::lazy_static;
use log::{debug, error, info, warn, LevelFilter};
use simpleservo::{
self, gl_glue, ContextMenuResult, Coordinates, DeviceIntRect, EventLoopWaker, HostTrait,
InitOptions, InputMethodType, MediaSessionActionType, MediaSessionPlaybackState, MouseButton,
PromptResult, ServoGlue, SurfmanIntegration, SERVO,
};
extern "C" fn default_panic_handler(msg: *const c_char) {
let c_str: &CStr = unsafe { CStr::from_ptr(msg) };
error!("{}", c_str.to_str().unwrap());
}
type LogHandlerFn = extern "C" fn(buffer: *const c_char, len: u32);
lazy_static! {
static ref ON_PANIC: RwLock<extern "C" fn(*const c_char)> = RwLock::new(default_panic_handler);
static ref SERVO_VERSION: CString =
CString::new(simpleservo::servo_version()).expect("Can't create string");
pub(crate) static ref OUTPUT_LOG_HANDLER: Mutex<Option<LogHandlerFn>> = Mutex::new(None);
}
#[no_mangle]
pub extern "C" fn register_panic_handler(on_panic: extern "C" fn(*const c_char)) {
*ON_PANIC.write().unwrap() = on_panic;
}
/// Report panic to embedder.
fn report_panic(reason: &str, backtrace: Option<String>) {
let message = match backtrace {
Some(bt) => format!("Servo panic ({})\n{}", reason, bt),
None => format!("Servo panic ({})", reason),
};
let error = CString::new(message).expect("Can't create string");
(ON_PANIC.read().unwrap())(error.as_ptr());
// At this point, embedder should probably have thrown, so we never reach
// this point. But if it didn't, don't recursively panic.
}
#[cfg(not(target_os = "windows"))]
fn redirect_stdout_stderr(_handler: LogHandlerFn) -> Result<(), String> {
Ok(())
}
#[cfg(target_os = "windows")]
fn redirect_stdout_stderr(handler: LogHandlerFn) -> Result<(), String> {
do_redirect_stdout_stderr(handler).map_err(|()| {
format!("GetLastError() = {}", unsafe {
winapi::um::errhandlingapi::GetLastError()
})
})
}
#[cfg(target_os = "windows")]
// Function to redirect STDOUT (1) and STDERR(2) to Windows API
// OutputDebugString().
// Return Value: Result<(), String>
// Ok() - stdout and stderr redirects.
// Err(str) - The Err value can contain the string value of GetLastError.
fn do_redirect_stdout_stderr(handler: LogHandlerFn) -> Result<(), ()> {
use std::thread;
use winapi::shared;
use winapi::um::{handleapi, minwinbase, namedpipeapi, processenv, winbase, winnt};
let mut h_read_pipe: winnt::HANDLE = handleapi::INVALID_HANDLE_VALUE;
let mut h_write_pipe: winnt::HANDLE = handleapi::INVALID_HANDLE_VALUE;
let mut secattr: minwinbase::SECURITY_ATTRIBUTES = unsafe { mem::zeroed() };
const BUF_LENGTH: usize = 1024;
secattr.nLength = mem::size_of::<minwinbase::SECURITY_ATTRIBUTES>() as u32;
secattr.bInheritHandle = shared::minwindef::TRUE;
secattr.lpSecurityDescriptor = shared::ntdef::NULL;
unsafe {
if namedpipeapi::CreatePipe(
&mut h_read_pipe,
&mut h_write_pipe,
&mut secattr,
BUF_LENGTH as u32,
) == 0
{
return Err(());
}
if processenv::SetStdHandle(winbase::STD_OUTPUT_HANDLE, h_write_pipe) == 0 ||
processenv::SetStdHandle(winbase::STD_ERROR_HANDLE, h_write_pipe) == 0
{
return Err(());
}
if handleapi::SetHandleInformation(
h_read_pipe,
winbase::HANDLE_FLAG_INHERIT,
winbase::HANDLE_FLAG_INHERIT,
) == 0 ||
handleapi::SetHandleInformation(
h_write_pipe,
winbase::HANDLE_FLAG_INHERIT,
winbase::HANDLE_FLAG_INHERIT,
) == 0
{
return Err(());
}
let h_read_pipe_fd = libc::open_osfhandle(h_read_pipe as libc::intptr_t, libc::O_RDONLY);
let h_write_pipe_fd = libc::open_osfhandle(h_write_pipe as libc::intptr_t, libc::O_WRONLY);
if h_read_pipe_fd == -1 || h_write_pipe_fd == -1 {
return Err(());
}
// 0 indicates success.
if libc::dup2(h_write_pipe_fd, 1) != 0 || libc::dup2(h_write_pipe_fd, 2) != 0 {
return Err(());
}
// If SetStdHandle(winbase::STD_OUTPUT_HANDLE, hWritePipe) is not called prior,
// this will fail. GetStdHandle() is used to make certain "servo" has the stdout
// file descriptor associated.
let h_stdout = processenv::GetStdHandle(winbase::STD_OUTPUT_HANDLE);
if h_stdout == handleapi::INVALID_HANDLE_VALUE || h_stdout == shared::ntdef::NULL {
return Err(());
}
// If SetStdHandle(winbase::STD_ERROR_HANDLE, hWritePipe) is not called prior,
// this will fail. GetStdHandle() is used to make certain "servo" has the stderr
// file descriptor associated.
let h_stderr = processenv::GetStdHandle(winbase::STD_ERROR_HANDLE);
if h_stderr == handleapi::INVALID_HANDLE_VALUE || h_stderr == shared::ntdef::NULL {
return Err(());
}
// Spawn a thread. The thread will redirect all STDOUT and STDERR messages
// to the provided handler function.
let _handler = thread::spawn(move || loop {
let mut read_buf: [i8; BUF_LENGTH] = [0; BUF_LENGTH];
let result = libc::read(
h_read_pipe_fd,
read_buf.as_mut_ptr() as *mut _,
read_buf.len() as u32 - 1,
);
if result == -1 {
break;
}
handler(read_buf.as_ptr(), result as u32);
});
}
Ok(())
}
fn call<T, F>(f: F) -> T
where
F: FnOnce(&mut ServoGlue) -> Result<T, &'static str>,
{
match SERVO.with(|s| match s.borrow_mut().as_mut() {
Some(ref mut s) => (f)(s),
None => Err("Servo not available in this thread"),
}) {
Err(e) => panic!("{}", e),
Ok(r) => r,
}
}
/// Callback used by Servo internals
#[repr(C)]
pub struct CHostCallbacks {
pub on_load_started: extern "C" fn(),
pub on_load_ended: extern "C" fn(),
pub on_title_changed: extern "C" fn(title: *const c_char),
pub on_allow_navigation: extern "C" fn(url: *const c_char) -> bool,
pub on_url_changed: extern "C" fn(url: *const c_char),
pub on_history_changed: extern "C" fn(can_go_back: bool, can_go_forward: bool),
pub on_animating_changed: extern "C" fn(animating: bool),
pub on_shutdown_complete: extern "C" fn(),
pub on_ime_show: extern "C" fn(
text: *const c_char,
text_index: i32,
multiline: bool,
x: i32,
y: i32,
width: i32,
height: i32,
),
pub on_ime_hide: extern "C" fn(),
pub get_clipboard_contents: extern "C" fn() -> *const c_char,
pub set_clipboard_contents: extern "C" fn(contents: *const c_char),
pub on_media_session_metadata:
extern "C" fn(title: *const c_char, album: *const c_char, artist: *const c_char),
pub on_media_session_playback_state_change: extern "C" fn(state: CMediaSessionPlaybackState),
pub on_media_session_set_position_state:
extern "C" fn(duration: f64, position: f64, playback_rate: f64),
pub prompt_alert: extern "C" fn(message: *const c_char, trusted: bool),
pub prompt_ok_cancel: extern "C" fn(message: *const c_char, trusted: bool) -> CPromptResult,
pub prompt_yes_no: extern "C" fn(message: *const c_char, trusted: bool) -> CPromptResult,
pub prompt_input:
extern "C" fn(message: *const c_char, def: *const c_char, trusted: bool) -> *const c_char,
pub on_devtools_started:
extern "C" fn(result: CDevtoolsServerState, port: c_uint, token: *const c_char),
pub show_context_menu:
extern "C" fn(title: *const c_char, items_list: *const *const c_char, items_size: u32),
pub on_log_output: extern "C" fn(buffer: *const c_char, buffer_length: u32),
}
/// Servo options
#[repr(C)]
pub struct CInitOptions {
pub args: *const c_char,
pub width: i32,
pub height: i32,
pub density: f32,
pub vslogger_mod_list: *const *const c_char,
pub vslogger_mod_size: u32,
pub native_widget: *mut c_void,
pub prefs: *const prefs::CPrefList,
}
#[repr(C)]
pub enum CMouseButton {
Left,
Right,
Middle,
}
impl CMouseButton {
pub fn convert(&self) -> MouseButton {
match self {
CMouseButton::Left => MouseButton::Left,
CMouseButton::Right => MouseButton::Right,
CMouseButton::Middle => MouseButton::Middle,
}
}
}
#[repr(C)]
pub enum CPromptResult {
Dismissed,
Primary,
Secondary,
}
impl CPromptResult {
pub fn convert(&self) -> PromptResult {
match self {
CPromptResult::Primary => PromptResult::Primary,
CPromptResult::Secondary => PromptResult::Secondary,
CPromptResult::Dismissed => PromptResult::Dismissed,
}
}
}
#[repr(C)]
pub enum CMediaSessionPlaybackState {
None = 1,
Playing,
Paused,
}
#[repr(C)]
pub enum CDevtoolsServerState {
Started,
Error,
}
impl From<MediaSessionPlaybackState> for CMediaSessionPlaybackState {
fn from(state: MediaSessionPlaybackState) -> Self {
match state {
MediaSessionPlaybackState::None_ => CMediaSessionPlaybackState::None,
MediaSessionPlaybackState::Playing => CMediaSessionPlaybackState::Playing,
MediaSessionPlaybackState::Paused => CMediaSessionPlaybackState::Paused,
}
}
}
#[repr(C)]
pub enum CMediaSessionActionType {
Play = 1,
Pause,
SeekBackward,
SeekForward,
PreviousTrack,
NextTrack,
SkipAd,
Stop,
SeekTo,
}
impl CMediaSessionActionType {
pub fn convert(&self) -> MediaSessionActionType {
match self {
CMediaSessionActionType::Play => MediaSessionActionType::Play,
CMediaSessionActionType::Pause => MediaSessionActionType::Pause,
CMediaSessionActionType::SeekBackward => MediaSessionActionType::SeekBackward,
CMediaSessionActionType::SeekForward => MediaSessionActionType::SeekForward,
CMediaSessionActionType::PreviousTrack => MediaSessionActionType::PreviousTrack,
CMediaSessionActionType::NextTrack => MediaSessionActionType::NextTrack,
CMediaSessionActionType::SkipAd => MediaSessionActionType::SkipAd,
CMediaSessionActionType::Stop => MediaSessionActionType::Stop,
CMediaSessionActionType::SeekTo => MediaSessionActionType::SeekTo,
}
}
}
#[repr(C)]
pub enum CContextMenuResult {
Ignored,
Selected,
// Can't use Dismissed. Already used by PromptResult. See:
// https://github.com/servo/servo/issues/25986
Dismissed_,
}
impl CContextMenuResult {
pub fn convert(&self, idx: u32) -> ContextMenuResult {
match self {
CContextMenuResult::Ignored => ContextMenuResult::Ignored,
CContextMenuResult::Dismissed_ => ContextMenuResult::Dismissed,
CContextMenuResult::Selected => ContextMenuResult::Selected(idx as usize),
}
}
}
/// The returned string is not freed. This will leak.
#[no_mangle]
pub extern "C" fn servo_version() -> *const c_char {
SERVO_VERSION.as_ptr()
}
#[cfg(target_os = "windows")]
fn init_logger(modules: &[*const c_char], level: LevelFilter) {
use std::sync::Once;
use vslogger::VSLogger;
use crate::vslogger::LOG_MODULE_FILTERS;
static LOGGER: VSLogger = VSLogger;
static LOGGER_INIT: Once = Once::new();
if !modules.is_empty() {
*LOG_MODULE_FILTERS.lock().unwrap() = modules
.iter()
.map(|modules| unsafe { CStr::from_ptr(*modules).to_string_lossy().into_owned() })
.collect::<Vec<_>>();
}
LOGGER_INIT.call_once(|| {
log::set_logger(&LOGGER)
.map(|_| log::set_max_level(level))
.unwrap();
});
}
#[cfg(not(target_os = "windows"))]
fn init_logger(_modules: &[*const c_char], _level: LevelFilter) {
crate::env_logger::init();
}
unsafe fn init(
opts: CInitOptions,
gl: gl_glue::ServoGl,
gl_context: Option<*const c_void>,
display: Option<*const c_void>,
wakeup: extern "C" fn(),
callbacks: CHostCallbacks,
) {
// Catch any panics.
std::panic::set_hook(Box::new(|info| {
let msg = match info.payload().downcast_ref::<&'static str>() {
Some(s) => *s,
None => match info.payload().downcast_ref::<String>() {
Some(s) => &**s,
None => "Box<Any>",
},
};
let current_thread = std::thread::current();
let name = current_thread.name().unwrap_or("<unnamed>");
let details = if let Some(location) = info.location() {
format!(
"{} (thread {}, at {}:{})",
msg,
name,
location.file(),
location.line()
)
} else {
format!("{} (thread {})", msg, name)
};
report_panic("General panic handler", Some(details));
}));
let args = if !opts.args.is_null() {
let args = CStr::from_ptr(opts.args);
args.to_str()
.unwrap_or("")
.split(' ')
.map(|s| s.to_owned())
.collect()
} else {
vec![]
};
let logger_level = if let Some(level_index) = args.iter().position(|s| s == "--vslogger-level")
{
if args.len() >= level_index + 1 {
LevelFilter::from_str(&args[level_index + 1]).unwrap_or(LevelFilter::Warn)
} else {
LevelFilter::Warn
}
} else {
LevelFilter::Warn
};
let logger_modules = if opts.vslogger_mod_list.is_null() {
&[]
} else {
slice::from_raw_parts(opts.vslogger_mod_list, opts.vslogger_mod_size as usize)
};
*OUTPUT_LOG_HANDLER.lock().unwrap() = Some(callbacks.on_log_output);
init_logger(logger_modules, logger_level);
if let Err(reason) = redirect_stdout_stderr(callbacks.on_log_output) {
warn!("Error redirecting stdout/stderr: {}", reason);
}
let coordinates = Coordinates::new(0, 0, opts.width, opts.height, opts.width, opts.height);
let prefs = if opts.prefs.is_null() {
None
} else {
Some((*opts.prefs).convert())
};
let opts = InitOptions {
args,
coordinates,
prefs,
density: opts.density,
xr_discovery: None,
gl_context_pointer: gl_context,
native_display_pointer: display,
surfman_integration: SurfmanIntegration::Widget(opts.native_widget),
};
let wakeup = Box::new(WakeupCallback::new(wakeup));
let callbacks = Box::new(HostCallbacks::new(callbacks));
simpleservo::init(opts, gl, wakeup, callbacks).unwrap();
}
#[cfg(target_os = "windows")]
#[no_mangle]
pub extern "C" fn init_with_egl(
opts: CInitOptions,
wakeup: extern "C" fn(),
callbacks: CHostCallbacks,
) {
let gl = gl_glue::egl::init().unwrap();
unsafe {
init(
opts,
gl.gl_wrapper,
Some(gl.gl_context),
Some(gl.display),
wakeup,
callbacks,
)
}
}
#[cfg(any(
target_os = "linux",
all(target_os = "windows", not(feature = "no-wgl")),
target_os = "macos"
))]
#[no_mangle]
pub extern "C" fn init_with_gl(
opts: CInitOptions,
wakeup: extern "C" fn(),
callbacks: CHostCallbacks,
) {
let gl = gl_glue::gl::init().unwrap();
unsafe { init(opts, gl, None, None, wakeup, callbacks) }
}
#[no_mangle]
pub extern "C" fn deinit() {
debug!("deinit");
simpleservo::deinit();
}
#[no_mangle]
pub extern "C" fn request_shutdown() {
debug!("request_shutdown");
call(|s| s.request_shutdown());
}
#[no_mangle]
pub extern "C" fn set_batch_mode(batch: bool) {
debug!("set_batch_mode");
call(|s| s.set_batch_mode(batch));
}
#[no_mangle]
pub extern "C" fn on_context_menu_closed(result: CContextMenuResult, item: u32) {
debug!("on_context_menu_closed");
call(|s| s.on_context_menu_closed(result.convert(item)));
}
#[no_mangle]
pub extern "C" fn resize(width: i32, height: i32) {
debug!("resize {}/{}", width, height);
call(|s| {
let coordinates = Coordinates::new(0, 0, width, height, width, height);
s.resize(coordinates)
});
}
#[no_mangle]
pub extern "C" fn perform_updates() {
debug!("perform_updates");
// We might have allocated some memory to respond to a potential
// request, from the embedder, for a copy of Servo's preferences.
prefs::free_prefs();
call(|s| s.perform_updates());
}
#[no_mangle]
pub extern "C" fn is_uri_valid(url: *const c_char) -> bool {
debug!("is_uri_valid");
let url = unsafe { CStr::from_ptr(url) };
let url = url.to_str().expect("Can't read string");
simpleservo::is_uri_valid(url)
}
#[no_mangle]
pub extern "C" fn load_uri(url: *const c_char) -> bool {
debug!("load_url");
let url = unsafe { CStr::from_ptr(url) };
let url = url.to_str().expect("Can't read string");
call(|s| Ok(s.load_uri(url).is_ok()))
}
#[no_mangle]
pub extern "C" fn clear_cache() {
debug!("clear_cache");
call(|s| s.clear_cache())
}
#[no_mangle]
pub extern "C" fn reload() {
debug!("reload");
call(|s| s.reload());
}
#[no_mangle]
pub extern "C" fn stop() {
debug!("stop");
call(|s| s.stop());
}
#[no_mangle]
pub extern "C" fn refresh() {
debug!("refresh");
call(|s| s.refresh());
}
#[no_mangle]
pub extern "C" fn go_back() {
debug!("go_back");
call(|s| s.go_back());
}
#[no_mangle]
pub extern "C" fn go_forward() {
debug!("go_forward");
call(|s| s.go_forward());
}
#[no_mangle]
pub extern "C" fn scroll_start(dx: i32, dy: i32, x: i32, y: i32) {
debug!("scroll_start");
call(|s| s.scroll_start(dx as f32, dy as f32, x, y));
}
#[no_mangle]
pub extern "C" fn scroll_end(dx: i32, dy: i32, x: i32, y: i32) {
debug!("scroll_end");
call(|s| s.scroll_end(dx as f32, dy as f32, x, y));
}
#[no_mangle]
pub extern "C" fn scroll(dx: i32, dy: i32, x: i32, y: i32) {
debug!("scroll");
call(|s| s.scroll(dx as f32, dy as f32, x, y));
}
#[no_mangle]
pub extern "C" fn touch_down(x: f32, y: f32, pointer_id: i32) {
debug!("touch down");
call(|s| s.touch_down(x, y, pointer_id));
}
#[no_mangle]
pub extern "C" fn touch_up(x: f32, y: f32, pointer_id: i32) {
debug!("touch up");
call(|s| s.touch_up(x, y, pointer_id));
}
#[no_mangle]
pub extern "C" fn touch_move(x: f32, y: f32, pointer_id: i32) {
debug!("touch move");
call(|s| s.touch_move(x, y, pointer_id));
}
#[no_mangle]
pub extern "C" fn touch_cancel(x: f32, y: f32, pointer_id: i32) {
debug!("touch cancel");
call(|s| s.touch_cancel(x, y, pointer_id));
}
#[no_mangle]
pub extern "C" fn pinchzoom_start(factor: f32, x: i32, y: i32) {
debug!("pinchzoom_start");
call(|s| s.pinchzoom_start(factor, x as u32, y as u32));
}
#[no_mangle]
pub extern "C" fn pinchzoom(factor: f32, x: i32, y: i32) {
debug!("pinchzoom");
call(|s| s.pinchzoom(factor, x as u32, y as u32));
}
#[no_mangle]
pub extern "C" fn pinchzoom_end(factor: f32, x: i32, y: i32) {
debug!("pinchzoom_end");
call(|s| s.pinchzoom_end(factor, x as u32, y as u32));
}
#[no_mangle]
pub extern "C" fn mouse_move(x: f32, y: f32) {
debug!("mouse_move");
call(|s| s.mouse_move(x, y));
}
#[no_mangle]
pub extern "C" fn mouse_down(x: f32, y: f32, button: CMouseButton) {
debug!("mouse_down");
call(|s| s.mouse_down(x, y, button.convert()));
}
#[no_mangle]
pub extern "C" fn mouse_up(x: f32, y: f32, button: CMouseButton) {
debug!("mouse_up");
call(|s| s.mouse_up(x, y, button.convert()));
}
#[no_mangle]
pub extern "C" fn click(x: f32, y: f32) {
debug!("click");
call(|s| s.click(x, y));
}
#[no_mangle]
pub extern "C" fn key_down(name: *const c_char) {
debug!("key_up");
key_event(name, false);
}
#[no_mangle]
pub extern "C" fn key_up(name: *const c_char) {
debug!("key_up");
key_event(name, true);
}
fn key_event(name: *const c_char, up: bool) {
let name = unsafe { CStr::from_ptr(name) };
let name = match name.to_str() {
Ok(name) => name,
Err(..) => {
warn!("Couldn't not read str");
return;
},
};
let key = Key::from_str(&name);
if let Ok(key) = key {
call(|s| if up { s.key_up(key) } else { s.key_down(key) });
} else {
warn!("Received unknown keys");
}
}
#[no_mangle]
pub extern "C" fn media_session_action(action: CMediaSessionActionType) {
debug!("media_session_action");
call(|s| s.media_session_action(action.convert()));
}
#[no_mangle]
pub extern "C" fn change_visibility(visible: bool) {
debug!("change_visibility");
call(|s| s.change_visibility(visible));
}
#[no_mangle]
pub extern "C" fn ime_dismissed() {
debug!("ime_dismissed");
call(|s| s.ime_dismissed());
}
pub struct WakeupCallback(extern "C" fn());
impl WakeupCallback {
fn new(callback: extern "C" fn()) -> WakeupCallback {
WakeupCallback(callback)
}
}
impl EventLoopWaker for WakeupCallback {
fn clone_box(&self) -> Box<dyn EventLoopWaker> {
Box::new(WakeupCallback(self.0))
}
fn wake(&self) {
(self.0)();
}
}
struct HostCallbacks(CHostCallbacks);
impl HostCallbacks {
fn new(callback: CHostCallbacks) -> HostCallbacks {
HostCallbacks(callback)
}
}
impl HostTrait for HostCallbacks {
fn on_load_started(&self) {
debug!("on_load_started");
(self.0.on_load_started)();
}
fn on_load_ended(&self) {
debug!("on_load_ended");
(self.0.on_load_ended)();
}
fn on_title_changed(&self, title: Option<String>) {
debug!("on_title_changed");
match title {
None => (self.0.on_title_changed)(std::ptr::null()),
Some(title) => {
let title = CString::new(title).expect("Can't create string");
(self.0.on_title_changed)(title.as_ptr());
},
};
}
fn on_allow_navigation(&self, url: String) -> bool {
debug!("on_allow_navigation");
let url = CString::new(url).expect("Can't create string");
(self.0.on_allow_navigation)(url.as_ptr())
}
fn on_url_changed(&self, url: String) {
debug!("on_url_changed");
let url = CString::new(url).expect("Can't create string");
(self.0.on_url_changed)(url.as_ptr());
}
fn on_history_changed(&self, can_go_back: bool, can_go_forward: bool) {
debug!("on_history_changed");
(self.0.on_history_changed)(can_go_back, can_go_forward);
}
fn on_animating_changed(&self, animating: bool) {
debug!("on_animating_changed");
(self.0.on_animating_changed)(animating);
}
fn on_shutdown_complete(&self) {
debug!("on_shutdown_complete");
(self.0.on_shutdown_complete)();
}
fn on_ime_show(
&self,
_input_type: InputMethodType,
text: Option<(String, i32)>,
multiline: bool,
bounds: DeviceIntRect,
) {
debug!("on_ime_show");
let text_index = text.as_ref().map_or(0, |(_, i)| *i);
let text = text.and_then(|(s, _)| CString::new(s).ok());
let text_ptr = text
.as_ref()
.map(|cstr| cstr.as_ptr())
.unwrap_or(std::ptr::null());
(self.0.on_ime_show)(
text_ptr,
text_index,
multiline,
bounds.origin.x,
bounds.origin.y,
bounds.size.width,
bounds.size.height,
);
}
fn on_ime_hide(&self) {
debug!("on_ime_hide");
(self.0.on_ime_hide)();
}
fn get_clipboard_contents(&self) -> Option<String> {
debug!("get_clipboard_contents");
let raw_contents = (self.0.get_clipboard_contents)();
if raw_contents.is_null() {
return None;
}
let c_str = unsafe { CStr::from_ptr(raw_contents) };
let contents_str = c_str.to_str().expect("Can't create str");
Some(contents_str.to_owned())
}
fn set_clipboard_contents(&self, contents: String) {
debug!("set_clipboard_contents");
let contents = CString::new(contents).expect("Can't create string");
(self.0.set_clipboard_contents)(contents.as_ptr());
}
fn on_media_session_metadata(&self, title: String, artist: String, album: String) {
debug!(
"on_media_session_metadata ({:?} {:?} {:?})",
title, artist, album
);
let title = CString::new(title).expect("Can't create string");
let artist = CString::new(artist).expect("Can't create string");
let album = CString::new(album).expect("Can't create string");
(self.0.on_media_session_metadata)(title.as_ptr(), artist.as_ptr(), album.as_ptr());
}
fn on_media_session_playback_state_change(&self, state: MediaSessionPlaybackState) {
debug!("on_media_session_playback_state_change {:?}", state);
(self.0.on_media_session_playback_state_change)(state.into());
}
fn on_media_session_set_position_state(
&self,
duration: f64,
position: f64,
playback_rate: f64,
) {
debug!(
"on_media_session_set_position_state ({:?} {:?} {:?})",
duration, position, playback_rate
);
(self.0.on_media_session_set_position_state)(duration, position, playback_rate);
}
fn prompt_alert(&self, message: String, trusted: bool) {
debug!("prompt_alert");
let message = CString::new(message).expect("Can't create string");
(self.0.prompt_alert)(message.as_ptr(), trusted);
}
fn prompt_ok_cancel(&self, message: String, trusted: bool) -> PromptResult {
debug!("prompt_ok_cancel");
let message = CString::new(message).expect("Can't create string");
(self.0.prompt_ok_cancel)(message.as_ptr(), trusted).convert()
}
fn prompt_yes_no(&self, message: String, trusted: bool) -> PromptResult {
debug!("prompt_yes_no");
let message = CString::new(message).expect("Can't create string");
(self.0.prompt_yes_no)(message.as_ptr(), trusted).convert()
}
fn prompt_input(&self, message: String, default: String, trusted: bool) -> Option<String> {
debug!("prompt_input");
let message = CString::new(message).expect("Can't create string");
let default = CString::new(default).expect("Can't create string");
let raw_contents = (self.0.prompt_input)(message.as_ptr(), default.as_ptr(), trusted);
if raw_contents.is_null() {
return None;
}
let c_str = unsafe { CStr::from_ptr(raw_contents) };
let contents_str = c_str.to_str().expect("Can't create str");
Some(contents_str.to_owned())
}
fn on_panic(&self, reason: String, details: Option<String>) {
report_panic(&reason, details);
}
fn on_devtools_started(&self, port: Result<u16, ()>, token: String) {
let token = CString::new(token).expect("Can't create string");
match port {
Ok(p) => {
info!("Devtools Server running on port {}", p);
(self.0.on_devtools_started)(
CDevtoolsServerState::Started,
p.into(),
token.as_ptr(),
);
},
Err(()) => {
error!("Error running devtools server");
(self.0.on_devtools_started)(CDevtoolsServerState::Error, 0, token.as_ptr());
},
}
}
fn show_context_menu(&self, title: Option<String>, items: Vec<String>) {
debug!("show_context_menu");
let items_size = items.len() as u32;
let cstrs: Vec<CString> = items
.into_iter()
.map(|i| CString::new(i).expect("Can't create string"))
.collect();
let items: Vec<*const c_char> = cstrs.iter().map(|cstr| cstr.as_ptr()).collect();
let title = title.map(|s| CString::new(s).expect("Can't create string"));
let title_ptr = title
.as_ref()
.map(|cstr| cstr.as_ptr())
.unwrap_or(std::ptr::null());
(self.0.show_context_menu)(title_ptr, items.as_ptr(), items_size);
}
}

View file

@ -1,233 +0,0 @@
/* 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/. */
//! Servo's internal preferences are not C-compatible. To expose the preference to the embedder,
//! we keep a C-compatible copy of the preferences alive (LOCALCPREFS). The embedder can
//! retrieve an array (CPREFS) of struct of pointers (CPrefs) to the C-compatible preferences
//! (LocalCPref).
use std::cell::RefCell;
use std::collections::{BTreeMap, HashMap};
use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_void};
use log::debug;
use crate::simpleservo::{self, PrefValue};
thread_local! {
// CPREFS keeps alive a set of CPref that are sent over to the embedder.
// The CPREFS are structs holding pointers to values held alive by LOCALCPREFS.
// This is emptied in free_prefs the next time perform_updates is called.
static CPREFS: RefCell<Vec<CPref>> = RefCell::new(Vec::new());
static LOCALCPREFS: RefCell<BTreeMap<String, Box<LocalCPref>>> = RefCell::new(BTreeMap::new());
}
struct LocalCPref {
key: CString,
value: LocalCPrefValue,
is_default: bool,
}
#[derive(Debug)]
enum LocalCPrefValue {
Float(f64),
Int(i64),
Str(CString),
Bool(bool),
Missing,
}
impl LocalCPrefValue {
pub fn new(v: &PrefValue) -> LocalCPrefValue {
match v {
PrefValue::Float(v) => LocalCPrefValue::Float(*v),
PrefValue::Int(v) => LocalCPrefValue::Int(*v),
PrefValue::Str(v) => LocalCPrefValue::Str(CString::new(v.as_bytes()).unwrap()),
PrefValue::Bool(v) => LocalCPrefValue::Bool(*v),
PrefValue::Missing => LocalCPrefValue::Missing,
}
}
}
#[repr(C)]
pub struct CPrefList {
pub len: usize,
pub list: *const CPref,
}
impl CPrefList {
pub fn convert(&self) -> HashMap<String, PrefValue> {
let slice = unsafe { std::slice::from_raw_parts(self.list, self.len) };
slice.iter().map(|cpref| cpref.convert()).collect()
}
}
#[repr(C)]
pub struct CPref {
pub pref_type: CPrefType,
pub key: *const c_char,
pub value: *const c_void,
pub is_default: bool,
}
impl CPref {
fn new(local: &Box<LocalCPref>) -> CPref {
let (pref_type, value) = match &local.value {
LocalCPrefValue::Float(v) => (CPrefType::Float, v as *const f64 as *const c_void),
LocalCPrefValue::Int(v) => (CPrefType::Int, v as *const i64 as *const c_void),
LocalCPrefValue::Bool(v) => (CPrefType::Bool, v as *const bool as *const c_void),
LocalCPrefValue::Str(v) => (CPrefType::Str, v.as_ptr() as *const c_void),
LocalCPrefValue::Missing => (CPrefType::Missing, std::ptr::null()),
};
CPref {
key: local.key.as_ptr(),
is_default: local.is_default,
pref_type,
value,
}
}
fn convert(&self) -> (String, PrefValue) {
let key = unsafe { CStr::from_ptr(self.key) };
let key = key.to_str().expect("Can't read string").to_string();
let value = unsafe {
match self.pref_type {
CPrefType::Float => PrefValue::Float(*(self.value as *const f64)),
CPrefType::Int => PrefValue::Int(*(self.value as *const i64)),
CPrefType::Bool => PrefValue::Bool(*(self.value as *const bool)),
CPrefType::Str => PrefValue::Str({
let value = CStr::from_ptr(self.value as *const c_char);
value.to_str().expect("Can't read string").to_string()
}),
CPrefType::Missing => PrefValue::Missing,
}
};
(key, value)
}
}
#[repr(C)]
pub enum CPrefType {
Float,
Int,
Str,
Bool,
Missing,
}
#[no_mangle]
pub extern "C" fn get_pref_as_float(ptr: *const c_void) -> *const f64 {
ptr as *const f64
}
#[no_mangle]
pub extern "C" fn get_pref_as_int(ptr: *const c_void) -> *const i64 {
ptr as *const i64
}
#[no_mangle]
pub extern "C" fn get_pref_as_str(ptr: *const c_void) -> *const c_char {
ptr as *const c_char
}
#[no_mangle]
pub extern "C" fn get_pref_as_bool(ptr: *const c_void) -> *const bool {
ptr as *const bool
}
#[no_mangle]
pub extern "C" fn reset_all_prefs() {
debug!("reset_all_prefs");
simpleservo::reset_all_prefs()
}
#[no_mangle]
pub extern "C" fn reset_pref(key: *const c_char) -> bool {
debug!("reset_pref");
let key = unsafe { CStr::from_ptr(key) };
let key = key.to_str().expect("Can't read string");
simpleservo::reset_pref(key)
}
#[no_mangle]
pub extern "C" fn get_pref(key: *const c_char) -> CPref {
debug!("get_pref");
LOCALCPREFS.with(|localmap| {
let key = unsafe { CStr::from_ptr(key) };
let key = key.to_str().expect("Can't read string");
let (value, is_default) = simpleservo::get_pref(key);
let local = Box::new(LocalCPref {
key: CString::new(key).unwrap(),
value: LocalCPrefValue::new(&value),
is_default: is_default,
});
let cpref = CPref::new(&local);
localmap.borrow_mut().insert(key.to_string(), local);
cpref
})
}
fn set_pref(key: *const c_char, value: PrefValue) -> bool {
debug!("set_pref");
let key = unsafe { CStr::from_ptr(key) };
let key = key.to_str().expect("Can't read string");
simpleservo::set_pref(key, value).is_ok()
}
#[no_mangle]
pub extern "C" fn set_float_pref(key: *const c_char, value: f64) -> bool {
set_pref(key, PrefValue::Float(value))
}
#[no_mangle]
pub extern "C" fn set_int_pref(key: *const c_char, value: i64) -> bool {
set_pref(key, PrefValue::Int(value))
}
#[no_mangle]
pub extern "C" fn set_bool_pref(key: *const c_char, value: bool) -> bool {
set_pref(key, PrefValue::Bool(value))
}
#[no_mangle]
pub extern "C" fn set_str_pref(key: *const c_char, value: *const c_char) -> bool {
let value = unsafe { CStr::from_ptr(value) };
let value = value.to_str().expect("Can't read string").to_string();
set_pref(key, PrefValue::Str(value))
}
#[no_mangle]
pub extern "C" fn get_prefs() -> CPrefList {
// Called from any thread
debug!("get_prefs");
let map = simpleservo::get_prefs();
let local: BTreeMap<String, Box<LocalCPref>> = map
.into_iter()
.map(|(key, (value, is_default))| {
let l = Box::new(LocalCPref {
key: CString::new(key.as_bytes()).unwrap(),
value: LocalCPrefValue::new(&value),
is_default: is_default,
});
(key, l)
})
.collect();
let ptrs: Vec<CPref> = local.iter().map(|(_, local)| CPref::new(&local)).collect();
let list = CPrefList {
len: ptrs.len(),
list: ptrs.as_ptr(),
};
LOCALCPREFS.with(|p| *p.borrow_mut() = local);
CPREFS.with(|p| *p.borrow_mut() = ptrs);
list
}
pub(crate) fn free_prefs() {
LOCALCPREFS.with(|p| p.borrow_mut().clear());
CPREFS.with(|p| p.borrow_mut().clear());
}

View file

@ -1,42 +0,0 @@
/* 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::sync::{Arc, Mutex};
use log::{self, Metadata, Record};
use crate::OUTPUT_LOG_HANDLER;
lazy_static! {
pub static ref LOG_MODULE_FILTERS: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
}
pub struct VSLogger;
impl log::Log for VSLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
let modules = LOG_MODULE_FILTERS.lock().unwrap();
modules.is_empty() ||
modules.iter().any(|module| {
metadata.target() == module ||
metadata.target().starts_with(&format!("{}::", module))
})
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
let log = format!(
"RUST: {} - {} - {}\r\n\0",
record.level(),
record.target(),
record.args()
);
if let Some(handler) = OUTPUT_LOG_HANDLER.lock().unwrap().as_ref() {
(handler)(log.as_ptr() as _, log.len() as u32);
}
}
}
fn flush(&self) {}
}

View file

@ -1,38 +0,0 @@
[package]
name = "simpleservo_jniapi"
version = "0.0.1"
authors = ["The Servo Project Developers"]
license = "MPL-2.0"
edition = "2018"
publish = false
[lib]
name = "simpleservo"
crate-type = ["cdylib"]
test = false
bench = false
[dependencies]
android_logger = "0.13"
jni = "0.18.0"
libc = { workspace = true }
log = { workspace = true }
serde_json = { workspace = true }
# TODO: Once the native-bluetooth feature works for
# Android, remove the explicit feature list here.
simpleservo = { path = "../api", default-features = false, features = ["max_log_level", "webdriver"] }
[build-dependencies]
cc = "1.0"
[features]
debugmozjs = ["simpleservo/debugmozjs"]
default = ["max_log_level", "webdriver", "no_static_freetype"]
googlevr = ["simpleservo/googlevr"]
js_backtrace = ["simpleservo/js_backtrace"]
max_log_level = ["simpleservo/max_log_level"]
media-gstreamer = ["simpleservo/media-gstreamer"]
native-bluetooth = ["simpleservo/native-bluetooth"]
webdriver = ["simpleservo/webdriver"]
webgl_backtrace = ["simpleservo/webgl_backtrace"]
no_static_freetype = ["simpleservo/no_static_freetype"]

View file

@ -57,7 +57,7 @@ class MachCommands(CommandBase):
help="Command-line arguments to be passed through to Cargo")
@CommandBase.common_command_arguments(build_configuration=True, build_type=True)
def build(self, build_type: BuildType, jobs=None, params=None, no_package=False,
verbose=False, very_verbose=False, libsimpleservo=False, **kwargs):
verbose=False, very_verbose=False, **kwargs):
opts = params or []
if build_type.is_release():
@ -99,19 +99,15 @@ class MachCommands(CommandBase):
print((key, env[key]))
status = self.run_cargo_build_like_command(
"build", opts, env=env, verbose=verbose,
libsimpleservo=libsimpleservo, **kwargs
)
"build", opts, env=env, verbose=verbose, **kwargs)
# Do some additional things if the build succeeded
if status == 0:
built_binary = self.get_binary_path(
build_type,
target=self.cross_compile_target,
android=self.is_android_build,
simpleservo=libsimpleservo
)
if status == 0:
if self.is_android_build and not no_package:
flavor = None
if "googlevr" in self.features:
@ -128,14 +124,12 @@ class MachCommands(CommandBase):
status = 1
elif sys.platform == "darwin":
servo_path = self.get_binary_path(
build_type, target=self.cross_compile_target, simpleservo=libsimpleservo)
servo_bin_dir = os.path.dirname(servo_path)
servo_bin_dir = os.path.dirname(built_binary)
assert os.path.exists(servo_bin_dir)
if self.enable_media:
print("Packaging gstreamer dylibs")
if not package_gstreamer_dylibs(self.cross_compile_target, servo_path):
if not package_gstreamer_dylibs(self.cross_compile_target, built_binary):
return 1
# On the Mac, set a lovely icon. This makes it easier to pick out the Servo binary in tools
@ -146,7 +140,7 @@ class MachCommands(CommandBase):
icon = Cocoa.NSImage.alloc().initWithContentsOfFile_(icon_path)
if icon is not None:
Cocoa.NSWorkspace.sharedWorkspace().setIcon_forFile_options_(icon,
servo_path,
built_binary,
0)
except ImportError:
pass

View file

@ -331,23 +331,15 @@ class CommandBase(object):
apk_name = "servoapp.apk"
return path.join(base_path, build_type.directory_name(), apk_name)
def get_binary_path(self, build_type: BuildType, target=None, android=False, simpleservo=False):
def get_binary_path(self, build_type: BuildType, target=None, android=False):
base_path = util.get_target_dir()
if android:
base_path = path.join(base_path, self.config["android"]["target"])
simpleservo = True
return path.join(base_path, build_type.directory_name(), "libsimpleservo.so")
elif target:
base_path = path.join(base_path, target)
binary_name = f"servo{servo.platform.get().executable_suffix()}"
if simpleservo:
if sys.platform == "win32":
binary_name = "simpleservo.dll"
elif sys.platform == "darwin":
binary_name = "libsimpleservo.dylib"
else:
binary_name = "libsimpleservo.so"
binary_path = path.join(base_path, build_type.directory_name(), binary_name)
if not path.exists(binary_path):
@ -688,13 +680,6 @@ class CommandBase(object):
'--media-stack', default=None, group="Feature Selection",
choices=["gstreamer", "dummy"], help='Which media stack to use',
),
CommandArgument(
'--libsimpleservo',
default=None,
group="Feature Selection",
action='store_true',
help='Build the libsimpleservo library instead of the servo executable',
),
CommandArgument(
'--debug-mozjs',
default=False,
@ -828,7 +813,6 @@ class CommandBase(object):
def run_cargo_build_like_command(
self, command: str, cargo_args: List[str],
env=None, verbose=False,
libsimpleservo=False,
debug_mozjs=False, with_debug_assertions=False,
with_frame_pointer=False, without_wgl=False,
**_kwargs
@ -848,12 +832,8 @@ class CommandBase(object):
args = []
if "--manifest-path" not in cargo_args:
if libsimpleservo or self.is_android_build:
if self.is_android_build:
api = "jniapi"
else:
api = "capi"
port = path.join("libsimpleservo", api)
port = "jniapi"
else:
port = "servoshell"
args += [

View file

@ -37,10 +37,6 @@ packages = [
"proc-macro-crate",
"toml",
# This dependency is for "hermit os" which Servo doesn't support.
# Theoretically, it's never fetched.
"hermit-abi",
# Duplicated by winit.
"windows-sys",
"windows-targets",