mirror of
https://github.com/servo/servo.git
synced 2025-06-06 00:25:37 +00:00
new android port: introduce a simple servo library
This commit is contained in:
parent
cbaf19c65c
commit
baf6635a4c
16 changed files with 1398 additions and 30 deletions
78
Cargo.lock
generated
78
Cargo.lock
generated
|
@ -26,6 +26,21 @@ name = "android_injected_glue"
|
|||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "android_log-sys"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "android_logger"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"android_log-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.10.2"
|
||||
|
@ -53,6 +68,11 @@ dependencies = [
|
|||
"nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ascii"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "atomic_refcell"
|
||||
version = "0.1.0"
|
||||
|
@ -317,6 +337,11 @@ name = "cc"
|
|||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cesu8"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.2.0"
|
||||
|
@ -418,6 +443,15 @@ name = "color_quant"
|
|||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "2.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ascii 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "compositing"
|
||||
version = "0.0.1"
|
||||
|
@ -1360,6 +1394,24 @@ dependencies = [
|
|||
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jni"
|
||||
version = "0.10.2"
|
||||
source = "git+https://github.com/paulrouget/jni-rs?branch=return_javavm#27a18dbc01b723055b60726c43d43c0f8c26448e"
|
||||
dependencies = [
|
||||
"cesu8 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"combine 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jni-sys"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.1.14"
|
||||
|
@ -1587,6 +1639,22 @@ dependencies = [
|
|||
"webvr_traits 0.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsimpleservo"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"android_injected_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"android_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gl_generator 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jni 0.10.2 (git+https://github.com/paulrouget/jni-rs?branch=return_javavm)",
|
||||
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libservo 0.0.1",
|
||||
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.0.18"
|
||||
|
@ -2764,7 +2832,6 @@ dependencies = [
|
|||
name = "servo"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"android_injected_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"backtrace 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.18.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2776,7 +2843,6 @@ dependencies = [
|
|||
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"osmesa-src 18.1.0-devel (git+https://github.com/servo/osmesa-src)",
|
||||
"osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"servo-egl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sig 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tinyfiledialogs 3.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"user32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2886,7 +2952,6 @@ dependencies = [
|
|||
name = "servo_config"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"android_injected_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dirs 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"embedder_traits 0.0.1",
|
||||
"env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -3795,10 +3860,13 @@ dependencies = [
|
|||
"checksum alloc-no-stdlib 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b21f6ad9c9957eb5d70c3dee16d31c092b3cab339628f821766b05e6833d72b8"
|
||||
"checksum android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407"
|
||||
"checksum android_injected_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "80b9e34fcbf29c0563547cb2ecce9b49504597cad6166769b1e4efb45c6c2951"
|
||||
"checksum android_log-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b8052e2d8aabbb8d556d6abbcce2a22b9590996c5f849b9c7ce4544a2e3b984e"
|
||||
"checksum android_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bad99185bc195e796e1591740c26716667b58ac9210a48731f71f803fc6ca43a"
|
||||
"checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455"
|
||||
"checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5"
|
||||
"checksum app_units 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c4720c83543de184d9f6add2fdb8e8031543497b8506620884c16e125b493c09"
|
||||
"checksum arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2f0ef4a9820019a0c91d918918c93dc71d469f581a49b47ddc1d285d4270bbe2"
|
||||
"checksum ascii 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae7d751998c189c1d4468cf0a39bb2eae052a9c58d50ebb3b9591ee3813ad50"
|
||||
"checksum atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb2dcb6e6d35f20276943cc04bb98e538b348d525a04ac79c10021561d202f21"
|
||||
"checksum atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21e50800ec991574876040fff8ee46b136a53e985286fbe6a3bdfe6421b78860"
|
||||
"checksum audio-video-metadata 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "71536082079f5ba92c274fba7c2dcd4e2f9d5c13ce6d7f8fe9acbbb258916d18"
|
||||
|
@ -3823,6 +3891,7 @@ dependencies = [
|
|||
"checksum bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1b7db437d718977f6dc9b2e3fd6fc343c02ac6b899b73fdd2179163447bd9ce9"
|
||||
"checksum caseless 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "808dab3318747be122cb31d36de18d4d1c81277a76f8332a02b81a3d73463d7f"
|
||||
"checksum cc 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9be26b24e988625409b19736d130f0c7d224f01d06454b5f81d8d23d6c1a618f"
|
||||
"checksum cesu8 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
|
||||
"checksum cexpr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393a5f0088efbe41f9d1fcd062f24e83c278608420e62109feb2c8abee07de7d"
|
||||
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
|
||||
"checksum cgl 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "55e7ec0b74fe5897894cbc207092c577e87c52f8a59e8ca8d97ef37551f60a49"
|
||||
|
@ -3834,6 +3903,7 @@ dependencies = [
|
|||
"checksum cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "56d741ea7a69e577f6d06b36b7dff4738f680593dc27a701ffa8506b73ce28bb"
|
||||
"checksum cocoa 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b44bd25bd275e9d74a5dff8ca55f2fb66c9ad5e12170d58697701df21a56e0e"
|
||||
"checksum color_quant 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a475fc4af42d83d28adf72968d9bcfaf035a1a9381642d8e85d8a04957767b0d"
|
||||
"checksum combine 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1645a65a99c7c8d345761f4b75a6ffe5be3b3b27a93ee731fccc5050ba6be97c"
|
||||
"checksum cookie 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "746858cae4eae40fff37e1998320068df317bc247dc91a67c6cfa053afdc2abb"
|
||||
"checksum core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7caa6cb9e76ddddbea09a03266d6b3bc98cd41e9fb9b017c473e7cca593ec25"
|
||||
"checksum core-foundation-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b2a53cce0ddcf7e7e1f998738d757d5a3bf08bf799a180e50ebe50d298f52f5a"
|
||||
|
@ -3913,6 +3983,8 @@ dependencies = [
|
|||
"checksum itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b07332223953b5051bceb67e8c4700aa65291535568e1f12408c43c4a42c0394"
|
||||
"checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682"
|
||||
"checksum jemalloc-sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "479294d130502fada93c7a957e8d059b632b03d6204aca37af557dee947f30a9"
|
||||
"checksum jni 0.10.2 (git+https://github.com/paulrouget/jni-rs?branch=return_javavm)" = "<none>"
|
||||
"checksum jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
||||
"checksum jpeg-decoder 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "0dfe27a6c0dabd772d0f9b9f8701c4ca12c4d1eebcadf2be1f6f70396f6a1434"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum khronos_api 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9ef23fcc4059260c5936f638c9805ebfc87cb172fa6661d130cba7f97d58f55"
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"ports/servo",
|
||||
"ports/libsimpleservo/",
|
||||
"tests/unit/*",
|
||||
]
|
||||
default-members = [
|
||||
"ports/servo",
|
||||
"ports/libsimpleservo/",
|
||||
"tests/unit/*",
|
||||
]
|
||||
exclude = [".cargo"]
|
||||
|
|
|
@ -30,6 +30,3 @@ embedder_traits = { path = "../embedder_traits", features = ["tests"] }
|
|||
|
||||
[target.'cfg(not(target_os = "android"))'.dependencies]
|
||||
dirs = "1.0"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
android_injected_glue = "0.2"
|
||||
|
|
|
@ -6,41 +6,33 @@
|
|||
//! For linux based platforms, it uses the XDG base directory spec but provides
|
||||
//! similar abstractions for non-linux platforms.
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
use android_injected_glue;
|
||||
#[cfg(target_os = "android")]
|
||||
use std::ffi::CStr;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))]
|
||||
pub fn default_config_dir() -> PathBuf {
|
||||
pub fn default_config_dir() -> Option<PathBuf> {
|
||||
let mut config_dir = ::dirs::config_dir().unwrap();
|
||||
config_dir.push("servo");
|
||||
config_dir.push("default");
|
||||
config_dir
|
||||
Some(config_dir)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(unsafe_code)]
|
||||
pub fn default_config_dir() -> PathBuf {
|
||||
let dir = unsafe {
|
||||
CStr::from_ptr((*android_injected_glue::get_app().activity).externalDataPath)
|
||||
};
|
||||
PathBuf::from(dir.to_str().unwrap())
|
||||
pub fn default_config_dir() -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn default_config_dir() -> PathBuf {
|
||||
pub fn default_config_dir() -> Option<PathBuf> {
|
||||
// FIXME: use `config_dir()` ($HOME/Library/Preferences)
|
||||
// instead of `data_dir()` ($HOME/Library/Application Support) ?
|
||||
let mut config_dir = ::dirs::data_dir().unwrap();
|
||||
config_dir.push("Servo");
|
||||
config_dir
|
||||
Some(config_dir)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn default_config_dir() -> PathBuf {
|
||||
pub fn default_config_dir() -> Option<PathBuf> {
|
||||
let mut config_dir = ::dirs::config_dir().unwrap();
|
||||
config_dir.push("Servo");
|
||||
config_dir
|
||||
Some(config_dir)
|
||||
}
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
|
||||
#![deny(unsafe_code)]
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
extern crate android_injected_glue;
|
||||
#[cfg(not(target_os = "android"))]
|
||||
extern crate dirs;
|
||||
extern crate embedder_traits;
|
||||
|
|
|
@ -183,9 +183,10 @@ pub fn add_user_prefs() {
|
|||
init_user_prefs(&mut path);
|
||||
}
|
||||
None => {
|
||||
let mut path = default_config_dir();
|
||||
if path.join("prefs.json").exists() {
|
||||
init_user_prefs(&mut path);
|
||||
if let Some(mut path) = default_config_dir() {
|
||||
if path.join("prefs.json").exists() {
|
||||
init_user_prefs(&mut path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ fn test_get_set_reset_extend() {
|
|||
assert_eq!(*PREFS.get("extra.stuff"), PrefValue::Boolean(false));
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
#[test]
|
||||
fn test_default_config_dir_create_read_write() {
|
||||
let json_str = "{\
|
||||
|
@ -56,7 +57,7 @@ fn test_default_config_dir_create_read_write() {
|
|||
\"shell.homepage\": \"https://google.com\"\
|
||||
}";
|
||||
let mut expected_json = String::new();
|
||||
let config_path = basedir::default_config_dir();
|
||||
let config_path = basedir::default_config_dir().unwrap();
|
||||
|
||||
if !config_path.exists() {
|
||||
fs::create_dir_all(&config_path).unwrap();
|
||||
|
|
46
ports/libsimpleservo/Cargo.toml
Normal file
46
ports/libsimpleservo/Cargo.toml
Normal file
|
@ -0,0 +1,46 @@
|
|||
[package]
|
||||
name = "libsimpleservo"
|
||||
version = "0.0.1"
|
||||
build = "build.rs"
|
||||
authors = ["The Servo Project Developers"]
|
||||
license = "MPL-2.0"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
name = "simpleservo"
|
||||
crate-type = ["cdylib"]
|
||||
test = false
|
||||
bench = false
|
||||
|
||||
[dependencies]
|
||||
libservo = { path = "../../components/servo" }
|
||||
log = "0.4"
|
||||
serde_json = "1.0"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
android_injected_glue = "0.2"
|
||||
android_logger = "0.6"
|
||||
# FIXME: use `jni = "0.10.2"` once
|
||||
# https://github.com/prevoty/jni-rs/pull/98 lands and is published
|
||||
jni = { git = "https://github.com/paulrouget/jni-rs", branch = "return_javavm" }
|
||||
|
||||
[target.'cfg(not(target_os = "macos"))'.dependencies]
|
||||
libc = "0.2"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = "0.3.2"
|
||||
|
||||
[build-dependencies]
|
||||
gl_generator = "0.9"
|
||||
cc = "1.0"
|
||||
|
||||
[features]
|
||||
default = ["unstable", "default-except-unstable"]
|
||||
default-except-unstable = ["webdriver", "max_log_level"]
|
||||
max_log_level = ["log/release_max_level_info"]
|
||||
webdriver = ["libservo/webdriver"]
|
||||
energy-profiling = ["libservo/energy-profiling"]
|
||||
debugmozjs = ["libservo/debugmozjs"]
|
||||
unstable = ["libservo/unstable"]
|
||||
googlevr = ["libservo/googlevr"]
|
||||
oculusvr = ["libservo/oculusvr"]
|
3
ports/libsimpleservo/README.md
Normal file
3
ports/libsimpleservo/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
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).
|
81
ports/libsimpleservo/build.rs
Normal file
81
ports/libsimpleservo/build.rs
Normal file
|
@ -0,0 +1,81 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
extern crate cc;
|
||||
extern crate gl_generator;
|
||||
|
||||
use gl_generator::{Api, Fallbacks, Profile, Registry};
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
let target = env::var("TARGET").unwrap();
|
||||
if target.contains("android") {
|
||||
android_main()
|
||||
}
|
||||
generate_gl_bindings(&target);
|
||||
}
|
||||
|
||||
fn android_main() {
|
||||
// Get the NDK path from NDK_HOME env.
|
||||
let ndk_path = env::var_os("ANDROID_NDK").expect("Please set the ANDROID_NDK environment variable");
|
||||
let ndk_path = Path::new(&ndk_path);
|
||||
|
||||
let target = env::var("TARGET").unwrap();
|
||||
let arch = if target.contains("arm") {
|
||||
"arch-arm"
|
||||
} else if target.contains("aarch64") {
|
||||
"arch-arm64"
|
||||
} else if target.contains("x86") || target.contains("i686") {
|
||||
"arch-x86"
|
||||
} else if target.contains("mips") {
|
||||
"arch-mips"
|
||||
} else {
|
||||
panic!("Invalid target architecture {}", target);
|
||||
};
|
||||
|
||||
let platform = if target.contains("aarch64") {
|
||||
"android-21"
|
||||
} else {
|
||||
"android-18"
|
||||
};
|
||||
|
||||
// compiling android_native_app_glue.c
|
||||
let c_file = ndk_path.join("sources").join("android").join("native_app_glue").join("android_native_app_glue.c");
|
||||
let sysroot = ndk_path.join("platforms").join(platform).join(arch);
|
||||
cc::Build::new()
|
||||
.file(c_file)
|
||||
.flag("--sysroot")
|
||||
.flag(sysroot.to_str().unwrap())
|
||||
.warnings(false)
|
||||
.compile("android_native_app_glue");
|
||||
|
||||
// Get the output directory.
|
||||
let out_dir = env::var("OUT_DIR").expect("Cargo should have set the OUT_DIR environment variable");
|
||||
|
||||
println!("cargo:rustc-link-lib=static=android_native_app_glue");
|
||||
println!("cargo:rustc-link-search=native={}", out_dir);
|
||||
println!("cargo:rustc-link-lib=log");
|
||||
println!("cargo:rustc-link-lib=android");
|
||||
|
||||
}
|
||||
|
||||
fn generate_gl_bindings(target: &str) {
|
||||
// For now, we only support EGL, and only on Windows and Android.
|
||||
if target.contains("android") || target.contains("windows") {
|
||||
let dest = env::var("OUT_DIR").unwrap();
|
||||
let mut file = File::create(&Path::new(&dest).join("egl_bindings.rs")).unwrap();
|
||||
if target.contains("android") {
|
||||
Registry::new(Api::Egl, (1, 5), Profile::Core, Fallbacks::All, [])
|
||||
.write_bindings(gl_generator::StaticStructGenerator, &mut file)
|
||||
.unwrap();
|
||||
}
|
||||
if target.contains("windows") {
|
||||
Registry::new(Api::Egl, (1, 5), Profile::Core, Fallbacks::All, [])
|
||||
.write_bindings(gl_generator::StructGenerator, &mut file)
|
||||
.unwrap();
|
||||
};
|
||||
}
|
||||
}
|
424
ports/libsimpleservo/src/api.rs
Normal file
424
ports/libsimpleservo/src/api.rs
Normal file
|
@ -0,0 +1,424 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use serde_json;
|
||||
use servo::{self, gl, webrender_api, BrowserId, Servo};
|
||||
use servo::compositing::windowing::{AnimationState, EmbedderCoordinates, MouseWindowEvent, WindowEvent, WindowMethods};
|
||||
use servo::embedder_traits::EmbedderMsg;
|
||||
use servo::embedder_traits::resources::{self, Resource};
|
||||
use servo::euclid::{Length, TypedPoint2D, TypedScale, TypedSize2D, TypedVector2D};
|
||||
use servo::ipc_channel::ipc;
|
||||
use servo::msg::constellation_msg::TraversalDirection;
|
||||
use servo::script_traits::{MouseButton, TouchEventType};
|
||||
use servo::servo_config::opts;
|
||||
use servo::servo_config::prefs::PREFS;
|
||||
use servo::servo_url::ServoUrl;
|
||||
use servo::style_traits::DevicePixel;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::mem;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
|
||||
thread_local! {
|
||||
pub static SERVO: RefCell<Option<ServoGlue>> = RefCell::new(None);
|
||||
}
|
||||
|
||||
/// The EventLoopWaker::wake function will be called from any thread.
|
||||
/// It will be called to notify embedder that some events are available,
|
||||
/// and that perform_updates need to be called
|
||||
pub use servo::embedder_traits::EventLoopWaker;
|
||||
|
||||
/// Delegate resource file reading to the embedder.
|
||||
pub trait ReadFileTrait {
|
||||
fn readfile(&self, file: &str) -> Vec<u8>;
|
||||
}
|
||||
|
||||
/// Callbacks. Implemented by embedder. Called by Servo.
|
||||
pub trait HostTrait {
|
||||
/// Will be called from the thread used for the init call.
|
||||
/// Will be called when the GL buffer has been updated.
|
||||
fn flush(&self);
|
||||
/// Page starts loading.
|
||||
/// "Reload button" should be disabled.
|
||||
/// "Stop button" should be enabled.
|
||||
/// Throbber starts spinning.
|
||||
fn on_load_started(&self);
|
||||
/// Page has loaded.
|
||||
/// "Reload button" should be enabled.
|
||||
/// "Stop button" should be disabled.
|
||||
/// Throbber stops spinning.
|
||||
fn on_load_ended(&self);
|
||||
/// Page title has changed.
|
||||
fn on_title_changed(&self, title: String);
|
||||
/// Page URL has changed.
|
||||
fn on_url_changed(&self, url: String);
|
||||
/// Back/forward state has changed.
|
||||
/// Back/forward buttons need to be disabled/enabled.
|
||||
fn on_history_changed(&self, can_go_back: bool, can_go_forward: bool);
|
||||
/// Page animation state has changed. If animating, it's recommended
|
||||
/// that the embedder doesn't wait for the wake function to be called
|
||||
/// to call perform_updates. Usually, it means doing:
|
||||
/// while true { servo.perform_updates() }. This will end up calling flush
|
||||
/// which will call swap_buffer which will be blocking long enough to limit
|
||||
/// drawing at 60 FPS.
|
||||
/// If not animating, call perform_updates only when needed (when the embedder
|
||||
/// has events for Servo, or Servo has woken up the embedder event loop via
|
||||
/// EventLoopWaker).
|
||||
fn on_animating_changed(&self, animating: bool);
|
||||
}
|
||||
|
||||
pub struct ServoGlue {
|
||||
servo: Servo<ServoCallbacks>,
|
||||
batch_mode: bool,
|
||||
callbacks: Rc<ServoCallbacks>,
|
||||
browser_id: BrowserId,
|
||||
events: Vec<WindowEvent>,
|
||||
current_url: Option<ServoUrl>,
|
||||
}
|
||||
|
||||
pub fn servo_version() -> String {
|
||||
servo::config::servo_version()
|
||||
}
|
||||
|
||||
/// Initialize Servo. At that point, we need a valid GL context.
|
||||
/// In the future, this will be done in multiple steps.
|
||||
pub fn init(
|
||||
gl: Rc<gl::Gl>,
|
||||
argsline: String,
|
||||
embedder_url: Option<String>,
|
||||
waker: Box<EventLoopWaker>,
|
||||
readfile: Box<ReadFileTrait + Send + Sync>,
|
||||
callbacks: Box<HostTrait>,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> Result<(), &'static str> {
|
||||
resources::set(Box::new(ResourceReader(readfile)));
|
||||
|
||||
let mut args: Vec<String> = serde_json::from_str(&argsline).map_err(|_| {
|
||||
"Invalid arguments. Servo arguments must be formatted as a JSON array"
|
||||
})?;
|
||||
// opts::from_cmdline_args expects the first argument to be the binary name.
|
||||
args.insert(0, "servo".to_string());
|
||||
opts::from_cmdline_args(&args);
|
||||
|
||||
let embedder_url = embedder_url.as_ref().and_then(|s| {
|
||||
ServoUrl::parse(s).ok()
|
||||
});
|
||||
let cmdline_url = opts::get().url.clone();
|
||||
let pref_url = PREFS.get("shell.homepage").as_string().and_then(|s| {
|
||||
ServoUrl::parse(s).ok()
|
||||
});
|
||||
let blank_url = ServoUrl::parse("about:blank").ok();
|
||||
|
||||
let url = embedder_url
|
||||
.or(cmdline_url)
|
||||
.or(pref_url)
|
||||
.or(blank_url).unwrap();
|
||||
|
||||
gl.clear_color(1.0, 1.0, 1.0, 1.0);
|
||||
gl.clear(gl::COLOR_BUFFER_BIT);
|
||||
gl.finish();
|
||||
|
||||
let callbacks = Rc::new(ServoCallbacks {
|
||||
gl: gl.clone(),
|
||||
host_callbacks: callbacks,
|
||||
width: Cell::new(width),
|
||||
height: Cell::new(height),
|
||||
waker,
|
||||
});
|
||||
|
||||
let mut servo = Servo::new(callbacks.clone());
|
||||
|
||||
let (sender, receiver) = ipc::channel().map_err(|_| "Can't create ipc::channel")?;
|
||||
servo.handle_events(vec![WindowEvent::NewBrowser(url.clone(), sender)]);
|
||||
let browser_id = receiver.recv().map_err(|_| "Can't receive browser_id")?;
|
||||
servo.handle_events(vec![WindowEvent::SelectBrowser(browser_id)]);
|
||||
|
||||
SERVO.with(|s| {
|
||||
*s.borrow_mut() = Some(ServoGlue {
|
||||
servo,
|
||||
batch_mode: false,
|
||||
callbacks,
|
||||
browser_id,
|
||||
events: vec![],
|
||||
current_url: Some(url),
|
||||
});
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl ServoGlue {
|
||||
/// This is the Servo heartbeat. This needs to be called
|
||||
/// everytime wakeup is called or when embedder wants Servo
|
||||
/// to act on its pending events.
|
||||
pub fn perform_updates(&mut self) -> Result<(), &'static str> {
|
||||
debug!("perform_updates");
|
||||
let events = mem::replace(&mut self.events, Vec::new());
|
||||
self.servo.handle_events(events);
|
||||
self.handle_servo_events()
|
||||
}
|
||||
|
||||
/// In batch mode, Servo won't call perform_updates automatically.
|
||||
/// This can be useful when the embedder wants to control when Servo
|
||||
/// acts on its pending events. For example, if the embedder wants Servo
|
||||
/// to act on the scroll events only at a certain time, not everytime
|
||||
/// scroll() is called.
|
||||
pub fn set_batch_mode(&mut self, batch: bool) -> Result<(), &'static str> {
|
||||
debug!("set_batch_mode");
|
||||
self.batch_mode = batch;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Load an URL. This needs to be a valid url.
|
||||
pub fn load_uri(&mut self, url: &str) -> Result<(), &'static str> {
|
||||
debug!("load_uri: {}", url);
|
||||
ServoUrl::parse(url)
|
||||
.map_err(|_| "Can't parse URL")
|
||||
.and_then(|url| {
|
||||
let event = WindowEvent::LoadUrl(self.browser_id, url);
|
||||
self.process_event(event)
|
||||
})
|
||||
}
|
||||
|
||||
/// Reload the page.
|
||||
pub fn reload(&mut self) -> Result<(), &'static str> {
|
||||
debug!("reload");
|
||||
let event = WindowEvent::Reload(self.browser_id);
|
||||
self.process_event(event)
|
||||
}
|
||||
|
||||
/// Go back in history.
|
||||
pub fn go_back(&mut self) -> Result<(), &'static str> {
|
||||
debug!("go_back");
|
||||
let event = WindowEvent::Navigation(self.browser_id, TraversalDirection::Back(1));
|
||||
self.process_event(event)
|
||||
}
|
||||
|
||||
/// Go forward in history.
|
||||
pub fn go_forward(&mut self) -> Result<(), &'static str> {
|
||||
debug!("go_forward");
|
||||
let event = WindowEvent::Navigation(self.browser_id, TraversalDirection::Forward(1));
|
||||
self.process_event(event)
|
||||
}
|
||||
|
||||
/// Let Servo know that the window has been resized.
|
||||
pub fn resize(&mut self, width: u32, height: u32) -> Result<(), &'static str> {
|
||||
debug!("resize");
|
||||
self.callbacks.width.set(width);
|
||||
self.callbacks.height.set(height);
|
||||
self.process_event(WindowEvent::Resize)
|
||||
}
|
||||
|
||||
/// Start scrolling.
|
||||
/// x/y are scroll coordinates.
|
||||
/// dx/dy are scroll deltas.
|
||||
pub fn scroll_start(&mut self, dx: i32, dy: i32, x: u32, y: u32) -> Result<(), &'static str> {
|
||||
let delta = TypedVector2D::new(dx as f32, dy as f32);
|
||||
let scroll_location = webrender_api::ScrollLocation::Delta(delta);
|
||||
let event = WindowEvent::Scroll(
|
||||
scroll_location,
|
||||
TypedPoint2D::new(x as i32, y as i32),
|
||||
TouchEventType::Down,
|
||||
);
|
||||
self.process_event(event)
|
||||
}
|
||||
|
||||
/// Scroll.
|
||||
/// x/y are scroll coordinates.
|
||||
/// dx/dy are scroll deltas.
|
||||
pub fn scroll(&mut self, dx: i32, dy: i32, x: u32, y: u32) -> Result<(), &'static str> {
|
||||
let delta = TypedVector2D::new(dx as f32, dy as f32);
|
||||
let scroll_location = webrender_api::ScrollLocation::Delta(delta);
|
||||
let event = WindowEvent::Scroll(
|
||||
scroll_location,
|
||||
TypedPoint2D::new(x as i32, y as i32),
|
||||
TouchEventType::Move,
|
||||
);
|
||||
self.process_event(event)
|
||||
}
|
||||
|
||||
/// End scrolling.
|
||||
/// x/y are scroll coordinates.
|
||||
/// dx/dy are scroll deltas.
|
||||
pub fn scroll_end(&mut self, dx: i32, dy: i32, x: u32, y: u32) -> Result<(), &'static str> {
|
||||
let delta = TypedVector2D::new(dx as f32, dy as f32);
|
||||
let scroll_location = webrender_api::ScrollLocation::Delta(delta);
|
||||
let event = WindowEvent::Scroll(
|
||||
scroll_location,
|
||||
TypedPoint2D::new(x as i32, y as i32),
|
||||
TouchEventType::Up,
|
||||
);
|
||||
self.process_event(event)
|
||||
}
|
||||
|
||||
/// Perform a click.
|
||||
pub fn click(&mut self, x: u32, y: u32) -> Result<(), &'static str> {
|
||||
let mouse_event =
|
||||
MouseWindowEvent::Click(MouseButton::Left, TypedPoint2D::new(x as f32, y as f32));
|
||||
let event = WindowEvent::MouseWindowEventClass(mouse_event);
|
||||
self.process_event(event)
|
||||
}
|
||||
|
||||
fn process_event(&mut self, event: WindowEvent) -> Result<(), &'static str> {
|
||||
self.events.push(event);
|
||||
if !self.batch_mode {
|
||||
self.perform_updates()
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_servo_events(&mut self) -> Result<(), &'static str> {
|
||||
for (_browser_id, event) in self.servo.get_events() {
|
||||
match event {
|
||||
EmbedderMsg::ChangePageTitle(title) => {
|
||||
let fallback_title: String = if let Some(ref current_url) = self.current_url {
|
||||
current_url.to_string()
|
||||
} else {
|
||||
String::from("Untitled")
|
||||
};
|
||||
let title = match title {
|
||||
Some(ref title) if title.len() > 0 => &**title,
|
||||
_ => &fallback_title,
|
||||
};
|
||||
let title = format!("{} - Servo", title);
|
||||
self.callbacks.host_callbacks.on_title_changed(title);
|
||||
},
|
||||
EmbedderMsg::AllowNavigation(_url, response_chan) => {
|
||||
if let Err(e) = response_chan.send(true) {
|
||||
warn!("Failed to send allow_navigation() response: {}", e);
|
||||
};
|
||||
},
|
||||
EmbedderMsg::HistoryChanged(entries, current) => {
|
||||
let can_go_back = current > 0;
|
||||
let can_go_forward = current < entries.len() - 1;
|
||||
self.callbacks
|
||||
.host_callbacks
|
||||
.on_history_changed(can_go_back, can_go_forward);
|
||||
self.callbacks
|
||||
.host_callbacks
|
||||
.on_url_changed(entries[current].clone().to_string());
|
||||
self.current_url = Some(entries[current].clone());
|
||||
},
|
||||
EmbedderMsg::LoadStart => {
|
||||
self.callbacks.host_callbacks.on_load_started();
|
||||
},
|
||||
EmbedderMsg::LoadComplete => {
|
||||
self.callbacks.host_callbacks.on_load_ended();
|
||||
},
|
||||
EmbedderMsg::GetSelectedBluetoothDevice(_, sender) => {
|
||||
let _ = sender.send(None);
|
||||
},
|
||||
EmbedderMsg::AllowUnload(sender) => {
|
||||
let _ = sender.send(true);
|
||||
},
|
||||
EmbedderMsg::Alert(message, sender) => {
|
||||
info!("Alert: {}", message);
|
||||
let _ = sender.send(());
|
||||
},
|
||||
EmbedderMsg::CloseBrowser |
|
||||
EmbedderMsg::Status(..) |
|
||||
EmbedderMsg::SelectFiles(..) |
|
||||
EmbedderMsg::MoveTo(..) |
|
||||
EmbedderMsg::ResizeTo(..) |
|
||||
EmbedderMsg::KeyEvent(..) |
|
||||
EmbedderMsg::SetCursor(..) |
|
||||
EmbedderMsg::NewFavicon(..) |
|
||||
EmbedderMsg::HeadParsed |
|
||||
EmbedderMsg::SetFullscreenState(..) |
|
||||
EmbedderMsg::ShowIME(..) |
|
||||
EmbedderMsg::HideIME |
|
||||
EmbedderMsg::Shutdown |
|
||||
EmbedderMsg::Panic(..) => {},
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct ServoCallbacks {
|
||||
waker: Box<EventLoopWaker>,
|
||||
gl: Rc<gl::Gl>,
|
||||
host_callbacks: Box<HostTrait>,
|
||||
width: Cell<u32>,
|
||||
height: Cell<u32>,
|
||||
}
|
||||
|
||||
impl WindowMethods for ServoCallbacks {
|
||||
fn prepare_for_composite(
|
||||
&self,
|
||||
_width: Length<u32, DevicePixel>,
|
||||
_height: Length<u32, DevicePixel>,
|
||||
) -> bool {
|
||||
debug!("WindowMethods::prepare_for_composite");
|
||||
true
|
||||
}
|
||||
|
||||
fn present(&self) {
|
||||
debug!("WindowMethods::present");
|
||||
self.host_callbacks.flush();
|
||||
}
|
||||
|
||||
fn supports_clipboard(&self) -> bool {
|
||||
debug!("WindowMethods::supports_clipboard");
|
||||
false
|
||||
}
|
||||
|
||||
fn create_event_loop_waker(&self) -> Box<EventLoopWaker> {
|
||||
debug!("WindowMethods::create_event_loop_waker");
|
||||
self.waker.clone()
|
||||
}
|
||||
|
||||
fn gl(&self) -> Rc<gl::Gl> {
|
||||
debug!("WindowMethods::gl");
|
||||
self.gl.clone()
|
||||
}
|
||||
|
||||
fn set_animation_state(&self, state: AnimationState) {
|
||||
debug!("WindowMethods::set_animation_state");
|
||||
self.host_callbacks.on_animating_changed(state == AnimationState::Animating);
|
||||
}
|
||||
|
||||
fn get_coordinates(&self) -> EmbedderCoordinates {
|
||||
let size = TypedSize2D::new(self.width.get(), self.height.get());
|
||||
EmbedderCoordinates {
|
||||
viewport: webrender_api::DeviceUintRect::new(TypedPoint2D::zero(), size),
|
||||
framebuffer: size,
|
||||
window: (size, TypedPoint2D::new(0, 0)),
|
||||
screen: size,
|
||||
screen_avail: size,
|
||||
hidpi_factor: TypedScale::new(2.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ResourceReader(Box<ReadFileTrait + Send + Sync>);
|
||||
|
||||
impl resources::ResourceReaderMethods for ResourceReader {
|
||||
fn read(&self, file: Resource) -> Vec<u8> {
|
||||
let file = match file {
|
||||
Resource::Preferences => "prefs.json",
|
||||
Resource::BluetoothBlocklist => "gatt_blocklist.txt",
|
||||
Resource::DomainList => "public_domains.txt",
|
||||
Resource::HstsPreloadList => "hsts_preload.json",
|
||||
Resource::SSLCertificates => "certs",
|
||||
Resource::BadCertHTML => "badcert.html",
|
||||
Resource::NetErrorHTML => "neterror.html",
|
||||
Resource::UserAgentCSS => "user-agent.css",
|
||||
Resource::ServoCSS => "servo.css",
|
||||
Resource::PresentationalHintsCSS => "presentational-hints.css",
|
||||
Resource::QuirksModeCSS => "quirks-mode.css",
|
||||
Resource::RippyPNG => "rippy.png",
|
||||
};
|
||||
debug!("ResourceReader::read({})", file);
|
||||
self.0.readfile(file)
|
||||
}
|
||||
fn sandbox_access_files_dirs(&self) -> Vec<PathBuf> {
|
||||
vec![]
|
||||
}
|
||||
fn sandbox_access_files(&self) -> Vec<PathBuf> {
|
||||
vec![]
|
||||
}
|
||||
}
|
261
ports/libsimpleservo/src/capi.rs
Normal file
261
ports/libsimpleservo/src/capi.rs
Normal file
|
@ -0,0 +1,261 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use api::{self, EventLoopWaker, ServoGlue, SERVO, HostTrait, ReadFileTrait};
|
||||
use gl_glue;
|
||||
use servo::gl;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::mem;
|
||||
use std::os::raw::c_char;
|
||||
use std::rc::Rc;
|
||||
|
||||
fn call<F>(f: F) where F: Fn(&mut ServoGlue) -> Result<(), &'static str> {
|
||||
SERVO.with(|s| {
|
||||
if let Err(error) = match s.borrow_mut().as_mut() {
|
||||
Some(ref mut s) => (f)(s),
|
||||
None => Err("Servo not available in this thread"),
|
||||
} {
|
||||
// FIXME: All C calls should have a have generic Result-like
|
||||
// return type. For now, we just panic instead of notifying
|
||||
// the embedder.
|
||||
panic!(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Callback used by Servo internals
|
||||
#[repr(C)]
|
||||
pub struct CHostCallbacks {
|
||||
pub flush: extern fn(),
|
||||
pub on_load_started: extern fn(),
|
||||
pub on_load_ended: extern fn(),
|
||||
pub on_title_changed: extern fn(title: *const c_char),
|
||||
pub on_url_changed: extern fn(url: *const c_char),
|
||||
pub on_history_changed: extern fn(can_go_back: bool, can_go_forward: bool),
|
||||
pub on_animating_changed: extern fn(animating: bool),
|
||||
}
|
||||
|
||||
/// The returned string is not freed. This will leak.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn servo_version() -> *const c_char {
|
||||
let v = api::servo_version();
|
||||
let text = CString::new(v).expect("Can't create string");
|
||||
let ptr = text.as_ptr();
|
||||
mem::forget(text);
|
||||
ptr
|
||||
}
|
||||
|
||||
fn init(
|
||||
gl: Rc<gl::Gl>,
|
||||
args: *const c_char,
|
||||
url: *const c_char,
|
||||
wakeup: extern fn(),
|
||||
readfile: extern fn(*const c_char) -> *const c_char,
|
||||
callbacks: CHostCallbacks,
|
||||
width: u32,
|
||||
height: u32) {
|
||||
let args = unsafe { CStr::from_ptr(args) };
|
||||
let args = args.to_str().expect("Can't read string").to_string();
|
||||
|
||||
let url = unsafe { CStr::from_ptr(url) };
|
||||
let url = url.to_str().map(|s| s.to_string());
|
||||
|
||||
let wakeup = Box::new(WakeupCallback::new(wakeup));
|
||||
let readfile = Box::new(ReadFileCallback::new(readfile));
|
||||
let callbacks = Box::new(HostCallbacks::new(callbacks));
|
||||
|
||||
api::init(
|
||||
gl,
|
||||
args,
|
||||
url.ok(),
|
||||
wakeup,
|
||||
readfile,
|
||||
callbacks,
|
||||
width,
|
||||
height,
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn init_with_egl(
|
||||
args: *const c_char,
|
||||
url: *const c_char,
|
||||
wakeup: extern fn(),
|
||||
readfile: extern fn(*const c_char) -> *const c_char,
|
||||
callbacks: CHostCallbacks,
|
||||
width: u32,
|
||||
height: u32) {
|
||||
let gl = gl_glue::egl::init().unwrap();
|
||||
init(gl, args, url, wakeup, readfile, callbacks, width, height)
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn init_with_gl(
|
||||
args: *const c_char,
|
||||
url: *const c_char,
|
||||
wakeup: extern fn(),
|
||||
readfile: extern fn(*const c_char) -> *const c_char,
|
||||
callbacks: CHostCallbacks,
|
||||
width: u32,
|
||||
height: u32) {
|
||||
let gl = gl_glue::gl::init().unwrap();
|
||||
init(gl, args, url, wakeup, readfile, callbacks, width, height)
|
||||
}
|
||||
|
||||
#[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 resize(width: u32, height: u32) {
|
||||
debug!("resize {}/{}", width, height);
|
||||
call(|s| s.resize(width, height));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn perform_updates() {
|
||||
debug!("perform_updates");
|
||||
call(|s| s.perform_updates());
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn load_uri(url: *const c_char) {
|
||||
debug!("load_url");
|
||||
let url = unsafe { CStr::from_ptr(url) };
|
||||
let url = url.to_str().expect("Can't read string");
|
||||
call(|s| s.load_uri(url));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn reload() {
|
||||
debug!("reload");
|
||||
call(|s| s.reload());
|
||||
}
|
||||
|
||||
#[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 i32, dy as i32, x as u32, y as u32));
|
||||
}
|
||||
|
||||
#[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 i32, dy as i32, x as u32, y as u32));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn scroll(dx: i32, dy: i32, x: i32, y: i32) {
|
||||
debug!("scroll");
|
||||
call(|s| s.scroll(dx as i32, dy as i32, x as u32, y as u32));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn click(x: i32, y: i32) {
|
||||
debug!("click");
|
||||
call(|s| s.click(x as u32, y as u32));
|
||||
}
|
||||
|
||||
pub struct WakeupCallback(extern fn());
|
||||
|
||||
impl WakeupCallback {
|
||||
fn new(callback: extern fn()) -> WakeupCallback {
|
||||
WakeupCallback(callback)
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopWaker for WakeupCallback {
|
||||
fn clone(&self) -> Box<EventLoopWaker + Send> {
|
||||
Box::new(WakeupCallback(self.0))
|
||||
}
|
||||
fn wake(&self) {
|
||||
(self.0)();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReadFileCallback(extern fn(*const c_char) -> *const c_char);
|
||||
|
||||
impl ReadFileCallback {
|
||||
fn new(callback: extern fn(*const c_char) -> *const c_char) -> ReadFileCallback {
|
||||
ReadFileCallback(callback)
|
||||
}
|
||||
}
|
||||
|
||||
impl ReadFileTrait for ReadFileCallback {
|
||||
fn readfile(&self, file: &str) -> Vec<u8> {
|
||||
debug!("readfile: {}", file);
|
||||
let file = CString::new(file).expect("Can't create string");
|
||||
let file_ptr = file.as_ptr();
|
||||
let content = (self.0)(file_ptr);
|
||||
let content = unsafe { CStr::from_ptr(content) };
|
||||
content.to_bytes().to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
struct HostCallbacks(CHostCallbacks);
|
||||
|
||||
impl HostCallbacks {
|
||||
fn new(callback: CHostCallbacks) -> HostCallbacks {
|
||||
HostCallbacks(callback)
|
||||
}
|
||||
}
|
||||
|
||||
impl HostTrait for HostCallbacks {
|
||||
fn flush(&self) {
|
||||
debug!("flush");
|
||||
(self.0.flush)();
|
||||
}
|
||||
|
||||
fn on_load_started(&self) {
|
||||
debug!("on_load_ended");
|
||||
(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: String) {
|
||||
debug!("on_title_changed");
|
||||
let title = CString::new(title).expect("Can't create string");
|
||||
let title_ptr = title.as_ptr();
|
||||
mem::forget(title);
|
||||
(self.0.on_title_changed)(title_ptr);
|
||||
}
|
||||
|
||||
fn on_url_changed(&self, url: String) {
|
||||
debug!("on_url_changed");
|
||||
let url = CString::new(url).expect("Can't create string");
|
||||
let url_ptr = url.as_ptr();
|
||||
mem::forget(url);
|
||||
(self.0.on_url_changed)(url_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);
|
||||
}
|
||||
}
|
84
ports/libsimpleservo/src/gl_glue.rs
Normal file
84
ports/libsimpleservo/src/gl_glue.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "windows"))]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub mod egl {
|
||||
use libc;
|
||||
use servo::gl::{Gl, GlesFns};
|
||||
use std::ffi::CString;
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
use std::os::raw::c_void;
|
||||
use std::rc::Rc;
|
||||
#[cfg(target_os = "windows")]
|
||||
use winapi;
|
||||
#[cfg(target_os = "windows")]
|
||||
use winapi::um::libloaderapi::{GetProcAddress, LoadLibraryA};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub type EGLNativeWindowType = winapi::shared::windef::HWND;
|
||||
#[cfg(target_os = "linux")]
|
||||
pub type EGLNativeWindowType = *const libc::c_void;
|
||||
#[cfg(target_os = "android")]
|
||||
pub type EGLNativeWindowType = *const libc::c_void;
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
pub type EGLNativeWindowType = *const libc::c_void;
|
||||
|
||||
pub type khronos_utime_nanoseconds_t = khronos_uint64_t;
|
||||
pub type khronos_uint64_t = libc::uint64_t;
|
||||
pub type khronos_ssize_t = libc::c_long;
|
||||
pub type EGLint = libc::int32_t;
|
||||
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"));
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
pub fn init() -> Result<Rc<Gl>, &'static str> {
|
||||
debug!("init_egl");
|
||||
unsafe {
|
||||
let egl = Egl;
|
||||
let d = egl.GetCurrentDisplay();
|
||||
egl.SwapInterval(d, 1);
|
||||
Ok(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
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn init() -> Result<Rc<Gl>, &'static str> {
|
||||
debug!("init_egl");
|
||||
|
||||
let dll = b"libEGL.dll\0" as &[u8];
|
||||
let dll = unsafe { LoadLibraryA(dll.as_ptr() as *const _) };
|
||||
if dll.is_null() {
|
||||
Err("Can't find libEGL.dll")
|
||||
} else {
|
||||
unsafe {
|
||||
Ok(GlesFns::load_with(|addr| {
|
||||
let addr = CString::new(addr.as_bytes()).unwrap();
|
||||
let addr = addr.as_ptr();
|
||||
GetProcAddress(dll, addr) as *const _
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
pub mod gl {
|
||||
use servo::gl::Gl;
|
||||
use std::rc::Rc;
|
||||
pub fn init() -> Result<Rc<Gl>, &'static str> {
|
||||
// FIXME: Add an OpenGL version
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
375
ports/libsimpleservo/src/jniapi.rs
Normal file
375
ports/libsimpleservo/src/jniapi.rs
Normal file
|
@ -0,0 +1,375 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use android_logger::{self, Filter};
|
||||
use api::{self, EventLoopWaker, ServoGlue, SERVO, HostTrait, ReadFileTrait};
|
||||
use gl_glue;
|
||||
use jni::{JNIEnv, JavaVM};
|
||||
use jni::objects::{GlobalRef, JClass, JObject, JString, JValue};
|
||||
use jni::sys::{jboolean, jint, jstring, JNI_TRUE};
|
||||
use log::Level;
|
||||
use std;
|
||||
use std::os::raw::c_void;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
struct HostCallbacks {
|
||||
callbacks: GlobalRef,
|
||||
jvm: JavaVM,
|
||||
}
|
||||
|
||||
fn call<F>(env: JNIEnv, f: F)
|
||||
where
|
||||
F: Fn(&mut ServoGlue) -> Result<(), &'static str>,
|
||||
{
|
||||
SERVO.with(|s| {
|
||||
if let Err(error) = match s.borrow_mut().as_mut() {
|
||||
Some(ref mut s) => (f)(s),
|
||||
None => Err("Servo not available in this thread"),
|
||||
} {
|
||||
env.throw(("java/lang/Exception", error))
|
||||
.expect("Error while throwing");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn Java_com_mozilla_servoview_NativeServo_version(env: JNIEnv, _class: JClass) -> jstring {
|
||||
let v = api::servo_version();
|
||||
let output = env.new_string(v).expect("Couldn't create java string");
|
||||
output.into_inner()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn Java_com_mozilla_servoview_NativeServo_init(
|
||||
env: JNIEnv,
|
||||
_: JClass,
|
||||
activity: JObject,
|
||||
args: JString,
|
||||
url: JString,
|
||||
wakeup_obj: JObject,
|
||||
readfile_obj: JObject,
|
||||
callbacks_obj: JObject,
|
||||
width: jint,
|
||||
height: jint,
|
||||
log: jboolean,
|
||||
) {
|
||||
if log == JNI_TRUE {
|
||||
android_logger::init_once(
|
||||
Filter::default()
|
||||
.with_min_level(Level::Debug)
|
||||
.with_allowed_module_path("simpleservo::api")
|
||||
.with_allowed_module_path("simpleservo::jniapi"),
|
||||
Some("simpleservo")
|
||||
);
|
||||
}
|
||||
|
||||
debug!("init");
|
||||
|
||||
initialize_android_glue(&env, activity);
|
||||
|
||||
let args = env.get_string(args)
|
||||
.expect("Couldn't get java string")
|
||||
.into();
|
||||
|
||||
let url = if url.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(env.get_string(url).expect("Couldn't get java string").into())
|
||||
};
|
||||
|
||||
let wakeup = Box::new(WakeupCallback::new(wakeup_obj, &env));
|
||||
let readfile = Box::new(ReadFileCallback::new(readfile_obj, &env));
|
||||
let callbacks = Box::new(HostCallbacks::new(callbacks_obj, &env));
|
||||
|
||||
gl_glue::egl::init().and_then(|gl| {
|
||||
api::init(
|
||||
gl,
|
||||
args,
|
||||
url,
|
||||
wakeup,
|
||||
readfile,
|
||||
callbacks,
|
||||
width as u32,
|
||||
height as u32)
|
||||
}).or_else(|err| {
|
||||
env.throw(("java/lang/Exception", err))
|
||||
}).unwrap();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn Java_com_mozilla_servoview_NativeServo_setBatchMode(
|
||||
env: JNIEnv,
|
||||
_: JClass,
|
||||
batch: jboolean,
|
||||
) {
|
||||
debug!("setBatchMode");
|
||||
call(env, |s| s.set_batch_mode(batch == JNI_TRUE));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn Java_com_mozilla_servoview_NativeServo_resize(
|
||||
env: JNIEnv,
|
||||
_: JClass,
|
||||
width: jint,
|
||||
height: jint,
|
||||
) {
|
||||
debug!("resize {}/{}", width, height);
|
||||
call(env, |s| s.resize(width as u32, height as u32));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn Java_com_mozilla_servoview_NativeServo_performUpdates(env: JNIEnv, _class: JClass) {
|
||||
debug!("performUpdates");
|
||||
call(env, |s| s.perform_updates());
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn Java_com_mozilla_servoview_NativeServo_loadUri(env: JNIEnv, _class: JClass, url: JString) {
|
||||
debug!("loadUri");
|
||||
let url: String = env.get_string(url).unwrap().into();
|
||||
call(env, |s| s.load_uri(&url));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn Java_com_mozilla_servoview_NativeServo_reload(env: JNIEnv, _class: JClass) {
|
||||
debug!("reload");
|
||||
call(env, |s| s.reload());
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn Java_com_mozilla_servoview_NativeServo_goBack(env: JNIEnv, _class: JClass) {
|
||||
debug!("goBack");
|
||||
call(env, |s| s.go_back());
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn Java_com_mozilla_servoview_NativeServo_goForward(env: JNIEnv, _class: JClass) {
|
||||
debug!("goForward");
|
||||
call(env, |s| s.go_forward());
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn Java_com_mozilla_servoview_NativeServo_scrollStart(
|
||||
env: JNIEnv,
|
||||
_: JClass,
|
||||
dx: jint,
|
||||
dy: jint,
|
||||
x: jint,
|
||||
y: jint,
|
||||
) {
|
||||
debug!("scrollStart");
|
||||
call(env, |s| s.scroll_start(dx as i32, dy as i32, x as u32, y as u32));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn Java_com_mozilla_servoview_NativeServo_scrollEnd(
|
||||
env: JNIEnv,
|
||||
_: JClass,
|
||||
dx: jint,
|
||||
dy: jint,
|
||||
x: jint,
|
||||
y: jint,
|
||||
) {
|
||||
debug!("scrollEnd");
|
||||
call(env, |s| s.scroll_end(dx as i32, dy as i32, x as u32, y as u32));
|
||||
}
|
||||
|
||||
|
||||
#[no_mangle]
|
||||
pub fn Java_com_mozilla_servoview_NativeServo_scroll(
|
||||
env: JNIEnv,
|
||||
_: JClass,
|
||||
dx: jint,
|
||||
dy: jint,
|
||||
x: jint,
|
||||
y: jint,
|
||||
) {
|
||||
debug!("scroll");
|
||||
call(env, |s| s.scroll(dx as i32, dy as i32, x as u32, y as u32));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn Java_com_mozilla_servoview_NativeServo_click(env: JNIEnv, _: JClass, x: jint, y: jint) {
|
||||
debug!("click");
|
||||
call(env, |s| s.click(x as u32, y as u32));
|
||||
}
|
||||
|
||||
pub struct WakeupCallback {
|
||||
callback: GlobalRef,
|
||||
jvm: Arc<JavaVM>,
|
||||
}
|
||||
|
||||
impl WakeupCallback {
|
||||
pub fn new(jobject: JObject, env: &JNIEnv) -> WakeupCallback {
|
||||
let jvm = Arc::new(env.get_java_vm().unwrap());
|
||||
WakeupCallback {
|
||||
callback: env.new_global_ref(jobject).unwrap(),
|
||||
jvm,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopWaker for WakeupCallback {
|
||||
fn clone(&self) -> Box<EventLoopWaker + Send> {
|
||||
Box::new(WakeupCallback {
|
||||
callback: self.callback.clone(),
|
||||
jvm: self.jvm.clone(),
|
||||
})
|
||||
}
|
||||
fn wake(&self) {
|
||||
debug!("wakeup");
|
||||
let env = self.jvm.attach_current_thread().unwrap();
|
||||
env.call_method(self.callback.as_obj(), "wakeup", "()V", &[])
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReadFileCallback {
|
||||
callback: Mutex<GlobalRef>,
|
||||
jvm: JavaVM,
|
||||
}
|
||||
|
||||
impl ReadFileCallback {
|
||||
pub fn new(jobject: JObject, env: &JNIEnv) -> ReadFileCallback {
|
||||
let jvm = env.get_java_vm().unwrap();
|
||||
let callback = Mutex::new(env.new_global_ref(jobject).unwrap());
|
||||
ReadFileCallback { callback, jvm }
|
||||
}
|
||||
}
|
||||
|
||||
impl ReadFileTrait for ReadFileCallback {
|
||||
fn readfile(&self, file: &str) -> Vec<u8> {
|
||||
// FIXME: we'd rather use attach_current_thread but it detaches the VM too early.
|
||||
let env = self.jvm.attach_current_thread_as_daemon().unwrap();
|
||||
let s = env.new_string(&file)
|
||||
.expect("Couldn't create java string")
|
||||
.into_inner();
|
||||
let s = JValue::from(JObject::from(s));
|
||||
let array = env.call_method(
|
||||
self.callback.lock().unwrap().as_obj(),
|
||||
"readfile",
|
||||
"(Ljava/lang/String;)[B",
|
||||
&[s],
|
||||
);
|
||||
let array = array.unwrap().l().unwrap().into_inner();
|
||||
env.convert_byte_array(array).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl HostCallbacks {
|
||||
pub fn new(jobject: JObject, env: &JNIEnv) -> HostCallbacks {
|
||||
let jvm = env.get_java_vm().unwrap();
|
||||
HostCallbacks {
|
||||
callbacks: env.new_global_ref(jobject).unwrap(),
|
||||
jvm,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HostTrait for HostCallbacks {
|
||||
fn flush(&self) {
|
||||
debug!("flush");
|
||||
let env = self.jvm.get_env().unwrap();
|
||||
env.call_method(self.callbacks.as_obj(), "flush", "()V", &[])
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn on_load_started(&self) {
|
||||
debug!("on_load_started");
|
||||
let env = self.jvm.get_env().unwrap();
|
||||
env.call_method(self.callbacks.as_obj(), "onLoadStarted", "()V", &[])
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn on_load_ended(&self) {
|
||||
debug!("on_load_ended");
|
||||
let env = self.jvm.get_env().unwrap();
|
||||
env.call_method(self.callbacks.as_obj(), "onLoadEnded", "()V", &[])
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn on_title_changed(&self, title: String) {
|
||||
debug!("on_title_changed");
|
||||
let env = self.jvm.get_env().unwrap();
|
||||
let s = env.new_string(&title)
|
||||
.expect("Couldn't create java string")
|
||||
.into_inner();
|
||||
let s = JValue::from(JObject::from(s));
|
||||
env.call_method(
|
||||
self.callbacks.as_obj(),
|
||||
"onTitleChanged",
|
||||
"(Ljava/lang/String;)V",
|
||||
&[s],
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
fn on_url_changed(&self, url: String) {
|
||||
debug!("on_url_changed");
|
||||
let env = self.jvm.get_env().unwrap();
|
||||
let s = env.new_string(&url)
|
||||
.expect("Couldn't create java string")
|
||||
.into_inner();
|
||||
let s = JValue::Object(JObject::from(s));
|
||||
env.call_method(
|
||||
self.callbacks.as_obj(),
|
||||
"onUrlChanged",
|
||||
"(Ljava/lang/String;)V",
|
||||
&[s],
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
fn on_history_changed(&self, can_go_back: bool, can_go_forward: bool) {
|
||||
debug!("on_history_changed");
|
||||
let env = self.jvm.get_env().unwrap();
|
||||
let can_go_back = JValue::Bool(can_go_back as jboolean);
|
||||
let can_go_forward = JValue::Bool(can_go_forward as jboolean);
|
||||
env.call_method(
|
||||
self.callbacks.as_obj(),
|
||||
"onHistoryChanged",
|
||||
"(ZZ)V",
|
||||
&[can_go_back, can_go_forward],
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
fn on_animating_changed(&self, animating: bool) {
|
||||
debug!("on_animating_changed");
|
||||
let env = self.jvm.get_env().unwrap();
|
||||
let animating = JValue::Bool(animating as jboolean);
|
||||
env.call_method(
|
||||
self.callbacks.as_obj(),
|
||||
"onAnimatingChanged",
|
||||
"(Z)V",
|
||||
&[animating],
|
||||
).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn initialize_android_glue(env: &JNIEnv, activity: JObject) {
|
||||
use android_injected_glue::{ANDROID_APP, ffi};
|
||||
|
||||
// From jni-rs to android_injected_glue
|
||||
|
||||
let mut app: ffi::android_app = unsafe {
|
||||
std::mem::zeroed()
|
||||
};
|
||||
let mut native_activity: ffi::ANativeActivity = unsafe {
|
||||
std::mem::zeroed()
|
||||
};
|
||||
|
||||
let clazz = Box::into_raw(Box::new(env.new_global_ref(activity).unwrap()));
|
||||
native_activity.clazz = unsafe {
|
||||
(*clazz).as_obj().into_inner() as *mut c_void
|
||||
};
|
||||
|
||||
let vm = env.get_java_vm().unwrap().get_java_vm_pointer();
|
||||
native_activity.vm = vm as *mut ffi::_JavaVM;
|
||||
|
||||
app.activity = Box::into_raw(Box::new(native_activity));
|
||||
|
||||
unsafe {
|
||||
ANDROID_APP = Box::into_raw(Box::new(app));
|
||||
}
|
||||
}
|
33
ports/libsimpleservo/src/lib.rs
Normal file
33
ports/libsimpleservo/src/lib.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
extern crate android_injected_glue;
|
||||
#[cfg(target_os = "android")]
|
||||
extern crate android_logger;
|
||||
#[cfg(target_os = "android")]
|
||||
extern crate jni;
|
||||
#[cfg(any(target_os = "android", target_os = "windows"))]
|
||||
extern crate libc;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate serde_json;
|
||||
extern crate servo;
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate winapi;
|
||||
|
||||
mod api;
|
||||
mod gl_glue;
|
||||
|
||||
// If not Android, expose the C-API
|
||||
#[cfg(not(target_os = "android"))]
|
||||
mod capi;
|
||||
#[cfg(not(target_os = "android"))]
|
||||
pub use capi::*;
|
||||
|
||||
// If Android, expose the JNI-API
|
||||
#[cfg(target_os = "android")]
|
||||
mod jniapi;
|
||||
#[cfg(target_os = "android")]
|
||||
pub use jniapi::*;
|
|
@ -29,8 +29,6 @@ max_log_level = ["log/release_max_level_info"]
|
|||
webdriver = ["libservo/webdriver"]
|
||||
energy-profiling = ["libservo/energy-profiling"]
|
||||
debugmozjs = ["libservo/debugmozjs"]
|
||||
googlevr = ["libservo/googlevr"]
|
||||
oculusvr = ["libservo/oculusvr"]
|
||||
unstable = ["libservo/unstable"]
|
||||
|
||||
[target.'cfg(not(target_os = "android"))'.dependencies]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue