mirror of
https://github.com/servo/servo.git
synced 2025-07-23 07:13:52 +01:00
Implemented Houdini worklets.
This commit is contained in:
parent
abb2985ffe
commit
af8436c9be
34 changed files with 1209 additions and 17 deletions
46
Cargo.lock
generated
46
Cargo.lock
generated
|
@ -194,6 +194,11 @@ name = "bit-vec"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
|
@ -2142,6 +2147,15 @@ dependencies = [
|
||||||
"time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
"time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pulldown-cmark"
|
||||||
|
version = "0.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quasi"
|
name = "quasi"
|
||||||
version = "0.32.0"
|
version = "0.32.0"
|
||||||
|
@ -2351,6 +2365,7 @@ dependencies = [
|
||||||
"smallvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"smallvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"style 0.0.1",
|
"style 0.0.1",
|
||||||
"style_traits 0.0.1",
|
"style_traits 0.0.1",
|
||||||
|
"swapper 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
"time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"tinyfiledialogs 2.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tinyfiledialogs 2.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -2407,7 +2422,6 @@ dependencies = [
|
||||||
name = "script_traits"
|
name = "script_traits"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"app_units 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"bluetooth_traits 0.0.1",
|
"bluetooth_traits 0.0.1",
|
||||||
"canvas_traits 0.0.1",
|
"canvas_traits 0.0.1",
|
||||||
"cookie 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cookie 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -2738,6 +2752,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
name = "size_of_test"
|
name = "size_of_test"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "skeptic"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"pulldown-cmark 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
@ -2882,6 +2905,14 @@ dependencies = [
|
||||||
"style_traits 0.0.1",
|
"style_traits 0.0.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "swapper"
|
||||||
|
version = "0.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"skeptic 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "0.11.11"
|
version = "0.11.11"
|
||||||
|
@ -2968,6 +2999,14 @@ dependencies = [
|
||||||
"gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempdir"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tendril"
|
name = "tendril"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
|
@ -3395,6 +3434,7 @@ dependencies = [
|
||||||
"checksum bindgen 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ccaf8958532d7e570e905266ee2dc1094c3e5c3c3cfc2c299368747a30a5e654"
|
"checksum bindgen 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ccaf8958532d7e570e905266ee2dc1094c3e5c3c3cfc2c299368747a30a5e654"
|
||||||
"checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c"
|
"checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c"
|
||||||
"checksum bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5b97c2c8e8bbb4251754f559df8af22fb264853c7d009084a576cdf12565089d"
|
"checksum bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5b97c2c8e8bbb4251754f559df8af22fb264853c7d009084a576cdf12565089d"
|
||||||
|
"checksum bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4f67931368edf3a9a51d29886d245f1c3db2f1ef0dcc9e35ff70341b78c10d23"
|
||||||
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
||||||
"checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4"
|
"checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4"
|
||||||
"checksum bitreader 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "80b13e2ab064ff3aa0bdbf1eff533f9822dc37899821f5f98c67f263eab51707"
|
"checksum bitreader 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "80b13e2ab064ff3aa0bdbf1eff533f9822dc37899821f5f98c67f263eab51707"
|
||||||
|
@ -3550,6 +3590,7 @@ dependencies = [
|
||||||
"checksum png 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3cb773e9a557edb568ce9935cf783e3cdcabe06a9449d41b3e5506d88e582c82"
|
"checksum png 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3cb773e9a557edb568ce9935cf783e3cdcabe06a9449d41b3e5506d88e582c82"
|
||||||
"checksum precomputed-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf1fc3616b3ef726a847f2cd2388c646ef6a1f1ba4835c2629004da48184150"
|
"checksum precomputed-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf1fc3616b3ef726a847f2cd2388c646ef6a1f1ba4835c2629004da48184150"
|
||||||
"checksum procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f566249236c6ca4340f7ca78968271f0ed2b0f234007a61b66f9ecd0af09260"
|
"checksum procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f566249236c6ca4340f7ca78968271f0ed2b0f234007a61b66f9ecd0af09260"
|
||||||
|
"checksum pulldown-cmark 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1058d7bb927ca067656537eec4e02c2b4b70eaaa129664c5b90c111e20326f41"
|
||||||
"checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3"
|
"checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3"
|
||||||
"checksum quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9e25fa23c044c1803f43ca59c98dac608976dd04ce799411edd58ece776d4"
|
"checksum quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9e25fa23c044c1803f43ca59c98dac608976dd04ce799411edd58ece776d4"
|
||||||
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
||||||
|
@ -3586,12 +3627,14 @@ dependencies = [
|
||||||
"checksum signpost 0.1.0 (git+https://github.com/pcwalton/signpost.git)" = "<none>"
|
"checksum signpost 0.1.0 (git+https://github.com/pcwalton/signpost.git)" = "<none>"
|
||||||
"checksum simd 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a94d14a2ae1f1f110937de5fb69e494372560181c7e1739a097fcc2cee37ba0"
|
"checksum simd 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a94d14a2ae1f1f110937de5fb69e494372560181c7e1739a097fcc2cee37ba0"
|
||||||
"checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537"
|
"checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537"
|
||||||
|
"checksum skeptic 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd7d8dc1315094150052d0ab767840376335a98ac66ef313ff911cdf439a5b69"
|
||||||
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
|
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
|
||||||
"checksum smallvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4f8266519bc1d17d0b5b16f6c21295625d562841c708f6376f49028a43e9c11e"
|
"checksum smallvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4f8266519bc1d17d0b5b16f6c21295625d562841c708f6376f49028a43e9c11e"
|
||||||
"checksum string_cache 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f55fba06c5e294108f22e8512eb598cb13388a117991e411a8df8f41a1219a75"
|
"checksum string_cache 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f55fba06c5e294108f22e8512eb598cb13388a117991e411a8df8f41a1219a75"
|
||||||
"checksum string_cache_codegen 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "479cde50c3539481f33906a387f2bd17c8e87cb848c35b6021d41fb81ff9b4d7"
|
"checksum string_cache_codegen 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "479cde50c3539481f33906a387f2bd17c8e87cb848c35b6021d41fb81ff9b4d7"
|
||||||
"checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc"
|
"checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc"
|
||||||
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
|
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
|
||||||
|
"checksum swapper 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca610b32bb8bfc5e7f705480c3a1edfeb70b6582495d343872c8bee0dcf758c"
|
||||||
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
|
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
|
||||||
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
|
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
|
||||||
"checksum synstructure 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ccc9780bf1aa601943988c2876ab22413c01ad1739689aa6af18d0aa0b3f38b"
|
"checksum synstructure 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ccc9780bf1aa601943988c2876ab22413c01ad1739689aa6af18d0aa0b3f38b"
|
||||||
|
@ -3600,6 +3643,7 @@ dependencies = [
|
||||||
"checksum syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13ad4762fe52abc9f4008e85c4fb1b1fe3aa91ccb99ff4826a439c7c598e1047"
|
"checksum syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13ad4762fe52abc9f4008e85c4fb1b1fe3aa91ccb99ff4826a439c7c598e1047"
|
||||||
"checksum syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6e0e4dbae163dd98989464c23dd503161b338790640e11537686f2ef0f25c791"
|
"checksum syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6e0e4dbae163dd98989464c23dd503161b338790640e11537686f2ef0f25c791"
|
||||||
"checksum target_build_utils 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f42dc058080c19c6a58bdd1bf962904ee4f5ef1fe2a81b529f31dacc750c679f"
|
"checksum target_build_utils 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f42dc058080c19c6a58bdd1bf962904ee4f5ef1fe2a81b529f31dacc750c679f"
|
||||||
|
"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
|
||||||
"checksum tendril 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4ce04c250d202db8004921e3d3bc95eaa4f2126c6937a428ae39d12d0e38df62"
|
"checksum tendril 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4ce04c250d202db8004921e3d3bc95eaa4f2126c6937a428ae39d12d0e38df62"
|
||||||
"checksum term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d168af3930b369cfe245132550579d47dfd873d69470755a19c2c6568dbbd989"
|
"checksum term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d168af3930b369cfe245132550579d47dfd873d69470755a19c2c6568dbbd989"
|
||||||
"checksum term_size 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "07b6c1ac5b3fffd75073276bca1ceed01f67a28537097a2a9539e116e50fb21a"
|
"checksum term_size 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "07b6c1ac5b3fffd75073276bca1ceed01f67a28537097a2a9539e116e50fb21a"
|
||||||
|
|
|
@ -150,6 +150,7 @@ impl Formattable for ProfilerCategory {
|
||||||
ProfilerCategory::ScriptEnterFullscreen => "Script Enter Fullscreen",
|
ProfilerCategory::ScriptEnterFullscreen => "Script Enter Fullscreen",
|
||||||
ProfilerCategory::ScriptExitFullscreen => "Script Exit Fullscreen",
|
ProfilerCategory::ScriptExitFullscreen => "Script Exit Fullscreen",
|
||||||
ProfilerCategory::ScriptWebVREvent => "Script WebVR Event",
|
ProfilerCategory::ScriptWebVREvent => "Script WebVR Event",
|
||||||
|
ProfilerCategory::ScriptWorkletEvent => "Script Worklet Event",
|
||||||
ProfilerCategory::ApplicationHeartbeat => "Application Heartbeat",
|
ProfilerCategory::ApplicationHeartbeat => "Application Heartbeat",
|
||||||
};
|
};
|
||||||
format!("{}{}", padding, name)
|
format!("{}{}", padding, name)
|
||||||
|
|
|
@ -89,6 +89,7 @@ pub enum ProfilerCategory {
|
||||||
ScriptEnterFullscreen = 0x77,
|
ScriptEnterFullscreen = 0x77,
|
||||||
ScriptExitFullscreen = 0x78,
|
ScriptExitFullscreen = 0x78,
|
||||||
ScriptWebVREvent = 0x79,
|
ScriptWebVREvent = 0x79,
|
||||||
|
ScriptWorkletEvent = 0x7a,
|
||||||
ApplicationHeartbeat = 0x90,
|
ApplicationHeartbeat = 0x90,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,7 @@ servo_url = {path = "../url"}
|
||||||
smallvec = "0.3"
|
smallvec = "0.3"
|
||||||
style = {path = "../style"}
|
style = {path = "../style"}
|
||||||
style_traits = {path = "../style_traits"}
|
style_traits = {path = "../style_traits"}
|
||||||
|
swapper = "0.0.4"
|
||||||
time = "0.1.12"
|
time = "0.1.12"
|
||||||
unicode-segmentation = "1.1.0"
|
unicode-segmentation = "1.1.0"
|
||||||
url = {version = "1.2", features = ["heap_size", "query_encoding"]}
|
url = {version = "1.2", features = ["heap_size", "query_encoding"]}
|
||||||
|
|
|
@ -23,12 +23,17 @@
|
||||||
//! as JS roots.
|
//! as JS roots.
|
||||||
|
|
||||||
use core::nonzero::NonZero;
|
use core::nonzero::NonZero;
|
||||||
|
use dom::bindings::conversions::ToJSValConvertible;
|
||||||
|
use dom::bindings::error::Error;
|
||||||
use dom::bindings::js::Root;
|
use dom::bindings::js::Root;
|
||||||
use dom::bindings::reflector::{DomObject, Reflector};
|
use dom::bindings::reflector::{DomObject, Reflector};
|
||||||
use dom::bindings::trace::trace_reflector;
|
use dom::bindings::trace::trace_reflector;
|
||||||
use dom::promise::Promise;
|
use dom::promise::Promise;
|
||||||
|
use js::jsapi::JSAutoCompartment;
|
||||||
use js::jsapi::JSTracer;
|
use js::jsapi::JSTracer;
|
||||||
use libc;
|
use libc;
|
||||||
|
use script_thread::Runnable;
|
||||||
|
use script_thread::ScriptThread;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||||
use std::collections::hash_map::HashMap;
|
use std::collections::hash_map::HashMap;
|
||||||
|
@ -115,6 +120,40 @@ impl TrustedPromise {
|
||||||
promise
|
promise
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A runnable which will reject the promise.
|
||||||
|
#[allow(unrooted_must_root)]
|
||||||
|
pub fn reject_runnable(self, error: Error) -> impl Runnable + Send {
|
||||||
|
struct RejectPromise(TrustedPromise, Error);
|
||||||
|
impl Runnable for RejectPromise {
|
||||||
|
fn main_thread_handler(self: Box<Self>, script_thread: &ScriptThread) {
|
||||||
|
let this = *self;
|
||||||
|
let cx = script_thread.get_cx();
|
||||||
|
let promise = this.0.root();
|
||||||
|
let _ac = JSAutoCompartment::new(cx, promise.reflector().get_jsobject().get());
|
||||||
|
promise.reject_error(cx, this.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RejectPromise(self, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A runnable which will resolve the promise.
|
||||||
|
#[allow(unrooted_must_root)]
|
||||||
|
pub fn resolve_runnable<T>(self, value: T) -> impl Runnable + Send where
|
||||||
|
T: ToJSValConvertible + Send
|
||||||
|
{
|
||||||
|
struct ResolvePromise<T>(TrustedPromise, T);
|
||||||
|
impl<T: ToJSValConvertible> Runnable for ResolvePromise<T> {
|
||||||
|
fn main_thread_handler(self: Box<Self>, script_thread: &ScriptThread) {
|
||||||
|
let this = *self;
|
||||||
|
let cx = script_thread.get_cx();
|
||||||
|
let promise = this.0.root();
|
||||||
|
let _ac = JSAutoCompartment::new(cx, promise.reflector().get_jsobject().get());
|
||||||
|
promise.resolve_native(cx, &this.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ResolvePromise(self, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A safe wrapper around a raw pointer to a DOM object that can be
|
/// A safe wrapper around a raw pointer to a DOM object that can be
|
||||||
|
|
|
@ -19,6 +19,7 @@ use dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
|
||||||
use dom::eventtarget::EventTarget;
|
use dom::eventtarget::EventTarget;
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
use dom::workerglobalscope::WorkerGlobalScope;
|
use dom::workerglobalscope::WorkerGlobalScope;
|
||||||
|
use dom::workletglobalscope::WorkletGlobalScope;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use ipc_channel::ipc::IpcSender;
|
use ipc_channel::ipc::IpcSender;
|
||||||
use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
|
use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
|
||||||
|
@ -259,6 +260,10 @@ impl GlobalScope {
|
||||||
// https://html.spec.whatwg.org/multipage/#script-settings-for-workers:api-base-url
|
// https://html.spec.whatwg.org/multipage/#script-settings-for-workers:api-base-url
|
||||||
return worker.get_url().clone();
|
return worker.get_url().clone();
|
||||||
}
|
}
|
||||||
|
if let Some(worker) = self.downcast::<WorkletGlobalScope>() {
|
||||||
|
// https://drafts.css-houdini.org/worklets/#script-settings-for-worklets
|
||||||
|
return worker.base_url();
|
||||||
|
}
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,6 +275,10 @@ impl GlobalScope {
|
||||||
if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
|
if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
|
||||||
return worker.get_url().clone();
|
return worker.get_url().clone();
|
||||||
}
|
}
|
||||||
|
if let Some(worker) = self.downcast::<WorkletGlobalScope>() {
|
||||||
|
// TODO: is this the right URL to return?
|
||||||
|
return worker.base_url();
|
||||||
|
}
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,14 +358,14 @@ impl GlobalScope {
|
||||||
|
|
||||||
/// Evaluate JS code on this global scope.
|
/// Evaluate JS code on this global scope.
|
||||||
pub fn evaluate_js_on_global_with_result(
|
pub fn evaluate_js_on_global_with_result(
|
||||||
&self, code: &str, rval: MutableHandleValue) {
|
&self, code: &str, rval: MutableHandleValue) -> bool {
|
||||||
self.evaluate_script_on_global_with_result(code, "", rval, 1)
|
self.evaluate_script_on_global_with_result(code, "", rval, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a JS script on this global scope.
|
/// Evaluate a JS script on this global scope.
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
pub fn evaluate_script_on_global_with_result(
|
pub fn evaluate_script_on_global_with_result(
|
||||||
&self, code: &str, filename: &str, rval: MutableHandleValue, line_number: u32) {
|
&self, code: &str, filename: &str, rval: MutableHandleValue, line_number: u32) -> bool {
|
||||||
let metadata = time::TimerMetadata {
|
let metadata = time::TimerMetadata {
|
||||||
url: if filename.is_empty() {
|
url: if filename.is_empty() {
|
||||||
self.get_url().as_str().into()
|
self.get_url().as_str().into()
|
||||||
|
@ -379,16 +388,21 @@ impl GlobalScope {
|
||||||
let _ac = JSAutoCompartment::new(cx, globalhandle.get());
|
let _ac = JSAutoCompartment::new(cx, globalhandle.get());
|
||||||
let _aes = AutoEntryScript::new(self);
|
let _aes = AutoEntryScript::new(self);
|
||||||
let options = CompileOptionsWrapper::new(cx, filename.as_ptr(), line_number);
|
let options = CompileOptionsWrapper::new(cx, filename.as_ptr(), line_number);
|
||||||
unsafe {
|
|
||||||
if !Evaluate2(cx, options.ptr, code.as_ptr(),
|
debug!("evaluating JS string");
|
||||||
code.len() as libc::size_t,
|
let result = unsafe {
|
||||||
rval) {
|
Evaluate2(cx, options.ptr, code.as_ptr(),
|
||||||
debug!("error evaluating JS string");
|
code.len() as libc::size_t,
|
||||||
report_pending_exception(cx, true);
|
rval)
|
||||||
}
|
};
|
||||||
|
|
||||||
|
if !result {
|
||||||
|
debug!("error evaluating JS string");
|
||||||
|
unsafe { report_pending_exception(cx, true) };
|
||||||
}
|
}
|
||||||
|
|
||||||
maybe_resume_unwind();
|
maybe_resume_unwind();
|
||||||
|
result
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -468,6 +482,9 @@ impl GlobalScope {
|
||||||
if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
|
if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
|
||||||
return worker.perform_a_microtask_checkpoint();
|
return worker.perform_a_microtask_checkpoint();
|
||||||
}
|
}
|
||||||
|
if let Some(worker) = self.downcast::<WorkletGlobalScope>() {
|
||||||
|
return worker.perform_a_microtask_checkpoint();
|
||||||
|
}
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -479,6 +496,9 @@ impl GlobalScope {
|
||||||
if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
|
if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
|
||||||
return worker.enqueue_microtask(job);
|
return worker.enqueue_microtask(job);
|
||||||
}
|
}
|
||||||
|
if let Some(worker) = self.downcast::<WorkletGlobalScope>() {
|
||||||
|
return worker.enqueue_microtask(job);
|
||||||
|
}
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -423,6 +423,8 @@ pub mod testbindingiterable;
|
||||||
pub mod testbindingpairiterable;
|
pub mod testbindingpairiterable;
|
||||||
pub mod testbindingproxy;
|
pub mod testbindingproxy;
|
||||||
pub mod testrunner;
|
pub mod testrunner;
|
||||||
|
pub mod testworklet;
|
||||||
|
pub mod testworkletglobalscope;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
pub mod textdecoder;
|
pub mod textdecoder;
|
||||||
pub mod textencoder;
|
pub mod textencoder;
|
||||||
|
@ -469,6 +471,8 @@ pub mod worker;
|
||||||
pub mod workerglobalscope;
|
pub mod workerglobalscope;
|
||||||
pub mod workerlocation;
|
pub mod workerlocation;
|
||||||
pub mod workernavigator;
|
pub mod workernavigator;
|
||||||
|
pub mod worklet;
|
||||||
|
pub mod workletglobalscope;
|
||||||
pub mod xmldocument;
|
pub mod xmldocument;
|
||||||
pub mod xmlhttprequest;
|
pub mod xmlhttprequest;
|
||||||
pub mod xmlhttprequesteventtarget;
|
pub mod xmlhttprequesteventtarget;
|
||||||
|
|
|
@ -296,3 +296,4 @@ fn create_native_handler_function(cx: *mut JSContext,
|
||||||
obj.get()
|
obj.get()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
61
components/script/dom/testworklet.rs
Normal file
61
components/script/dom/testworklet.rs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// check-tidy: no specs after this line
|
||||||
|
|
||||||
|
use dom::bindings::codegen::Bindings::TestWorkletBinding::TestWorkletMethods;
|
||||||
|
use dom::bindings::codegen::Bindings::TestWorkletBinding::Wrap;
|
||||||
|
use dom::bindings::codegen::Bindings::WorkletBinding::WorkletBinding::WorkletMethods;
|
||||||
|
use dom::bindings::codegen::Bindings::WorkletBinding::WorkletOptions;
|
||||||
|
use dom::bindings::error::Fallible;
|
||||||
|
use dom::bindings::js::JS;
|
||||||
|
use dom::bindings::js::Root;
|
||||||
|
use dom::bindings::reflector::Reflector;
|
||||||
|
use dom::bindings::reflector::reflect_dom_object;
|
||||||
|
use dom::bindings::str::DOMString;
|
||||||
|
use dom::bindings::str::USVString;
|
||||||
|
use dom::promise::Promise;
|
||||||
|
use dom::window::Window;
|
||||||
|
use dom::worklet::Worklet;
|
||||||
|
use dom::workletglobalscope::WorkletGlobalScopeType;
|
||||||
|
use dom_struct::dom_struct;
|
||||||
|
use script_thread::ScriptThread;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
#[dom_struct]
|
||||||
|
pub struct TestWorklet {
|
||||||
|
reflector: Reflector,
|
||||||
|
worklet: JS<Worklet>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestWorklet {
|
||||||
|
fn new_inherited(worklet: &Worklet) -> TestWorklet {
|
||||||
|
TestWorklet {
|
||||||
|
reflector: Reflector::new(),
|
||||||
|
worklet: JS::from_ref(worklet),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(window: &Window) -> Root<TestWorklet> {
|
||||||
|
let worklet = Worklet::new(window, WorkletGlobalScopeType::Test);
|
||||||
|
reflect_dom_object(box TestWorklet::new_inherited(&*worklet), window, Wrap)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn Constructor(window: &Window) -> Fallible<Root<TestWorklet>> {
|
||||||
|
Ok(TestWorklet::new(window))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestWorkletMethods for TestWorklet {
|
||||||
|
#[allow(unrooted_must_root)]
|
||||||
|
fn AddModule(&self, moduleURL: USVString, options: &WorkletOptions) -> Rc<Promise> {
|
||||||
|
self.worklet.AddModule(moduleURL, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Lookup(&self, key: DOMString) -> Option<DOMString> {
|
||||||
|
let id = self.worklet.worklet_id();
|
||||||
|
let pool = ScriptThread::worklet_thread_pool();
|
||||||
|
pool.test_worklet_lookup(id, String::from(key)).map(DOMString::from)
|
||||||
|
}
|
||||||
|
}
|
66
components/script/dom/testworkletglobalscope.rs
Normal file
66
components/script/dom/testworkletglobalscope.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use dom::bindings::cell::DOMRefCell;
|
||||||
|
use dom::bindings::codegen::Bindings::TestWorkletGlobalScopeBinding;
|
||||||
|
use dom::bindings::codegen::Bindings::TestWorkletGlobalScopeBinding::TestWorkletGlobalScopeMethods;
|
||||||
|
use dom::bindings::js::Root;
|
||||||
|
use dom::bindings::str::DOMString;
|
||||||
|
use dom::workletglobalscope::WorkletGlobalScope;
|
||||||
|
use dom::workletglobalscope::WorkletGlobalScopeInit;
|
||||||
|
use dom_struct::dom_struct;
|
||||||
|
use js::rust::Runtime;
|
||||||
|
use msg::constellation_msg::PipelineId;
|
||||||
|
use servo_url::ServoUrl;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
|
// check-tidy: no specs after this line
|
||||||
|
|
||||||
|
#[dom_struct]
|
||||||
|
pub struct TestWorkletGlobalScope {
|
||||||
|
// The worklet global for this object
|
||||||
|
worklet_global: WorkletGlobalScope,
|
||||||
|
// The key/value pairs
|
||||||
|
lookup_table: DOMRefCell<HashMap<String, String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestWorkletGlobalScope {
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
pub fn new(runtime: &Runtime,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
base_url: ServoUrl,
|
||||||
|
init: &WorkletGlobalScopeInit)
|
||||||
|
-> Root<TestWorkletGlobalScope>
|
||||||
|
{
|
||||||
|
debug!("Creating test worklet global scope for pipeline {}.", pipeline_id);
|
||||||
|
let global = box TestWorkletGlobalScope {
|
||||||
|
worklet_global: WorkletGlobalScope::new_inherited(pipeline_id, base_url, init),
|
||||||
|
lookup_table: Default::default(),
|
||||||
|
};
|
||||||
|
unsafe { TestWorkletGlobalScopeBinding::Wrap(runtime.cx(), global) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn perform_a_worklet_task(&self, task: TestWorkletTask) {
|
||||||
|
match task {
|
||||||
|
TestWorkletTask::Lookup(key, sender) => {
|
||||||
|
debug!("Looking up key {}.", key);
|
||||||
|
let result = self.lookup_table.borrow().get(&key).cloned();
|
||||||
|
let _ = sender.send(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestWorkletGlobalScopeMethods for TestWorkletGlobalScope {
|
||||||
|
fn RegisterKeyValue(&self, key: DOMString, value: DOMString) {
|
||||||
|
debug!("Registering test worklet key/value {}/{}.", key, value);
|
||||||
|
self.lookup_table.borrow_mut().insert(String::from(key), String::from(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tasks which can be performed by test worklets.
|
||||||
|
pub enum TestWorkletTask {
|
||||||
|
Lookup(String, Sender<Option<String>>),
|
||||||
|
}
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
[ClassString="Console",
|
[ClassString="Console",
|
||||||
Exposed=(Window,Worker),
|
Exposed=(Window,Worker,Worklet),
|
||||||
ProtoObjectHack]
|
ProtoObjectHack]
|
||||||
namespace console {
|
namespace console {
|
||||||
// These should be DOMString message, DOMString message2, ...
|
// These should be DOMString message, DOMString message2, ...
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* https://dom.spec.whatwg.org/#interface-eventtarget
|
* https://dom.spec.whatwg.org/#interface-eventtarget
|
||||||
*/
|
*/
|
||||||
|
|
||||||
[Abstract, Exposed=(Window,Worker)]
|
[Abstract, Exposed=(Window,Worker,Worklet)]
|
||||||
interface EventTarget {
|
interface EventTarget {
|
||||||
void addEventListener(DOMString type,
|
void addEventListener(DOMString type,
|
||||||
EventListener? listener,
|
EventListener? listener,
|
||||||
|
|
|
@ -5,6 +5,6 @@
|
||||||
// This interface is entirely internal to Servo, and should not be accessible to
|
// This interface is entirely internal to Servo, and should not be accessible to
|
||||||
// web pages.
|
// web pages.
|
||||||
|
|
||||||
[Exposed=(Window,Worker),
|
[Exposed=(Window,Worker,Worklet),
|
||||||
Inline]
|
Inline]
|
||||||
interface GlobalScope : EventTarget {};
|
interface GlobalScope : EventTarget {};
|
||||||
|
|
12
components/script/dom/webidls/TestWorklet.webidl
Normal file
12
components/script/dom/webidls/TestWorklet.webidl
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// This interface is entirely internal to Servo, and should not be accessible to
|
||||||
|
// web pages.
|
||||||
|
|
||||||
|
[Pref="dom.worklet.testing.enabled", Exposed=(Window), Constructor]
|
||||||
|
interface TestWorklet {
|
||||||
|
[NewObject] Promise<void> addModule(USVString moduleURL, optional WorkletOptions options);
|
||||||
|
DOMString? lookup(DOMString key);
|
||||||
|
};
|
11
components/script/dom/webidls/TestWorkletGlobalScope.webidl
Normal file
11
components/script/dom/webidls/TestWorkletGlobalScope.webidl
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// This interface is entirely internal to Servo, and should not be accessible to
|
||||||
|
// web pages.
|
||||||
|
|
||||||
|
[Global=(Worklet,TestWorklet), Exposed=TestWorklet]
|
||||||
|
interface TestWorkletGlobalScope : WorkletGlobalScope {
|
||||||
|
void registerKeyValue(DOMString key, DOMString value);
|
||||||
|
};
|
13
components/script/dom/webidls/VoidFunction.webidl
Normal file
13
components/script/dom/webidls/VoidFunction.webidl
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
/*
|
||||||
|
* The origin of this IDL file is
|
||||||
|
* https://heycam.github.io/webidl/#VoidFunction
|
||||||
|
*
|
||||||
|
* © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
|
||||||
|
* Opera Software ASA. You are granted a license to use, reproduce
|
||||||
|
* and create derivative works of this document.
|
||||||
|
*/
|
||||||
|
|
||||||
|
callback VoidFunction = void ();
|
|
@ -201,3 +201,4 @@ partial interface Window {
|
||||||
readonly attribute TestRunner testRunner;
|
readonly attribute TestRunner testRunner;
|
||||||
//readonly attribute EventSender eventSender;
|
//readonly attribute EventSender eventSender;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
13
components/script/dom/webidls/Worklet.webidl
Normal file
13
components/script/dom/webidls/Worklet.webidl
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// https://drafts.css-houdini.org/worklets/#worklet
|
||||||
|
[Exposed=(Window)]
|
||||||
|
interface Worklet {
|
||||||
|
[NewObject] Promise<void> addModule(USVString moduleURL, optional WorkletOptions options);
|
||||||
|
};
|
||||||
|
|
||||||
|
dictionary WorkletOptions {
|
||||||
|
RequestCredentials credentials = "omit";
|
||||||
|
};
|
10
components/script/dom/webidls/WorkletGlobalScope.webidl
Normal file
10
components/script/dom/webidls/WorkletGlobalScope.webidl
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// https://drafts.css-houdini.org/worklets/#workletglobalscope
|
||||||
|
// TODO: The spec IDL doesn't make this a subclass of EventTarget
|
||||||
|
// https://github.com/whatwg/html/issues/2611
|
||||||
|
[Exposed=Worklet]
|
||||||
|
interface WorkletGlobalScope: GlobalScope {
|
||||||
|
};
|
|
@ -49,6 +49,7 @@ use dom::screen::Screen;
|
||||||
use dom::storage::Storage;
|
use dom::storage::Storage;
|
||||||
use dom::testrunner::TestRunner;
|
use dom::testrunner::TestRunner;
|
||||||
use dom::windowproxy::WindowProxy;
|
use dom::windowproxy::WindowProxy;
|
||||||
|
use dom::worklet::Worklet;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use euclid::{Point2D, Rect, Size2D};
|
use euclid::{Point2D, Rect, Size2D};
|
||||||
use fetch;
|
use fetch;
|
||||||
|
@ -273,6 +274,9 @@ pub struct Window {
|
||||||
/// Directory to store unminified scripts for this window if unminify-js
|
/// Directory to store unminified scripts for this window if unminify-js
|
||||||
/// opt is enabled.
|
/// opt is enabled.
|
||||||
unminified_js_dir: DOMRefCell<Option<String>>,
|
unminified_js_dir: DOMRefCell<Option<String>>,
|
||||||
|
|
||||||
|
/// Worklets
|
||||||
|
test_worklet: MutNullableJS<Worklet>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
|
@ -1830,6 +1834,7 @@ impl Window {
|
||||||
permission_state_invocation_results: DOMRefCell::new(HashMap::new()),
|
permission_state_invocation_results: DOMRefCell::new(HashMap::new()),
|
||||||
pending_layout_images: DOMRefCell::new(HashMap::new()),
|
pending_layout_images: DOMRefCell::new(HashMap::new()),
|
||||||
unminified_js_dir: DOMRefCell::new(None),
|
unminified_js_dir: DOMRefCell::new(None),
|
||||||
|
test_worklet: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
637
components/script/dom/worklet.rs
Normal file
637
components/script/dom/worklet.rs
Normal file
|
@ -0,0 +1,637 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
//! An implementation of Houdini worklets.
|
||||||
|
//!
|
||||||
|
//! The goal of this implementation is to maximize responsiveness of worklets,
|
||||||
|
//! and in particular to ensure that the thread performing worklet tasks
|
||||||
|
//! is never busy GCing or loading worklet code. We do this by providing a custom
|
||||||
|
//! thread pool implementation, which only performs GC or code loading on
|
||||||
|
//! a backup thread, not on the primary worklet thread.
|
||||||
|
|
||||||
|
use dom::bindings::codegen::Bindings::RequestBinding::RequestCredentials;
|
||||||
|
use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
|
||||||
|
use dom::bindings::codegen::Bindings::WorkletBinding::WorkletMethods;
|
||||||
|
use dom::bindings::codegen::Bindings::WorkletBinding::WorkletOptions;
|
||||||
|
use dom::bindings::codegen::Bindings::WorkletBinding::Wrap;
|
||||||
|
use dom::bindings::error::Error;
|
||||||
|
use dom::bindings::inheritance::Castable;
|
||||||
|
use dom::bindings::js::JS;
|
||||||
|
use dom::bindings::js::Root;
|
||||||
|
use dom::bindings::js::RootCollection;
|
||||||
|
use dom::bindings::refcounted::TrustedPromise;
|
||||||
|
use dom::bindings::reflector::Reflector;
|
||||||
|
use dom::bindings::reflector::reflect_dom_object;
|
||||||
|
use dom::bindings::str::USVString;
|
||||||
|
use dom::bindings::trace::JSTraceable;
|
||||||
|
use dom::bindings::trace::RootedTraceableBox;
|
||||||
|
use dom::globalscope::GlobalScope;
|
||||||
|
use dom::promise::Promise;
|
||||||
|
use dom::testworkletglobalscope::TestWorkletTask;
|
||||||
|
use dom::window::Window;
|
||||||
|
use dom::workletglobalscope::WorkletGlobalScope;
|
||||||
|
use dom::workletglobalscope::WorkletGlobalScopeInit;
|
||||||
|
use dom::workletglobalscope::WorkletGlobalScopeType;
|
||||||
|
use dom::workletglobalscope::WorkletTask;
|
||||||
|
use dom_struct::dom_struct;
|
||||||
|
use js::jsapi::JSGCParamKey;
|
||||||
|
use js::jsapi::JSTracer;
|
||||||
|
use js::jsapi::JS_GC;
|
||||||
|
use js::jsapi::JS_GetGCParameter;
|
||||||
|
use js::rust::Runtime;
|
||||||
|
use msg::constellation_msg::PipelineId;
|
||||||
|
use net_traits::IpcSend;
|
||||||
|
use net_traits::load_whole_resource;
|
||||||
|
use net_traits::request::Destination;
|
||||||
|
use net_traits::request::RequestInit;
|
||||||
|
use net_traits::request::RequestMode;
|
||||||
|
use net_traits::request::Type as RequestType;
|
||||||
|
use script_runtime::CommonScriptMsg;
|
||||||
|
use script_runtime::ScriptThreadEventCategory;
|
||||||
|
use script_runtime::StackRootTLS;
|
||||||
|
use script_runtime::new_rt_and_cx;
|
||||||
|
use script_thread::MainThreadScriptMsg;
|
||||||
|
use script_thread::Runnable;
|
||||||
|
use script_thread::ScriptThread;
|
||||||
|
use servo_rand;
|
||||||
|
use servo_url::ImmutableOrigin;
|
||||||
|
use servo_url::ServoUrl;
|
||||||
|
use std::cmp::max;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::collections::hash_map;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::AtomicIsize;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
use std::sync::mpsc;
|
||||||
|
use std::sync::mpsc::Receiver;
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
|
use std::thread;
|
||||||
|
use style::thread_state;
|
||||||
|
use swapper::Swapper;
|
||||||
|
use swapper::swapper;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
// Magic numbers
|
||||||
|
const WORKLET_THREAD_POOL_SIZE: u32 = 3;
|
||||||
|
const MIN_GC_THRESHOLD: u32 = 1_000_000;
|
||||||
|
|
||||||
|
#[dom_struct]
|
||||||
|
/// https://drafts.css-houdini.org/worklets/#worklet
|
||||||
|
pub struct Worklet {
|
||||||
|
reflector: Reflector,
|
||||||
|
window: JS<Window>,
|
||||||
|
worklet_id: WorkletId,
|
||||||
|
global_type: WorkletGlobalScopeType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Worklet {
|
||||||
|
fn new_inherited(window: &Window, global_type: WorkletGlobalScopeType) -> Worklet {
|
||||||
|
Worklet {
|
||||||
|
reflector: Reflector::new(),
|
||||||
|
window: JS::from_ref(window),
|
||||||
|
worklet_id: WorkletId::new(),
|
||||||
|
global_type: global_type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(window: &Window, global_type: WorkletGlobalScopeType) -> Root<Worklet> {
|
||||||
|
debug!("Creating worklet {:?}.", global_type);
|
||||||
|
reflect_dom_object(box Worklet::new_inherited(window, global_type), window, Wrap)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn worklet_id(&self) -> WorkletId {
|
||||||
|
self.worklet_id
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn worklet_global_scope_type(&self) -> WorkletGlobalScopeType {
|
||||||
|
self.global_type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorkletMethods for Worklet {
|
||||||
|
#[allow(unrooted_must_root)]
|
||||||
|
/// https://drafts.css-houdini.org/worklets/#dom-worklet-addmodule
|
||||||
|
fn AddModule(&self, module_url: USVString, options: &WorkletOptions) -> Rc<Promise> {
|
||||||
|
// Step 1.
|
||||||
|
let promise = Promise::new(self.window.upcast());
|
||||||
|
|
||||||
|
// Step 3.
|
||||||
|
let module_url_record = match self.window.Document().base_url().join(&module_url.0) {
|
||||||
|
Ok(url) => url,
|
||||||
|
Err(err) => {
|
||||||
|
// Step 4.
|
||||||
|
debug!("URL {:?} parse error {:?}.", module_url.0, err);
|
||||||
|
promise.reject_error(self.window.get_cx(), Error::Syntax);
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
debug!("Adding Worklet module {}.", module_url_record);
|
||||||
|
|
||||||
|
// Steps 6-12 in parallel.
|
||||||
|
let pending_tasks_struct = PendingTasksStruct::new();
|
||||||
|
let global = self.window.upcast::<GlobalScope>();
|
||||||
|
let pool = ScriptThread::worklet_thread_pool();
|
||||||
|
|
||||||
|
pool.fetch_and_invoke_a_worklet_script(global.pipeline_id(),
|
||||||
|
self.worklet_id,
|
||||||
|
self.global_type,
|
||||||
|
self.window.origin().immutable().clone(),
|
||||||
|
global.api_base_url(),
|
||||||
|
module_url_record,
|
||||||
|
options.credentials.clone(),
|
||||||
|
pending_tasks_struct,
|
||||||
|
&promise);
|
||||||
|
|
||||||
|
// Step 5.
|
||||||
|
promise
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A guid for worklets.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, JSTraceable)]
|
||||||
|
pub struct WorkletId(Uuid);
|
||||||
|
|
||||||
|
known_heap_size!(0, WorkletId);
|
||||||
|
|
||||||
|
impl WorkletId {
|
||||||
|
fn new() -> WorkletId {
|
||||||
|
WorkletId(servo_rand::random())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.css-houdini.org/worklets/#pending-tasks-struct
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct PendingTasksStruct(Arc<AtomicIsize>);
|
||||||
|
|
||||||
|
impl PendingTasksStruct {
|
||||||
|
fn new() -> PendingTasksStruct {
|
||||||
|
PendingTasksStruct(Arc::new(AtomicIsize::new(WORKLET_THREAD_POOL_SIZE as isize)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_counter_to(&self, value: isize) -> isize {
|
||||||
|
self.0.swap(value, Ordering::AcqRel)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decrement_counter_by(&self, offset: isize) -> isize {
|
||||||
|
self.0.fetch_sub(offset, Ordering::AcqRel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Worklets execute in a dedicated thread pool.
|
||||||
|
///
|
||||||
|
/// The goal is to ensure that there is a primary worklet thread,
|
||||||
|
/// which is able to responsively execute worklet code. In particular,
|
||||||
|
/// worklet execution should not be delayed by GC, or by script
|
||||||
|
/// loading.
|
||||||
|
///
|
||||||
|
/// To achieve this, we implement a three-thread pool, with the
|
||||||
|
/// threads cycling between three thread roles:
|
||||||
|
///
|
||||||
|
/// * The primary worklet thread is the one available to execute
|
||||||
|
/// worklet code.
|
||||||
|
///
|
||||||
|
/// * The hot backup thread may peform GC, but otherwise is expected
|
||||||
|
/// to take over the primary role.
|
||||||
|
///
|
||||||
|
/// * The cold backup thread may peform script loading and other
|
||||||
|
/// long-running tasks.
|
||||||
|
///
|
||||||
|
/// In the implementation, we use two kinds of messages:
|
||||||
|
///
|
||||||
|
/// * Data messages are expected to be processed quickly, and include
|
||||||
|
/// the worklet tasks to be performed by the primary thread, as
|
||||||
|
/// well as requests to change role or quit execution.
|
||||||
|
///
|
||||||
|
/// * Control messages are expected to be processed more slowly, and
|
||||||
|
/// include script loading.
|
||||||
|
///
|
||||||
|
/// Data messages are targeted at a role, for example, task execution
|
||||||
|
/// is expected to be performed by whichever thread is currently
|
||||||
|
/// primary. Control messages are targeted at a thread, for example
|
||||||
|
/// adding a module is performed in every thread, even if they change roles
|
||||||
|
/// in the middle of module loading.
|
||||||
|
///
|
||||||
|
/// The thread pool lives in the script thread, and is initialized
|
||||||
|
/// when a worklet adds a module. It is dropped when the script thread
|
||||||
|
/// is dropped, and asks each of the worklet threads to quit.
|
||||||
|
|
||||||
|
#[derive(Clone, JSTraceable)]
|
||||||
|
pub struct WorkletThreadPool {
|
||||||
|
// Channels to send data messages to the three roles.
|
||||||
|
primary_sender: Sender<WorkletData>,
|
||||||
|
hot_backup_sender: Sender<WorkletData>,
|
||||||
|
cold_backup_sender: Sender<WorkletData>,
|
||||||
|
// Channels to send control messages to the three threads.
|
||||||
|
control_sender_0: Sender<WorkletControl>,
|
||||||
|
control_sender_1: Sender<WorkletControl>,
|
||||||
|
control_sender_2: Sender<WorkletControl>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for WorkletThreadPool {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let _ = self.cold_backup_sender.send(WorkletData::Quit);
|
||||||
|
let _ = self.hot_backup_sender.send(WorkletData::Quit);
|
||||||
|
let _ = self.primary_sender.send(WorkletData::Quit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorkletThreadPool {
|
||||||
|
/// Create a new thread pool and spawn the threads.
|
||||||
|
/// When the thread pool is dropped, the threads will be asked to quit.
|
||||||
|
pub fn spawn(script_sender: Sender<MainThreadScriptMsg>, global_init: WorkletGlobalScopeInit) -> WorkletThreadPool {
|
||||||
|
let primary_role = WorkletThreadRole::new(false, false);
|
||||||
|
let hot_backup_role = WorkletThreadRole::new(true, false);
|
||||||
|
let cold_backup_role = WorkletThreadRole::new(false, true);
|
||||||
|
let primary_sender = primary_role.sender.clone();
|
||||||
|
let hot_backup_sender = hot_backup_role.sender.clone();
|
||||||
|
let cold_backup_sender = cold_backup_role.sender.clone();
|
||||||
|
let init = WorkletThreadInit {
|
||||||
|
hot_backup_sender: hot_backup_sender.clone(),
|
||||||
|
cold_backup_sender: cold_backup_sender.clone(),
|
||||||
|
script_sender: script_sender.clone(),
|
||||||
|
global_init: global_init,
|
||||||
|
};
|
||||||
|
WorkletThreadPool {
|
||||||
|
primary_sender: primary_sender,
|
||||||
|
hot_backup_sender: hot_backup_sender,
|
||||||
|
cold_backup_sender: cold_backup_sender,
|
||||||
|
control_sender_0: WorkletThread::spawn(primary_role, init.clone()),
|
||||||
|
control_sender_1: WorkletThread::spawn(hot_backup_role, init.clone()),
|
||||||
|
control_sender_2: WorkletThread::spawn(cold_backup_role, init),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads a worklet module into every worklet thread.
|
||||||
|
/// If all of the threads load successfully, the promise is resolved.
|
||||||
|
/// If any of the threads fails to load, the promise is rejected.
|
||||||
|
/// https://drafts.css-houdini.org/worklets/#fetch-and-invoke-a-worklet-script
|
||||||
|
fn fetch_and_invoke_a_worklet_script(&self,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
worklet_id: WorkletId,
|
||||||
|
global_type: WorkletGlobalScopeType,
|
||||||
|
origin: ImmutableOrigin,
|
||||||
|
base_url: ServoUrl,
|
||||||
|
script_url: ServoUrl,
|
||||||
|
credentials: RequestCredentials,
|
||||||
|
pending_tasks_struct: PendingTasksStruct,
|
||||||
|
promise: &Rc<Promise>)
|
||||||
|
{
|
||||||
|
// Send each thread a control message asking it to load the script.
|
||||||
|
for sender in &[&self.control_sender_0, &self.control_sender_1, &self.control_sender_2] {
|
||||||
|
let _ = sender.send(WorkletControl::FetchAndInvokeAWorkletScript {
|
||||||
|
pipeline_id: pipeline_id,
|
||||||
|
worklet_id: worklet_id,
|
||||||
|
global_type: global_type,
|
||||||
|
origin: origin.clone(),
|
||||||
|
base_url: base_url.clone(),
|
||||||
|
script_url: script_url.clone(),
|
||||||
|
credentials: credentials,
|
||||||
|
pending_tasks_struct: pending_tasks_struct.clone(),
|
||||||
|
promise: TrustedPromise::new(promise.clone()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// If any of the threads are blocked waiting on data, wake them up.
|
||||||
|
let _ = self.cold_backup_sender.send(WorkletData::WakeUp);
|
||||||
|
let _ = self.hot_backup_sender.send(WorkletData::WakeUp);
|
||||||
|
let _ = self.primary_sender.send(WorkletData::WakeUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// For testing.
|
||||||
|
pub fn test_worklet_lookup(&self, id: WorkletId, key: String) -> Option<String> {
|
||||||
|
let (sender, receiver) = mpsc::channel();
|
||||||
|
let msg = WorkletData::Task(id, WorkletTask::Test(TestWorkletTask::Lookup(key, sender)));
|
||||||
|
let _ = self.primary_sender.send(msg);
|
||||||
|
receiver.recv().expect("Test worklet has died?")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The data messages sent to worklet threads
|
||||||
|
enum WorkletData {
|
||||||
|
Task(WorkletId, WorkletTask),
|
||||||
|
StartSwapRoles(Sender<WorkletData>),
|
||||||
|
FinishSwapRoles(Swapper<WorkletThreadRole>),
|
||||||
|
WakeUp,
|
||||||
|
Quit,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The control message sent to worklet threads
|
||||||
|
enum WorkletControl {
|
||||||
|
FetchAndInvokeAWorkletScript {
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
worklet_id: WorkletId,
|
||||||
|
global_type: WorkletGlobalScopeType,
|
||||||
|
origin: ImmutableOrigin,
|
||||||
|
base_url: ServoUrl,
|
||||||
|
script_url: ServoUrl,
|
||||||
|
credentials: RequestCredentials,
|
||||||
|
pending_tasks_struct: PendingTasksStruct,
|
||||||
|
promise: TrustedPromise,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A role that a worklet thread can be playing.
|
||||||
|
///
|
||||||
|
/// These roles are used as tokens or capabilities, we track unique
|
||||||
|
/// ownership using Rust's types, and use atomic swapping to exchange
|
||||||
|
/// them between worklet threads. This ensures that each thread pool has
|
||||||
|
/// exactly one primary, one hot backup and one cold backup.
|
||||||
|
struct WorkletThreadRole {
|
||||||
|
receiver: Receiver<WorkletData>,
|
||||||
|
sender: Sender<WorkletData>,
|
||||||
|
is_hot_backup: bool,
|
||||||
|
is_cold_backup: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorkletThreadRole {
|
||||||
|
fn new(is_hot_backup: bool, is_cold_backup: bool) -> WorkletThreadRole {
|
||||||
|
let (sender, receiver) = mpsc::channel();
|
||||||
|
WorkletThreadRole {
|
||||||
|
sender: sender,
|
||||||
|
receiver: receiver,
|
||||||
|
is_hot_backup: is_hot_backup,
|
||||||
|
is_cold_backup: is_cold_backup,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Data to initialize a worklet thread.
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct WorkletThreadInit {
|
||||||
|
/// Senders
|
||||||
|
hot_backup_sender: Sender<WorkletData>,
|
||||||
|
cold_backup_sender: Sender<WorkletData>,
|
||||||
|
script_sender: Sender<MainThreadScriptMsg>,
|
||||||
|
|
||||||
|
/// Data for initializing new worklet global scopes
|
||||||
|
global_init: WorkletGlobalScopeInit,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A thread for executing worklets.
|
||||||
|
#[must_root]
|
||||||
|
struct WorkletThread {
|
||||||
|
/// Which role the thread is currently playing
|
||||||
|
role: WorkletThreadRole,
|
||||||
|
|
||||||
|
/// The thread's receiver for control messages
|
||||||
|
control_receiver: Receiver<WorkletControl>,
|
||||||
|
|
||||||
|
/// Senders
|
||||||
|
hot_backup_sender: Sender<WorkletData>,
|
||||||
|
cold_backup_sender: Sender<WorkletData>,
|
||||||
|
script_sender: Sender<MainThreadScriptMsg>,
|
||||||
|
|
||||||
|
/// Data for initializing new worklet global scopes
|
||||||
|
global_init: WorkletGlobalScopeInit,
|
||||||
|
|
||||||
|
/// The global scopes created by this thread
|
||||||
|
global_scopes: HashMap<WorkletId, JS<WorkletGlobalScope>>,
|
||||||
|
|
||||||
|
/// A one-place buffer for control messages
|
||||||
|
control_buffer: Option<WorkletControl>,
|
||||||
|
|
||||||
|
/// The JS runtime
|
||||||
|
runtime: Runtime,
|
||||||
|
should_gc: bool,
|
||||||
|
gc_threshold: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe impl JSTraceable for WorkletThread {
|
||||||
|
unsafe fn trace(&self, trc: *mut JSTracer) {
|
||||||
|
debug!("Tracing worklet thread.");
|
||||||
|
self.global_scopes.trace(trc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorkletThread {
|
||||||
|
/// Spawn a new worklet thread, returning the channel to send it control messages.
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
#[allow(unrooted_must_root)]
|
||||||
|
fn spawn(role: WorkletThreadRole, init: WorkletThreadInit) -> Sender<WorkletControl> {
|
||||||
|
let (control_sender, control_receiver) = mpsc::channel();
|
||||||
|
// TODO: name this thread
|
||||||
|
thread::spawn(move || {
|
||||||
|
// TODO: add a new IN_WORKLET thread state?
|
||||||
|
// TODO: set interrupt handler?
|
||||||
|
// TODO: configure the JS runtime (e.g. discourage GC, encourage agressive JIT)
|
||||||
|
debug!("Initializing worklet thread.");
|
||||||
|
thread_state::initialize(thread_state::SCRIPT | thread_state::IN_WORKER);
|
||||||
|
let roots = RootCollection::new();
|
||||||
|
let _stack_roots_tls = StackRootTLS::new(&roots);
|
||||||
|
let mut thread = RootedTraceableBox::new(WorkletThread {
|
||||||
|
role: role,
|
||||||
|
control_receiver: control_receiver,
|
||||||
|
hot_backup_sender: init.hot_backup_sender,
|
||||||
|
cold_backup_sender: init.cold_backup_sender,
|
||||||
|
script_sender: init.script_sender,
|
||||||
|
global_init: init.global_init,
|
||||||
|
global_scopes: HashMap::new(),
|
||||||
|
control_buffer: None,
|
||||||
|
runtime: unsafe { new_rt_and_cx() },
|
||||||
|
should_gc: false,
|
||||||
|
gc_threshold: MIN_GC_THRESHOLD,
|
||||||
|
});
|
||||||
|
thread.run();
|
||||||
|
});
|
||||||
|
control_sender
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The main event loop for a worklet thread
|
||||||
|
fn run(&mut self) {
|
||||||
|
loop {
|
||||||
|
// The handler for data messages
|
||||||
|
let message = self.role.receiver.recv().unwrap();
|
||||||
|
match message {
|
||||||
|
// The whole point of this thread pool is to perform tasks!
|
||||||
|
WorkletData::Task(id, task) => {
|
||||||
|
self.perform_a_worklet_task(id, task);
|
||||||
|
}
|
||||||
|
// To start swapping roles, get ready to perform an atomic swap,
|
||||||
|
// and block waiting for the other end to finish it.
|
||||||
|
// NOTE: the cold backup can block on the primary or the hot backup;
|
||||||
|
// the hot backup can block on the primary;
|
||||||
|
// the primary can block on nothing;
|
||||||
|
// this total ordering on thread roles is what guarantees deadlock-freedom.
|
||||||
|
WorkletData::StartSwapRoles(sender) => {
|
||||||
|
let (our_swapper, their_swapper) = swapper();
|
||||||
|
sender.send(WorkletData::FinishSwapRoles(their_swapper)).unwrap();
|
||||||
|
let _ = our_swapper.swap(&mut self.role);
|
||||||
|
}
|
||||||
|
// To finish swapping roles, perform the atomic swap.
|
||||||
|
// The other end should have already started the swap, so this shouldn't block.
|
||||||
|
WorkletData::FinishSwapRoles(swapper) => {
|
||||||
|
let _ = swapper.swap(&mut self.role);
|
||||||
|
}
|
||||||
|
// Wake up! There may be control messages to process.
|
||||||
|
WorkletData::WakeUp => {
|
||||||
|
}
|
||||||
|
// Quit!
|
||||||
|
WorkletData::Quit => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Only process control messages if we're the cold backup,
|
||||||
|
// otherwise if there are outstanding control messages,
|
||||||
|
// try to become the cold backup.
|
||||||
|
if self.role.is_cold_backup {
|
||||||
|
if let Some(control) = self.control_buffer.take() {
|
||||||
|
self.process_control(control);
|
||||||
|
}
|
||||||
|
while let Ok(control) = self.control_receiver.try_recv() {
|
||||||
|
self.process_control(control);
|
||||||
|
}
|
||||||
|
self.gc();
|
||||||
|
} else if self.control_buffer.is_none() {
|
||||||
|
if let Ok(control) = self.control_receiver.try_recv() {
|
||||||
|
self.control_buffer = Some(control);
|
||||||
|
let msg = WorkletData::StartSwapRoles(self.role.sender.clone());
|
||||||
|
let _ = self.cold_backup_sender.send(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we are tight on memory, and we're a backup then perform a gc.
|
||||||
|
// If we are tight on memory, and we're the primary then try to become the hot backup.
|
||||||
|
// Hopefully this happens soon!
|
||||||
|
if self.current_memory_usage() > self.gc_threshold {
|
||||||
|
if self.role.is_hot_backup || self.role.is_cold_backup {
|
||||||
|
self.should_gc = false;
|
||||||
|
self.gc();
|
||||||
|
} else if !self.should_gc {
|
||||||
|
self.should_gc = true;
|
||||||
|
let msg = WorkletData::StartSwapRoles(self.role.sender.clone());
|
||||||
|
let _ = self.hot_backup_sender.send(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The current memory usage of the thread
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
fn current_memory_usage(&self) -> u32 {
|
||||||
|
unsafe { JS_GetGCParameter(self.runtime.rt(), JSGCParamKey::JSGC_BYTES) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform a GC.
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
fn gc(&mut self) {
|
||||||
|
debug!("BEGIN GC (usage = {}, threshold = {}).", self.current_memory_usage(), self.gc_threshold);
|
||||||
|
unsafe { JS_GC(self.runtime.rt()) };
|
||||||
|
self.gc_threshold = max(MIN_GC_THRESHOLD, self.current_memory_usage() * 2);
|
||||||
|
debug!("END GC (usage = {}, threshold = {}).", self.current_memory_usage(), self.gc_threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the worklet global scope for a given worklet.
|
||||||
|
/// Creates the worklet global scope if it doesn't exist.
|
||||||
|
fn get_worklet_global_scope(&mut self,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
worklet_id: WorkletId,
|
||||||
|
global_type: WorkletGlobalScopeType,
|
||||||
|
base_url: ServoUrl)
|
||||||
|
-> Root<WorkletGlobalScope>
|
||||||
|
{
|
||||||
|
match self.global_scopes.entry(worklet_id) {
|
||||||
|
hash_map::Entry::Occupied(entry) => Root::from_ref(entry.get()),
|
||||||
|
hash_map::Entry::Vacant(entry) => {
|
||||||
|
let result = global_type.new(&self.runtime, pipeline_id, base_url, &self.global_init);
|
||||||
|
entry.insert(JS::from_ref(&*result));
|
||||||
|
result
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch and invoke a worklet script.
|
||||||
|
/// https://drafts.css-houdini.org/worklets/#fetch-and-invoke-a-worklet-script
|
||||||
|
fn fetch_and_invoke_a_worklet_script(&self,
|
||||||
|
global_scope: &WorkletGlobalScope,
|
||||||
|
origin: ImmutableOrigin,
|
||||||
|
script_url: ServoUrl,
|
||||||
|
credentials: RequestCredentials,
|
||||||
|
pending_tasks_struct: PendingTasksStruct,
|
||||||
|
promise: TrustedPromise)
|
||||||
|
{
|
||||||
|
debug!("Fetching from {}.", script_url);
|
||||||
|
// Step 1.
|
||||||
|
// TODO: Settings object?
|
||||||
|
|
||||||
|
// Step 2.
|
||||||
|
// TODO: Fetch a module graph, not just a single script.
|
||||||
|
// TODO: Fetch the script asynchronously?
|
||||||
|
// TODO: Caching.
|
||||||
|
// TODO: Avoid re-parsing the origin as a URL.
|
||||||
|
let resource_fetcher = self.global_init.resource_threads.sender();
|
||||||
|
let origin_url = ServoUrl::parse(&*origin.unicode_serialization()).expect("Failed to parse origin as URL.");
|
||||||
|
let request = RequestInit {
|
||||||
|
url: script_url,
|
||||||
|
type_: RequestType::Script,
|
||||||
|
destination: Destination::Script,
|
||||||
|
mode: RequestMode::CorsMode,
|
||||||
|
origin: origin_url,
|
||||||
|
credentials_mode: credentials.into(),
|
||||||
|
.. RequestInit::default()
|
||||||
|
};
|
||||||
|
let script = load_whole_resource(request, &resource_fetcher).ok()
|
||||||
|
.and_then(|(_, bytes)| String::from_utf8(bytes).ok());
|
||||||
|
|
||||||
|
// Step 4.
|
||||||
|
// NOTE: the spec parses and executes the script in separate steps,
|
||||||
|
// but our JS API doesn't separate these, so we do the steps out of order.
|
||||||
|
let ok = script.map(|script| global_scope.evaluate_js(&*script)).unwrap_or(false);
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
// Step 3.
|
||||||
|
debug!("Failed to load script.");
|
||||||
|
let old_counter = pending_tasks_struct.set_counter_to(-1);
|
||||||
|
if old_counter > 0 {
|
||||||
|
self.run_in_script_thread(promise.reject_runnable(Error::Abort));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Step 5.
|
||||||
|
debug!("Finished adding script.");
|
||||||
|
let old_counter = pending_tasks_struct.decrement_counter_by(1);
|
||||||
|
if old_counter == 1 {
|
||||||
|
// TODO: trigger a reflow?
|
||||||
|
self.run_in_script_thread(promise.resolve_runnable(()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform a task.
|
||||||
|
fn perform_a_worklet_task(&self, worklet_id: WorkletId, task: WorkletTask) {
|
||||||
|
match self.global_scopes.get(&worklet_id) {
|
||||||
|
Some(global) => global.perform_a_worklet_task(task),
|
||||||
|
None => return warn!("No such worklet as {:?}.", worklet_id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process a control message.
|
||||||
|
fn process_control(&mut self, control: WorkletControl) {
|
||||||
|
match control {
|
||||||
|
WorkletControl::FetchAndInvokeAWorkletScript {
|
||||||
|
pipeline_id, worklet_id, global_type, origin, base_url,
|
||||||
|
script_url, credentials, pending_tasks_struct, promise,
|
||||||
|
} => {
|
||||||
|
let global = self.get_worklet_global_scope(pipeline_id,
|
||||||
|
worklet_id,
|
||||||
|
global_type,
|
||||||
|
base_url);
|
||||||
|
self.fetch_and_invoke_a_worklet_script(&*global,
|
||||||
|
origin,
|
||||||
|
script_url,
|
||||||
|
credentials,
|
||||||
|
pending_tasks_struct,
|
||||||
|
promise)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run a runnable in the main script thread.
|
||||||
|
fn run_in_script_thread<R>(&self, runnable: R) where
|
||||||
|
R: 'static + Send + Runnable,
|
||||||
|
{
|
||||||
|
let msg = CommonScriptMsg::RunnableMsg(ScriptThreadEventCategory::WorkletEvent, box runnable);
|
||||||
|
let msg = MainThreadScriptMsg::Common(msg);
|
||||||
|
self.script_sender.send(msg).expect("Worklet thread outlived script thread.");
|
||||||
|
}
|
||||||
|
}
|
143
components/script/dom/workletglobalscope.rs
Normal file
143
components/script/dom/workletglobalscope.rs
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use devtools_traits::ScriptToDevtoolsControlMsg;
|
||||||
|
use dom::bindings::inheritance::Castable;
|
||||||
|
use dom::bindings::js::Root;
|
||||||
|
use dom::globalscope::GlobalScope;
|
||||||
|
use dom::testworkletglobalscope::TestWorkletGlobalScope;
|
||||||
|
use dom::testworkletglobalscope::TestWorkletTask;
|
||||||
|
use dom_struct::dom_struct;
|
||||||
|
use ipc_channel::ipc;
|
||||||
|
use ipc_channel::ipc::IpcSender;
|
||||||
|
use js::jsval::UndefinedValue;
|
||||||
|
use js::rust::Runtime;
|
||||||
|
use microtask::Microtask;
|
||||||
|
use microtask::MicrotaskQueue;
|
||||||
|
use msg::constellation_msg::PipelineId;
|
||||||
|
use net_traits::ResourceThreads;
|
||||||
|
use profile_traits::mem;
|
||||||
|
use profile_traits::time;
|
||||||
|
use script_traits::ScriptMsg;
|
||||||
|
use script_traits::TimerSchedulerMsg;
|
||||||
|
use servo_url::ImmutableOrigin;
|
||||||
|
use servo_url::MutableOrigin;
|
||||||
|
use servo_url::ServoUrl;
|
||||||
|
|
||||||
|
#[dom_struct]
|
||||||
|
/// https://drafts.css-houdini.org/worklets/#workletglobalscope
|
||||||
|
pub struct WorkletGlobalScope {
|
||||||
|
/// The global for this worklet.
|
||||||
|
globalscope: GlobalScope,
|
||||||
|
/// The base URL for this worklet.
|
||||||
|
base_url: ServoUrl,
|
||||||
|
/// The microtask queue for this worklet
|
||||||
|
microtask_queue: MicrotaskQueue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorkletGlobalScope {
|
||||||
|
/// Create a new stack-allocated `WorkletGlobalScope`.
|
||||||
|
pub fn new_inherited(pipeline_id: PipelineId,
|
||||||
|
base_url: ServoUrl,
|
||||||
|
init: &WorkletGlobalScopeInit)
|
||||||
|
-> WorkletGlobalScope {
|
||||||
|
// Any timer events fired on this global are ignored.
|
||||||
|
let (timer_event_chan, _) = ipc::channel().unwrap();
|
||||||
|
WorkletGlobalScope {
|
||||||
|
globalscope: GlobalScope::new_inherited(pipeline_id,
|
||||||
|
init.devtools_chan.clone(),
|
||||||
|
init.mem_profiler_chan.clone(),
|
||||||
|
init.time_profiler_chan.clone(),
|
||||||
|
init.constellation_chan.clone(),
|
||||||
|
init.scheduler_chan.clone(),
|
||||||
|
init.resource_threads.clone(),
|
||||||
|
timer_event_chan,
|
||||||
|
MutableOrigin::new(ImmutableOrigin::new_opaque())),
|
||||||
|
base_url: base_url,
|
||||||
|
microtask_queue: MicrotaskQueue::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate a JS script in this global.
|
||||||
|
pub fn evaluate_js(&self, script: &str) -> bool {
|
||||||
|
debug!("Evaluating JS.");
|
||||||
|
rooted!(in (self.globalscope.get_cx()) let mut rval = UndefinedValue());
|
||||||
|
self.globalscope.evaluate_js_on_global_with_result(&*script, rval.handle_mut())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The base URL of this global.
|
||||||
|
pub fn base_url(&self) -> ServoUrl {
|
||||||
|
self.base_url.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Queue up a microtask to be executed in this global.
|
||||||
|
pub fn enqueue_microtask(&self, job: Microtask) {
|
||||||
|
self.microtask_queue.enqueue(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform any queued microtasks.
|
||||||
|
pub fn perform_a_microtask_checkpoint(&self) {
|
||||||
|
self.microtask_queue.checkpoint(|id| {
|
||||||
|
let global = self.upcast::<GlobalScope>();
|
||||||
|
assert_eq!(global.pipeline_id(), id);
|
||||||
|
Some(Root::from_ref(global))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform a worklet task
|
||||||
|
pub fn perform_a_worklet_task(&self, task: WorkletTask) {
|
||||||
|
match task {
|
||||||
|
WorkletTask::Test(task) => match self.downcast::<TestWorkletGlobalScope>() {
|
||||||
|
Some(global) => global.perform_a_worklet_task(task),
|
||||||
|
None => warn!("This is not a test worklet."),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resources required by workletglobalscopes
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct WorkletGlobalScopeInit {
|
||||||
|
/// Channel to a resource thread
|
||||||
|
pub resource_threads: ResourceThreads,
|
||||||
|
/// Channel to the memory profiler
|
||||||
|
pub mem_profiler_chan: mem::ProfilerChan,
|
||||||
|
/// Channel to the time profiler
|
||||||
|
pub time_profiler_chan: time::ProfilerChan,
|
||||||
|
/// Channel to devtools
|
||||||
|
pub devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
|
||||||
|
/// Messages to send to constellation
|
||||||
|
pub constellation_chan: IpcSender<ScriptMsg>,
|
||||||
|
/// Message to send to the scheduler
|
||||||
|
pub scheduler_chan: IpcSender<TimerSchedulerMsg>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.css-houdini.org/worklets/#worklet-global-scope-type
|
||||||
|
#[derive(Clone, Copy, Debug, HeapSizeOf, JSTraceable)]
|
||||||
|
pub enum WorkletGlobalScopeType {
|
||||||
|
/// https://drafts.css-houdini.org/worklets/#examples
|
||||||
|
Test,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorkletGlobalScopeType {
|
||||||
|
/// Create a new heap-allocated `WorkletGlobalScope`.
|
||||||
|
pub fn new(&self,
|
||||||
|
runtime: &Runtime,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
base_url: ServoUrl,
|
||||||
|
init: &WorkletGlobalScopeInit)
|
||||||
|
-> Root<WorkletGlobalScope>
|
||||||
|
{
|
||||||
|
match *self {
|
||||||
|
WorkletGlobalScopeType::Test =>
|
||||||
|
Root::upcast(TestWorkletGlobalScope::new(runtime, pipeline_id, base_url, init)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A task which can be performed in the context of a worklet global.
|
||||||
|
pub enum WorkletTask {
|
||||||
|
Test(TestWorkletTask),
|
||||||
|
}
|
||||||
|
|
|
@ -10,10 +10,12 @@
|
||||||
#![feature(nonzero)]
|
#![feature(nonzero)]
|
||||||
#![feature(on_unimplemented)]
|
#![feature(on_unimplemented)]
|
||||||
#![feature(optin_builtin_traits)]
|
#![feature(optin_builtin_traits)]
|
||||||
|
#![feature(option_entry)]
|
||||||
#![feature(plugin)]
|
#![feature(plugin)]
|
||||||
#![feature(proc_macro)]
|
#![feature(proc_macro)]
|
||||||
#![feature(stmt_expr_attributes)]
|
#![feature(stmt_expr_attributes)]
|
||||||
#![feature(try_from)]
|
#![feature(try_from)]
|
||||||
|
#![feature(unboxed_closures)]
|
||||||
#![feature(untagged_unions)]
|
#![feature(untagged_unions)]
|
||||||
|
|
||||||
#![deny(unsafe_code)]
|
#![deny(unsafe_code)]
|
||||||
|
@ -46,7 +48,7 @@ extern crate encoding;
|
||||||
extern crate euclid;
|
extern crate euclid;
|
||||||
extern crate fnv;
|
extern crate fnv;
|
||||||
extern crate gfx_traits;
|
extern crate gfx_traits;
|
||||||
extern crate heapsize;
|
#[macro_use] extern crate heapsize;
|
||||||
#[macro_use] extern crate heapsize_derive;
|
#[macro_use] extern crate heapsize_derive;
|
||||||
#[macro_use] extern crate html5ever;
|
#[macro_use] extern crate html5ever;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -92,6 +94,7 @@ extern crate smallvec;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate style;
|
extern crate style;
|
||||||
extern crate style_traits;
|
extern crate style_traits;
|
||||||
|
extern crate swapper;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
||||||
extern crate tinyfiledialogs;
|
extern crate tinyfiledialogs;
|
||||||
|
|
|
@ -74,6 +74,7 @@ pub enum ScriptThreadEventCategory {
|
||||||
UpdateReplacedElement,
|
UpdateReplacedElement,
|
||||||
WebSocketEvent,
|
WebSocketEvent,
|
||||||
WorkerEvent,
|
WorkerEvent,
|
||||||
|
WorkletEvent,
|
||||||
ServiceWorkerEvent,
|
ServiceWorkerEvent,
|
||||||
EnterFullscreen,
|
EnterFullscreen,
|
||||||
ExitFullscreen,
|
ExitFullscreen,
|
||||||
|
|
|
@ -56,6 +56,8 @@ use dom::uievent::UIEvent;
|
||||||
use dom::window::{ReflowReason, Window};
|
use dom::window::{ReflowReason, Window};
|
||||||
use dom::windowproxy::WindowProxy;
|
use dom::windowproxy::WindowProxy;
|
||||||
use dom::worker::TrustedWorkerAddress;
|
use dom::worker::TrustedWorkerAddress;
|
||||||
|
use dom::worklet::WorkletThreadPool;
|
||||||
|
use dom::workletglobalscope::WorkletGlobalScopeInit;
|
||||||
use euclid::Rect;
|
use euclid::Rect;
|
||||||
use euclid::point::Point2D;
|
use euclid::point::Point2D;
|
||||||
use hyper::header::{ContentType, HttpDate, LastModified, Headers};
|
use hyper::header::{ContentType, HttpDate, LastModified, Headers};
|
||||||
|
@ -490,6 +492,9 @@ pub struct ScriptThread {
|
||||||
/// A handle to the webvr thread, if available
|
/// A handle to the webvr thread, if available
|
||||||
webvr_thread: Option<IpcSender<WebVRMsg>>,
|
webvr_thread: Option<IpcSender<WebVRMsg>>,
|
||||||
|
|
||||||
|
/// The worklet thread pool
|
||||||
|
worklet_thread_pool: DOMRefCell<Option<Rc<WorkletThreadPool>>>,
|
||||||
|
|
||||||
/// A list of pipelines containing documents that finished loading all their blocking
|
/// A list of pipelines containing documents that finished loading all their blocking
|
||||||
/// resources during a turn of the event loop.
|
/// resources during a turn of the event loop.
|
||||||
docs_with_no_blocking_loads: DOMRefCell<HashSet<JS<Document>>>,
|
docs_with_no_blocking_loads: DOMRefCell<HashSet<JS<Document>>>,
|
||||||
|
@ -703,6 +708,24 @@ impl ScriptThread {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn worklet_thread_pool() -> Rc<WorkletThreadPool> {
|
||||||
|
SCRIPT_THREAD_ROOT.with(|root| {
|
||||||
|
let script_thread = unsafe { &*root.get().unwrap() };
|
||||||
|
script_thread.worklet_thread_pool.borrow_mut().get_or_insert_with(|| {
|
||||||
|
let chan = script_thread.chan.0.clone();
|
||||||
|
let init = WorkletGlobalScopeInit {
|
||||||
|
resource_threads: script_thread.resource_threads.clone(),
|
||||||
|
mem_profiler_chan: script_thread.mem_profiler_chan.clone(),
|
||||||
|
time_profiler_chan: script_thread.time_profiler_chan.clone(),
|
||||||
|
devtools_chan: script_thread.devtools_chan.clone(),
|
||||||
|
constellation_chan: script_thread.constellation_chan.clone(),
|
||||||
|
scheduler_chan: script_thread.scheduler_chan.clone(),
|
||||||
|
};
|
||||||
|
Rc::new(WorkletThreadPool::spawn(chan, init))
|
||||||
|
}).clone()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a new script thread.
|
/// Creates a new script thread.
|
||||||
pub fn new(state: InitialScriptState,
|
pub fn new(state: InitialScriptState,
|
||||||
port: Receiver<MainThreadScriptMsg>,
|
port: Receiver<MainThreadScriptMsg>,
|
||||||
|
@ -782,6 +805,8 @@ impl ScriptThread {
|
||||||
|
|
||||||
webvr_thread: state.webvr_thread,
|
webvr_thread: state.webvr_thread,
|
||||||
|
|
||||||
|
worklet_thread_pool: Default::default(),
|
||||||
|
|
||||||
docs_with_no_blocking_loads: Default::default(),
|
docs_with_no_blocking_loads: Default::default(),
|
||||||
|
|
||||||
transitioning_nodes: Default::default(),
|
transitioning_nodes: Default::default(),
|
||||||
|
@ -1065,6 +1090,7 @@ impl ScriptThread {
|
||||||
ScriptThreadEventCategory::WebSocketEvent => ProfilerCategory::ScriptWebSocketEvent,
|
ScriptThreadEventCategory::WebSocketEvent => ProfilerCategory::ScriptWebSocketEvent,
|
||||||
ScriptThreadEventCategory::WebVREvent => ProfilerCategory::ScriptWebVREvent,
|
ScriptThreadEventCategory::WebVREvent => ProfilerCategory::ScriptWebVREvent,
|
||||||
ScriptThreadEventCategory::WorkerEvent => ProfilerCategory::ScriptWorkerEvent,
|
ScriptThreadEventCategory::WorkerEvent => ProfilerCategory::ScriptWorkerEvent,
|
||||||
|
ScriptThreadEventCategory::WorkletEvent => ProfilerCategory::ScriptWorkletEvent,
|
||||||
ScriptThreadEventCategory::ServiceWorkerEvent => ProfilerCategory::ScriptServiceWorkerEvent,
|
ScriptThreadEventCategory::ServiceWorkerEvent => ProfilerCategory::ScriptServiceWorkerEvent,
|
||||||
ScriptThreadEventCategory::EnterFullscreen => ProfilerCategory::ScriptEnterFullscreen,
|
ScriptThreadEventCategory::EnterFullscreen => ProfilerCategory::ScriptEnterFullscreen,
|
||||||
ScriptThreadEventCategory::ExitFullscreen => ProfilerCategory::ScriptExitFullscreen,
|
ScriptThreadEventCategory::ExitFullscreen => ProfilerCategory::ScriptExitFullscreen,
|
||||||
|
@ -1149,7 +1175,7 @@ impl ScriptThread {
|
||||||
// The category of the runnable is ignored by the pattern, however
|
// The category of the runnable is ignored by the pattern, however
|
||||||
// it is still respected by profiling (see categorize_msg).
|
// it is still respected by profiling (see categorize_msg).
|
||||||
if !runnable.is_cancelled() {
|
if !runnable.is_cancelled() {
|
||||||
runnable.handler()
|
runnable.main_thread_handler(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(reports_chan)) =>
|
MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(reports_chan)) =>
|
||||||
|
|
|
@ -10,7 +10,6 @@ name = "script_traits"
|
||||||
path = "lib.rs"
|
path = "lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
app_units = "0.4"
|
|
||||||
bluetooth_traits = {path = "../bluetooth_traits"}
|
bluetooth_traits = {path = "../bluetooth_traits"}
|
||||||
canvas_traits = {path = "../canvas_traits"}
|
canvas_traits = {path = "../canvas_traits"}
|
||||||
cookie = "0.6"
|
cookie = "0.6"
|
||||||
|
|
|
@ -67,6 +67,7 @@ WEBIDL_STANDARDS = [
|
||||||
"//dom.spec.whatwg.org",
|
"//dom.spec.whatwg.org",
|
||||||
"//domparsing.spec.whatwg.org",
|
"//domparsing.spec.whatwg.org",
|
||||||
"//drafts.csswg.org",
|
"//drafts.csswg.org",
|
||||||
|
"//drafts.css-houdini.org",
|
||||||
"//drafts.fxtf.org",
|
"//drafts.fxtf.org",
|
||||||
"//encoding.spec.whatwg.org",
|
"//encoding.spec.whatwg.org",
|
||||||
"//fetch.spec.whatwg.org",
|
"//fetch.spec.whatwg.org",
|
||||||
|
|
|
@ -11185,6 +11185,21 @@
|
||||||
[
|
[
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
|
],
|
||||||
|
"mozilla/worklets/syntax_error.js": [
|
||||||
|
[
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"mozilla/worklets/test_worklet.js": [
|
||||||
|
[
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"mozilla/worklets/throw_exception.js": [
|
||||||
|
[
|
||||||
|
{}
|
||||||
|
]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"testharness": {
|
"testharness": {
|
||||||
|
@ -20027,6 +20042,12 @@
|
||||||
"/_mozilla/mozilla/windowproxy.html",
|
"/_mozilla/mozilla/windowproxy.html",
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
|
],
|
||||||
|
"mozilla/worklets/test_worklet.html": [
|
||||||
|
[
|
||||||
|
"/_mozilla/mozilla/worklets/test_worklet.html",
|
||||||
|
{}
|
||||||
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -25796,7 +25817,7 @@
|
||||||
"testharness"
|
"testharness"
|
||||||
],
|
],
|
||||||
"mozilla/interfaces.html": [
|
"mozilla/interfaces.html": [
|
||||||
"21e18bafdbfe5f3aa0ee71766bdc3b6a7e334226",
|
"49dd9f6ef449813f2ce943d4c9fac351357e5c74",
|
||||||
"testharness"
|
"testharness"
|
||||||
],
|
],
|
||||||
"mozilla/interfaces.js": [
|
"mozilla/interfaces.js": [
|
||||||
|
@ -31714,6 +31735,22 @@
|
||||||
"mozilla/windowproxy.html": [
|
"mozilla/windowproxy.html": [
|
||||||
"128cd0aa5cf80f60078979039036d32b470b0616",
|
"128cd0aa5cf80f60078979039036d32b470b0616",
|
||||||
"testharness"
|
"testharness"
|
||||||
|
],
|
||||||
|
"mozilla/worklets/syntax_error.js": [
|
||||||
|
"f3a9b8c78346507bc0b3190c8000ccf80cc133f6",
|
||||||
|
"support"
|
||||||
|
],
|
||||||
|
"mozilla/worklets/test_worklet.html": [
|
||||||
|
"fe9c93a5307c616f878b6623155e1b04c86dd994",
|
||||||
|
"testharness"
|
||||||
|
],
|
||||||
|
"mozilla/worklets/test_worklet.js": [
|
||||||
|
"9d5f8a07cd62a10f4f5ff93744672e5a6fdbc2b0",
|
||||||
|
"support"
|
||||||
|
],
|
||||||
|
"mozilla/worklets/throw_exception.js": [
|
||||||
|
"ebfdae19db68fed8e69142ef73842ac9921e4463",
|
||||||
|
"support"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"url_base": "/_mozilla/",
|
"url_base": "/_mozilla/",
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
[test_worklet.html]
|
||||||
|
type: testharness
|
||||||
|
prefs: [dom.worklet.testing.enabled:true]
|
|
@ -200,6 +200,7 @@ test_interfaces([
|
||||||
"WebSocket",
|
"WebSocket",
|
||||||
"Window",
|
"Window",
|
||||||
"Worker",
|
"Worker",
|
||||||
|
"Worklet",
|
||||||
"XMLDocument",
|
"XMLDocument",
|
||||||
"XMLHttpRequest",
|
"XMLHttpRequest",
|
||||||
"XMLHttpRequestEventTarget",
|
"XMLHttpRequestEventTarget",
|
||||||
|
|
1
tests/wpt/mozilla/tests/mozilla/worklets/syntax_error.js
Normal file
1
tests/wpt/mozilla/tests/mozilla/worklets/syntax_error.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{];
|
35
tests/wpt/mozilla/tests/mozilla/worklets/test_worklet.html
Normal file
35
tests/wpt/mozilla/tests/mozilla/worklets/test_worklet.html
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Test worklet loading</title>
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="/common/get-host-info.sub.js"></script>
|
||||||
|
<script>
|
||||||
|
var testWorklet = new TestWorklet();
|
||||||
|
var host_info = get_host_info();
|
||||||
|
|
||||||
|
promise_test(function() {
|
||||||
|
return testWorklet.addModule("test_worklet.js")
|
||||||
|
.then(function () {
|
||||||
|
assert_equals(testWorklet.lookup("hello"), "world");
|
||||||
|
});
|
||||||
|
}, "Loading a test worklet.");
|
||||||
|
|
||||||
|
promise_test(function(t) {
|
||||||
|
var path = new URL("test_worklet.js", document.location).pathname;
|
||||||
|
var url = new URL(path, host_info.HTTP_REMOTE_ORIGIN);
|
||||||
|
return promise_rejects(t, "AbortError", testWorklet.addModule(url));
|
||||||
|
}, "Loading a cross-origin test worklet.");
|
||||||
|
|
||||||
|
promise_test(function(t) {
|
||||||
|
return promise_rejects(t, "AbortError", testWorklet.addModule("nonexistent_worklet.js"));
|
||||||
|
}, "Loading a nonexistent test worklet.");
|
||||||
|
|
||||||
|
promise_test(function(t) {
|
||||||
|
return promise_rejects(t, "AbortError", testWorklet.addModule("syntax_error.js"));
|
||||||
|
}, "Loading a syntactically incorrect test worklet.");
|
||||||
|
|
||||||
|
promise_test(function(t) {
|
||||||
|
return promise_rejects(t, "AbortError", testWorklet.addModule("throw_exception.js"));
|
||||||
|
}, "Loading an exception-throwing test worklet.");
|
||||||
|
</script>
|
1
tests/wpt/mozilla/tests/mozilla/worklets/test_worklet.js
Normal file
1
tests/wpt/mozilla/tests/mozilla/worklets/test_worklet.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
registerKeyValue("hello", "world");
|
|
@ -0,0 +1 @@
|
||||||
|
throw new TypeError();
|
Loading…
Add table
Add a link
Reference in a new issue