Rewild Your Web
18
fork

Configure Feed

Select the types of activity you want to include in your feed.

everywhere: at:// an trusted:// protocol handlers with minimal UI

Signed-off-by: webbeef <me@webbeef.org>

webbeef 834061d0 a2b10044

+4304 -141
+477 -39
Cargo.lock
··· 233 233 "bitflags 2.11.0", 234 234 "cc", 235 235 "cesu8", 236 - "jni", 236 + "jni 0.21.1", 237 237 "jni-sys 0.3.1", 238 238 "libc", 239 239 "log", ··· 566 566 checksum = "41e67cd8309bbd06cd603a9e693a784ac2e5d1e955f11286e355089fcab3047c" 567 567 568 568 [[package]] 569 + name = "atproto-identity" 570 + version = "0.13.0" 571 + source = "registry+https://github.com/rust-lang/crates.io-index" 572 + checksum = "b956c07726fce812630be63c5cb31b1961cbb70f0a05614278523102d78c3a48" 573 + dependencies = [ 574 + "anyhow", 575 + "async-trait", 576 + "ecdsa", 577 + "elliptic-curve", 578 + "hickory-resolver", 579 + "k256", 580 + "lru 0.12.5", 581 + "multibase", 582 + "p256", 583 + "p384", 584 + "rand 0.8.5", 585 + "reqwest 0.12.28", 586 + "serde", 587 + "serde_ipld_dagcbor", 588 + "serde_json", 589 + "thiserror 2.0.18", 590 + "tokio", 591 + "tracing", 592 + "urlencoding", 593 + ] 594 + 595 + [[package]] 569 596 name = "attohttpc" 570 597 version = "0.30.1" 571 598 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 727 754 ] 728 755 729 756 [[package]] 757 + name = "base-x" 758 + version = "0.2.11" 759 + source = "registry+https://github.com/rust-lang/crates.io-index" 760 + checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" 761 + 762 + [[package]] 730 763 name = "base16ct" 731 764 version = "0.2.0" 732 765 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 737 770 version = "1.0.0" 738 771 source = "registry+https://github.com/rust-lang/crates.io-index" 739 772 checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6" 773 + 774 + [[package]] 775 + name = "base256emoji" 776 + version = "1.0.2" 777 + source = "registry+https://github.com/rust-lang/crates.io-index" 778 + checksum = "b5e9430d9a245a77c92176e649af6e275f20839a48389859d1661e9a128d077c" 779 + dependencies = [ 780 + "const-str", 781 + "match-lookup", 782 + ] 740 783 741 784 [[package]] 742 785 name = "base32" ··· 977 1020 ] 978 1021 979 1022 [[package]] 980 - name = "blurdroid" 981 - version = "0.1.6" 1023 + name = "bluez-async" 1024 + version = "0.8.2" 982 1025 source = "registry+https://github.com/rust-lang/crates.io-index" 983 - checksum = "19b23557dd27704797128f9db2816416bef20dad62d4a9768714eeb65f07d296" 984 - 985 - [[package]] 986 - name = "blurmac" 987 - version = "0.1.0" 1026 + checksum = "84ae4213cc2a8dc663acecac67bbdad05142be4d8ef372b6903abf878b0c690a" 988 1027 dependencies = [ 1028 + "bitflags 2.11.0", 1029 + "bluez-generated", 1030 + "dbus", 1031 + "dbus-tokio", 1032 + "futures", 1033 + "itertools 0.14.0", 989 1034 "log", 990 - "objc2 0.2.7", 1035 + "serde", 1036 + "serde-xml-rs", 1037 + "thiserror 2.0.18", 1038 + "tokio", 1039 + "uuid", 991 1040 ] 992 1041 993 1042 [[package]] 994 - name = "blurmock" 995 - version = "0.1.3" 1043 + name = "bluez-generated" 1044 + version = "0.4.0" 996 1045 source = "registry+https://github.com/rust-lang/crates.io-index" 997 - checksum = "9c150fd617830fd121919bbd500a784507e8af1bae744efcf587591c65c375d4" 1046 + checksum = "9676783265eadd6f11829982792c6f303f3854d014edfba384685dcf237dd062" 998 1047 dependencies = [ 999 - "hex", 1048 + "dbus", 1000 1049 ] 1001 1050 1002 1051 [[package]] 1003 - name = "blurz" 1004 - version = "0.3.0" 1052 + name = "blurmock" 1053 + version = "0.1.3" 1005 1054 source = "registry+https://github.com/rust-lang/crates.io-index" 1006 - checksum = "f6dae8337ff67fe8ead29a28a0115605753e6a5205d4b6017e9f42f198c3c50a" 1055 + checksum = "9c150fd617830fd121919bbd500a784507e8af1bae744efcf587591c65c375d4" 1007 1056 dependencies = [ 1008 - "dbus", 1009 1057 "hex", 1010 1058 ] 1011 1059 ··· 1031 1079 ] 1032 1080 1033 1081 [[package]] 1082 + name = "btleplug" 1083 + version = "0.12.0" 1084 + source = "registry+https://github.com/rust-lang/crates.io-index" 1085 + checksum = "52c3264dbe2c8e29381e4e95aa2d2783ad0b9192b511240f3755b7e5e3cee87e" 1086 + dependencies = [ 1087 + "async-trait", 1088 + "bitflags 2.11.0", 1089 + "bluez-async", 1090 + "dashmap", 1091 + "dbus", 1092 + "futures", 1093 + "jni 0.19.0", 1094 + "log", 1095 + "objc2 0.5.2", 1096 + "objc2-core-bluetooth", 1097 + "objc2-foundation 0.2.2", 1098 + "once_cell", 1099 + "static_assertions", 1100 + "thiserror 2.0.18", 1101 + "tokio", 1102 + "tokio-stream", 1103 + "uuid", 1104 + "windows 0.62.2", 1105 + "windows-future 0.3.2", 1106 + ] 1107 + 1108 + [[package]] 1034 1109 name = "buf-read-ext" 1035 1110 version = "0.4.0" 1036 1111 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1180 1255 ] 1181 1256 1182 1257 [[package]] 1258 + name = "cbor4ii" 1259 + version = "0.2.14" 1260 + source = "registry+https://github.com/rust-lang/crates.io-index" 1261 + checksum = "b544cf8c89359205f4f990d0e6f3828db42df85b5dac95d09157a250eb0749c4" 1262 + dependencies = [ 1263 + "serde", 1264 + ] 1265 + 1266 + [[package]] 1183 1267 name = "cc" 1184 1268 version = "1.2.57" 1185 1269 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1336 1420 ] 1337 1421 1338 1422 [[package]] 1423 + name = "cid" 1424 + version = "0.11.1" 1425 + source = "registry+https://github.com/rust-lang/crates.io-index" 1426 + checksum = "3147d8272e8fa0ccd29ce51194dd98f79ddfb8191ba9e3409884e751798acf3a" 1427 + dependencies = [ 1428 + "core2", 1429 + "multibase", 1430 + "multihash", 1431 + "serde", 1432 + "serde_bytes", 1433 + "unsigned-varint", 1434 + ] 1435 + 1436 + [[package]] 1339 1437 name = "cipher" 1340 1438 version = "0.4.4" 1341 1439 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1524 1622 version = "0.10.2" 1525 1623 source = "registry+https://github.com/rust-lang/crates.io-index" 1526 1624 checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" 1625 + 1626 + [[package]] 1627 + name = "const-str" 1628 + version = "0.4.3" 1629 + source = "registry+https://github.com/rust-lang/crates.io-index" 1630 + checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" 1527 1631 1528 1632 [[package]] 1529 1633 name = "constant_time_eq" ··· 1621 1725 "bitflags 1.3.2", 1622 1726 "core-foundation 0.9.4", 1623 1727 "core-graphics-types 0.1.3", 1624 - "foreign-types", 1728 + "foreign-types 0.5.0", 1625 1729 "libc", 1626 1730 ] 1627 1731 ··· 1655 1759 dependencies = [ 1656 1760 "core-foundation 0.9.4", 1657 1761 "core-graphics", 1658 - "foreign-types", 1762 + "foreign-types 0.5.0", 1659 1763 "libc", 1660 1764 ] 1661 1765 ··· 1961 2065 ] 1962 2066 1963 2067 [[package]] 2068 + name = "dashmap" 2069 + version = "6.1.0" 2070 + source = "registry+https://github.com/rust-lang/crates.io-index" 2071 + checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" 2072 + dependencies = [ 2073 + "cfg-if", 2074 + "crossbeam-utils", 2075 + "hashbrown 0.14.5", 2076 + "lock_api", 2077 + "once_cell", 2078 + "parking_lot_core", 2079 + ] 2080 + 2081 + [[package]] 1964 2082 name = "data-encoding" 1965 2083 version = "2.10.0" 1966 2084 source = "registry+https://github.com/rust-lang/crates.io-index" 1967 2085 checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" 1968 2086 1969 2087 [[package]] 2088 + name = "data-encoding-macro" 2089 + version = "0.1.19" 2090 + source = "registry+https://github.com/rust-lang/crates.io-index" 2091 + checksum = "8142a83c17aa9461d637e649271eae18bf2edd00e91f2e105df36c3c16355bdb" 2092 + dependencies = [ 2093 + "data-encoding", 2094 + "data-encoding-macro-internal", 2095 + ] 2096 + 2097 + [[package]] 2098 + name = "data-encoding-macro-internal" 2099 + version = "0.1.17" 2100 + source = "registry+https://github.com/rust-lang/crates.io-index" 2101 + checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de" 2102 + dependencies = [ 2103 + "data-encoding", 2104 + "syn 2.0.117", 2105 + ] 2106 + 2107 + [[package]] 1970 2108 name = "data-url" 1971 2109 version = "0.3.2" 1972 2110 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1974 2112 1975 2113 [[package]] 1976 2114 name = "dbus" 1977 - version = "0.6.5" 2115 + version = "0.9.10" 1978 2116 source = "registry+https://github.com/rust-lang/crates.io-index" 1979 - checksum = "48b5f0f36f1eebe901b0e6bee369a77ed3396334bf3f09abd46454a576f71819" 2117 + checksum = "21b3aa68d7e7abee336255bd7248ea965cc393f3e70411135a6f6a4b651345d4" 1980 2118 dependencies = [ 2119 + "futures-channel", 2120 + "futures-util", 1981 2121 "libc", 1982 2122 "libdbus-sys", 2123 + "windows-sys 0.59.0", 2124 + ] 2125 + 2126 + [[package]] 2127 + name = "dbus-tokio" 2128 + version = "0.7.6" 2129 + source = "registry+https://github.com/rust-lang/crates.io-index" 2130 + checksum = "007688d459bc677131c063a3a77fb899526e17b7980f390b69644bdbc41fad13" 2131 + dependencies = [ 2132 + "dbus", 2133 + "libc", 2134 + "tokio", 1983 2135 ] 1984 2136 1985 2137 [[package]] ··· 2337 2489 checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" 2338 2490 dependencies = [ 2339 2491 "base16ct 0.2.0", 2492 + "base64ct", 2340 2493 "crypto-bigint", 2341 2494 "digest 0.10.7", 2342 2495 "ff", ··· 2347 2500 "pkcs8 0.10.2", 2348 2501 "rand_core 0.6.4", 2349 2502 "sec1 0.7.3", 2503 + "serde_json", 2504 + "serdect", 2350 2505 "subtle", 2351 2506 "zeroize", 2352 2507 ] ··· 2751 2906 2752 2907 [[package]] 2753 2908 name = "foreign-types" 2909 + version = "0.3.2" 2910 + source = "registry+https://github.com/rust-lang/crates.io-index" 2911 + checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 2912 + dependencies = [ 2913 + "foreign-types-shared 0.1.1", 2914 + ] 2915 + 2916 + [[package]] 2917 + name = "foreign-types" 2754 2918 version = "0.5.0" 2755 2919 source = "registry+https://github.com/rust-lang/crates.io-index" 2756 2920 checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" 2757 2921 dependencies = [ 2758 2922 "foreign-types-macros", 2759 - "foreign-types-shared", 2923 + "foreign-types-shared 0.3.1", 2760 2924 ] 2761 2925 2762 2926 [[package]] ··· 2769 2933 "quote", 2770 2934 "syn 2.0.117", 2771 2935 ] 2936 + 2937 + [[package]] 2938 + name = "foreign-types-shared" 2939 + version = "0.1.1" 2940 + source = "registry+https://github.com/rust-lang/crates.io-index" 2941 + checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 2772 2942 2773 2943 [[package]] 2774 2944 name = "foreign-types-shared" ··· 3909 4079 "cc", 3910 4080 "core-graphics", 3911 4081 "core-text", 3912 - "foreign-types", 4082 + "foreign-types 0.5.0", 3913 4083 "freetype-sys", 3914 4084 "pkg-config", 3915 4085 "winapi", ··· 3923 4093 dependencies = [ 3924 4094 "byteorder", 3925 4095 ] 4096 + 4097 + [[package]] 4098 + name = "hashbrown" 4099 + version = "0.14.5" 4100 + source = "registry+https://github.com/rust-lang/crates.io-index" 4101 + checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 3926 4102 3927 4103 [[package]] 3928 4104 name = "hashbrown" ··· 4305 4481 ] 4306 4482 4307 4483 [[package]] 4484 + name = "hyper-tls" 4485 + version = "0.6.0" 4486 + source = "registry+https://github.com/rust-lang/crates.io-index" 4487 + checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" 4488 + dependencies = [ 4489 + "bytes", 4490 + "http-body-util", 4491 + "hyper 1.8.1", 4492 + "hyper-util", 4493 + "native-tls", 4494 + "tokio", 4495 + "tokio-native-tls", 4496 + "tower-service", 4497 + ] 4498 + 4499 + [[package]] 4308 4500 name = "hyper-util" 4309 4501 version = "0.1.20" 4310 4502 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4322 4514 "percent-encoding", 4323 4515 "pin-project-lite", 4324 4516 "socket2 0.6.3", 4517 + "system-configuration", 4325 4518 "tokio", 4326 4519 "tower-service", 4327 4520 "tracing", 4521 + "windows-registry", 4328 4522 ] 4329 4523 4330 4524 [[package]] ··· 5087 5281 ] 5088 5282 5089 5283 [[package]] 5284 + name = "ipld-core" 5285 + version = "0.4.3" 5286 + source = "registry+https://github.com/rust-lang/crates.io-index" 5287 + checksum = "090f624976d72f0b0bb71b86d58dc16c15e069193067cb3a3a09d655246cbbda" 5288 + dependencies = [ 5289 + "cid", 5290 + "serde", 5291 + "serde_bytes", 5292 + ] 5293 + 5294 + [[package]] 5090 5295 name = "ipnet" 5091 5296 version = "2.12.0" 5092 5297 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 5300 5505 "iroh-metrics", 5301 5506 "iroh-quinn", 5302 5507 "iroh-quinn-proto", 5303 - "lru", 5508 + "lru 0.16.3", 5304 5509 "n0-error", 5305 5510 "n0-future", 5306 5511 "num_enum", ··· 5411 5616 5412 5617 [[package]] 5413 5618 name = "jni" 5619 + version = "0.19.0" 5620 + source = "registry+https://github.com/rust-lang/crates.io-index" 5621 + checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" 5622 + dependencies = [ 5623 + "cesu8", 5624 + "combine", 5625 + "jni-sys 0.3.1", 5626 + "log", 5627 + "thiserror 1.0.69", 5628 + "walkdir", 5629 + ] 5630 + 5631 + [[package]] 5632 + name = "jni" 5414 5633 version = "0.21.1" 5415 5634 source = "registry+https://github.com/rust-lang/crates.io-index" 5416 5635 checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" ··· 5471 5690 dependencies = [ 5472 5691 "once_cell", 5473 5692 "wasm-bindgen", 5693 + ] 5694 + 5695 + [[package]] 5696 + name = "k256" 5697 + version = "0.13.4" 5698 + source = "registry+https://github.com/rust-lang/crates.io-index" 5699 + checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" 5700 + dependencies = [ 5701 + "cfg-if", 5702 + "ecdsa", 5703 + "elliptic-curve", 5704 + "once_cell", 5705 + "sha2 0.10.9", 5706 + "signature 2.2.0", 5474 5707 ] 5475 5708 5476 5709 [[package]] ··· 5789 6022 5790 6023 [[package]] 5791 6024 name = "lru" 6025 + version = "0.12.5" 6026 + source = "registry+https://github.com/rust-lang/crates.io-index" 6027 + checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" 6028 + dependencies = [ 6029 + "hashbrown 0.15.5", 6030 + ] 6031 + 6032 + [[package]] 6033 + name = "lru" 5792 6034 version = "0.16.3" 5793 6035 source = "registry+https://github.com/rust-lang/crates.io-index" 5794 6036 checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" ··· 5846 6088 ] 5847 6089 5848 6090 [[package]] 6091 + name = "match-lookup" 6092 + version = "0.1.2" 6093 + source = "registry+https://github.com/rust-lang/crates.io-index" 6094 + checksum = "757aee279b8bdbb9f9e676796fd459e4207a1f986e87886700abf589f5abf771" 6095 + dependencies = [ 6096 + "proc-macro2", 6097 + "quote", 6098 + "syn 2.0.117", 6099 + ] 6100 + 6101 + [[package]] 5849 6102 name = "matchers" 5850 6103 version = "0.2.0" 5851 6104 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 5903 6156 "bitflags 2.11.0", 5904 6157 "block", 5905 6158 "core-graphics-types 0.2.0", 5906 - "foreign-types", 6159 + "foreign-types 0.5.0", 5907 6160 "log", 5908 6161 "objc", 5909 6162 "paste", ··· 6095 6348 checksum = "956787520e75e9bd233246045d19f42fb73242759cc57fba9611d940ae96d4b0" 6096 6349 6097 6350 [[package]] 6351 + name = "multibase" 6352 + version = "0.9.2" 6353 + source = "registry+https://github.com/rust-lang/crates.io-index" 6354 + checksum = "8694bb4835f452b0e3bb06dbebb1d6fc5385b6ca1caf2e55fd165c042390ec77" 6355 + dependencies = [ 6356 + "base-x", 6357 + "base256emoji", 6358 + "data-encoding", 6359 + "data-encoding-macro", 6360 + ] 6361 + 6362 + [[package]] 6363 + name = "multihash" 6364 + version = "0.19.3" 6365 + source = "registry+https://github.com/rust-lang/crates.io-index" 6366 + checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d" 6367 + dependencies = [ 6368 + "core2", 6369 + "serde", 6370 + "unsigned-varint", 6371 + ] 6372 + 6373 + [[package]] 6098 6374 name = "n0-error" 6099 6375 version = "0.1.3" 6100 6376 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 6171 6447 "spirv", 6172 6448 "thiserror 2.0.18", 6173 6449 "unicode-ident", 6450 + ] 6451 + 6452 + [[package]] 6453 + name = "native-tls" 6454 + version = "0.2.18" 6455 + source = "registry+https://github.com/rust-lang/crates.io-index" 6456 + checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" 6457 + dependencies = [ 6458 + "libc", 6459 + "log", 6460 + "openssl", 6461 + "openssl-probe", 6462 + "openssl-sys", 6463 + "schannel", 6464 + "security-framework", 6465 + "security-framework-sys", 6466 + "tempfile", 6174 6467 ] 6175 6468 6176 6469 [[package]] ··· 6562 6855 6563 6856 [[package]] 6564 6857 name = "objc2" 6565 - version = "0.2.7" 6566 - source = "registry+https://github.com/rust-lang/crates.io-index" 6567 - checksum = "612c08b52f6d8b11d8c199d0798bc38ab35c0a95da3be548a60808a4ae56d1f4" 6568 - dependencies = [ 6569 - "malloc_buf", 6570 - ] 6571 - 6572 - [[package]] 6573 - name = "objc2" 6574 6858 version = "0.5.2" 6575 6859 source = "registry+https://github.com/rust-lang/crates.io-index" 6576 6860 checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" ··· 6643 6927 ] 6644 6928 6645 6929 [[package]] 6930 + name = "objc2-core-bluetooth" 6931 + version = "0.2.2" 6932 + source = "registry+https://github.com/rust-lang/crates.io-index" 6933 + checksum = "5a644b62ffb826a5277f536cf0f701493de420b13d40e700c452c36567771111" 6934 + dependencies = [ 6935 + "bitflags 2.11.0", 6936 + "objc2 0.5.2", 6937 + "objc2-foundation 0.2.2", 6938 + ] 6939 + 6940 + [[package]] 6646 6941 name = "objc2-core-data" 6647 6942 version = "0.2.2" 6648 6943 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 6965 7260 checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" 6966 7261 6967 7262 [[package]] 7263 + name = "openssl" 7264 + version = "0.10.76" 7265 + source = "registry+https://github.com/rust-lang/crates.io-index" 7266 + checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" 7267 + dependencies = [ 7268 + "bitflags 2.11.0", 7269 + "cfg-if", 7270 + "foreign-types 0.3.2", 7271 + "libc", 7272 + "once_cell", 7273 + "openssl-macros", 7274 + "openssl-sys", 7275 + ] 7276 + 7277 + [[package]] 7278 + name = "openssl-macros" 7279 + version = "0.1.1" 7280 + source = "registry+https://github.com/rust-lang/crates.io-index" 7281 + checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 7282 + dependencies = [ 7283 + "proc-macro2", 7284 + "quote", 7285 + "syn 2.0.117", 7286 + ] 7287 + 7288 + [[package]] 6968 7289 name = "openssl-probe" 6969 7290 version = "0.2.1" 6970 7291 source = "registry+https://github.com/rust-lang/crates.io-index" 6971 7292 checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" 7293 + 7294 + [[package]] 7295 + name = "openssl-sys" 7296 + version = "0.9.112" 7297 + source = "registry+https://github.com/rust-lang/crates.io-index" 7298 + checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" 7299 + dependencies = [ 7300 + "cc", 7301 + "libc", 7302 + "pkg-config", 7303 + "vcpkg", 7304 + ] 6972 7305 6973 7306 [[package]] 6974 7307 name = "openxr" ··· 7372 7705 "futures-lite", 7373 7706 "getrandom 0.4.2", 7374 7707 "log", 7375 - "lru", 7708 + "lru 0.16.3", 7376 7709 "ntimestamp", 7377 7710 "reqwest 0.13.2", 7378 7711 "self_cell", ··· 8234 8567 dependencies = [ 8235 8568 "base64 0.22.1", 8236 8569 "bytes", 8570 + "encoding_rs", 8237 8571 "futures-core", 8238 8572 "futures-util", 8573 + "h2 0.4.13", 8239 8574 "http 1.4.0", 8240 8575 "http-body 1.0.1", 8241 8576 "http-body-util", 8242 8577 "hyper 1.8.1", 8243 8578 "hyper-rustls", 8579 + "hyper-tls", 8244 8580 "hyper-util", 8245 8581 "js-sys", 8246 8582 "log", 8583 + "mime", 8584 + "native-tls", 8247 8585 "percent-encoding", 8248 8586 "pin-project-lite", 8249 8587 "quinn", ··· 8254 8592 "serde_urlencoded", 8255 8593 "sync_wrapper", 8256 8594 "tokio", 8595 + "tokio-native-tls", 8257 8596 "tokio-rustls", 8258 8597 "tokio-util", 8259 8598 "tower", ··· 8512 8851 dependencies = [ 8513 8852 "core-foundation 0.10.1", 8514 8853 "core-foundation-sys", 8515 - "jni", 8854 + "jni 0.21.1", 8516 8855 "log", 8517 8856 "once_cell", 8518 8857 "rustls", ··· 8660 8999 "der 0.7.10", 8661 9000 "generic-array", 8662 9001 "pkcs8 0.10.2", 9002 + "serdect", 8663 9003 "subtle", 8664 9004 "zeroize", 8665 9005 ] ··· 8756 9096 ] 8757 9097 8758 9098 [[package]] 9099 + name = "serde-xml-rs" 9100 + version = "0.8.2" 9101 + source = "registry+https://github.com/rust-lang/crates.io-index" 9102 + checksum = "cc2215ce3e6a77550b80a1c37251b7d294febaf42e36e21b7b411e0bf54d540d" 9103 + dependencies = [ 9104 + "log", 9105 + "serde", 9106 + "thiserror 2.0.18", 9107 + "xml", 9108 + ] 9109 + 9110 + [[package]] 8759 9111 name = "serde_bytes" 8760 9112 version = "0.11.19" 8761 9113 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 8783 9135 "proc-macro2", 8784 9136 "quote", 8785 9137 "syn 2.0.117", 9138 + ] 9139 + 9140 + [[package]] 9141 + name = "serde_ipld_dagcbor" 9142 + version = "0.6.4" 9143 + source = "registry+https://github.com/rust-lang/crates.io-index" 9144 + checksum = "46182f4f08349a02b45c998ba3215d3f9de826246ba02bb9dddfe9a2a2100778" 9145 + dependencies = [ 9146 + "cbor4ii", 9147 + "ipld-core", 9148 + "scopeguard", 9149 + "serde", 8786 9150 ] 8787 9151 8788 9152 [[package]] ··· 8846 9210 "form_urlencoded", 8847 9211 "itoa", 8848 9212 "ryu", 9213 + "serde", 9214 + ] 9215 + 9216 + [[package]] 9217 + name = "serdect" 9218 + version = "0.2.0" 9219 + source = "registry+https://github.com/rust-lang/crates.io-index" 9220 + checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" 9221 + dependencies = [ 9222 + "base16ct 0.2.0", 8849 9223 "serde", 8850 9224 ] 8851 9225 ··· 8989 9363 version = "0.0.6" 8990 9364 dependencies = [ 8991 9365 "bitflags 2.11.0", 8992 - "blurdroid", 8993 - "blurmac", 8994 9366 "blurmock", 8995 - "blurz", 9367 + "btleplug", 9368 + "futures", 8996 9369 "log", 8997 9370 "rand 0.9.2", 8998 9371 "servo-base", 8999 9372 "servo-bluetooth-traits", 9000 9373 "servo-config", 9001 9374 "servo-embedder-traits", 9375 + "tokio", 9002 9376 "uuid", 9003 9377 ] 9004 9378 ··· 9128 9502 "servo-webxr-api", 9129 9503 "stylo", 9130 9504 "stylo_traits", 9505 + "sync_wrapper", 9131 9506 "tokio", 9132 9507 "tracing", 9133 9508 ] ··· 9697 10072 name = "servo-net" 9698 10073 version = "0.0.6" 9699 10074 dependencies = [ 10075 + "anyhow", 9700 10076 "async-compression", 9701 10077 "async-recursion", 9702 10078 "async-tungstenite", 10079 + "atproto-identity", 9703 10080 "base64 0.22.1", 9704 10081 "bytes", 9705 10082 "chrono", ··· 9730 10107 "parking_lot", 9731 10108 "quick_cache", 9732 10109 "regex", 10110 + "reqwest 0.12.28", 9733 10111 "resvg", 9734 10112 "rustc-hash 2.1.1", 9735 10113 "rustls", 9736 10114 "rustls-pki-types", 9737 10115 "rustls-platform-verifier", 9738 10116 "serde", 10117 + "serde_json", 9739 10118 "servo-base", 9740 10119 "servo-config", 10120 + "servo-constellation-traits", 9741 10121 "servo-devtools-traits", 9742 10122 "servo-embedder-traits", 9743 10123 "servo-hyper-serde", ··· 9750 10130 "servo-url", 9751 10131 "servo_arc", 9752 10132 "sha2 0.10.9", 10133 + "sync_wrapper", 9753 10134 "time", 9754 10135 "tokio", 9755 10136 "tokio-rustls", ··· 9785 10166 "rustc-hash 2.1.1", 9786 10167 "rustls-pki-types", 9787 10168 "serde", 10169 + "serde_json", 9788 10170 "servo-base", 9789 10171 "servo-config", 9790 10172 "servo-embedder-traits", ··· 9795 10177 "servo-profile-traits", 9796 10178 "servo-url", 9797 10179 "servo_arc", 10180 + "sync_wrapper", 9798 10181 "sys-locale", 9799 10182 "tokio", 10183 + "tower", 9800 10184 "url", 9801 10185 "uuid", 9802 10186 "webrender_api", ··· 11029 11413 ] 11030 11414 11031 11415 [[package]] 11416 + name = "system-configuration" 11417 + version = "0.7.0" 11418 + source = "registry+https://github.com/rust-lang/crates.io-index" 11419 + checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" 11420 + dependencies = [ 11421 + "bitflags 2.11.0", 11422 + "core-foundation 0.9.4", 11423 + "system-configuration-sys", 11424 + ] 11425 + 11426 + [[package]] 11427 + name = "system-configuration-sys" 11428 + version = "0.6.0" 11429 + source = "registry+https://github.com/rust-lang/crates.io-index" 11430 + checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" 11431 + dependencies = [ 11432 + "core-foundation-sys", 11433 + "libc", 11434 + ] 11435 + 11436 + [[package]] 11032 11437 name = "system-deps" 11033 11438 version = "6.2.2" 11034 11439 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 11418 11823 ] 11419 11824 11420 11825 [[package]] 11826 + name = "tokio-native-tls" 11827 + version = "0.3.1" 11828 + source = "registry+https://github.com/rust-lang/crates.io-index" 11829 + checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 11830 + dependencies = [ 11831 + "native-tls", 11832 + "tokio", 11833 + ] 11834 + 11835 + [[package]] 11421 11836 name = "tokio-rustls" 11422 11837 version = "0.26.4" 11423 11838 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 11931 12346 ] 11932 12347 11933 12348 [[package]] 12349 + name = "unsigned-varint" 12350 + version = "0.8.0" 12351 + source = "registry+https://github.com/rust-lang/crates.io-index" 12352 + checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" 12353 + 12354 + [[package]] 11934 12355 name = "untrusted" 11935 12356 version = "0.9.0" 11936 12357 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 11948 12369 "serde", 11949 12370 "serde_derive", 11950 12371 ] 12372 + 12373 + [[package]] 12374 + name = "urlencoding" 12375 + version = "2.1.3" 12376 + source = "registry+https://github.com/rust-lang/crates.io-index" 12377 + checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" 11951 12378 11952 12379 [[package]] 11953 12380 name = "urlpattern" ··· 12998 13425 dependencies = [ 12999 13426 "windows-core 0.62.2", 13000 13427 "windows-link 0.2.1", 13428 + ] 13429 + 13430 + [[package]] 13431 + name = "windows-registry" 13432 + version = "0.6.1" 13433 + source = "registry+https://github.com/rust-lang/crates.io-index" 13434 + checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" 13435 + dependencies = [ 13436 + "windows-link 0.2.1", 13437 + "windows-result 0.4.1", 13438 + "windows-strings 0.5.1", 13001 13439 ] 13002 13440 13003 13441 [[package]]
+1 -1
forkme.lock
··· 1 - de4a79facf12bf758d8939617f5bd83e5953314d 1 + c9926615334912b17ea31ff95df10795c397c3ec
+1223
patches/Cargo.lock.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -635,6 +635,33 @@ 4 + checksum = "41e67cd8309bbd06cd603a9e693a784ac2e5d1e955f11286e355089fcab3047c" 5 + 6 + [[package]] 7 + +name = "atproto-identity" 8 + +version = "0.13.0" 9 + +source = "registry+https://github.com/rust-lang/crates.io-index" 10 + +checksum = "b956c07726fce812630be63c5cb31b1961cbb70f0a05614278523102d78c3a48" 11 + +dependencies = [ 12 + + "anyhow", 13 + + "async-trait", 14 + + "ecdsa", 15 + + "elliptic-curve", 16 + + "hickory-resolver", 17 + + "k256", 18 + + "lru", 19 + + "multibase", 20 + + "p256", 21 + + "p384", 22 + + "rand 0.8.5", 23 + + "reqwest", 24 + + "serde", 25 + + "serde_ipld_dagcbor", 26 + + "serde_json", 27 + + "thiserror 2.0.18", 28 + + "tokio", 29 + + "tracing", 30 + + "urlencoding", 31 + +] 32 + + 33 + +[[package]] 34 + name = "atspi" 35 + version = "0.29.0" 36 + source = "registry+https://github.com/rust-lang/crates.io-index" 37 + @@ -738,6 +765,12 @@ 38 + ] 39 + 40 + [[package]] 41 + +name = "base-x" 42 + +version = "0.2.11" 43 + +source = "registry+https://github.com/rust-lang/crates.io-index" 44 + +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" 45 + + 46 + +[[package]] 47 + name = "base16ct" 48 + version = "0.2.0" 49 + source = "registry+https://github.com/rust-lang/crates.io-index" 50 + @@ -744,6 +777,16 @@ 51 + checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" 52 + 53 + [[package]] 54 + +name = "base256emoji" 55 + +version = "1.0.2" 56 + +source = "registry+https://github.com/rust-lang/crates.io-index" 57 + +checksum = "b5e9430d9a245a77c92176e649af6e275f20839a48389859d1661e9a128d077c" 58 + +dependencies = [ 59 + + "const-str", 60 + + "match-lookup", 61 + +] 62 + + 63 + +[[package]] 64 + name = "base64" 65 + version = "0.12.3" 66 + source = "registry+https://github.com/rust-lang/crates.io-index" 67 + @@ -1125,6 +1168,15 @@ 68 + ] 69 + 70 + [[package]] 71 + +name = "cbor4ii" 72 + +version = "0.2.14" 73 + +source = "registry+https://github.com/rust-lang/crates.io-index" 74 + +checksum = "b544cf8c89359205f4f990d0e6f3828db42df85b5dac95d09157a250eb0749c4" 75 + +dependencies = [ 76 + + "serde", 77 + +] 78 + + 79 + +[[package]] 80 + name = "cc" 81 + version = "1.2.57" 82 + source = "registry+https://github.com/rust-lang/crates.io-index" 83 + @@ -1269,6 +1321,20 @@ 84 + ] 85 + 86 + [[package]] 87 + +name = "cid" 88 + +version = "0.11.1" 89 + +source = "registry+https://github.com/rust-lang/crates.io-index" 90 + +checksum = "3147d8272e8fa0ccd29ce51194dd98f79ddfb8191ba9e3409884e751798acf3a" 91 + +dependencies = [ 92 + + "core2", 93 + + "multibase", 94 + + "multihash", 95 + + "serde", 96 + + "serde_bytes", 97 + + "unsigned-varint", 98 + +] 99 + + 100 + +[[package]] 101 + name = "cipher" 102 + version = "0.4.4" 103 + source = "registry+https://github.com/rust-lang/crates.io-index" 104 + @@ -1420,6 +1486,12 @@ 105 + checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" 106 + 107 + [[package]] 108 + +name = "const-str" 109 + +version = "0.4.3" 110 + +source = "registry+https://github.com/rust-lang/crates.io-index" 111 + +checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" 112 + + 113 + +[[package]] 114 + name = "content-security-policy" 115 + version = "0.7.0" 116 + source = "registry+https://github.com/rust-lang/crates.io-index" 117 + @@ -1499,7 +1571,7 @@ 118 + "bitflags 1.3.2", 119 + "core-foundation 0.9.4", 120 + "core-graphics-types 0.1.3", 121 + - "foreign-types", 122 + + "foreign-types 0.5.0", 123 + "libc", 124 + ] 125 + 126 + @@ -1533,11 +1605,20 @@ 127 + dependencies = [ 128 + "core-foundation 0.9.4", 129 + "core-graphics", 130 + - "foreign-types", 131 + + "foreign-types 0.5.0", 132 + "libc", 133 + ] 134 + 135 + [[package]] 136 + +name = "core2" 137 + +version = "0.4.0" 138 + +source = "registry+https://github.com/rust-lang/crates.io-index" 139 + +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" 140 + +dependencies = [ 141 + + "memchr", 142 + +] 143 + + 144 + +[[package]] 145 + name = "core_maths" 146 + version = "0.1.1" 147 + source = "registry+https://github.com/rust-lang/crates.io-index" 148 + @@ -1601,6 +1682,12 @@ 149 + ] 150 + 151 + [[package]] 152 + +name = "critical-section" 153 + +version = "1.2.0" 154 + +source = "registry+https://github.com/rust-lang/crates.io-index" 155 + +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" 156 + + 157 + +[[package]] 158 + name = "crossbeam-channel" 159 + version = "0.5.15" 160 + source = "registry+https://github.com/rust-lang/crates.io-index" 161 + @@ -1799,6 +1886,26 @@ 162 + checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" 163 + 164 + [[package]] 165 + +name = "data-encoding-macro" 166 + +version = "0.1.19" 167 + +source = "registry+https://github.com/rust-lang/crates.io-index" 168 + +checksum = "8142a83c17aa9461d637e649271eae18bf2edd00e91f2e105df36c3c16355bdb" 169 + +dependencies = [ 170 + + "data-encoding", 171 + + "data-encoding-macro-internal", 172 + +] 173 + + 174 + +[[package]] 175 + +name = "data-encoding-macro-internal" 176 + +version = "0.1.17" 177 + +source = "registry+https://github.com/rust-lang/crates.io-index" 178 + +checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de" 179 + +dependencies = [ 180 + + "data-encoding", 181 + + "syn", 182 + +] 183 + + 184 + +[[package]] 185 + name = "data-url" 186 + version = "0.3.2" 187 + source = "registry+https://github.com/rust-lang/crates.io-index" 188 + @@ -2175,6 +2282,7 @@ 189 + checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" 190 + dependencies = [ 191 + "base16ct", 192 + + "base64ct", 193 + "crypto-bigint", 194 + "digest", 195 + "ff", 196 + @@ -2185,6 +2293,8 @@ 197 + "pkcs8", 198 + "rand_core 0.6.4", 199 + "sec1", 200 + + "serde_json", 201 + + "serdect", 202 + "subtle", 203 + "zeroize", 204 + ] 205 + @@ -2244,6 +2354,18 @@ 206 + checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" 207 + 208 + [[package]] 209 + +name = "enum-as-inner" 210 + +version = "0.6.1" 211 + +source = "registry+https://github.com/rust-lang/crates.io-index" 212 + +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" 213 + +dependencies = [ 214 + + "heck 0.5.0", 215 + + "proc-macro2", 216 + + "quote", 217 + + "syn", 218 + +] 219 + + 220 + +[[package]] 221 + name = "enumflags2" 222 + version = "0.7.12" 223 + source = "registry+https://github.com/rust-lang/crates.io-index" 224 + @@ -2581,12 +2703,21 @@ 225 + 226 + [[package]] 227 + name = "foreign-types" 228 + +version = "0.3.2" 229 + +source = "registry+https://github.com/rust-lang/crates.io-index" 230 + +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 231 + +dependencies = [ 232 + + "foreign-types-shared 0.1.1", 233 + +] 234 + + 235 + +[[package]] 236 + +name = "foreign-types" 237 + version = "0.5.0" 238 + source = "registry+https://github.com/rust-lang/crates.io-index" 239 + checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" 240 + dependencies = [ 241 + "foreign-types-macros", 242 + - "foreign-types-shared", 243 + + "foreign-types-shared 0.3.1", 244 + ] 245 + 246 + [[package]] 247 + @@ -2602,6 +2733,12 @@ 248 + 249 + [[package]] 250 + name = "foreign-types-shared" 251 + +version = "0.1.1" 252 + +source = "registry+https://github.com/rust-lang/crates.io-index" 253 + +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 254 + + 255 + +[[package]] 256 + +name = "foreign-types-shared" 257 + version = "0.3.1" 258 + source = "registry+https://github.com/rust-lang/crates.io-index" 259 + checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" 260 + @@ -2809,8 +2946,10 @@ 261 + checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" 262 + dependencies = [ 263 + "cfg-if", 264 + + "js-sys", 265 + "libc", 266 + "wasi 0.11.1+wasi-snapshot-preview1", 267 + + "wasm-bindgen", 268 + ] 269 + 270 + [[package]] 271 + @@ -2820,9 +2959,11 @@ 272 + checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" 273 + dependencies = [ 274 + "cfg-if", 275 + + "js-sys", 276 + "libc", 277 + "r-efi", 278 + "wasip2", 279 + + "wasm-bindgen", 280 + ] 281 + 282 + [[package]] 283 + @@ -3491,7 +3632,7 @@ 284 + "cc", 285 + "core-graphics", 286 + "core-text", 287 + - "foreign-types", 288 + + "foreign-types 0.5.0", 289 + "freetype-sys", 290 + "pkg-config", 291 + "winapi", 292 + @@ -3618,6 +3759,52 @@ 293 + checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" 294 + 295 + [[package]] 296 + +name = "hickory-proto" 297 + +version = "0.25.2" 298 + +source = "registry+https://github.com/rust-lang/crates.io-index" 299 + +checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" 300 + +dependencies = [ 301 + + "async-trait", 302 + + "cfg-if", 303 + + "data-encoding", 304 + + "enum-as-inner", 305 + + "futures-channel", 306 + + "futures-io", 307 + + "futures-util", 308 + + "idna", 309 + + "ipnet", 310 + + "once_cell", 311 + + "rand 0.9.2", 312 + + "ring", 313 + + "thiserror 2.0.18", 314 + + "tinyvec", 315 + + "tokio", 316 + + "tracing", 317 + + "url", 318 + +] 319 + + 320 + +[[package]] 321 + +name = "hickory-resolver" 322 + +version = "0.25.2" 323 + +source = "registry+https://github.com/rust-lang/crates.io-index" 324 + +checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a" 325 + +dependencies = [ 326 + + "cfg-if", 327 + + "futures-util", 328 + + "hickory-proto", 329 + + "ipconfig", 330 + + "moka", 331 + + "once_cell", 332 + + "parking_lot", 333 + + "rand 0.9.2", 334 + + "resolv-conf", 335 + + "smallvec", 336 + + "thiserror 2.0.18", 337 + + "tokio", 338 + + "tracing", 339 + +] 340 + + 341 + +[[package]] 342 + name = "hilog" 343 + version = "0.2.2" 344 + source = "registry+https://github.com/rust-lang/crates.io-index" 345 + @@ -3833,6 +4020,22 @@ 346 + ] 347 + 348 + [[package]] 349 + +name = "hyper-tls" 350 + +version = "0.6.0" 351 + +source = "registry+https://github.com/rust-lang/crates.io-index" 352 + +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" 353 + +dependencies = [ 354 + + "bytes", 355 + + "http-body-util", 356 + + "hyper 1.8.1", 357 + + "hyper-util", 358 + + "native-tls", 359 + + "tokio", 360 + + "tokio-native-tls", 361 + + "tower-service", 362 + +] 363 + + 364 + +[[package]] 365 + name = "hyper-util" 366 + version = "0.1.20" 367 + source = "registry+https://github.com/rust-lang/crates.io-index" 368 + @@ -3850,9 +4053,11 @@ 369 + "percent-encoding", 370 + "pin-project-lite", 371 + "socket2 0.6.1", 372 + + "system-configuration", 373 + "tokio", 374 + "tower-service", 375 + "tracing", 376 + + "windows-registry", 377 + ] 378 + 379 + [[package]] 380 + @@ -4458,6 +4663,29 @@ 381 + ] 382 + 383 + [[package]] 384 + +name = "ipconfig" 385 + +version = "0.3.2" 386 + +source = "registry+https://github.com/rust-lang/crates.io-index" 387 + +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" 388 + +dependencies = [ 389 + + "socket2 0.5.10", 390 + + "widestring", 391 + + "windows-sys 0.48.0", 392 + + "winreg", 393 + +] 394 + + 395 + +[[package]] 396 + +name = "ipld-core" 397 + +version = "0.4.3" 398 + +source = "registry+https://github.com/rust-lang/crates.io-index" 399 + +checksum = "090f624976d72f0b0bb71b86d58dc16c15e069193067cb3a3a09d655246cbbda" 400 + +dependencies = [ 401 + + "cid", 402 + + "serde", 403 + + "serde_bytes", 404 + +] 405 + + 406 + +[[package]] 407 + name = "ipnet" 408 + version = "2.12.0" 409 + source = "registry+https://github.com/rust-lang/crates.io-index" 410 + @@ -4464,6 +4692,16 @@ 411 + checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" 412 + 413 + [[package]] 414 + +name = "iri-string" 415 + +version = "0.7.11" 416 + +source = "registry+https://github.com/rust-lang/crates.io-index" 417 + +checksum = "d8e7418f59cc01c88316161279a7f665217ae316b388e58a0d10e29f54f1e5eb" 418 + +dependencies = [ 419 + + "memchr", 420 + + "serde", 421 + +] 422 + + 423 + +[[package]] 424 + name = "is-terminal" 425 + version = "0.4.17" 426 + source = "registry+https://github.com/rust-lang/crates.io-index" 427 + @@ -4622,6 +4860,20 @@ 428 + ] 429 + 430 + [[package]] 431 + +name = "k256" 432 + +version = "0.13.4" 433 + +source = "registry+https://github.com/rust-lang/crates.io-index" 434 + +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" 435 + +dependencies = [ 436 + + "cfg-if", 437 + + "ecdsa", 438 + + "elliptic-curve", 439 + + "once_cell", 440 + + "sha2", 441 + + "signature", 442 + +] 443 + + 444 + +[[package]] 445 + name = "keccak" 446 + version = "0.1.6" 447 + source = "registry+https://github.com/rust-lang/crates.io-index" 448 + @@ -4867,6 +5119,21 @@ 449 + ] 450 + 451 + [[package]] 452 + +name = "lru" 453 + +version = "0.12.5" 454 + +source = "registry+https://github.com/rust-lang/crates.io-index" 455 + +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" 456 + +dependencies = [ 457 + + "hashbrown 0.15.5", 458 + +] 459 + + 460 + +[[package]] 461 + +name = "lru-slab" 462 + +version = "0.1.2" 463 + +source = "registry+https://github.com/rust-lang/crates.io-index" 464 + +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" 465 + + 466 + +[[package]] 467 + name = "mach2" 468 + version = "0.6.0" 469 + source = "registry+https://github.com/rust-lang/crates.io-index" 470 + @@ -4912,6 +5179,17 @@ 471 + ] 472 + 473 + [[package]] 474 + +name = "match-lookup" 475 + +version = "0.1.2" 476 + +source = "registry+https://github.com/rust-lang/crates.io-index" 477 + +checksum = "757aee279b8bdbb9f9e676796fd459e4207a1f986e87886700abf589f5abf771" 478 + +dependencies = [ 479 + + "proc-macro2", 480 + + "quote", 481 + + "syn", 482 + +] 483 + + 484 + +[[package]] 485 + name = "matchers" 486 + version = "0.2.0" 487 + source = "registry+https://github.com/rust-lang/crates.io-index" 488 + @@ -4963,7 +5241,7 @@ 489 + "bitflags 2.11.0", 490 + "block", 491 + "core-graphics-types 0.2.0", 492 + - "foreign-types", 493 + + "foreign-types 0.5.0", 494 + "log", 495 + "objc", 496 + "paste", 497 + @@ -5055,6 +5333,23 @@ 498 + ] 499 + 500 + [[package]] 501 + +name = "moka" 502 + +version = "0.12.15" 503 + +source = "registry+https://github.com/rust-lang/crates.io-index" 504 + +checksum = "957228ad12042ee839f93c8f257b62b4c0ab5eaae1d4fa60de53b27c9d7c5046" 505 + +dependencies = [ 506 + + "crossbeam-channel", 507 + + "crossbeam-epoch", 508 + + "crossbeam-utils", 509 + + "equivalent", 510 + + "parking_lot", 511 + + "portable-atomic", 512 + + "smallvec", 513 + + "tagptr", 514 + + "uuid", 515 + +] 516 + + 517 + +[[package]] 518 + name = "mozangle" 519 + version = "0.5.5" 520 + source = "registry+https://github.com/rust-lang/crates.io-index" 521 + @@ -5107,6 +5402,29 @@ 522 + checksum = "956787520e75e9bd233246045d19f42fb73242759cc57fba9611d940ae96d4b0" 523 + 524 + [[package]] 525 + +name = "multibase" 526 + +version = "0.9.2" 527 + +source = "registry+https://github.com/rust-lang/crates.io-index" 528 + +checksum = "8694bb4835f452b0e3bb06dbebb1d6fc5385b6ca1caf2e55fd165c042390ec77" 529 + +dependencies = [ 530 + + "base-x", 531 + + "base256emoji", 532 + + "data-encoding", 533 + + "data-encoding-macro", 534 + +] 535 + + 536 + +[[package]] 537 + +name = "multihash" 538 + +version = "0.19.3" 539 + +source = "registry+https://github.com/rust-lang/crates.io-index" 540 + +checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d" 541 + +dependencies = [ 542 + + "core2", 543 + + "serde", 544 + + "unsigned-varint", 545 + +] 546 + + 547 + +[[package]] 548 + name = "naga" 549 + version = "26.0.0" 550 + source = "registry+https://github.com/rust-lang/crates.io-index" 551 + @@ -5190,6 +5508,23 @@ 552 + ] 553 + 554 + [[package]] 555 + +name = "native-tls" 556 + +version = "0.2.18" 557 + +source = "registry+https://github.com/rust-lang/crates.io-index" 558 + +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" 559 + +dependencies = [ 560 + + "libc", 561 + + "log", 562 + + "openssl", 563 + + "openssl-probe", 564 + + "openssl-sys", 565 + + "schannel", 566 + + "security-framework", 567 + + "security-framework-sys", 568 + + "tempfile", 569 + +] 570 + + 571 + +[[package]] 572 + name = "ndk" 573 + version = "0.9.0" 574 + source = "registry+https://github.com/rust-lang/crates.io-index" 575 + @@ -5896,6 +6231,10 @@ 576 + version = "1.21.4" 577 + source = "registry+https://github.com/rust-lang/crates.io-index" 578 + checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" 579 + +dependencies = [ 580 + + "critical-section", 581 + + "portable-atomic", 582 + +] 583 + 584 + [[package]] 585 + name = "once_cell_polyfill" 586 + @@ -5916,6 +6255,32 @@ 587 + checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" 588 + 589 + [[package]] 590 + +name = "openssl" 591 + +version = "0.10.76" 592 + +source = "registry+https://github.com/rust-lang/crates.io-index" 593 + +checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" 594 + +dependencies = [ 595 + + "bitflags 2.11.0", 596 + + "cfg-if", 597 + + "foreign-types 0.3.2", 598 + + "libc", 599 + + "once_cell", 600 + + "openssl-macros", 601 + + "openssl-sys", 602 + +] 603 + + 604 + +[[package]] 605 + +name = "openssl-macros" 606 + +version = "0.1.1" 607 + +source = "registry+https://github.com/rust-lang/crates.io-index" 608 + +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 609 + +dependencies = [ 610 + + "proc-macro2", 611 + + "quote", 612 + + "syn", 613 + +] 614 + + 615 + +[[package]] 616 + name = "openssl-probe" 617 + version = "0.2.1" 618 + source = "registry+https://github.com/rust-lang/crates.io-index" 619 + @@ -5922,6 +6287,18 @@ 620 + checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" 621 + 622 + [[package]] 623 + +name = "openssl-sys" 624 + +version = "0.9.112" 625 + +source = "registry+https://github.com/rust-lang/crates.io-index" 626 + +checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" 627 + +dependencies = [ 628 + + "cc", 629 + + "libc", 630 + + "pkg-config", 631 + + "vcpkg", 632 + +] 633 + + 634 + +[[package]] 635 + name = "openxr" 636 + version = "0.20.0" 637 + source = "registry+https://github.com/rust-lang/crates.io-index" 638 + @@ -6574,6 +6951,61 @@ 639 + ] 640 + 641 + [[package]] 642 + +name = "quinn" 643 + +version = "0.11.9" 644 + +source = "registry+https://github.com/rust-lang/crates.io-index" 645 + +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" 646 + +dependencies = [ 647 + + "bytes", 648 + + "cfg_aliases", 649 + + "pin-project-lite", 650 + + "quinn-proto", 651 + + "quinn-udp", 652 + + "rustc-hash 2.1.1", 653 + + "rustls", 654 + + "socket2 0.6.1", 655 + + "thiserror 2.0.18", 656 + + "tokio", 657 + + "tracing", 658 + + "web-time", 659 + +] 660 + + 661 + +[[package]] 662 + +name = "quinn-proto" 663 + +version = "0.11.14" 664 + +source = "registry+https://github.com/rust-lang/crates.io-index" 665 + +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" 666 + +dependencies = [ 667 + + "bytes", 668 + + "getrandom 0.3.4", 669 + + "lru-slab", 670 + + "rand 0.9.2", 671 + + "ring", 672 + + "rustc-hash 2.1.1", 673 + + "rustls", 674 + + "rustls-pki-types", 675 + + "slab", 676 + + "thiserror 2.0.18", 677 + + "tinyvec", 678 + + "tracing", 679 + + "web-time", 680 + +] 681 + + 682 + +[[package]] 683 + +name = "quinn-udp" 684 + +version = "0.5.14" 685 + +source = "registry+https://github.com/rust-lang/crates.io-index" 686 + +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" 687 + +dependencies = [ 688 + + "cfg_aliases", 689 + + "libc", 690 + + "once_cell", 691 + + "socket2 0.6.1", 692 + + "tracing", 693 + + "windows-sys 0.60.2", 694 + +] 695 + + 696 + +[[package]] 697 + name = "quote" 698 + version = "1.0.45" 699 + source = "registry+https://github.com/rust-lang/crates.io-index" 700 + @@ -6879,6 +7311,56 @@ 701 + checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" 702 + 703 + [[package]] 704 + +name = "reqwest" 705 + +version = "0.12.28" 706 + +source = "registry+https://github.com/rust-lang/crates.io-index" 707 + +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" 708 + +dependencies = [ 709 + + "base64 0.22.1", 710 + + "bytes", 711 + + "encoding_rs", 712 + + "futures-core", 713 + + "h2 0.4.12", 714 + + "http 1.4.0", 715 + + "http-body 1.0.1", 716 + + "http-body-util", 717 + + "hyper 1.8.1", 718 + + "hyper-rustls", 719 + + "hyper-tls", 720 + + "hyper-util", 721 + + "js-sys", 722 + + "log", 723 + + "mime", 724 + + "native-tls", 725 + + "percent-encoding", 726 + + "pin-project-lite", 727 + + "quinn", 728 + + "rustls", 729 + + "rustls-pki-types", 730 + + "serde", 731 + + "serde_json", 732 + + "serde_urlencoded", 733 + + "sync_wrapper", 734 + + "tokio", 735 + + "tokio-native-tls", 736 + + "tokio-rustls", 737 + + "tower", 738 + + "tower-http", 739 + + "tower-service", 740 + + "url", 741 + + "wasm-bindgen", 742 + + "wasm-bindgen-futures", 743 + + "web-sys", 744 + + "webpki-roots", 745 + +] 746 + + 747 + +[[package]] 748 + +name = "resolv-conf" 749 + +version = "0.7.6" 750 + +source = "registry+https://github.com/rust-lang/crates.io-index" 751 + +checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" 752 + + 753 + +[[package]] 754 + name = "resvg" 755 + version = "0.45.1" 756 + source = "registry+https://github.com/rust-lang/crates.io-index" 757 + @@ -7045,6 +7527,7 @@ 758 + "aws-lc-rs", 759 + "log", 760 + "once_cell", 761 + + "ring", 762 + "rustls-pki-types", 763 + "rustls-webpki", 764 + "subtle", 765 + @@ -7069,6 +7552,7 @@ 766 + source = "registry+https://github.com/rust-lang/crates.io-index" 767 + checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" 768 + dependencies = [ 769 + + "web-time", 770 + "zeroize", 771 + ] 772 + 773 + @@ -7240,6 +7724,7 @@ 774 + "der", 775 + "generic-array", 776 + "pkcs8", 777 + + "serdect", 778 + "subtle", 779 + "zeroize", 780 + ] 781 + @@ -7346,6 +7831,18 @@ 782 + ] 783 + 784 + [[package]] 785 + +name = "serde_ipld_dagcbor" 786 + +version = "0.6.4" 787 + +source = "registry+https://github.com/rust-lang/crates.io-index" 788 + +checksum = "46182f4f08349a02b45c998ba3215d3f9de826246ba02bb9dddfe9a2a2100778" 789 + +dependencies = [ 790 + + "cbor4ii", 791 + + "ipld-core", 792 + + "scopeguard", 793 + + "serde", 794 + +] 795 + + 796 + +[[package]] 797 + name = "serde_json" 798 + version = "1.0.149" 799 + source = "registry+https://github.com/rust-lang/crates.io-index" 800 + @@ -7401,6 +7898,16 @@ 801 + ] 802 + 803 + [[package]] 804 + +name = "serdect" 805 + +version = "0.2.0" 806 + +source = "registry+https://github.com/rust-lang/crates.io-index" 807 + +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" 808 + +dependencies = [ 809 + + "base16ct", 810 + + "serde", 811 + +] 812 + + 813 + +[[package]] 814 + name = "servo" 815 + version = "0.0.6" 816 + dependencies = [ 817 + @@ -7647,6 +8154,7 @@ 818 + "rand 0.9.2", 819 + "rustc-hash 2.1.1", 820 + "serde", 821 + + "serde_json", 822 + "servo-background-hang-monitor", 823 + "servo-background-hang-monitor-api", 824 + "servo-base", 825 + @@ -7673,6 +8181,8 @@ 826 + "servo-webxr-api", 827 + "stylo", 828 + "stylo_traits", 829 + + "sync_wrapper", 830 + + "tokio", 831 + "tracing", 832 + ] 833 + 834 + @@ -8260,9 +8770,11 @@ 835 + name = "servo-net" 836 + version = "0.0.6" 837 + dependencies = [ 838 + + "anyhow", 839 + "async-compression", 840 + "async-recursion", 841 + "async-tungstenite", 842 + + "atproto-identity", 843 + "base64 0.22.1", 844 + "bytes", 845 + "chrono", 846 + @@ -8293,6 +8805,7 @@ 847 + "parking_lot", 848 + "quick_cache", 849 + "regex", 850 + + "reqwest", 851 + "resvg", 852 + "rustc-hash 2.1.1", 853 + "rustls", 854 + @@ -8299,8 +8812,10 @@ 855 + "rustls-pki-types", 856 + "rustls-platform-verifier", 857 + "serde", 858 + + "serde_json", 859 + "servo-base", 860 + "servo-config", 861 + + "servo-constellation-traits", 862 + "servo-devtools-traits", 863 + "servo-embedder-traits", 864 + "servo-hyper-serde", 865 + @@ -8313,6 +8828,7 @@ 866 + "servo-url", 867 + "servo_arc", 868 + "sha2", 869 + + "sync_wrapper", 870 + "time", 871 + "tokio", 872 + "tokio-rustls", 873 + @@ -8348,6 +8864,7 @@ 874 + "rustc-hash 2.1.1", 875 + "rustls-pki-types", 876 + "serde", 877 + + "serde_json", 878 + "servo-base", 879 + "servo-config", 880 + "servo-embedder-traits", 881 + @@ -8358,8 +8875,10 @@ 882 + "servo-profile-traits", 883 + "servo-url", 884 + "servo_arc", 885 + + "sync_wrapper", 886 + "sys-locale", 887 + "tokio", 888 + + "tower", 889 + "url", 890 + "uuid", 891 + "webrender_api", 892 + @@ -8918,6 +9437,7 @@ 893 + "bpaf", 894 + "cc", 895 + "cfg-if", 896 + + "content-security-policy", 897 + "crossbeam-channel", 898 + "dirs", 899 + "dpi", 900 + @@ -8932,6 +9452,7 @@ 901 + "headers 0.4.1", 902 + "hilog", 903 + "hitrace", 904 + + "http 1.4.0", 905 + "image", 906 + "ipc-channel", 907 + "jni 0.21.1", 908 + @@ -8952,11 +9473,13 @@ 909 + "ohos-window-manager-sys", 910 + "raw-window-handle", 911 + "rustls", 912 + + "serde", 913 + "serde_json", 914 + "servo", 915 + "servo-allocator", 916 + "servo-base", 917 + "servo-webdriver-server", 918 + + "servo_arc", 919 + "sig", 920 + "surfman", 921 + "tokio", 922 + @@ -9522,6 +10045,9 @@ 923 + version = "1.0.2" 924 + source = "registry+https://github.com/rust-lang/crates.io-index" 925 + checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 926 + +dependencies = [ 927 + + "futures-core", 928 + +] 929 + 930 + [[package]] 931 + name = "synstructure" 932 + @@ -9556,6 +10082,27 @@ 933 + ] 934 + 935 + [[package]] 936 + +name = "system-configuration" 937 + +version = "0.7.0" 938 + +source = "registry+https://github.com/rust-lang/crates.io-index" 939 + +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" 940 + +dependencies = [ 941 + + "bitflags 2.11.0", 942 + + "core-foundation 0.9.4", 943 + + "system-configuration-sys", 944 + +] 945 + + 946 + +[[package]] 947 + +name = "system-configuration-sys" 948 + +version = "0.6.0" 949 + +source = "registry+https://github.com/rust-lang/crates.io-index" 950 + +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" 951 + +dependencies = [ 952 + + "core-foundation-sys", 953 + + "libc", 954 + +] 955 + + 956 + +[[package]] 957 + name = "system-deps" 958 + version = "6.2.2" 959 + source = "registry+https://github.com/rust-lang/crates.io-index" 960 + @@ -9594,6 +10141,12 @@ 961 + ] 962 + 963 + [[package]] 964 + +name = "tagptr" 965 + +version = "0.2.0" 966 + +source = "registry+https://github.com/rust-lang/crates.io-index" 967 + +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" 968 + + 969 + +[[package]] 970 + name = "tar" 971 + version = "0.4.45" 972 + source = "registry+https://github.com/rust-lang/crates.io-index" 973 + @@ -9901,6 +10454,16 @@ 974 + ] 975 + 976 + [[package]] 977 + +name = "tokio-native-tls" 978 + +version = "0.3.1" 979 + +source = "registry+https://github.com/rust-lang/crates.io-index" 980 + +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 981 + +dependencies = [ 982 + + "native-tls", 983 + + "tokio", 984 + +] 985 + + 986 + +[[package]] 987 + name = "tokio-rustls" 988 + version = "0.26.4" 989 + source = "registry+https://github.com/rust-lang/crates.io-index" 990 + @@ -10022,11 +10585,30 @@ 991 + "futures-util", 992 + "pin-project-lite", 993 + "sync_wrapper", 994 + + "tokio", 995 + "tower-layer", 996 + "tower-service", 997 + ] 998 + 999 + [[package]] 1000 + +name = "tower-http" 1001 + +version = "0.6.8" 1002 + +source = "registry+https://github.com/rust-lang/crates.io-index" 1003 + +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" 1004 + +dependencies = [ 1005 + + "bitflags 2.11.0", 1006 + + "bytes", 1007 + + "futures-util", 1008 + + "http 1.4.0", 1009 + + "http-body 1.0.1", 1010 + + "iri-string", 1011 + + "pin-project-lite", 1012 + + "tower", 1013 + + "tower-layer", 1014 + + "tower-service", 1015 + +] 1016 + + 1017 + +[[package]] 1018 + name = "tower-layer" 1019 + version = "0.3.3" 1020 + source = "registry+https://github.com/rust-lang/crates.io-index" 1021 + @@ -10305,6 +10887,12 @@ 1022 + ] 1023 + 1024 + [[package]] 1025 + +name = "unsigned-varint" 1026 + +version = "0.8.0" 1027 + +source = "registry+https://github.com/rust-lang/crates.io-index" 1028 + +checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" 1029 + + 1030 + +[[package]] 1031 + name = "untrusted" 1032 + version = "0.9.0" 1033 + source = "registry+https://github.com/rust-lang/crates.io-index" 1034 + @@ -10324,6 +10912,12 @@ 1035 + ] 1036 + 1037 + [[package]] 1038 + +name = "urlencoding" 1039 + +version = "2.1.3" 1040 + +source = "registry+https://github.com/rust-lang/crates.io-index" 1041 + +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" 1042 + + 1043 + +[[package]] 1044 + name = "urlpattern" 1045 + version = "0.3.0" 1046 + source = "registry+https://github.com/rust-lang/crates.io-index" 1047 + @@ -11092,6 +11686,12 @@ 1048 + ] 1049 + 1050 + [[package]] 1051 + +name = "widestring" 1052 + +version = "1.2.1" 1053 + +source = "registry+https://github.com/rust-lang/crates.io-index" 1054 + +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" 1055 + + 1056 + +[[package]] 1057 + name = "winapi" 1058 + version = "0.3.9" 1059 + source = "registry+https://github.com/rust-lang/crates.io-index" 1060 + @@ -11313,6 +11913,17 @@ 1061 + ] 1062 + 1063 + [[package]] 1064 + +name = "windows-registry" 1065 + +version = "0.6.1" 1066 + +source = "registry+https://github.com/rust-lang/crates.io-index" 1067 + +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" 1068 + +dependencies = [ 1069 + + "windows-link 0.2.1", 1070 + + "windows-result 0.4.1", 1071 + + "windows-strings 0.5.1", 1072 + +] 1073 + + 1074 + +[[package]] 1075 + name = "windows-result" 1076 + version = "0.2.0" 1077 + source = "registry+https://github.com/rust-lang/crates.io-index" 1078 + @@ -11378,6 +11989,15 @@ 1079 + 1080 + [[package]] 1081 + name = "windows-sys" 1082 + +version = "0.48.0" 1083 + +source = "registry+https://github.com/rust-lang/crates.io-index" 1084 + +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 1085 + +dependencies = [ 1086 + + "windows-targets 0.48.5", 1087 + +] 1088 + + 1089 + +[[package]] 1090 + +name = "windows-sys" 1091 + version = "0.52.0" 1092 + source = "registry+https://github.com/rust-lang/crates.io-index" 1093 + checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1094 + @@ -11429,6 +12049,21 @@ 1095 + 1096 + [[package]] 1097 + name = "windows-targets" 1098 + +version = "0.48.5" 1099 + +source = "registry+https://github.com/rust-lang/crates.io-index" 1100 + +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 1101 + +dependencies = [ 1102 + + "windows_aarch64_gnullvm 0.48.5", 1103 + + "windows_aarch64_msvc 0.48.5", 1104 + + "windows_i686_gnu 0.48.5", 1105 + + "windows_i686_msvc 0.48.5", 1106 + + "windows_x86_64_gnu 0.48.5", 1107 + + "windows_x86_64_gnullvm 0.48.5", 1108 + + "windows_x86_64_msvc 0.48.5", 1109 + +] 1110 + + 1111 + +[[package]] 1112 + +name = "windows-targets" 1113 + version = "0.52.6" 1114 + source = "registry+https://github.com/rust-lang/crates.io-index" 1115 + checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1116 + @@ -11486,6 +12121,12 @@ 1117 + 1118 + [[package]] 1119 + name = "windows_aarch64_gnullvm" 1120 + +version = "0.48.5" 1121 + +source = "registry+https://github.com/rust-lang/crates.io-index" 1122 + +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 1123 + + 1124 + +[[package]] 1125 + +name = "windows_aarch64_gnullvm" 1126 + version = "0.52.6" 1127 + source = "registry+https://github.com/rust-lang/crates.io-index" 1128 + checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1129 + @@ -11504,6 +12145,12 @@ 1130 + 1131 + [[package]] 1132 + name = "windows_aarch64_msvc" 1133 + +version = "0.48.5" 1134 + +source = "registry+https://github.com/rust-lang/crates.io-index" 1135 + +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 1136 + + 1137 + +[[package]] 1138 + +name = "windows_aarch64_msvc" 1139 + version = "0.52.6" 1140 + source = "registry+https://github.com/rust-lang/crates.io-index" 1141 + checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1142 + @@ -11522,6 +12169,12 @@ 1143 + 1144 + [[package]] 1145 + name = "windows_i686_gnu" 1146 + +version = "0.48.5" 1147 + +source = "registry+https://github.com/rust-lang/crates.io-index" 1148 + +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 1149 + + 1150 + +[[package]] 1151 + +name = "windows_i686_gnu" 1152 + version = "0.52.6" 1153 + source = "registry+https://github.com/rust-lang/crates.io-index" 1154 + checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1155 + @@ -11552,6 +12205,12 @@ 1156 + 1157 + [[package]] 1158 + name = "windows_i686_msvc" 1159 + +version = "0.48.5" 1160 + +source = "registry+https://github.com/rust-lang/crates.io-index" 1161 + +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 1162 + + 1163 + +[[package]] 1164 + +name = "windows_i686_msvc" 1165 + version = "0.52.6" 1166 + source = "registry+https://github.com/rust-lang/crates.io-index" 1167 + checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1168 + @@ -11570,6 +12229,12 @@ 1169 + 1170 + [[package]] 1171 + name = "windows_x86_64_gnu" 1172 + +version = "0.48.5" 1173 + +source = "registry+https://github.com/rust-lang/crates.io-index" 1174 + +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 1175 + + 1176 + +[[package]] 1177 + +name = "windows_x86_64_gnu" 1178 + version = "0.52.6" 1179 + source = "registry+https://github.com/rust-lang/crates.io-index" 1180 + checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1181 + @@ -11588,6 +12253,12 @@ 1182 + 1183 + [[package]] 1184 + name = "windows_x86_64_gnullvm" 1185 + +version = "0.48.5" 1186 + +source = "registry+https://github.com/rust-lang/crates.io-index" 1187 + +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 1188 + + 1189 + +[[package]] 1190 + +name = "windows_x86_64_gnullvm" 1191 + version = "0.52.6" 1192 + source = "registry+https://github.com/rust-lang/crates.io-index" 1193 + checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1194 + @@ -11606,6 +12277,12 @@ 1195 + 1196 + [[package]] 1197 + name = "windows_x86_64_msvc" 1198 + +version = "0.48.5" 1199 + +source = "registry+https://github.com/rust-lang/crates.io-index" 1200 + +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 1201 + + 1202 + +[[package]] 1203 + +name = "windows_x86_64_msvc" 1204 + version = "0.52.6" 1205 + source = "registry+https://github.com/rust-lang/crates.io-index" 1206 + checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1207 + @@ -11678,6 +12355,16 @@ 1208 + ] 1209 + 1210 + [[package]] 1211 + +name = "winreg" 1212 + +version = "0.50.0" 1213 + +source = "registry+https://github.com/rust-lang/crates.io-index" 1214 + +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" 1215 + +dependencies = [ 1216 + + "cfg-if", 1217 + + "windows-sys 0.48.0", 1218 + +] 1219 + + 1220 + +[[package]] 1221 + name = "winresource" 1222 + version = "0.1.23" 1223 + source = "registry+https://github.com/rust-lang/crates.io-index"
+2 -1
patches/components/constellation/Cargo.toml.patch
··· 32 32 servo-background-hang-monitor = { workspace = true } 33 33 servo-background-hang-monitor-api = { workspace = true } 34 34 servo-base = { workspace = true } 35 - @@ -60,6 +66,7 @@ 35 + @@ -60,6 +66,8 @@ 36 36 storage_traits = { workspace = true } 37 37 stylo = { workspace = true } 38 38 stylo_traits = { workspace = true } 39 + +sync_wrapper = "1.0" 39 40 +tokio = { workspace = true } 40 41 tracing = { workspace = true, optional = true } 41 42 webgpu = { workspace = true }
+207
patches/components/constellation/atproto.rs.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -0,0 +1,204 @@ 4 + +// SPDX-License-Identifier: AGPL-3.0-or-later 5 + + 6 + +use std::sync::Arc; 7 + + 8 + +use constellation_traits::{AtProtoRequest, AtProtoResult}; 9 + +use ipc_channel::ipc::channel; 10 + +use log::error; 11 + +use net::async_runtime::spawn_task; 12 + +use net::atproto::session::SessionClient; 13 + +use net_traits::{AtProtoSessionState, CoreResourceMsg, CoreResourceThread}; 14 + +use parking_lot::Mutex; 15 + +use servo_base::generic_channel::GenericCallback; 16 + +use servo_url::ServoUrl; 17 + +use sync_wrapper::SyncWrapper; 18 + + 19 + +pub(crate) struct AtProtoManager { 20 + + resource_thread: CoreResourceThread, 21 + + session: Arc<Mutex<Option<AtProtoSessionState>>>, 22 + +} 23 + + 24 + +impl AtProtoManager { 25 + + pub(crate) fn new(resource_thread: CoreResourceThread) -> Self { 26 + + let (tx, rx) = channel().expect("Failed to create IPC channel"); 27 + + let _ = resource_thread.send(CoreResourceMsg::GetAtProtoSession(tx)); 28 + + let session = rx.recv().unwrap_or(None); 29 + + 30 + + println!("ATProto session: {session:?}"); 31 + + 32 + + Self { 33 + + resource_thread, 34 + + session: Arc::new(Mutex::new(session)), 35 + + } 36 + + } 37 + + 38 + + pub(crate) fn process_request( 39 + + &self, 40 + + request: AtProtoRequest, 41 + + response: GenericCallback<AtProtoResult>, 42 + + ) { 43 + + match request { 44 + + AtProtoRequest::Login(login, password) => self.login(login, password, response), 45 + + AtProtoRequest::Logout => self.logout(response), 46 + + AtProtoRequest::Current => self.current(response), 47 + + } 48 + + } 49 + + 50 + + fn login(&self, handle: String, password: String, response: GenericCallback<AtProtoResult>) { 51 + + let resource_thread = SyncWrapper::new(self.resource_thread.clone()); 52 + + let resource_thread2 = SyncWrapper::new(self.resource_thread.clone()); 53 + + let local_session = Arc::clone(&self.session); 54 + + 55 + + spawn_task(async move { 56 + + let result = SessionClient::create(&handle, &password, resource_thread).await; 57 + + 58 + + if let AtProtoResult::NewSession(ref session, ref endpoint_url) = result { 59 + + Self::update_session_state( 60 + + endpoint_url, 61 + + &session.access_jwt, 62 + + &session.refresh_jwt, 63 + + &resource_thread2.into_inner(), 64 + + local_session, 65 + + ); 66 + + } 67 + + if let Err(err) = response.send(result) { 68 + + error!("Failed to send new session: {err:?}"); 69 + + } 70 + + }); 71 + + } 72 + + 73 + + fn logout(&self, response: GenericCallback<AtProtoResult>) { 74 + + let Some(ref session) = *self.session.lock() else { 75 + + error!("No session available"); 76 + + let _ = response.send(AtProtoResult::Error); 77 + + return; 78 + + }; 79 + + 80 + + let resource_thread = SyncWrapper::new(self.resource_thread.clone()); 81 + + let resource_thread2 = SyncWrapper::new(self.resource_thread.clone()); 82 + + let resource_thread3 = SyncWrapper::new(self.resource_thread.clone()); 83 + + let resource_thread4 = SyncWrapper::new(self.resource_thread.clone()); 84 + + let resource_thread5 = SyncWrapper::new(self.resource_thread.clone()); 85 + + 86 + + let session = session.clone(); 87 + + let local_session = Arc::clone(&self.session); 88 + + let local_session2 = Arc::clone(&self.session); 89 + + 90 + + spawn_task(async move { 91 + + let mut result = 92 + + SessionClient::delete(&session.endpoint, resource_thread, &session.refresh_jwt) 93 + + .await; 94 + + if let AtProtoResult::RefreshRequired = result { 95 + + error!("RefreshRequired"); 96 + + if let Some(refresh_session) = SessionClient::refresh( 97 + + &session.endpoint, 98 + + resource_thread2, 99 + + &session.refresh_jwt, 100 + + ) 101 + + .await 102 + + { 103 + + Self::update_session_state( 104 + + &session.endpoint, 105 + + &refresh_session.access_jwt, 106 + + &refresh_session.refresh_jwt, 107 + + &resource_thread3.into_inner(), 108 + + local_session, 109 + + ); 110 + + 111 + + result = SessionClient::delete( 112 + + &session.endpoint, 113 + + resource_thread4, 114 + + &session.refresh_jwt, 115 + + ) 116 + + .await; 117 + + } else { 118 + + result = AtProtoResult::Error; 119 + + } 120 + + } 121 + + 122 + + // Reset the ATProto session to an empty one. 123 + + match result { 124 + + AtProtoResult::Error => {}, 125 + + _ => { 126 + + let mut lock = local_session2.lock(); 127 + + *lock = None; 128 + + let _ = resource_thread5 129 + + .into_inner() 130 + + .send(CoreResourceMsg::UpdateAtProtoSession(None)); 131 + + }, 132 + + } 133 + + 134 + + // Finally send the result. 135 + + if let Err(err) = response.send(result) { 136 + + error!("Failed to send current session: {err:?}"); 137 + + } 138 + + }); 139 + + } 140 + + 141 + + fn current(&self, response: GenericCallback<AtProtoResult>) { 142 + + let Some(ref session) = *self.session.lock() else { 143 + + error!("No session available"); 144 + + let _ = response.send(AtProtoResult::Error); 145 + + return; 146 + + }; 147 + + 148 + + let resource_thread = SyncWrapper::new(self.resource_thread.clone()); 149 + + let resource_thread2 = SyncWrapper::new(self.resource_thread.clone()); 150 + + let resource_thread3 = SyncWrapper::new(self.resource_thread.clone()); 151 + + let resource_thread4 = SyncWrapper::new(self.resource_thread.clone()); 152 + + 153 + + let session = session.clone(); 154 + + let local_session = Arc::clone(&self.session); 155 + + 156 + + spawn_task(async move { 157 + + let mut result = SessionClient::current(&session.endpoint, resource_thread).await; 158 + + if let AtProtoResult::RefreshRequired = result { 159 + + error!("RefreshRequired"); 160 + + if let Some(refresh_session) = SessionClient::refresh( 161 + + &session.endpoint, 162 + + resource_thread2, 163 + + &session.refresh_jwt, 164 + + ) 165 + + .await 166 + + { 167 + + Self::update_session_state( 168 + + &session.endpoint, 169 + + &refresh_session.access_jwt, 170 + + &refresh_session.refresh_jwt, 171 + + &resource_thread3.into_inner(), 172 + + local_session, 173 + + ); 174 + + 175 + + result = SessionClient::current(&session.endpoint, resource_thread4).await; 176 + + } else { 177 + + result = AtProtoResult::Error; 178 + + } 179 + + } 180 + + 181 + + if let Err(err) = response.send(result) { 182 + + error!("Failed to send current session: {err:?}"); 183 + + } 184 + + }); 185 + + } 186 + + 187 + + // Register the new session state. 188 + + fn update_session_state( 189 + + endpoint: &ServoUrl, 190 + + access_jwt: &str, 191 + + refresh_jwt: &str, 192 + + resource_thread: &CoreResourceThread, 193 + + local_session: Arc<Mutex<Option<AtProtoSessionState>>>, 194 + + ) { 195 + + // Update the HTTP state. 196 + + let session_msg = AtProtoSessionState { 197 + + endpoint: endpoint.clone(), 198 + + access_jwt: access_jwt.to_owned(), 199 + + refresh_jwt: refresh_jwt.to_owned(), 200 + + }; 201 + + 202 + + let mut lock = local_session.lock(); 203 + + *lock = Some(session_msg.clone()); 204 + + 205 + + let _ = resource_thread.send(CoreResourceMsg::UpdateAtProtoSession(Some(session_msg))); 206 + + } 207 + +}
+82 -34
patches/components/constellation/constellation.rs.patch
··· 1 1 --- original 2 2 +++ modified 3 - @@ -104,6 +104,7 @@ 3 + @@ -90,6 +90,7 @@ 4 + use std::collections::{HashMap, HashSet, VecDeque}; 5 + use std::marker::PhantomData; 6 + use std::mem::replace; 7 + +use std::path::PathBuf; 8 + use std::rc::{Rc, Weak}; 9 + use std::sync::Arc; 10 + use std::thread::JoinHandle; 11 + @@ -104,6 +105,7 @@ 4 12 use canvas_traits::webgl::WebGLThreads; 5 13 use constellation_traits::{ 6 14 AuxiliaryWebViewCreationRequest, AuxiliaryWebViewCreationResponse, DocumentState, ··· 8 16 EmbedderToConstellationMessage, IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSizeMsg, Job, 9 17 LoadData, LogEntry, MessagePortMsg, NavigationHistoryBehavior, PaintMetricEvent, 10 18 PortMessageTask, PortTransferInfo, SWManagerMsg, SWManagerSenders, ScreenshotReadinessResponse, 11 - @@ -119,12 +120,12 @@ 19 + @@ -119,12 +121,12 @@ 12 20 use embedder_traits::resources::{self, Resource}; 13 21 use embedder_traits::user_contents::{UserContentManagerId, UserContents}; 14 22 use embedder_traits::{ ··· 27 35 }; 28 36 use euclid::Size2D; 29 37 use euclid::default::Size2D as UntypedSize2D; 30 - @@ -183,6 +184,7 @@ 38 + @@ -176,6 +178,7 @@ 39 + #[cfg(feature = "webgpu")] 40 + use webgpu_traits::{WebGPU, WebGPURequest}; 41 + 42 + +use crate::atproto::AtProtoManager; 43 + use crate::broadcastchannel::BroadcastChannels; 44 + use crate::browsingcontext::{ 45 + AllBrowsingContextsIterator, BrowsingContext, FullyActiveBrowsingContextsIterator, 46 + @@ -183,6 +186,7 @@ 31 47 }; 32 48 use crate::constellation_webview::ConstellationWebView; 33 49 use crate::event_loop::EventLoop; ··· 35 51 use crate::pipeline::Pipeline; 36 52 use crate::process_manager::ProcessManager; 37 53 use crate::serviceworker::ServiceWorkerUnprivilegedContent; 38 - @@ -211,6 +213,9 @@ 54 + @@ -211,6 +215,9 @@ 39 55 /// While a completion failed, another global requested to complete the transfer. 40 56 /// We are still buffering messages, and awaiting the return of the buffer from the global who failed. 41 57 CompletionRequested(MessagePortRouterId, VecDeque<PortMessageTask>), ··· 45 61 } 46 62 47 63 #[derive(Debug)] 48 - @@ -507,6 +512,22 @@ 64 + @@ -507,6 +514,25 @@ 49 65 /// to the `UserContents` need to be forwared to all the `ScriptThread`s that host 50 66 /// the relevant `WebView`. 51 67 pub(crate) user_contents_for_manager_id: FxHashMap<UserContentManagerId, UserContents>, ··· 65 81 + 66 82 + /// The P2P pairing service. 67 83 + pairing: PairingService, 84 + + 85 + + /// The main process side of the ATProdo DOM API. 86 + + at_proto: AtProtoManager, 68 87 } 69 88 70 89 /// State needed to construct a constellation. 71 - @@ -725,6 +746,10 @@ 90 + @@ -562,6 +588,9 @@ 91 + 92 + /// The async runtime. 93 + pub async_runtime: Box<dyn AsyncRuntime>, 94 + + 95 + + /// The configuration directory used for storage. 96 + + pub config_dir: Option<PathBuf>, 97 + } 98 + 99 + /// When we are exiting a pipeline, we can either force exiting or not. A normal exit 100 + @@ -671,7 +700,7 @@ 101 + script_to_devtools_callback: Default::default(), 102 + #[cfg(feature = "bluetooth")] 103 + bluetooth_ipc_sender: state.bluetooth_thread, 104 + - public_resource_threads: state.public_resource_threads, 105 + + public_resource_threads: state.public_resource_threads.clone(), 106 + private_resource_threads: state.private_resource_threads, 107 + public_storage_threads: state.public_storage_threads, 108 + private_storage_threads: state.private_storage_threads, 109 + @@ -725,6 +754,13 @@ 72 110 pending_viewport_changes: Default::default(), 73 111 screenshot_readiness_requests: Vec::new(), 74 112 user_contents_for_manager_id: Default::default(), ··· 76 114 + active_ime_webview: None, 77 115 + embedder_error_listeners: Default::default(), 78 116 + pairing: PairingService::new(), 117 + + at_proto: AtProtoManager::new( 118 + + state.public_resource_threads.core_thread, 119 + + ), 79 120 }; 80 121 81 122 constellation.run(); 82 - @@ -750,6 +775,18 @@ 123 + @@ -750,6 +786,18 @@ 83 124 fn clean_up_finished_script_event_loops(&mut self) { 84 125 self.event_loop_join_handles 85 126 .retain(|join_handle| !join_handle.is_finished()); ··· 98 139 self.event_loops 99 140 .retain(|event_loop| event_loop.upgrade().is_some()); 100 141 } 101 - @@ -1041,6 +1078,11 @@ 142 + @@ -1041,6 +1089,11 @@ 102 143 .get(&webview_id) 103 144 .and_then(|webview| webview.user_content_manager_id); 104 145 ··· 110 151 let new_pipeline_info = NewPipelineInfo { 111 152 parent_info: parent_pipeline_id, 112 153 new_pipeline_id, 113 - @@ -1051,6 +1093,13 @@ 154 + @@ -1051,6 +1104,13 @@ 114 155 viewport_details: initial_viewport_details, 115 156 user_content_manager_id, 116 157 theme, ··· 124 165 }; 125 166 let pipeline = match Pipeline::spawn(new_pipeline_info, event_loop, self, throttled) { 126 167 Ok(pipeline) => pipeline, 127 - @@ -1217,6 +1266,7 @@ 168 + @@ -1217,6 +1277,7 @@ 128 169 BackgroundHangMonitor(HangMonitorAlert), 129 170 Embedder(EmbedderToConstellationMessage), 130 171 FromSWManager(SWManagerMsg), ··· 132 173 RemoveProcess(usize), 133 174 } 134 175 // Get one incoming request. 135 - @@ -1237,6 +1287,15 @@ 176 + @@ -1237,6 +1298,15 @@ 136 177 sel.recv(&self.embedder_to_constellation_receiver); 137 178 sel.recv(&self.swmanager_receiver); 138 179 ··· 148 189 self.process_manager.register(&mut sel); 149 190 150 191 let request = { 151 - @@ -1265,9 +1324,13 @@ 192 + @@ -1265,9 +1335,13 @@ 152 193 .recv(&self.swmanager_receiver) 153 194 .expect("Unexpected SW channel panic in constellation") 154 195 .map(Request::FromSWManager), ··· 163 204 let _ = oper.recv(self.process_manager.receiver_at(process_index)); 164 205 Ok(Request::RemoveProcess(process_index)) 165 206 }, 166 - @@ -1293,6 +1356,9 @@ 207 + @@ -1293,6 +1367,9 @@ 167 208 Request::FromSWManager(message) => { 168 209 self.handle_request_from_swmanager(message); 169 210 }, ··· 173 214 Request::RemoveProcess(index) => self.process_manager.remove(index), 174 215 } 175 216 } 176 - @@ -1522,11 +1588,7 @@ 217 + @@ -1522,11 +1599,7 @@ 177 218 } 178 219 }, 179 220 EmbedderToConstellationMessage::PreferencesUpdated(updates) => { ··· 186 227 let _ = event_loop.send(ScriptThreadMessage::PreferencesUpdated( 187 228 updates 188 229 .iter() 189 - @@ -1553,6 +1615,18 @@ 230 + @@ -1553,6 +1626,18 @@ 190 231 EmbedderToConstellationMessage::SetAccessibilityActive(webview_id, active) => { 191 232 self.set_accessibility_active(webview_id, active); 192 233 }, ··· 205 246 } 206 247 } 207 248 208 - @@ -1750,7 +1824,13 @@ 249 + @@ -1750,7 +1835,13 @@ 209 250 return warn!("Attempt to add channel name from an unexpected origin."); 210 251 } 211 252 self.broadcast_channels ··· 220 261 }, 221 262 ScriptToConstellationMessage::RemoveBroadcastChannelNameInRouter( 222 263 router_id, 223 - @@ -1764,7 +1844,13 @@ 264 + @@ -1764,7 +1855,13 @@ 224 265 return warn!("Attempt to remove channel name from an unexpected origin."); 225 266 } 226 267 self.broadcast_channels ··· 235 276 }, 236 277 ScriptToConstellationMessage::RemoveBroadcastChannelRouter(router_id, origin) => { 237 278 if self 238 - @@ -1776,6 +1862,12 @@ 279 + @@ -1776,6 +1873,12 @@ 239 280 self.broadcast_channels 240 281 .remove_broadcast_channel_router(router_id); 241 282 }, ··· 248 289 ScriptToConstellationMessage::ScheduleBroadcast(router_id, message) => { 249 290 if self 250 291 .check_origin_against_pipeline(&source_pipeline_id, &message.origin) 251 - @@ -1785,8 +1877,15 @@ 292 + @@ -1785,8 +1888,15 @@ 252 293 "Attempt to schedule broadcast from an origin not matching the origin of the msg." 253 294 ); 254 295 } ··· 265 306 }, 266 307 ScriptToConstellationMessage::PipelineExited => { 267 308 self.handle_pipeline_exited(source_pipeline_id); 268 - @@ -1806,6 +1905,12 @@ 309 + @@ -1806,6 +1916,12 @@ 269 310 ScriptToConstellationMessage::CreateAuxiliaryWebView(load_info) => { 270 311 self.handle_script_new_auxiliary(load_info); 271 312 }, ··· 278 319 ScriptToConstellationMessage::ChangeRunningAnimationsState(animation_state) => { 279 320 self.handle_change_running_animations_state(source_pipeline_id, animation_state) 280 321 }, 281 - @@ -1972,6 +2077,29 @@ 322 + @@ -1972,6 +2088,29 @@ 282 323 new_value, 283 324 ); 284 325 }, ··· 308 349 ScriptToConstellationMessage::MediaSessionEvent(pipeline_id, event) => { 309 350 // Unlikely at this point, but we may receive events coming from 310 351 // different media sessions, so we set the active media session based 311 - @@ -1991,7 +2119,12 @@ 352 + @@ -1991,7 +2130,12 @@ 312 353 } 313 354 self.active_media_session = Some(pipeline_id); 314 355 self.embedder_proxy ··· 322 363 }, 323 364 #[cfg(feature = "webgpu")] 324 365 ScriptToConstellationMessage::RequestAdapter(response_sender, options, ids) => self 325 - @@ -2045,6 +2178,407 @@ 366 + @@ -2045,9 +2189,414 @@ 326 367 let _ = event_loop.send(ScriptThreadMessage::TriggerGarbageCollection); 327 368 } 328 369 }, ··· 553 594 + .send_message(&from_peer, &P2pMessage::PortOfferDenied { stream_id }); 554 595 + } 555 596 + }, 556 - + } 557 - + } 558 597 + 598 + + ScriptToConstellationMessage::AtProto(request, response) => { 599 + + self.at_proto.process_request(request, response); 600 + + }, 601 + } 602 + } 603 + 559 604 + fn handle_pairing_event(&mut self, event: constellation_traits::PairingEvent) { 560 605 + if let constellation_traits::PairingEvent::MessageReceived { ref from, ref data } = event { 561 606 + debug!("P2P message received from {from}, {} bytes", data.len()); ··· 727 772 + if self.embedder_error_listeners.contains(&event_loop.id()) { 728 773 + let _ = event_loop.send(ScriptThreadMessage::DispatchPairingEvent(event.clone())); 729 774 + } 730 - } 731 - } 732 - 733 - @@ -2364,6 +2898,29 @@ 775 + + } 776 + + } 777 + + 778 + /// Check the origin of a message against that of the pipeline it came from. 779 + /// Note: this is still limited as a security check, 780 + /// see <https://github.com/servo/servo/issues/11722> 781 + @@ -2364,6 +2913,29 @@ 734 782 TransferState::TransferInProgress(queue) => queue.push_back(task), 735 783 TransferState::CompletionFailed(queue) => queue.push_back(task), 736 784 TransferState::CompletionRequested(_, queue) => queue.push_back(task), ··· 760 808 } 761 809 } 762 810 763 - @@ -3243,6 +3800,13 @@ 811 + @@ -3243,6 +3815,13 @@ 764 812 /// <https://html.spec.whatwg.org/multipage/#destroy-a-top-level-traversable> 765 813 fn handle_close_top_level_browsing_context(&mut self, webview_id: WebViewId) { 766 814 debug!("{webview_id}: Closing"); ··· 774 822 let browsing_context_id = BrowsingContextId::from(webview_id); 775 823 // Step 5. Remove traversable from the user agent's top-level traversable set. 776 824 let browsing_context = 777 - @@ -3519,8 +4083,27 @@ 825 + @@ -3519,8 +4098,27 @@ 778 826 opener_webview_id, 779 827 opener_pipeline_id, 780 828 response_sender, ··· 802 850 let Some((webview_id_sender, webview_id_receiver)) = generic_channel::channel() else { 803 851 warn!("Failed to create channel"); 804 852 let _ = response_sender.send(None); 805 - @@ -3619,6 +4202,361 @@ 853 + @@ -3619,6 +4217,361 @@ 806 854 }); 807 855 } 808 856 ··· 1164 1212 #[servo_tracing::instrument(skip_all)] 1165 1213 fn handle_refresh_cursor(&self, pipeline_id: PipelineId) { 1166 1214 let Some(pipeline) = self.pipelines.get(&pipeline_id) else { 1167 - @@ -4744,7 +5682,7 @@ 1215 + @@ -4744,7 +5697,7 @@ 1168 1216 } 1169 1217 1170 1218 #[servo_tracing::instrument(skip_all)] ··· 1173 1221 // Send a flat projection of the history to embedder. 1174 1222 // The final vector is a concatenation of the URLs of the past 1175 1223 // entries, the current entry and the future entries. 1176 - @@ -4847,9 +5785,23 @@ 1224 + @@ -4847,9 +5800,23 @@ 1177 1225 ); 1178 1226 self.embedder_proxy.send(EmbedderMsg::HistoryChanged( 1179 1227 webview_id,
+9 -1
patches/components/constellation/lib.rs.patch
··· 1 1 --- original 2 2 +++ modified 3 - @@ -13,6 +13,7 @@ 3 + @@ -7,6 +7,7 @@ 4 + #[macro_use] 5 + mod tracing; 6 + 7 + +mod atproto; 8 + mod broadcastchannel; 9 + mod browsingcontext; 10 + mod constellation; 11 + @@ -13,6 +14,7 @@ 4 12 mod constellation_webview; 5 13 mod event_loop; 6 14 mod logging;
+2 -1
patches/components/constellation/tracing.rs.patch
··· 36 36 Self::ActivateDocument => target!("ActivateDocument"), 37 37 Self::SetDocumentState(..) => target!("SetDocumentState"), 38 38 Self::SetFinalUrl(..) => target!("SetFinalUrl"), 39 - @@ -186,6 +194,49 @@ 39 + @@ -186,6 +194,50 @@ 40 40 target!("RespondToScreenshotReadinessRequest") 41 41 }, 42 42 Self::TriggerGarbageCollection => target!("TriggerGarbageCollection"), ··· 83 83 + Self::PairingRemovePeer(..) => target!("PairingRemovePeer"), 84 84 + Self::CreatePeerStream(..) => target!("CreatePeerStream"), 85 85 + Self::PeerStreamResponse(..) => target!("PeerStreamResponse"), 86 + + Self::AtProto(..) => target!("AtProto"), 86 87 } 87 88 } 88 89 }
+40
patches/components/net/Cargo.toml.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -20,12 +20,15 @@ 4 + test-util = ["hyper-util/server-graceful"] 5 + 6 + [dependencies] 7 + +anyhow = "1.0" 8 + async-compression = { version = "0.4.12", default-features = false, features = ["brotli", "gzip", "tokio", "zlib", "zstd"] } 9 + async-recursion = "1.1" 10 + async-tungstenite = { workspace = true } 11 + +atproto-identity = "0.13" 12 + base64 = { workspace = true } 13 + bytes = "1" 14 + chrono = { workspace = true } 15 + +constellation_traits = { workspace = true } 16 + content-security-policy = { workspace = true } 17 + cookie = { workspace = true } 18 + crossbeam-channel = { workspace = true } 19 + @@ -60,6 +63,7 @@ 20 + profile_traits = { workspace = true } 21 + quick_cache = { version = "0.6.18", default-features = false, features = ["ahash"] } 22 + regex = { workspace = true } 23 + +reqwest = "0.12" 24 + resvg = { workspace = true } 25 + rustc-hash = { workspace = true } 26 + rustls = { workspace = true, features = ["aws-lc-rs"] } 27 + @@ -66,11 +70,13 @@ 28 + rustls-pki-types = { workspace = true } 29 + rustls-platform-verifier = { workspace = true } 30 + serde = { workspace = true } 31 + +serde_json = { workspace = true } 32 + servo_arc = { workspace = true } 33 + servo-base = { workspace = true } 34 + servo-config = { workspace = true } 35 + servo-url = { workspace = true } 36 + sha2 = { workspace = true } 37 + +sync_wrapper = "1.0" 38 + time = { workspace = true } 39 + tokio = { workspace = true, features = ["macros", "rt-multi-thread", "sync"] } 40 + tokio-rustls = { workspace = true }
+69
patches/components/net/atproto/pds.rs.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -0,0 +1,66 @@ 4 + +// SPDX-License-Identifier: AGPL-3.0-or-later 5 + + 6 + +//! Helpers to find a handle's pds. 7 + + 8 + +use std::num::NonZeroUsize; 9 + +use std::sync::Arc; 10 + + 11 + +use anyhow::{Result, anyhow}; 12 + +use atproto_identity::model::Document; 13 + +use atproto_identity::resolve::{ 14 + + HickoryDnsResolver, InnerIdentityResolver, SharedIdentityResolver, resolve_subject, 15 + +}; 16 + +use atproto_identity::storage::DidDocumentStorage; 17 + +use atproto_identity::storage_lru::LruDidDocumentStorage; 18 + +use servo_url::ServoUrl; 19 + + 20 + +static PLC_HOSTNAME: &str = "plc.directory"; 21 + + 22 + +async fn fetch_did_document( 23 + + document_storage: &LruDidDocumentStorage, 24 + + did: &str, 25 + + dns_resolver: Arc<HickoryDnsResolver>, 26 + +) -> Result<Document> { 27 + + if let Ok(Some(document)) = document_storage.get_document_by_did(did).await { 28 + + return Ok(document); 29 + + } 30 + + 31 + + // Get the document and store it. 32 + + let client_builder = reqwest::Client::builder(); 33 + + let http_client = client_builder.build().unwrap(); 34 + + let inner_resolver = InnerIdentityResolver { 35 + + dns_resolver, 36 + + http_client, 37 + + plc_hostname: PLC_HOSTNAME.to_owned(), 38 + + }; 39 + + let resolver = SharedIdentityResolver(Arc::new(inner_resolver)); 40 + + let document = resolver.resolve(did).await?; 41 + + document_storage.store_document(document.clone()).await?; 42 + + Ok(document) 43 + +} 44 + + 45 + +pub async fn get_endpoint_for_subject( 46 + + subject: &str, 47 + + document_storage: Option<LruDidDocumentStorage>, 48 + + dns_resolver: Option<Arc<HickoryDnsResolver>>, 49 + +) -> Result<(ServoUrl, Document)> { 50 + + let dns_resolver = dns_resolver 51 + + .unwrap_or_else(|| Arc::new(HickoryDnsResolver::create_resolver(Default::default()))); 52 + + let document_storage = document_storage 53 + + .unwrap_or_else(|| LruDidDocumentStorage::new(NonZeroUsize::new(1000).unwrap())); 54 + + 55 + + let client_builder = reqwest::Client::builder(); 56 + + let http_client = client_builder.build().unwrap(); 57 + + let subject = resolve_subject(&http_client, &*dns_resolver, subject).await?; 58 + + 59 + + let document = fetch_did_document(&document_storage, &subject, dns_resolver).await?; 60 + + 61 + + let endpoints = document.pds_endpoints(); 62 + + let endpoint = if !endpoints.is_empty() { 63 + + endpoints[0] 64 + + } else { 65 + + return Err(anyhow!("No PDS endpoint")); 66 + + }; 67 + + 68 + + Ok((ServoUrl::parse(endpoint)?, document)) 69 + +}
+155
patches/components/net/atproto/session.rs.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -0,0 +1,152 @@ 4 + +// SPDX-License-Identifier: AGPL-3.0-or-later 5 + + 6 + +use constellation_traits::{ 7 + + AtProtoCurrentSession, AtProtoError, AtProtoErrorKind, AtProtoNewSession, 8 + + AtProtoRefreshSession, AtProtoResult, 9 + +}; 10 + +use headers::{Authorization, HeaderMap, HeaderMapExt}; 11 + +use http::Method; 12 + +use log::error; 13 + +use net_traits::CoreResourceThread; 14 + +use net_traits::fetch::utils::{FetchJsonError, fetch_json}; 15 + +use serde::Deserialize; 16 + +use servo_url::ServoUrl; 17 + +use sync_wrapper::SyncWrapper; 18 + + 19 + +use crate::atproto::pds::get_endpoint_for_subject; 20 + + 21 + +pub struct SessionClient {} 22 + + 23 + +pub type AtProtoResultResponseCallback = Box<dyn FnOnce(AtProtoResult) + Send>; 24 + + 25 + +impl SessionClient { 26 + + pub async fn create( 27 + + handle: &str, 28 + + password: &str, 29 + + resource_thread: SyncWrapper<CoreResourceThread>, 30 + + ) -> AtProtoResult { 31 + + let Ok((endpoint, _doc)) = get_endpoint_for_subject(handle, None, None).await else { 32 + + return AtProtoResult::Error; 33 + + }; 34 + + 35 + + let Ok(url) = endpoint.join("/xrpc/com.atproto.server.createSession") else { 36 + + return AtProtoResult::Error; 37 + + }; 38 + + 39 + + match fetch_json::<AtProtoNewSession, AtProtoError>( 40 + + url, 41 + + &[("identifier", handle), ("password", password)], 42 + + resource_thread, 43 + + Method::POST, 44 + + None, 45 + + false, 46 + + ) 47 + + .await 48 + + { 49 + + Ok(session) => AtProtoResult::NewSession(session, endpoint), 50 + + Err(err) => { 51 + + error!("create error: {err:?}"); 52 + + AtProtoResult::Error 53 + + }, 54 + + } 55 + + } 56 + + 57 + + pub async fn current( 58 + + endpoint: &ServoUrl, 59 + + resource_thread: SyncWrapper<CoreResourceThread>, 60 + + ) -> AtProtoResult { 61 + + let Ok(url) = endpoint.join("/xrpc/com.atproto.server.getSession") else { 62 + + return AtProtoResult::Error; 63 + + }; 64 + + 65 + + match fetch_json::<AtProtoCurrentSession, AtProtoError>( 66 + + url, 67 + + &[], 68 + + resource_thread, 69 + + Method::GET, 70 + + None, 71 + + true, 72 + + ) 73 + + .await 74 + + { 75 + + Ok(session) => AtProtoResult::CurrentSession(session), 76 + + Err(FetchJsonError::Other(err)) => { 77 + + if err.error == AtProtoErrorKind::ExpiredToken { 78 + + AtProtoResult::RefreshRequired 79 + + } else { 80 + + error!("current error: {err:?}"); 81 + + AtProtoResult::Error 82 + + } 83 + + }, 84 + + Err(err) => { 85 + + error!("current error: {err:?}"); 86 + + AtProtoResult::Error 87 + + }, 88 + + } 89 + + } 90 + + 91 + + pub async fn delete( 92 + + endpoint: &ServoUrl, 93 + + resource_thread: SyncWrapper<CoreResourceThread>, 94 + + refresh_jwt: &str, 95 + + ) -> AtProtoResult { 96 + + let Ok(url) = endpoint.join("/xrpc/com.atproto.server.deleteSession") else { 97 + + return AtProtoResult::Error; 98 + + }; 99 + + 100 + + #[derive(Deserialize)] 101 + + struct DeleteSessionResult {} 102 + + 103 + + let mut headers = HeaderMap::new(); 104 + + headers.typed_insert(Authorization::bearer(refresh_jwt).unwrap()); 105 + + 106 + + match fetch_json::<DeleteSessionResult, AtProtoError>( 107 + + url, 108 + + &[], 109 + + resource_thread, 110 + + Method::POST, 111 + + Some(headers), 112 + + false, 113 + + ) 114 + + .await 115 + + { 116 + + Ok(_) | Err(FetchJsonError::NoContent) => AtProtoResult::Logout, 117 + + Err(FetchJsonError::Other(err)) => { 118 + + if err.error == AtProtoErrorKind::ExpiredToken { 119 + + AtProtoResult::RefreshRequired 120 + + } else { 121 + + error!("delete error: {err:?}"); 122 + + AtProtoResult::Error 123 + + } 124 + + }, 125 + + Err(err) => { 126 + + error!("delete error: {err:?}"); 127 + + AtProtoResult::Error 128 + + }, 129 + + } 130 + + } 131 + + 132 + + pub async fn refresh( 133 + + endpoint: &ServoUrl, 134 + + resource_thread: SyncWrapper<CoreResourceThread>, 135 + + refresh_jwt: &str, 136 + + ) -> Option<AtProtoRefreshSession> { 137 + + let Ok(url) = endpoint.join("/xrpc/com.atproto.server.refreshSession") else { 138 + + return None; 139 + + }; 140 + + 141 + + let mut headers = HeaderMap::new(); 142 + + headers.typed_insert(Authorization::bearer(refresh_jwt).unwrap()); 143 + + 144 + + fetch_json::<AtProtoRefreshSession, AtProtoError>( 145 + + url, 146 + + &[], 147 + + resource_thread, 148 + + Method::POST, 149 + + Some(headers), 150 + + false, 151 + + ) 152 + + .await 153 + + .ok() 154 + + } 155 + +}
+271
patches/components/net/atproto/xrpc.rs.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -0,0 +1,268 @@ 4 + +// SPDX-License-Identifier: AGPL-3.0-or-later 5 + + 6 + +use async_recursion::async_recursion; 7 + +use constellation_traits::{AtProtoError, AtProtoErrorKind, AtProtoRefreshSession}; 8 + +use headers::{Authorization, HeaderMapExt}; 9 + +use http::header::CONTENT_TYPE; 10 + +use http::{HeaderMap, Method}; 11 + +use log::error; 12 + +use net_traits::AtProtoSessionState; 13 + +use net_traits::fetch::utils::{SimpleFetchTarget, rewrite_response_url}; 14 + +use net_traits::request::{ 15 + + CredentialsMode, Referrer, Request, RequestBody, RequestBuilder, 16 + + create_request_body_with_content, 17 + +}; 18 + +use net_traits::response::{Response, ResponseType}; 19 + +use serde_json::json; 20 + +use servo_url::ServoUrl; 21 + + 22 + +use crate::fetch::methods::{FetchContext, fetch}; 23 + + 24 + +pub struct XrpcClient { 25 + + endpoint: ServoUrl, 26 + + at_url: ServoUrl, 27 + + repo_did: String, 28 + + context: FetchContext, 29 + +} 30 + + 31 + +impl XrpcClient { 32 + + pub fn new( 33 + + endpoint: ServoUrl, 34 + + at_url: ServoUrl, 35 + + repo_did: &str, 36 + + context: FetchContext, 37 + + ) -> Self { 38 + + Self { 39 + + endpoint, 40 + + at_url, 41 + + repo_did: repo_did.into(), 42 + + context, 43 + + } 44 + + } 45 + + 46 + + fn build_request( 47 + + &self, 48 + + xrpc_call: &str, 49 + + params: &[(&str, &str)], 50 + + requires_auth: bool, 51 + + body: Option<RequestBody>, 52 + + method: Option<Method>, 53 + + headers: Option<HeaderMap>, 54 + + ) -> (Request, ServoUrl) { 55 + + let mut xrpc_url = self 56 + + .endpoint 57 + + .join(xrpc_call) 58 + + .map_err(|_| ()) 59 + + .expect("Failed to build xrpc url"); 60 + + if !params.is_empty() { 61 + + let mut url_params = xrpc_url.as_mut_url().query_pairs_mut(); 62 + + for param in params { 63 + + url_params.append_pair(param.0, param.1); 64 + + } 65 + + } 66 + + let mut builder = RequestBuilder::new(None, xrpc_url.clone(), Referrer::NoReferrer) 67 + + .method(method.unwrap_or(Method::GET)) 68 + + .headers(headers.unwrap_or_default()) 69 + + .origin(xrpc_url.origin()) 70 + + .body(body); 71 + + if requires_auth { 72 + + builder = builder.credentials_mode(CredentialsMode::Include); 73 + + } 74 + + (builder.build(), xrpc_url) 75 + + } 76 + + 77 + + // TODO: proper errors 78 + + #[async_recursion] 79 + + async fn fetch( 80 + + &self, 81 + + xrpc_call: &str, 82 + + params: &[(&str, &str)], 83 + + requires_auth: bool, 84 + + body: Option<RequestBody>, 85 + + method: Option<Method>, 86 + + headers: Option<HeaderMap>, 87 + + ) -> Result<Response, ()> { 88 + + let (request, xrpc_url) = self.build_request( 89 + + xrpc_call, 90 + + params, 91 + + requires_auth, 92 + + body.clone(), 93 + + method.clone(), 94 + + headers.clone(), 95 + + ); 96 + + let mut target = SimpleFetchTarget::default(); 97 + + 98 + + let mut xrpc_response = fetch(request, &mut target, &self.context).await; 99 + + 100 + + if xrpc_response.status.raw_code() == 400 { 101 + + error!( 102 + + "Error 400 for {} (auth: {}): {}", 103 + + xrpc_call, 104 + + requires_auth, 105 + + String::from_utf8_lossy(&target.body) 106 + + ); 107 + + // Check if the error is an ExpiredToken one. 108 + + let Ok(error_response) = serde_json::from_slice::<AtProtoError>(&target.body) else { 109 + + error!("Unexpected error response."); 110 + + return Err(()); 111 + + }; 112 + + 113 + + if error_response.error != AtProtoErrorKind::ExpiredToken { 114 + + error!("Unexpected error kind"); 115 + + return Err(()); 116 + + } 117 + + 118 + + let atproto_session = { 119 + + let atproto_session = self.context.state.atproto_session.read(); 120 + + let Some(ref atproto_session) = *atproto_session else { 121 + + return Err(()); 122 + + }; 123 + + atproto_session.clone() 124 + + }; 125 + + 126 + + // Try to refresh the auth token and retry the request. 127 + + let mut refresh_headers = HeaderMap::new(); 128 + + refresh_headers 129 + + .typed_insert(Authorization::bearer(&atproto_session.refresh_jwt).unwrap()); 130 + + let (refesh_request, _xrpc_url) = self.build_request( 131 + + "/xrpc/com.atproto.server.refreshSession", 132 + + &[], 133 + + false, 134 + + None, 135 + + Some(Method::POST), 136 + + Some(refresh_headers), 137 + + ); 138 + + let mut target = SimpleFetchTarget::default(); 139 + + 140 + + let refresh_response = fetch(refesh_request, &mut target, &self.context).await; 141 + + if refresh_response.status.is_success() { 142 + + let Ok(new_session) = serde_json::from_slice::<AtProtoRefreshSession>(&target.body) 143 + + else { 144 + + error!("Unexpected refreshSession response."); 145 + + return Err(()); 146 + + }; 147 + + 148 + + // Update the http state. 149 + + let session_state = AtProtoSessionState { 150 + + endpoint: self.endpoint.clone(), 151 + + access_jwt: new_session.access_jwt, 152 + + refresh_jwt: new_session.refresh_jwt, 153 + + }; 154 + + self.context 155 + + .state 156 + + .update_atproto_session(Some(session_state)); 157 + + 158 + + // TODO: update jwts storage. 159 + + 160 + + // Replay the call 161 + + self.fetch(xrpc_call, params, requires_auth, body, method, headers) 162 + + .await 163 + + } else { 164 + + Err(()) 165 + + } 166 + + } else { 167 + + xrpc_response.response_type = ResponseType::Basic; 168 + + Ok(rewrite_response_url( 169 + + self.at_url.clone(), 170 + + xrpc_url, 171 + + xrpc_response, 172 + + )) 173 + + } 174 + + } 175 + + 176 + + // GET requests 177 + + async fn fetch_get(&self, xprc_call: &str, params: &[(&str, &str)]) -> Result<Response, ()> { 178 + + self.fetch(xprc_call, params, false, None, None, None).await 179 + + } 180 + + 181 + + pub async fn describe_repo(&self) -> Result<Response, ()> { 182 + + self.fetch_get( 183 + + "/xrpc/com.atproto.repo.describeRepo", 184 + + &[("repo", &self.repo_did)], 185 + + ) 186 + + .await 187 + + } 188 + + 189 + + pub async fn list_records(&self, collection: &str) -> Result<Response, ()> { 190 + + self.fetch_get( 191 + + "/xrpc/com.atproto.repo.listRecords", 192 + + &[("repo", &self.repo_did), ("collection", collection)], 193 + + ) 194 + + .await 195 + + } 196 + + 197 + + pub async fn get_record(&self, collection: &str, rkey: &str) -> Result<Response, ()> { 198 + + // If the collection name is "com.atproto.sync.blob", this is a link to a blob. 199 + + if collection == "com.atproto.sync.blob" { 200 + + return self 201 + + .fetch_get( 202 + + "/xrpc/com.atproto.sync.getBlob", 203 + + &[("did", &self.repo_did), ("cid", rkey)], 204 + + ) 205 + + .await; 206 + + } 207 + + 208 + + self.fetch_get( 209 + + "/xrpc/com.atproto.repo.getRecord", 210 + + &[ 211 + + ("repo", &self.repo_did), 212 + + ("collection", collection), 213 + + ("rkey", rkey), 214 + + ], 215 + + ) 216 + + .await 217 + + } 218 + + 219 + + pub async fn delete_record(&self, collection: &str, rkey: &str) -> Result<Response, ()> { 220 + + let mut headers = HeaderMap::new(); 221 + + headers.insert(CONTENT_TYPE, "application/json".parse().unwrap()); 222 + + 223 + + // Build the JSON body. 224 + + let json = json!({ 225 + + "repo": self.repo_did, 226 + + "collection": collection, 227 + + "rkey": rkey 228 + + }); 229 + + let body = create_request_body_with_content(&json.to_string()); 230 + + 231 + + self.fetch( 232 + + "/xrpc/com.atproto.repo.deleteRecord", 233 + + &[], 234 + + true, 235 + + Some(body), 236 + + Some(Method::POST), 237 + + Some(headers), 238 + + ) 239 + + .await 240 + + } 241 + + 242 + + pub async fn create_record(&self, collection: &str, body: RequestBody) -> Result<Response, ()> { 243 + + let mut headers = HeaderMap::new(); 244 + + headers.insert(CONTENT_TYPE, "application/json".parse().unwrap()); 245 + + 246 + + self.fetch( 247 + + "/xrpc/com.atproto.repo.createRecord", 248 + + &[("repo", &self.repo_did), ("collection", collection)], 249 + + true, 250 + + Some(body), 251 + + Some(Method::POST), 252 + + Some(headers), 253 + + ) 254 + + .await 255 + + } 256 + + 257 + + pub async fn upload_blob(&self, content_type: &str, body: RequestBody) -> Result<Response, ()> { 258 + + let mut headers = HeaderMap::new(); 259 + + headers.insert(CONTENT_TYPE, content_type.parse().unwrap()); 260 + + 261 + + self.fetch( 262 + + "/xrpc/com.atproto.repo.uploadBlob", 263 + + &[], 264 + + true, 265 + + Some(body), 266 + + Some(Method::POST), 267 + + Some(headers), 268 + + ) 269 + + .await 270 + + } 271 + +}
+173
patches/components/net/http_loader.rs.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -4,6 +4,7 @@ 4 + 5 + use std::collections::HashSet; 6 + use std::iter::FromIterator; 7 + +use std::path::PathBuf; 8 + use std::sync::Arc as StdArc; 9 + use std::time::{Duration, SystemTime, UNIX_EPOCH}; 10 + 11 + @@ -15,7 +16,7 @@ 12 + }; 13 + use embedder_traits::{AuthenticationResponse, GenericEmbedderProxy}; 14 + use futures::{TryFutureExt, TryStreamExt, future}; 15 + -use headers::authorization::Basic; 16 + +use headers::authorization::{Basic, Bearer}; 17 + use headers::{ 18 + AccessControlAllowCredentials, AccessControlAllowHeaders, AccessControlAllowMethods, 19 + AccessControlMaxAge, AccessControlRequestMethod, Authorization, CacheControl, ContentLength, 20 + @@ -56,9 +57,10 @@ 21 + CacheState, HttpsState, RedirectTaint, Response, ResponseBody, ResponseType, 22 + }; 23 + use net_traits::{ 24 + - CookieSource, DOCUMENT_ACCEPT_HEADER_VALUE, DebugVec, FetchMetadata, NetworkError, 25 + - RedirectEndValue, RedirectStartValue, ReferrerPolicy, ResourceAttribute, ResourceFetchTiming, 26 + - ResourceTimeValue, TlsSecurityInfo, TlsSecurityState, 27 + + AtProtoSessionState, AuthCacheEntry, BasicAuthCacheEntry, BearerAuthCacheEntry, CookieSource, 28 + + DOCUMENT_ACCEPT_HEADER_VALUE, DebugVec, FetchMetadata, NetworkError, RedirectEndValue, 29 + + RedirectStartValue, ReferrerPolicy, ResourceAttribute, ResourceFetchTiming, ResourceTimeValue, 30 + + TlsSecurityInfo, TlsSecurityState, 31 + }; 32 + use parking_lot::{Mutex, RwLock}; 33 + use profile_traits::mem::{Report, ReportKind}; 34 + @@ -91,7 +93,7 @@ 35 + use crate::http_cache::{ 36 + CacheKey, CachedResourcesOrGuard, HttpCache, construct_response, invalidate, refresh, 37 + }; 38 + -use crate::resource_thread::{AuthCache, AuthCacheEntry}; 39 + +use crate::resource_thread::AuthCache; 40 + use crate::websocket_loader::start_websocket; 41 + 42 + /// The various states an entry of the HttpCache can be in. 43 + @@ -106,6 +108,7 @@ 44 + } 45 + 46 + pub struct HttpState { 47 + + pub config_dir: Option<PathBuf>, 48 + pub hsts_list: RwLock<HstsList>, 49 + pub cookie_jar: RwLock<CookieStorage>, 50 + pub http_cache: HttpCache, 51 + @@ -114,9 +117,40 @@ 52 + pub client: ServoClient, 53 + pub override_manager: CertificateErrorOverrideManager, 54 + pub embedder_proxy: GenericEmbedderProxy<NetToEmbedderMsg>, 55 + + pub atproto_session: RwLock<Option<AtProtoSessionState>>, 56 + } 57 + 58 + impl HttpState { 59 + + pub(crate) fn update_atproto_session(&self, session: Option<AtProtoSessionState>) { 60 + + let mut atproto_session = self.atproto_session.write(); 61 + + let current_endpoint = atproto_session.clone().map(|s| s.endpoint); 62 + + *atproto_session = session.clone(); 63 + + if let Some(ref session) = session { 64 + + if let Some(config_dir) = &self.config_dir { 65 + + session.save(config_dir); 66 + + } 67 + + } else if let Some(config_dir) = &self.config_dir { 68 + + AtProtoSessionState::reset(config_dir); 69 + + } 70 + + 71 + + if let Some(session) = session { 72 + + let mut auth_cache = self.auth_cache.write(); 73 + + let origin = session.endpoint.origin().ascii_serialization(); 74 + + auth_cache.entries.insert( 75 + + origin, 76 + + AuthCacheEntry::Bearer(BearerAuthCacheEntry { 77 + + token: session.access_jwt.clone(), 78 + + }), 79 + + ); 80 + + } else if let Some(endpoint) = current_endpoint { 81 + + let mut auth_cache = self.auth_cache.write(); 82 + + let origin = endpoint.origin().ascii_serialization(); 83 + + auth_cache.entries.remove(&origin); 84 + + } else { 85 + + error!("Failed to remove Bearer auth entry"); 86 + + } 87 + + } 88 + + 89 + pub(crate) fn memory_reports(&self, suffix: &str, ops: &mut MallocSizeOfOps) -> Vec<Report> { 90 + vec![ 91 + Report { 92 + @@ -577,14 +611,40 @@ 93 + } 94 + } 95 + 96 + +enum AuthCredential { 97 + + Basic(Authorization<Basic>), 98 + + Bearer(Authorization<Bearer>), 99 + +} 100 + + 101 + +impl AuthCredential { 102 + + fn update_headers(self, headers: &mut HeaderMap) { 103 + + match self { 104 + + Self::Basic(auth) => headers.typed_insert(auth), 105 + + Self::Bearer(auth) => headers.typed_insert(auth), 106 + + } 107 + + } 108 + +} 109 + + 110 + fn auth_from_cache( 111 + auth_cache: &RwLock<AuthCache>, 112 + origin: &ImmutableOrigin, 113 + -) -> Option<Authorization<Basic>> { 114 + +) -> Option<AuthCredential> { 115 + if let Some(auth_entry) = auth_cache.read().entries.get(&origin.ascii_serialization()) { 116 + - let user_name = &auth_entry.user_name; 117 + - let password = &auth_entry.password; 118 + - Some(Authorization::basic(user_name, password)) 119 + + match auth_entry { 120 + + AuthCacheEntry::Basic(auth_entry) => { 121 + + let user_name = &auth_entry.user_name; 122 + + let password = &auth_entry.password; 123 + + Some(AuthCredential::Basic(Authorization::basic( 124 + + user_name, password, 125 + + ))) 126 + + }, 127 + + AuthCacheEntry::Bearer(auth_entry) => { 128 + + let token = &auth_entry.token; 129 + + Some(AuthCredential::Bearer( 130 + + Authorization::bearer(token).unwrap(), 131 + + )) 132 + + }, 133 + + } 134 + } else { 135 + None 136 + } 137 + @@ -1645,15 +1705,15 @@ 138 + authorization_value.is_none() && 139 + has_credentials(&current_url) 140 + { 141 + - authorization_value = Some(Authorization::basic( 142 + + authorization_value = Some(AuthCredential::Basic(Authorization::basic( 143 + current_url.username(), 144 + current_url.password().unwrap_or(""), 145 + - )); 146 + + ))); 147 + } 148 + 149 + // Substep 6 150 + if let Some(basic) = authorization_value { 151 + - http_request.headers.typed_insert(basic); 152 + + basic.update_headers(&mut http_request.headers); 153 + } 154 + } 155 + } 156 + @@ -1858,7 +1918,7 @@ 157 + }; 158 + 159 + // Store the credentials as a proxy-authentication entry. 160 + - let entry = AuthCacheEntry { 161 + + let entry = BasicAuthCacheEntry { 162 + user_name: credentials.username, 163 + password: credentials.password, 164 + }; 165 + @@ -1865,7 +1925,7 @@ 166 + { 167 + let mut auth_cache = context.state.auth_cache.write(); 168 + let key = request.current_url().origin().ascii_serialization(); 169 + - auth_cache.entries.insert(key, entry); 170 + + auth_cache.entries.insert(key, AuthCacheEntry::Basic(entry)); 171 + } 172 + 173 + // Make sure this is set to None,
+14
patches/components/net/lib.rs.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -24,6 +24,11 @@ 4 + #[cfg(feature = "test-util")] 5 + pub mod test_util; 6 + mod websocket_loader; 7 + +pub mod atproto { 8 + + pub mod pds; 9 + + pub mod session; 10 + + pub mod xrpc; 11 + +} 12 + 13 + /// An implementation of the [Fetch specification](https://fetch.spec.whatwg.org/) 14 + pub mod fetch {
+6
patches/components/net/protocols/atproto/mod.rs.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -0,0 +1,3 @@ 4 + +// SPDX-License-Identifier: AGPL-3.0-or-later 5 + + 6 + +pub(crate) mod protocol;
+182
patches/components/net/protocols/atproto/protocol.rs.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -0,0 +1,179 @@ 4 + +// SPDX-License-Identifier: AGPL-3.0-or-later 5 + + 6 + +use std::future::{self, Future}; 7 + +use std::num::NonZeroUsize; 8 + +use std::pin::Pin; 9 + +use std::sync::Arc; 10 + + 11 + +use atproto_identity::resolve::HickoryDnsResolver; 12 + +use atproto_identity::storage_lru::LruDidDocumentStorage; 13 + +use content_security_policy::percent_encoding::percent_decode_str; 14 + +use http::header::CONTENT_TYPE; 15 + +use http::{Method, StatusCode}; 16 + +use log::info; 17 + +use net_traits::fetch::utils::http_response; 18 + +use net_traits::request::Request; 19 + +use net_traits::response::Response; 20 + +use servo_url::ServoUrl; 21 + +use sync_wrapper::SyncWrapper; 22 + + 23 + +use crate::atproto::pds::get_endpoint_for_subject; 24 + +use crate::atproto::xrpc::XrpcClient; 25 + +use crate::fetch::methods::{DoneChannel, FetchContext}; 26 + +use crate::protocols::ProtocolHandler; 27 + + 28 + +pub struct AtProtocolHandler { 29 + + document_storage: LruDidDocumentStorage, 30 + + dns_resolver: Arc<HickoryDnsResolver>, 31 + +} 32 + + 33 + +impl Default for AtProtocolHandler { 34 + + fn default() -> Self { 35 + + Self { 36 + + document_storage: LruDidDocumentStorage::new(NonZeroUsize::new(1000).unwrap()), 37 + + dns_resolver: Arc::new(HickoryDnsResolver::create_resolver(Default::default())), 38 + + } 39 + + } 40 + +} 41 + + 42 + +fn maybe_bad_request(url: &ServoUrl, reason: &str, response: Result<Response, ()>) -> Response { 43 + + response.unwrap_or_else(|_| http_response(url.clone(), StatusCode::BAD_REQUEST, reason)) 44 + +} 45 + + 46 + +/// Implementation of the at:// protocol handler. 47 + +/// at://did:plc:44ybard66vv44zksje25o7dz/app.bsky.feed.post/3jwdwj2ctlk26 48 + +/// at://bnewbold.bsky.team/app.bsky.feed.post/3jwdwj2ctlk26 49 + +impl ProtocolHandler for AtProtocolHandler { 50 + + fn load( 51 + + &self, 52 + + request: &mut Request, 53 + + _done_chan: &mut DoneChannel, 54 + + context: &FetchContext, 55 + + ) -> Pin<Box<dyn Future<Output = Response> + Send>> { 56 + + let url = request.current_url(); 57 + + 58 + + let method = request.method.clone(); 59 + + if method != Method::GET && method != Method::POST && method != Method::DELETE { 60 + + return Box::pin(future::ready(http_response( 61 + + url, 62 + + StatusCode::BAD_REQUEST, 63 + + "Invalid method", 64 + + ))); 65 + + } 66 + + 67 + + let context2 = context.clone(); 68 + + let document_storage = self.document_storage.clone(); 69 + + let dns_resolver = Arc::clone(&self.dns_resolver); 70 + + let mut request = SyncWrapper::new(request.clone()); 71 + + Box::pin(async move { 72 + + // Get the host and run percent decoding. 73 + + let Some(host) = url.host_str() else { 74 + + return http_response(url, StatusCode::BAD_REQUEST, "No host"); 75 + + }; 76 + + 77 + + let Ok(subject) = percent_decode_str(host).decode_utf8() else { 78 + + return http_response(url, StatusCode::BAD_REQUEST, "Host decoding error"); 79 + + }; 80 + + 81 + + let Ok((endpoint_url, document)) = 82 + + get_endpoint_for_subject(&subject, Some(document_storage), Some(dns_resolver)) 83 + + .await 84 + + else { 85 + + return http_response( 86 + + url, 87 + + StatusCode::BAD_REQUEST, 88 + + "Failed to resolve endpoint for subject", 89 + + ); 90 + + }; 91 + + 92 + + let (collection, rkey) = if let Some(mut segments) = url.path_segments() { 93 + + let collection = segments.next(); 94 + + let rkey = segments.next(); 95 + + (collection, rkey) 96 + + } else { 97 + + (None, None) 98 + + }; 99 + + 100 + + let client = XrpcClient::new(endpoint_url, url.clone(), &document.id, context2.clone()); 101 + + 102 + + if method == Method::GET { 103 + + let (response, reason) = match (collection, rkey) { 104 + + (None, _) => { 105 + + // If we have no collection, send a com.atproto.repo.describeRepo request. 106 + + (client.describe_repo().await, "Failed to describe repo") 107 + + }, 108 + + (Some(coll), None) => { 109 + + // If we have a collection but no rkey, send a com.atproto.repo.listRecords request. 110 + + (client.list_records(coll).await, "Failed to list records") 111 + + }, 112 + + (Some(coll), Some(rkey)) => { 113 + + // Both collection and rkey are present, send a com.atproto.repo.getRecord request. 114 + + (client.get_record(coll, rkey).await, "Failed to get record") 115 + + }, 116 + + }; 117 + + maybe_bad_request(&url, reason, response) 118 + + } else if method == Method::POST { 119 + + let request = request.get_mut(); 120 + + 121 + + // Check the mandatory content type. 122 + + let Some(header_value) = request.headers.get(CONTENT_TYPE) else { 123 + + return http_response(url, StatusCode::BAD_REQUEST, "Content-Type missing"); 124 + + }; 125 + + let Ok(content_type) = header_value.to_str() else { 126 + + return http_response(url, StatusCode::BAD_REQUEST, "Invalid Content-Type"); 127 + + }; 128 + + info!("CONTENT_TYPE is: {:?}", content_type); 129 + + 130 + + let Some(ref body) = request.body else { 131 + + return http_response(url, StatusCode::BAD_REQUEST, "Missing body"); 132 + + }; 133 + + 134 + + match (collection, rkey) { 135 + + (None, _) => { 136 + + // When no collection is specified, this is a blob update. 137 + + let response = client.upload_blob(content_type, body.clone()).await; 138 + + maybe_bad_request(&url, "Failed to create record", response) 139 + + }, 140 + + (Some(_), Some(_)) => { 141 + + // TODO: allow rkey when it makes sense 142 + + http_response( 143 + + url, 144 + + StatusCode::BAD_REQUEST, 145 + + "Invalid parameters: expects repo and no rkey", 146 + + ) 147 + + }, 148 + + (Some(collection), None) => { 149 + + let response = client.create_record(collection, body.clone()).await; 150 + + maybe_bad_request(&url, "Failed to create record", response) 151 + + }, 152 + + } 153 + + } else if method == Method::DELETE { 154 + + // Only support record deletion requiring collection and rkey. 155 + + if let (Some(collection), Some(rkey)) = (collection, rkey) { 156 + + let response = client.delete_record(collection, rkey).await; 157 + + maybe_bad_request(&url, "Failed to delete record", response) 158 + + } else { 159 + + http_response( 160 + + url, 161 + + StatusCode::BAD_REQUEST, 162 + + "Missing parameters to delete record", 163 + + ) 164 + + } 165 + + } else { 166 + + http_response( 167 + + url, 168 + + StatusCode::BAD_REQUEST, 169 + + &format!("{method} not implemented yet"), 170 + + ) 171 + + } 172 + + }) 173 + + } 174 + + 175 + + fn is_fetchable(&self) -> bool { 176 + + true 177 + + } 178 + + 179 + + fn is_secure(&self) -> bool { 180 + + true 181 + + } 182 + +}
+33
patches/components/net/protocols/mod.rs.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -21,13 +21,17 @@ 4 + 5 + use crate::fetch::methods::{DoneChannel, FetchContext, RangeRequestBounds, fetch}; 6 + 7 + +mod atproto; 8 + mod blob; 9 + mod data; 10 + mod file; 11 + +mod trusted; 12 + 13 + +use atproto::protocol::AtProtocolHandler; 14 + use blob::BlobProtocolHander; 15 + use data::DataProtocolHander; 16 + use file::FileProtocolHander; 17 + +use trusted::protocol::TrustedProtocolHandler; 18 + 19 + type FutureResponse<'a> = Pin<Box<dyn Future<Output = Response> + Send + 'a>>; 20 + 21 + @@ -97,6 +101,12 @@ 22 + .register("file", FileProtocolHander::default()) 23 + .expect("Infallible"); 24 + registry 25 + + .register("trusted", TrustedProtocolHandler::default()) 26 + + .expect("Infallible"); 27 + + registry 28 + + .register("at", AtProtocolHandler::default()) 29 + + .expect("Infallible"); 30 + + registry 31 + } 32 + 33 + /// Do not allow users to enter an arbitrary protocol as this can lead to slowdowns.
+7
patches/components/net/protocols/trusted/mod.rs.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -0,0 +1,4 @@ 4 + +// SPDX-License-Identifier: AGPL-3.0-or-late 5 + + 6 + +pub(crate) mod protocol; 7 + +pub(crate) mod zone_description;
+146
patches/components/net/protocols/trusted/protocol.rs.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -0,0 +1,143 @@ 4 + +/// SPDX-License-Identifier: AGPL-3.0-or-later 5 + + 6 + +use std::future::{self, Future}; 7 + +use std::pin::Pin; 8 + + 9 + +use http::StatusCode; 10 + +use net_traits::fetch::utils::{SimpleFetchTarget, http_response, rewrite_response_url}; 11 + +use net_traits::request::{Referrer, Request, RequestBuilder}; 12 + +use net_traits::response::{Response, ResponseBody}; 13 + +use serde::de::DeserializeOwned; 14 + +use servo_url::ServoUrl; 15 + + 16 + +use super::zone_description::{TrustedZoneUrl, ZoneFile}; 17 + +use crate::fetch::methods::{DoneChannel, FetchContext, fetch}; 18 + +use crate::protocols::ProtocolHandler; 19 + + 20 + +async fn fetch_json<T: DeserializeOwned>( 21 + + url: ServoUrl, 22 + + context: &FetchContext, 23 + +) -> Result<T, StatusCode> { 24 + + let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer) 25 + + .origin(url.origin()) 26 + + .build(); 27 + + let mut target = SimpleFetchTarget::default(); 28 + + let response = fetch(request, &mut target, context).await; 29 + + if response.is_network_error() { 30 + + return Err(response.status.code()); 31 + + } 32 + + let ResponseBody::Done(_) = *response.body.lock() else { 33 + + return Err(response.status.code()); 34 + + }; 35 + + serde_json::from_slice::<T>(&target.body).map_err(|_| StatusCode::UNPROCESSABLE_ENTITY) 36 + +} 37 + + 38 + +#[derive(Default)] 39 + +pub struct TrustedProtocolHandler {} 40 + + 41 + +impl ProtocolHandler for TrustedProtocolHandler { 42 + + fn load( 43 + + &self, 44 + + request: &mut Request, 45 + + _done_chan: &mut DoneChannel, 46 + + context: &FetchContext, 47 + + ) -> Pin<Box<dyn Future<Output = Response> + Send>> { 48 + + let url = request.current_url(); 49 + + 50 + + // Reject paths that could lead to url rewriting attacks. 51 + + let path = url.path(); 52 + + if path.contains("..") { 53 + + return Box::pin(future::ready(http_response( 54 + + url, 55 + + StatusCode::BAD_REQUEST, 56 + + "Invalid path", 57 + + ))); 58 + + } 59 + + 60 + + // url looks like: trusted://demo.trustedweb.org:8000/port8800/index.html 61 + + 62 + + let Some((zone_name, user_id)) = url.zone_and_user() else { 63 + + return Box::pin(future::ready(http_response( 64 + + url, 65 + + StatusCode::BAD_REQUEST, 66 + + "Invalid trusted:// url", 67 + + ))); 68 + + }; 69 + + 70 + + let port = match url.port() { 71 + + Some(value) => &format!(":{value}"), 72 + + None => "", 73 + + }; 74 + + let Ok(zone_base_url) = ServoUrl::parse(&format!("http://{user_id}{port}")) else { 75 + + return Box::pin(future::ready(http_response( 76 + + url, 77 + + StatusCode::BAD_REQUEST, 78 + + "Invalid trusted user_id", 79 + + ))); 80 + + }; 81 + + 82 + + let Ok(zone_url) = zone_base_url.join(".well-known/trusted-zones.json") else { 83 + + return Box::pin(future::ready(http_response( 84 + + url, 85 + + StatusCode::BAD_REQUEST, 86 + + "Invalid zone url", 87 + + ))); 88 + + }; 89 + + 90 + + let context2 = context.clone(); 91 + + Box::pin(async move { 92 + + // Fetch the zone file. 93 + + let zone_file = match fetch_json::<ZoneFile>(zone_url, &context2).await { 94 + + Ok(zone_file) => zone_file, 95 + + Err(status) => return http_response(url, status, "Failed to fetch zone file!"), 96 + + }; 97 + + 98 + + // Look for the zone by name. 99 + + let Some(zone) = zone_file.find_zone(&zone_name) else { 100 + + return http_response(url, StatusCode::BAD_REQUEST, "Unkwnon zone name"); 101 + + }; 102 + + 103 + + let Some(mut segments) = url.path_segments() else { 104 + + return http_response(url, StatusCode::BAD_REQUEST, "Invalid path segments"); 105 + + }; 106 + + 107 + + let Some(mapping_path) = segments.next() else { 108 + + return http_response(url, StatusCode::BAD_REQUEST, "Failed to find first segment"); 109 + + }; 110 + + 111 + + let Some(mapping) = zone.find_mapping(mapping_path) else { 112 + + return http_response(url, StatusCode::BAD_REQUEST, "Invalid Mapping Name"); 113 + + }; 114 + + 115 + + // Build the final url by using the mapping source as the base url and the remaining path segments as the path. 116 + + let Ok(base_url) = ServoUrl::parse(&mapping.source) else { 117 + + return http_response(url, StatusCode::BAD_REQUEST, "Invalid source url"); 118 + + }; 119 + + 120 + + let Ok(inner_url) = 121 + + base_url.join(&segments.fold("".to_owned(), |mut current, item| { 122 + + current.push_str(item); 123 + + current 124 + + })) 125 + + else { 126 + + return http_response(url, StatusCode::BAD_REQUEST, "Failed to build final url"); 127 + + }; 128 + + 129 + + let request = RequestBuilder::new(None, inner_url.clone(), Referrer::NoReferrer) 130 + + .origin(inner_url.origin()) 131 + + .build(); 132 + + let mut target = SimpleFetchTarget::default(); 133 + + 134 + + let fetched = fetch(request, &mut target, &context2).await; 135 + + rewrite_response_url(url, inner_url, fetched) 136 + + }) 137 + + } 138 + + 139 + + fn is_fetchable(&self) -> bool { 140 + + true 141 + + } 142 + + 143 + + fn is_secure(&self) -> bool { 144 + + true 145 + + } 146 + +}
+51
patches/components/net/protocols/trusted/web-user-trust.md.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -0,0 +1,48 @@ 4 + +# A proposal for a new trust model on the Web 5 + + 6 + +The current security model of the Web sits mostly on what is known as the Same Origin Policy (SOP): this ties cross-page capabilities to a domain name, delegating trust to the CA infrastructure. 7 + + 8 + +This means that the mechanisms defining the trust relationship from a page to another are shared by the service providers (who can decide which domains they use) and the User Agent (UA) in charge of implementing proper isolation according to the SOP. Of course this has been abused by service providers who ended up using the same domain to share data among different apps by scoping them with url paths. 9 + + 10 + +On the other hand, end users have no say on how two or more pages could trust each other. This is a very top down, rigid model. Instead, what about giving users ways to create their own "trust zones" that can include content from different sources? 11 + + 12 + +A trust zone establishes its own origin, so trusted urls are of the form: 13 + +trusted://<zoneID>.<userID>/<zone specific path> 14 + + 15 + +User IDs are used to fetch a zone description resource that lists the set of valid zones for that user and describes how to interpret the path component of the trusted url. A user ID can be: 16 + + 17 + +- a DID for that user. The zone description resource url needs to be set in the DID document. 18 + +- a web domain controlled by the user. The zone description resource will then be fetched at the `.well-known/trusted-zones.json` relative url. 19 + + 20 + +TODO: integrity checks of the zone resource. 21 + + 22 + +# Trusted url example: 23 + + 24 + +Let's consider that the resource at https://webbeef.org/.well-known/trusted-zones.json is: 25 + + 26 + +```json 27 + +{ 28 + + "zones": [ 29 + + { 30 + + "name": "demo", 31 + + "mappings": [ 32 + + { 33 + + "path": "port8000", 34 + + "source": "https://localhost:8000/" 35 + + }, 36 + + { 37 + + "path": "port8080", 38 + + "source": "https://localhost:8080/" 39 + + } 40 + + ] 41 + + } 42 + + ] 43 + +} 44 + +``` 45 + + 46 + +That will make these 2 urls same-origin, and would let them share eg. BroadcastChannel messages. 47 + + 48 + +trusted://demo.webbeef.org/port8000/index.html 49 + +trusted://demo.webbeef.org/port8080/messages/send.html 50 + + 51 + +
+50
patches/components/net/protocols/trusted/zone_description.rs.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -0,0 +1,47 @@ 4 + +// SPDX-License-Identifier: AGPL-3.0-or-later 5 + + 6 + +use serde::Deserialize; 7 + +use servo_url::ServoUrl; 8 + + 9 + +#[derive(Debug, Deserialize)] 10 + +pub(crate) struct ZoneFile { 11 + + zones: Vec<Zone>, 12 + +} 13 + + 14 + +impl ZoneFile { 15 + + pub fn find_zone(&self, name: &str) -> Option<&Zone> { 16 + + self.zones.iter().find(|zone| zone.name == name) 17 + + } 18 + +} 19 + + 20 + +#[derive(Debug, Deserialize)] 21 + +pub(crate) struct Zone { 22 + + name: String, 23 + + mappings: Vec<ZoneMapping>, 24 + +} 25 + + 26 + +impl Zone { 27 + + pub fn find_mapping(&self, path: &str) -> Option<&ZoneMapping> { 28 + + self.mappings.iter().find(|mapping| mapping.path == path) 29 + + } 30 + +} 31 + + 32 + +#[derive(Debug, Deserialize)] 33 + +pub(crate) struct ZoneMapping { 34 + + path: String, 35 + + pub source: String, 36 + +} 37 + + 38 + +pub(crate) trait TrustedZoneUrl { 39 + + fn zone_and_user(&self) -> Option<(String, String)>; 40 + +} 41 + + 42 + +impl TrustedZoneUrl for ServoUrl { 43 + + fn zone_and_user(&self) -> Option<(String, String)> { 44 + + let domain = self.domain()?; 45 + + let mut parts = domain.splitn(2, '.'); 46 + + let zone = parts.next().map(|z| z.to_owned())?; 47 + + let user = parts.next().map(|u| u.to_owned())?; 48 + + Some((zone, user)) 49 + + } 50 + +}
+81
patches/components/net/resource_thread.rs.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -25,10 +25,10 @@ 4 + use net_traits::request::{Destination, PreloadEntry, PreloadId, RequestBuilder, RequestId}; 5 + use net_traits::response::{Response, ResponseInit}; 6 + use net_traits::{ 7 + - AsyncRuntime, CookieAsyncResponse, CookieData, CookieSource, CoreResourceMsg, 8 + - CoreResourceThread, CustomResponseMediator, DiscardFetch, FetchChannels, FetchTaskTarget, 9 + - ResourceFetchTiming, ResourceThreads, ResourceTimingType, WebSocketDomAction, 10 + - WebSocketNetworkEvent, 11 + + AsyncRuntime, AtProtoSessionState, AuthCacheEntry, CookieAsyncResponse, CookieData, 12 + + CookieSource, CoreResourceMsg, CoreResourceThread, CustomResponseMediator, DiscardFetch, 13 + + FetchChannels, FetchTaskTarget, ResourceFetchTiming, ResourceThreads, ResourceTimingType, 14 + + WebSocketDomAction, WebSocketNetworkEvent, 15 + }; 16 + use parking_lot::{Mutex, RwLock}; 17 + use profile_traits::mem::{ 18 + @@ -199,14 +199,17 @@ 19 + let mut hsts_list = HstsList::default(); 20 + let mut auth_cache = AuthCache::default(); 21 + let mut cookie_jar = CookieStorage::new(150); 22 + + let mut atproto_session = None; 23 + if let Some(config_dir) = config_dir { 24 + servo_base::read_json_from_file(&mut auth_cache, config_dir, "auth_cache.json"); 25 + servo_base::read_json_from_file(&mut hsts_list, config_dir, "hsts_list.json"); 26 + servo_base::read_json_from_file(&mut cookie_jar, config_dir, "cookie_jar.json"); 27 + + atproto_session = AtProtoSessionState::load(config_dir); 28 + } 29 + 30 + let override_manager = CertificateErrorOverrideManager::new(); 31 + let http_state = HttpState { 32 + + config_dir: config_dir.map(|p| p.into()), 33 + hsts_list: RwLock::new(hsts_list), 34 + cookie_jar: RwLock::new(cookie_jar), 35 + auth_cache: RwLock::new(auth_cache), 36 + @@ -219,10 +222,12 @@ 37 + )), 38 + override_manager, 39 + embedder_proxy: embedder_proxy.clone(), 40 + + atproto_session: RwLock::new(atproto_session), 41 + }; 42 + 43 + let override_manager = CertificateErrorOverrideManager::new(); 44 + let private_http_state = HttpState { 45 + + config_dir: config_dir.map(|p| p.into()), 46 + hsts_list: RwLock::new(HstsList::default()), 47 + cookie_jar: RwLock::new(CookieStorage::new(150)), 48 + auth_cache: RwLock::new(AuthCache::default()), 49 + @@ -235,6 +240,7 @@ 50 + )), 51 + override_manager, 52 + embedder_proxy, 53 + + atproto_session: RwLock::new(None), 54 + }; 55 + 56 + (Arc::new(http_state), Arc::new(private_http_state)) 57 + @@ -592,17 +598,18 @@ 58 + }, 59 + // Ignore this message as we handle it only in the reporter chan 60 + CoreResourceMsg::CollectMemoryReport(_) => {}, 61 + + CoreResourceMsg::UpdateAtProtoSession(session) => { 62 + + http_state.update_atproto_session(session); 63 + + }, 64 + + CoreResourceMsg::GetAtProtoSession(sender) => { 65 + + let atproto_session = http_state.atproto_session.read(); 66 + + let _ = sender.send(atproto_session.clone()); 67 + + }, 68 + } 69 + true 70 + } 71 + } 72 + 73 + -#[derive(Clone, Debug, Deserialize, Serialize)] 74 + -pub struct AuthCacheEntry { 75 + - pub user_name: String, 76 + - pub password: String, 77 + -} 78 + - 79 + impl Default for AuthCache { 80 + fn default() -> Self { 81 + Self {
+121
patches/components/script/dom/atproto.rs.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -0,0 +1,118 @@ 4 + +// SPDX-License-Identifier: AGPL-3.0-or-later 5 + + 6 + +use std::rc::Rc; 7 + + 8 + +use constellation_traits::{AtProtoRequest, AtProtoResult, ScriptToConstellationMessage}; 9 + +use dom_struct::dom_struct; 10 + +use js::jsval::UndefinedValue; 11 + +use script_bindings::error::Error; 12 + +use script_bindings::str::USVString; 13 + + 14 + +use crate::dom::bindings::codegen::Bindings::AtProtoBinding::{AtProtoMethods, AtProtoSession}; 15 + +use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; 16 + +use crate::dom::bindings::root::DomRoot; 17 + +use crate::dom::globalscope::GlobalScope; 18 + +use crate::dom::promise::Promise; 19 + +use crate::realms::InRealm; 20 + +use crate::routed_promise::{RoutedPromiseListener, callback_promise}; 21 + +use crate::script_runtime::CanGc; 22 + + 23 + +#[dom_struct] 24 + +pub(crate) struct AtProto { 25 + + reflector_: Reflector, 26 + +} 27 + + 28 + +impl AtProto { 29 + + pub fn new_inherited() -> AtProto { 30 + + AtProto { 31 + + reflector_: Reflector::new(), 32 + + } 33 + + } 34 + + 35 + + pub(crate) fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<AtProto> { 36 + + reflect_dom_object(Box::new(AtProto::new_inherited()), global, can_gc) 37 + + } 38 + +} 39 + + 40 + +impl AtProto { 41 + + fn request(&self, request: AtProtoRequest, comp: InRealm, can_gc: CanGc) -> Rc<Promise> { 42 + + let global = &self.global(); 43 + + let promise = Promise::new_in_current_realm(comp, can_gc); 44 + + let task_source = global.task_manager().dom_manipulation_task_source(); 45 + + let callback = callback_promise(&promise, self, task_source); 46 + + 47 + + let script_to_constellation_chan = global.script_to_constellation_chan(); 48 + + if script_to_constellation_chan 49 + + .send(ScriptToConstellationMessage::AtProto(request, callback)) 50 + + .is_err() 51 + + { 52 + + promise.reject_error( 53 + + Error::Operation(Some("Constellation is dead".to_owned())), 54 + + can_gc, 55 + + ); 56 + + } 57 + + promise 58 + + } 59 + +} 60 + + 61 + +impl AtProtoMethods<crate::DomTypeHolder> for AtProto { 62 + + /// <https://webbeef.org/atproto> 63 + + fn Login( 64 + + &self, 65 + + handle: USVString, 66 + + password: USVString, 67 + + comp: InRealm, 68 + + can_gc: CanGc, 69 + + ) -> Rc<Promise> { 70 + + self.request( 71 + + AtProtoRequest::Login(handle.to_string(), password.to_string()), 72 + + comp, 73 + + can_gc, 74 + + ) 75 + + } 76 + + 77 + + /// <https://webbeef.org/atproto> 78 + + fn Logout(&self, comp: InRealm, can_gc: CanGc) -> Rc<Promise> { 79 + + self.request(AtProtoRequest::Logout, comp, can_gc) 80 + + } 81 + + 82 + + /// <https://webbeef.org/atproto> 83 + + fn Current(&self, comp: InRealm, can_gc: CanGc) -> Rc<Promise> { 84 + + self.request(AtProtoRequest::Current, comp, can_gc) 85 + + } 86 + +} 87 + + 88 + +impl RoutedPromiseListener<AtProtoResult> for AtProto { 89 + + fn handle_response(&self, response: AtProtoResult, promise: &Rc<Promise>, can_gc: CanGc) { 90 + + match response { 91 + + AtProtoResult::NewSession(session, _) => { 92 + + println!("New session is {session:?}"); 93 + + let dom_session = AtProtoSession { 94 + + did: session.did.into(), 95 + + handle: session.handle.into(), 96 + + }; 97 + + promise.resolve_native(&dom_session, can_gc); 98 + + }, 99 + + AtProtoResult::CurrentSession(session) => { 100 + + println!("Current session is {session:?}"); 101 + + let dom_session = AtProtoSession { 102 + + did: session.did.into(), 103 + + handle: session.handle.into(), 104 + + }; 105 + + promise.resolve_native(&dom_session, can_gc); 106 + + }, 107 + + AtProtoResult::Logout => promise.resolve_native(&UndefinedValue(), can_gc), 108 + + AtProtoResult::Error => { 109 + + error!("ATProto error :("); 110 + + promise.reject_error(Error::Operation(Some("ATProto error".to_owned())), can_gc) 111 + + }, 112 + + AtProtoResult::RefreshRequired => { 113 + + error!("ATProto refresh required :("); 114 + + promise.reject_error( 115 + + Error::Operation(Some("ATProto refresh required".to_owned())), 116 + + can_gc, 117 + + ) 118 + + }, 119 + + } 120 + + } 121 + +}
+11 -3
patches/components/script/dom/mod.rs.patch
··· 1 1 --- original 2 2 +++ modified 3 - @@ -284,6 +284,7 @@ 3 + @@ -215,6 +215,7 @@ 4 + pub(crate) mod abstractrange; 5 + pub(crate) mod activation; 6 + pub(crate) mod animationevent; 7 + +pub(crate) mod atproto; 8 + pub(crate) mod attr; 9 + pub(crate) mod audio; 10 + pub(crate) use self::audio::*; 11 + @@ -284,6 +285,7 @@ 4 12 pub(crate) mod elementinternals; 5 13 pub(crate) mod encoding; 6 14 pub(crate) use self::encoding::*; ··· 8 16 pub(crate) mod errorevent; 9 17 pub(crate) mod event; 10 18 pub(crate) mod eventsource; 11 - @@ -317,6 +318,7 @@ 19 + @@ -317,6 +319,7 @@ 12 20 pub(crate) mod inputevent; 13 21 pub(crate) mod intersectionobserver; 14 22 pub(crate) mod intersectionobserverentry; ··· 16 24 pub(crate) mod keyboardevent; 17 25 pub(crate) mod location; 18 26 pub(crate) mod media; 19 - @@ -344,6 +346,10 @@ 27 + @@ -344,6 +347,10 @@ 20 28 pub(crate) mod pagetransitionevent; 21 29 pub(crate) mod paintsize; 22 30 pub(crate) mod paintworkletglobalscope;
+22 -9
patches/components/script/dom/navigator.rs.patch
··· 11 11 use dom_struct::dom_struct; 12 12 use embedder_traits::{EmbedderMsg, ProtocolHandlerUpdateRegistration, RegisterOrUnregister}; 13 13 use headers::HeaderMap; 14 - @@ -20,6 +22,7 @@ 14 + @@ -20,10 +22,12 @@ 15 15 use net_traits::{FetchMetadata, NetworkError, ResourceFetchTiming}; 16 16 use regex::Regex; 17 17 use servo_base::generic_channel; ··· 19 19 use servo_config::pref; 20 20 use servo_url::ServoUrl; 21 21 22 - @@ -40,6 +43,7 @@ 22 + use crate::body::Extractable; 23 + +use crate::dom::atproto::AtProto; 24 + #[cfg(feature = "gamepad")] 25 + use crate::dom::bindings::cell::DomRefCell; 26 + use crate::dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorMethods; 27 + @@ -40,6 +44,7 @@ 23 28 use crate::dom::clipboard::Clipboard; 24 29 use crate::dom::credentialmanagement::credentialscontainer::CredentialsContainer; 25 30 use crate::dom::csp::{GlobalCspReporting, Violation}; ··· 27 32 #[cfg(feature = "gamepad")] 28 33 use crate::dom::gamepad::Gamepad; 29 34 #[cfg(feature = "gamepad")] 30 - @@ -46,13 +50,16 @@ 35 + @@ -46,13 +51,16 @@ 31 36 use crate::dom::gamepad::gamepadevent::GamepadEventType; 32 37 use crate::dom::geolocation::Geolocation; 33 38 use crate::dom::globalscope::GlobalScope; ··· 44 49 use crate::dom::serviceworkercontainer::ServiceWorkerContainer; 45 50 use crate::dom::servointernals::ServoInternals; 46 51 use crate::dom::types::UserActivation; 47 - @@ -63,6 +70,7 @@ 52 + @@ -63,6 +71,7 @@ 48 53 use crate::dom::xrsystem::XRSystem; 49 54 use crate::fetch::RequestWithGlobalScope; 50 55 use crate::network_listener::{FetchResponseListener, ResourceTimingListener, submit_timing}; ··· 52 57 use crate::script_runtime::{CanGc, JSContext}; 53 58 54 59 pub(super) fn hardware_concurrency() -> u64 { 55 - @@ -132,6 +140,8 @@ 60 + @@ -132,6 +141,9 @@ 56 61 has_gamepad_gesture: Cell<bool>, 57 62 servo_internals: MutNullableDom<ServoInternals>, 58 63 user_activation: MutNullableDom<UserActivation>, 59 64 + embedder: MutNullableDom<Embedder>, 60 65 + keyboard: MutNullableDom<Keyboard>, 66 + + at_proto: MutNullableDom<AtProto>, 61 67 } 62 68 63 69 impl Navigator { 64 - @@ -158,6 +168,8 @@ 70 + @@ -158,6 +170,9 @@ 65 71 has_gamepad_gesture: Cell::new(false), 66 72 servo_internals: Default::default(), 67 73 user_activation: Default::default(), 68 74 + embedder: Default::default(), 69 75 + keyboard: Default::default(), 76 + + at_proto: Default::default(), 70 77 } 71 78 } 72 79 73 - @@ -170,6 +182,11 @@ 80 + @@ -170,6 +185,11 @@ 74 81 self.xr.get() 75 82 } 76 83 ··· 82 89 #[cfg(feature = "gamepad")] 83 90 pub(crate) fn get_gamepad(&self, index: usize) -> Option<DomRoot<Gamepad>> { 84 91 self.gamepads.borrow().get(index).and_then(|g| g.get()) 85 - @@ -561,6 +578,18 @@ 92 + @@ -561,6 +581,18 @@ 86 93 .or_init(|| ServoInternals::new(&self.global(), CanGc::note())) 87 94 } 88 95 ··· 101 108 /// <https://html.spec.whatwg.org/multipage/#dom-navigator-registerprotocolhandler> 102 109 fn RegisterProtocolHandler(&self, scheme: DOMString, url: USVString) -> Fallible<()> { 103 110 // Step 1. Let (normalizedScheme, normalizedURLString) be the result of 104 - @@ -604,6 +633,56 @@ 111 + @@ -604,6 +636,62 @@ 105 112 self.user_activation 106 113 .or_init(|| UserActivation::new(&self.global(), can_gc)) 107 114 } ··· 154 161 + // Resolve with port1 immediately. 155 162 + promise.resolve_native(&port1, can_gc); 156 163 + Ok(promise) 164 + + } 165 + + 166 + + /// <https://webbeef.org/atproto> 167 + + fn Atproto(&self) -> DomRoot<AtProto> { 168 + + self.at_proto 169 + + .or_init(|| AtProto::new(&self.global(), CanGc::note())) 157 170 + } 158 171 } 159 172
+1 -1
patches/components/script/dom/url.rs.patch
··· 1 1 --- original 2 2 +++ modified 3 - @@ -50,7 +50,7 @@ 3 + @@ -51,7 +51,7 @@ 4 4 } 5 5 } 6 6
+15 -3
patches/components/script_bindings/codegen/Bindings.conf.patch
··· 1 1 --- original 2 2 +++ modified 3 - @@ -288,6 +288,11 @@ 3 + @@ -28,6 +28,11 @@ 4 + 'weakReferenceable': True, 5 + }, 6 + 7 + +'AtProto': { 8 + + 'inRealms': ['Login', 'Logout', 'Current'], 9 + + 'canGc': ['Login', 'Logout', 'Current'], 10 + +}, 11 + + 12 + 'Attr': { 13 + 'cx':['SetValue'], 14 + }, 15 + @@ -288,6 +293,11 @@ 4 16 'cx': ['CheckValidity', 'ReportValidity'], 5 17 }, 6 18 ··· 12 24 'EventSource': { 13 25 'weakReferenceable': True, 14 26 }, 15 - @@ -659,8 +664,8 @@ 27 + @@ -659,8 +669,8 @@ 16 28 }, 17 29 18 30 'Navigator': { ··· 23 35 }, 24 36 25 37 'Node': { 26 - @@ -693,6 +698,11 @@ 38 + @@ -693,6 +703,11 @@ 27 39 'cx': ['CreateLinearGradient', 'CreatePattern', 'CreateRadialGradient', 'GetTransform'], 28 40 }, 29 41
+32
patches/components/script_bindings/webidls/AtProto.webidl.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -0,0 +1,29 @@ 4 + +/* This Source Code Form is subject to the terms of the Mozilla Public 5 + + * License, v. 2.0. If a copy of the MPL was not distributed with this 6 + + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 7 + + 8 + +// TODO: expose some methods only to internal pages, and figure out the privacy story. 9 + + 10 + +dictionary AtProtoSession { 11 + + required USVString did; 12 + + required USVString handle; 13 + +}; 14 + + 15 + +[Exposed=Window, 16 + +Func="Embedder::is_allowed_to_embed"] 17 + +interface AtProto { 18 + + // Tries to login with the submitted credentials. 19 + + // Resolves with the new session if successful, rejects otherwise. 20 + + Promise<AtProtoSession> login(USVString handle, USVString password); 21 + + 22 + + // Revokes the current session if it exists. 23 + + Promise<undefined> logout(); 24 + + 25 + + // Resolves with the logged in user DID if any, rejects otherwise. 26 + + Promise<AtProtoSession> current(); 27 + +}; 28 + + 29 + +partial interface Navigator { 30 + + [Func="Embedder::is_allowed_to_embed"] 31 + + readonly attribute AtProto atproto; 32 + +};
+39 -7
patches/components/servo/servo.rs.patch
··· 1 1 --- original 2 2 +++ modified 3 - @@ -63,7 +63,7 @@ 3 + @@ -4,6 +4,7 @@ 4 + 5 + use std::cell::{Cell, Ref, RefCell, RefMut}; 6 + use std::cmp::max; 7 + +use std::path::PathBuf; 8 + use std::rc::{Rc, Weak}; 9 + use std::sync::Arc; 10 + use std::time::Duration; 11 + @@ -63,7 +64,7 @@ 4 12 use servo_bluetooth_traits::BluetoothRequest; 5 13 use servo_config::opts::Opts; 6 14 use servo_config::prefs::{PrefValue, Preferences}; ··· 9 17 use servo_geometry::{ 10 18 DeviceIndependentIntRect, convert_rect_to_css_pixel, convert_size_to_css_pixel, 11 19 }; 12 - @@ -229,9 +229,7 @@ 20 + @@ -229,9 +230,7 @@ 13 21 } 14 22 15 23 if self.constellation_proxy.disconnected() { ··· 20 28 } 21 29 22 30 self.paint.borrow_mut().perform_updates(); 23 - @@ -283,10 +281,39 @@ 31 + @@ -283,10 +282,39 @@ 24 32 25 33 fn handle_delegate_errors(&self) { 26 34 while let Some(error) = self.servo_errors.try_recv() { ··· 61 69 fn clean_up_destroyed_webview_handles(&self) { 62 70 // Remove any webview handles that have been destroyed and would not be upgradable. 63 71 // Note that `retain` is O(capacity) because it visits empty buckets, so it may be worth 64 - @@ -444,6 +471,11 @@ 72 + @@ -444,6 +472,11 @@ 65 73 webview.request_create_new(response_sender); 66 74 } 67 75 }, ··· 73 81 EmbedderMsg::WebViewClosed(webview_id) => { 74 82 if let Some(webview) = self.get_webview_handle(webview_id) { 75 83 webview.delegate().notify_closed(webview); 76 - @@ -597,10 +629,7 @@ 84 + @@ -597,10 +630,7 @@ 77 85 .delegate 78 86 .borrow() 79 87 .notify_devtools_server_started(port, token), ··· 85 93 }, 86 94 EmbedderMsg::RequestDevtoolsConnection(response_sender) => { 87 95 self.delegate 88 - @@ -725,6 +754,47 @@ 96 + @@ -725,6 +755,47 @@ 89 97 .notify_accessibility_tree_update(webview, tree_update); 90 98 } 91 99 }, ··· 133 141 } 134 142 } 135 143 } 136 - @@ -959,6 +1029,14 @@ 144 + @@ -863,6 +934,7 @@ 145 + async_runtime, 146 + public_storage_threads.clone(), 147 + private_storage_threads.clone(), 148 + + opts.config_dir.clone(), 149 + ); 150 + 151 + if opts::get().multiprocess { 152 + @@ -959,6 +1031,14 @@ 137 153 self.0.site_data_manager.borrow() 138 154 } 139 155 ··· 148 164 pub(crate) fn paint<'a>(&'a self) -> Ref<'a, Paint> { 149 165 self.0.paint.borrow() 150 166 } 167 + @@ -1060,6 +1140,7 @@ 168 + async_runtime: Box<dyn net_traits::AsyncRuntime>, 169 + public_storage_threads: StorageThreads, 170 + private_storage_threads: StorageThreads, 171 + + config_dir: Option<PathBuf>, 172 + ) { 173 + // Global configuration options, parsed from the command line. 174 + let opts = opts::get(); 175 + @@ -1101,6 +1182,7 @@ 176 + wgpu_image_map: paint.webgpu_image_map(), 177 + async_runtime, 178 + privileged_urls, 179 + + config_dir, 180 + }; 181 + 182 + let layout_factory = Arc::new(LayoutFactoryImpl());
+86 -4
patches/components/shared/constellation/from_script_message.rs.patch
··· 86 86 /// Specifies the information required to load an iframe. 87 87 #[derive(Debug, Deserialize, Serialize)] 88 88 pub struct IFrameLoadInfo { 89 - @@ -585,6 +623,10 @@ 89 + @@ -541,6 +579,79 @@ 90 + NoLongerActive, 91 + } 92 + 93 + +#[derive(Deserialize, Serialize)] 94 + +pub enum AtProtoRequest { 95 + + /// User, Password 96 + + Login(String, String), 97 + + Logout, 98 + + Current, 99 + +} 100 + + 101 + +/// Data returned by com.atproto.server.createSession xrpc calls. 102 + +#[derive(Clone, Debug, Deserialize, Serialize)] 103 + +#[serde(rename_all = "camelCase")] 104 + +pub struct AtProtoNewSession { 105 + + pub access_jwt: String, 106 + + pub refresh_jwt: String, 107 + + pub handle: String, 108 + + pub did: String, 109 + + pub email: String, 110 + + pub email_confirmed: bool, 111 + + pub email_auth_factor: bool, 112 + + pub active: bool, 113 + + pub status: Option<String>, 114 + +} 115 + + 116 + +/// Error data for atproto calls that return a 400 error. 117 + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] 118 + +pub enum AtProtoErrorKind { 119 + + InvalidRequest, 120 + + ExpiredToken, 121 + + InvalidToken, 122 + + AccountTakedown, 123 + + AuthFactorTokenRequired, 124 + +} 125 + + 126 + +#[derive(Clone, Debug, Deserialize, Serialize)] 127 + +pub struct AtProtoError { 128 + + pub error: AtProtoErrorKind, 129 + + pub message: String, 130 + +} 131 + + 132 + +/// Data returned by com.atproto.server.getSession xrpc calls. 133 + +#[derive(Clone, Debug, Deserialize, Serialize)] 134 + +#[serde(rename_all = "camelCase")] 135 + +pub struct AtProtoCurrentSession { 136 + + pub handle: String, 137 + + pub did: String, 138 + + pub email: String, 139 + + pub email_confirmed: bool, 140 + + pub email_auth_factor: bool, 141 + + pub active: bool, 142 + + pub status: Option<String>, 143 + +} 144 + + 145 + +/// Data returned by com.atproto.server.refreshSession xrpc calls. 146 + +#[derive(Clone, Debug, Deserialize, Serialize)] 147 + +#[serde(rename_all = "camelCase")] 148 + +pub struct AtProtoRefreshSession { 149 + + pub access_jwt: String, 150 + + pub refresh_jwt: String, 151 + + pub handle: String, 152 + + pub did: String, 153 + + pub active: bool, 154 + + pub status: Option<String>, 155 + +} 156 + + 157 + +#[derive(Debug, Deserialize, Serialize)] 158 + +pub enum AtProtoResult { 159 + + NewSession(AtProtoNewSession, ServoUrl), // (session, endpoint_url) 160 + + CurrentSession(AtProtoCurrentSession), 161 + + RefreshRequired, 162 + + Logout, // For Logout success 163 + + Error, 164 + +} 165 + + 166 + /// Messages from the script to the constellation. 167 + #[derive(Deserialize, IntoStaticStr, Serialize)] 168 + pub enum ScriptToConstellationMessage { 169 + @@ -585,6 +696,10 @@ 90 170 NewBroadcastChannelNameInRouter(BroadcastChannelRouterId, String, ImmutableOrigin), 91 171 /// A global stopped managing broadcast channels for a given channel-name. 92 172 RemoveBroadcastChannelNameInRouter(BroadcastChannelRouterId, String, ImmutableOrigin), ··· 97 177 /// Broadcast a message to all same-origin broadcast channels, 98 178 /// excluding the source of the broadcast. 99 179 ScheduleBroadcast(BroadcastChannelRouterId, BroadcastChannelMsg), 100 - @@ -597,6 +639,9 @@ 180 + @@ -597,6 +712,9 @@ 101 181 Option<String>, 102 182 Option<String>, 103 183 ), ··· 107 187 /// Indicates whether this pipeline is currently running animations. 108 188 ChangeRunningAnimationsState(AnimationState), 109 189 /// Requests that a new 2D canvas thread be created. (This is done in the constellation because 110 - @@ -677,6 +722,10 @@ 190 + @@ -677,6 +795,10 @@ 111 191 ScriptNewIFrame(IFrameLoadInfoWithData), 112 192 /// Script has opened a new auxiliary browsing context. 113 193 CreateAuxiliaryWebView(AuxiliaryWebViewCreationRequest), ··· 118 198 /// Mark a new document as active 119 199 ActivateDocument, 120 200 /// Set the document state for a pipeline (used by screenshot / reftests) 121 - @@ -726,6 +775,77 @@ 201 + @@ -726,6 +848,79 @@ 122 202 RespondToScreenshotReadinessRequest(ScreenshotReadinessResponse), 123 203 /// Request the constellation to force garbage collection in all `ScriptThread`'s. 124 204 TriggerGarbageCollection, ··· 193 273 + /// Response to a DispatchPeerStream — whether the peer stream was accepted or denied. 194 274 + /// Args: stream_id, from_peer_id, accepted. 195 275 + PeerStreamResponse(String, String, bool), 276 + + /// ATProto api message. 277 + + AtProto(AtProtoRequest, GenericCallback<AtProtoResult>), 196 278 } 197 279 198 280 impl fmt::Debug for ScriptToConstellationMessage {
+18
patches/components/shared/net/Cargo.toml.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -40,12 +40,15 @@ 4 + rustc-hash = { workspace = true } 5 + rustls-pki-types = { workspace = true } 6 + serde = { workspace = true } 7 + +serde_json = { workspace = true } 8 + servo-base = { workspace = true } 9 + servo-config = { workspace = true } 10 + servo-url = { workspace = true } 11 + servo_arc = { workspace = true } 12 + +sync_wrapper = "1.0" 13 + sys-locale = "0.3" 14 + tokio = { workspace = true } 15 + +tower = { workspace = true } 16 + url = { workspace = true } 17 + uuid = { workspace = true } 18 + webrender_api = { workspace = true }
+243
patches/components/shared/net/fetch/utils.rs.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -0,0 +1,240 @@ 4 + +// SPDX-License-Identifier: AGPL-3.0-or-later 5 + + 6 + +use std::fmt::Debug; 7 + + 8 + +use content_security_policy::{self as csp}; 9 + +use http::{Method, StatusCode}; 10 + +use parking_lot::Mutex; 11 + +use serde::de::DeserializeOwned; 12 + +use serde_json::json; 13 + +use servo_arc::Arc; 14 + +use servo_url::ServoUrl; 15 + +use sync_wrapper::SyncWrapper; 16 + + 17 + +use crate::header::CONTENT_TYPE; 18 + +use crate::http_status::HttpStatus; 19 + +use crate::request::{CredentialsMode, Referrer, Request, create_request_body_with_content}; 20 + +use crate::response::{Response, ResponseBody}; 21 + +use crate::{ 22 + + CoreResourceThread, FetchMetadata, FetchResponseMsg, FetchTaskTarget, HeaderMap, HeaderValue, 23 + + RequestBuilder, ResourceFetchTiming, ResourceTimingType, fetch_async, 24 + +}; 25 + + 26 + +/// A FetchTaskTarget that doesn't do anything. 27 + +#[derive(Default)] 28 + +pub struct SimpleFetchTarget { 29 + + pub body: Vec<u8>, 30 + +} 31 + + 32 + +impl FetchTaskTarget for SimpleFetchTarget { 33 + + fn process_request_body(&mut self, _: &Request) {} 34 + + fn process_response(&mut self, _: &Request, _: &Response) {} 35 + + fn process_response_chunk(&mut self, _: &Request, payload: Vec<u8>) { 36 + + self.body.extend(payload); 37 + + } 38 + + fn process_response_eof(&mut self, _: &Request, response: &Response) { 39 + + // TODO: figure out a way to not clone. 40 + + *response.body.lock() = ResponseBody::Done(self.body.clone()); 41 + + } 42 + + fn process_csp_violations(&mut self, _: &Request, _: Vec<csp::Violation>) {} 43 + +} 44 + + 45 + +/// Rewrites a response to replace the url. 46 + +pub fn rewrite_response_url(url: ServoUrl, inner_url: ServoUrl, fetched: Response) -> Response { 47 + + let mut response = Response::new( 48 + + url.clone(), 49 + + ResourceFetchTiming::new(ResourceTimingType::Resource), 50 + + ); 51 + + response.response_type = fetched.response_type; 52 + + response.termination_reason = fetched.termination_reason; 53 + + response.url_list = fetched 54 + + .url_list 55 + + .into_iter() 56 + + .map(|list_url| { 57 + + if list_url == inner_url { 58 + + url.clone() 59 + + } else { 60 + + list_url.clone() 61 + + } 62 + + }) 63 + + .collect(); 64 + + response.status = fetched.status; 65 + + response.headers = fetched.headers; 66 + + response.body = fetched.body; 67 + + response.cache_state = fetched.cache_state; 68 + + response.https_state = fetched.https_state; 69 + + response.referrer = fetched.referrer; 70 + + response.referrer_policy = fetched.referrer_policy; 71 + + response.cors_exposed_header_name_list = fetched.cors_exposed_header_name_list; 72 + + response.location_url = fetched.location_url; 73 + + response.internal_response = fetched.internal_response.map(|internal_response| { 74 + + Box::new(rewrite_response_url(url, inner_url, *internal_response)) 75 + + }); 76 + + response.return_internal = fetched.return_internal; 77 + + response.aborted = fetched.aborted; 78 + + response.resource_timing = fetched.resource_timing; 79 + + response.range_requested = fetched.range_requested; 80 + + response 81 + +} 82 + + 83 + +/// Creates a http response with the given status code and the message as the body. 84 + +pub fn http_response(url: ServoUrl, status: StatusCode, message: &str) -> Response { 85 + + let mut response = Response::new(url, ResourceFetchTiming::new(ResourceTimingType::Resource)); 86 + + 87 + + response.status = HttpStatus::new(status, vec![]); 88 + + let body = ResponseBody::Done(message.as_bytes().to_vec()); 89 + + response.body = Arc::new(Mutex::new(body)); 90 + + 91 + + response 92 + +} 93 + + 94 + +// A simple fetch response. 95 + +#[derive(Default, Clone)] 96 + +pub struct FetchResponse { 97 + + pub metadata: Option<FetchMetadata>, 98 + + pub data: Arc<Vec<u8>>, 99 + +} 100 + + 101 + +// pub type BoxedResponseCallback = Box<dyn FnMut(FetchResponse) + Send + 'static>; 102 + + 103 + +// pub fn fetch_url( 104 + +// url: ServoUrl, 105 + +// params: &[(&str, &str)], 106 + +// resource_thread: &CoreResourceThread, 107 + +// mut callback: BoxedResponseCallback, 108 + +// ) { 109 + +// let mut full_url = url.clone(); 110 + +// { 111 + +// let mut url_params = full_url.as_mut_url().query_pairs_mut(); 112 + +// for param in params { 113 + +// url_params.append_pair(param.0, param.1); 114 + +// } 115 + +// } 116 + + 117 + +// let request = RequestBuilder::new(None, full_url, Referrer::NoReferrer); 118 + + 119 + +// let mut state: FetchResponse = Default::default(); 120 + + 121 + +// fetch_async( 122 + +// resource_thread, 123 + +// request, 124 + +// None, 125 + +// Box::new(move |response_message| match response_message { 126 + +// FetchResponseMsg::ProcessResponseChunk(_id, chunk) => { 127 + +// Arc::get_mut(&mut state.data).unwrap().extend(chunk); 128 + +// }, 129 + +// FetchResponseMsg::ProcessResponse(_id, Ok(metadata)) => state.metadata = Some(metadata), 130 + +// FetchResponseMsg::ProcessResponseEOF(_id, _timing) => { 131 + +// callback(state.clone()); 132 + +// }, 133 + +// _ => {}, 134 + +// }), 135 + +// ); 136 + +// } 137 + + 138 + +#[derive(Debug)] 139 + +pub enum FetchJsonError<T: DeserializeOwned + Debug> { 140 + + Json(String), 141 + + NoMetadata, 142 + + NoContent, 143 + + Other(T), 144 + + Network, 145 + +} 146 + + 147 + +pub async fn fetch_json<S: DeserializeOwned, E: DeserializeOwned + Debug>( 148 + + url: ServoUrl, 149 + + params: &[(&str, &str)], 150 + + resource_thread: SyncWrapper<CoreResourceThread>, 151 + + method: Method, 152 + + headers: Option<HeaderMap>, 153 + + requires_auth: bool, 154 + +) -> Result<S, FetchJsonError<E>> { 155 + + let mut full_url = url.clone(); 156 + + 157 + + if method == Method::GET && !params.is_empty() { 158 + + let mut url_params = full_url.as_mut_url().query_pairs_mut(); 159 + + for param in params { 160 + + url_params.append_pair(param.0, param.1); 161 + + } 162 + + } 163 + + 164 + + let mut headers = headers.unwrap_or_default(); 165 + + 166 + + let body = if method == Method::POST && !params.is_empty() { 167 + + headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); 168 + + // Create a JSON encoded body. 169 + + let mut object = serde_json::Map::new(); 170 + + for param in params { 171 + + object.insert(param.0.into(), json!(param.1)); 172 + + } 173 + + let value = serde_json::Value::Object(object); 174 + + Some(create_request_body_with_content( 175 + + &serde_json::to_string(&value).unwrap_or("{}".to_owned()), 176 + + )) 177 + + } else { 178 + + None 179 + + }; 180 + + 181 + + let mut builder = RequestBuilder::new(None, full_url.clone(), Referrer::NoReferrer) 182 + + .method(method) 183 + + .headers(headers) 184 + + .origin(full_url.origin()) 185 + + .body(body); 186 + + 187 + + if requires_auth { 188 + + builder = builder.credentials_mode(CredentialsMode::Include); 189 + + } 190 + + 191 + + let mut state: FetchResponse = Default::default(); 192 + + 193 + + let (tx, rx) = tokio::sync::oneshot::channel(); 194 + + 195 + + let mut tx = Some(tx); 196 + + 197 + + fetch_async( 198 + + &resource_thread.into_inner(), 199 + + builder, 200 + + None, 201 + + Box::new(move |response_message| match response_message { 202 + + FetchResponseMsg::ProcessResponseChunk(_id, chunk) => { 203 + + Arc::get_mut(&mut state.data).unwrap().extend(chunk.0) 204 + + }, 205 + + FetchResponseMsg::ProcessResponse(_id, Ok(metadata)) => state.metadata = Some(metadata), 206 + + FetchResponseMsg::ProcessResponseEOF(_id, _result, _timing) => { 207 + + let _ = tx.take().unwrap().send(state.clone()); 208 + + }, 209 + + _ => {}, 210 + + }), 211 + + ); 212 + + 213 + + let response = rx.await.unwrap(); 214 + + 215 + + let Some(metadata) = response.metadata else { 216 + + return Err(FetchJsonError::NoMetadata); 217 + + }; 218 + + 219 + + let status = &metadata.metadata().status; 220 + + 221 + + if status.is_success() { 222 + + if response.data.is_empty() { 223 + + Err(FetchJsonError::NoContent) 224 + + } else { 225 + + serde_json::from_slice::<S>(&response.data) 226 + + .map_err(|err| FetchJsonError::Json(err.to_string())) 227 + + } 228 + + } else { 229 + + log::error!( 230 + + "Fetch Error {}: {}", 231 + + status.raw_code(), 232 + + String::from_utf8_lossy(&response.data) 233 + + ); 234 + + if status.raw_code() == 400 { 235 + + match serde_json::from_slice::<E>(&response.data) { 236 + + Ok(err) => Err(FetchJsonError::Other(err)), 237 + + Err(err) => Err(FetchJsonError::Json(err.to_string())), 238 + + } 239 + + } else { 240 + + Err(FetchJsonError::Network) 241 + + } 242 + + } 243 + +}
+101
patches/components/shared/net/lib.rs.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -5,6 +5,9 @@ 4 + #![deny(unsafe_code)] 5 + 6 + use std::fmt::{self, Debug, Display}; 7 + +use std::fs::File; 8 + +use std::io::Read; 9 + +use std::path::Path; 10 + use std::sync::{LazyLock, OnceLock}; 11 + use std::thread::{self, JoinHandle}; 12 + 13 + @@ -17,6 +20,7 @@ 14 + use hyper_util::client::legacy::Error as HyperError; 15 + use ipc_channel::ipc::{self, IpcSender}; 16 + use ipc_channel::router::ROUTER; 17 + +use log::warn; 18 + use malloc_size_of::malloc_size_of_is_0; 19 + use malloc_size_of_derive::MallocSizeOf; 20 + use mime::Mime; 21 + @@ -59,6 +63,7 @@ 22 + /// An implementation of the [Fetch specification](https://fetch.spec.whatwg.org/) 23 + pub mod fetch { 24 + pub mod headers; 25 + + pub mod utils; 26 + } 27 + 28 + /// A loading context, for context-specific sniffing, as defined in 29 + @@ -596,6 +601,61 @@ 30 + Prefetch, 31 + } 32 + 33 + +#[derive(Clone, Debug, Deserialize, Serialize)] 34 + +pub struct BasicAuthCacheEntry { 35 + + pub user_name: String, 36 + + pub password: String, 37 + +} 38 + + 39 + +#[derive(Clone, Debug, Deserialize, Serialize)] 40 + +pub struct BearerAuthCacheEntry { 41 + + pub token: String, 42 + +} 43 + + 44 + +#[derive(Clone, Debug, Deserialize, Serialize)] 45 + +pub enum AuthCacheEntry { 46 + + Basic(BasicAuthCacheEntry), 47 + + Bearer(BearerAuthCacheEntry), 48 + +} 49 + + 50 + +#[derive(Clone, Debug, Deserialize, Serialize)] 51 + +pub struct AtProtoSessionState { 52 + + pub endpoint: ServoUrl, 53 + + pub access_jwt: String, 54 + + pub refresh_jwt: String, 55 + +} 56 + + 57 + +static ATPROTO_SESSION_FILE: &str = "atproto_session.json"; 58 + + 59 + +impl AtProtoSessionState { 60 + + pub fn load(config_dir: &Path) -> Option<Self> { 61 + + let path = config_dir.join(ATPROTO_SESSION_FILE); 62 + + 63 + + let Ok(mut file) = File::open(&path) else { 64 + + return None; 65 + + }; 66 + + 67 + + let mut string_buffer: String = String::new(); 68 + + if file.read_to_string(&mut string_buffer).is_err() { 69 + + return None; 70 + + } 71 + + 72 + + serde_json::from_str(&string_buffer).ok() 73 + + } 74 + + 75 + + pub fn save(&self, config_dir: &Path) { 76 + + servo_base::write_json_to_file(&self, config_dir, ATPROTO_SESSION_FILE); 77 + + } 78 + + 79 + + // Delete the persistent storage. 80 + + pub fn reset(config_dir: &Path) { 81 + + let path = config_dir.join(ATPROTO_SESSION_FILE); 82 + + if let Err(err) = std::fs::remove_file(path) { 83 + + warn!("Failed to remove {ATPROTO_SESSION_FILE}: {err}"); 84 + + } 85 + + } 86 + +} 87 + + 88 + #[derive(Debug, Deserialize, Serialize)] 89 + pub enum CoreResourceMsg { 90 + Fetch(RequestBuilder, FetchChannels), 91 + @@ -655,6 +715,10 @@ 92 + /// and exit 93 + Exit(GenericOneshotSender<()>), 94 + CollectMemoryReport(ReportsChan), 95 + + /// Update the ATProto session state. 96 + + UpdateAtProtoSession(Option<AtProtoSessionState>), 97 + + /// Retrieve the current ATProto session state. 98 + + GetAtProtoSession(IpcSender<Option<AtProtoSessionState>>), 99 + } 100 + 101 + #[derive(Clone, Debug, Deserialize, Serialize)]
+22
patches/components/url/lib.rs.patch
··· 1 + --- original 2 + +++ modified 3 + @@ -88,6 +88,19 @@ 4 + } 5 + 6 + pub fn origin(&self) -> ImmutableOrigin { 7 + + // We want trusted:// urls to have a tuple origin similar to https:// ones. 8 + + // TODO: maybe fork the url crate instead... 9 + + if self.0.scheme() == "trusted" { 10 + + use url::Origin; 11 + + 12 + + let url_str = self.as_str(); 13 + + let new_url = Url::parse(&url_str.replace("trusted://", "https://")).unwrap(); 14 + + let origin = match new_url.origin() { 15 + + Origin::Tuple(_scheme, host, port) => Origin::Tuple("trusted".into(), host, port), 16 + + Origin::Opaque(val) => Origin::Opaque(val), 17 + + }; 18 + + return ImmutableOrigin::new(origin); 19 + + } 20 + ImmutableOrigin::new(self.0.origin()) 21 + } 22 +
+3 -1
ui/shared/search/utils.js
··· 25 25 export function normalizeUrl(url) { 26 26 if ( 27 27 !url.startsWith("about:") && 28 + !url.startsWith("at:") && 28 29 !url.startsWith("data:") && 29 30 !url.startsWith("file://") && 30 31 !url.startsWith("http://") && 31 - !url.startsWith("https://") 32 + !url.startsWith("https://") && 33 + !url.startsWith("trusted:") 32 34 ) { 33 35 return "https://" + url; 34 36 }
+56
ui/system/atproto/browser.css
··· 1 + /* SPDX-License-Identifier: AGPL-3.0-or-later */ 2 + 3 + html { 4 + font-family: var(--font-family-base); 5 + color: var(--color-text); 6 + } 7 + 8 + img.avatar { 9 + max-width: 256px; 10 + } 11 + 12 + .hidden { 13 + display: none; 14 + } 15 + 16 + table, 17 + input, 18 + button { 19 + border-radius: var(--radius-sm); 20 + } 21 + 22 + header { 23 + display: flex; 24 + gap: var(--spacing-sm); 25 + align-items: center; 26 + border-bottom: 2px solid var(--color-border); 27 + padding-bottom: var(--spacing-sm); 28 + margin-bottom: var(--spacing-sm); 29 + } 30 + 31 + table { 32 + border: 2px solid var(--color-border); 33 + margin: var(--spacing-sm); 34 + } 35 + 36 + table td { 37 + padding-left: var(--spacing-xs); 38 + padding-right: var(--spacing-xs); 39 + } 40 + 41 + table tr:nth-child(odd) { 42 + background-color: var(--color-menu-item-hover); 43 + } 44 + 45 + details button { 46 + margin-left: 2em; 47 + } 48 + 49 + a { 50 + text-decoration: underline; 51 + cursor: pointer; 52 + } 53 + 54 + a.external::after { 55 + content: "🔗"; 56 + }
+25
ui/system/atproto/browser.html
··· 1 + <!doctype html> 2 + <!-- SPDX-License-Identifier: AGPL-3.0-or-later --> 3 + <html> 4 + <head> 5 + <title>at:// browser</title> 6 + <meta charset="utf-8" /> 7 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 8 + <link rel="stylesheet" href="//theme.localhost:8888/index.css" /> 9 + <link rel="stylesheet" href="//shared.localhost:8888/fonts/fonts.css" /> 10 + <link rel="stylesheet" href="browser.css" /> 11 + <script src="browser.js"></script> 12 + </head> 13 + <body> 14 + <h2>at:// browser</h2> 15 + <header> 16 + User: 17 + <input 18 + id="handle" 19 + value="me.webbeef.org" 20 + placeholder="Handle or DID" 21 + /><button id="start-button">Start</button> 22 + </header> 23 + <main id="result"></main> 24 + </body> 25 + </html>
+172
ui/system/atproto/browser.js
··· 1 + // SPDX-License-Identifier: AGPL-3.0-or-later 2 + 3 + // Basic at:// data browser. 4 + 5 + document.addEventListener("DOMContentLoaded", () => { 6 + window["start-button"].onclick = () => { 7 + let handle = window.handle.value.trim(); 8 + console.log(`Handle is ${handle}`); 9 + if (handle.length > 0) { 10 + location.hash = `#${handle}`; 11 + } 12 + }; 13 + 14 + window.onhashchange = (event) => { 15 + let url = location.hash?.substring(1); 16 + window.handle.value = currentHandle(); 17 + if (url) { 18 + atFetch(`at://${url}`); 19 + } 20 + }; 21 + 22 + window.onhashchange(); 23 + }); 24 + 25 + function router(data) { 26 + window["result"].innerHTML = ""; 27 + 28 + if (data.collections && data.collections.length != 0) { 29 + return displayRoot(data); 30 + } 31 + 32 + if (data.records && data.records.length != 0) { 33 + data.records.forEach(displayRecord); 34 + return; 35 + } 36 + 37 + console.log(data); 38 + } 39 + 40 + async function atFetch(url) { 41 + console.log(`Fetching ${url}`); 42 + try { 43 + let response = await fetch(url); 44 + let data = await response.json(); 45 + router(data); 46 + } catch (e) { 47 + console.error(e); 48 + } 49 + } 50 + 51 + function currentHandle() { 52 + let end = location.hash.indexOf("/"); 53 + console.log(`==== end=${end} hash=${location.hash}`); 54 + if (end == -1) { 55 + return decodeURIComponent(location.hash.substring(1)); 56 + } 57 + return location.hash.substring(1, end); 58 + } 59 + 60 + function buildImage(record, kind) { 61 + if (!record.mimeType.startsWith("image/") || !record.ref?.$link) { 62 + return null; 63 + } 64 + 65 + let img = document.createElement("img"); 66 + img.classList.add(kind); 67 + img.src = `at://${currentHandle()}/com.atproto.sync.blob/${record.ref.$link}`; 68 + return img; 69 + } 70 + 71 + function buildSubjectLink(subject) { 72 + let handle = encodeURIComponent(subject); 73 + let anchor = document.createElement("a"); 74 + anchor.textContent = subject; 75 + anchor.onclick = () => { 76 + location.hash = `#${handle}`; 77 + }; 78 + return anchor; 79 + } 80 + 81 + // Makes sure the authority part is properly encoded. 82 + function sanitizeAtURI(uri) { 83 + if (!uri.startsWith("at://")) { 84 + return uri; 85 + } 86 + 87 + let comps = uri.substring(5).split("/"); 88 + comps[0] = encodeURIComponent(comps[0]); 89 + return "at://" + comps.join("/"); 90 + } 91 + 92 + // Displays a single record. 93 + // TODO: instanciate custom elements based on the value's type. 94 + function displayRecord(record) { 95 + let container = document.createElement("details"); 96 + let summary = document.createElement("summary"); 97 + summary.textContent = record.value.$type; 98 + let table = document.createElement("table"); 99 + 100 + // Iterate over each property. 101 + for (let prop in record.value) { 102 + if (prop == "$type") { 103 + continue; 104 + } 105 + 106 + let value = record.value[prop]; 107 + let row = document.createElement("tr"); 108 + let colProp = document.createElement("td"); 109 + let colValue = document.createElement("td"); 110 + row.append(colProp); 111 + colProp.textContent = prop; 112 + row.append(colValue); 113 + 114 + table.append(row); 115 + 116 + if (prop == "avatar" || prop == "banner") { 117 + let node = buildImage(value, prop); 118 + if (node) { 119 + colValue.append(node); 120 + } 121 + continue; 122 + } 123 + 124 + if (prop == "subject" && typeof value == "string") { 125 + colValue.append(buildSubjectLink(value)); 126 + } else if (!value.startsWith("did:") && URL.canParse(value)) { 127 + let anchor = document.createElement("a"); 128 + anchor.setAttribute("href", value); 129 + anchor.setAttribute("target", "_blank"); 130 + anchor.className = "external"; 131 + anchor.textContent = value; 132 + colValue.append(anchor); 133 + } else { 134 + colValue.textContent = value; 135 + if (colValue.textContent == "[object Object]") { 136 + colValue.textContent = JSON.stringify(value); 137 + } 138 + } 139 + } 140 + 141 + container.append(summary); 142 + container.append(table); 143 + let deleteButton = document.createElement("button"); 144 + deleteButton.onclick = async () => { 145 + try { 146 + let uri = sanitizeAtURI(record.uri); 147 + let response = await fetch(uri, { method: "DELETE" }); 148 + console.log(await response.text()); 149 + location.reload(); 150 + } catch (e) { 151 + console.error(e); 152 + } 153 + }; 154 + deleteButton.textContent = "Delete Record"; 155 + container.append(deleteButton); 156 + window["result"].append(container); 157 + } 158 + 159 + // Displays the list of collections attached to a user. 160 + function displayRoot(data) { 161 + let result = window["result"]; 162 + let list = document.createElement("ul"); 163 + data.collections.forEach((collection) => { 164 + let item = document.createElement("li"); 165 + let anchor = document.createElement("a"); 166 + anchor.setAttribute("href", `#${data.handle}/${collection}`); 167 + anchor.textContent = collection; 168 + item.append(anchor); 169 + list.append(item); 170 + }); 171 + result.append(list); 172 + }
+51 -35
ui/system/index.js
··· 236 236 } 237 237 } 238 238 239 - function openSettingsView() { 240 - const settingsView = new WebView( 241 - "http://settings.localhost:8888/index.html", 242 - "Settings", 243 - {}, 244 - ); 239 + function openView(url, title) { 240 + const settingsView = new WebView(url, title, {}); 245 241 layoutManager.addWebView(settingsView); 246 242 } 247 243 ··· 798 794 }; 799 795 } 800 796 801 - systemMenu.addEventListener("menu-action", (e) => { 802 - switch (e.detail.action) { 797 + systemMenu.addEventListener("menu-action", (event) => { 798 + switch (event.detail.action) { 803 799 case "new-tab": 804 800 createNewView(); 805 801 break; ··· 818 814 layoutManager.toggleOverview(); 819 815 break; 820 816 case "settings": 821 - openSettingsView(); 817 + openView("http://settings.localhost:8888/index.html", "Settings"); 818 + break; 819 + case "atproto": 820 + openView( 821 + "http://system.localhost:8888/atproto/browser.html", 822 + "Settings", 823 + ); 822 824 break; 823 825 case "reload-ui": 824 826 window.location.reload(); ··· 922 924 const mediaChannel = new BroadcastChannel("beaver-media-control"); 923 925 let localDeviceName = null; 924 926 let localSessionId = null; 925 - let localMediaState = { title: "", artist: "", album: "", playbackState: "none", duration: 0, position: 0 }; 927 + let localMediaState = { 928 + title: "", 929 + artist: "", 930 + album: "", 931 + playbackState: "none", 932 + duration: 0, 933 + position: 0, 934 + }; 926 935 const remoteMediaControls = new Map(); // sessionId -> media-control element 927 936 928 937 mediaChannel.onmessage = (e) => { ··· 1108 1117 // Clear session ID when playback stops 1109 1118 if (eventType === "playbackstate" && e.detail.playbackState === "none") { 1110 1119 localSessionId = null; 1111 - localMediaState = { title: "", artist: "", album: "", playbackState: "none", duration: 0, position: 0 }; 1120 + localMediaState = { 1121 + title: "", 1122 + artist: "", 1123 + album: "", 1124 + playbackState: "none", 1125 + duration: 0, 1126 + position: 0, 1127 + }; 1112 1128 } 1113 1129 }); 1114 1130 ··· 1118 1134 } 1119 1135 const entry = layoutManager.webviews.get(mediaSessionWebviewId); 1120 1136 if (entry) { 1121 - console.log(`[MediaControl] Action "${event.detail.action}" triggered for webview ${mediaSessionWebviewId}`); 1137 + console.log( 1138 + `[MediaControl] Action "${event.detail.action}" triggered for webview ${mediaSessionWebviewId}`, 1139 + ); 1122 1140 entry.webview.ensureIframe(); 1123 1141 entry.webview.iframe.mediaSessionAction(event.detail.action); 1124 1142 } ··· 1160 1178 }); 1161 1179 1162 1180 // Send current page URL to a peer when "Open in <peer>" is selected. 1163 - document 1164 - .getElementById("root") 1165 - .addEventListener("p2p-open-in", async (e) => { 1166 - const { peerId, url } = e.detail; 1167 - if (!peerId || !url) return; 1181 + document.getElementById("root").addEventListener("p2p-open-in", async (e) => { 1182 + const { peerId, url } = e.detail; 1183 + if (!peerId || !url) return; 1168 1184 1169 - // Try both system app URLs since we don't know the remote platform. 1170 - const targetURLs = [ 1171 - "http://system.localhost:8888/index.html", 1172 - "http://system.localhost:8888/index_mobile.html", 1173 - ]; 1174 - try { 1175 - await Promise.allSettled( 1176 - targetURLs.map(async (targetURL) => { 1177 - const port = await navigator.createPeerStream(peerId, targetURL); 1178 - port.postMessage({ action: "open-view", url }); 1179 - port.close(); 1180 - }), 1181 - ); 1182 - toastManager?.add("Sent to peer"); 1183 - } catch { 1184 - toastManager?.add("Failed to send"); 1185 - } 1186 - }); 1185 + // Try both system app URLs since we don't know the remote platform. 1186 + const targetURLs = [ 1187 + "http://system.localhost:8888/index.html", 1188 + "http://system.localhost:8888/index_mobile.html", 1189 + ]; 1190 + try { 1191 + await Promise.allSettled( 1192 + targetURLs.map(async (targetURL) => { 1193 + const port = await navigator.createPeerStream(peerId, targetURL); 1194 + port.postMessage({ action: "open-view", url }); 1195 + port.close(); 1196 + }), 1197 + ); 1198 + toastManager?.add("Sent to peer"); 1199 + } catch { 1200 + toastManager?.add("Failed to send"); 1201 + } 1202 + }); 1187 1203 1188 1204 // Receive targeted P2P messages via PeerStream. 1189 1205 window.onpeerstream = (e) => {
+5 -1
ui/system/system_menu.js
··· 59 59 <lucide-icon name="app-window"></lucide-icon> 60 60 <span>New Window</span> 61 61 </li> 62 - <li class="menu-separator"></li> 63 62 <li @click=${() => this.handleItemClick("overview")}> 64 63 <lucide-icon name="layout-grid"></lucide-icon> 65 64 <span>Overview</span> 66 65 </li> 66 + <li class="menu-separator"></li> 67 67 <li @click=${() => this.handleItemClick("settings")}> 68 68 <lucide-icon name="settings"></lucide-icon> 69 69 <span>Settings</span> 70 + </li> 71 + <li @click=${() => this.handleItemClick("atproto")}> 72 + <lucide-icon name="at-sign"></lucide-icon> 73 + <span>ATProto</span> 70 74 </li> 71 75 <li class="menu-separator"></li> 72 76 <li @click=${() => this.handleItemClick("reload-ui")}>