Compare commits
10
Commits
c772e8ee46
...
359c71413d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
359c71413d
|
||
|
|
07b558eb0a | ||
|
|
8976fba9d0 | ||
|
|
5616744c04 | ||
|
|
e4bedb6dc6 | ||
|
|
534411f36f | ||
|
|
d5feda9da5 | ||
|
|
303a3aa926 | ||
|
|
37eb0a7825 | ||
|
|
3c3d7edcd6 |
@@ -8,3 +8,7 @@
|
|||||||
*.log
|
*.log
|
||||||
*.tmp
|
*.tmp
|
||||||
claude-goal/
|
claude-goal/
|
||||||
|
PRD.MD
|
||||||
|
agents.md
|
||||||
|
/ELY Browser Design
|
||||||
|
/docs
|
||||||
Generated
+55
-55
@@ -8459,7 +8459,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo"
|
name = "servo"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"arboard",
|
"arboard",
|
||||||
@@ -8523,7 +8523,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-allocator"
|
name = "servo-allocator"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"tikv-jemalloc-sys",
|
"tikv-jemalloc-sys",
|
||||||
@@ -8534,7 +8534,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-background-hang-monitor"
|
name = "servo-background-hang-monitor"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
@@ -8549,7 +8549,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-background-hang-monitor-api"
|
name = "servo-background-hang-monitor-api"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"servo-base",
|
"servo-base",
|
||||||
@@ -8558,7 +8558,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-base"
|
name = "servo-base"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
@@ -8583,7 +8583,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-canvas"
|
name = "servo-canvas"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
@@ -8608,7 +8608,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-canvas-traits"
|
name = "servo-canvas-traits"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"euclid",
|
"euclid",
|
||||||
@@ -8630,7 +8630,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-config"
|
name = "servo-config"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num_enum",
|
"num_enum",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -8643,7 +8643,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-config-macro"
|
name = "servo-config-macro"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -8654,7 +8654,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-constellation"
|
name = "servo-constellation"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"backtrace",
|
"backtrace",
|
||||||
@@ -8698,7 +8698,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-constellation-traits"
|
name = "servo-constellation-traits"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"content-security-policy",
|
"content-security-policy",
|
||||||
@@ -8733,7 +8733,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-default-resources"
|
name = "servo-default-resources"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"servo-embedder-traits",
|
"servo-embedder-traits",
|
||||||
]
|
]
|
||||||
@@ -8741,7 +8741,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-deny-public-fields"
|
name = "servo-deny-public-fields"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
@@ -8751,7 +8751,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-devtools"
|
name = "servo-devtools"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic_refcell",
|
"atomic_refcell",
|
||||||
"base64",
|
"base64",
|
||||||
@@ -8781,7 +8781,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-devtools-traits"
|
name = "servo-devtools-traits"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"http 1.4.0",
|
"http 1.4.0",
|
||||||
"malloc_size_of_derive",
|
"malloc_size_of_derive",
|
||||||
@@ -8798,7 +8798,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-dom-struct"
|
name = "servo-dom-struct"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"prettyplease",
|
"prettyplease",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -8809,7 +8809,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-embedder-traits"
|
name = "servo-embedder-traits"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"bitflags 2.11.1",
|
"bitflags 2.11.1",
|
||||||
@@ -8843,7 +8843,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-fonts"
|
name = "servo-fonts"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"app_units",
|
"app_units",
|
||||||
"bitflags 2.11.1",
|
"bitflags 2.11.1",
|
||||||
@@ -8900,7 +8900,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-fonts-traits"
|
name = "servo-fonts-traits"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic_refcell",
|
"atomic_refcell",
|
||||||
"dwrote",
|
"dwrote",
|
||||||
@@ -8924,7 +8924,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-geometry"
|
name = "servo-geometry"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"app_units",
|
"app_units",
|
||||||
"euclid",
|
"euclid",
|
||||||
@@ -8937,7 +8937,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-hyper-serde"
|
name = "servo-hyper-serde"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cookie 0.18.1",
|
"cookie 0.18.1",
|
||||||
"headers",
|
"headers",
|
||||||
@@ -8951,7 +8951,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-jstraceable-derive"
|
name = "servo-jstraceable-derive"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
@@ -8961,7 +8961,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-layout"
|
name = "servo-layout"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"app_units",
|
"app_units",
|
||||||
@@ -9018,7 +9018,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-layout-api"
|
name = "servo-layout-api"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"app_units",
|
"app_units",
|
||||||
"atomic_refcell",
|
"atomic_refcell",
|
||||||
@@ -9051,7 +9051,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-malloc-size-of"
|
name = "servo-malloc-size-of"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"app_units",
|
"app_units",
|
||||||
"atomic_refcell",
|
"atomic_refcell",
|
||||||
@@ -9094,7 +9094,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-media"
|
name = "servo-media"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"servo-base",
|
"servo-base",
|
||||||
"servo-media-audio",
|
"servo-media-audio",
|
||||||
@@ -9107,7 +9107,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-media-audio"
|
name = "servo-media-audio"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byte-slice-cast",
|
"byte-slice-cast",
|
||||||
"euclid",
|
"euclid",
|
||||||
@@ -9130,7 +9130,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-media-derive"
|
name = "servo-media-derive"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -9140,7 +9140,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-media-dummy"
|
name = "servo-media-dummy"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"servo-base",
|
"servo-base",
|
||||||
"servo-media",
|
"servo-media",
|
||||||
@@ -9154,7 +9154,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-media-ohos"
|
name = "servo-media-ohos"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -9177,7 +9177,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-media-player"
|
name = "servo-media-player"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"malloc_size_of_derive",
|
"malloc_size_of_derive",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -9190,7 +9190,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-media-streams"
|
name = "servo-media-streams"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"malloc_size_of_derive",
|
"malloc_size_of_derive",
|
||||||
"servo-malloc-size-of",
|
"servo-malloc-size-of",
|
||||||
@@ -9200,7 +9200,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-media-thread"
|
name = "servo-media-thread"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"euclid",
|
"euclid",
|
||||||
"ipc-channel",
|
"ipc-channel",
|
||||||
@@ -9219,7 +9219,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-media-traits"
|
name = "servo-media-traits"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"malloc_size_of_derive",
|
"malloc_size_of_derive",
|
||||||
"servo-malloc-size-of",
|
"servo-malloc-size-of",
|
||||||
@@ -9228,7 +9228,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-media-webrtc"
|
name = "servo-media-webrtc"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"servo-media-streams",
|
"servo-media-streams",
|
||||||
@@ -9238,7 +9238,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-metrics"
|
name = "servo-metrics"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"malloc_size_of_derive",
|
"malloc_size_of_derive",
|
||||||
"servo-base",
|
"servo-base",
|
||||||
@@ -9253,7 +9253,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-net"
|
name = "servo-net"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-compression",
|
"async-compression",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
@@ -9323,7 +9323,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-net-traits"
|
name = "servo-net-traits"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"content-security-policy",
|
"content-security-policy",
|
||||||
"cookie 0.18.1",
|
"cookie 0.18.1",
|
||||||
@@ -9363,7 +9363,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-paint"
|
name = "servo-paint"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.1",
|
"bitflags 2.11.1",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
@@ -9400,7 +9400,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-paint-api"
|
name = "servo-paint-api"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.1",
|
"bitflags 2.11.1",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
@@ -9435,7 +9435,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-pixels"
|
name = "servo-pixels"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"euclid",
|
"euclid",
|
||||||
"image",
|
"image",
|
||||||
@@ -9450,7 +9450,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-profile"
|
name = "servo-profile"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
@@ -9468,7 +9468,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-profile-traits"
|
name = "servo-profile-traits"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"ipc-channel",
|
"ipc-channel",
|
||||||
@@ -9484,7 +9484,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-script"
|
name = "servo-script"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes 0.9.1",
|
"aes 0.9.1",
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
@@ -9624,7 +9624,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-script-bindings"
|
name = "servo-script-bindings"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.1",
|
"bitflags 2.11.1",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
@@ -9663,7 +9663,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-script-traits"
|
name = "servo-script-traits"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
@@ -9697,7 +9697,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-storage"
|
name = "servo-storage"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
@@ -9722,7 +9722,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-storage-traits"
|
name = "servo-storage-traits"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"malloc_size_of_derive",
|
"malloc_size_of_derive",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -9736,7 +9736,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-timers"
|
name = "servo-timers"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"malloc_size_of_derive",
|
"malloc_size_of_derive",
|
||||||
@@ -9746,7 +9746,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-tracing"
|
name = "servo-tracing"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -9756,7 +9756,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-url"
|
name = "servo-url"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"malloc_size_of_derive",
|
"malloc_size_of_derive",
|
||||||
@@ -9770,7 +9770,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-wakelock"
|
name = "servo-wakelock"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"servo-embedder-traits",
|
"servo-embedder-traits",
|
||||||
@@ -9779,7 +9779,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-webgl"
|
name = "servo-webgl"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.1",
|
"bitflags 2.11.1",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
@@ -9803,7 +9803,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-webxr-api"
|
name = "servo-webxr-api"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"euclid",
|
"euclid",
|
||||||
"ipc-channel",
|
"ipc-channel",
|
||||||
@@ -9819,7 +9819,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-xpath"
|
name = "servo-xpath"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/servo/servo.git?rev=a87625150758fe91a29e7f6553ff2f798b87027e#a87625150758fe91a29e7f6553ff2f798b87027e"
|
source = "git+https://github.com/ZacharyZhang-NY/servo.git?rev=8f6850ceb0bd92aea4127d8078ebb2c0485e7f39#8f6850ceb0bd92aea4127d8078ebb2c0485e7f39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"malloc_size_of_derive",
|
"malloc_size_of_derive",
|
||||||
|
|||||||
+1
-1
@@ -36,7 +36,7 @@ hpke = { version = "0.14.0", default-features = false, features = ["alloc", "cha
|
|||||||
image = "0.25.10"
|
image = "0.25.10"
|
||||||
keyring-core = "1.0.0"
|
keyring-core = "1.0.0"
|
||||||
rustix = "1.1.4"
|
rustix = "1.1.4"
|
||||||
servo = { version = "0.4.0", git = "https://github.com/servo/servo.git", rev = "a87625150758fe91a29e7f6553ff2f798b87027e" }
|
servo = { version = "0.4.0", git = "https://github.com/ZacharyZhang-NY/servo.git", rev = "8f6850ceb0bd92aea4127d8078ebb2c0485e7f39" }
|
||||||
sha2 = "0.10.9"
|
sha2 = "0.10.9"
|
||||||
semver = "1.0.28"
|
semver = "1.0.28"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ in the same commit as any change that makes it stale.
|
|||||||
local persistence. No IO except through callers.
|
local persistence. No IO except through callers.
|
||||||
- `crates/ely_servo_host` — Servo embedding, rendering contexts, the
|
- `crates/ely_servo_host` — Servo embedding, rendering contexts, the
|
||||||
`ely_servo_sidecar` binary (one Servo process per profile; stdio JSON
|
`ely_servo_sidecar` binary (one Servo process per profile; stdio JSON
|
||||||
protocol v3 + macOS IOSurface Mach transport). See
|
protocol v4 + macOS IOSurface Mach transport). See
|
||||||
`docs/servo-embedding-architecture.md`.
|
`docs/servo-embedding-architecture.md`.
|
||||||
- `crates/ely_sync_client` — worker API client, device identity/trust,
|
- `crates/ely_sync_client` — worker API client, device identity/trust,
|
||||||
E2E snapshot crypto, native-keychain bearer storage, sync-owner store.
|
E2E snapshot crypto, native-keychain bearer storage, sync-owner store.
|
||||||
@@ -88,9 +88,24 @@ Real and verified:
|
|||||||
(LOAD 1 → LOAD 2). The webview loading state reconciles through
|
(LOAD 1 → LOAD 2). The webview loading state reconciles through
|
||||||
redirects/pushState (`awaiting_url_change`), and `run_dev.sh` resolves the
|
redirects/pushState (`awaiting_url_change`), and `run_dev.sh` resolves the
|
||||||
macOS Metal toolchain so `cargo run`'s shader-build failure is fixed.
|
macOS Metal toolchain so `cargo run`'s shader-build failure is fixed.
|
||||||
|
- Scroll input: consecutive same-document wheel requests coalesce at the
|
||||||
|
profile worker boundary while preserving total device-pixel distance;
|
||||||
|
frames from the active render epoch remain presentable while newer input is
|
||||||
|
queued. Clicks, text, and allow-once permission transfers retain strict
|
||||||
|
ordering. Verified by generation, blocked-worker burst, and live-site tests.
|
||||||
|
- macOS font fallback: CoreText selects a style- and language-compatible system
|
||||||
|
font before Servo's static fallback list, preserving sans-serif CJK and
|
||||||
|
Devanagari typography while retaining serif Chinese. Verified against
|
||||||
|
PingFang SC, Hiragino Sans, Apple SD Gothic Neo, Kohinoor Devanagari, and
|
||||||
|
Songti SC with real Servo canvas pixels.
|
||||||
|
- Website color scheme: resolved System/Light/Dark state crosses sidecar
|
||||||
|
protocol v4 and reaches `WebView::notify_theme_change`; GPUI appearance
|
||||||
|
events trigger a shell repaint, and first-navigation WebView replacement
|
||||||
|
preserves the selected scheme. Verified with real Servo media-query pixels.
|
||||||
- Cross-platform: download Open/Reveal use per-OS launchers (macOS `open`,
|
- Cross-platform: download Open/Reveal use per-OS launchers (macOS `open`,
|
||||||
Windows `cmd start`/`explorer /select`, Linux `xdg-open`); the command
|
Windows `cmd start`/`explorer /select`, Linux `xdg-open`); the command
|
||||||
overlay closes on Escape.
|
overlay closes on Escape. The Windows app binary uses the GUI PE subsystem,
|
||||||
|
so release launches do not open a companion console window.
|
||||||
|
|
||||||
Deferred deliberately (do NOT fake; ship with their subsystem):
|
Deferred deliberately (do NOT fake; ship with their subsystem):
|
||||||
- Updates settings page — returns with a real updater.
|
- Updates settings page — returns with a real updater.
|
||||||
@@ -114,24 +129,22 @@ file:line evidence lives in the 2026-07-10 bug-sweep report):
|
|||||||
the syncable entities persist, but back/forward stacks, splits, tab
|
the syncable entities persist, but back/forward stacks, splits, tab
|
||||||
groups, archived tabs, and downloads do not yet — extend the local-state
|
groups, archived tabs, and downloads do not yet — extend the local-state
|
||||||
document (its `settings`/`body` split is built to grow).
|
document (its `settings`/`body` split is built to grow).
|
||||||
3. Engine/webview: redirect or pushState leaves tab state "loading"
|
3. Engine/webview (partial): persistent-profile sidecars are never
|
||||||
forever and pins stale pixels (`ely_servo_host/src/runtime_webview.rs`
|
reclaimed while the app runs; sidecar stderr is nulled; final URLs
|
||||||
requested-vs-current URL reconciliation); persistent-profile sidecars
|
>32KiB cause a reload loop. (Fixed: redirect/pushState loading-state
|
||||||
are never reclaimed while the app runs; sidecar stderr is nulled;
|
reconciliation.)
|
||||||
final URLs >32KiB cause a reload loop.
|
|
||||||
4. Downloads engine: pause/resume/cancel/retry are UI-only, progress
|
4. Downloads engine: pause/resume/cancel/retry are UI-only, progress
|
||||||
never updates, checksum runs on the UI thread, open/reveal hardcode
|
never updates, checksum runs on the UI thread; `ely://auth/callback`
|
||||||
`/usr/bin/open`; `ely://auth/callback` exchange and save-page commands
|
exchange and save-page commands are unimplemented. (Fixed:
|
||||||
are unimplemented.
|
cross-platform open/reveal.)
|
||||||
5. Smaller confirmed papercuts: several synced mutations never schedule
|
5. Smaller confirmed papercuts: several synced mutations never schedule
|
||||||
an upload (splits, group toggles, deletions), trash_space leaks split
|
an upload (splits, group toggles, deletions), trash_space leaks split
|
||||||
layouts, reload of the current URL is a no-op (crashed tabs can't
|
layouts, mid-Vec tab inserts skip sort normalization, "Switch
|
||||||
reload in place), mid-Vec tab inserts skip sort normalization, Esc
|
workspace" palette entry mislabels its action, Profiles page cannot
|
||||||
doesn't close the command overlay, "Switch workspace" palette entry
|
create/delete profiles, vault rotation silently skips devices without
|
||||||
mislabels its action, Profiles page cannot create/delete profiles,
|
wrapping keys, SyncStatus counters are hardcoded, second in-process
|
||||||
vault rotation silently skips devices without wrapping keys,
|
Servo host panics (upstream OnceLock). (Fixed: Esc closes the command
|
||||||
SyncStatus counters are hardcoded, second in-process Servo host panics
|
overlay; the current-URL reload is real now — Cmd/Ctrl+R.)
|
||||||
(upstream OnceLock).
|
|
||||||
6. Integration tests litter the real data root with `profile_*` dirs
|
6. Integration tests litter the real data root with `profile_*` dirs
|
||||||
(`~/Library/Application Support/com.elydora.ELY-Browser/profiles/`);
|
(`~/Library/Application Support/com.elydora.ELY-Browser/profiles/`);
|
||||||
tests should take an overridable data root.
|
tests should take an overridable data root.
|
||||||
@@ -147,5 +160,9 @@ the trigger to ungate clipboard/geolocation/notification/webrtc prefs.
|
|||||||
- Red-green: reproduce with a failing test before fixing.
|
- Red-green: reproduce with a failing test before fixing.
|
||||||
- Files stay under 500 lines (CI-enforced); split like
|
- Files stay under 500 lines (CI-enforced); split like
|
||||||
`runtime.rs`/`runtime_paint.rs` or `state/local_visibility.rs`.
|
`runtime.rs`/`runtime_paint.rs` or `state/local_visibility.rs`.
|
||||||
|
- GPUI color encoding is explicit: pass 24-bit `RRGGBB` tokens to `rgb()` and
|
||||||
|
32-bit `RRGGBBAA` tokens to `rgba()`.
|
||||||
|
- Release builds remap workspace, Cargo, Rustup, sysroot, and target paths in
|
||||||
|
both Rust and native C/C++ objects; `verify_release_artifacts.sh` enforces it.
|
||||||
- Tracing targets: `ely::sync`, `ely::local_state` — add spans/fields on
|
- Tracing targets: `ely::sync`, `ely::local_state` — add spans/fields on
|
||||||
every new failure path so field issues are diagnosable.
|
every new failure path so field issues are diagnosable.
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#![cfg_attr(windows, windows_subsystem = "windows")]
|
||||||
|
|
||||||
mod brand;
|
mod brand;
|
||||||
mod services;
|
mod services;
|
||||||
mod shell;
|
mod shell;
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ impl ServoLiveClient {
|
|||||||
height: request.height,
|
height: request.height,
|
||||||
page_zoom_percent: request.page_zoom_percent,
|
page_zoom_percent: request.page_zoom_percent,
|
||||||
device_pixel_ratio: request.device_pixel_ratio,
|
device_pixel_ratio: request.device_pixel_ratio,
|
||||||
|
color_scheme: request.color_scheme,
|
||||||
scroll_delta_x: request.scroll_delta_x,
|
scroll_delta_x: request.scroll_delta_x,
|
||||||
scroll_delta_y: request.scroll_delta_y,
|
scroll_delta_y: request.scroll_delta_y,
|
||||||
scroll_point_x: request.scroll_point_x,
|
scroll_point_x: request.scroll_point_x,
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn reply_rejects_oversized_frame_before_readback_allocation() {
|
fn reply_rejects_oversized_frame_before_readback_allocation() {
|
||||||
let header = format!(
|
let header = format!(
|
||||||
"{{\"protocol_version\":3,\"error\":null,\"frame\":{{\"loaded_url\":null,\"title\":null,\"state\":\"complete\",\"width\":{0},\"height\":{0},\"device_pixel_ratio\":1.0,\"css_viewport_width\":{0},\"css_viewport_height\":{0},\"rgba_byte_count\":1073741824,\"pixels_changed\":true}}}}\n",
|
"{{\"protocol_version\":4,\"error\":null,\"frame\":{{\"loaded_url\":null,\"title\":null,\"state\":\"complete\",\"width\":{0},\"height\":{0},\"device_pixel_ratio\":1.0,\"css_viewport_width\":{0},\"css_viewport_height\":{0},\"rgba_byte_count\":1073741824,\"pixels_changed\":true}}}}\n",
|
||||||
MAX_FRAME_DIMENSION
|
MAX_FRAME_DIMENSION
|
||||||
);
|
);
|
||||||
let mut input = Cursor::new(header.into_bytes());
|
let mut input = Cursor::new(header.into_bytes());
|
||||||
@@ -269,19 +269,19 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reply_accepts_the_exact_header_limit() -> Result<(), ServoLiveError> {
|
fn reply_accepts_the_exact_header_limit() -> Result<(), ServoLiveError> {
|
||||||
let mut header = br#"{"protocol_version":3,"error":null,"frame":null}"#.to_vec();
|
let mut header = br#"{"protocol_version":4,"error":null,"frame":null}"#.to_vec();
|
||||||
header.resize(MAX_RESPONSE_HEADER_BYTES - 1, b' ');
|
header.resize(MAX_RESPONSE_HEADER_BYTES - 1, b' ');
|
||||||
header.push(b'\n');
|
header.push(b'\n');
|
||||||
|
|
||||||
let reply = read_reply(&mut Cursor::new(header))?;
|
let reply = read_reply(&mut Cursor::new(header))?;
|
||||||
|
|
||||||
assert_eq!(reply.protocol_version, Some(3));
|
assert_eq!(reply.protocol_version, Some(4));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reply_rejects_one_byte_over_the_header_limit() {
|
fn reply_rejects_one_byte_over_the_header_limit() {
|
||||||
let mut header = br#"{"protocol_version":3,"error":null,"frame":null}"#.to_vec();
|
let mut header = br#"{"protocol_version":4,"error":null,"frame":null}"#.to_vec();
|
||||||
header.resize(MAX_RESPONSE_HEADER_BYTES, b' ');
|
header.resize(MAX_RESPONSE_HEADER_BYTES, b' ');
|
||||||
header.push(b'\n');
|
header.push(b'\n');
|
||||||
|
|
||||||
@@ -327,7 +327,7 @@ mod tests {
|
|||||||
fn reply_parses_permission_consumption_without_a_frame() -> Result<(), ServoLiveError> {
|
fn reply_parses_permission_consumption_without_a_frame() -> Result<(), ServoLiveError> {
|
||||||
let profile_id = ely_domain::ProfileId::new();
|
let profile_id = ely_domain::ProfileId::new();
|
||||||
let header = format!(
|
let header = format!(
|
||||||
"{{\"protocol_version\":3,\"error\":null,\"frame\":null,\"permission_consumptions\":[{{\"profile_id\":\"{}\",\"origin\":\"https://example.com\",\"feature\":\"camera\",\"grant_revision\":7}}]}}\n",
|
"{{\"protocol_version\":4,\"error\":null,\"frame\":null,\"permission_consumptions\":[{{\"profile_id\":\"{}\",\"origin\":\"https://example.com\",\"feature\":\"camera\",\"grant_revision\":7}}]}}\n",
|
||||||
profile_id.as_str(),
|
profile_id.as_str(),
|
||||||
);
|
);
|
||||||
let mut input = Cursor::new(header.into_bytes());
|
let mut input = Cursor::new(header.into_bytes());
|
||||||
@@ -350,7 +350,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn hardware_reply_uses_surface_without_rgba_allocation() -> Result<(), ServoLiveError> {
|
fn hardware_reply_uses_surface_without_rgba_allocation() -> Result<(), ServoLiveError> {
|
||||||
let header = concat!(
|
let header = concat!(
|
||||||
"{\"protocol_version\":3,\"error\":null,",
|
"{\"protocol_version\":4,\"error\":null,",
|
||||||
"\"surface_handle\":{\"mach_port_name\":91,\"surface_id\":7,\"width\":64,\"height\":48},",
|
"\"surface_handle\":{\"mach_port_name\":91,\"surface_id\":7,\"width\":64,\"height\":48},",
|
||||||
"\"current_surface_id\":7,",
|
"\"current_surface_id\":7,",
|
||||||
"\"frame\":{\"loaded_url\":null,\"title\":null,\"state\":\"complete\",",
|
"\"frame\":{\"loaded_url\":null,\"title\":null,\"state\":\"complete\",",
|
||||||
@@ -402,7 +402,7 @@ mod tests {
|
|||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
fn hardware_header(current_surface_id: u64, handle_width: u32, handle_height: u32) -> String {
|
fn hardware_header(current_surface_id: u64, handle_width: u32, handle_height: u32) -> String {
|
||||||
format!(
|
format!(
|
||||||
"{{\"protocol_version\":3,\"error\":null,\"surface_handle\":{{\"mach_port_name\":91,\"surface_id\":7,\"width\":{handle_width},\"height\":{handle_height}}},\"current_surface_id\":{current_surface_id},\"frame\":{{\"loaded_url\":null,\"title\":null,\"state\":\"complete\",\"width\":64,\"height\":48,\"device_pixel_ratio\":1.0,\"css_viewport_width\":64,\"css_viewport_height\":48,\"rgba_byte_count\":0,\"pixels_changed\":true}}}}\n"
|
"{{\"protocol_version\":4,\"error\":null,\"surface_handle\":{{\"mach_port_name\":91,\"surface_id\":7,\"width\":{handle_width},\"height\":{handle_height}}},\"current_surface_id\":{current_surface_id},\"frame\":{{\"loaded_url\":null,\"title\":null,\"state\":\"complete\",\"width\":64,\"height\":48,\"device_pixel_ratio\":1.0,\"css_viewport_width\":64,\"css_viewport_height\":48,\"rgba_byte_count\":0,\"pixels_changed\":true}}}}\n"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{collections::TryReserveError, io, path::PathBuf};
|
use std::{collections::TryReserveError, io, path::PathBuf};
|
||||||
|
|
||||||
|
use ely_domain::ColorScheme;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
@@ -24,6 +25,7 @@ pub(crate) struct ServoLiveEnsureRequest {
|
|||||||
pub(crate) page_zoom_percent: u16,
|
pub(crate) page_zoom_percent: u16,
|
||||||
/// Display scale factor used to derive Servo's CSS viewport.
|
/// Display scale factor used to derive Servo's CSS viewport.
|
||||||
pub(crate) device_pixel_ratio: f32,
|
pub(crate) device_pixel_ratio: f32,
|
||||||
|
pub(crate) color_scheme: ColorScheme,
|
||||||
pub(crate) scroll_delta_x: i32,
|
pub(crate) scroll_delta_x: i32,
|
||||||
pub(crate) scroll_delta_y: i32,
|
pub(crate) scroll_delta_y: i32,
|
||||||
pub(crate) scroll_point_x: Option<u32>,
|
pub(crate) scroll_point_x: Option<u32>,
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
use ely_domain::ColorScheme;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::ServoLiveSitePermission;
|
use super::ServoLiveSitePermission;
|
||||||
|
|
||||||
pub(super) const LIVE_PROTOCOL_VERSION: u32 = 3;
|
pub(super) const LIVE_PROTOCOL_VERSION: u32 = 4;
|
||||||
pub(super) const MAX_FRAME_DIMENSION: u32 = 16_384;
|
pub(super) const MAX_FRAME_DIMENSION: u32 = 16_384;
|
||||||
pub(super) const MAX_FRAME_BYTE_COUNT: usize = 256 * 1024 * 1024;
|
pub(super) const MAX_FRAME_BYTE_COUNT: usize = 256 * 1024 * 1024;
|
||||||
pub(super) const MAX_RESPONSE_HEADER_BYTES: usize = 256 * 1024;
|
pub(super) const MAX_RESPONSE_HEADER_BYTES: usize = 256 * 1024;
|
||||||
@@ -22,6 +23,7 @@ pub(super) enum LiveRequest {
|
|||||||
height: u32,
|
height: u32,
|
||||||
page_zoom_percent: u16,
|
page_zoom_percent: u16,
|
||||||
device_pixel_ratio: f32,
|
device_pixel_ratio: f32,
|
||||||
|
color_scheme: ColorScheme,
|
||||||
scroll_delta_x: i32,
|
scroll_delta_x: i32,
|
||||||
scroll_delta_y: i32,
|
scroll_delta_y: i32,
|
||||||
scroll_point_x: Option<u32>,
|
scroll_point_x: Option<u32>,
|
||||||
|
|||||||
@@ -11,9 +11,10 @@ use gpui_component::{input::Input, scroll::ScrollableElement};
|
|||||||
use crate::shell::auth::AuthFlowPhase;
|
use crate::shell::auth::AuthFlowPhase;
|
||||||
|
|
||||||
use super::sync_controls::{
|
use super::sync_controls::{
|
||||||
button_bg, render_card_heading, render_dual_button_row, render_field_label,
|
button_bg, primary_control_background, render_card_heading, render_dual_button_row,
|
||||||
render_inline_error, render_input, render_policy_toggle, render_primary_button,
|
render_field_label, render_inline_error, render_input, render_policy_toggle,
|
||||||
render_reset_button, render_secondary_button, render_sign_out_button,
|
render_primary_button, render_reset_button, render_secondary_button, render_sign_out_button,
|
||||||
|
render_sync_now_button,
|
||||||
};
|
};
|
||||||
use super::{ElyShell, render_canvas_surface};
|
use super::{ElyShell, render_canvas_surface};
|
||||||
impl ElyShell {
|
impl ElyShell {
|
||||||
@@ -36,9 +37,10 @@ impl ElyShell {
|
|||||||
render_canvas_surface(
|
render_canvas_surface(
|
||||||
div()
|
div()
|
||||||
.size_full()
|
.size_full()
|
||||||
.p(px(40.0))
|
.overflow_y_scrollbar()
|
||||||
.flex()
|
.pt(px(28.0))
|
||||||
.justify_center()
|
.px(px(40.0))
|
||||||
|
.pb(px(32.0))
|
||||||
.child(render_sync_body(self, snapshot, cx)),
|
.child(render_sync_body(self, snapshot, cx)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -48,7 +50,7 @@ fn render_sync_body(
|
|||||||
snapshot: &BrowserSnapshot,
|
snapshot: &BrowserSnapshot,
|
||||||
cx: &mut Context<ElyShell>,
|
cx: &mut Context<ElyShell>,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
let body = div().max_w(px(860.0)).flex().flex_col().gap(px(18.0)).child(
|
let body = div().w_full().max_w(px(780.0)).mx_auto().flex().flex_col().gap(px(20.0)).child(
|
||||||
div()
|
div()
|
||||||
.text_size(px(26.0))
|
.text_size(px(26.0))
|
||||||
.font_weight(FontWeight(500.0))
|
.font_weight(FontWeight(500.0))
|
||||||
@@ -58,15 +60,9 @@ fn render_sync_body(
|
|||||||
if !profile_allows_sync_controls(&snapshot.active_profile_kind) {
|
if !profile_allows_sync_controls(&snapshot.active_profile_kind) {
|
||||||
return body.child(render_private_profile_card()).into_any_element();
|
return body.child(render_private_profile_card()).into_any_element();
|
||||||
}
|
}
|
||||||
body.child(
|
body.child(render_account_card(shell, snapshot, cx))
|
||||||
div()
|
.child(render_data_section(shell, snapshot, cx))
|
||||||
.grid()
|
.into_any_element()
|
||||||
.grid_cols(2)
|
|
||||||
.gap(px(18.0))
|
|
||||||
.child(render_account_card(shell, snapshot, cx))
|
|
||||||
.child(render_data_card(shell, snapshot, cx)),
|
|
||||||
)
|
|
||||||
.into_any_element()
|
|
||||||
}
|
}
|
||||||
fn profile_allows_sync_controls(profile_kind: &ProfileKind) -> bool {
|
fn profile_allows_sync_controls(profile_kind: &ProfileKind) -> bool {
|
||||||
profile_kind == &ProfileKind::Standard
|
profile_kind == &ProfileKind::Standard
|
||||||
@@ -101,13 +97,13 @@ fn render_account_card(
|
|||||||
|
|
||||||
match snapshot.sync_status.connection() {
|
match snapshot.sync_status.connection() {
|
||||||
SyncConnectionState::SignedOut => card
|
SyncConnectionState::SignedOut => card
|
||||||
.child(render_card_heading("Account"))
|
.child(render_card_heading("Sign in to sync"))
|
||||||
.children(account_form(shell, &snapshot.active_profile_id, cx))
|
.child(render_account_form(shell, &snapshot.active_profile_id, cx))
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
SyncConnectionState::CredentialUnavailable { message } => card
|
SyncConnectionState::CredentialUnavailable { message } => card
|
||||||
.child(render_card_heading("Account"))
|
.child(render_card_heading("Sign in to sync"))
|
||||||
.child(render_inline_error(message))
|
.child(render_inline_error(message))
|
||||||
.children(account_form(shell, &snapshot.active_profile_id, cx))
|
.child(render_account_form(shell, &snapshot.active_profile_id, cx))
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
SyncConnectionState::SigningOut => card
|
SyncConnectionState::SigningOut => card
|
||||||
.child(
|
.child(
|
||||||
@@ -296,7 +292,7 @@ fn render_device_approve_button(
|
|||||||
.px(px(12.0))
|
.px(px(12.0))
|
||||||
.py(px(8.0))
|
.py(px(8.0))
|
||||||
.rounded(px(8.0))
|
.rounded(px(8.0))
|
||||||
.bg(rgba(colors::accent()))
|
.bg(primary_control_background())
|
||||||
.text_size(px(12.0))
|
.text_size(px(12.0))
|
||||||
.font_weight(FontWeight(500.0))
|
.font_weight(FontWeight(500.0))
|
||||||
.text_color(rgb(0xfff5e6))
|
.text_color(rgb(0xfff5e6))
|
||||||
@@ -354,24 +350,23 @@ fn render_device_note(message: &'static str) -> AnyElement {
|
|||||||
div().text_size(px(11.5)).text_color(rgb(colors::ink_4())).child(message).into_any_element()
|
div().text_size(px(11.5)).text_color(rgb(colors::ink_4())).child(message).into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn account_form(
|
fn render_account_form(
|
||||||
shell: &ElyShell,
|
shell: &ElyShell,
|
||||||
profile_id: &ProfileId,
|
profile_id: &ProfileId,
|
||||||
cx: &mut Context<ElyShell>,
|
cx: &mut Context<ElyShell>,
|
||||||
) -> Vec<AnyElement> {
|
) -> AnyElement {
|
||||||
let mut elements: Vec<AnyElement> = Vec::new();
|
|
||||||
let phase = if shell.auth_flow_phase.belongs_to(profile_id) {
|
let phase = if shell.auth_flow_phase.belongs_to(profile_id) {
|
||||||
shell.auth_flow_phase.clone()
|
shell.auth_flow_phase.clone()
|
||||||
} else {
|
} else {
|
||||||
AuthFlowPhase::Idle
|
AuthFlowPhase::Idle
|
||||||
};
|
};
|
||||||
|
let mut form = div().max_w(px(440.0)).flex().flex_col().gap(px(10.0));
|
||||||
|
|
||||||
elements.push(render_field_label("Email"));
|
form = form.child(render_field_label("Email")).child(render_input(&shell.auth_email_input));
|
||||||
elements.push(render_input(&shell.auth_email_input));
|
|
||||||
|
|
||||||
match &phase {
|
match &phase {
|
||||||
AuthFlowPhase::Idle | AuthFlowPhase::Error { .. } => {
|
AuthFlowPhase::Idle | AuthFlowPhase::Error { .. } => {
|
||||||
elements.push(render_primary_button(
|
form = form.child(render_primary_button(
|
||||||
shell,
|
shell,
|
||||||
"send-otp",
|
"send-otp",
|
||||||
"Send code",
|
"Send code",
|
||||||
@@ -383,53 +378,63 @@ fn account_form(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
AuthFlowPhase::SendingCode { .. } => {
|
AuthFlowPhase::SendingCode { .. } => {
|
||||||
elements.push(render_primary_button(shell, "send-otp", "Sending", true, cx, |_, _| {}));
|
form = form.child(render_primary_button(
|
||||||
|
shell,
|
||||||
|
"send-otp",
|
||||||
|
"Sending",
|
||||||
|
true,
|
||||||
|
cx,
|
||||||
|
|_, _| {},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
AuthFlowPhase::AwaitingOtp { .. } | AuthFlowPhase::Verifying { .. } => {
|
AuthFlowPhase::AwaitingOtp { .. } | AuthFlowPhase::Verifying { .. } => {
|
||||||
elements.push(render_field_label("Code"));
|
form = form
|
||||||
elements.push(render_input(&shell.auth_otp_input));
|
.child(render_field_label("Code"))
|
||||||
elements.push(render_dual_button_row(
|
.child(render_input(&shell.auth_otp_input))
|
||||||
shell,
|
.child(render_dual_button_row(
|
||||||
phase.is_busy(),
|
shell,
|
||||||
cx,
|
phase.is_busy(),
|
||||||
|shell, cx| shell.submit_email_otp_verify(cx),
|
cx,
|
||||||
|shell, cx| shell.submit_email_otp_request(cx),
|
|shell, cx| shell.submit_email_otp_verify(cx),
|
||||||
));
|
|shell, cx| shell.submit_email_otp_request(cx),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(message) = phase.error_message() {
|
if let Some(message) = phase.error_message() {
|
||||||
elements.push(render_inline_error(message));
|
form = form.child(render_inline_error(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
elements
|
form.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_data_card(
|
fn render_data_section(
|
||||||
shell: &ElyShell,
|
shell: &ElyShell,
|
||||||
snapshot: &BrowserSnapshot,
|
snapshot: &BrowserSnapshot,
|
||||||
cx: &mut Context<ElyShell>,
|
cx: &mut Context<ElyShell>,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
|
let can_sync_now = matches!(
|
||||||
|
snapshot.sync_status.connection(),
|
||||||
|
SyncConnectionState::SignedIn
|
||||||
|
| SyncConnectionState::AwaitingDeviceApproval
|
||||||
|
| SyncConnectionState::SyncReady { .. }
|
||||||
|
| SyncConnectionState::SyncError { .. }
|
||||||
|
);
|
||||||
div()
|
div()
|
||||||
.p(px(18.0))
|
|
||||||
.rounded(px(12.0))
|
|
||||||
.bg(rgba(card_bg()))
|
|
||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.gap(px(12.0))
|
.gap(px(12.0))
|
||||||
.max_h(px(640.0))
|
|
||||||
.overflow_y_scrollbar()
|
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
.justify_between()
|
.justify_between()
|
||||||
.gap(px(10.0))
|
.gap(px(10.0))
|
||||||
.child(render_card_heading("Data"))
|
.child(render_card_heading("Choose what to sync"))
|
||||||
.child(render_reset_button(shell, cx)),
|
.when(can_sync_now, |header| header.child(render_sync_now_button(shell, cx))),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
div().flex().flex_col().gap(px(2.0)).children(
|
div().border_t_1().border_color(rgba(colors::divider())).flex().flex_col().children(
|
||||||
snapshot
|
snapshot
|
||||||
.sync_status
|
.sync_status
|
||||||
.objects()
|
.objects()
|
||||||
@@ -438,6 +443,7 @@ fn render_data_card(
|
|||||||
.map(|(index, status)| render_sync_object_row(shell, index, status, cx)),
|
.map(|(index, status)| render_sync_object_row(shell, index, status, cx)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.child(div().pt(px(2.0)).flex().child(render_reset_button(cx)))
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,7 +458,7 @@ fn render_sync_object_row(
|
|||||||
.items_center()
|
.items_center()
|
||||||
.justify_between()
|
.justify_between()
|
||||||
.gap(px(12.0))
|
.gap(px(12.0))
|
||||||
.py(px(9.0))
|
.py(px(11.0))
|
||||||
.border_b_1()
|
.border_b_1()
|
||||||
.border_color(rgba(colors::divider()))
|
.border_color(rgba(colors::divider()))
|
||||||
.child(
|
.child(
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use ely_design_system::colors;
|
use ely_design_system::colors;
|
||||||
use ely_domain::{SyncObjectPolicy, SyncObjectStatus};
|
use ely_domain::{SyncObjectPolicy, SyncObjectStatus};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AnyElement, Context, FontWeight, InteractiveElement, IntoElement, ParentElement, SharedString,
|
AnyElement, Context, FontWeight, InteractiveElement, IntoElement, ParentElement, Rgba,
|
||||||
StatefulInteractiveElement, Styled, div, prelude::FluentBuilder, px, rgb, rgba,
|
SharedString, StatefulInteractiveElement, Styled, div, prelude::FluentBuilder, px, rgb, rgba,
|
||||||
};
|
};
|
||||||
use gpui_component::{
|
use gpui_component::{
|
||||||
Disableable, Sizable,
|
Disableable, IconName, Sizable,
|
||||||
button::{Button, ButtonVariants},
|
button::{Button, ButtonVariants},
|
||||||
input::{Input, InputState},
|
input::{Input, InputState},
|
||||||
};
|
};
|
||||||
@@ -13,6 +13,10 @@ use gpui_component::{
|
|||||||
use crate::shell::ElyShell;
|
use crate::shell::ElyShell;
|
||||||
use crate::shell::chrome::animations::{chrome_motion_feedback, toggle_thumb_motion};
|
use crate::shell::chrome::animations::{chrome_motion_feedback, toggle_thumb_motion};
|
||||||
|
|
||||||
|
pub(super) fn primary_control_background() -> Rgba {
|
||||||
|
rgb(colors::accent())
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn render_primary_button<F>(
|
pub(super) fn render_primary_button<F>(
|
||||||
shell: &ElyShell,
|
shell: &ElyShell,
|
||||||
id: &'static str,
|
id: &'static str,
|
||||||
@@ -30,7 +34,7 @@ where
|
|||||||
.px(px(14.0))
|
.px(px(14.0))
|
||||||
.py(px(8.0))
|
.py(px(8.0))
|
||||||
.rounded(px(8.0))
|
.rounded(px(8.0))
|
||||||
.bg(rgba(colors::accent()))
|
.bg(primary_control_background())
|
||||||
.text_size(px(12.5))
|
.text_size(px(12.5))
|
||||||
.font_weight(FontWeight(500.0))
|
.font_weight(FontWeight(500.0))
|
||||||
.text_color(rgb(0xfff5e6))
|
.text_color(rgb(0xfff5e6))
|
||||||
@@ -128,21 +132,21 @@ pub(super) fn render_inline_error(message: &str) -> AnyElement {
|
|||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn render_reset_button(shell: &ElyShell, cx: &mut Context<ElyShell>) -> AnyElement {
|
pub(super) fn render_sync_now_button(shell: &ElyShell, cx: &mut Context<ElyShell>) -> AnyElement {
|
||||||
div()
|
render_primary_button(shell, "sync-upload", "Sync now", false, cx, |shell, _| {
|
||||||
.flex()
|
shell.trigger_cloud_sync_upload();
|
||||||
.gap(px(8.0))
|
})
|
||||||
.child(render_primary_button(shell, "sync-upload", "Sync now", false, cx, |shell, _| {
|
}
|
||||||
shell.trigger_cloud_sync_upload();
|
|
||||||
|
pub(super) fn render_reset_button(cx: &mut Context<ElyShell>) -> AnyElement {
|
||||||
|
Button::new("sync-reset")
|
||||||
|
.ghost()
|
||||||
|
.xsmall()
|
||||||
|
.icon(IconName::Undo2)
|
||||||
|
.label("Reset sync preferences")
|
||||||
|
.on_click(cx.listener(|shell, _, _, cx| {
|
||||||
|
shell.reset_sync_settings(cx);
|
||||||
}))
|
}))
|
||||||
.child(render_secondary_button(
|
|
||||||
shell,
|
|
||||||
"sync-reset",
|
|
||||||
"Reset to defaults",
|
|
||||||
false,
|
|
||||||
cx,
|
|
||||||
|shell, cx| shell.reset_sync_settings(cx),
|
|
||||||
))
|
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +159,7 @@ pub(super) fn render_policy_toggle(
|
|||||||
let enabled = status.policy() == SyncObjectPolicy::Enabled;
|
let enabled = status.policy() == SyncObjectPolicy::Enabled;
|
||||||
let next_policy = if enabled { SyncObjectPolicy::Paused } else { SyncObjectPolicy::Enabled };
|
let next_policy = if enabled { SyncObjectPolicy::Paused } else { SyncObjectPolicy::Enabled };
|
||||||
let kind = status.kind();
|
let kind = status.kind();
|
||||||
let track_color = if enabled { colors::accent() } else { 0x281e1426 };
|
let track_color = if enabled { primary_control_background() } else { rgba(0x281e1426) };
|
||||||
let id = SharedString::from(format!("sync-policy-{index}"));
|
let id = SharedString::from(format!("sync-policy-{index}"));
|
||||||
let press_id = shell.chrome_motion_animation_id(id.as_str());
|
let press_id = shell.chrome_motion_animation_id(id.as_str());
|
||||||
let thumb_press_id = press_id.clone();
|
let thumb_press_id = press_id.clone();
|
||||||
@@ -166,7 +170,7 @@ pub(super) fn render_policy_toggle(
|
|||||||
.w(px(34.0))
|
.w(px(34.0))
|
||||||
.h(px(20.0))
|
.h(px(20.0))
|
||||||
.rounded_full()
|
.rounded_full()
|
||||||
.bg(rgba(track_color))
|
.bg(track_color)
|
||||||
.p(px(2.0))
|
.p(px(2.0))
|
||||||
.cursor_pointer()
|
.cursor_pointer()
|
||||||
.hover(|style| style.opacity(0.9))
|
.hover(|style| style.opacity(0.9))
|
||||||
@@ -227,3 +231,13 @@ pub(super) fn button_bg() -> u32 {
|
|||||||
fn button_bg_hover() -> u32 {
|
fn button_bg_hover() -> u32 {
|
||||||
colors::pick(0xffffffeb, 0x1f1d1beb)
|
colors::pick(0xffffffeb, 0x1f1d1beb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::primary_control_background;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn primary_control_background_is_opaque_brand_accent() {
|
||||||
|
assert_eq!(u32::from(primary_control_background()), 0xc96442ff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ pub struct ElyShell {
|
|||||||
pub(crate) local_state_save_scheduled: bool,
|
pub(crate) local_state_save_scheduled: bool,
|
||||||
_command_subscription: Subscription,
|
_command_subscription: Subscription,
|
||||||
_translucency_subscription: Subscription,
|
_translucency_subscription: Subscription,
|
||||||
|
_appearance_subscription: Subscription,
|
||||||
_quit_save_subscription: Option<Subscription>,
|
_quit_save_subscription: Option<Subscription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,6 +252,8 @@ impl ElyShell {
|
|||||||
}
|
}
|
||||||
ShellState::StartupError(_) => None,
|
ShellState::StartupError(_) => None,
|
||||||
};
|
};
|
||||||
|
let appearance_subscription =
|
||||||
|
cx.observe_window_appearance(window, |_shell, _window, cx| cx.notify());
|
||||||
let mut shell = Self {
|
let mut shell = Self {
|
||||||
state,
|
state,
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
@@ -307,6 +310,7 @@ impl ElyShell {
|
|||||||
auth_flow_phase: auth::AuthFlowPhase::Idle,
|
auth_flow_phase: auth::AuthFlowPhase::Idle,
|
||||||
_command_subscription: command_subscription,
|
_command_subscription: command_subscription,
|
||||||
_translucency_subscription: translucency_subscription,
|
_translucency_subscription: translucency_subscription,
|
||||||
|
_appearance_subscription: appearance_subscription,
|
||||||
_quit_save_subscription: None,
|
_quit_save_subscription: None,
|
||||||
};
|
};
|
||||||
shell._quit_save_subscription = Some(local_persistence::register_quit_save(cx));
|
shell._quit_save_subscription = Some(local_persistence::register_quit_save(cx));
|
||||||
|
|||||||
@@ -27,35 +27,39 @@ impl Render for ElyShell {
|
|||||||
match &self.state {
|
match &self.state {
|
||||||
ShellState::Ready(core) => match core.snapshot() {
|
ShellState::Ready(core) => match core.snapshot() {
|
||||||
Ok(snapshot) => {
|
Ok(snapshot) => {
|
||||||
apply_color_mode(
|
let color_scheme =
|
||||||
resolve_color_mode(snapshot.appearance.theme_mode(), appearance),
|
resolve_color_scheme(snapshot.appearance.theme_mode(), appearance);
|
||||||
cx,
|
self.web_surfaces.set_color_scheme(color_scheme);
|
||||||
);
|
apply_color_scheme(color_scheme, cx);
|
||||||
match active_tab_from_snapshot(&snapshot) {
|
match active_tab_from_snapshot(&snapshot) {
|
||||||
Some(active_tab) => self.render_browser(&snapshot, active_tab, window, cx),
|
Some(active_tab) => self.render_browser(&snapshot, active_tab, window, cx),
|
||||||
None => render_error("active tab missing from snapshot".to_string()),
|
None => render_error("active tab missing from snapshot".to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
apply_color_mode(
|
let color_scheme =
|
||||||
resolve_color_mode(ely_domain::ThemeMode::default(), appearance),
|
resolve_color_scheme(ely_domain::ThemeMode::default(), appearance);
|
||||||
cx,
|
self.web_surfaces.set_color_scheme(color_scheme);
|
||||||
);
|
apply_color_scheme(color_scheme, cx);
|
||||||
render_error(error.to_string())
|
render_error(error.to_string())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ShellState::StartupError(message) => {
|
ShellState::StartupError(message) => {
|
||||||
apply_color_mode(
|
let color_scheme =
|
||||||
resolve_color_mode(ely_domain::ThemeMode::default(), appearance),
|
resolve_color_scheme(ely_domain::ThemeMode::default(), appearance);
|
||||||
cx,
|
self.web_surfaces.set_color_scheme(color_scheme);
|
||||||
);
|
apply_color_scheme(color_scheme, cx);
|
||||||
render_error(message.clone())
|
render_error(message.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_color_mode(mode: colors::Mode, cx: &mut Context<ElyShell>) {
|
fn apply_color_scheme(color_scheme: ely_domain::ColorScheme, cx: &mut Context<ElyShell>) {
|
||||||
|
let mode = match color_scheme {
|
||||||
|
ely_domain::ColorScheme::Light => colors::Mode::Light,
|
||||||
|
ely_domain::ColorScheme::Dark => colors::Mode::Dark,
|
||||||
|
};
|
||||||
colors::set_mode(mode);
|
colors::set_mode(mode);
|
||||||
|
|
||||||
let component_mode = match mode {
|
let component_mode = match mode {
|
||||||
@@ -68,22 +72,19 @@ fn apply_color_mode(mode: colors::Mode, cx: &mut Context<ElyShell>) {
|
|||||||
gpui_component::Theme::global_mut(cx).font_family = SANS_FAMILY.into();
|
gpui_component::Theme::global_mut(cx).font_family = SANS_FAMILY.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_color_mode(
|
fn resolve_color_scheme(
|
||||||
theme_mode: ely_domain::ThemeMode,
|
theme_mode: ely_domain::ThemeMode,
|
||||||
window_appearance: gpui::WindowAppearance,
|
window_appearance: gpui::WindowAppearance,
|
||||||
) -> colors::Mode {
|
) -> ely_domain::ColorScheme {
|
||||||
match theme_mode {
|
let system = match window_appearance {
|
||||||
ely_domain::ThemeMode::Light => colors::Mode::Light,
|
gpui::WindowAppearance::Dark | gpui::WindowAppearance::VibrantDark => {
|
||||||
ely_domain::ThemeMode::Dark => colors::Mode::Dark,
|
ely_domain::ColorScheme::Dark
|
||||||
ely_domain::ThemeMode::System => match window_appearance {
|
}
|
||||||
gpui::WindowAppearance::Dark | gpui::WindowAppearance::VibrantDark => {
|
gpui::WindowAppearance::Light | gpui::WindowAppearance::VibrantLight => {
|
||||||
colors::Mode::Dark
|
ely_domain::ColorScheme::Light
|
||||||
}
|
}
|
||||||
gpui::WindowAppearance::Light | gpui::WindowAppearance::VibrantLight => {
|
};
|
||||||
colors::Mode::Light
|
theme_mode.resolve(system)
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn active_tab_from_snapshot(snapshot: &BrowserSnapshot) -> Option<&BrowserTab> {
|
fn active_tab_from_snapshot(snapshot: &BrowserSnapshot) -> Option<&BrowserTab> {
|
||||||
@@ -428,3 +429,31 @@ pub(super) fn tab_profile_label(tab: &BrowserTab, profiles: &[ely_domain::Profil
|
|||||||
.map(|profile| format!("Profile: {}", profile.name()))
|
.map(|profile| format!("Profile: {}", profile.name()))
|
||||||
.unwrap_or_else(|| format!("Profile: {}", tab.profile_id().as_str()))
|
.unwrap_or_else(|| format!("Profile: {}", tab.profile_id().as_str()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use ely_domain::{ColorScheme, ThemeMode};
|
||||||
|
use gpui::WindowAppearance;
|
||||||
|
|
||||||
|
use super::resolve_color_scheme;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resolved_color_scheme_tracks_browser_and_system_modes() {
|
||||||
|
assert_eq!(
|
||||||
|
resolve_color_scheme(ThemeMode::System, WindowAppearance::Dark),
|
||||||
|
ColorScheme::Dark,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
resolve_color_scheme(ThemeMode::System, WindowAppearance::VibrantLight),
|
||||||
|
ColorScheme::Light,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
resolve_color_scheme(ThemeMode::Light, WindowAppearance::Dark),
|
||||||
|
ColorScheme::Light,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
resolve_color_scheme(ThemeMode::Dark, WindowAppearance::Light),
|
||||||
|
ColorScheme::Dark,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use ely_domain::{BrowserTab, ProfileId, TabId, UrlText};
|
use ely_domain::{BrowserTab, ColorScheme, ProfileId, TabId, UrlText};
|
||||||
|
|
||||||
use crate::services::{ProfileDataMode, servo_live::ServoLivePermissionGrant};
|
use crate::services::{ProfileDataMode, servo_live::ServoLivePermissionGrant};
|
||||||
|
|
||||||
@@ -23,16 +23,27 @@ pub(super) struct WebSurfaceStore {
|
|||||||
/// Singleton because only one tab at a time holds keyboard focus
|
/// Singleton because only one tab at a time holds keyboard focus
|
||||||
/// across the whole window. Lives on the store, not per-tab.
|
/// across the whole window. Lives on the store, not per-tab.
|
||||||
pub(super) keyboard_focus: Option<WebSurfaceKeyboardFocusState>,
|
pub(super) keyboard_focus: Option<WebSurfaceKeyboardFocusState>,
|
||||||
|
color_scheme: ColorScheme,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSurfaceStore {
|
impl WebSurfaceStore {
|
||||||
pub(super) fn new() -> Self {
|
pub(super) fn new() -> Self {
|
||||||
Self { runtime: WebSurfaceRuntime::new(), surfaces: BTreeMap::new(), keyboard_focus: None }
|
Self {
|
||||||
|
runtime: WebSurfaceRuntime::new(),
|
||||||
|
surfaces: BTreeMap::new(),
|
||||||
|
keyboard_focus: None,
|
||||||
|
color_scheme: ColorScheme::Light,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(super) fn new_with_runtime(runtime: WebSurfaceRuntime) -> Self {
|
pub(super) fn new_with_runtime(runtime: WebSurfaceRuntime) -> Self {
|
||||||
Self { runtime, surfaces: BTreeMap::new(), keyboard_focus: None }
|
Self {
|
||||||
|
runtime,
|
||||||
|
surfaces: BTreeMap::new(),
|
||||||
|
keyboard_focus: None,
|
||||||
|
color_scheme: ColorScheme::Light,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -72,6 +83,7 @@ impl WebSurfaceStore {
|
|||||||
tab.profile_id().clone(),
|
tab.profile_id().clone(),
|
||||||
profile_data_mode,
|
profile_data_mode,
|
||||||
tab.zoom_percent(),
|
tab.zoom_percent(),
|
||||||
|
self.color_scheme,
|
||||||
permissions,
|
permissions,
|
||||||
);
|
);
|
||||||
let scope_changed = self.surface_mut(tab.id()).reset_for_scope_change(&ensure_key);
|
let scope_changed = self.surface_mut(tab.id()).reset_for_scope_change(&ensure_key);
|
||||||
@@ -365,6 +377,11 @@ impl WebSurfaceStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_color_scheme(&mut self, color_scheme: ColorScheme) {
|
||||||
|
self.color_scheme = color_scheme;
|
||||||
|
self.runtime.set_color_scheme(color_scheme);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(super) fn surface_for_test(&self, tab_id: &TabId) -> Option<&PerTabSurface> {
|
pub(super) fn surface_for_test(&self, tab_id: &TabId) -> Option<&PerTabSurface> {
|
||||||
self.surfaces.get(tab_id)
|
self.surfaces.get(tab_id)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::{
|
|||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use ely_domain::{BrowserTab, TabId};
|
use ely_domain::{BrowserTab, ColorScheme, TabId};
|
||||||
|
|
||||||
use crate::services::{
|
use crate::services::{
|
||||||
ProfileDataMode,
|
ProfileDataMode,
|
||||||
@@ -43,6 +43,7 @@ pub(super) struct WebSurfaceRuntime {
|
|||||||
transient_cleanup_error: Option<String>,
|
transient_cleanup_error: Option<String>,
|
||||||
client_factory: LiveRuntimeClientFactory,
|
client_factory: LiveRuntimeClientFactory,
|
||||||
last_generation: u64,
|
last_generation: u64,
|
||||||
|
color_scheme: ColorScheme,
|
||||||
}
|
}
|
||||||
|
|
||||||
const SIDECAR_RESTART_BASE_DELAY: Duration = Duration::from_millis(250);
|
const SIDECAR_RESTART_BASE_DELAY: Duration = Duration::from_millis(250);
|
||||||
@@ -60,6 +61,7 @@ impl WebSurfaceRuntime {
|
|||||||
transient_cleanup_error,
|
transient_cleanup_error,
|
||||||
client_factory: new_servo_live_client,
|
client_factory: new_servo_live_client,
|
||||||
last_generation: 0,
|
last_generation: 0,
|
||||||
|
color_scheme: ColorScheme::Light,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +76,7 @@ impl WebSurfaceRuntime {
|
|||||||
transient_cleanup_error: None,
|
transient_cleanup_error: None,
|
||||||
client_factory,
|
client_factory,
|
||||||
last_generation: 0,
|
last_generation: 0,
|
||||||
|
color_scheme: ColorScheme::Light,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,9 +104,11 @@ impl WebSurfaceRuntime {
|
|||||||
let submitted_at = Instant::now();
|
let submitted_at = Instant::now();
|
||||||
let started_loading = {
|
let started_loading = {
|
||||||
let session = session_for_scope(&mut self.sessions, tab.id(), scope.clone());
|
let session = session_for_scope(&mut self.sessions, tab.id(), scope.clone());
|
||||||
let started_loading = session.started_loading(&requested_url, size, zoom_percent);
|
let started_loading =
|
||||||
|
session.started_loading(&requested_url, size, zoom_percent, self.color_scheme);
|
||||||
if started_loading {
|
if started_loading {
|
||||||
session.pending_user_navigation = false;
|
session.pending_user_navigation = false;
|
||||||
|
session.frame_generation_floor = Some(generation);
|
||||||
}
|
}
|
||||||
if user_navigation_input {
|
if user_navigation_input {
|
||||||
session.pending_user_navigation = true;
|
session.pending_user_navigation = true;
|
||||||
@@ -111,6 +116,7 @@ impl WebSurfaceRuntime {
|
|||||||
session.requested_url = requested_url.clone();
|
session.requested_url = requested_url.clone();
|
||||||
session.size = size;
|
session.size = size;
|
||||||
session.zoom_percent = zoom_percent;
|
session.zoom_percent = zoom_percent;
|
||||||
|
session.color_scheme = Some(self.color_scheme);
|
||||||
session.scroll_offset = next_scroll_offset;
|
session.scroll_offset = next_scroll_offset;
|
||||||
session.generation = Some(generation);
|
session.generation = Some(generation);
|
||||||
session.cadence.note_ensure(input_kind, started_loading, submitted_at);
|
session.cadence.note_ensure(input_kind, started_loading, submitted_at);
|
||||||
@@ -125,6 +131,7 @@ impl WebSurfaceRuntime {
|
|||||||
height: size.height,
|
height: size.height,
|
||||||
page_zoom_percent: zoom_percent,
|
page_zoom_percent: zoom_percent,
|
||||||
device_pixel_ratio: size.device_pixel_ratio_f32(),
|
device_pixel_ratio: size.device_pixel_ratio_f32(),
|
||||||
|
color_scheme: self.color_scheme,
|
||||||
scroll_delta_x,
|
scroll_delta_x,
|
||||||
scroll_delta_y,
|
scroll_delta_y,
|
||||||
scroll_point_x,
|
scroll_point_x,
|
||||||
@@ -226,6 +233,10 @@ impl WebSurfaceRuntime {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_color_scheme(&mut self, color_scheme: ColorScheme) {
|
||||||
|
self.color_scheme = color_scheme;
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn prepare_tab_scope(
|
pub(super) fn prepare_tab_scope(
|
||||||
&mut self,
|
&mut self,
|
||||||
tab_id: &TabId,
|
tab_id: &TabId,
|
||||||
@@ -288,7 +299,7 @@ impl WebSurfaceRuntime {
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if !self.sessions.get(&tab_id_obj).is_some_and(|session| {
|
if !self.sessions.get(&tab_id_obj).is_some_and(|session| {
|
||||||
&session.scope == scope && session.generation == Some(generation)
|
&session.scope == scope && session.accepts_frame_generation(generation)
|
||||||
}) {
|
}) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -472,3 +483,6 @@ mod retry_tests;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "web_surface_runtime_tests.rs"]
|
#[path = "web_surface_runtime_tests.rs"]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "web_surface_theme_tests.rs"]
|
||||||
|
mod theme_tests;
|
||||||
|
|||||||
@@ -145,6 +145,35 @@ fn late_frame_is_discarded_after_tab_session_closes() -> Result<(), String> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn earlier_frame_from_same_render_epoch_remains_presentable() -> Result<(), String> {
|
||||||
|
let mut runtime = WebSurfaceRuntime::new_with_client_factory(empty_client_factory);
|
||||||
|
let tab_id = TabId::new();
|
||||||
|
let profile_id = ProfileId::new();
|
||||||
|
let tab = web_tab(tab_id.clone(), profile_id.clone(), "https://example.com/page")?;
|
||||||
|
|
||||||
|
runtime.ensure_tab(&tab, surface_size(), ProfileDataMode::Transient, &[], pending_input())?;
|
||||||
|
let earlier = current_generation(&runtime, &tab_id)?;
|
||||||
|
runtime.ensure_tab(&tab, surface_size(), ProfileDataMode::Transient, &[], pending_input())?;
|
||||||
|
let latest = current_generation(&runtime, &tab_id)?;
|
||||||
|
|
||||||
|
let mut frames = Vec::new();
|
||||||
|
runtime.collect_responses(
|
||||||
|
&scope(&profile_id),
|
||||||
|
vec![WorkerResponse::Frame {
|
||||||
|
generation: earlier,
|
||||||
|
tab_id: tab_id.as_str().to_string(),
|
||||||
|
frame: live_frame(),
|
||||||
|
}],
|
||||||
|
Instant::now(),
|
||||||
|
&mut frames,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(earlier < latest);
|
||||||
|
assert!(matches!(frames.as_slice(), [WebSurfaceRuntimeFrame::Ready { .. }]));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn scope_change_returns_submitted_permission_grants_on_the_next_tick() -> Result<(), String> {
|
fn scope_change_returns_submitted_permission_grants_on_the_next_tick() -> Result<(), String> {
|
||||||
let mut runtime = WebSurfaceRuntime::new_with_client_factory(empty_client_factory);
|
let mut runtime = WebSurfaceRuntime::new_with_client_factory(empty_client_factory);
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ fn successful_frame_resets_scope_retry_state() {
|
|||||||
let scope = WebSurfaceRuntimeScope::new(ProfileId::new(), ProfileDataMode::Transient);
|
let scope = WebSurfaceRuntimeScope::new(ProfileId::new(), ProfileDataMode::Transient);
|
||||||
let tab_id = TabId::new();
|
let tab_id = TabId::new();
|
||||||
let generation = RequestGeneration::new(1);
|
let generation = RequestGeneration::new(1);
|
||||||
session_for_scope(&mut runtime.sessions, &tab_id, scope.clone()).generation = Some(generation);
|
let session = session_for_scope(&mut runtime.sessions, &tab_id, scope.clone());
|
||||||
|
session.generation = Some(generation);
|
||||||
|
session.frame_generation_floor = Some(generation);
|
||||||
runtime.note_scope_failure(&scope, Instant::now());
|
runtime.note_scope_failure(&scope, Instant::now());
|
||||||
let mut frames = Vec::new();
|
let mut frames = Vec::new();
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use crate::services::{
|
|||||||
transient_profile_data_dir,
|
transient_profile_data_dir,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use ely_domain::{ProfileId, TabId};
|
use ely_domain::{ColorScheme, ProfileId, TabId};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
web_surface_cadence::WebSurfacePollCadence,
|
web_surface_cadence::WebSurfacePollCadence,
|
||||||
@@ -42,6 +42,8 @@ pub(super) struct WebSurfaceSession {
|
|||||||
pub(super) scroll_offset: WebSurfaceScrollOffset,
|
pub(super) scroll_offset: WebSurfaceScrollOffset,
|
||||||
pub(super) pending_user_navigation: bool,
|
pub(super) pending_user_navigation: bool,
|
||||||
pub(super) generation: Option<RequestGeneration>,
|
pub(super) generation: Option<RequestGeneration>,
|
||||||
|
pub(super) frame_generation_floor: Option<RequestGeneration>,
|
||||||
|
pub(super) color_scheme: Option<ColorScheme>,
|
||||||
pub(super) cadence: WebSurfacePollCadence,
|
pub(super) cadence: WebSurfacePollCadence,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +57,8 @@ impl WebSurfaceSession {
|
|||||||
scroll_offset: WebSurfaceScrollOffset::default(),
|
scroll_offset: WebSurfaceScrollOffset::default(),
|
||||||
pending_user_navigation: false,
|
pending_user_navigation: false,
|
||||||
generation: None,
|
generation: None,
|
||||||
|
frame_generation_floor: None,
|
||||||
|
color_scheme: None,
|
||||||
cadence: WebSurfacePollCadence::default(),
|
cadence: WebSurfacePollCadence::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,10 +68,17 @@ impl WebSurfaceSession {
|
|||||||
requested_url: &str,
|
requested_url: &str,
|
||||||
size: WebSurfaceSize,
|
size: WebSurfaceSize,
|
||||||
zoom_percent: u16,
|
zoom_percent: u16,
|
||||||
|
color_scheme: ColorScheme,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.requested_url != requested_url
|
self.requested_url != requested_url
|
||||||
|| self.size != size
|
|| self.size != size
|
||||||
|| self.zoom_percent != zoom_percent
|
|| self.zoom_percent != zoom_percent
|
||||||
|
|| self.color_scheme != Some(color_scheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn accepts_frame_generation(&self, generation: RequestGeneration) -> bool {
|
||||||
|
self.frame_generation_floor.is_some_and(|floor| generation >= floor)
|
||||||
|
&& self.generation.is_some_and(|latest| generation <= latest)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn url_change_for(
|
pub(super) fn url_change_for(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use ely_domain::{ProfileId, TabId};
|
use ely_domain::{ColorScheme, ProfileId, TabId};
|
||||||
use gpui::{Bounds, Pixels};
|
use gpui::{Bounds, Pixels};
|
||||||
|
|
||||||
use crate::services::ProfileDataMode;
|
use crate::services::ProfileDataMode;
|
||||||
@@ -309,6 +309,7 @@ pub(super) struct WebSurfaceEnsureKey {
|
|||||||
profile_id: ProfileId,
|
profile_id: ProfileId,
|
||||||
profile_data_mode: ProfileDataMode,
|
profile_data_mode: ProfileDataMode,
|
||||||
zoom_percent: u16,
|
zoom_percent: u16,
|
||||||
|
color_scheme: ColorScheme,
|
||||||
permissions: Vec<WebSurfaceSitePermission>,
|
permissions: Vec<WebSurfaceSitePermission>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,6 +320,7 @@ impl WebSurfaceEnsureKey {
|
|||||||
profile_id: ProfileId,
|
profile_id: ProfileId,
|
||||||
profile_data_mode: ProfileDataMode,
|
profile_data_mode: ProfileDataMode,
|
||||||
zoom_percent: u16,
|
zoom_percent: u16,
|
||||||
|
color_scheme: ColorScheme,
|
||||||
permissions: &[WebSurfaceSitePermission],
|
permissions: &[WebSurfaceSitePermission],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -327,6 +329,7 @@ impl WebSurfaceEnsureKey {
|
|||||||
profile_id,
|
profile_id,
|
||||||
profile_data_mode,
|
profile_data_mode,
|
||||||
zoom_percent,
|
zoom_percent,
|
||||||
|
color_scheme,
|
||||||
permissions: permissions.to_vec(),
|
permissions: permissions.to_vec(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -446,6 +449,7 @@ mod tests {
|
|||||||
profile_id.clone(),
|
profile_id.clone(),
|
||||||
ProfileDataMode::Persistent,
|
ProfileDataMode::Persistent,
|
||||||
100,
|
100,
|
||||||
|
ColorScheme::Light,
|
||||||
&[],
|
&[],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
use ely_domain::{BrowserTab, ColorScheme, ProfileId, SpaceId, TabId, UrlText};
|
||||||
|
use gpui::{Bounds, point, px, size};
|
||||||
|
|
||||||
|
use crate::services::{
|
||||||
|
ProfileDataMode,
|
||||||
|
servo_live::{ServoLiveEnsureRequest, ServoLiveFrame},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
super::{
|
||||||
|
web_surface::WebSurfaceStore,
|
||||||
|
web_surface_state::WebSurfaceInputOutcome,
|
||||||
|
web_surface_worker::{LiveRuntimeClient, LiveRuntimeClientError},
|
||||||
|
},
|
||||||
|
WebSurfaceRuntime,
|
||||||
|
};
|
||||||
|
|
||||||
|
static COLOR_SCHEMES: Mutex<Vec<ColorScheme>> = Mutex::new(Vec::new());
|
||||||
|
|
||||||
|
struct ThemeRecordingClient;
|
||||||
|
|
||||||
|
impl LiveRuntimeClient for ThemeRecordingClient {
|
||||||
|
fn ensure(
|
||||||
|
&mut self,
|
||||||
|
request: ServoLiveEnsureRequest,
|
||||||
|
) -> Result<Option<ServoLiveFrame>, LiveRuntimeClientError> {
|
||||||
|
COLOR_SCHEMES
|
||||||
|
.lock()
|
||||||
|
.map_err(|_| "color scheme recorder lock was poisoned".to_string())?
|
||||||
|
.push(request.color_scheme);
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll(&mut self, _tab_id: String) -> Result<Option<ServoLiveFrame>, LiveRuntimeClientError> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close(&mut self, _tab_id: String) -> Result<(), LiveRuntimeClientError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn browser_color_scheme_reaches_each_web_surface_ensure() -> Result<(), String> {
|
||||||
|
COLOR_SCHEMES.lock().map_err(|_| "color scheme recorder lock was poisoned")?.clear();
|
||||||
|
let runtime =
|
||||||
|
WebSurfaceRuntime::new_with_client_factory(|_| Ok(Box::new(ThemeRecordingClient)));
|
||||||
|
let mut store = WebSurfaceStore::new_with_runtime(runtime);
|
||||||
|
let tab = BrowserTab::new(
|
||||||
|
TabId::new(),
|
||||||
|
SpaceId::new(),
|
||||||
|
ProfileId::new(),
|
||||||
|
"Theme",
|
||||||
|
UrlText::parse("https://example.com/theme").map_err(|error| error.to_string())?,
|
||||||
|
);
|
||||||
|
let bounds = Bounds::new(point(px(0.0), px(0.0)), size(px(640.0), px(480.0)));
|
||||||
|
|
||||||
|
assert_eq!(store.record_viewport_size(tab.id(), bounds, 1.0), WebSurfaceInputOutcome::Applied,);
|
||||||
|
let _ = store.ensure_surface(&tab, ProfileDataMode::Transient, &[]);
|
||||||
|
store.flush_runtime_for_test();
|
||||||
|
|
||||||
|
store.set_color_scheme(ColorScheme::Dark);
|
||||||
|
let _ = store.ensure_surface(&tab, ProfileDataMode::Transient, &[]);
|
||||||
|
store.flush_runtime_for_test();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
*COLOR_SCHEMES.lock().map_err(|_| "color scheme recorder lock was poisoned")?,
|
||||||
|
vec![ColorScheme::Light, ColorScheme::Dark],
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -13,8 +13,8 @@ use crate::services::servo_live::{
|
|||||||
#[path = "web_surface_worker_dispatch.rs"]
|
#[path = "web_surface_worker_dispatch.rs"]
|
||||||
mod dispatch;
|
mod dispatch;
|
||||||
use dispatch::{
|
use dispatch::{
|
||||||
dispatch_result, forward_permission_consumptions, preserve_latest_hover,
|
can_merge_consecutive_scroll, dispatch_result, forward_permission_consumptions,
|
||||||
request_has_ordered_input,
|
merge_consecutive_scroll, preserve_latest_hover, request_has_ordered_input,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Blocking transport for one profile-scoped Servo sidecar.
|
/// Blocking transport for one profile-scoped Servo sidecar.
|
||||||
@@ -225,11 +225,12 @@ impl LiveRuntimeWorker {
|
|||||||
}
|
}
|
||||||
let mut request = WorkerRequest::Ensure { generation, request: Box::new(request) };
|
let mut request = WorkerRequest::Ensure { generation, request: Box::new(request) };
|
||||||
if let Some(pending) = q.pending.get_mut(&tab_id) {
|
if let Some(pending) = q.pending.get_mut(&tab_id) {
|
||||||
let replace_tail = pending.back().is_some_and(|tail| {
|
if let Some(tail) = pending.back_mut()
|
||||||
matches!(tail, WorkerRequest::Poll { .. })
|
&& (can_merge_consecutive_scroll(&request, tail)
|
||||||
|| (!request_has_ordered_input(&request) && !request_has_ordered_input(tail))
|
|| matches!(tail, WorkerRequest::Poll { .. })
|
||||||
});
|
|| (!request_has_ordered_input(&request) && !request_has_ordered_input(tail)))
|
||||||
if replace_tail && let Some(tail) = pending.back_mut() {
|
{
|
||||||
|
merge_consecutive_scroll(&mut request, tail);
|
||||||
preserve_latest_hover(&mut request, tail);
|
preserve_latest_hover(&mut request, tail);
|
||||||
*tail = request;
|
*tail = request;
|
||||||
} else {
|
} else {
|
||||||
@@ -241,7 +242,6 @@ impl LiveRuntimeWorker {
|
|||||||
}
|
}
|
||||||
cvar.notify_one();
|
cvar.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn submit_poll(&self, generation: RequestGeneration, tab_id: String) -> bool {
|
pub(super) fn submit_poll(&self, generation: RequestGeneration, tab_id: String) -> bool {
|
||||||
let (lock, cvar) = &*self.queue;
|
let (lock, cvar) = &*self.queue;
|
||||||
let mut q = match lock.lock() {
|
let mut q = match lock.lock() {
|
||||||
|
|||||||
@@ -6,6 +6,56 @@ use super::{
|
|||||||
LiveRuntimeClient, LiveRuntimeClientError, RequestGeneration, WorkerRequest, WorkerResponse,
|
LiveRuntimeClient, LiveRuntimeClientError, RequestGeneration, WorkerRequest, WorkerResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub(super) fn can_merge_consecutive_scroll(
|
||||||
|
latest: &WorkerRequest,
|
||||||
|
previous: &WorkerRequest,
|
||||||
|
) -> bool {
|
||||||
|
let (
|
||||||
|
WorkerRequest::Ensure { request: latest, .. },
|
||||||
|
WorkerRequest::Ensure { request: previous, .. },
|
||||||
|
) = (latest, previous)
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
is_scroll_only(latest)
|
||||||
|
&& is_scroll_only(previous)
|
||||||
|
&& latest.profile_id == previous.profile_id
|
||||||
|
&& latest.url == previous.url
|
||||||
|
&& latest.width == previous.width
|
||||||
|
&& latest.height == previous.height
|
||||||
|
&& latest.page_zoom_percent == previous.page_zoom_percent
|
||||||
|
&& latest.device_pixel_ratio == previous.device_pixel_ratio
|
||||||
|
&& latest.color_scheme == previous.color_scheme
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn merge_consecutive_scroll(latest: &mut WorkerRequest, previous: &WorkerRequest) {
|
||||||
|
if !can_merge_consecutive_scroll(latest, previous) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let (
|
||||||
|
WorkerRequest::Ensure { request: latest, .. },
|
||||||
|
WorkerRequest::Ensure { request: previous, .. },
|
||||||
|
) = (latest, previous)
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
latest.scroll_delta_x = previous.scroll_delta_x.saturating_add(latest.scroll_delta_x);
|
||||||
|
latest.scroll_delta_y = previous.scroll_delta_y.saturating_add(latest.scroll_delta_y);
|
||||||
|
if latest.hover_x.is_none() && latest.hover_y.is_none() {
|
||||||
|
latest.hover_x = previous.hover_x;
|
||||||
|
latest.hover_y = previous.hover_y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_scroll_only(request: &crate::services::servo_live::ServoLiveEnsureRequest) -> bool {
|
||||||
|
(request.scroll_delta_x != 0 || request.scroll_delta_y != 0)
|
||||||
|
&& request.click_x.is_none()
|
||||||
|
&& request.click_y.is_none()
|
||||||
|
&& request.typed_text.is_none()
|
||||||
|
&& !request.site_permissions.iter().any(|permission| permission.state == "allow-once")
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn forward_permission_consumptions(
|
pub(super) fn forward_permission_consumptions(
|
||||||
client: &mut dyn LiveRuntimeClient,
|
client: &mut dyn LiveRuntimeClient,
|
||||||
response_tx: &mpsc::Sender<WorkerResponse>,
|
response_tx: &mpsc::Sender<WorkerResponse>,
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
use std::{
|
||||||
|
sync::{Arc, Mutex, mpsc},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::services::servo_live::{ServoLiveEnsureRequest, ServoLiveFrame};
|
||||||
|
|
||||||
|
use super::super::{
|
||||||
|
LiveRuntimeClient, LiveRuntimeClientError, LiveRuntimeWorker, RequestGeneration,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BlockingScrollClient {
|
||||||
|
deltas: Arc<Mutex<Vec<(i32, i32)>>>,
|
||||||
|
first_started_tx: Option<mpsc::Sender<()>>,
|
||||||
|
release_first_rx: mpsc::Receiver<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LiveRuntimeClient for BlockingScrollClient {
|
||||||
|
fn ensure(
|
||||||
|
&mut self,
|
||||||
|
request: ServoLiveEnsureRequest,
|
||||||
|
) -> Result<Option<ServoLiveFrame>, LiveRuntimeClientError> {
|
||||||
|
self.deltas
|
||||||
|
.lock()
|
||||||
|
.map_err(|_| "scroll recorder lock was poisoned".to_string())?
|
||||||
|
.push((request.scroll_delta_x, request.scroll_delta_y));
|
||||||
|
if let Some(first_started_tx) = self.first_started_tx.take() {
|
||||||
|
first_started_tx.send(()).map_err(|error| error.to_string())?;
|
||||||
|
self.release_first_rx.recv().map_err(|error| error.to_string())?;
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll(&mut self, _tab_id: String) -> Result<Option<ServoLiveFrame>, LiveRuntimeClientError> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close(&mut self, _tab_id: String) -> Result<(), LiveRuntimeClientError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn consecutive_scroll_updates_coalesce_without_losing_distance() -> Result<(), String> {
|
||||||
|
let deltas = Arc::new(Mutex::new(Vec::new()));
|
||||||
|
let client_deltas = deltas.clone();
|
||||||
|
let (first_started_tx, first_started_rx) = mpsc::channel();
|
||||||
|
let (release_first_tx, release_first_rx) = mpsc::channel();
|
||||||
|
let worker = LiveRuntimeWorker::new(move || {
|
||||||
|
Ok(Box::new(BlockingScrollClient {
|
||||||
|
deltas: client_deltas,
|
||||||
|
first_started_tx: Some(first_started_tx),
|
||||||
|
release_first_rx,
|
||||||
|
}))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
worker.submit_ensure(RequestGeneration::new(1), ensure_request(0));
|
||||||
|
first_started_rx.recv_timeout(Duration::from_secs(1)).map_err(|error| error.to_string())?;
|
||||||
|
for generation in 2..=101 {
|
||||||
|
worker.submit_ensure(RequestGeneration::new(generation), ensure_request(1));
|
||||||
|
}
|
||||||
|
release_first_tx.send(()).map_err(|error| error.to_string())?;
|
||||||
|
worker.wait_until_idle();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
*deltas.lock().map_err(|_| "scroll recorder lock was poisoned".to_string())?,
|
||||||
|
vec![(0, 0), (100, 100)]
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_request(scroll_delta: i32) -> ServoLiveEnsureRequest {
|
||||||
|
ServoLiveEnsureRequest {
|
||||||
|
tab_id: "tab-a".to_string(),
|
||||||
|
profile_id: "profile".to_string(),
|
||||||
|
url: "https://example.com/".to_string(),
|
||||||
|
width: 640,
|
||||||
|
height: 480,
|
||||||
|
page_zoom_percent: 100,
|
||||||
|
device_pixel_ratio: 1.0,
|
||||||
|
color_scheme: ely_domain::ColorScheme::Light,
|
||||||
|
scroll_delta_x: scroll_delta,
|
||||||
|
scroll_delta_y: scroll_delta,
|
||||||
|
scroll_point_x: (scroll_delta != 0).then_some(1),
|
||||||
|
scroll_point_y: (scroll_delta != 0).then_some(1),
|
||||||
|
click_x: None,
|
||||||
|
click_y: None,
|
||||||
|
hover_x: None,
|
||||||
|
hover_y: None,
|
||||||
|
typed_text: None,
|
||||||
|
site_permission_generation: 1,
|
||||||
|
site_permissions: Vec::new(),
|
||||||
|
allow_once_grants: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -450,6 +450,7 @@ fn ensure_request(tab_id: &str, input: RecordedInput) -> ServoLiveEnsureRequest
|
|||||||
height: 480,
|
height: 480,
|
||||||
page_zoom_percent: 100,
|
page_zoom_percent: 100,
|
||||||
device_pixel_ratio: 1.0,
|
device_pixel_ratio: 1.0,
|
||||||
|
color_scheme: ely_domain::ColorScheme::Light,
|
||||||
scroll_delta_x: i32::from(scroll),
|
scroll_delta_x: i32::from(scroll),
|
||||||
scroll_delta_y: i32::from(scroll),
|
scroll_delta_y: i32::from(scroll),
|
||||||
scroll_point_x: scroll.then_some(1),
|
scroll_point_x: scroll.then_some(1),
|
||||||
@@ -464,3 +465,6 @@ fn ensure_request(tab_id: &str, input: RecordedInput) -> ServoLiveEnsureRequest
|
|||||||
allow_once_grants: Vec::new(),
|
allow_once_grants: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[path = "web_surface_worker_scroll_tests.rs"]
|
||||||
|
mod scroll_tests;
|
||||||
|
|||||||
@@ -19,6 +19,24 @@ pub enum ThemeMode {
|
|||||||
Dark,
|
Dark,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
pub enum ColorScheme {
|
||||||
|
#[default]
|
||||||
|
Light,
|
||||||
|
Dark,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThemeMode {
|
||||||
|
pub fn resolve(self, system: ColorScheme) -> ColorScheme {
|
||||||
|
match self {
|
||||||
|
Self::System => system,
|
||||||
|
Self::Light => ColorScheme::Light,
|
||||||
|
Self::Dark => ColorScheme::Dark,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const DEFAULT_TRANSLUCENCY_PCT: u8 = 40;
|
pub const DEFAULT_TRANSLUCENCY_PCT: u8 = 40;
|
||||||
pub const MAX_TRANSLUCENCY_PCT: u8 = 100;
|
pub const MAX_TRANSLUCENCY_PCT: u8 = 100;
|
||||||
|
|
||||||
@@ -77,7 +95,7 @@ impl AppearanceSettings {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{AppearanceSettings, ThemeMode, WallpaperTheme};
|
use super::{AppearanceSettings, ColorScheme, ThemeMode, WallpaperTheme};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn default_settings_use_dawn_and_system_mode() {
|
fn default_settings_use_dawn_and_system_mode() {
|
||||||
@@ -103,6 +121,13 @@ mod tests {
|
|||||||
assert_eq!(settings.translucency_pct(), 75);
|
assert_eq!(settings.translucency_pct(), 75);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn theme_mode_resolves_explicit_and_system_color_schemes() {
|
||||||
|
assert_eq!(ThemeMode::System.resolve(ColorScheme::Dark), ColorScheme::Dark);
|
||||||
|
assert_eq!(ThemeMode::Light.resolve(ColorScheme::Dark), ColorScheme::Light);
|
||||||
|
assert_eq!(ThemeMode::Dark.resolve(ColorScheme::Light), ColorScheme::Dark);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn translucency_setter_clamps_above_max() {
|
fn translucency_setter_clamps_above_max() {
|
||||||
let mut settings = AppearanceSettings::default();
|
let mut settings = AppearanceSettings::default();
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ mod tab_group;
|
|||||||
mod url_text;
|
mod url_text;
|
||||||
|
|
||||||
pub use appearance::{
|
pub use appearance::{
|
||||||
AppearanceSettings, DEFAULT_TRANSLUCENCY_PCT, MAX_TRANSLUCENCY_PCT, ThemeMode, WallpaperTheme,
|
AppearanceSettings, ColorScheme, DEFAULT_TRANSLUCENCY_PCT, MAX_TRANSLUCENCY_PCT, ThemeMode,
|
||||||
|
WallpaperTheme,
|
||||||
};
|
};
|
||||||
pub use archive::{ArchiveSource, ArchivedTab};
|
pub use archive::{ArchiveSource, ArchivedTab};
|
||||||
pub use bookmark::BookmarkEntry;
|
pub use bookmark::BookmarkEntry;
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ use super::{
|
|||||||
},
|
},
|
||||||
live_request::{MAX_REQUEST_LINE_BYTES, RequestLineRead, read_request_line},
|
live_request::{MAX_REQUEST_LINE_BYTES, RequestLineRead, read_request_line},
|
||||||
live_session::{
|
live_session::{
|
||||||
LiveInput, LiveSession, apply_input, apply_layout, apply_permissions, bind_profile,
|
LiveInput, LiveSession, apply_color_scheme, apply_input, apply_layout, apply_permissions,
|
||||||
ensure_session,
|
bind_profile, ensure_session,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -159,6 +159,7 @@ fn handle_request(
|
|||||||
height,
|
height,
|
||||||
page_zoom_percent,
|
page_zoom_percent,
|
||||||
device_pixel_ratio,
|
device_pixel_ratio,
|
||||||
|
color_scheme,
|
||||||
scroll_delta_x,
|
scroll_delta_x,
|
||||||
scroll_delta_y,
|
scroll_delta_y,
|
||||||
scroll_point_x,
|
scroll_point_x,
|
||||||
@@ -184,6 +185,7 @@ fn handle_request(
|
|||||||
let session =
|
let session =
|
||||||
ensure_session(host, sessions, tab_id.clone(), &tab, &profile, width, height)?;
|
ensure_session(host, sessions, tab_id.clone(), &tab, &profile, width, height)?;
|
||||||
apply_layout(host, session, width, height, page_zoom_percent, device_pixel_ratio)?;
|
apply_layout(host, session, width, height, page_zoom_percent, device_pixel_ratio)?;
|
||||||
|
apply_color_scheme(host, session, color_scheme)?;
|
||||||
apply_permissions(
|
apply_permissions(
|
||||||
host,
|
host,
|
||||||
session,
|
session,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::{io, path::PathBuf};
|
use std::{io, path::PathBuf};
|
||||||
|
|
||||||
|
use ely_domain::ColorScheme;
|
||||||
use ely_servo_host::{
|
use ely_servo_host::{
|
||||||
ConsumedPermission, IOSurfaceHandle, RenderedFrame, ServoHostError, WebViewSnapshot,
|
ConsumedPermission, IOSurfaceHandle, RenderedFrame, ServoHostError, WebViewSnapshot,
|
||||||
WebViewState,
|
WebViewState,
|
||||||
@@ -10,7 +11,7 @@ use thiserror::Error;
|
|||||||
#[cfg(all(feature = "hardware-render", target_os = "macos"))]
|
#[cfg(all(feature = "hardware-render", target_os = "macos"))]
|
||||||
use super::iosurface_mach::IOSurfaceMachError;
|
use super::iosurface_mach::IOSurfaceMachError;
|
||||||
|
|
||||||
pub(super) const LIVE_PROTOCOL_VERSION: u32 = 3;
|
pub(super) const LIVE_PROTOCOL_VERSION: u32 = 4;
|
||||||
pub(super) const MAX_FRAME_DIMENSION: u32 = 16_384;
|
pub(super) const MAX_FRAME_DIMENSION: u32 = 16_384;
|
||||||
pub(super) const MAX_FRAME_BYTE_COUNT: usize = 256 * 1024 * 1024;
|
pub(super) const MAX_FRAME_BYTE_COUNT: usize = 256 * 1024 * 1024;
|
||||||
pub(super) const MAX_RESPONSE_HEADER_BYTES: usize = 256 * 1024;
|
pub(super) const MAX_RESPONSE_HEADER_BYTES: usize = 256 * 1024;
|
||||||
@@ -35,6 +36,7 @@ pub(super) enum LiveRequest {
|
|||||||
page_zoom_percent: u16,
|
page_zoom_percent: u16,
|
||||||
#[serde(default = "default_device_pixel_ratio")]
|
#[serde(default = "default_device_pixel_ratio")]
|
||||||
device_pixel_ratio: f32,
|
device_pixel_ratio: f32,
|
||||||
|
color_scheme: ColorScheme,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
scroll_delta_x: i32,
|
scroll_delta_x: i32,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@@ -401,7 +403,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ensure_defaults_optional_input_fields() -> Result<(), serde_json::Error> {
|
fn ensure_defaults_optional_input_fields() -> Result<(), serde_json::Error> {
|
||||||
let request = serde_json::from_str::<LiveRequest>(
|
let request = serde_json::from_str::<LiveRequest>(
|
||||||
r#"{"type":"ensure","tab_id":"tab","profile_id":"profile","url":"https://example.com","width":800,"height":600,"site_permission_generation":0}"#,
|
r#"{"type":"ensure","tab_id":"tab","profile_id":"profile","url":"https://example.com","width":800,"height":600,"color_scheme":"dark","site_permission_generation":0}"#,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
@@ -409,6 +411,7 @@ mod tests {
|
|||||||
LiveRequest::Ensure {
|
LiveRequest::Ensure {
|
||||||
page_zoom_percent: 100,
|
page_zoom_percent: 100,
|
||||||
device_pixel_ratio: 1.0,
|
device_pixel_ratio: 1.0,
|
||||||
|
color_scheme: ColorScheme::Dark,
|
||||||
scroll_delta_x: 0,
|
scroll_delta_x: 0,
|
||||||
scroll_delta_y: 0,
|
scroll_delta_y: 0,
|
||||||
ready_surface_ids,
|
ready_surface_ids,
|
||||||
@@ -419,19 +422,28 @@ mod tests {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ensure_requires_color_scheme() {
|
||||||
|
let request = serde_json::from_str::<LiveRequest>(
|
||||||
|
r#"{"type":"ensure","tab_id":"tab","profile_id":"profile","url":"https://example.com","width":800,"height":600,"site_permission_generation":0}"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(request.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn handshake_deserializes_protocol_version() -> Result<(), serde_json::Error> {
|
fn handshake_deserializes_protocol_version() -> Result<(), serde_json::Error> {
|
||||||
let request =
|
let request =
|
||||||
serde_json::from_str::<LiveRequest>(r#"{"type":"handshake","protocol_version":3}"#)?;
|
serde_json::from_str::<LiveRequest>(r#"{"type":"handshake","protocol_version":4}"#)?;
|
||||||
|
|
||||||
assert!(matches!(request, LiveRequest::Handshake { protocol_version: 3 }));
|
assert!(matches!(request, LiveRequest::Handshake { protocol_version: 4 }));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn site_permission_requires_revision() {
|
fn site_permission_requires_revision() {
|
||||||
let request = serde_json::from_str::<LiveRequest>(
|
let request = serde_json::from_str::<LiveRequest>(
|
||||||
r#"{"type":"ensure","tab_id":"tab","profile_id":"profile","url":"https://example.com","width":800,"height":600,"site_permission_generation":0,"site_permissions":[{"origin":"https://example.com","feature":"camera","state":"allow-once"}]}"#,
|
r#"{"type":"ensure","tab_id":"tab","profile_id":"profile","url":"https://example.com","width":800,"height":600,"color_scheme":"light","site_permission_generation":0,"site_permissions":[{"origin":"https://example.com","feature":"camera","state":"allow-once"}]}"#,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(request.is_err());
|
assert!(request.is_err());
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use std::collections::{HashMap, hash_map::Entry};
|
use std::collections::{HashMap, hash_map::Entry};
|
||||||
|
|
||||||
use ely_domain::{ProfileId, TabId, validate_zoom_percent};
|
use ely_domain::{ColorScheme, ProfileId, TabId, validate_zoom_percent};
|
||||||
use ely_servo_host::{
|
use ely_servo_host::{
|
||||||
HidpiScaleRequest, KeyboardTextRequest, MouseClickRequest, MouseHoverRequest, PageZoomRequest,
|
ColorSchemeRequest, HidpiScaleRequest, KeyboardTextRequest, MouseClickRequest,
|
||||||
PermissionDecision, PermissionSnapshotEntry, PermissionSnapshotRequest,
|
MouseHoverRequest, PageZoomRequest, PermissionDecision, PermissionSnapshotEntry,
|
||||||
PermissionSnapshotState, RenderedFrame, ResizeRequest, ScrollRequest, ServoHost,
|
PermissionSnapshotRequest, PermissionSnapshotState, RenderedFrame, ResizeRequest,
|
||||||
ServoSurfaceSize, SoftwareServoHost,
|
ScrollRequest, ServoHost, ServoSurfaceSize, SoftwareServoHost,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::live_protocol::{LiveSidecarError, LiveSitePermission};
|
use super::live_protocol::{LiveSidecarError, LiveSitePermission};
|
||||||
@@ -170,6 +170,18 @@ pub(super) fn apply_permissions(
|
|||||||
.map_err(LiveSidecarError::from)
|
.map_err(LiveSidecarError::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn apply_color_scheme(
|
||||||
|
host: &mut SoftwareServoHost,
|
||||||
|
session: &LiveSession,
|
||||||
|
color_scheme: ColorScheme,
|
||||||
|
) -> Result<(), LiveSidecarError> {
|
||||||
|
host.set_color_scheme(ColorSchemeRequest {
|
||||||
|
webview_id: session.webview_id.clone(),
|
||||||
|
color_scheme,
|
||||||
|
})
|
||||||
|
.map_err(LiveSidecarError::from)
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) struct LiveInput {
|
pub(super) struct LiveInput {
|
||||||
pub(super) scroll_delta_x: i32,
|
pub(super) scroll_delta_x: i32,
|
||||||
pub(super) scroll_delta_y: i32,
|
pub(super) scroll_delta_y: i32,
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ fn request_line_accepts_a_bounded_eof_terminated_frame() -> Result<(), Box<dyn s
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn padded_handshake_line(bytes: usize) -> Vec<u8> {
|
fn padded_handshake_line(bytes: usize) -> Vec<u8> {
|
||||||
let mut line = br#"{"type":"handshake","protocol_version":3}"#.to_vec();
|
let mut line = br#"{"type":"handshake","protocol_version":4}"#.to_vec();
|
||||||
assert!(bytes > line.len());
|
assert!(bytes > line.len());
|
||||||
line.resize(bytes - 1, b' ');
|
line.resize(bytes - 1, b' ');
|
||||||
line.push(b'\n');
|
line.push(b'\n');
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use ely_domain::{
|
use ely_domain::{
|
||||||
ProfileId, SiteOrigin, SitePermissionDecision, SitePermissionFeature, TabId, UrlText, WebViewId,
|
ColorScheme, ProfileId, SiteOrigin, SitePermissionDecision, SitePermissionFeature, TabId,
|
||||||
|
UrlText, WebViewId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::ServoHostError;
|
use crate::ServoHostError;
|
||||||
@@ -280,6 +281,12 @@ pub struct HidpiScaleRequest {
|
|||||||
pub scale_factor: f32,
|
pub scale_factor: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct ColorSchemeRequest {
|
||||||
|
pub webview_id: WebViewId,
|
||||||
|
pub color_scheme: ColorScheme,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct MouseClickRequest {
|
pub struct MouseClickRequest {
|
||||||
pub webview_id: WebViewId,
|
pub webview_id: WebViewId,
|
||||||
@@ -385,6 +392,8 @@ pub trait ServoHost {
|
|||||||
|
|
||||||
fn set_hidpi_scale(&mut self, request: HidpiScaleRequest) -> Result<(), ServoHostError>;
|
fn set_hidpi_scale(&mut self, request: HidpiScaleRequest) -> Result<(), ServoHostError>;
|
||||||
|
|
||||||
|
fn set_color_scheme(&mut self, request: ColorSchemeRequest) -> Result<(), ServoHostError>;
|
||||||
|
|
||||||
fn click(&mut self, request: MouseClickRequest) -> Result<(), ServoHostError>;
|
fn click(&mut self, request: MouseClickRequest) -> Result<(), ServoHostError>;
|
||||||
|
|
||||||
fn hover(&mut self, request: MouseHoverRequest) -> Result<(), ServoHostError>;
|
fn hover(&mut self, request: MouseHoverRequest) -> Result<(), ServoHostError>;
|
||||||
|
|||||||
@@ -22,11 +22,12 @@ pub use error::ServoHostError;
|
|||||||
#[cfg(feature = "hardware-render")]
|
#[cfg(feature = "hardware-render")]
|
||||||
pub use hardware_rendering_context::HardwareOffscreenContext;
|
pub use hardware_rendering_context::HardwareOffscreenContext;
|
||||||
pub use host::{
|
pub use host::{
|
||||||
ConsumedPermission, HidpiScaleRequest, KeyboardTextRequest, MAX_PAGE_TITLE_BYTES,
|
ColorSchemeRequest, ConsumedPermission, HidpiScaleRequest, KeyboardTextRequest,
|
||||||
MouseClickRequest, MouseDragRequest, MouseHoverRequest, NavigationRequest, PageZoomRequest,
|
MAX_PAGE_TITLE_BYTES, MouseClickRequest, MouseDragRequest, MouseHoverRequest,
|
||||||
PermissionDecision, PermissionSnapshotEntry, PermissionSnapshotRequest,
|
NavigationRequest, PageZoomRequest, PermissionDecision, PermissionSnapshotEntry,
|
||||||
PermissionSnapshotState, RenderedFrame, RenderedFrameSummary, ResizeRequest, ScrollRequest,
|
PermissionSnapshotRequest, PermissionSnapshotState, RenderedFrame, RenderedFrameSummary,
|
||||||
ServoHost, TouchTapRequest, WebViewSnapshot, WebViewSnapshotPending, WebViewState,
|
ResizeRequest, ScrollRequest, ServoHost, TouchTapRequest, WebViewSnapshot,
|
||||||
|
WebViewSnapshotPending, WebViewState,
|
||||||
};
|
};
|
||||||
pub use iosurface_handle::{IOSurfaceHandle, IOSurfaceIdentity};
|
pub use iosurface_handle::{IOSurfaceHandle, IOSurfaceIdentity};
|
||||||
#[cfg(feature = "servo-engine")]
|
#[cfg(feature = "servo-engine")]
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use dpi::PhysicalSize;
|
use dpi::PhysicalSize;
|
||||||
use ely_domain::{ProfileId, TabId, WebViewId};
|
use ely_domain::{ColorScheme, ProfileId, TabId, WebViewId};
|
||||||
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
|
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
|
||||||
use servo::{
|
use servo::{
|
||||||
DevicePoint, DeviceVector2D, Opts, Scroll, Servo, ServoBuilder, WebViewBuilder, WebViewPoint,
|
DevicePoint, DeviceVector2D, Opts, Scroll, Servo, ServoBuilder, Theme, WebViewBuilder,
|
||||||
WebViewVector,
|
WebViewPoint, WebViewVector,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[path = "runtime_context.rs"]
|
#[path = "runtime_context.rs"]
|
||||||
@@ -32,8 +32,8 @@ use runtime_preferences::ely_servo_preferences;
|
|||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ConsumedPermission, HidpiScaleRequest, KeyboardTextRequest, MouseClickRequest,
|
ColorSchemeRequest, ConsumedPermission, HidpiScaleRequest, KeyboardTextRequest,
|
||||||
MouseDragRequest, MouseHoverRequest, NavigationRequest, PageZoomRequest,
|
MouseClickRequest, MouseDragRequest, MouseHoverRequest, NavigationRequest, PageZoomRequest,
|
||||||
PermissionSnapshotRequest, RenderedFrame, ResizeRequest, ScrollRequest, ServoHost,
|
PermissionSnapshotRequest, RenderedFrame, ResizeRequest, ScrollRequest, ServoHost,
|
||||||
ServoHostError, TouchTapRequest, WebViewSnapshot, WebViewState,
|
ServoHostError, TouchTapRequest, WebViewSnapshot, WebViewState,
|
||||||
runtime_input::{
|
runtime_input::{
|
||||||
@@ -204,13 +204,15 @@ impl ServoHost for SoftwareServoHost {
|
|||||||
webview.delegate.set_state(WebViewState::Loading);
|
webview.delegate.set_state(WebViewState::Loading);
|
||||||
if should_create_initial_document {
|
if should_create_initial_document {
|
||||||
let hidpi_scale_factor = webview.webview.hidpi_scale_factor();
|
let hidpi_scale_factor = webview.webview.hidpi_scale_factor();
|
||||||
webview.webview = WebViewBuilder::new(&servo, webview.rendering_context.clone())
|
let replacement = WebViewBuilder::new(&servo, webview.rendering_context.clone())
|
||||||
.delegate(webview.delegate.clone())
|
.delegate(webview.delegate.clone())
|
||||||
.url(url)
|
.url(url)
|
||||||
// The live path pushes DPR before first navigation. Preserve that scale when
|
// The live path pushes DPR before first navigation. Preserve that scale when
|
||||||
// replacing the about:blank WebView so CSS viewport = physical surface / DPR.
|
// replacing the about:blank WebView so CSS viewport = physical surface / DPR.
|
||||||
.hidpi_scale_factor(hidpi_scale_factor)
|
.hidpi_scale_factor(hidpi_scale_factor)
|
||||||
.build();
|
.build();
|
||||||
|
replacement.notify_theme_change(servo_theme(webview.color_scheme));
|
||||||
|
webview.webview = replacement;
|
||||||
// The input-accepting invariant lives in `webview_for_input`.
|
// The input-accepting invariant lives in `webview_for_input`.
|
||||||
webview.webview.show();
|
webview.webview.show();
|
||||||
webview.webview.focus();
|
webview.webview.focus();
|
||||||
@@ -303,6 +305,18 @@ impl ServoHost for SoftwareServoHost {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_color_scheme(&mut self, request: ColorSchemeRequest) -> Result<(), ServoHostError> {
|
||||||
|
let webview = self
|
||||||
|
.webviews
|
||||||
|
.get_mut(&request.webview_id)
|
||||||
|
.ok_or_else(|| ServoHostError::WebViewNotFound { id: request.webview_id.clone() })?;
|
||||||
|
if webview.color_scheme != request.color_scheme {
|
||||||
|
webview.color_scheme = request.color_scheme;
|
||||||
|
webview.webview.notify_theme_change(servo_theme(request.color_scheme));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn hover(&mut self, request: MouseHoverRequest) -> Result<(), ServoHostError> {
|
fn hover(&mut self, request: MouseHoverRequest) -> Result<(), ServoHostError> {
|
||||||
let webview = self.webview_for_input(&request.webview_id)?;
|
let webview = self.webview_for_input(&request.webview_id)?;
|
||||||
send_mouse_hover(&webview.webview, request.x, request.y);
|
send_mouse_hover(&webview.webview, request.x, request.y);
|
||||||
@@ -440,6 +454,7 @@ impl SoftwareServoHost {
|
|||||||
webview,
|
webview,
|
||||||
delegate,
|
delegate,
|
||||||
requested_url: None,
|
requested_url: None,
|
||||||
|
color_scheme: ColorScheme::Light,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -472,3 +487,10 @@ impl SoftwareServoHost {
|
|||||||
Ok(webview)
|
Ok(webview)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn servo_theme(color_scheme: ColorScheme) -> Theme {
|
||||||
|
match color_scheme {
|
||||||
|
ColorScheme::Light => Theme::Light,
|
||||||
|
ColorScheme::Dark => Theme::Dark,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::{cell::Cell, cell::RefCell, rc::Rc};
|
use std::{cell::Cell, cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use ely_domain::{ProfileId, TabId, WebViewId};
|
use ely_domain::{ColorScheme, ProfileId, TabId, WebViewId};
|
||||||
use servo::{LoadStatus, RenderingContext, WebView, WebViewDelegate};
|
use servo::{LoadStatus, RenderingContext, WebView, WebViewDelegate};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ pub(super) struct HostWebView {
|
|||||||
pub(super) webview: WebView,
|
pub(super) webview: WebView,
|
||||||
pub(super) delegate: Rc<HostWebViewDelegate>,
|
pub(super) delegate: Rc<HostWebViewDelegate>,
|
||||||
pub(super) requested_url: Option<String>,
|
pub(super) requested_url: Option<String>,
|
||||||
|
pub(super) color_scheme: ColorScheme,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HostWebView {
|
impl HostWebView {
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
#![cfg(all(feature = "servo-engine", target_os = "macos"))]
|
||||||
|
|
||||||
|
use std::{error::Error, thread, time::Duration};
|
||||||
|
|
||||||
|
use ely_domain::{ProfileId, TabId, UrlText};
|
||||||
|
use ely_servo_host::{
|
||||||
|
NavigationRequest, ServoHost, ServoSurfaceSize, SoftwareServoHost, WebViewState,
|
||||||
|
};
|
||||||
|
|
||||||
|
const EXPECTED_PASS_COLOR: [u8; 3] = [31, 143, 76];
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn macos_fallback_preserves_css_family_style_and_language() -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut host = SoftwareServoHost::new(ServoSurfaceSize::new(320, 240))?;
|
||||||
|
let tab_id = TabId::new();
|
||||||
|
let profile_id = ProfileId::new();
|
||||||
|
let webview_id = host.create_webview(tab_id.clone(), profile_id)?;
|
||||||
|
|
||||||
|
host.navigate(NavigationRequest {
|
||||||
|
webview_id: webview_id.clone(),
|
||||||
|
tab_id,
|
||||||
|
url: UrlText::parse(font_probe_url())?,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
for _ in 0..5_000 {
|
||||||
|
host.tick();
|
||||||
|
let snapshot = host.snapshot(&webview_id)?;
|
||||||
|
if snapshot.has_pending_frame() {
|
||||||
|
host.paint(&webview_id)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let snapshot = host.snapshot(&webview_id)?;
|
||||||
|
if snapshot.state() == &WebViewState::Complete
|
||||||
|
&& host
|
||||||
|
.last_rendered_frame()
|
||||||
|
.is_ok_and(|frame| center_pixel_rgb(&frame) == EXPECTED_PASS_COLOR)
|
||||||
|
{
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
thread::sleep(Duration::from_millis(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
let actual = host.last_rendered_frame().map(|frame| center_pixel_rgb(&frame));
|
||||||
|
let title = host.snapshot(&webview_id)?.title().map(str::to_owned);
|
||||||
|
Err(format!(
|
||||||
|
"macOS font fallback probe failed with center pixel {actual:?} and title {title:?}"
|
||||||
|
)
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn font_probe_url() -> String {
|
||||||
|
let html = r#"<!doctype html>
|
||||||
|
<html><head><meta charset="utf-8"><title>starting</title><style>
|
||||||
|
html,body{margin:0;width:100%;height:100%;background:rgb(181,37,46)}
|
||||||
|
canvas{position:absolute;left:-10000px;top:-10000px}
|
||||||
|
</style></head><body><script>
|
||||||
|
try {
|
||||||
|
const cases = [
|
||||||
|
['zh-Hans', '搜索中文', '48px Arial, sans-serif', "48px 'PingFang SC'"],
|
||||||
|
['ja', '日本語検索', '48px Arial, sans-serif', "48px 'Hiragino Sans'"],
|
||||||
|
['ko', '한국어검색', '48px Arial, sans-serif', "48px 'Apple SD Gothic Neo'"],
|
||||||
|
['hi', 'हिन्दी', '48px Arial, sans-serif', "48px 'Kohinoor Devanagari'"],
|
||||||
|
['zh-Hans', '搜索中文', '48px Times New Roman, serif', "48px 'Songti SC'"]
|
||||||
|
];
|
||||||
|
function pixels(lang, text, font) {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.lang = lang;
|
||||||
|
canvas.width = 512;
|
||||||
|
canvas.height = 96;
|
||||||
|
document.body.appendChild(canvas);
|
||||||
|
const context = canvas.getContext('2d');
|
||||||
|
context.fillStyle = 'black';
|
||||||
|
context.font = font;
|
||||||
|
context.fillText(text, 8, 64);
|
||||||
|
return context.getImageData(0, 0, canvas.width, canvas.height).data;
|
||||||
|
}
|
||||||
|
function equal(left, right) {
|
||||||
|
if (left.length !== right.length) return false;
|
||||||
|
for (let index = 0; index < left.length; index++) {
|
||||||
|
if (left[index] !== right[index]) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const results = cases.map(([lang, text, requested, expected]) =>
|
||||||
|
equal(pixels(lang, text, requested), pixels(lang, text, expected))
|
||||||
|
);
|
||||||
|
const passed = results.every(Boolean);
|
||||||
|
document.title = results.map((result, index) => `${index}:${result}`).join(',');
|
||||||
|
document.body.style.background = passed ? 'rgb(31,143,76)' : 'rgb(181,37,46)';
|
||||||
|
} catch (error) {
|
||||||
|
document.title = `${error.name}:${error.message}`;
|
||||||
|
document.body.style.background = 'rgb(181,37,46)';
|
||||||
|
}
|
||||||
|
</script></body></html>"#;
|
||||||
|
format!("data:text/html,{}", percent_encode(html))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn percent_encode(value: &str) -> String {
|
||||||
|
value
|
||||||
|
.bytes()
|
||||||
|
.map(|byte| match byte {
|
||||||
|
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
|
||||||
|
(byte as char).to_string()
|
||||||
|
}
|
||||||
|
_ => format!("%{byte:02X}"),
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn center_pixel_rgb(frame: &ely_servo_host::RenderedFrame) -> [u8; 3] {
|
||||||
|
let index = (((frame.height() / 2) * frame.width() + frame.width() / 2) * 4) as usize;
|
||||||
|
let rgba = &frame.rgba_bytes()[index..index + 4];
|
||||||
|
[rgba[0], rgba[1], rgba[2]]
|
||||||
|
}
|
||||||
@@ -208,7 +208,7 @@ fn live_sidecar_drains_an_oversized_request_line_and_recovers_the_handshake()
|
|||||||
let mut stdin = child.stdin.take().ok_or_else(|| io::Error::other("missing stdin"))?;
|
let mut stdin = child.stdin.take().ok_or_else(|| io::Error::other("missing stdin"))?;
|
||||||
let stdout = child.stdout.take().ok_or_else(|| io::Error::other("missing stdout"))?;
|
let stdout = child.stdout.take().ok_or_else(|| io::Error::other("missing stdout"))?;
|
||||||
let mut stdout = BufReader::new(stdout);
|
let mut stdout = BufReader::new(stdout);
|
||||||
let mut oversized = br#"{"type":"handshake","protocol_version":3}"#.to_vec();
|
let mut oversized = br#"{"type":"handshake","protocol_version":4}"#.to_vec();
|
||||||
oversized.resize(MAX_REQUEST_LINE_BYTES + 4_096, b' ');
|
oversized.resize(MAX_REQUEST_LINE_BYTES + 4_096, b' ');
|
||||||
oversized.push(b'\n');
|
oversized.push(b'\n');
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ use super::pages::{
|
|||||||
pub(super) const WIDTH: u32 = 360;
|
pub(super) const WIDTH: u32 = 360;
|
||||||
pub(super) const HEIGHT: u32 = 240;
|
pub(super) const HEIGHT: u32 = 240;
|
||||||
pub(super) const RESPONSE_TIMEOUT: Duration = Duration::from_secs(20);
|
pub(super) const RESPONSE_TIMEOUT: Duration = Duration::from_secs(20);
|
||||||
pub(super) const LIVE_PROTOCOL_VERSION: u32 = 3;
|
pub(super) const LIVE_PROTOCOL_VERSION: u32 = 4;
|
||||||
pub(super) const MAX_FRAME_DIMENSION: u32 = 16_384;
|
pub(super) const MAX_FRAME_DIMENSION: u32 = 16_384;
|
||||||
const MAX_FRAME_BYTE_COUNT: usize = 256 * 1024 * 1024;
|
const MAX_FRAME_BYTE_COUNT: usize = 256 * 1024 * 1024;
|
||||||
|
|
||||||
@@ -38,6 +38,7 @@ pub(super) fn ensure_request(tab_id: &TabId, profile_id: &ProfileId, url: &str)
|
|||||||
"height": HEIGHT,
|
"height": HEIGHT,
|
||||||
"page_zoom_percent": 100,
|
"page_zoom_percent": 100,
|
||||||
"device_pixel_ratio": 1.0,
|
"device_pixel_ratio": 1.0,
|
||||||
|
"color_scheme": "light",
|
||||||
"site_permission_generation": 0,
|
"site_permission_generation": 0,
|
||||||
"site_permissions": [],
|
"site_permissions": [],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ use std::{
|
|||||||
process::{Command, Stdio},
|
process::{Command, Stdio},
|
||||||
};
|
};
|
||||||
|
|
||||||
use ely_domain::{ProfileId, SiteOrigin, SitePermissionFeature, TabId, UrlText};
|
use ely_domain::{ColorScheme, ProfileId, SiteOrigin, SitePermissionFeature, TabId, UrlText};
|
||||||
use ely_servo_host::{
|
use ely_servo_host::{
|
||||||
HidpiScaleRequest, KeyboardTextRequest, MouseClickRequest, MouseDragRequest, NavigationRequest,
|
ColorSchemeRequest, HidpiScaleRequest, KeyboardTextRequest, MouseClickRequest,
|
||||||
PageZoomRequest, PermissionDecision, PermissionSnapshotEntry, PermissionSnapshotRequest,
|
MouseDragRequest, NavigationRequest, PageZoomRequest, PermissionDecision,
|
||||||
PermissionSnapshotState, ResizeRequest, ScrollRequest, ServoHost, ServoHostError,
|
PermissionSnapshotEntry, PermissionSnapshotRequest, PermissionSnapshotState, ResizeRequest,
|
||||||
ServoSurfaceSize, SoftwareServoHost, TouchTapRequest, WebViewState,
|
ScrollRequest, ServoHost, ServoHostError, ServoSurfaceSize, SoftwareServoHost, TouchTapRequest,
|
||||||
|
WebViewState,
|
||||||
};
|
};
|
||||||
|
|
||||||
const MINIMUM_CONTENT_PIXELS: u64 = 1_000;
|
const MINIMUM_CONTENT_PIXELS: u64 = 1_000;
|
||||||
@@ -29,6 +30,7 @@ const CLICK_PROBE_URL: &str = "data:text/html,%3C!doctype%20html%3E%3Ctitle%3ECl
|
|||||||
const DRAG_PROBE_URL: &str = "data:text/html,%3C%21doctype%20html%3E%3Ctitle%3EDrag%20Probe%3C%2Ftitle%3E%3Cstyle%3Ebody%7Bmargin%3A0%3Bbackground%3A%23f6d365%3B%7Dbutton%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A80px%3Bwidth%3A220px%3Bheight%3A90px%3Bfont%3A28px%20sans-serif%3Bbackground%3A%23ffffff%3Bcolor%3A%23111111%3B%7D%3C%2Fstyle%3E%3Cbutton%20id%3Dbox%3EDrag%3C%2Fbutton%3E%3Cscript%3Elet%20dragging%3Dfalse%3Bconst%20box%3Ddocument.getElementById%28%27box%27%29%3BaddEventListener%28%27mousedown%27%2Cevent%3D%3E%7Bif%28event.target%3D%3D%3Dbox%29%7Bdragging%3Dtrue%3B%7D%7D%29%3BaddEventListener%28%27mousemove%27%2Cevent%3D%3E%7Bif%28dragging%26%26event.clientX%3E280%29%7Bdocument.body.style.background%3D%27%230039ff%27%3Bdocument.title%3D%27Dragged%27%3Bbox.textContent%3D%27Dragged%27%3B%7D%7D%29%3BaddEventListener%28%27mouseup%27%2C%28%29%3D%3E%7Bdragging%3Dfalse%3B%7D%29%3B%3C%2Fscript%3E";
|
const DRAG_PROBE_URL: &str = "data:text/html,%3C%21doctype%20html%3E%3Ctitle%3EDrag%20Probe%3C%2Ftitle%3E%3Cstyle%3Ebody%7Bmargin%3A0%3Bbackground%3A%23f6d365%3B%7Dbutton%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A80px%3Bwidth%3A220px%3Bheight%3A90px%3Bfont%3A28px%20sans-serif%3Bbackground%3A%23ffffff%3Bcolor%3A%23111111%3B%7D%3C%2Fstyle%3E%3Cbutton%20id%3Dbox%3EDrag%3C%2Fbutton%3E%3Cscript%3Elet%20dragging%3Dfalse%3Bconst%20box%3Ddocument.getElementById%28%27box%27%29%3BaddEventListener%28%27mousedown%27%2Cevent%3D%3E%7Bif%28event.target%3D%3D%3Dbox%29%7Bdragging%3Dtrue%3B%7D%7D%29%3BaddEventListener%28%27mousemove%27%2Cevent%3D%3E%7Bif%28dragging%26%26event.clientX%3E280%29%7Bdocument.body.style.background%3D%27%230039ff%27%3Bdocument.title%3D%27Dragged%27%3Bbox.textContent%3D%27Dragged%27%3B%7D%7D%29%3BaddEventListener%28%27mouseup%27%2C%28%29%3D%3E%7Bdragging%3Dfalse%3B%7D%29%3B%3C%2Fscript%3E";
|
||||||
const TOUCH_PROBE_URL: &str = "data:text/html,%3C%21doctype%20html%3E%3Ctitle%3ETouch%20Probe%3C%2Ftitle%3E%3Cstyle%3Ebody%7Bmargin%3A0%3Bbackground%3A%23c7f5d9%3B%7Dbutton%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A80px%3Bwidth%3A220px%3Bheight%3A90px%3Bfont%3A28px%20sans-serif%3Bbackground%3A%23ffffff%3Bcolor%3A%23111111%3Btouch-action%3Amanipulation%3B%7D%3C%2Fstyle%3E%3Cbutton%20ontouchstart%3D%22document.body.dataset.touch%3D%27start%27%3B%22%20onpointerdown%3D%22if%28%21document.body.dataset.pointerType%29%7Bdocument.body.dataset.pointerType%3Devent.pointerType%3B%7D%22%20onclick%3D%22if%28document.body.dataset.pointerType%21%3D%3D%27touch%27%29%7Bdocument.title%3Ddocument.body.dataset.pointerType%3Breturn%3B%7Ddocument.body.style.background%3D%27%230039ff%27%3Bdocument.title%3D%27Touched%27%3Bthis.textContent%3D%27Touched%27%3B%22%3ETap%3C%2Fbutton%3E";
|
const TOUCH_PROBE_URL: &str = "data:text/html,%3C%21doctype%20html%3E%3Ctitle%3ETouch%20Probe%3C%2Ftitle%3E%3Cstyle%3Ebody%7Bmargin%3A0%3Bbackground%3A%23c7f5d9%3B%7Dbutton%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A80px%3Bwidth%3A220px%3Bheight%3A90px%3Bfont%3A28px%20sans-serif%3Bbackground%3A%23ffffff%3Bcolor%3A%23111111%3Btouch-action%3Amanipulation%3B%7D%3C%2Fstyle%3E%3Cbutton%20ontouchstart%3D%22document.body.dataset.touch%3D%27start%27%3B%22%20onpointerdown%3D%22if%28%21document.body.dataset.pointerType%29%7Bdocument.body.dataset.pointerType%3Devent.pointerType%3B%7D%22%20onclick%3D%22if%28document.body.dataset.pointerType%21%3D%3D%27touch%27%29%7Bdocument.title%3Ddocument.body.dataset.pointerType%3Breturn%3B%7Ddocument.body.style.background%3D%27%230039ff%27%3Bdocument.title%3D%27Touched%27%3Bthis.textContent%3D%27Touched%27%3B%22%3ETap%3C%2Fbutton%3E";
|
||||||
const TEXT_PROBE_URL: &str = "data:text/html,%3C!doctype%20html%3E%3Ctitle%3EText%20Probe%3C%2Ftitle%3E%3Cstyle%3Ebody%7Bmargin%3A0%3Bbackground%3A%23d9e8ff%3Bfont%3A28px%20sans-serif%3B%7Dinput%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A80px%3Bwidth%3A260px%3Bheight%3A70px%3Bfont%3A28px%20sans-serif%3B%7Doutput%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A180px%3Bfont%3A32px%20sans-serif%3B%7D%3C%2Fstyle%3E%3Cinput%20id%3Dq%20autofocus%20oninput%3D%22document.body.style.background%3D%27%230039ff%27%3Bdocument.getElementById%28%27out%27%29.textContent%3Dthis.value%3B%22%3E%3Coutput%20id%3Dout%3Eempty%3C%2Foutput%3E";
|
const TEXT_PROBE_URL: &str = "data:text/html,%3C!doctype%20html%3E%3Ctitle%3EText%20Probe%3C%2Ftitle%3E%3Cstyle%3Ebody%7Bmargin%3A0%3Bbackground%3A%23d9e8ff%3Bfont%3A28px%20sans-serif%3B%7Dinput%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A80px%3Bwidth%3A260px%3Bheight%3A70px%3Bfont%3A28px%20sans-serif%3B%7Doutput%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A180px%3Bfont%3A32px%20sans-serif%3B%7D%3C%2Fstyle%3E%3Cinput%20id%3Dq%20autofocus%20oninput%3D%22document.body.style.background%3D%27%230039ff%27%3Bdocument.getElementById%28%27out%27%29.textContent%3Dthis.value%3B%22%3E%3Coutput%20id%3Dout%3Eempty%3C%2Foutput%3E";
|
||||||
|
const THEME_PROBE_URL: &str = "data:text/html,%3C!doctype%20html%3E%3Ctitle%3ETheme%20Probe%3C%2Ftitle%3E%3Cstyle%3Ebody%7Bmargin%3A0%3Bbackground%3A%23f1e2d3%3B%7D%40media%28prefers-color-scheme%3Adark%29%7Bbody%7Bbackground%3A%23112233%3B%7D%7D%3C%2Fstyle%3E";
|
||||||
const TEXT_PROBE_VALUE: &str = "ely42";
|
const TEXT_PROBE_VALUE: &str = "ely42";
|
||||||
|
|
||||||
struct PrdSiteCompatibilityCase {
|
struct PrdSiteCompatibilityCase {
|
||||||
@@ -235,6 +237,29 @@ fn exercise_real_servo_webview_lifecycle() -> Result<(), Box<dyn Error>> {
|
|||||||
"mismatch: {mismatch:?}"
|
"mismatch: {mismatch:?}"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
host.set_color_scheme(ColorSchemeRequest {
|
||||||
|
webview_id: webview_id.clone(),
|
||||||
|
color_scheme: ColorScheme::Dark,
|
||||||
|
})?;
|
||||||
|
host.navigate(NavigationRequest {
|
||||||
|
webview_id: webview_id.clone(),
|
||||||
|
tab_id: tab_id.clone(),
|
||||||
|
url: UrlText::parse(THEME_PROBE_URL)?,
|
||||||
|
})?;
|
||||||
|
wait_for_rendered_webview_with_center_pixel(&mut host, &webview_id, None, [17, 34, 51])?;
|
||||||
|
|
||||||
|
let previous_frame_hash = host.last_rendered_frame()?.sample_hash();
|
||||||
|
host.set_color_scheme(ColorSchemeRequest {
|
||||||
|
webview_id: webview_id.clone(),
|
||||||
|
color_scheme: ColorScheme::Light,
|
||||||
|
})?;
|
||||||
|
wait_for_rendered_webview_with_center_pixel(
|
||||||
|
&mut host,
|
||||||
|
&webview_id,
|
||||||
|
Some(previous_frame_hash),
|
||||||
|
[241, 226, 211],
|
||||||
|
)?;
|
||||||
|
|
||||||
let url = UrlText::parse(CLICK_PROBE_URL)?;
|
let url = UrlText::parse(CLICK_PROBE_URL)?;
|
||||||
|
|
||||||
host.navigate(NavigationRequest { webview_id: webview_id.clone(), tab_id, url })?;
|
host.navigate(NavigationRequest { webview_id: webview_id.clone(), tab_id, url })?;
|
||||||
|
|||||||
@@ -50,8 +50,9 @@ transfers the IOSurface send right; the JSON port number is diagnostic metadata.
|
|||||||
|
|
||||||
The protocol supports:
|
The protocol supports:
|
||||||
|
|
||||||
- `handshake`: verify protocol version `3` before accepting browser commands.
|
- `handshake`: verify protocol version `4` before accepting browser commands.
|
||||||
- `ensure`: create or update a WebView, navigation, viewport, zoom, permissions, and input.
|
- `ensure`: create or update a WebView, navigation, viewport, zoom, resolved color scheme,
|
||||||
|
permissions, and input.
|
||||||
- `poll`: advance Servo and return pending frame or metadata state.
|
- `poll`: advance Servo and return pending frame or metadata state.
|
||||||
- `close`: destroy one tab's WebView.
|
- `close`: destroy one tab's WebView.
|
||||||
- `shutdown`: acknowledge graceful process shutdown so Servo flushes profile storage.
|
- `shutdown`: acknowledge graceful process shutdown so Servo flushes profile storage.
|
||||||
@@ -108,8 +109,10 @@ The integration gates prove these invariants with real Servo networking:
|
|||||||
|
|
||||||
## Upstream Servo Route
|
## Upstream Servo Route
|
||||||
|
|
||||||
ELY pins Servo upstream commit `a87625150758fe91a29e7f6553ff2f798b87027e`, workspace version
|
ELY pins fork commit `8f6850ceb0bd92aea4127d8078ebb2c0485e7f39`, based on Servo upstream
|
||||||
`0.4.0`, in `Cargo.toml` and `Cargo.lock`.
|
commit `a87625150758fe91a29e7f6553ff2f798b87027e`, workspace version `0.4.0`, in
|
||||||
|
`Cargo.toml` and `Cargo.lock`. The fork moves macOS CoreText fallback ahead of Servo's static
|
||||||
|
fallback-family list so the first CSS font preserves its serif or sans-serif cascade and language.
|
||||||
|
|
||||||
Servo's rendering lifecycle remains authoritative inside each sidecar:
|
Servo's rendering lifecycle remains authoritative inside each sidecar:
|
||||||
|
|
||||||
@@ -128,6 +131,8 @@ Relevant upstream evidence at the pinned revision:
|
|||||||
- `ports/servoshell/window.rs` repaints with `webview.paint()` and presents the context.
|
- `ports/servoshell/window.rs` repaints with `webview.paint()` and presents the context.
|
||||||
- `ports/servoshell/running_app_state.rs` maps `notify_new_frame_ready` to repaint scheduling.
|
- `ports/servoshell/running_app_state.rs` maps `notify_new_frame_ready` to repaint scheduling.
|
||||||
- `components/paint/paint.rs` owns one WebRender painter per rendering context.
|
- `components/paint/paint.rs` owns one WebRender painter per rendering context.
|
||||||
|
- `components/fonts/font.rs` asks CoreText for a style- and language-compatible macOS fallback
|
||||||
|
before consulting the cross-platform fallback-family list.
|
||||||
|
|
||||||
## Ownership
|
## Ownership
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,13 @@ ely_append_encoded_rustflag() {
|
|||||||
export CARGO_ENCODED_RUSTFLAGS
|
export CARGO_ENCODED_RUSTFLAGS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ely_append_c_family_flag() {
|
||||||
|
local flag="$1"
|
||||||
|
CFLAGS="${CFLAGS:+${CFLAGS} }${flag}"
|
||||||
|
CXXFLAGS="${CXXFLAGS:+${CXXFLAGS} }${flag}"
|
||||||
|
export CFLAGS CXXFLAGS
|
||||||
|
}
|
||||||
|
|
||||||
ely_path_variants() {
|
ely_path_variants() {
|
||||||
local path="$1"
|
local path="$1"
|
||||||
local canonical_path=""
|
local canonical_path=""
|
||||||
@@ -34,6 +41,14 @@ ely_add_release_path_remap() {
|
|||||||
while IFS= read -r variant; do
|
while IFS= read -r variant; do
|
||||||
[[ -n "${variant}" ]] || continue
|
[[ -n "${variant}" ]] || continue
|
||||||
ely_append_encoded_rustflag "--remap-path-prefix=${variant}=${replacement}"
|
ely_append_encoded_rustflag "--remap-path-prefix=${variant}=${replacement}"
|
||||||
|
case "$(uname -s)" in
|
||||||
|
MINGW*|MSYS*|CYGWIN*)
|
||||||
|
ely_append_c_family_flag "/pathmap:${variant}=${replacement}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
ely_append_c_family_flag "-ffile-prefix-map=${variant}=${replacement}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
done < <(ely_path_variants "${path}")
|
done < <(ely_path_variants "${path}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user