mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
Auto merge of #20912 - paulrouget:androidv2, r=jdm,mortimergoro,simonsapin
Revisit how the Android port works. Fix #20855 Fix #18625 Fix #21147 Before polishing and making sure everything works fine (like the VR code, the android-x86 version, the non-android version of the lib, …), I'd like to get some early feedback on the approach. I recommend reviewing commit by commit. To test, just follow the regular steps: ``` ./mach build -d --android ./mach package --dev --android builds servo.apk ./mach install --dev --android && ./mach run --android ``` --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [ ] `./mach build -d` does not report any errors - [ ] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/20912) <!-- Reviewable:end -->
This commit is contained in:
commit
95b54ca8f3
57 changed files with 2450 additions and 915 deletions
|
@ -1,17 +1,17 @@
|
||||||
[target.arm-linux-androideabi]
|
[target.arm-linux-androideabi]
|
||||||
linker = "./ports/servo/fake-ld-arm.sh"
|
linker = "./support/android/fakeld/fake-ld-arm.sh"
|
||||||
ar = "arm-linux-androideabi-ar"
|
ar = "arm-linux-androideabi-ar"
|
||||||
|
|
||||||
[target.armv7-linux-androideabi]
|
[target.armv7-linux-androideabi]
|
||||||
linker = "./ports/servo/fake-ld-armv7.sh"
|
linker = "./support/android/fakeld/fake-ld-armv7.sh"
|
||||||
ar = "arm-linux-androideabi-ar"
|
ar = "arm-linux-androideabi-ar"
|
||||||
|
|
||||||
[target.aarch64-linux-android]
|
[target.aarch64-linux-android]
|
||||||
linker = "./ports/servo/fake-ld-arm64.sh"
|
linker = "./support/android/fakeld/fake-ld-arm64.sh"
|
||||||
ar = "aarch64-linux-android-ar"
|
ar = "aarch64-linux-android-ar"
|
||||||
|
|
||||||
[target.i686-linux-android]
|
[target.i686-linux-android]
|
||||||
linker = "./ports/servo/fake-ld-x86.sh"
|
linker = "./support/android/fakeld/fake-ld-x86.sh"
|
||||||
ar = "i686-linux-android-ar"
|
ar = "i686-linux-android-ar"
|
||||||
|
|
||||||
[target.arm-unknown-linux-gnueabihf]
|
[target.arm-unknown-linux-gnueabihf]
|
||||||
|
@ -23,4 +23,4 @@ linker = "aarch64-linux-gnu-gcc"
|
||||||
ar = "aarch64-linux-gnu-ar"
|
ar = "aarch64-linux-gnu-ar"
|
||||||
|
|
||||||
[target.'cfg(target_os=windows)']
|
[target.'cfg(target_os=windows)']
|
||||||
linker = "./ports/servo/fake-ld.cmd"
|
linker = "./support/android/fakeld/fake-ld.cmd"
|
||||||
|
|
|
@ -30,7 +30,7 @@ matrix:
|
||||||
script:
|
script:
|
||||||
# see https://github.com/servo/servo/issues/20664
|
# see https://github.com/servo/servo/issues/20664
|
||||||
#- ./mach cargo check -p compositing --no-default-features
|
#- ./mach cargo check -p compositing --no-default-features
|
||||||
- ./mach build -d --verbose
|
- ./mach build -d --verbose -p servo
|
||||||
- ./mach test-unit
|
- ./mach test-unit
|
||||||
- ./mach clean
|
- ./mach clean
|
||||||
- bash etc/ci/lockfile_changed.sh
|
- bash etc/ci/lockfile_changed.sh
|
||||||
|
|
78
Cargo.lock
generated
78
Cargo.lock
generated
|
@ -26,6 +26,21 @@ name = "android_injected_glue"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]]
|
[[package]]
|
||||||
name = "ansi_term"
|
name = "ansi_term"
|
||||||
version = "0.10.2"
|
version = "0.10.2"
|
||||||
|
@ -61,6 +76,11 @@ dependencies = [
|
||||||
"nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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]]
|
[[package]]
|
||||||
name = "atomic_refcell"
|
name = "atomic_refcell"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -353,6 +373,11 @@ name = "cc"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]]
|
[[package]]
|
||||||
name = "cexpr"
|
name = "cexpr"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
@ -454,6 +479,15 @@ name = "color_quant"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]]
|
[[package]]
|
||||||
name = "compositing"
|
name = "compositing"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
|
@ -1617,6 +1651,24 @@ dependencies = [
|
||||||
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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]]
|
[[package]]
|
||||||
name = "jpeg-decoder"
|
name = "jpeg-decoder"
|
||||||
version = "0.1.14"
|
version = "0.1.14"
|
||||||
|
@ -1844,6 +1896,22 @@ dependencies = [
|
||||||
"webvr_traits 0.0.1",
|
"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]]
|
[[package]]
|
||||||
name = "libz-sys"
|
name = "libz-sys"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
|
@ -3075,7 +3143,6 @@ dependencies = [
|
||||||
name = "servo"
|
name = "servo"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
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)",
|
"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)",
|
"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)",
|
"euclid 0.18.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -3087,7 +3154,6 @@ dependencies = [
|
||||||
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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-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)",
|
"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)",
|
"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)",
|
"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)",
|
"user32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -3242,7 +3308,6 @@ dependencies = [
|
||||||
name = "servo_config"
|
name = "servo_config"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
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)",
|
"dirs 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"embedder_traits 0.0.1",
|
"embedder_traits 0.0.1",
|
||||||
"env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -4172,11 +4237,14 @@ dependencies = [
|
||||||
"checksum alloc-no-stdlib 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b21f6ad9c9957eb5d70c3dee16d31c092b3cab339628f821766b05e6833d72b8"
|
"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_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_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 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 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 app_units 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c4720c83543de184d9f6add2fdb8e8031543497b8506620884c16e125b493c09"
|
||||||
"checksum array-init 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c3cc8456d0ae81a8c76f59e384683a601548c38949a4bfcb65dd31ded5c75ff3"
|
"checksum array-init 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c3cc8456d0ae81a8c76f59e384683a601548c38949a4bfcb65dd31ded5c75ff3"
|
||||||
"checksum arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2f0ef4a9820019a0c91d918918c93dc71d469f581a49b47ddc1d285d4270bbe2"
|
"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 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 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"
|
"checksum audio-video-metadata 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "71536082079f5ba92c274fba7c2dcd4e2f9d5c13ce6d7f8fe9acbbb258916d18"
|
||||||
|
@ -4205,6 +4273,7 @@ dependencies = [
|
||||||
"checksum bzip2-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2c5162604199bbb17690ede847eaa6120a3f33d5ab4dcc8e7c25b16d849ae79b"
|
"checksum bzip2-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2c5162604199bbb17690ede847eaa6120a3f33d5ab4dcc8e7c25b16d849ae79b"
|
||||||
"checksum caseless 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "808dab3318747be122cb31d36de18d4d1c81277a76f8332a02b81a3d73463d7f"
|
"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 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 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 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"
|
"checksum cgl 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "55e7ec0b74fe5897894cbc207092c577e87c52f8a59e8ca8d97ef37551f60a49"
|
||||||
|
@ -4216,6 +4285,7 @@ dependencies = [
|
||||||
"checksum cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "56d741ea7a69e577f6d06b36b7dff4738f680593dc27a701ffa8506b73ce28bb"
|
"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 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 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 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 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"
|
"checksum core-foundation-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b2a53cce0ddcf7e7e1f998738d757d5a3bf08bf799a180e50ebe50d298f52f5a"
|
||||||
|
@ -4312,6 +4382,8 @@ dependencies = [
|
||||||
"checksum itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b07332223953b5051bceb67e8c4700aa65291535568e1f12408c43c4a42c0394"
|
"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 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 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 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 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"
|
"checksum khronos_api 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9ef23fcc4059260c5936f638c9805ebfc87cb172fa6661d130cba7f97d58f55"
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"ports/servo",
|
"ports/servo",
|
||||||
|
"ports/libsimpleservo/",
|
||||||
"tests/unit/*",
|
"tests/unit/*",
|
||||||
]
|
]
|
||||||
default-members = [
|
default-members = [
|
||||||
"ports/servo",
|
"ports/servo",
|
||||||
|
"ports/libsimpleservo/",
|
||||||
"tests/unit/*",
|
"tests/unit/*",
|
||||||
]
|
]
|
||||||
exclude = [".cargo"]
|
exclude = [".cargo"]
|
||||||
|
|
|
@ -30,6 +30,3 @@ embedder_traits = { path = "../embedder_traits", features = ["tests"] }
|
||||||
|
|
||||||
[target.'cfg(not(target_os = "android"))'.dependencies]
|
[target.'cfg(not(target_os = "android"))'.dependencies]
|
||||||
dirs = "1.0"
|
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
|
//! For linux based platforms, it uses the XDG base directory spec but provides
|
||||||
//! similar abstractions for non-linux platforms.
|
//! 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;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))]
|
#[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();
|
let mut config_dir = ::dirs::config_dir().unwrap();
|
||||||
config_dir.push("servo");
|
config_dir.push("servo");
|
||||||
config_dir.push("default");
|
config_dir.push("default");
|
||||||
config_dir
|
Some(config_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
#[allow(unsafe_code)]
|
pub fn default_config_dir() -> Option<PathBuf> {
|
||||||
pub fn default_config_dir() -> PathBuf {
|
None
|
||||||
let dir = unsafe {
|
|
||||||
CStr::from_ptr((*android_injected_glue::get_app().activity).externalDataPath)
|
|
||||||
};
|
|
||||||
PathBuf::from(dir.to_str().unwrap())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
pub fn default_config_dir() -> PathBuf {
|
pub fn default_config_dir() -> Option<PathBuf> {
|
||||||
// FIXME: use `config_dir()` ($HOME/Library/Preferences)
|
// FIXME: use `config_dir()` ($HOME/Library/Preferences)
|
||||||
// instead of `data_dir()` ($HOME/Library/Application Support) ?
|
// instead of `data_dir()` ($HOME/Library/Application Support) ?
|
||||||
let mut config_dir = ::dirs::data_dir().unwrap();
|
let mut config_dir = ::dirs::data_dir().unwrap();
|
||||||
config_dir.push("Servo");
|
config_dir.push("Servo");
|
||||||
config_dir
|
Some(config_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[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();
|
let mut config_dir = ::dirs::config_dir().unwrap();
|
||||||
config_dir.push("Servo");
|
config_dir.push("Servo");
|
||||||
config_dir
|
Some(config_dir)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
|
|
||||||
#![deny(unsafe_code)]
|
#![deny(unsafe_code)]
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
extern crate android_injected_glue;
|
|
||||||
#[cfg(not(target_os = "android"))]
|
#[cfg(not(target_os = "android"))]
|
||||||
extern crate dirs;
|
extern crate dirs;
|
||||||
extern crate embedder_traits;
|
extern crate embedder_traits;
|
||||||
|
|
|
@ -183,13 +183,14 @@ pub fn add_user_prefs() {
|
||||||
init_user_prefs(&mut path);
|
init_user_prefs(&mut path);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let mut path = default_config_dir();
|
if let Some(mut path) = default_config_dir() {
|
||||||
if path.join("prefs.json").exists() {
|
if path.join("prefs.json").exists() {
|
||||||
init_user_prefs(&mut path);
|
init_user_prefs(&mut path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn init_user_prefs(path: &mut PathBuf) {
|
fn init_user_prefs(path: &mut PathBuf) {
|
||||||
path.push("prefs.json");
|
path.push("prefs.json");
|
||||||
|
|
|
@ -48,6 +48,7 @@ fn test_get_set_reset_extend() {
|
||||||
assert_eq!(*PREFS.get("extra.stuff"), PrefValue::Boolean(false));
|
assert_eq!(*PREFS.get("extra.stuff"), PrefValue::Boolean(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "android"))]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_default_config_dir_create_read_write() {
|
fn test_default_config_dir_create_read_write() {
|
||||||
let json_str = "{\
|
let json_str = "{\
|
||||||
|
@ -56,7 +57,7 @@ fn test_default_config_dir_create_read_write() {
|
||||||
\"shell.homepage\": \"https://google.com\"\
|
\"shell.homepage\": \"https://google.com\"\
|
||||||
}";
|
}";
|
||||||
let mut expected_json = String::new();
|
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() {
|
if !config_path.exists() {
|
||||||
fs::create_dir_all(&config_path).unwrap();
|
fs::create_dir_all(&config_path).unwrap();
|
||||||
|
|
|
@ -76,14 +76,14 @@ linux-rel-intermittent:
|
||||||
- ./mach clean-nightlies --keep 3 --force
|
- ./mach clean-nightlies --keep 3 --force
|
||||||
- ./mach clean-cargo-cache --keep 3 --force
|
- ./mach clean-cargo-cache --keep 3 --force
|
||||||
- ./etc/ci/clean_build_artifacts.sh
|
- ./etc/ci/clean_build_artifacts.sh
|
||||||
- ./mach build --release
|
- ./mach build --release -p servo
|
||||||
- ./etc/ci/check_intermittents.sh --log-raw intermittents.log
|
- ./etc/ci/check_intermittents.sh --log-raw intermittents.log
|
||||||
|
|
||||||
linux-rel-nogate:
|
linux-rel-nogate:
|
||||||
- ./mach clean-nightlies --keep 3 --force
|
- ./mach clean-nightlies --keep 3 --force
|
||||||
- ./mach clean-cargo-cache --keep 3 --force
|
- ./mach clean-cargo-cache --keep 3 --force
|
||||||
- ./etc/ci/clean_build_artifacts.sh
|
- ./etc/ci/clean_build_artifacts.sh
|
||||||
- ./mach build --release
|
- ./mach build --release -p servo
|
||||||
- python ./etc/ci/chaos_monkey_test.py
|
- python ./etc/ci/chaos_monkey_test.py
|
||||||
- env RUSTFLAGS= bash ./etc/ci/mutation_test.sh
|
- env RUSTFLAGS= bash ./etc/ci/mutation_test.sh
|
||||||
|
|
||||||
|
@ -103,11 +103,11 @@ linux-dev:
|
||||||
- ./etc/ci/clean_build_artifacts.sh
|
- ./etc/ci/clean_build_artifacts.sh
|
||||||
- ./mach test-tidy --no-progress --all
|
- ./mach test-tidy --no-progress --all
|
||||||
- ./mach test-tidy --no-progress --self-test
|
- ./mach test-tidy --no-progress --self-test
|
||||||
- ./mach build --dev
|
- ./mach build --dev -p servo
|
||||||
- ./mach test-unit
|
- ./mach test-unit
|
||||||
- python ./etc/memory_reports_over_time.py --test
|
- python ./etc/memory_reports_over_time.py --test
|
||||||
- ./mach package --dev
|
- ./mach package --dev
|
||||||
- ./mach build --dev --no-default-features --features default-except-unstable
|
- ./mach build --dev --no-default-features --features default-except-unstable -p servo
|
||||||
- bash ./etc/ci/lockfile_changed.sh
|
- bash ./etc/ci/lockfile_changed.sh
|
||||||
- bash ./etc/ci/check_no_panic.sh
|
- bash ./etc/ci/check_no_panic.sh
|
||||||
- ./etc/ci/clean_build_artifacts.sh
|
- ./etc/ci/clean_build_artifacts.sh
|
||||||
|
@ -120,7 +120,7 @@ linux-rel-wpt:
|
||||||
- ./mach clean-nightlies --keep 3 --force
|
- ./mach clean-nightlies --keep 3 --force
|
||||||
- ./mach clean-cargo-cache --keep 3 --force
|
- ./mach clean-cargo-cache --keep 3 --force
|
||||||
- ./etc/ci/clean_build_artifacts.sh
|
- ./etc/ci/clean_build_artifacts.sh
|
||||||
- ./mach build --release --with-debug-assertions
|
- ./mach build --release --with-debug-assertions -p servo
|
||||||
- ./mach test-wpt-failure
|
- ./mach test-wpt-failure
|
||||||
- ./mach test-wpt --release --processes 24 --total-chunks 2 --this-chunk 1 --log-raw test-wpt.log --log-errorsummary wpt-errorsummary.log --always-succeed
|
- ./mach test-wpt --release --processes 24 --total-chunks 2 --this-chunk 1 --log-raw test-wpt.log --log-errorsummary wpt-errorsummary.log --always-succeed
|
||||||
- ./mach filter-intermittents wpt-errorsummary.log --log-intermittents intermittents.log --log-filteredsummary filtered-wpt-errorsummary.log --tracker-api default --reporter-api default
|
- ./mach filter-intermittents wpt-errorsummary.log --log-intermittents intermittents.log --log-filteredsummary filtered-wpt-errorsummary.log --tracker-api default --reporter-api default
|
||||||
|
@ -134,7 +134,7 @@ linux-rel-css:
|
||||||
- ./mach clean-nightlies --keep 3 --force
|
- ./mach clean-nightlies --keep 3 --force
|
||||||
- ./mach clean-cargo-cache --keep 3 --force
|
- ./mach clean-cargo-cache --keep 3 --force
|
||||||
- ./etc/ci/clean_build_artifacts.sh
|
- ./etc/ci/clean_build_artifacts.sh
|
||||||
- ./mach build --release --with-debug-assertions
|
- ./mach build --release --with-debug-assertions -p servo
|
||||||
- ./mach test-wpt --release --processes 24 --total-chunks 2 --this-chunk 2 --log-raw test-wpt.log --log-errorsummary wpt-errorsummary.log --always-succeed
|
- ./mach test-wpt --release --processes 24 --total-chunks 2 --this-chunk 2 --log-raw test-wpt.log --log-errorsummary wpt-errorsummary.log --always-succeed
|
||||||
- ./mach filter-intermittents wpt-errorsummary.log --log-intermittents intermittents.log --log-filteredsummary filtered-wpt-errorsummary.log --tracker-api default --reporter-api default
|
- ./mach filter-intermittents wpt-errorsummary.log --log-intermittents intermittents.log --log-filteredsummary filtered-wpt-errorsummary.log --tracker-api default --reporter-api default
|
||||||
- bash ./etc/ci/lockfile_changed.sh
|
- bash ./etc/ci/lockfile_changed.sh
|
||||||
|
@ -144,7 +144,7 @@ linux-nightly:
|
||||||
- ./mach clean-nightlies --keep 3 --force
|
- ./mach clean-nightlies --keep 3 --force
|
||||||
- ./mach clean-cargo-cache --keep 3 --force
|
- ./mach clean-cargo-cache --keep 3 --force
|
||||||
- ./etc/ci/clean_build_artifacts.sh
|
- ./etc/ci/clean_build_artifacts.sh
|
||||||
- ./mach build --release
|
- ./mach build --release -p servo
|
||||||
- ./mach package --release
|
- ./mach package --release
|
||||||
- ./mach upload-nightly linux
|
- ./mach upload-nightly linux
|
||||||
- ./mach test-perf
|
- ./mach test-perf
|
||||||
|
|
|
@ -23,7 +23,7 @@ objdump_output = subprocess.check_output([
|
||||||
'android-toolchains', 'ndk', 'toolchains', 'arm-linux-androideabi-4.9',
|
'android-toolchains', 'ndk', 'toolchains', 'arm-linux-androideabi-4.9',
|
||||||
'prebuilt', 'linux-x86_64', 'bin', 'arm-linux-androideabi-objdump'),
|
'prebuilt', 'linux-x86_64', 'bin', 'arm-linux-androideabi-objdump'),
|
||||||
'-T',
|
'-T',
|
||||||
'target/armv7-linux-androideabi/debug/libservo.so']
|
'target/armv7-linux-androideabi/debug/libsimpleservo.so']
|
||||||
).split(b'\n')
|
).split(b'\n')
|
||||||
|
|
||||||
for line in objdump_output:
|
for line in objdump_output:
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
# except according to those terms.
|
# except according to those terms.
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
import subprocess
|
import subprocess
|
||||||
|
@ -55,16 +56,17 @@ def main(avd_name, apk_path, *args):
|
||||||
args = list(args)
|
args = list(args)
|
||||||
write_user_stylesheets(adb, args)
|
write_user_stylesheets(adb, args)
|
||||||
write_hosts_file(adb)
|
write_hosts_file(adb)
|
||||||
write_args(adb, args)
|
|
||||||
|
|
||||||
check_call(adb + ["shell", "am start com.mozilla.servo/com.mozilla.servo.MainActivity"],
|
json_params = shell_quote(json.dumps(args))
|
||||||
stdout=sys.stderr)
|
extra = "-e servoargs " + json_params
|
||||||
|
cmd = "am start " + extra + " com.mozilla.servo/com.mozilla.servo.MainActivity"
|
||||||
|
check_call(adb + ["shell", cmd], stdout=sys.stderr)
|
||||||
|
|
||||||
# Start showing logs as soon as the application starts,
|
# Start showing logs as soon as the application starts,
|
||||||
# in case they say something useful while we wait in subsequent steps.
|
# in case they say something useful while we wait in subsequent steps.
|
||||||
logcat_args = [
|
logcat_args = [
|
||||||
"--format=raw", # Print no metadata, only log messages
|
"--format=raw", # Print no metadata, only log messages
|
||||||
"RustAndroidGlueStdouterr:D", # Show (debug level) Rust stdio
|
"simpleservo:D", # Show (debug level) Rust stdio
|
||||||
"*:S", # Hide everything else
|
"*:S", # Hide everything else
|
||||||
]
|
]
|
||||||
with terminate_on_exit(adb + ["logcat"] + logcat_args) as logcat:
|
with terminate_on_exit(adb + ["logcat"] + logcat_args) as logcat:
|
||||||
|
@ -125,16 +127,6 @@ def check_call(*args, **kwargs):
|
||||||
sys.exit(exit_code)
|
sys.exit(exit_code)
|
||||||
|
|
||||||
|
|
||||||
def write_args(adb, args):
|
|
||||||
data_dir = "/sdcard/Android/data/com.mozilla.servo/files"
|
|
||||||
params_file = data_dir + "/android_params"
|
|
||||||
|
|
||||||
check_call(adb + ["shell", "mkdir -p %s" % data_dir])
|
|
||||||
check_call(adb + ["shell", "echo 'servo' > %s" % params_file])
|
|
||||||
for arg in args:
|
|
||||||
check_call(adb + ["shell", "echo %s >> %s" % (shell_quote(arg), params_file)])
|
|
||||||
|
|
||||||
|
|
||||||
def write_user_stylesheets(adb, args):
|
def write_user_stylesheets(adb, args):
|
||||||
data_dir = "/sdcard/Android/data/com.mozilla.servo/files"
|
data_dir = "/sdcard/Android/data/com.mozilla.servo/files"
|
||||||
check_call(adb + ["shell", "mkdir -p %s" % data_dir])
|
check_call(adb + ["shell", "mkdir -p %s" % data_dir])
|
||||||
|
|
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,11 +29,9 @@ max_log_level = ["log/release_max_level_info"]
|
||||||
webdriver = ["libservo/webdriver"]
|
webdriver = ["libservo/webdriver"]
|
||||||
energy-profiling = ["libservo/energy-profiling"]
|
energy-profiling = ["libservo/energy-profiling"]
|
||||||
debugmozjs = ["libservo/debugmozjs"]
|
debugmozjs = ["libservo/debugmozjs"]
|
||||||
googlevr = ["libservo/googlevr"]
|
|
||||||
oculusvr = ["libservo/oculusvr"]
|
|
||||||
unstable = ["libservo/unstable"]
|
unstable = ["libservo/unstable"]
|
||||||
|
|
||||||
[dependencies]
|
[target.'cfg(not(target_os = "android"))'.dependencies]
|
||||||
backtrace = "0.3"
|
backtrace = "0.3"
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
euclid = "0.18"
|
euclid = "0.18"
|
||||||
|
@ -44,8 +42,6 @@ libservo = {path = "../../components/servo"}
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
tinyfiledialogs = "3.0"
|
tinyfiledialogs = "3.0"
|
||||||
winit = {version = "0.16", features = ["icon_loading"]}
|
winit = {version = "0.16", features = ["icon_loading"]}
|
||||||
|
|
||||||
[target.'cfg(not(target_os = "android"))'.dependencies]
|
|
||||||
sig = "0.1"
|
sig = "0.1"
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
|
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
|
||||||
|
@ -54,10 +50,6 @@ osmesa-sys = "0.1.2"
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
x11 = "2.0.0"
|
x11 = "2.0.0"
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
|
||||||
android_injected_glue = "0.2"
|
|
||||||
servo-egl = "0.2"
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
winapi = "0.2"
|
winapi = "0.2"
|
||||||
user32-sys = "0.2"
|
user32-sys = "0.2"
|
||||||
|
|
|
@ -17,9 +17,7 @@ use servo::servo_url::ServoUrl;
|
||||||
use servo::webrender_api::ScrollLocation;
|
use servo::webrender_api::ScrollLocation;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
|
||||||
use tinyfiledialogs::{self, MessageBoxIcon};
|
use tinyfiledialogs::{self, MessageBoxIcon};
|
||||||
|
|
||||||
pub struct Browser {
|
pub struct Browser {
|
||||||
|
@ -100,7 +98,8 @@ impl Browser {
|
||||||
String::from("")
|
String::from("")
|
||||||
};
|
};
|
||||||
let title = "URL or search query";
|
let title = "URL or search query";
|
||||||
if let Some(input) = get_url_input(title, &url) {
|
let input = tinyfiledialogs::input_box(title, title, &url);
|
||||||
|
if let Some(input) = input {
|
||||||
if let Some(url) = sanitize_url(&input) {
|
if let Some(url) = sanitize_url(&input) {
|
||||||
self.event_queue.push(WindowEvent::LoadUrl(id, url));
|
self.event_queue.push(WindowEvent::LoadUrl(id, url));
|
||||||
}
|
}
|
||||||
|
@ -263,7 +262,11 @@ impl Browser {
|
||||||
self.window.set_inner_size(size);
|
self.window.set_inner_size(size);
|
||||||
}
|
}
|
||||||
EmbedderMsg::Alert(message, sender) => {
|
EmbedderMsg::Alert(message, sender) => {
|
||||||
display_alert_dialog(message.to_owned());
|
if !opts::get().headless {
|
||||||
|
let _ = thread::Builder::new().name("display alert dialog".to_owned()).spawn(move || {
|
||||||
|
tinyfiledialogs::message_box_ok("Alert!", &message, MessageBoxIcon::Warning);
|
||||||
|
}).unwrap().join().expect("Thread spawning failed");
|
||||||
|
}
|
||||||
if let Err(e) = sender.send(()) {
|
if let Err(e) = sender.send(()) {
|
||||||
let reason = format!("Failed to send Alert response: {}", e);
|
let reason = format!("Failed to send Alert response: {}", e);
|
||||||
self.event_queue.push(WindowEvent::SendError(browser_id, reason));
|
self.event_queue.push(WindowEvent::SendError(browser_id, reason));
|
||||||
|
@ -324,8 +327,7 @@ impl Browser {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
EmbedderMsg::SelectFiles(patterns, multiple_files, sender) => {
|
EmbedderMsg::SelectFiles(patterns, multiple_files, sender) => {
|
||||||
let res = match (opts::get().headless,
|
let res = match (opts::get().headless, get_selected_files(patterns, multiple_files)) {
|
||||||
platform_get_selected_files(patterns, multiple_files)) {
|
|
||||||
(true, _) | (false, None) => sender.send(None),
|
(true, _) | (false, None) => sender.send(None),
|
||||||
(false, Some(files)) => sender.send(Some(files))
|
(false, Some(files)) => sender.send(Some(files))
|
||||||
};
|
};
|
||||||
|
@ -346,31 +348,6 @@ impl Browser {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
|
||||||
fn display_alert_dialog(message: String) {
|
|
||||||
if !opts::get().headless {
|
|
||||||
let _ = thread::Builder::new().name("display alert dialog".to_owned()).spawn(move || {
|
|
||||||
tinyfiledialogs::message_box_ok("Alert!", &message, MessageBoxIcon::Warning);
|
|
||||||
}).unwrap().join().expect("Thread spawning failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
|
|
||||||
fn display_alert_dialog(_message: String) {
|
|
||||||
// tinyfiledialogs not supported on Android
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
|
|
||||||
fn get_url_input(_title: &str, _url: &str) -> Option<String> {
|
|
||||||
// tinyfiledialogs not supported on Android
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
|
||||||
fn get_url_input(title: &str, url: &str) -> Option<String> {
|
|
||||||
tinyfiledialogs::input_box(title, title, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
fn platform_get_selected_devices(devices: Vec<String>) -> Option<String> {
|
fn platform_get_selected_devices(devices: Vec<String>) -> Option<String> {
|
||||||
let picker_name = "Choose a device";
|
let picker_name = "Choose a device";
|
||||||
|
@ -403,10 +380,7 @@ fn platform_get_selected_devices(devices: Vec<String>) -> Option<String> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
fn get_selected_files(patterns: Vec<FilterPattern>, multiple_files: bool) -> Option<Vec<String>> {
|
||||||
fn platform_get_selected_files(patterns: Vec<FilterPattern>,
|
|
||||||
multiple_files: bool)
|
|
||||||
-> Option<Vec<String>> {
|
|
||||||
let picker_name = if multiple_files { "Pick files" } else { "Pick a file" };
|
let picker_name = if multiple_files { "Pick files" } else { "Pick a file" };
|
||||||
thread::Builder::new().name(picker_name.to_owned()).spawn(move || {
|
thread::Builder::new().name(picker_name.to_owned()).spawn(move || {
|
||||||
let mut filters = vec![];
|
let mut filters = vec![];
|
||||||
|
@ -426,14 +400,6 @@ fn platform_get_selected_files(patterns: Vec<FilterPattern>,
|
||||||
}).unwrap().join().expect("Thread spawning failed")
|
}).unwrap().join().expect("Thread spawning failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
|
|
||||||
fn platform_get_selected_files(_patterns: Vec<FilterPattern>,
|
|
||||||
_multiple_files: bool)
|
|
||||||
-> Option<Vec<String>> {
|
|
||||||
warn!("File picker not implemented");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sanitize_url(request: &str) -> Option<ServoUrl> {
|
fn sanitize_url(request: &str) -> Option<ServoUrl> {
|
||||||
let request = request.trim();
|
let request = request.trim();
|
||||||
ServoUrl::parse(&request).ok()
|
ServoUrl::parse(&request).ok()
|
||||||
|
|
|
@ -5,18 +5,7 @@
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
extern crate winres;
|
extern crate winres;
|
||||||
|
|
||||||
use std::env;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::process;
|
|
||||||
use std::process::{Command, Stdio};
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// build.rs is not platform-specific, so we have to check the target here.
|
|
||||||
let target = env::var("TARGET").unwrap();
|
|
||||||
if target.contains("android") {
|
|
||||||
android_main()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
let mut res = winres::WindowsResource::new();
|
let mut res = winres::WindowsResource::new();
|
||||||
|
@ -25,89 +14,3 @@ fn main() {
|
||||||
res.compile().unwrap();
|
res.compile().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
// Build up the path to the NDK compilers
|
|
||||||
// Options for host are: "linux-x86_64" "linux-x86" "darwin-x86_64" "darwin-x86"
|
|
||||||
// per: https://android.googlesource.com/platform/ndk/+/ics-mr0/docs/STANDALONE-TOOLCHAIN.html
|
|
||||||
|
|
||||||
let host = env::var("HOST").unwrap();
|
|
||||||
let google_host = match host.as_ref() {
|
|
||||||
"i686-unknown-linux-gnu" => "linux-x86",
|
|
||||||
"x86_64-apple-darwin" => "darwin-x86_64",
|
|
||||||
"x86_64-unknown-linux-gnu" => "linux-x86_64",
|
|
||||||
_ => panic!("Unknown support android cross-compile host: {}", host)
|
|
||||||
};
|
|
||||||
|
|
||||||
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"
|
|
||||||
};
|
|
||||||
|
|
||||||
let (toolchain, prefix) = if target.contains("armv7") {
|
|
||||||
let toolchain = "arm-linux-androideabi";
|
|
||||||
(toolchain.into(), toolchain.into())
|
|
||||||
} else if target.contains("i686") {
|
|
||||||
("x86".into(), target)
|
|
||||||
} else {
|
|
||||||
(target.clone(), target)
|
|
||||||
};
|
|
||||||
|
|
||||||
let toolchain_path = ndk_path.join("toolchains").join(format!("{}-4.9", toolchain)).join("prebuilt").
|
|
||||||
join(google_host);
|
|
||||||
println!("toolchain path is: {}", toolchain_path.to_str().unwrap());
|
|
||||||
|
|
||||||
// Get the output directory.
|
|
||||||
let out_dir = env::var("OUT_DIR").expect("Cargo should have set the OUT_DIR environment variable");
|
|
||||||
let directory = Path::new(&out_dir);
|
|
||||||
|
|
||||||
// compiling android_native_app_glue.c
|
|
||||||
if Command::new(toolchain_path.join("bin").join(format!("{}-gcc", prefix)))
|
|
||||||
.arg(ndk_path.join("sources").join("android").join("native_app_glue").join("android_native_app_glue.c"))
|
|
||||||
.arg("-c")
|
|
||||||
.arg("-o").arg(directory.join("android_native_app_glue.o"))
|
|
||||||
.arg("--sysroot").arg(ndk_path.join("platforms").join(platform).join(arch))
|
|
||||||
.stdout(Stdio::inherit())
|
|
||||||
.stderr(Stdio::inherit())
|
|
||||||
.status().unwrap().code().unwrap() != 0
|
|
||||||
{
|
|
||||||
println!("Error while executing gcc");
|
|
||||||
process::exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// compiling libandroid_native_app_glue.a
|
|
||||||
if Command::new(toolchain_path.join("bin").join(format!("{}-ar", prefix)))
|
|
||||||
.arg("rcs")
|
|
||||||
.arg(directory.join("libandroid_native_app_glue.a"))
|
|
||||||
.arg(directory.join("android_native_app_glue.o"))
|
|
||||||
.stdout(Stdio::inherit())
|
|
||||||
.stderr(Stdio::inherit())
|
|
||||||
.status().unwrap().code().unwrap() != 0
|
|
||||||
{
|
|
||||||
println!("Error while executing ar");
|
|
||||||
process::exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
|
@ -401,12 +401,12 @@ impl Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(target_arch = "arm", target_arch = "aarch64", target_os = "android")))]
|
#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
|
||||||
fn gl_version() -> GlRequest {
|
fn gl_version() -> GlRequest {
|
||||||
return GlRequest::Specific(Api::OpenGl, (3, 2));
|
return GlRequest::Specific(Api::OpenGl, (3, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64", target_os = "android"))]
|
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||||
fn gl_version() -> GlRequest {
|
fn gl_version() -> GlRequest {
|
||||||
GlRequest::Specific(Api::OpenGlEs, (3, 0))
|
GlRequest::Specific(Api::OpenGlEs, (3, 0))
|
||||||
}
|
}
|
||||||
|
@ -634,7 +634,6 @@ impl Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Has no effect on Android.
|
|
||||||
pub fn set_cursor(&self, cursor: CursorKind) {
|
pub fn set_cursor(&self, cursor: CursorKind) {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
WindowKind::Window(ref window, ..) => {
|
WindowKind::Window(ref window, ..) => {
|
||||||
|
|
|
@ -17,316 +17,10 @@
|
||||||
|
|
||||||
#![cfg_attr(feature = "unstable", feature(core_intrinsics))]
|
#![cfg_attr(feature = "unstable", feature(core_intrinsics))]
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(not(target_os = "android"))] include!("non_android_main.rs");
|
||||||
extern crate android_injected_glue;
|
|
||||||
extern crate backtrace;
|
|
||||||
#[macro_use] extern crate bitflags;
|
|
||||||
extern crate euclid;
|
|
||||||
#[cfg(target_os = "windows")] extern crate gdi32;
|
|
||||||
extern crate gleam;
|
|
||||||
extern crate glutin;
|
|
||||||
#[cfg(not(target_os = "android"))]
|
|
||||||
#[macro_use] extern crate lazy_static;
|
|
||||||
// The window backed by glutin
|
|
||||||
#[macro_use] extern crate log;
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "macos"))] extern crate osmesa_sys;
|
|
||||||
extern crate servo;
|
|
||||||
#[cfg(all(feature = "unstable", not(target_os = "android")))]
|
|
||||||
#[macro_use]
|
|
||||||
extern crate sig;
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
|
||||||
extern crate tinyfiledialogs;
|
|
||||||
extern crate winit;
|
|
||||||
#[cfg(target_os = "windows")] extern crate winapi;
|
|
||||||
#[cfg(target_os = "windows")] extern crate user32;
|
|
||||||
|
|
||||||
mod glutin_app;
|
|
||||||
mod resources;
|
|
||||||
|
|
||||||
use backtrace::Backtrace;
|
|
||||||
use servo::Servo;
|
|
||||||
use servo::compositing::windowing::WindowEvent;
|
|
||||||
use servo::config;
|
|
||||||
use servo::config::opts::{self, ArgumentParsingResult, parse_url_or_filename};
|
|
||||||
use servo::config::servo_version;
|
|
||||||
use servo::ipc_channel::ipc;
|
|
||||||
use servo::servo_config::prefs::PREFS;
|
|
||||||
use servo::servo_url::ServoUrl;
|
|
||||||
use std::env;
|
|
||||||
use std::panic;
|
|
||||||
use std::process;
|
|
||||||
use std::thread;
|
|
||||||
|
|
||||||
mod browser;
|
|
||||||
|
|
||||||
pub mod platform {
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
pub use platform::macos::deinit;
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
pub mod macos;
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
pub fn deinit() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "unstable", not(target_os = "android")))]
|
|
||||||
fn install_crash_handler() {
|
|
||||||
use backtrace::Backtrace;
|
|
||||||
use sig::ffi::Sig;
|
|
||||||
use std::intrinsics::abort;
|
|
||||||
use std::thread;
|
|
||||||
|
|
||||||
fn handler(_sig: i32) {
|
|
||||||
let name = thread::current()
|
|
||||||
.name()
|
|
||||||
.map(|n| format!(" for thread \"{}\"", n))
|
|
||||||
.unwrap_or("".to_owned());
|
|
||||||
println!("Stack trace{}\n{:?}", name, Backtrace::new());
|
|
||||||
unsafe {
|
|
||||||
// N.B. Using process::abort() here causes the crash handler to be
|
|
||||||
// triggered recursively.
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
signal!(Sig::SEGV, handler); // handle segfaults
|
|
||||||
signal!(Sig::ILL, handler); // handle stack overflow and unsupported CPUs
|
|
||||||
signal!(Sig::IOT, handler); // handle double panics
|
|
||||||
signal!(Sig::BUS, handler); // handle invalid memory access
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(not(feature = "unstable"), target_os = "android"))]
|
|
||||||
fn install_crash_handler() {}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
install_crash_handler();
|
|
||||||
|
|
||||||
resources::init();
|
|
||||||
|
|
||||||
if cfg!(target_os = "android") && env::var_os("HOST_FILE").is_none() {
|
|
||||||
let mut path = config::basedir::default_config_dir();
|
|
||||||
path.push("android_hosts");
|
|
||||||
env::set_var("HOST_FILE", path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the command line options and store them globally
|
|
||||||
let opts_result = opts::from_cmdline_args(&*args());
|
|
||||||
|
|
||||||
let content_process_token = if let ArgumentParsingResult::ContentProcess(token) = opts_result {
|
|
||||||
Some(token)
|
|
||||||
} else {
|
|
||||||
if opts::get().is_running_problem_test && env::var("RUST_LOG").is_err() {
|
|
||||||
env::set_var("RUST_LOG", "compositing::constellation");
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: once log-panics is released, can this be replaced by
|
|
||||||
// log_panics::init()?
|
|
||||||
panic::set_hook(Box::new(|info| {
|
|
||||||
warn!("Panic hook called.");
|
|
||||||
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 = thread::current();
|
|
||||||
let name = current_thread.name().unwrap_or("<unnamed>");
|
|
||||||
if let Some(location) = info.location() {
|
|
||||||
println!("{} (thread {}, at {}:{})",
|
|
||||||
msg,
|
|
||||||
name,
|
|
||||||
location.file(),
|
|
||||||
location.line());
|
|
||||||
} else {
|
|
||||||
println!("{} (thread {})", msg, name);
|
|
||||||
}
|
|
||||||
if env::var("RUST_BACKTRACE").is_ok() {
|
|
||||||
println!("{:?}", Backtrace::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
error!("{}", msg);
|
|
||||||
}));
|
|
||||||
|
|
||||||
setup_logging();
|
|
||||||
|
|
||||||
if let Some(token) = content_process_token {
|
|
||||||
return servo::run_content_process(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts::get().is_printing_version {
|
|
||||||
println!("{}", servo_version());
|
|
||||||
process::exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
let window = glutin_app::create_window();
|
|
||||||
|
|
||||||
let mut browser = browser::Browser::new(window.clone());
|
|
||||||
|
|
||||||
// If the url is not provided, we fallback to the homepage in PREFS,
|
|
||||||
// or a blank page in case the homepage is not set either.
|
|
||||||
let cwd = env::current_dir().unwrap();
|
|
||||||
let cmdline_url = opts::get().url.clone();
|
|
||||||
let pref_url = PREFS.get("shell.homepage").as_string()
|
|
||||||
.and_then(|str| parse_url_or_filename(&cwd, str).ok());
|
|
||||||
let blank_url = ServoUrl::parse("about:blank").ok();
|
|
||||||
|
|
||||||
let target_url = cmdline_url.or(pref_url).or(blank_url).unwrap();
|
|
||||||
|
|
||||||
let mut servo = Servo::new(window.clone());
|
|
||||||
|
|
||||||
let (sender, receiver) = ipc::channel().unwrap();
|
|
||||||
servo.handle_events(vec![WindowEvent::NewBrowser(target_url, sender)]);
|
|
||||||
let browser_id = receiver.recv().unwrap();
|
|
||||||
browser.set_browser_id(browser_id);
|
|
||||||
servo.handle_events(vec![WindowEvent::SelectBrowser(browser_id)]);
|
|
||||||
|
|
||||||
servo.setup_logging();
|
|
||||||
|
|
||||||
window.run(|| {
|
|
||||||
let win_events = window.get_events();
|
|
||||||
|
|
||||||
// FIXME: this could be handled by Servo. We don't need
|
|
||||||
// a repaint_synchronously function exposed.
|
|
||||||
let need_resize = win_events.iter().any(|e| match *e {
|
|
||||||
WindowEvent::Resize => true,
|
|
||||||
_ => false,
|
|
||||||
});
|
|
||||||
|
|
||||||
browser.handle_window_events(win_events);
|
|
||||||
|
|
||||||
let mut servo_events = servo.get_events();
|
|
||||||
loop {
|
|
||||||
browser.handle_servo_events(servo_events);
|
|
||||||
servo.handle_events(browser.get_events());
|
|
||||||
if browser.shutdown_requested() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
servo_events = servo.get_events();
|
|
||||||
if servo_events.is_empty() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if need_resize {
|
|
||||||
servo.repaint_synchronously();
|
|
||||||
}
|
|
||||||
false
|
|
||||||
});
|
|
||||||
|
|
||||||
servo.deinit();
|
|
||||||
|
|
||||||
platform::deinit()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
fn setup_logging() {
|
pub fn main() {
|
||||||
// Piping logs from stdout/stderr to logcat happens in android_injected_glue.
|
println!("Cannot start /ports/servo/ on Android. \
|
||||||
env::set_var("RUST_LOG", "error");
|
Use /support/android/apk/ + /ports/libsimpleservo/ instead");
|
||||||
|
|
||||||
unsafe { android_injected_glue::ffi::app_dummy() };
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "android"))]
|
|
||||||
fn setup_logging() {}
|
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
/// Attempt to read parameters from a file since they are not passed to us in Android environments.
|
|
||||||
/// The first line should be the "servo" argument and the last should be the URL to load.
|
|
||||||
/// Blank lines and those beginning with a '#' are ignored.
|
|
||||||
/// Each line should be a separate parameter as would be parsed by the shell.
|
|
||||||
/// For example, "servo -p 10 http://en.wikipedia.org/wiki/Rust" would take 4 lines.
|
|
||||||
fn args() -> Vec<String> {
|
|
||||||
use std::error::Error;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{BufRead, BufReader};
|
|
||||||
|
|
||||||
let mut params_file = config::basedir::default_config_dir();
|
|
||||||
params_file.push("android_params");
|
|
||||||
match File::open(params_file.to_str().unwrap()) {
|
|
||||||
Ok(f) => {
|
|
||||||
let mut vec = Vec::new();
|
|
||||||
let file = BufReader::new(&f);
|
|
||||||
for line in file.lines() {
|
|
||||||
let l = line.unwrap().trim().to_owned();
|
|
||||||
// ignore blank lines and those that start with a '#'
|
|
||||||
match l.is_empty() || l.as_bytes()[0] == b'#' {
|
|
||||||
true => (),
|
|
||||||
false => vec.push(l),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vec
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
debug!("Failed to open params file '{}': {}",
|
|
||||||
params_file.to_str().unwrap(),
|
|
||||||
Error::description(&e));
|
|
||||||
vec!["servo".to_owned(), "http://en.wikipedia.org/wiki/Rust".to_owned()]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "android"))]
|
|
||||||
fn args() -> Vec<String> {
|
|
||||||
env::args().collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
#[no_mangle]
|
|
||||||
#[inline(never)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
pub extern "C" fn android_main(app: *mut ()) {
|
|
||||||
android_injected_glue::android_main2(app as *mut _, move |_, _| main());
|
|
||||||
}
|
|
||||||
|
|
||||||
// These functions aren't actually called. They are here as a link
|
|
||||||
// hack because Skia references them.
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn glBindVertexArrayOES(_array: usize)
|
|
||||||
{
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn glDeleteVertexArraysOES(_n: isize, _arrays: *const ())
|
|
||||||
{
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn glGenVertexArraysOES(_n: isize, _arrays: *const ())
|
|
||||||
{
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn glRenderbufferStorageMultisampleIMG(_: isize, _: isize, _: isize, _: isize, _: isize)
|
|
||||||
{
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn glFramebufferTexture2DMultisampleIMG(_: isize, _: isize, _: isize, _: isize, _: isize, _: isize)
|
|
||||||
{
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn glDiscardFramebufferEXT(_: isize, _: isize, _: *const ())
|
|
||||||
{
|
|
||||||
unimplemented!()
|
|
||||||
}
|
}
|
||||||
|
|
243
ports/servo/non_android_main.rs
Normal file
243
ports/servo/non_android_main.rs
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
/* 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 backtrace;
|
||||||
|
#[macro_use] extern crate bitflags;
|
||||||
|
extern crate euclid;
|
||||||
|
#[cfg(target_os = "windows")] extern crate gdi32;
|
||||||
|
extern crate gleam;
|
||||||
|
extern crate glutin;
|
||||||
|
#[macro_use] extern crate lazy_static;
|
||||||
|
// The window backed by glutin
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "macos"))] extern crate osmesa_sys;
|
||||||
|
extern crate servo;
|
||||||
|
#[cfg(feature = "unstable")]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate sig;
|
||||||
|
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
||||||
|
extern crate tinyfiledialogs;
|
||||||
|
extern crate winit;
|
||||||
|
#[cfg(target_os = "windows")] extern crate winapi;
|
||||||
|
#[cfg(target_os = "windows")] extern crate user32;
|
||||||
|
|
||||||
|
mod glutin_app;
|
||||||
|
mod resources;
|
||||||
|
|
||||||
|
use backtrace::Backtrace;
|
||||||
|
use servo::Servo;
|
||||||
|
use servo::compositing::windowing::WindowEvent;
|
||||||
|
use servo::config::opts::{self, ArgumentParsingResult, parse_url_or_filename};
|
||||||
|
use servo::config::servo_version;
|
||||||
|
use servo::ipc_channel::ipc;
|
||||||
|
use servo::servo_config::prefs::PREFS;
|
||||||
|
use servo::servo_url::ServoUrl;
|
||||||
|
use std::env;
|
||||||
|
use std::panic;
|
||||||
|
use std::process;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
mod browser;
|
||||||
|
|
||||||
|
pub mod platform {
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
pub use platform::macos::deinit;
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
pub mod macos;
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
pub fn deinit() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable")]
|
||||||
|
fn install_crash_handler() {
|
||||||
|
use backtrace::Backtrace;
|
||||||
|
use sig::ffi::Sig;
|
||||||
|
use std::intrinsics::abort;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
fn handler(_sig: i32) {
|
||||||
|
let name = thread::current()
|
||||||
|
.name()
|
||||||
|
.map(|n| format!(" for thread \"{}\"", n))
|
||||||
|
.unwrap_or("".to_owned());
|
||||||
|
println!("Stack trace{}\n{:?}", name, Backtrace::new());
|
||||||
|
unsafe {
|
||||||
|
// N.B. Using process::abort() here causes the crash handler to be
|
||||||
|
// triggered recursively.
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signal!(Sig::SEGV, handler); // handle segfaults
|
||||||
|
signal!(Sig::ILL, handler); // handle stack overflow and unsupported CPUs
|
||||||
|
signal!(Sig::IOT, handler); // handle double panics
|
||||||
|
signal!(Sig::BUS, handler); // handle invalid memory access
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
install_crash_handler();
|
||||||
|
|
||||||
|
resources::init();
|
||||||
|
|
||||||
|
// Parse the command line options and store them globally
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
let opts_result = opts::from_cmdline_args(&args);
|
||||||
|
|
||||||
|
let content_process_token = if let ArgumentParsingResult::ContentProcess(token) = opts_result {
|
||||||
|
Some(token)
|
||||||
|
} else {
|
||||||
|
if opts::get().is_running_problem_test && env::var("RUST_LOG").is_err() {
|
||||||
|
env::set_var("RUST_LOG", "compositing::constellation");
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: once log-panics is released, can this be replaced by
|
||||||
|
// log_panics::init()?
|
||||||
|
panic::set_hook(Box::new(|info| {
|
||||||
|
warn!("Panic hook called.");
|
||||||
|
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 = thread::current();
|
||||||
|
let name = current_thread.name().unwrap_or("<unnamed>");
|
||||||
|
if let Some(location) = info.location() {
|
||||||
|
println!("{} (thread {}, at {}:{})",
|
||||||
|
msg,
|
||||||
|
name,
|
||||||
|
location.file(),
|
||||||
|
location.line());
|
||||||
|
} else {
|
||||||
|
println!("{} (thread {})", msg, name);
|
||||||
|
}
|
||||||
|
if env::var("RUST_BACKTRACE").is_ok() {
|
||||||
|
println!("{:?}", Backtrace::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
error!("{}", msg);
|
||||||
|
}));
|
||||||
|
|
||||||
|
if let Some(token) = content_process_token {
|
||||||
|
return servo::run_content_process(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts::get().is_printing_version {
|
||||||
|
println!("{}", servo_version());
|
||||||
|
process::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let window = glutin_app::create_window();
|
||||||
|
|
||||||
|
let mut browser = browser::Browser::new(window.clone());
|
||||||
|
|
||||||
|
// If the url is not provided, we fallback to the homepage in PREFS,
|
||||||
|
// or a blank page in case the homepage is not set either.
|
||||||
|
let cwd = env::current_dir().unwrap();
|
||||||
|
let cmdline_url = opts::get().url.clone();
|
||||||
|
let pref_url = PREFS.get("shell.homepage").as_string()
|
||||||
|
.and_then(|str| parse_url_or_filename(&cwd, str).ok());
|
||||||
|
let blank_url = ServoUrl::parse("about:blank").ok();
|
||||||
|
|
||||||
|
let target_url = cmdline_url.or(pref_url).or(blank_url).unwrap();
|
||||||
|
|
||||||
|
let mut servo = Servo::new(window.clone());
|
||||||
|
|
||||||
|
let (sender, receiver) = ipc::channel().unwrap();
|
||||||
|
servo.handle_events(vec![WindowEvent::NewBrowser(target_url, sender)]);
|
||||||
|
let browser_id = receiver.recv().unwrap();
|
||||||
|
browser.set_browser_id(browser_id);
|
||||||
|
servo.handle_events(vec![WindowEvent::SelectBrowser(browser_id)]);
|
||||||
|
|
||||||
|
servo.setup_logging();
|
||||||
|
|
||||||
|
window.run(|| {
|
||||||
|
let win_events = window.get_events();
|
||||||
|
|
||||||
|
// FIXME: this could be handled by Servo. We don't need
|
||||||
|
// a repaint_synchronously function exposed.
|
||||||
|
let need_resize = win_events.iter().any(|e| match *e {
|
||||||
|
WindowEvent::Resize => true,
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
|
||||||
|
browser.handle_window_events(win_events);
|
||||||
|
|
||||||
|
let mut servo_events = servo.get_events();
|
||||||
|
loop {
|
||||||
|
browser.handle_servo_events(servo_events);
|
||||||
|
servo.handle_events(browser.get_events());
|
||||||
|
if browser.shutdown_requested() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
servo_events = servo.get_events();
|
||||||
|
if servo_events.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if need_resize {
|
||||||
|
servo.repaint_synchronously();
|
||||||
|
}
|
||||||
|
false
|
||||||
|
});
|
||||||
|
|
||||||
|
servo.deinit();
|
||||||
|
|
||||||
|
platform::deinit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// These functions aren't actually called. They are here as a link
|
||||||
|
// hack because Skia references them.
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn glBindVertexArrayOES(_array: usize)
|
||||||
|
{
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn glDeleteVertexArraysOES(_n: isize, _arrays: *const ())
|
||||||
|
{
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn glGenVertexArraysOES(_n: isize, _arrays: *const ())
|
||||||
|
{
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn glRenderbufferStorageMultisampleIMG(_: isize, _: isize, _: isize, _: isize, _: isize)
|
||||||
|
{
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn glFramebufferTexture2DMultisampleIMG(_: isize, _: isize, _: isize, _: isize, _: isize, _: isize)
|
||||||
|
{
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn glDiscardFramebufferEXT(_: isize, _: isize, _: *const ())
|
||||||
|
{
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,15 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use servo::embedder_traits::resources::{self, Resource};
|
use servo::embedder_traits::resources::{self, Resource};
|
||||||
|
use std::env;
|
||||||
|
use std::fs;
|
||||||
|
use std::io;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref CMD_RESOURCE_DIR: Mutex<Option<String>> = Mutex::new(None);
|
||||||
|
}
|
||||||
|
|
||||||
struct ResourceReader;
|
struct ResourceReader;
|
||||||
|
|
||||||
|
@ -27,34 +36,6 @@ pub fn init() {
|
||||||
resources::set(Box::new(ResourceReader));
|
resources::set(Box::new(ResourceReader));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "android"))]
|
|
||||||
mod not_android {
|
|
||||||
use servo::embedder_traits::resources::{self, Resource};
|
|
||||||
use std::env;
|
|
||||||
use std::fs;
|
|
||||||
use std::io;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref CMD_RESOURCE_DIR: Mutex<Option<String>> = Mutex::new(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl resources::ResourceReaderMethods for super::ResourceReader {
|
|
||||||
fn read(&self, file: Resource) -> Vec<u8> {
|
|
||||||
let file = super::filename(file);
|
|
||||||
let mut path = resources_dir_path().expect("Can't find resources directory");
|
|
||||||
path.push(file);
|
|
||||||
fs::read(path).expect("Can't read file")
|
|
||||||
}
|
|
||||||
fn sandbox_access_files_dirs(&self) -> Vec<PathBuf> {
|
|
||||||
vec![resources_dir_path().expect("Can't find resources directory")]
|
|
||||||
}
|
|
||||||
fn sandbox_access_files(&self) -> Vec<PathBuf> {
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resources_dir_path() -> io::Result<PathBuf> {
|
fn resources_dir_path() -> io::Result<PathBuf> {
|
||||||
// This needs to be called before the process is sandboxed
|
// This needs to be called before the process is sandboxed
|
||||||
// as we only give permission to read inside the resources directory,
|
// as we only give permission to read inside the resources directory,
|
||||||
|
@ -87,26 +68,18 @@ mod not_android {
|
||||||
*dir = Some(path.to_str().unwrap().to_owned());
|
*dir = Some(path.to_str().unwrap().to_owned());
|
||||||
Ok(path)
|
Ok(path)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
impl resources::ResourceReaderMethods for ResourceReader {
|
||||||
mod android {
|
|
||||||
use android_injected_glue::load_asset;
|
|
||||||
use servo::embedder_traits::resources::{self, Resource};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
impl resources::ResourceReaderMethods for super::ResourceReader {
|
|
||||||
fn read(&self, file: Resource) -> Vec<u8> {
|
fn read(&self, file: Resource) -> Vec<u8> {
|
||||||
let file = super::filename(file);
|
let file = filename(file);
|
||||||
load_asset(file).unwrap_or_else(|_| {
|
let mut path = resources_dir_path().expect("Can't find resources directory");
|
||||||
panic!("Can't load asset");
|
path.push(file);
|
||||||
})
|
fs::read(path).expect("Can't read file")
|
||||||
}
|
}
|
||||||
fn sandbox_access_files_dirs(&self) -> Vec<PathBuf> {
|
fn sandbox_access_files_dirs(&self) -> Vec<PathBuf> {
|
||||||
vec![]
|
vec![resources_dir_path().expect("Can't find resources directory")]
|
||||||
}
|
}
|
||||||
fn sandbox_access_files(&self) -> Vec<PathBuf> {
|
fn sandbox_access_files(&self) -> Vec<PathBuf> {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ class MachCommands(CommandBase):
|
||||||
ndk = "android-ndk-r12b-{system}-{arch}"
|
ndk = "android-ndk-r12b-{system}-{arch}"
|
||||||
tools = "sdk-tools-{system}-4333796"
|
tools = "sdk-tools-{system}-4333796"
|
||||||
|
|
||||||
sdk_build_tools = "25.0.2"
|
sdk_build_tools = "27.0.3"
|
||||||
emulator_images = [
|
emulator_images = [
|
||||||
("servo-arm", "25", "google_apis;armeabi-v7a"),
|
("servo-arm", "25", "google_apis;armeabi-v7a"),
|
||||||
("servo-x86", "28", "google_apis;x86"),
|
("servo-x86", "28", "google_apis;x86"),
|
||||||
|
|
|
@ -185,7 +185,6 @@ class MachCommands(CommandBase):
|
||||||
debug_mozjs=False, params=None, with_debug_assertions=False):
|
debug_mozjs=False, params=None, with_debug_assertions=False):
|
||||||
|
|
||||||
opts = params or []
|
opts = params or []
|
||||||
opts += ["--manifest-path", self.servo_manifest()]
|
|
||||||
|
|
||||||
if android is None:
|
if android is None:
|
||||||
android = self.config["build"]["android"]
|
android = self.config["build"]["android"]
|
||||||
|
@ -420,4 +419,4 @@ class MachCommands(CommandBase):
|
||||||
opts += ["-v"]
|
opts += ["-v"]
|
||||||
opts += params
|
opts += params
|
||||||
return check_call(["cargo", "clean"] + opts,
|
return check_call(["cargo", "clean"] + opts,
|
||||||
env=self.build_env(), cwd=self.servo_crate(), verbose=verbose)
|
env=self.build_env(), cwd=self.ports_servo_crate(), verbose=verbose)
|
||||||
|
|
|
@ -330,15 +330,24 @@ class CommandBase(object):
|
||||||
else:
|
else:
|
||||||
return path.join(self.context.topdir, "target")
|
return path.join(self.context.topdir, "target")
|
||||||
|
|
||||||
|
def get_apk_path(self, release):
|
||||||
|
base_path = self.get_target_dir()
|
||||||
|
base_path = path.join(base_path, self.config["android"]["target"])
|
||||||
|
apk_name = "servoapp.apk"
|
||||||
|
build_type = "release" if release else "debug"
|
||||||
|
return path.join(base_path, build_type, apk_name)
|
||||||
|
|
||||||
def get_binary_path(self, release, dev, android=False):
|
def get_binary_path(self, release, dev, android=False):
|
||||||
# TODO(autrilla): this function could still use work - it shouldn't
|
# TODO(autrilla): this function could still use work - it shouldn't
|
||||||
# handle quitting, or printing. It should return the path, or an error.
|
# handle quitting, or printing. It should return the path, or an error.
|
||||||
base_path = self.get_target_dir()
|
base_path = self.get_target_dir()
|
||||||
|
|
||||||
|
binary_name = "servo" + BIN_SUFFIX
|
||||||
|
|
||||||
if android:
|
if android:
|
||||||
base_path = path.join(base_path, self.config["android"]["target"])
|
base_path = path.join(base_path, self.config["android"]["target"])
|
||||||
|
binary_name = "libsimpleservo.so"
|
||||||
|
|
||||||
binary_name = "servo" + BIN_SUFFIX
|
|
||||||
release_path = path.join(base_path, "release", binary_name)
|
release_path = path.join(base_path, "release", binary_name)
|
||||||
dev_path = path.join(base_path, "debug", binary_name)
|
dev_path = path.join(base_path, "debug", binary_name)
|
||||||
|
|
||||||
|
@ -594,10 +603,10 @@ class CommandBase(object):
|
||||||
|
|
||||||
return env
|
return env
|
||||||
|
|
||||||
def servo_crate(self):
|
def ports_servo_crate(self):
|
||||||
return path.join(self.context.topdir, "ports", "servo")
|
return path.join(self.context.topdir, "ports", "servo")
|
||||||
|
|
||||||
def servo_manifest(self):
|
def ports_servo_manifest(self):
|
||||||
return path.join(self.context.topdir, "ports", "servo", "Cargo.toml")
|
return path.join(self.context.topdir, "ports", "servo", "Cargo.toml")
|
||||||
|
|
||||||
def servo_features(self):
|
def servo_features(self):
|
||||||
|
|
|
@ -42,7 +42,7 @@ from servo.util import delete
|
||||||
|
|
||||||
PACKAGES = {
|
PACKAGES = {
|
||||||
'android': [
|
'android': [
|
||||||
'target/armv7-linux-androideabi/release/servo.apk',
|
'target/armv7-linux-androideabi/release/servoapp.apk',
|
||||||
],
|
],
|
||||||
'linux': [
|
'linux': [
|
||||||
'target/release/servo-tech-demo.tar.gz',
|
'target/release/servo-tech-demo.tar.gz',
|
||||||
|
@ -421,7 +421,7 @@ class PackageCommands(CommandBase):
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
if android:
|
if android:
|
||||||
pkg_path = binary_path + ".apk"
|
pkg_path = self.get_apk_path(release)
|
||||||
exec_command = [self.android_adb_path(env)]
|
exec_command = [self.android_adb_path(env)]
|
||||||
if emulator and usb:
|
if emulator and usb:
|
||||||
print("Cannot install to both emulator and USB at the same time.")
|
print("Cannot install to both emulator and USB at the same time.")
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
from __future__ import print_function, unicode_literals
|
from __future__ import print_function, unicode_literals
|
||||||
|
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import os.path as path
|
import os.path as path
|
||||||
import subprocess
|
import subprocess
|
||||||
|
@ -34,6 +35,13 @@ def read_file(filename, if_exists=False):
|
||||||
return f.read()
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
|
# Copied from Python 3.3+'s shlex.quote()
|
||||||
|
def shell_quote(arg):
|
||||||
|
# use single quotes, and put single quotes into double quotes
|
||||||
|
# the string $'b is then quoted as '$'"'"'b'
|
||||||
|
return "'" + arg.replace("'", "'\"'\"'") + "'"
|
||||||
|
|
||||||
|
|
||||||
@CommandProvider
|
@CommandProvider
|
||||||
class PostBuildCommands(CommandBase):
|
class PostBuildCommands(CommandBase):
|
||||||
@Command('run',
|
@Command('run',
|
||||||
|
@ -88,15 +96,11 @@ class PostBuildCommands(CommandBase):
|
||||||
return
|
return
|
||||||
script = [
|
script = [
|
||||||
"am force-stop com.mozilla.servo",
|
"am force-stop com.mozilla.servo",
|
||||||
"echo servo >/sdcard/Android/data/com.mozilla.servo/files/android_params"
|
|
||||||
]
|
]
|
||||||
for param in params:
|
json_params = shell_quote(json.dumps(params))
|
||||||
|
extra = "-e servoargs " + json_params
|
||||||
script += [
|
script += [
|
||||||
"echo '%s' >>/sdcard/Android/data/com.mozilla.servo/files/android_params"
|
"am start " + extra + " com.mozilla.servo/com.mozilla.servo.MainActivity",
|
||||||
% param.replace("'", "\\'")
|
|
||||||
]
|
|
||||||
script += [
|
|
||||||
"am start com.mozilla.servo/com.mozilla.servo.MainActivity",
|
|
||||||
"sleep 0.5",
|
"sleep 0.5",
|
||||||
"echo Servo PID: $(pidof com.mozilla.servo)",
|
"echo Servo PID: $(pidof com.mozilla.servo)",
|
||||||
"exit"
|
"exit"
|
||||||
|
@ -257,7 +261,7 @@ class PostBuildCommands(CommandBase):
|
||||||
copy2(full_name, destination)
|
copy2(full_name, destination)
|
||||||
|
|
||||||
return self.call_rustup_run(
|
return self.call_rustup_run(
|
||||||
["cargo", "doc", "--manifest-path", self.servo_manifest()] + params,
|
["cargo", "doc", "--manifest-path", self.ports_servo_manifest()] + params,
|
||||||
env=self.build_env()
|
env=self.build_env()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -278,7 +278,7 @@ class MachCommands(CommandBase):
|
||||||
|
|
||||||
features = self.servo_features()
|
features = self.servo_features()
|
||||||
if len(packages) > 0 or len(in_crate_packages) > 0:
|
if len(packages) > 0 or len(in_crate_packages) > 0:
|
||||||
args = ["cargo", "bench" if bench else "test", "--manifest-path", self.servo_manifest()]
|
args = ["cargo", "bench" if bench else "test", "--manifest-path", self.ports_servo_manifest()]
|
||||||
for crate in packages:
|
for crate in packages:
|
||||||
args += ["-p", "%s_tests" % crate]
|
args += ["-p", "%s_tests" % crate]
|
||||||
for crate in in_crate_packages:
|
for crate in in_crate_packages:
|
||||||
|
@ -576,7 +576,7 @@ class MachCommands(CommandBase):
|
||||||
def test_android_startup(self, release, dev):
|
def test_android_startup(self, release, dev):
|
||||||
html = """
|
html = """
|
||||||
<script>
|
<script>
|
||||||
console.log("JavaScript is running!")
|
window.alert("JavaScript is running!")
|
||||||
</script>
|
</script>
|
||||||
"""
|
"""
|
||||||
url = "data:text/html;base64," + html.encode("base64").replace("\n", "")
|
url = "data:text/html;base64," + html.encode("base64").replace("\n", "")
|
||||||
|
@ -607,8 +607,7 @@ class MachCommands(CommandBase):
|
||||||
env = self.build_env(target=target)
|
env = self.build_env(target=target)
|
||||||
os.environ["PATH"] = env["PATH"]
|
os.environ["PATH"] = env["PATH"]
|
||||||
assert self.handle_android_target(target)
|
assert self.handle_android_target(target)
|
||||||
binary_path = self.get_binary_path(release, dev, android=True)
|
apk = self.get_apk_path(release)
|
||||||
apk = binary_path + ".apk"
|
|
||||||
|
|
||||||
py = path.join(self.context.topdir, "etc", "run_in_headless_android_emulator.py")
|
py = path.join(self.context.topdir, "etc", "run_in_headless_android_emulator.py")
|
||||||
return [py, avd, apk]
|
return [py, avd, apk]
|
||||||
|
|
|
@ -1,248 +0,0 @@
|
||||||
package com.mozilla.servo;
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.content.pm.ActivityInfo;
|
|
||||||
import android.content.pm.PackageInfo;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Environment;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.PowerManager;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.SurfaceView;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.webkit.URLUtil;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
|
|
||||||
import com.mozilla.servo.BuildConfig;
|
|
||||||
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.lang.System;
|
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.zip.ZipEntry;
|
|
||||||
import java.util.zip.ZipFile;
|
|
||||||
|
|
||||||
|
|
||||||
public class MainActivity extends android.app.NativeActivity {
|
|
||||||
private static final String LOGTAG = "Servo";
|
|
||||||
private boolean mFullScreen = false;
|
|
||||||
private static final String PREF_KEY_RESOURCES_SYNC = "res_sync_v";
|
|
||||||
|
|
||||||
static {
|
|
||||||
Log.i(LOGTAG, "Loading the NativeActivity");
|
|
||||||
|
|
||||||
// Libaries should be loaded in reverse dependency order
|
|
||||||
System.loadLibrary("c++_shared");
|
|
||||||
System.loadLibrary("servo");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
final Intent intent = getIntent();
|
|
||||||
if (intent != null && intent.getAction().equals(Intent.ACTION_VIEW)) {
|
|
||||||
final String url = intent.getDataString();
|
|
||||||
if (url != null && URLUtil.isValidUrl(url)) {
|
|
||||||
Log.d(LOGTAG, "Received url "+url);
|
|
||||||
set_url(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONObject preferences = loadPreferences();
|
|
||||||
|
|
||||||
boolean keepScreenOn = false;
|
|
||||||
|
|
||||||
if (BuildConfig.FLAVOR.contains("vr")) {
|
|
||||||
// Force fullscreen mode and keep screen on for VR experiences.
|
|
||||||
keepScreenOn = true;
|
|
||||||
mFullScreen = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
keepScreenOn = preferences.optBoolean("shell.keep_screen_on.enabled", false);
|
|
||||||
mFullScreen = !preferences.optBoolean("shell.native-titlebar.enabled", false);
|
|
||||||
|
|
||||||
String orientation = preferences.optString("shell.native-orientation", "both");
|
|
||||||
|
|
||||||
// Handle orientation preference
|
|
||||||
if (orientation.equalsIgnoreCase("portrait")) {
|
|
||||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
|
||||||
}
|
|
||||||
else if (orientation.equalsIgnoreCase("landscape")) {
|
|
||||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
// NativeActivity ignores the Android view hierarchy because it’s designed
|
|
||||||
// to take over the surface from the window to directly draw to it.
|
|
||||||
// Inject a custom SurfaceView in order to support adding views on top of the browser.
|
|
||||||
// (e.g. Native Banners, Daydream GVRLayout or other native views)
|
|
||||||
getWindow().takeSurface(null);
|
|
||||||
FrameLayout layout = new FrameLayout(this);
|
|
||||||
layout.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
|
|
||||||
FrameLayout.LayoutParams.MATCH_PARENT));
|
|
||||||
SurfaceView nativeSurface = new SurfaceView(this);
|
|
||||||
nativeSurface.getHolder().addCallback(this);
|
|
||||||
layout.addView(nativeSurface, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
|
|
||||||
setContentView(layout);
|
|
||||||
|
|
||||||
// Handle keep screen on preference
|
|
||||||
if (keepScreenOn) {
|
|
||||||
keepScreenOn();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle full screen preference
|
|
||||||
if (mFullScreen) {
|
|
||||||
addFullScreenListener();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStop() {
|
|
||||||
Log.d(LOGTAG, "onStop");
|
|
||||||
super.onStop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPause() {
|
|
||||||
Log.d(LOGTAG, "onPause");
|
|
||||||
super.onPause();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
Log.d(LOGTAG, "onPause");
|
|
||||||
if (mFullScreen) {
|
|
||||||
setFullScreen();
|
|
||||||
}
|
|
||||||
super.onResume();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onWindowFocusChanged(boolean hasFocus) {
|
|
||||||
super.onWindowFocusChanged(hasFocus);
|
|
||||||
if (hasFocus && mFullScreen) {
|
|
||||||
setFullScreen();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// keep the device's screen turned on and bright.
|
|
||||||
private void keepScreenOn() {
|
|
||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dim toolbar and make the view fullscreen
|
|
||||||
private void setFullScreen() {
|
|
||||||
int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
|
||||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
|
||||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
|
||||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // Hides navigation bar
|
|
||||||
| View.SYSTEM_UI_FLAG_FULLSCREEN; // Hides status bar
|
|
||||||
if( android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
|
|
||||||
flags |= getImmersiveFlag();
|
|
||||||
} else {
|
|
||||||
flags |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
|
|
||||||
}
|
|
||||||
getWindow().getDecorView().setSystemUiVisibility(flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
@TargetApi(19)
|
|
||||||
private int getImmersiveFlag() {
|
|
||||||
return View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addFullScreenListener() {
|
|
||||||
View decorView = getWindow().getDecorView();
|
|
||||||
decorView.setOnSystemUiVisibilityChangeListener(
|
|
||||||
new View.OnSystemUiVisibilityChangeListener() {
|
|
||||||
public void onSystemUiVisibilityChange(int visibility) {
|
|
||||||
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
|
|
||||||
setFullScreen();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private String loadAsset(String file) {
|
|
||||||
InputStream is = null;
|
|
||||||
BufferedReader reader = null;
|
|
||||||
try {
|
|
||||||
is = getAssets().open(file);
|
|
||||||
reader = new BufferedReader(new InputStreamReader(is));
|
|
||||||
StringBuilder result = new StringBuilder();
|
|
||||||
String line;
|
|
||||||
while ((line = reader.readLine()) != null) {
|
|
||||||
result.append(line).append('\n');
|
|
||||||
}
|
|
||||||
return result.toString();
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(LOGTAG, Log.getStackTraceString(e));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
try {
|
|
||||||
if (reader != null) {
|
|
||||||
reader.close();
|
|
||||||
}
|
|
||||||
if (is != null) {
|
|
||||||
is.close();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOGTAG, Log.getStackTraceString(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private JSONObject loadPreferences() {
|
|
||||||
String json = loadAsset("prefs.json");
|
|
||||||
try {
|
|
||||||
return new JSONObject(json);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
Log.e(LOGTAG, Log.getStackTraceString(e));
|
|
||||||
return new JSONObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getAppDataDir() {
|
|
||||||
File file = getExternalFilesDir(null);
|
|
||||||
return file != null ? file : getFilesDir();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void set_url(String url) {
|
|
||||||
try {
|
|
||||||
File file = new File(getAppDataDir() + "/android_params");
|
|
||||||
if (!file.exists()) {
|
|
||||||
file.createNewFile();
|
|
||||||
}
|
|
||||||
PrintStream out = new PrintStream(new FileOutputStream(file, false));
|
|
||||||
out.println("# The first line here should be the \"servo\" argument (without quotes) and the");
|
|
||||||
out.println("# last should be the URL to load.");
|
|
||||||
out.println("# Blank lines and those beginning with a '#' are ignored.");
|
|
||||||
out.println("# Each line should be a separate parameter as would be parsed by the shell.");
|
|
||||||
out.println("# For example, \"servo -p 10 http://en.wikipedia.org/wiki/Rust\" would take 4");
|
|
||||||
out.println("# lines (the \"-p\" and \"10\" are separate even though they are related).");
|
|
||||||
out.println("servo");
|
|
||||||
out.println("-w");
|
|
||||||
String absUrl = url.replace("file:///storage/emulated/0/", "/sdcard/");
|
|
||||||
out.println(absUrl);
|
|
||||||
out.flush();
|
|
||||||
out.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOGTAG, Log.getStackTraceString(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,9 +2,10 @@
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
|
google()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:2.2.3'
|
classpath 'com.android.tools.build:gradle:3.1.3'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +15,7 @@ allprojects {
|
||||||
flatDir {
|
flatDir {
|
||||||
dirs rootDir.absolutePath + "/../../../target/android_aar"
|
dirs rootDir.absolutePath + "/../../../target/android_aar"
|
||||||
}
|
}
|
||||||
|
google()
|
||||||
}
|
}
|
||||||
|
|
||||||
buildDir = rootDir.absolutePath + "/../../../target/gradle"
|
buildDir = rootDir.absolutePath + "/../../../target/gradle"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
#Wed Jul 11 13:23:08 CEST 2018
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
|
||||||
|
|
|
@ -18,5 +18,5 @@ MY_LOCAL_PATH := $(call my-dir)
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_PATH:= $(SERVO_TARGET_DIR)
|
LOCAL_PATH:= $(SERVO_TARGET_DIR)
|
||||||
LOCAL_MODULE := servo
|
LOCAL_MODULE := servo
|
||||||
LOCAL_SRC_FILES := libservo.so
|
LOCAL_SRC_FILES := libsimpleservo.so
|
||||||
include $(PREBUILT_SHARED_LIBRARY)
|
include $(PREBUILT_SHARED_LIBRARY)
|
||||||
|
|
|
@ -6,18 +6,15 @@ import java.util.regex.Matcher
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 25
|
compileSdkVersion 27
|
||||||
buildToolsVersion "25.0.2"
|
buildToolsVersion '27.0.3'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.mozilla.servo"
|
applicationId "com.mozilla.servo"
|
||||||
minSdkVersion 18
|
minSdkVersion 18
|
||||||
targetSdkVersion 25
|
targetSdkVersion 27
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0.0"
|
versionName "1.0.0"
|
||||||
jackOptions {
|
|
||||||
enabled true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
@ -33,6 +30,13 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
flavorDimensions "default"
|
||||||
|
|
||||||
productFlavors {
|
productFlavors {
|
||||||
main {
|
main {
|
||||||
}
|
}
|
||||||
|
@ -147,22 +151,24 @@ android {
|
||||||
|
|
||||||
// Define apk output directory
|
// Define apk output directory
|
||||||
applicationVariants.all { variant ->
|
applicationVariants.all { variant ->
|
||||||
variant.outputs.each { output ->
|
variant.outputs.all { output ->
|
||||||
def name = variant.buildType.name
|
Pattern pattern = Pattern.compile(/^[\w\d]+([A-Z][\w\d]+)(Debug|Release)/)
|
||||||
output.outputFile = new File(getApkPath(isDebug(name), getArch(name)))
|
Matcher matcher = pattern.matcher(variant.name)
|
||||||
|
if (!matcher.find()) {
|
||||||
|
throw "Invalid variant name for output"
|
||||||
|
}
|
||||||
|
def arch = matcher.group(1)
|
||||||
|
def debug = variant.name.contains("Debug")
|
||||||
|
def path = "../../../../../" + getSubTargetDir(debug, arch) + "/servoapp.apk";
|
||||||
|
outputFileName = new File(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call our custom NDK Build task using flavor parameters
|
// Call our custom NDK Build task using flavor parameters
|
||||||
tasks.all {
|
tasks.all {
|
||||||
compileTask ->
|
compileTask ->
|
||||||
// Parse architecture name from gradle task name:
|
Pattern pattern = Pattern.compile(/^compile[A-Z][\w\d]+([A-Z][\w\d]+)(Debug|Release)/)
|
||||||
// Examples: transformJackWithJackForMainArmv7Release, transformJackWithJackForOculusvrArmv7Release
|
Matcher matcher = pattern.matcher(compileTask.name)
|
||||||
Pattern pattern = Pattern.compile(/^transformJackWithJackFor[A-Z][\w\d]+([A-Z][\w\d]+)(Debug|Release)/);
|
|
||||||
Matcher matcher = pattern.matcher(compileTask.name);
|
|
||||||
// You can use this alternative pattern when jackCompiler is disabled
|
|
||||||
// Pattern pattern = Pattern.compile(/^compile([\w\d]+)(Debug|Release)/);
|
|
||||||
// Matcher matcher = pattern.matcher(compileTask.name);
|
|
||||||
if (!matcher.find()) {
|
if (!matcher.find()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -190,14 +196,13 @@ dependencies {
|
||||||
def deps = [
|
def deps = [
|
||||||
new ServoDependency("blurdroid.jar", "blurdroid")
|
new ServoDependency("blurdroid.jar", "blurdroid")
|
||||||
]
|
]
|
||||||
|
|
||||||
// Iterate all build types and dependencies
|
// Iterate all build types and dependencies
|
||||||
// For each dependency call the proper compile command and set the correct dependency path
|
// For each dependency call the proper implementation command and set the correct dependency path
|
||||||
def list = ['arm', 'armv7', 'arm64', 'x86']
|
def list = ['arm', 'armv7', 'arm64', 'x86']
|
||||||
for (arch in list) {
|
for (arch in list) {
|
||||||
for (debug in [true, false]) {
|
for (debug in [true, false]) {
|
||||||
String basePath = getTargetDir(debug, arch) + "/build"
|
String basePath = getTargetDir(debug, arch) + "/build"
|
||||||
String cmd = arch + (debug ? "Debug" : "Release") + "Compile"
|
String cmd = arch + (debug ? "Debug" : "Release") + "Implementation"
|
||||||
|
|
||||||
for (ServoDependency dep : deps) {
|
for (ServoDependency dep : deps) {
|
||||||
String path = findDependencyPath(basePath, dep.fileName, dep.folderFilter)
|
String path = findDependencyPath(basePath, dep.fileName, dep.folderFilter)
|
||||||
|
@ -207,35 +212,27 @@ dependencies {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
googlevrImplementation 'com.google.vr:sdk-base:1.140.0'
|
||||||
googlevrCompile 'com.google.vr:sdk-base:1.70.0'
|
googlevrImplementation(name: 'GVRService', ext: 'aar')
|
||||||
googlevrCompile(name:'GVRService', ext:'aar')
|
oculusvrImplementation(name: 'OVRService', ext: 'aar')
|
||||||
oculusvrCompile(name:'OVRService', ext:'aar')
|
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility methods
|
// Utility methods
|
||||||
String getTargetDir(boolean debug, String arch) {
|
String getTargetDir(boolean debug, String arch) {
|
||||||
def basePath = project.rootDir.getParentFile().getParentFile().getParentFile().absolutePath
|
def basePath = project.rootDir.getParentFile().getParentFile().getParentFile().absolutePath
|
||||||
return basePath + '/target/' + getRustTarget(arch) + '/' + (debug ? 'debug' : 'release')
|
return basePath + '/target/' + getSubTargetDir(debug, arch)
|
||||||
}
|
}
|
||||||
|
|
||||||
String getApkPath(boolean debug, String arch) {
|
String getSubTargetDir(boolean debug, String arch) {
|
||||||
return getTargetDir(debug, arch) + '/servo.apk'
|
return getRustTarget(arch) + '/' + (debug ? 'debug' : 'release')
|
||||||
}
|
}
|
||||||
|
|
||||||
String getJniLibsPath(boolean debug, String arch) {
|
String getJniLibsPath(boolean debug, String arch) {
|
||||||
return getTargetDir(debug, arch) + '/apk/jniLibs'
|
return getTargetDir(debug, arch) + '/apk/jniLibs'
|
||||||
}
|
}
|
||||||
|
|
||||||
String getArch(String buildType) {
|
static String getRustTarget(String arch) {
|
||||||
return buildType.replaceAll(/(Debug|Release)/, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isDebug(String buildType) {
|
|
||||||
return buildType.contains("Debug")
|
|
||||||
}
|
|
||||||
|
|
||||||
String getRustTarget(String arch) {
|
|
||||||
switch (arch.toLowerCase()) {
|
switch (arch.toLowerCase()) {
|
||||||
case 'arm' : return 'arm-linux-androideabi'
|
case 'arm' : return 'arm-linux-androideabi'
|
||||||
case 'armv7' : return 'armv7-linux-androideabi'
|
case 'armv7' : return 'armv7-linux-androideabi'
|
||||||
|
@ -245,7 +242,7 @@ String getRustTarget(String arch) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String getNDKAbi(String arch) {
|
static String getNDKAbi(String arch) {
|
||||||
switch (arch.toLowerCase()) {
|
switch (arch.toLowerCase()) {
|
||||||
case 'arm' : return 'armeabi'
|
case 'arm' : return 'armeabi'
|
||||||
case 'armv7' : return 'armeabi-v7a'
|
case 'armv7' : return 'armeabi-v7a'
|
||||||
|
@ -283,7 +280,7 @@ String getNdkDir() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// folderFilter can be used to improve search performance
|
// folderFilter can be used to improve search performance
|
||||||
String findDependencyPath(String basePath, String filename, String folderFilter) {
|
static String findDependencyPath(String basePath, String filename, String folderFilter) {
|
||||||
File path = new File(basePath);
|
File path = new File(basePath);
|
||||||
if (!path.exists()) {
|
if (!path.exists()) {
|
||||||
return ''
|
return ''
|
||||||
|
@ -307,7 +304,7 @@ String findDependencyPath(String basePath, String filename, String folderFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ServoDependency {
|
class ServoDependency {
|
||||||
public ServoDependency(String fileName, String folderFilter = null) {
|
ServoDependency(String fileName, String folderFilter = null) {
|
||||||
this.fileName = fileName;
|
this.fileName = fileName;
|
||||||
this.folderFilter = folderFilter;
|
this.folderFilter = folderFilter;
|
||||||
}
|
}
|
|
@ -3,13 +3,13 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto"
|
||||||
package="com.mozilla.servo">
|
package="com.mozilla.servo">
|
||||||
|
|
||||||
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
|
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
|
||||||
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
|
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
|
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
<application android:label="Servo" android:icon="@mipmap/servo">
|
<application android:label="Servo" android:icon="@mipmap/servo">
|
||||||
<activity android:name=".MainActivity"
|
<activity android:name=".MainActivity"
|
|
@ -0,0 +1,160 @@
|
||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
|
|
||||||
|
package com.mozilla.servo;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.system.ErrnoException;
|
||||||
|
import android.system.Os;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
import android.webkit.URLUtil;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
|
||||||
|
import com.mozilla.servoview.ServoView;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class MainActivity extends Activity implements ServoView.Client {
|
||||||
|
|
||||||
|
private static final String LOGTAG = "MainActivity";
|
||||||
|
|
||||||
|
ServoView mServoView;
|
||||||
|
Button mBackButton;
|
||||||
|
Button mFwdButton;
|
||||||
|
Button mReloadButton;
|
||||||
|
Button mStopButton;
|
||||||
|
EditText mUrlField;
|
||||||
|
ProgressBar mProgressBar;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
|
mServoView = (ServoView)findViewById(R.id.servoview);
|
||||||
|
mBackButton = (Button)findViewById(R.id.backbutton);
|
||||||
|
mFwdButton = (Button)findViewById(R.id.forwardbutton);
|
||||||
|
mReloadButton = (Button)findViewById(R.id.reloadbutton);
|
||||||
|
mStopButton = (Button)findViewById(R.id.stopbutton);
|
||||||
|
mUrlField = (EditText)findViewById(R.id.urlfield);
|
||||||
|
mProgressBar = (ProgressBar)findViewById(R.id.progressbar);
|
||||||
|
|
||||||
|
mServoView.setClient(this);
|
||||||
|
mBackButton.setEnabled(false);
|
||||||
|
mFwdButton.setEnabled(false);
|
||||||
|
|
||||||
|
mServoView.requestFocus();
|
||||||
|
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||||
|
File sdcard = getExternalFilesDir("");
|
||||||
|
String host = sdcard.toPath().resolve("android_hosts").toString();
|
||||||
|
try {
|
||||||
|
Os.setenv("HOST_FILE", host, false);
|
||||||
|
} catch (ErrnoException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String args = getIntent().getStringExtra("servoargs");
|
||||||
|
if (args != null) {
|
||||||
|
mServoView.setServoArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
setupUrlField();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupUrlField() {
|
||||||
|
mUrlField.setOnEditorActionListener((v, actionId, event) -> {
|
||||||
|
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||||
|
loadUrlFromField();
|
||||||
|
mServoView.requestFocus();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
mUrlField.setOnFocusChangeListener((v, hasFocus) -> {
|
||||||
|
if(v.getId() == R.id.urlfield && !hasFocus) {
|
||||||
|
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
assert imm != null;
|
||||||
|
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadUrlFromField() {
|
||||||
|
String text = mUrlField.getText().toString();
|
||||||
|
text = text.trim();
|
||||||
|
String uri;
|
||||||
|
|
||||||
|
if (text.contains(" ") || !text.contains(".")) {
|
||||||
|
uri = URLUtil.composeSearchUrl(text, "https://duckduckgo.com/html/?q=%s", "%s");
|
||||||
|
} else {
|
||||||
|
uri = URLUtil.guessUrl(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
mServoView.loadUri(Uri.parse(uri));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onReloadClicked(View v) {
|
||||||
|
mServoView.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onBackClicked(View v) {
|
||||||
|
mServoView.goBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onForwardClicked(View v) {
|
||||||
|
mServoView.goForward();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStopClicked(View v) {
|
||||||
|
mServoView.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadStarted() {
|
||||||
|
mReloadButton.setEnabled(false);
|
||||||
|
mStopButton.setEnabled(true);
|
||||||
|
mReloadButton.setVisibility(View.GONE);
|
||||||
|
mStopButton.setVisibility(View.VISIBLE);
|
||||||
|
mProgressBar.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadEnded() {
|
||||||
|
mReloadButton.setEnabled(true);
|
||||||
|
mStopButton.setEnabled(false);
|
||||||
|
mReloadButton.setVisibility(View.VISIBLE);
|
||||||
|
mStopButton.setVisibility(View.GONE);
|
||||||
|
mProgressBar.setVisibility(View.INVISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTitleChanged(String title) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUrlChanged(String url) {
|
||||||
|
mUrlField.setText(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHistoryChanged(boolean canGoBack, boolean canGoForward) {
|
||||||
|
mBackButton.setEnabled(canGoBack);
|
||||||
|
mFwdButton.setEnabled(canGoForward);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
|
package com.mozilla.servoview;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps /ports/libsimpleservo API
|
||||||
|
*/
|
||||||
|
public class NativeServo {
|
||||||
|
public native String version();
|
||||||
|
public native void init(Activity activity,
|
||||||
|
String args,
|
||||||
|
String url,
|
||||||
|
WakeupCallback wakeup,
|
||||||
|
ReadFileCallback readfile,
|
||||||
|
ServoCallbacks callbacks,
|
||||||
|
int width, int height, boolean log);
|
||||||
|
public native void setBatchMode(boolean mode);
|
||||||
|
public native void performUpdates();
|
||||||
|
public native void resize(int width, int height);
|
||||||
|
public native void reload();
|
||||||
|
public native void stop();
|
||||||
|
public native void goBack();
|
||||||
|
public native void goForward();
|
||||||
|
public native void loadUri(String uri);
|
||||||
|
public native void scrollStart(int dx, int dy, int x, int y);
|
||||||
|
public native void scroll(int dx, int dy, int x, int y);
|
||||||
|
public native void scrollEnd(int dx, int dy, int x, int y);
|
||||||
|
public native void click(int x, int y);
|
||||||
|
|
||||||
|
NativeServo() {
|
||||||
|
System.loadLibrary("c++_shared");
|
||||||
|
System.loadLibrary("simpleservo");
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ReadFileCallback {
|
||||||
|
byte[] readfile(String file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface WakeupCallback {
|
||||||
|
void wakeup();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ServoCallbacks {
|
||||||
|
void flush();
|
||||||
|
void onLoadStarted();
|
||||||
|
void onLoadEnded();
|
||||||
|
void onTitleChanged(String title);
|
||||||
|
void onUrlChanged(String url);
|
||||||
|
void onHistoryChanged(boolean canGoBack, boolean canGoForward);
|
||||||
|
void onAnimatingChanged(boolean animating);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
|
package com.mozilla.servoview;
|
||||||
|
|
||||||
|
import android.opengl.GLES31;
|
||||||
|
import android.opengl.GLSurfaceView;
|
||||||
|
import javax.microedition.khronos.egl.EGLConfig;
|
||||||
|
import javax.microedition.khronos.opengles.GL10;
|
||||||
|
|
||||||
|
public class ServoGLRenderer implements GLSurfaceView.Renderer {
|
||||||
|
|
||||||
|
private final ServoView mView;
|
||||||
|
|
||||||
|
ServoGLRenderer(ServoView view) {
|
||||||
|
mView = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
|
||||||
|
mView.onGLReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDrawFrame(GL10 unused) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onSurfaceChanged(GL10 unused, int width, int height) {
|
||||||
|
GLES31.glViewport(0, 0, width, height);
|
||||||
|
mView.onSurfaceResized(width, height);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,281 @@
|
||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
|
package com.mozilla.servoview;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.AssetManager;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.opengl.GLSurfaceView;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Choreographer;
|
||||||
|
import android.view.GestureDetector;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.widget.OverScroller;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public class ServoView extends GLSurfaceView implements GestureDetector.OnGestureListener, Choreographer.FrameCallback {
|
||||||
|
|
||||||
|
private static final String LOGTAG = "ServoView";
|
||||||
|
|
||||||
|
private Activity mActivity;
|
||||||
|
private NativeServo mServo;
|
||||||
|
private Client mClient = null;
|
||||||
|
private Uri mInitialUri = null;
|
||||||
|
private boolean mAnimating;
|
||||||
|
private String mServoArgs = "";
|
||||||
|
|
||||||
|
public ServoView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
mActivity = (Activity) context;
|
||||||
|
setFocusable(true);
|
||||||
|
setFocusableInTouchMode(true);
|
||||||
|
setWillNotCacheDrawing(false);
|
||||||
|
setEGLContextClientVersion(3);
|
||||||
|
setEGLConfigChooser(8, 8, 8, 8, 24, 0);
|
||||||
|
ServoGLRenderer mRenderer = new ServoGLRenderer(this);
|
||||||
|
setRenderer(mRenderer);
|
||||||
|
mServo = new NativeServo();
|
||||||
|
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
|
||||||
|
initGestures(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServoArgs(String args) {
|
||||||
|
mServoArgs = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reload() {
|
||||||
|
queueEvent(() -> mServo.reload());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void goBack() {
|
||||||
|
queueEvent(() -> mServo.goBack());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void goForward() {
|
||||||
|
queueEvent(() -> mServo.goForward());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
queueEvent(() -> mServo.stop());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onSurfaceResized(int width, int height) {
|
||||||
|
queueEvent(() -> mServo.resize(width, height));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadUri(Uri uri) {
|
||||||
|
if (mServo != null) {
|
||||||
|
queueEvent(() -> mServo.loadUri(uri.toString()));
|
||||||
|
} else {
|
||||||
|
mInitialUri = uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WakeupCallback implements NativeServo.WakeupCallback {
|
||||||
|
public void wakeup() {
|
||||||
|
queueEvent(() -> mServo.performUpdates());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReadFileCallback implements NativeServo.ReadFileCallback {
|
||||||
|
public byte[] readfile(String file) {
|
||||||
|
try {
|
||||||
|
AssetManager assetMgr = getContext().getResources().getAssets();
|
||||||
|
InputStream stream = assetMgr.open(file);
|
||||||
|
byte[] bytes = new byte[stream.available()];
|
||||||
|
stream.read(bytes);
|
||||||
|
stream.close();
|
||||||
|
return bytes;
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(LOGTAG, e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ServoCallbacks implements NativeServo.ServoCallbacks {
|
||||||
|
public void flush() {
|
||||||
|
requestRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onLoadStarted() {
|
||||||
|
if (mClient != null) {
|
||||||
|
post(() -> mClient.onLoadStarted());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onLoadEnded() {
|
||||||
|
if (mClient != null) {
|
||||||
|
post(() -> mClient.onLoadEnded());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTitleChanged(final String title) {
|
||||||
|
if (mClient != null) {
|
||||||
|
post(() -> mClient.onTitleChanged(title));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onUrlChanged(final String url) {
|
||||||
|
if (mClient != null) {
|
||||||
|
post(() -> mClient.onUrlChanged(url));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onHistoryChanged(final boolean canGoBack, final boolean canGoForward) {
|
||||||
|
if (mClient != null) {
|
||||||
|
post(() -> mClient.onHistoryChanged(canGoBack, canGoForward));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAnimatingChanged(final boolean animating) {
|
||||||
|
if (!mAnimating && animating) {
|
||||||
|
post(() -> Choreographer.getInstance().postFrameCallback(ServoView.this));
|
||||||
|
}
|
||||||
|
mAnimating = animating;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onGLReady() {
|
||||||
|
final WakeupCallback c1 = new WakeupCallback();
|
||||||
|
final ReadFileCallback c2 = new ReadFileCallback();
|
||||||
|
final ServoCallbacks c3 = new ServoCallbacks();
|
||||||
|
final boolean showLogs = true;
|
||||||
|
int width = getWidth();
|
||||||
|
int height = getHeight();
|
||||||
|
queueEvent(() -> {
|
||||||
|
String uri = mInitialUri == null ? null : mInitialUri.toString();
|
||||||
|
mServo.init(mActivity, mServoArgs, uri, c1, c2, c3, width, height, showLogs);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Client {
|
||||||
|
void onLoadStarted();
|
||||||
|
void onLoadEnded();
|
||||||
|
void onTitleChanged(String title);
|
||||||
|
void onUrlChanged(String url);
|
||||||
|
void onHistoryChanged(boolean canGoBack, boolean canGoForward);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClient(Client client) {
|
||||||
|
mClient = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scroll and click
|
||||||
|
|
||||||
|
private GestureDetector mGestureDetector;
|
||||||
|
private OverScroller mScroller;
|
||||||
|
private int mLastX = 0;
|
||||||
|
private int mCurX = 0;
|
||||||
|
private int mLastY = 0;
|
||||||
|
private int mCurY = 0;
|
||||||
|
private boolean mFlinging;
|
||||||
|
|
||||||
|
private void initGestures(Context context) {
|
||||||
|
mGestureDetector = new GestureDetector(context, this);
|
||||||
|
mScroller = new OverScroller(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doFrame(long frameTimeNanos) {
|
||||||
|
|
||||||
|
if (mScroller.isFinished() && mFlinging) {
|
||||||
|
mFlinging = false;
|
||||||
|
queueEvent(() -> mServo.scrollEnd(0, 0, mCurX, mCurY));
|
||||||
|
if (!mAnimating) {
|
||||||
|
// Not scrolling. Not animating. We don't need to schedule
|
||||||
|
// another frame.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mFlinging) {
|
||||||
|
mScroller.computeScrollOffset();
|
||||||
|
mCurX = mScroller.getCurrX();
|
||||||
|
mCurY = mScroller.getCurrY();
|
||||||
|
}
|
||||||
|
|
||||||
|
int dx = mCurX - mLastX;
|
||||||
|
int dy = mCurY - mLastY;
|
||||||
|
|
||||||
|
mLastX = mCurX;
|
||||||
|
mLastY = mCurY;
|
||||||
|
|
||||||
|
if (dx != 0 || dy != 0) {
|
||||||
|
queueEvent(() -> mServo.scroll(dx, dy, mCurX, mCurY));
|
||||||
|
} else {
|
||||||
|
if (mAnimating) {
|
||||||
|
requestRender();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Choreographer.getInstance().postFrameCallback(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||||
|
mFlinging = true;
|
||||||
|
|
||||||
|
// FIXME: magic values
|
||||||
|
// https://github.com/servo/servo/issues/20361
|
||||||
|
int mPageWidth = 80000;
|
||||||
|
int mPageHeight = 80000;
|
||||||
|
mCurX = velocityX < 0 ? mPageWidth : 0;
|
||||||
|
mLastX = mCurX;
|
||||||
|
mCurY = velocityY < 0 ? mPageHeight : 0;
|
||||||
|
mLastY = mCurY;
|
||||||
|
mScroller.fling(mCurX, mCurY, (int)velocityX, (int)velocityY, 0, mPageWidth, 0, mPageHeight);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onDown(MotionEvent e) {
|
||||||
|
mScroller.forceFinished(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onTouchEvent(final MotionEvent e) {
|
||||||
|
mGestureDetector.onTouchEvent(e);
|
||||||
|
|
||||||
|
int action = e.getActionMasked();
|
||||||
|
switch(action) {
|
||||||
|
case (MotionEvent.ACTION_DOWN):
|
||||||
|
mCurX = (int)e.getX();
|
||||||
|
mLastX = mCurX;
|
||||||
|
mCurY = (int)e.getY();
|
||||||
|
mLastY = mCurY;
|
||||||
|
mScroller.forceFinished(true);
|
||||||
|
queueEvent(() -> mServo.scrollStart(0, 0, mCurX, mCurY));
|
||||||
|
Choreographer.getInstance().postFrameCallback(this);
|
||||||
|
return true;
|
||||||
|
case (MotionEvent.ACTION_MOVE):
|
||||||
|
mCurX = (int)e.getX();
|
||||||
|
mCurY = (int)e.getY();
|
||||||
|
return true;
|
||||||
|
case (MotionEvent.ACTION_UP):
|
||||||
|
case (MotionEvent.ACTION_CANCEL):
|
||||||
|
if (!mFlinging) {
|
||||||
|
queueEvent(() -> mServo.scrollEnd(0, 0, mCurX, mCurY));
|
||||||
|
Choreographer.getInstance().removeFrameCallback(this);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onSingleTapUp(MotionEvent e) {
|
||||||
|
queueEvent(() -> mServo.click((int)e.getX(), (int)e.getY()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onLongPress(MotionEvent e) { }
|
||||||
|
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { return true; }
|
||||||
|
public void onShowPress(MotionEvent e) { }
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context="com.mozilla.servo.MainActivity">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/backbutton"
|
||||||
|
style="@android:style/Widget.Material.Button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:onClick="onBackClicked"
|
||||||
|
android:text="Back"
|
||||||
|
android:textSize="10sp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/forwardbutton"
|
||||||
|
style="@android:style/Widget.Material.Button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:onClick="onForwardClicked"
|
||||||
|
android:text="Fwd"
|
||||||
|
android:textSize="10sp" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/urlfield"
|
||||||
|
style="@android:style/Widget.Material.EditText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ems="10"
|
||||||
|
android:hint="URL or Search"
|
||||||
|
android:inputType="textUri"
|
||||||
|
android:selectAllOnFocus="true"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:imeOptions="actionDone"/>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progressbar"
|
||||||
|
style="@android:style/Widget.ProgressBar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:indeterminate="true" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/stopbutton"
|
||||||
|
style="@android:style/Widget.Material.Button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:onClick="onStopClicked"
|
||||||
|
android:text="Stop"
|
||||||
|
android:textSize="10sp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/reloadbutton"
|
||||||
|
style="@android:style/Widget.Material.Button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:onClick="onReloadClicked"
|
||||||
|
android:text="Rld"
|
||||||
|
android:textSize="10sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.mozilla.servoview.ServoView
|
||||||
|
android:id="@+id/servoview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:focusable="true"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</android.support.constraint.ConstraintLayout>
|
Before Width: | Height: | Size: 509 KiB After Width: | Height: | Size: 509 KiB |
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="colorPrimary">#3F51B5</color>
|
||||||
|
<color name="colorPrimaryDark">#303F9F</color>
|
||||||
|
<color name="colorAccent">#FF4081</color>
|
||||||
|
</resources>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Servo</string>
|
||||||
|
</resources>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</resources>
|
|
@ -1 +1 @@
|
||||||
include ':app'
|
include ':servoapp'
|
||||||
|
|
|
@ -8,7 +8,7 @@ set -o errexit
|
||||||
set -o nounset
|
set -o nounset
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
||||||
source ./ports/servo/fake-ld.sh
|
source ./support/android/fakeld/fake-ld.sh
|
||||||
|
|
||||||
export _GCC_PARAMS="${@}"
|
export _GCC_PARAMS="${@}"
|
||||||
call_gcc "arch-arm" "arm-linux-androideabi" "android-18" "armeabi" \
|
call_gcc "arch-arm" "arm-linux-androideabi" "android-18" "armeabi" \
|
|
@ -8,7 +8,7 @@ set -o errexit
|
||||||
set -o nounset
|
set -o nounset
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
||||||
source ./ports/servo/fake-ld.sh
|
source ./support/android/fakeld/fake-ld.sh
|
||||||
|
|
||||||
export _GCC_PARAMS="${@}"
|
export _GCC_PARAMS="${@}"
|
||||||
call_gcc "arch-arm64" "aarch64-linux-android" "android-21" "arm64-v8a"
|
call_gcc "arch-arm64" "aarch64-linux-android" "android-21" "arm64-v8a"
|
|
@ -8,7 +8,7 @@ set -o errexit
|
||||||
set -o nounset
|
set -o nounset
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
||||||
source ./ports/servo/fake-ld.sh
|
source ./support/android/fakeld/fake-ld.sh
|
||||||
|
|
||||||
export _GCC_PARAMS="${@}"
|
export _GCC_PARAMS="${@}"
|
||||||
call_gcc "arch-arm" "arm-linux-androideabi" "android-18" "armeabi-v7a" \
|
call_gcc "arch-arm" "arm-linux-androideabi" "android-18" "armeabi-v7a" \
|
|
@ -8,7 +8,7 @@ set -o errexit
|
||||||
set -o nounset
|
set -o nounset
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
||||||
source ./ports/servo/fake-ld.sh
|
source ./support/android/fakeld/fake-ld.sh
|
||||||
|
|
||||||
export _GCC_PARAMS="${@}"
|
export _GCC_PARAMS="${@}"
|
||||||
call_gcc "arch-x86" "x86" "android-18" "x86" "i686-linux-android"
|
call_gcc "arch-x86" "x86" "android-18" "x86" "i686-linux-android"
|
|
@ -33,6 +33,5 @@ call_gcc()
|
||||||
echo "targetdir: ${ANDROID_CXX_LIBS}"
|
echo "targetdir: ${ANDROID_CXX_LIBS}"
|
||||||
|
|
||||||
"${ANDROID_TOOLCHAIN}/$5-gcc" \
|
"${ANDROID_TOOLCHAIN}/$5-gcc" \
|
||||||
--sysroot="${ANDROID_SYSROOT}" -L "${ANDROID_CXX_LIBS}" ${_GCC_PARAMS} -lc++ \
|
--sysroot="${ANDROID_SYSROOT}" -L "${ANDROID_CXX_LIBS}" ${_GCC_PARAMS} -lc++
|
||||||
-o "${TARGET_DIR}/libservo.so" -shared && touch "${TARGET_DIR}/servo"
|
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue