Init commit
2
.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[env]
|
||||
RUSTC_BOOTSTRAP = "crown,script,style_tests"
|
||||
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
target
|
||||
Cargo.lock
|
||||
.DS_Store
|
||||
.vscode/
|
||||
129
Cargo.toml
Normal file
@@ -0,0 +1,129 @@
|
||||
workspace = {}
|
||||
|
||||
[package]
|
||||
name = "wry"
|
||||
version = "0.34.2"
|
||||
authors = ["Tauri Programme within The Commons Conservancy"]
|
||||
edition = "2021"
|
||||
license = "Apache-2.0 OR MIT"
|
||||
description = "Cross-platform WebView rendering library"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/tauri-apps/wry"
|
||||
documentation = "https://docs.rs/wry"
|
||||
categories = ["gui"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
no-default-features = true
|
||||
features = ["file-drop", "protocol"]
|
||||
targets = [
|
||||
"x86_64-unknown-linux-gnu",
|
||||
"x86_64-pc-windows-msvc",
|
||||
"x86_64-apple-darwin",
|
||||
]
|
||||
rustc-args = ["--cfg", "docsrs"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[features]
|
||||
default = ["file-drop", "objc-exception", "protocol", "servo"]
|
||||
objc-exception = ["objc/exception"]
|
||||
file-drop = []
|
||||
protocol = []
|
||||
devtools = []
|
||||
transparent = []
|
||||
fullscreen = []
|
||||
linux-body = ["webkit2gtk/v2_40", "os-webview"]
|
||||
mac-proxy = []
|
||||
os-webview = ["javascriptcore-rs", "webkit2gtk", "webkit2gtk-sys", "dep:gtk", "soup3", "x11-dl", "gdkx11"]
|
||||
servo = []
|
||||
|
||||
[build-dependencies]
|
||||
cfg_aliases = "0.1"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
log = "0.4"
|
||||
once_cell = "1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
thiserror = "1.0"
|
||||
url = "2.4"
|
||||
http = "0.2"
|
||||
raw-window-handle = { version = "0.5", features = ["std"] }
|
||||
# Servo TODO move to feature gate
|
||||
libservo = { git = "https://github.com/servo/servo.git", rev = "e7d2d23", features = ["max_log_level", "native-bluetooth", "webdriver"] }
|
||||
servo-media = { git = "https://github.com/servo/media" }
|
||||
crossbeam-channel = "0.5"
|
||||
getopts = "0.2.17"
|
||||
surfman = { version = "0.8", features = ["chains", "sm-angle", "sm-angle-default", "sm-x11", "sm-raw-window-handle"] }
|
||||
winit = { version = "0.29", features = ["rwh_05"] }
|
||||
|
||||
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
|
||||
javascriptcore-rs = { version = "=1.1", features = ["v2_28"], optional = true }
|
||||
webkit2gtk = { version = "=2.0", features = ["v2_38"], optional = true }
|
||||
webkit2gtk-sys = { version = "=2.0", optional = true }
|
||||
gtk = { version = "0.18", optional = true }
|
||||
soup3 = { version = "0.5", optional = true }
|
||||
x11-dl = { version = "2.9", optional = true }
|
||||
gdkx11 = { version = "0.18", optional = true }
|
||||
|
||||
[target."cfg(target_os = \"windows\")".dependencies]
|
||||
webview2-com = "0.27"
|
||||
windows-implement = "0.51"
|
||||
dunce = "1"
|
||||
|
||||
[target."cfg(target_os = \"windows\")".dependencies.windows]
|
||||
version = "0.51"
|
||||
features = [
|
||||
"implement",
|
||||
"Win32_Foundation",
|
||||
"Win32_Graphics_Gdi",
|
||||
"Win32_System_Com",
|
||||
"Win32_System_Com_StructuredStorage",
|
||||
"Win32_System_LibraryLoader",
|
||||
"Win32_System_Ole",
|
||||
"Win32_System_SystemInformation",
|
||||
"Win32_System_SystemServices",
|
||||
"Win32_UI_Shell",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
"Win32_Globalization",
|
||||
"Win32_UI_HiDpi",
|
||||
"Win32_UI_Input",
|
||||
"Win32_UI_Input_KeyboardAndMouse",
|
||||
]
|
||||
|
||||
[target."cfg(any(target_os = \"ios\", target_os = \"macos\"))".dependencies]
|
||||
block = "0.1"
|
||||
cocoa = "0.25"
|
||||
core-graphics = "0.23"
|
||||
objc = "0.2"
|
||||
objc_id = "0.1"
|
||||
|
||||
[target."cfg(target_os = \"android\")".dependencies]
|
||||
crossbeam-channel = "0.5"
|
||||
html5ever = "0.26"
|
||||
kuchiki = { package = "kuchikiki", version = "0.8" }
|
||||
sha2 = "0.10"
|
||||
base64 = "0.21"
|
||||
jni = "0.21"
|
||||
ndk = "0.7"
|
||||
ndk-sys = "0.4"
|
||||
ndk-context = "0.1"
|
||||
tao-macros = { version = "0.1.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
http-range = "0.1.5"
|
||||
pollster = "0.3.0"
|
||||
# tao = { git = "https://github.com/tauri-apps/tao", default-features = false, features = ["rwh_05"] }
|
||||
wgpu = "0.18.0"
|
||||
|
||||
[patch.crates-io]
|
||||
bindgen = { git = "https://github.com/sagudev/rust-bindgen", branch = "f16" }
|
||||
|
||||
# This is required because we want all dependencies that use WebRender to
|
||||
# use our vendored version.
|
||||
[patch."https://github.com/servo/webrender"]
|
||||
webrender = { git = "https://github.com/servo/servo.git" }
|
||||
webrender_api = { git = "https://github.com/servo/servo.git" }
|
||||
|
||||
[patch."https://github.com/servo/mozjs"]
|
||||
mozjs = { git = "https://github.com/wusyong/mozjs", branch = "mozjs_mirror" }
|
||||
201
LICENSE-APACHE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
21
LICENSE-MIT
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020-2023 Ngo Iok Ui & Tauri Programme within The Commons Conservancy
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
52
README.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Yippee
|
||||
|
||||
A web browser.
|
||||
|
||||
# Usage
|
||||
|
||||
The current demo works best on macOS at the moment, since it tries to customize its traffic light buttons to be seamless in the window.
|
||||
|
||||
It should also work on Windows, as well as Linux with X11. You may encounter problems running the demo on Linux with Wayland or Xwayland.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Windows
|
||||
|
||||
- Install [Rust](https://www.rust-lang.org/)
|
||||
- Install [scoop](https://scoop.sh/) and then install other tools from it. You can install them manually but this is easier and recommended:
|
||||
|
||||
```sh
|
||||
scoop install git python llvm cmake
|
||||
```
|
||||
|
||||
### Others(WIP)
|
||||
|
||||
For now, please follow the instructions in [Servo - Build Setup](https://github.com/servo/servo) to bootstrap first.
|
||||
|
||||
## Build
|
||||
|
||||
- Download [mozjs binary](https://github.com/wusyong/mozjs/releases/tag/tag-8410b587d66a36f1660cc3b828359e199eb0760a) and set the environment variable:
|
||||
|
||||
```sh
|
||||
MOZJS_MIRROR=path/to/libmozjs.tar.gz
|
||||
```
|
||||
|
||||
- **NixOS only:** add `wayland` and `libGL` to `LD_LIBRARY_PATH` in `../servo/etc/shell.nix`
|
||||
|
||||
- Run demo
|
||||
|
||||
```sh
|
||||
cargo run
|
||||
```
|
||||
|
||||
- Or if you are using Nix or NixOS:
|
||||
|
||||
```
|
||||
nix-shell ../servo/etc/shell.nix --run 'cargo run --example servo'
|
||||
```
|
||||
|
||||
## Future Work
|
||||
|
||||
- Add more window and servo features to make it feel more like a general web browser.
|
||||
- Improve development experience.
|
||||
- Multi webviews and multi browsing contexts in the same window.
|
||||
117
build.rs
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright 2020-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
fn main() {
|
||||
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
if target_os == "macos" || target_os == "ios" {
|
||||
println!("cargo:rustc-link-lib=framework=WebKit");
|
||||
}
|
||||
|
||||
let is_android = std::env::var("CARGO_CFG_TARGET_OS")
|
||||
.map(|t| t == "android")
|
||||
.unwrap_or_default();
|
||||
if is_android {
|
||||
use std::{fs, path::PathBuf};
|
||||
|
||||
fn env_var(var: &str) -> String {
|
||||
std::env::var(var).unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"`{}` is not set, which is needed to generate the kotlin files for android.",
|
||||
var
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
println!("cargo:rerun-if-env-changed=WRY_ANDROID_PACKAGE");
|
||||
println!("cargo:rerun-if-env-changed=WRY_ANDROID_LIBRARY");
|
||||
println!("cargo:rerun-if-env-changed=WRY_ANDROID_KOTLIN_FILES_OUT_DIR");
|
||||
|
||||
if let Ok(kotlin_out_dir) = std::env::var("WRY_ANDROID_KOTLIN_FILES_OUT_DIR") {
|
||||
let package = env_var("WRY_ANDROID_PACKAGE");
|
||||
let library = env_var("WRY_ANDROID_LIBRARY");
|
||||
|
||||
let kotlin_out_dir = PathBuf::from(&kotlin_out_dir)
|
||||
.canonicalize()
|
||||
.unwrap_or_else(move |_| {
|
||||
panic!("Failed to canonicalize `WRY_ANDROID_KOTLIN_FILES_OUT_DIR` path {kotlin_out_dir}")
|
||||
});
|
||||
|
||||
let kotlin_files_path =
|
||||
PathBuf::from(env_var("CARGO_MANIFEST_DIR")).join("src/android/kotlin");
|
||||
println!("cargo:rerun-if-changed={}", kotlin_files_path.display());
|
||||
let kotlin_files = fs::read_dir(kotlin_files_path).expect("failed to read kotlin directory");
|
||||
|
||||
for file in kotlin_files {
|
||||
let file = file.unwrap();
|
||||
|
||||
let class_extension_env = format!(
|
||||
"WRY_{}_CLASS_EXTENSION",
|
||||
file
|
||||
.path()
|
||||
.file_stem()
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.to_uppercase()
|
||||
);
|
||||
let class_init_env = format!(
|
||||
"WRY_{}_CLASS_INIT",
|
||||
file
|
||||
.path()
|
||||
.file_stem()
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.to_uppercase()
|
||||
);
|
||||
|
||||
println!("cargo:rerun-if-env-changed={}", class_extension_env);
|
||||
println!("cargo:rerun-if-env-changed={}", class_init_env);
|
||||
|
||||
let content = fs::read_to_string(file.path())
|
||||
.expect("failed to read kotlin file as string")
|
||||
.replace("{{package}}", &package)
|
||||
.replace("{{library}}", &library)
|
||||
.replace(
|
||||
"{{class-extension}}",
|
||||
&std::env::var(&class_extension_env).unwrap_or_default(),
|
||||
)
|
||||
.replace(
|
||||
"{{class-init}}",
|
||||
&std::env::var(&class_init_env).unwrap_or_default(),
|
||||
);
|
||||
|
||||
let auto_generated_comment = match file
|
||||
.path()
|
||||
.extension()
|
||||
.unwrap_or_default()
|
||||
.to_str()
|
||||
.unwrap_or_default()
|
||||
{
|
||||
"pro" => "# THIS FILE IS AUTO-GENERATED. DO NOT MODIFY!!\n\n",
|
||||
"kt" => "/* THIS FILE IS AUTO-GENERATED. DO NOT MODIFY!! */\n\n",
|
||||
_ => "String::new()",
|
||||
};
|
||||
let mut out = String::from(auto_generated_comment);
|
||||
out.push_str(&content);
|
||||
|
||||
let out_path = kotlin_out_dir.join(file.file_name());
|
||||
fs::write(&out_path, out).expect("Failed to write kotlin file");
|
||||
println!("cargo:rerun-if-changed={}", out_path.display());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cfg_aliases::cfg_aliases! {
|
||||
// Platforms
|
||||
android: { target_os = "android" },
|
||||
macos: { target_os = "macos" },
|
||||
ios: { target_os = "ios" },
|
||||
windows: { target_os = "windows" },
|
||||
apple: { any(target_os = "ios", target_os = "macos") },
|
||||
linux: { all(unix, not(apple), not(android)) },
|
||||
// Backends
|
||||
gtk: { all(feature = "native", linux) },
|
||||
gtk: { all(feature = "os-webview", linux) },
|
||||
servo: { all(feature = "servo", any(linux, macos, windows)) },
|
||||
}
|
||||
}
|
||||
178
demo.html
Normal file
@@ -0,0 +1,178 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.bg {
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: linear-gradient(-45deg, #f5b23f, #3cc8e7, #d8f931, #26e6b9);
|
||||
background-size: 400% 400%;
|
||||
animation: gradient 15s ease infinite;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
@keyframes gradient {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
.text-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
top: 100px;
|
||||
left: 40px;
|
||||
}
|
||||
.text {
|
||||
cursor: default;
|
||||
position: relative;
|
||||
color: #fff;
|
||||
line-height: 50px;
|
||||
font-weight: bold;
|
||||
font-size: 50px;
|
||||
font-family: 'Courier New';
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
.text-bg {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
padding: 5px;
|
||||
}
|
||||
.spotlight {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
background: radial-gradient(
|
||||
50px 50px at center center,
|
||||
transparent,
|
||||
transparent 100px,
|
||||
rgba(0, 0, 0, 0.05) 150px
|
||||
);
|
||||
opacity: 1;
|
||||
z-index: 1;
|
||||
}
|
||||
.traffic-light-area {
|
||||
display: block;
|
||||
height: 30px;
|
||||
width: 62px;
|
||||
background: #0000007e;
|
||||
user-select: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding-left: 10px;
|
||||
border-radius: 0 0 5px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- <div class="traffic-light-area"></div> -->
|
||||
<div id="spotlight" class="spotlight"></div>
|
||||
<div class="text-container">
|
||||
<span id="text-prefix" class="text">Hello </span>
|
||||
<span id="text-name" class="text text-bg"></span>
|
||||
<span id="cursor" class="text">_</span>
|
||||
</div>
|
||||
<div class="bg"></div>
|
||||
<script>
|
||||
/* Spotlight */
|
||||
const spotlightEl = document.getElementById('spotlight');
|
||||
function handleMouseMove(event) {
|
||||
const { clientX, clientY } = event;
|
||||
spotlightEl.style =
|
||||
'background:radial-gradient(50px 50px at ' +
|
||||
clientX +
|
||||
'px ' +
|
||||
clientY +
|
||||
'px, transparent, transparent 100px, rgba(0,0,0,0.05) 150px)';
|
||||
}
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
|
||||
/* Typing Effect */
|
||||
async function startCarosel() {
|
||||
const textNameEl = document.getElementById('text-name');
|
||||
const texts = ['Tauri.', 'Servo.', 'World!'];
|
||||
const colors = ['#ffc131', '#3cc8e7', '#ffffff'];
|
||||
let textIdx = 0;
|
||||
|
||||
while (true) {
|
||||
const text = texts[textIdx];
|
||||
|
||||
updateNameColor(textNameEl, colors[textIdx]);
|
||||
await type(texts[textIdx], textNameEl);
|
||||
let blinkTimerId = blinkCursor();
|
||||
await sleep(3200);
|
||||
cleanCursor(blinkTimerId);
|
||||
await backspace(textNameEl);
|
||||
await sleep(500);
|
||||
|
||||
textIdx = (textIdx + 1) % texts.length;
|
||||
}
|
||||
}
|
||||
|
||||
async function type(text, el, delay = 100) {
|
||||
el.classList.add('text-bg');
|
||||
for (const ch of text) {
|
||||
await sleep(delay);
|
||||
el.innerHTML += ch;
|
||||
}
|
||||
}
|
||||
|
||||
async function backspace(el) {
|
||||
let text = el.innerHTML;
|
||||
while (text.length > 0) {
|
||||
await sleep(60);
|
||||
text = text.slice(0, -1);
|
||||
el.innerHTML = text;
|
||||
}
|
||||
el.classList.remove('text-bg');
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
function blinkCursor() {
|
||||
const cursor = document.getElementById('cursor');
|
||||
return setInterval(() => {
|
||||
if (cursor.innerHTML === '_') {
|
||||
cursor.innerHTML = '';
|
||||
} else {
|
||||
cursor.innerHTML = '_';
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function cleanCursor(timerId) {
|
||||
clearTimeout(timerId);
|
||||
const cursor = document.getElementById('cursor');
|
||||
cursor.innerHTML = '_';
|
||||
}
|
||||
|
||||
function updateNameColor(el, color) {
|
||||
el.style.color = color;
|
||||
}
|
||||
|
||||
startCarosel();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
7
resources/Credits.rtf.mako
Normal file
@@ -0,0 +1,7 @@
|
||||
{\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460
|
||||
{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
|
||||
{\colortbl;\red255\green255\blue255;}
|
||||
\vieww12000\viewh15840\viewkind0
|
||||
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qc\partightenfactor0
|
||||
|
||||
\f0\fs24 \cf0 ${version}}
|
||||
4
resources/ahem.css
Normal file
@@ -0,0 +1,4 @@
|
||||
@font-face {
|
||||
font-family: Ahem;
|
||||
src: url(ahem/AHEM____.TTF);
|
||||
}
|
||||
BIN
resources/ahem/AHEM____.TTF
Normal file
1
resources/ahem/Ahem.ps
Normal file
BIN
resources/ahem/Ahem.sit
Normal file
1
resources/ahem/COPIED-FROM
Normal file
@@ -0,0 +1 @@
|
||||
The files in this directory are copied from http://www.w3.org/Style/CSS/Test/Fonts/Ahem/
|
||||
36
resources/ahem/COPYING
Normal file
@@ -0,0 +1,36 @@
|
||||
The Ahem font in this directory belongs to the public domain. In
|
||||
jurisdictions that do not recognize public domain ownership of these
|
||||
files, the following Creative Commons Zero declaration applies:
|
||||
|
||||
<http://labs.creativecommons.org/licenses/zero-waive/1.0/us/legalcode>
|
||||
|
||||
which is quoted below:
|
||||
|
||||
The person who has associated a work with this document (the "Work")
|
||||
affirms that he or she (the "Affirmer") is the/an author or owner of
|
||||
the Work. The Work may be any work of authorship, including a
|
||||
database.
|
||||
|
||||
The Affirmer hereby fully, permanently and irrevocably waives and
|
||||
relinquishes all of her or his copyright and related or neighboring
|
||||
legal rights in the Work available under any federal or state law,
|
||||
treaty or contract, including but not limited to moral rights,
|
||||
publicity and privacy rights, rights protecting against unfair
|
||||
competition and any rights protecting the extraction, dissemination
|
||||
and reuse of data, whether such rights are present or future, vested
|
||||
or contingent (the "Waiver"). The Affirmer makes the Waiver for the
|
||||
benefit of the public at large and to the detriment of the Affirmer's
|
||||
heirs or successors.
|
||||
|
||||
The Affirmer understands and intends that the Waiver has the effect
|
||||
of eliminating and entirely removing from the Affirmer's control all
|
||||
the copyright and related or neighboring legal rights previously held
|
||||
by the Affirmer in the Work, to that extent making the Work freely
|
||||
available to the public for any and all uses and purposes without
|
||||
restriction of any kind, including commercial use and uses in media
|
||||
and formats or by methods that have not yet been invented or
|
||||
conceived. Should the Waiver for any reason be judged legally
|
||||
ineffective in any jurisdiction, the Affirmer hereby grants a free,
|
||||
full, permanent, irrevocable, nonexclusive and worldwide license for
|
||||
all her or his copyright and related or neighboring legal rights in
|
||||
the Work.
|
||||
30
resources/ahem/README
Normal file
@@ -0,0 +1,30 @@
|
||||
The Ahem font was developed by Todd Fahrner to help test writers
|
||||
develop predictable tests. The font's em square is exactly square.
|
||||
Its ascent and descent is exactly the size of the em square. This
|
||||
means that the font's extent is exactly the same as its line-height,
|
||||
meaning that it can be exactly aligned with padding, borders, margins,
|
||||
and so forth.
|
||||
|
||||
The font's alphabetic baseline is 0.2em above its bottom, and 0.8em
|
||||
below its top. The font has an x-height of 0.8em.
|
||||
|
||||
The font has four glyphs:
|
||||
|
||||
'X' U+0058 A square exactly 1em in height and width.
|
||||
|
||||
'p' U+0070 A rectangle exactly 0.2em high, 1em wide, and aligned so
|
||||
that its top is flush with the baseline.
|
||||
|
||||
'É' U+00C9 A rectangle exactly 0.8em high, 1em wide, and aligned so
|
||||
that its bottom is flush with the baseline.
|
||||
|
||||
' ' U+0020 A transparent space exactly 1em high and wide.
|
||||
|
||||
Most other US-ASCII characters in the font have the same glyph as X.
|
||||
|
||||
The Ahem font belongs to the public domain as described in COPYING.
|
||||
|
||||
Acknowledgements: The font was originally created by Todd Fahrner in
|
||||
the late 90s, and was updated by Paul Nelson in the mid 2000s. The
|
||||
changes were the introduction of x-height information to the OS/2
|
||||
table and the addition of the space and non-breaking space glyphs.
|
||||
9
resources/android_params
Normal file
@@ -0,0 +1,9 @@
|
||||
# The first line here should be the "servo" argument (without quotes) and the
|
||||
# last should be the URL to load.
|
||||
# Blank lines and those beginning with a '#' are ignored.
|
||||
# Each line should be a separate parameter as would be parsed by the shell.
|
||||
# For example, "servo -p 10 http://en.wikipedia.org/wiki/Rust" would take 4
|
||||
# lines (the "-p" and "10" are separate even though they are related).
|
||||
|
||||
servo
|
||||
http://en.wikipedia.org/wiki/Rust
|
||||
28
resources/badcert.html
Normal file
@@ -0,0 +1,28 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Certificate error</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>${reason}</p>
|
||||
<button id="leave" onclick="history.back()">Go back (recommended)</button>
|
||||
<button id="allow">Allow certificate temporarily</button>
|
||||
<div style="word-break: break-all; font-family: monospace" id="bytes">${bytes}</div>
|
||||
<script>
|
||||
let bytes = document.getElementById('bytes').textContent;
|
||||
let button = document.getElementById('allow');
|
||||
let exitButton = document.getElementById('leave');
|
||||
if (bytes.length) {
|
||||
button.onclick = function() {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', 'chrome:allowcert');
|
||||
xhr.onloadend = function() {
|
||||
location.reload(true);
|
||||
};
|
||||
xhr.send("${secret}&${bytes}");
|
||||
};
|
||||
} else {
|
||||
button.style.display = "none";
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
resources/badcert.jpg
Normal file
|
After Width: | Height: | Size: 62 KiB |
19
resources/cert-google-only
Normal file
@@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
|
||||
UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
|
||||
dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
|
||||
MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
|
||||
dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
|
||||
AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
|
||||
BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
|
||||
cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
|
||||
AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
|
||||
MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
|
||||
aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
|
||||
ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
|
||||
IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
|
||||
MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
|
||||
A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
|
||||
7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
|
||||
1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
|
||||
-----END CERTIFICATE-----
|
||||
20
resources/cert-wpt-only
Normal file
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDUzCCAjugAwIBAgIDC3+YMA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNVBAMMEndl
|
||||
Yi1wbGF0Zm9ybS10ZXN0czAeFw0xODA3MTMxMjU5MTJaFw0yODA3MTAxMjU5MTJa
|
||||
MB0xGzAZBgNVBAMMEndlYi1wbGF0Zm9ybS10ZXN0czCCASIwDQYJKoZIhvcNAQEB
|
||||
BQADggEPADCCAQoCggEBANOUELdwzxsrHn9sIk8VvsmVA0XifCZ40ls8tTyGrKu6
|
||||
6cEokYOnKJImeQDCvebvu2vX+i45+hbI5SSObumxFYOrmFaZQklNXRY6KP5NLika
|
||||
PHh4CapzJmLCpSxIkmUScH4/fsfE6grIYO9qsGXc2hAxyYg6VjX9hb0RpFF9f3Ff
|
||||
FjhkBnglOxXhphkve/+SEa0OYBrUYY5GRQCbe8YfWw67nWz9eimWgiU91RmAtzBu
|
||||
YSofy1DcrPT/SwJnYFgEF7Zpk5PIkvigXvFW/egkIb30vtTuH99u001VG1jEZpuT
|
||||
trIQ6A77a5BP+V+ZWAqqjV85M3WDQUVxUwvwgXXcc20CAwEAAaOBmzCBmDAMBgNV
|
||||
HRMEBTADAQH/MB0GA1UdDgQWBBRb2sjBCfNNkjC870P7JpMerg2aDzBHBgNVHSME
|
||||
QDA+gBRb2sjBCfNNkjC870P7JpMerg2aD6EhpB8wHTEbMBkGA1UEAwwSd2ViLXBs
|
||||
YXRmb3JtLXRlc3RzggMLf5gwCwYDVR0PBAQDAgIEMBMGA1UdJQQMMAoGCCsGAQUF
|
||||
BwMBMA0GCSqGSIb3DQEBCwUAA4IBAQCX4/C5Ywfl3tkff96++OSY1iWX6dOtfG0w
|
||||
fQ2OxZvv66jFWbIscwhGIqv3YQEZngAMcrwqvYYRHz2AqhlYb80Ftlon/y45GkQI
|
||||
ouim4iwDUi9jazo7k8a4U8307GNcJFplpOOTnpbwAM+C/NwoGIbxon54gJ/0EbGO
|
||||
yBC1GJ39norLbY4VIjsfDXlTvSv30AkqpqnxPkzQqoGuKzSwUhbQeOIlweKSRxsv
|
||||
pqIpb4eaPF1EsI+VPOet0YMcOLc9hSu43EWBunGnHJabwTg+qabHOHFNN27KuOir
|
||||
zAdOIUOIIcBJEZ7WxBMsV3UdjlQfY5pGGVJAxR4sONK4YkOlhMjk
|
||||
-----END CERTIFICATE-----
|
||||
8
resources/crash.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<p>Servo crashed!</p>
|
||||
|
||||
<!-- NOTE: unlike in Firefox and Chrome, this reloads POST as GET -->
|
||||
<!-- see whatwg/html#6600 + whatwg/html#3215 -->
|
||||
<button onclick="location.reload()">Reload</button>
|
||||
|
||||
<pre><plaintext>
|
||||
${details}
|
||||
62
resources/gatt_blocklist.txt
Normal file
@@ -0,0 +1,62 @@
|
||||
# Source:
|
||||
# https://github.com/WebBluetoothCG/registries/blob/master/gatt_blocklist.txt
|
||||
# License:
|
||||
# https://github.com/WebBluetoothCG/registries/blob/master/LICENSE
|
||||
|
||||
# This file holds a list of GATT UUIDs that websites using the Web
|
||||
# Bluetooth API are forbidden from accessing.
|
||||
|
||||
## Services
|
||||
|
||||
# org.bluetooth.service.human_interface_device
|
||||
# Direct access to HID devices like keyboards would let web pages
|
||||
# become keyloggers.
|
||||
00001812-0000-1000-8000-00805f9b34fb
|
||||
|
||||
# Firmware update services that don't check the update's signature
|
||||
# present a risk of devices' software being modified by malicious web
|
||||
# pages. Users may connect to a device believing they are enabling
|
||||
# only simple interaction or that they're interacting with the
|
||||
# device's manufacturer, but the site might instead persistently
|
||||
# compromise the device.
|
||||
#
|
||||
# Nordic's Device Firmware Update service, http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk5.v11.0.0/examples_ble_dfu.html:
|
||||
00001530-1212-efde-1523-785feabcd123
|
||||
# TI's Over-the-Air Download service, http://www.ti.com/lit/ug/swru271g/swru271g.pdf:
|
||||
f000ffc0-0451-4000-b000-000000000000
|
||||
|
||||
|
||||
## Characteristics
|
||||
|
||||
# org.bluetooth.characteristic.gap.peripheral_privacy_flag
|
||||
# Don't let web pages turn off privacy mode.
|
||||
00002a02-0000-1000-8000-00805f9b34fb exclude-writes
|
||||
|
||||
# org.bluetooth.characteristic.gap.reconnection_address
|
||||
# Disallow messing with connection parameters
|
||||
00002a03-0000-1000-8000-00805f9b34fb
|
||||
|
||||
# org.bluetooth.characteristic.serial_number_string
|
||||
# Block access to standardized unique identifiers, for privacy reasons.
|
||||
00002a25-0000-1000-8000-00805f9b34fb
|
||||
|
||||
# Blocklisted characteristic used to test readValue function.
|
||||
bad1c9a2-9a5b-4015-8b60-1579bbbf2135 exclude-reads
|
||||
|
||||
|
||||
## Descriptors
|
||||
|
||||
# org.bluetooth.descriptor.gatt.client_characteristic_configuration
|
||||
# Writing to this would let a web page interfere with other pages'
|
||||
# notifications and indications.
|
||||
00002902-0000-1000-8000-00805f9b34fb exclude-writes
|
||||
|
||||
# org.bluetooth.descriptor.gatt.server_characteristic_configuration
|
||||
# Writing to this would let a web page interfere with the broadcasted services.
|
||||
00002903-0000-1000-8000-00805f9b34fb exclude-writes
|
||||
|
||||
# Blocklisted descriptor used to test.
|
||||
07711111-6104-0970-7011-1107105110aa
|
||||
|
||||
# Blocklisted descriptor used to test.
|
||||
aaaaaaaa-aaaa-1181-0510-810819516110 exclude-reads
|
||||
372832
resources/hsts_preload.json
Normal file
23
resources/iso-8859-8.css
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
|
||||
https://html.spec.whatwg.org/multipage/#bidi-rendering
|
||||
|
||||
> When the document's character encoding is ISO-8859-8,
|
||||
> the following rules are additionally expected to apply, following [user-agent.css]
|
||||
|
||||
*/
|
||||
|
||||
@namespace url(http://www.w3.org/1999/xhtml);
|
||||
|
||||
|
||||
address, blockquote, center, div, figure, figcaption, footer, form, header, hr,
|
||||
legend, listing, main, p, plaintext, pre, summary, xmp, article, aside, h1, h2,
|
||||
h3, h4, h5, h6, hgroup, nav, section, table, caption, colgroup, col, thead,
|
||||
tbody, tfoot, tr, td, th, dir, dd, dl, dt, menu, ol, ul, li, [dir=ltr i],
|
||||
[dir=rtl i], [dir=auto i], *|* {
|
||||
unicode-bidi: bidi-override;
|
||||
}
|
||||
input:not([type=submit i]):not([type=reset i]):not([type=button i]),
|
||||
textarea {
|
||||
unicode-bidi: normal;
|
||||
}
|
||||
61
resources/media-controls.css
Normal file
@@ -0,0 +1,61 @@
|
||||
button {
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
min-width: var(--button-size);
|
||||
min-height: var(--button-size);
|
||||
padding: 6px;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
background-color: transparent;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.root {
|
||||
display: block;
|
||||
position: relative;
|
||||
min-height: 40px;
|
||||
min-width: 230px;
|
||||
}
|
||||
|
||||
.controls {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background-color: rgba(26,26,26,.8);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.playing {
|
||||
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAALElEQVR4AWMYQeD///8KQOwAxQq4xQkb1PAfARpwiQ9Kg0YNGjWI8iwycgAA8BVdyMCA4SwAAAAASUVORK5CYII=") no-repeat;
|
||||
}
|
||||
|
||||
.paused {
|
||||
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAATklEQVR4AWMYgeD///8C1DLoARA7UG4QAqynyHX/UcF7II6n3CAE2A/ECpQbhHBdPTUM+gDEDZQadIB8ryFckUBpYG8ARf/AJ0i4K0YBAOu3zFnxHnpDAAAAAElFTkSuQmCC") no-repeat;
|
||||
}
|
||||
|
||||
.ended {
|
||||
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAQAAAD8x0bcAAAArElEQVR4AbWRIQ7CQBBFR7QBhcYRFKISVwQcgfsgmiDq8S22BgkGAxwBJBp0BSEkhOaxYs2wW6jhjZn8eeqPfEJbfkHItoECDRRgQq9eSrBQsWLgl1rsAJgyo+TG6IsmBrocudKp1ew25MVcNG5PHDjr05i+oy946qCkcKSCUgdrLgQqCUyy0VIMJE5rsWjIqUgJbfcpFUvfUzLgzsnMA8gIxQcROXszOZH8hTfry4rnukG7bgAAAABJRU5ErkJggg==") no-repeat;
|
||||
}
|
||||
|
||||
.volumeup {
|
||||
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAQAAAD8x0bcAAAAi0lEQVR4AWOgNfh/4n/Yfybc0g1gcs////8P/RfCoeT/fyjL9v+H/1f+c2NTAgRwnun/v/+nYCoBAzB7yX9GILni/8//IjBpFAAVyQKSLkA6BJ+i3UBSDEiX4lO0C6qoDJ+iTCDpCqRDCTl8JdDhooSDYDL+wLT7/xEYmDz4omU3ZrRgKj0NjmCqAwANy7W0uf6FTQAAAABJRU5ErkJggg==") no-repeat;
|
||||
}
|
||||
|
||||
.muted {
|
||||
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAQAAAD8x0bcAAAAy0lEQVR4AayRtUICUBRA7a7Jye5RF7u+wM4vsF2s0e7O37BzkZqZ+AF2WGk4XLqZeO/dPq/TUtuoZSki7mIgFloFlv3+icgOoKE8GlsH1sVOgOgy9jGjpzIuRjqv/rgNI3/hQCuD0t+8WK7Eywx6NfSGIAX+5t90CwOF5ODgNj4EO2TzxbDkdXwlgmCdLOolr+UlEbTn37QAK0fRBz8BAImvEYwMnmiJfdRzHxTAYoAQJjqPWWxJMf+3vHEAzCf+8A4avXYR9xSpm5QAaOvUDhZuhCsAAAAASUVORK5CYII=") no-repeat;
|
||||
}
|
||||
|
||||
.fullscreen {
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAa1JREFUSA3Vlj1Ow0AQhW1OAFUaKmoqOEAklCIlJQ3nyBFyBahc0IAEEnUUunQWVLiloqEKNzDfmJ2V7Z1JlpKRRrt+82bezq7/yrZtH4qiOMUt2wJelWX5aQXJnYDf4UdWHOyjgFTjlm0Bz53ECAsHF65lzUFkDiffXM5Y+esQTq8CZ0ZEchLzBF5gviVsBwgiz2aYvrwtuiFWmkkjEN7S2h+wpn8Gso/ViLhXBP64+COYnkkUiAdqJLgiHhdcD74TWAnQ79pL/AsniGxki+ReTswQmSuJ2BTvm9klhGPNMUcIur+LMYHYIiiYxcd895oiUy9IbI5n3WlejX+OS/uyDV4bxNzt83IiHorfMortOuRlTLImJCe3EpisXIsz7Syulis53L6ZIhAm8hxs8PigMbeKDwpkcuRpXolAg3evipxE3YVdXGL6qqhVAKwTeZJJzwYr1+I6wrO6rcD1ZTcQ6NXtpjuL7xHRWq5ApQVyRqpJJ+PuRaT2vmiXBOPBZ4icwbmweJ7AIeR1jkjgrOFLTmLSmvy2nCSRX0B+W6755n5ZcXLlGbrHvd+W9x/rQiZUwmH0RgAAAABJRU5ErkJggg==') no-repeat;
|
||||
}
|
||||
|
||||
.fullscreen.fullscreen-active {
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAbtJREFUSA3tlj0vBFEUhnd8hFhrE4mPbSREovIfdFQ6hcJ/EJVColP6GRoqhZbEFqKmET2xDSpWYjxncu915u6ZzWS20DjJ6577no/3zL1mqNWwNE1HwKb4g5rrdcjayHo54oxVbG8QAepl0FNphLVBQ6bflZ2ySiLU6+a+3bY/Hq/qA0EEog4m4ieDmwOzwrNazUOPwgQKpfkVWDAEVuHvQAsUDhjqSLKmeIQXKxKQ2Jv8UPY7eejuHJIsEantJ6B6l/glIXsKvOgq/DICT+Rld6IHH9IbEursz8GM5kv68+RdxiKJLibYYr+sOeffJknyqXlyZbhJzTn/g9yuwf9Tf3QC8SUPM8eoMUuXi/vWPJcsuSuac36H3I7BZ98UeQduQGwbcQEJ03ES+3vQ8y7kakkQkQegrYzAKwXyLuQs96K5yDrrYi6r3KZJ2jEicnS2EdwCX8Dbu3P6PcGzT3brCWuvCGTc/BpOPsVyJ0UC2ZkT3wfa8iJErObZZ4BYEyzFzww3DsKF4tsiBNaAPhaZ3PrGxBo9e0PkQP6ajYELIFa5uVejh3+SNn74z0JEjkClyX1zv9Jnxzf/AaCJ561FockLAAAAAElFTkSuQmCC') no-repeat;
|
||||
}
|
||||
416
resources/media-controls.js
Normal file
@@ -0,0 +1,416 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
(() => {
|
||||
"use strict";
|
||||
|
||||
// States.
|
||||
const BUFFERING = "buffering";
|
||||
const ENDED = "ended";
|
||||
const ERRORED = "errored";
|
||||
const PAUSED = "paused";
|
||||
const PLAYING = "playing";
|
||||
|
||||
// State transitions.
|
||||
const TRANSITIONS = {
|
||||
buffer: {
|
||||
paused: BUFFERING
|
||||
},
|
||||
end: {
|
||||
playing: ENDED,
|
||||
paused: ENDED
|
||||
},
|
||||
error: {
|
||||
buffering: ERRORED,
|
||||
playing: ERRORED,
|
||||
paused: ERRORED
|
||||
},
|
||||
pause: {
|
||||
buffering: PAUSED,
|
||||
playing: PAUSED
|
||||
},
|
||||
play: {
|
||||
buffering: PLAYING,
|
||||
ended: PLAYING,
|
||||
paused: PLAYING
|
||||
}
|
||||
};
|
||||
|
||||
function generateMarkup(isAudioOnly) {
|
||||
return `
|
||||
<div class="controls">
|
||||
<button id="play-pause-button"></button>
|
||||
<input id="progress" type="range" value="0" min="0" max="100" step="1"></input>
|
||||
<span id="position-duration-box" class="hidden">
|
||||
<span id="position-text">#1</span>
|
||||
<span id="duration"> / #2</span>
|
||||
</span>
|
||||
<button id="volume-switch"></button>
|
||||
<input id="volume-level" type="range" value="100" min="0" max="100" step="1"></input>
|
||||
${isAudioOnly ? "" : '<button id="fullscreen-switch" class="fullscreen"></button>'}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function camelCase(str) {
|
||||
const rdashes = /-(.)/g;
|
||||
return str.replace(rdashes, (str, p1) => {
|
||||
return p1.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
function formatTime(time, showHours = false) {
|
||||
// Format the duration as "h:mm:ss" or "m:ss"
|
||||
time = Math.round(time / 1000);
|
||||
|
||||
const hours = Math.floor(time / 3600);
|
||||
const mins = Math.floor((time % 3600) / 60);
|
||||
const secs = Math.floor(time % 60);
|
||||
|
||||
const formattedHours =
|
||||
hours || showHours ? `${hours.toString().padStart(2, "0")}:` : "";
|
||||
|
||||
return `${formattedHours}${mins
|
||||
.toString()
|
||||
.padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
class MediaControls {
|
||||
constructor() {
|
||||
this.nonce = Date.now();
|
||||
// Get the instance of the shadow root where these controls live.
|
||||
this.controls = document.servoGetMediaControls("@@@id@@@");
|
||||
// Get the instance of the host of these controls.
|
||||
this.media = this.controls.host;
|
||||
|
||||
this.mutationObserver = new MutationObserver(() => {
|
||||
// We can only get here if the `controls` attribute is removed.
|
||||
this.cleanup();
|
||||
});
|
||||
this.mutationObserver.observe(this.media, {
|
||||
attributeFilter: ["controls"]
|
||||
});
|
||||
|
||||
this.isAudioOnly = this.media.localName == "audio";
|
||||
|
||||
// Create root element and load markup.
|
||||
this.root = document.createElement("div");
|
||||
this.root.classList.add("root");
|
||||
this.root.innerHTML = generateMarkup(this.isAudioOnly);
|
||||
this.controls.appendChild(this.root);
|
||||
|
||||
|
||||
const elementNames = [
|
||||
"duration",
|
||||
"play-pause-button",
|
||||
"position-duration-box",
|
||||
"position-text",
|
||||
"progress",
|
||||
"volume-switch",
|
||||
"volume-level"
|
||||
];
|
||||
|
||||
if (!this.isAudioOnly) {
|
||||
elementNames.push("fullscreen-switch");
|
||||
}
|
||||
|
||||
// Import elements.
|
||||
this.elements = {};
|
||||
elementNames.forEach(id => {
|
||||
this.elements[camelCase(id)] = this.controls.getElementById(id);
|
||||
});
|
||||
|
||||
// Init position duration box.
|
||||
const positionTextNode = this.elements.positionText;
|
||||
const durationSpan = this.elements.duration;
|
||||
const durationFormat = durationSpan.textContent;
|
||||
const positionFormat = positionTextNode.textContent;
|
||||
|
||||
durationSpan.classList.add("duration");
|
||||
durationSpan.setAttribute("role", "none");
|
||||
|
||||
Object.defineProperties(this.elements.positionDurationBox, {
|
||||
durationSpan: {
|
||||
value: durationSpan
|
||||
},
|
||||
position: {
|
||||
get: () => {
|
||||
return positionTextNode.textContent;
|
||||
},
|
||||
set: v => {
|
||||
positionTextNode.textContent = positionFormat.replace("#1", v);
|
||||
}
|
||||
},
|
||||
duration: {
|
||||
get: () => {
|
||||
return durationSpan.textContent;
|
||||
},
|
||||
set: v => {
|
||||
durationSpan.textContent = v ? durationFormat.replace("#2", v) : "";
|
||||
}
|
||||
},
|
||||
show: {
|
||||
value: (currentTime, duration) => {
|
||||
const self = this.elements.positionDurationBox;
|
||||
if (self.position != currentTime) {
|
||||
self.position = currentTime;
|
||||
}
|
||||
if (self.duration != duration) {
|
||||
self.duration = duration;
|
||||
}
|
||||
self.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add event listeners.
|
||||
this.mediaEvents = [
|
||||
"play",
|
||||
"pause",
|
||||
"ended",
|
||||
"volumechange",
|
||||
"loadeddata",
|
||||
"loadstart",
|
||||
"timeupdate",
|
||||
"progress",
|
||||
"playing",
|
||||
"waiting",
|
||||
"canplay",
|
||||
"canplaythrough",
|
||||
"seeking",
|
||||
"seeked",
|
||||
"emptied",
|
||||
"loadedmetadata",
|
||||
"error",
|
||||
"suspend"
|
||||
];
|
||||
this.mediaEvents.forEach(event => {
|
||||
this.media.addEventListener(event, this);
|
||||
});
|
||||
|
||||
this.controlEvents = [
|
||||
{ el: this.elements.playPauseButton, type: "click" },
|
||||
{ el: this.elements.volumeSwitch, type: "click" },
|
||||
{ el: this.elements.volumeLevel, type: "input" }
|
||||
];
|
||||
|
||||
if (!this.isAudioOnly) {
|
||||
this.controlEvents.push({ el: this.elements.fullscreenSwitch, type: "click" });
|
||||
}
|
||||
|
||||
this.controlEvents.forEach(({ el, type }) => {
|
||||
el.addEventListener(type, this);
|
||||
});
|
||||
|
||||
// Create state transitions.
|
||||
//
|
||||
// It exposes one method per transition. i.e. this.pause(), this.play(), etc.
|
||||
// For each transition, we check that the transition is possible and call
|
||||
// the `onStateChange` handler.
|
||||
for (let name in TRANSITIONS) {
|
||||
if (!TRANSITIONS.hasOwnProperty(name)) {
|
||||
continue;
|
||||
}
|
||||
this[name] = () => {
|
||||
const from = this.state;
|
||||
|
||||
// Checks if the transition is valid in the current state.
|
||||
if (!TRANSITIONS[name][from]) {
|
||||
const error = `Transition "${name}" invalid for the current state "${from}"`;
|
||||
console.error(error);
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
const to = TRANSITIONS[name][from];
|
||||
|
||||
if (from == to) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Transition to the next state.
|
||||
this.state = to;
|
||||
this.onStateChange(from);
|
||||
};
|
||||
}
|
||||
|
||||
// Set initial state.
|
||||
this.state = this.media.paused ? PAUSED : PLAYING;
|
||||
this.onStateChange(null);
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
this.mutationObserver.disconnect();
|
||||
this.mediaEvents.forEach(event => {
|
||||
this.media.removeEventListener(event, this);
|
||||
});
|
||||
this.controlEvents.forEach(({ el, type }) => {
|
||||
el.removeEventListener(type, this);
|
||||
});
|
||||
}
|
||||
|
||||
// State change handler
|
||||
onStateChange(from) {
|
||||
this.render(from);
|
||||
}
|
||||
|
||||
render(from = this.state) {
|
||||
if (!this.isAudioOnly) {
|
||||
// XXX This should ideally use clientHeight/clientWidth,
|
||||
// but for some reason I couldn't figure out yet,
|
||||
// using it breaks layout.
|
||||
this.root.style.height = this.media.videoHeight;
|
||||
this.root.style.width = this.media.videoWidth;
|
||||
}
|
||||
|
||||
// Error
|
||||
if (this.state == ERRORED) {
|
||||
//XXX render errored state
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.state != from) {
|
||||
// Play/Pause button.
|
||||
const playPauseButton = this.elements.playPauseButton;
|
||||
playPauseButton.classList.remove(from);
|
||||
playPauseButton.classList.add(this.state);
|
||||
}
|
||||
|
||||
// Progress.
|
||||
const positionPercent =
|
||||
(this.media.currentTime / this.media.duration) * 100;
|
||||
if (Number.isFinite(positionPercent)) {
|
||||
this.elements.progress.value = positionPercent;
|
||||
} else {
|
||||
this.elements.progress.value = 0;
|
||||
}
|
||||
|
||||
// Current time and duration.
|
||||
let currentTime = formatTime(0);
|
||||
let duration = formatTime(0);
|
||||
if (!isNaN(this.media.currentTime) && !isNaN(this.media.duration)) {
|
||||
currentTime = formatTime(Math.round(this.media.currentTime * 1000));
|
||||
duration = formatTime(Math.round(this.media.duration * 1000));
|
||||
}
|
||||
this.elements.positionDurationBox.show(currentTime, duration);
|
||||
|
||||
// Volume.
|
||||
this.elements.volumeSwitch.className =
|
||||
this.media.muted || !this.media.volume ? "muted" : "volumeup";
|
||||
const volumeLevelValue = this.media.muted
|
||||
? 0
|
||||
: Math.round(this.media.volume * 100);
|
||||
if (this.elements.volumeLevel.value != volumeLevelValue) {
|
||||
this.elements.volumeLevel.value = volumeLevelValue;
|
||||
}
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
if (!event.isTrusted) {
|
||||
console.warn(`Drop untrusted event ${event.type}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.mediaEvents.includes(event.type)) {
|
||||
this.onMediaEvent(event);
|
||||
} else {
|
||||
this.onControlEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
onControlEvent(event) {
|
||||
switch (event.type) {
|
||||
case "click":
|
||||
switch (event.currentTarget) {
|
||||
case this.elements.playPauseButton:
|
||||
this.playOrPause();
|
||||
break;
|
||||
case this.elements.volumeSwitch:
|
||||
this.toggleMuted();
|
||||
break;
|
||||
case this.elements.fullscreenSwitch:
|
||||
this.toggleFullscreen();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "input":
|
||||
switch (event.currentTarget) {
|
||||
case this.elements.volumeLevel:
|
||||
this.changeVolume();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown event ${event.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
// HTMLMediaElement event handler
|
||||
onMediaEvent(event) {
|
||||
switch (event.type) {
|
||||
case "ended":
|
||||
this.end();
|
||||
break;
|
||||
case "play":
|
||||
case "pause":
|
||||
// Transition to PLAYING or PAUSED state.
|
||||
this[event.type]();
|
||||
break;
|
||||
case "volumechange":
|
||||
case "timeupdate":
|
||||
case "resize":
|
||||
this.render();
|
||||
break;
|
||||
case "loadedmetadata":
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Media actions */
|
||||
|
||||
playOrPause() {
|
||||
switch (this.state) {
|
||||
case PLAYING:
|
||||
this.media.pause();
|
||||
break;
|
||||
case BUFFERING:
|
||||
case ENDED:
|
||||
case PAUSED:
|
||||
this.media.play();
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid state ${this.state}`);
|
||||
}
|
||||
}
|
||||
|
||||
toggleMuted() {
|
||||
this.media.muted = !this.media.muted;
|
||||
}
|
||||
|
||||
toggleFullscreen() {
|
||||
const { fullscreenEnabled, fullscreenElement } = document;
|
||||
|
||||
const isElementFullscreen = fullscreenElement && fullscreenElement === this.media;
|
||||
|
||||
if (fullscreenEnabled && isElementFullscreen) {
|
||||
document.exitFullscreen().then(() => {
|
||||
this.elements.fullscreenSwitch.classList.remove("fullscreen-active");
|
||||
});
|
||||
} else {
|
||||
this.media.requestFullscreen().then(() => {
|
||||
this.elements.fullscreenSwitch.classList.add("fullscreen-active");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
changeVolume() {
|
||||
const volume = parseInt(this.elements.volumeLevel.value);
|
||||
if (!isNaN(volume)) {
|
||||
this.media.volume = volume / 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new MediaControls();
|
||||
})();
|
||||
|
||||
8
resources/neterror.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Error loading page</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Could not load the requested page: ${reason}</p>
|
||||
</body>
|
||||
</html>
|
||||
9
resources/not-found.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>about:not-found</title>
|
||||
</head>
|
||||
<body>
|
||||
<!-- courtesy of https://mozillians.org/blahblah -->
|
||||
<img src="tumbeast.png">
|
||||
</body>
|
||||
</html>
|
||||
13
resources/package-prefs.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"_comment": "this file is used to add some specific preferences to the Servo package (nightly builds)",
|
||||
"all": {},
|
||||
"macosx": {},
|
||||
"linux": {},
|
||||
"android": {},
|
||||
"windows": {},
|
||||
"vr": {
|
||||
"_comment": "settings specific to VR builds",
|
||||
"dom.webvr.enabled": true,
|
||||
"dom.webxr.enabled": true
|
||||
}
|
||||
}
|
||||
122
resources/prefs.json
Normal file
@@ -0,0 +1,122 @@
|
||||
{
|
||||
"devtools.server.enabled": false,
|
||||
"devtools.server.port": 0,
|
||||
"dom.bluetooth.enabled": false,
|
||||
"dom.bluetooth.testing.enabled": false,
|
||||
"dom.canvas_capture.enabled": false,
|
||||
"dom.canvas_text.enabled": true,
|
||||
"dom.compositionevent.enabled": false,
|
||||
"dom.customelements.enabled": true,
|
||||
"dom.document.dblclick_dist": 1,
|
||||
"dom.document.dblclick_timeout": 300,
|
||||
"dom.forcetouch.enabled": false,
|
||||
"dom.fullscreen.test": false,
|
||||
"dom.gamepad.enabled": false,
|
||||
"dom.imagebitmap.enabled": false,
|
||||
"dom.microdata.enabled": false,
|
||||
"dom.microdata.testing.enabled": false,
|
||||
"dom.mouseevent.which.enabled": false,
|
||||
"dom.mutation_observer.enabled": true,
|
||||
"dom.offscreen_canvas.enabled": false,
|
||||
"dom.permissions.enabled": false,
|
||||
"dom.permissions.testing.allowed_in_nonsecure_contexts": false,
|
||||
"dom.script.asynch": true,
|
||||
"dom.serviceworker.enabled": false,
|
||||
"dom.serviceworker.timeout_seconds": 60,
|
||||
"dom.servo_helpers.enabled": false,
|
||||
"dom.servoparser.async_html_tokenizer.enabled": false,
|
||||
"dom.shadowdom.enabled": false,
|
||||
"dom.svg.enabled": false,
|
||||
"dom.testable_crash.enabled": false,
|
||||
"dom.testbinding.enabled": false,
|
||||
"dom.testing.htmlinputelement.select_files.enabled": false,
|
||||
"dom.webgl2.enabled": false,
|
||||
"dom.webgpu.enabled": false,
|
||||
"dom.webrtc.enabled": false,
|
||||
"dom.webrtc.transceiver.enabled": false,
|
||||
"dom.webvr.enabled": false,
|
||||
"dom.webvr.event_polling_interval": 500,
|
||||
"dom.webvr.test": false,
|
||||
"dom.webvtt.enabled": false,
|
||||
"dom.webxr.enabled": true,
|
||||
"dom.webxr.first_person_observer_view": false,
|
||||
"dom.webxr.glwindow.cubemap": false,
|
||||
"dom.webxr.glwindow.enabled": true,
|
||||
"dom.webxr.glwindow.left-right": false,
|
||||
"dom.webxr.glwindow.red-cyan": false,
|
||||
"dom.webxr.glwindow.spherical": false,
|
||||
"dom.webxr.hands.enabled": false,
|
||||
"dom.webxr.layers.enabled": false,
|
||||
"dom.webxr.sessionavailable": false,
|
||||
"dom.webxr.test": false,
|
||||
"dom.webxr.unsafe-assume-user-intent": false,
|
||||
"dom.worklet.timeout_ms": 10,
|
||||
"gfx.subpixel-text-antialiasing.enabled": true,
|
||||
"gfx.texture-swizzling.enabled": true,
|
||||
"js.asmjs.enabled": true,
|
||||
"js.asyncstack.enabled": false,
|
||||
"js.baseline.enabled": true,
|
||||
"js.baseline.unsafe_eager_compilation.enabled": false,
|
||||
"js.discard_system_source.enabled": false,
|
||||
"js.dump_stack_on_debuggee_would_run.enabled": false,
|
||||
"js.ion.enabled": true,
|
||||
"js.ion.offthread_compilation.enabled": true,
|
||||
"js.ion.unsafe_eager_compilation.enabled": false,
|
||||
"js.mem.gc.allocation_threshold_avoid_interrupt_factor": 100,
|
||||
"js.mem.gc.allocation_threshold_factor": 100,
|
||||
"js.mem.gc.allocation_threshold_mb": 30,
|
||||
"js.mem.gc.compacting.enabled": true,
|
||||
"js.mem.gc.decommit_threshold_mb": 32,
|
||||
"js.mem.gc.dynamic_heap_growth.enabled": true,
|
||||
"js.mem.gc.dynamic_mark_slice.enabled": true,
|
||||
"js.mem.gc.empty_chunk_count_max": 30,
|
||||
"js.mem.gc.empty_chunk_count_min": 1,
|
||||
"js.mem.gc.high_frequency_heap_growth_max": 300,
|
||||
"js.mem.gc.high_frequency_heap_growth_min": 150,
|
||||
"js.mem.gc.high_frequency_high_limit_mb": 500,
|
||||
"js.mem.gc.high_frequency_low_limit_mb": 100,
|
||||
"js.mem.gc.high_frequency_time_limit_ms": 1000,
|
||||
"js.mem.gc.incremental.enabled": true,
|
||||
"js.mem.gc.incremental.slice_ms": 10,
|
||||
"js.mem.gc.low_frequency_heap_growth": 150,
|
||||
"js.mem.gc.per_zone.enabled": false,
|
||||
"js.mem.gc.zeal.frequency": 100,
|
||||
"js.mem.gc.zeal.level": 0,
|
||||
"js.mem.max": -1,
|
||||
"js.native_regex.enabled": true,
|
||||
"js.offthread_compilation.enabled": true,
|
||||
"js.parallel_parsing.enabled": true,
|
||||
"js.shared_memory.enabled": true,
|
||||
"js.strict.debug.enabled": false,
|
||||
"js.strict.enabled": false,
|
||||
"js.throw_on_asmjs_validation_failure.enabled": false,
|
||||
"js.throw_on_debuggee_would_run.enabled": false,
|
||||
"js.timers.minimum_duration": 1000,
|
||||
"js.wasm.baseline.enabled": true,
|
||||
"js.wasm.enabled": true,
|
||||
"js.wasm.ion.enabled": true,
|
||||
"js.werror.enabled": false,
|
||||
"layout.animations.test.enabled": false,
|
||||
"layout.columns.enabled": false,
|
||||
"layout.flexbox.enabled": false,
|
||||
"layout.legacy_layout": false,
|
||||
"layout.tables.enabled": false,
|
||||
"layout.threads": 3,
|
||||
"layout.writing-mode.enabled": false,
|
||||
"media.glvideo.enabled": false,
|
||||
"media.testing.enabled": false,
|
||||
"network.enforce_tls.enabled": false,
|
||||
"network.enforce_tls.localhost": false,
|
||||
"network.enforce_tls.onion": false,
|
||||
"network.http-cache.disabled": false,
|
||||
"network.mime.sniff": false,
|
||||
"session-history.max-length": 20,
|
||||
"shell.background-color.rgba": [1.0, 1.0, 1.0, 1.0],
|
||||
"shell.crash_reporter.enabled": false,
|
||||
"shell.homepage": "https://servo.org",
|
||||
"shell.keep_screen_on.enabled": false,
|
||||
"shell.native-orientation": "both",
|
||||
"shell.native-titlebar.enabled": true,
|
||||
"shell.searchpage": "https://duckduckgo.com/html/?q=%s",
|
||||
"webgl.testing.context_creation_error": false
|
||||
}
|
||||
267
resources/presentational-hints.css
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
https://html.spec.whatwg.org/multipage/#presentational-hints
|
||||
*/
|
||||
|
||||
@namespace url(http://www.w3.org/1999/xhtml);
|
||||
|
||||
|
||||
pre[wrap] { white-space: pre-wrap; }
|
||||
|
||||
div[align=left i] { text-align: -servo-left; }
|
||||
div[align=right i] { text-align: -servo-right; }
|
||||
div[align=center i], div[align=middle i] { text-align: -servo-center; }
|
||||
div[align=justify i] { text-align: justify; }
|
||||
|
||||
|
||||
br[clear=left i] { clear: left; }
|
||||
br[clear=right i] { clear: right; }
|
||||
br[clear=all i], br[clear=both i] { clear: both; }
|
||||
|
||||
|
||||
ol[type="1"], li[type="1"] { list-style-type: decimal; }
|
||||
ol[type=a s], li[type=a s] { list-style-type: lower-alpha; }
|
||||
ol[type=A s], li[type=A s] { list-style-type: upper-alpha; }
|
||||
ol[type=i s], li[type=i s] { list-style-type: lower-roman; }
|
||||
ol[type=I s], li[type=I s] { list-style-type: upper-roman; }
|
||||
ul[type=none i], li[type=none i] { list-style-type: none; }
|
||||
ul[type=disc i], li[type=disc i] { list-style-type: disc; }
|
||||
ul[type=circle i], li[type=circle i] { list-style-type: circle; }
|
||||
ul[type=square i], li[type=square i] { list-style-type: square; }
|
||||
|
||||
|
||||
table[align=left i] { float: left; }
|
||||
table[align=right i] { float: right; }
|
||||
table[align=center i] { margin-left: auto; margin-right: auto; }
|
||||
:matches(thead, tbody, tfoot, tr, td, th)[align=absmiddle i] {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
caption[align=bottom i] { caption-side: bottom; }
|
||||
p[align=left i], h1[align=left i], h2[align=left i], h3[align=left i], h4[align=left i], h5[align=left i], h6[align=left i] { text-align: left; }
|
||||
p[align=right i], h1[align=right i], h2[align=right i], h3[align=right i], h4[align=right i], h5[align=right i], h6[align=right i] { text-align: right; }
|
||||
p[align=center i], h1[align=center i], h2[align=center i], h3[align=center i], h4[align=center i], h5[align=center i], h6[align=center i] { text-align: center; }
|
||||
p[align=justify i], h1[align=justify i], h2[align=justify i], h3[align=justify i], h4[align=justify i], h5[align=justify i], h6[align=justify i] { text-align: justify; }
|
||||
thead[valign=top i], tbody[valign=top i], tfoot[valign=top i], tr[valign=top i], td[valign=top i], th[valign=top i] { vertical-align: top; }
|
||||
thead[valign=middle i], tbody[valign=middle i], tfoot[valign=middle i], tr[valign=middle i], td[valign=middle i], th[valign=middle i] { vertical-align: middle; }
|
||||
thead[valign=bottom i], tbody[valign=bottom i], tfoot[valign=bottom i], tr[valign=bottom i], td[valign=bottom i], th[valign=bottom i] { vertical-align: bottom; }
|
||||
thead[valign=baseline i], tbody[valign=baseline i], tfoot[valign=baseline i], tr[valign=baseline i], td[valign=baseline i], th[valign=baseline i] { vertical-align: baseline; }
|
||||
|
||||
td[nowrap], th[nowrap] { white-space: nowrap; }
|
||||
|
||||
table[rules=none i], table[rules=groups i], table[rules=rows i], table[rules=cols i], table[rules=all i] {
|
||||
border-style: hidden;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table:-servo-nonzero-border {
|
||||
border-style: outset;
|
||||
}
|
||||
table[frame=void i] { border-style: hidden; }
|
||||
table[frame=above i] { border-style: outset hidden hidden hidden; }
|
||||
table[frame=below i] { border-style: hidden hidden outset hidden; }
|
||||
table[frame=hsides i] { border-style: outset hidden outset hidden; }
|
||||
table[frame=lhs i] { border-style: hidden hidden hidden outset; }
|
||||
table[frame=rhs i] { border-style: hidden outset hidden hidden; }
|
||||
table[frame=vsides i] { border-style: hidden outset; }
|
||||
table[frame=box i], table[frame=border i] { border-style: outset; }
|
||||
|
||||
|
||||
table:-servo-nonzero-border > tr > td,
|
||||
table:-servo-nonzero-border > tr > th,
|
||||
table:-servo-nonzero-border > thead > tr > td,
|
||||
table:-servo-nonzero-border > thead > tr > th,
|
||||
table:-servo-nonzero-border > tbody > tr > td,
|
||||
table:-servo-nonzero-border > tbody > tr > th,
|
||||
table:-servo-nonzero-border > tfoot > tr > td,
|
||||
table:-servo-nonzero-border > tfoot > tr > th {
|
||||
border-width: 1px;
|
||||
border-style: inset;
|
||||
}
|
||||
|
||||
table[rules=none i] > tr > td, table[rules=groups i] > tr > td, table[rules=rows i] > tr > td, table[rules=none i] > tr > th, table[rules=groups i] > tr > th, table[rules=rows i] > tr > th,
|
||||
table[rules=none i] > thead > tr > td, table[rules=groups i] > thead > tr > td, table[rules=rows i] > thead > tr > td, table[rules=none i] > thead > tr > th, table[rules=groups i] > thead > tr > th, table[rules=rows i] > thead > tr > th,
|
||||
table[rules=none i] > tbody > tr > td, table[rules=groups i] > tbody > tr > td, table[rules=rows i] > tbody > tr > td, table[rules=none i] > tbody > tr > th, table[rules=groups i] > tbody > tr > th, table[rules=rows i] > tbody > tr > th,
|
||||
table[rules=none i] > tfoot > tr > td, table[rules=groups i] > tfoot > tr > td, table[rules=rows i] > tfoot > tr > td, table[rules=none i] > tfoot > tr > th, table[rules=groups i] > tfoot > tr > th, table[rules=rows i] > tfoot > tr > th {
|
||||
border-width: 1px;
|
||||
border-style: none;
|
||||
}
|
||||
table[rules=cols i] > tr > td, table[rules=cols i] > tr > th,
|
||||
table[rules=cols i] > thead > tr > td, table[rules=cols i] > thead > tr > th,
|
||||
table[rules=cols i] > tbody > tr > td, table[rules=cols i] > tbody > tr > th,
|
||||
table[rules=cols i] > tfoot > tr > td, table[rules=cols i] > tfoot > tr > th {
|
||||
border-width: 1px;
|
||||
border-style: none solid;
|
||||
}
|
||||
table[rules=all i] > tr > td, table[rules=all i] > tr > th,
|
||||
table[rules=all i] > thead > tr > td, table[rules=all i] > thead > tr > th,
|
||||
table[rules=all i] > tbody > tr > td, table[rules=all i] > tbody > tr > th,
|
||||
table[rules=all i] > tfoot > tr > td, table[rules=all i] > tfoot > tr > th {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
table[rules=groups i] > colgroup {
|
||||
border-left-width: 1px;
|
||||
border-left-style: solid;
|
||||
border-right-width: 1px;
|
||||
border-right-style: solid;
|
||||
}
|
||||
table[rules=groups i] > tr,
|
||||
table[rules=groups i] > thead > tr,
|
||||
table[rules=groups i] > tbody > tr,
|
||||
table[rules=groups i] > tfoot > tr {
|
||||
border-top-width: 1px;
|
||||
border-top-style: solid;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
table[rules=rows i] > tr,
|
||||
table[rules=rows i] > thead > tr,
|
||||
table[rules=rows i] > tbody > tr,
|
||||
table[rules=rows i] > tfoot > tr {
|
||||
border-top-width: 1px;
|
||||
border-top-style: solid;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
|
||||
hr[align=left] { margin-left: 0; margin-right: auto; }
|
||||
hr[align=right] { margin-left: auto; margin-right: 0; }
|
||||
hr[align=center] { margin-left: auto; margin-right: auto; }
|
||||
hr[color], hr[noshade] { border-style: solid; }
|
||||
|
||||
|
||||
|
||||
iframe[frameborder="0"], iframe[frameborder=no i] { border: none; }
|
||||
|
||||
embed[align=left i], iframe[align=left i], img[type=image i][align=left i], object[align=left i] {
|
||||
float: left;
|
||||
}
|
||||
embed[align=right i], iframe[align=right i], img[type=image i][align=right i], object[align=right i] {
|
||||
float: right;
|
||||
}
|
||||
embed[align=top i], iframe[align=top i], img[type=image i][align=top i], object[align=top i] {
|
||||
vertical-align: top;
|
||||
}
|
||||
embed[align=baseline i], iframe[align=baseline i], img[type=image i][align=baseline i], object[align=baseline i] {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
embed[align=texttop i], iframe[align=texttop i], img[type=image i][align=texttop i], object[align=texttop i] {
|
||||
vertical-align: text-top;
|
||||
}
|
||||
embed[align=absmiddle i], iframe[align=absmiddle i], img[type=image i][align=absmiddle i], object[align=absmiddle i],
|
||||
embed[align=abscenter i], iframe[align=abscenter i], img[type=image i][align=abscenter i], object[align=abscenter i] {
|
||||
vertical-align: middle;
|
||||
}
|
||||
embed[align=bottom i], iframe[align=bottom i], img[type=image i][align=bottom i], object[align=bottom i] {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
/*
|
||||
FIXME:
|
||||
:matches(embed, iframe, img, input[type=image i], object):matches([align=center i], [align=middle i]) {
|
||||
vertical-align: "aligns the vertical middle of the element with the parent element's baseline."
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
Presentational attributes which can not currently be expressed in CSS.
|
||||
FIXME: Deal with them with attr(foo dimension) and the like?
|
||||
|
||||
body
|
||||
marginheight
|
||||
marginwidth
|
||||
topmargin
|
||||
rightmargin
|
||||
bottommargin
|
||||
leftmargin
|
||||
background
|
||||
bgcolor
|
||||
text
|
||||
link
|
||||
vlink
|
||||
alink
|
||||
|
||||
frame, iframe
|
||||
marginheight
|
||||
marginwidth
|
||||
|
||||
font
|
||||
face
|
||||
color
|
||||
size
|
||||
|
||||
table
|
||||
cellspacing
|
||||
cellpadding
|
||||
hspace
|
||||
vspace
|
||||
height
|
||||
width
|
||||
bordercolor
|
||||
border
|
||||
|
||||
col
|
||||
width
|
||||
|
||||
tr
|
||||
height
|
||||
|
||||
td, th
|
||||
width
|
||||
height
|
||||
|
||||
caption, thead, tbody, tfoot, tr, td, and th
|
||||
align
|
||||
|
||||
table, thead, tbody, tfoot, tr, td, or th
|
||||
background
|
||||
bgcolor
|
||||
|
||||
(quirks mode) th, td
|
||||
nowrap
|
||||
|
||||
hr
|
||||
color
|
||||
noshade
|
||||
size
|
||||
width
|
||||
|
||||
legend
|
||||
align
|
||||
|
||||
embed, iframe, img, input[type=image i], object
|
||||
hspace
|
||||
vspace
|
||||
|
||||
img, input[type=image i], object
|
||||
border
|
||||
|
||||
embed, iframe, img, input[type=image i], object, video
|
||||
width
|
||||
height
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
Extra
|
||||
ol > li
|
||||
https://html.spec.whatwg.org/multipage/#ordinal-value
|
||||
col
|
||||
span
|
||||
colgroup (if not col child)
|
||||
span
|
||||
td, th
|
||||
colspan
|
||||
rowspan
|
||||
|
||||
:computed-value(text-align is initial) > th {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
https://html.spec.whatwg.org/multipage/#rendered-legend
|
||||
|
||||
*/
|
||||
|
||||
28
resources/privatekey_for_testing.key
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDwJhD2k5vROwKG
|
||||
ZFbOVShnx0nubyUrPXTe2wnUcU6Pr32qguIOmHD+TttSFVmU5TVe0UlMUkbyRjGM
|
||||
Lzguf/Eo0FoUKH2GbUpS07hGyxcqUQd+DzFWYY4hoLrWhd+ICPer7Y9OC/qiUOgv
|
||||
vP7hjaBI//ODWzN0AZbB+SyBfRq0hbLyUIFtfxV42UEC8qQzQItq9eGNSaalvM8a
|
||||
P8+XZu/4d1dll3u3qwdDoC7M9oEMCemHY+O6dnaJUu38+s41nyatSSKqDkZRFDvH
|
||||
8TCE7+PQRARkN3qs57EeyeYKhSwXYpgzOC4P38DpD6u6jcETpQZvZJv2x4Z+slrW
|
||||
48zO21vdAgMBAAECggEBAL8nLb14BUFoXTwoRkh61Gy23wxhgA6JHqv9Yjet8UDC
|
||||
CZ9eCx5fDSIAFuehgurX/8F3iYasvzg901aoh2nMAWPhZLJDAJeuCskfKcGACvJu
|
||||
CS64XSdLA92UmOQFL8aSjMJXmAgh1OC60fad06wqFXnF8kmOoMgoM542/swbjtQ9
|
||||
RUoXUKa2x9ve2b0og4iuEj/iZR+d2/HYPNtpNIFt7+rA1qCd2RIfMSAB6j3ohFNA
|
||||
AqVZdASgNznj5Sa8jz6zU40zdyLPIDYfCdi3Gmq42LaB4HWHiSpjx49aCQD6UV4+
|
||||
NxFsYi9WRA79DxdoeWsYm017G5NrHsELCdqwxg46fEECgYEA+/AJsiIjoBJ0SGoe
|
||||
Oqm86EbOtYtOEsMLF0Pk7OQejAN3VPqP6xN0u6unldo5RLJMsTlTHxIZuZNbXhw8
|
||||
aKWz0ULoXWi5J8Af2NHI0zaY0naGv3yew+6mq9xpZLHsY9wBWtT2tXEziH7gjPvk
|
||||
LmJ8+GCKSq9XLHoSyyjjqdr1aOUCgYEA9AVdhIFZ9EDb6zgnCjMvWlNFU/G2+WbU
|
||||
jCc9GRTFbzsahbawjuJ+47Ajz5aTBWOA1HE4wcMMMEjRebP/15CiCQs34+BIQckW
|
||||
2zvMipAh6dlbyx2O9t0gdIjAUNiYYMfuHYBQ3Nwmi6Nop3kHQuFHRh6uneGTxcEq
|
||||
wq3IEhN9T5kCgYAnip8a9Dy/LOZPT0h7UJSzqBldaQXR8AbXmfJeM9ePhiO+lKzt
|
||||
6lnR8rkUzfFbFNjwn9yp7b9X3wbiGHBqxEcauvOZZYxZ7s+QyixI6jLGJZA0kayh
|
||||
d910790izsIZUjhsNyyZgbDi5Xb86bQAi7itiwlpe2elNWupszs4N4N4+QKBgQDQ
|
||||
Le+dhtkeV2MHZm1v90Dzt0zTE8j9Nuwn8aXfSugxP+QdpUE1dSe5yeCTTit5km0r
|
||||
ULiSHXu9ibIkORsQZdTHEGoLH6Gldg/o0zhqEhLMtWHpg/sewoHYyX4AuvgswQR0
|
||||
6K6T7cF4qd0z2z4FobmzqaNhEDyInoaDdczVFwl2KQKBgQCHjZLwMZLg0be9i1IS
|
||||
jzaG2d5CF3WJ+A1zYQLXqzpX5qfbgsEIbpTtdCOhgf94YJsiJ9XdIG8OgGVxhrkT
|
||||
C41PGdIoq7wVxOwfW9qXQTOarO6ymFZxOnmHukpjfbcp54xDpcF73Xe6XkvmiESd
|
||||
gcNMfunAcg8sNgIGJzsKJ50+hw==
|
||||
-----END PRIVATE KEY-----
|
||||
8818
resources/public_domains.txt
Normal file
44
resources/quirks-mode.css
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
|
||||
https://html.spec.whatwg.org/multipage/#flow-content-3
|
||||
|
||||
> In quirks mode, the following rules are also expected to apply:
|
||||
|
||||
*/
|
||||
|
||||
@namespace url(http://www.w3.org/1999/xhtml);
|
||||
|
||||
|
||||
form { margin-bottom: 1em; }
|
||||
|
||||
|
||||
table {
|
||||
font-weight: initial;
|
||||
font-style: initial;
|
||||
font-variant: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
white-space: initial;
|
||||
/* text-align: initial; -- see FIXME below */
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME(pcwalton): Actually saying `text-align: initial` above breaks `<table>` inside `<center>`
|
||||
* in quirks mode. This is because we (following Gecko, WebKit, and Blink) implement the HTML5
|
||||
* align-descendants rules with a special `text-align: -servo-center`. `text-align: initial`, if
|
||||
* placed on the `<table>` element per the spec, would break this behavior. So we place it on
|
||||
* `<tbody>` instead.
|
||||
*/
|
||||
tbody {
|
||||
text-align: initial;
|
||||
}
|
||||
|
||||
|
||||
/* FIXME: https://html.spec.whatwg.org/multipage/#margin-collapsing-quirks */
|
||||
|
||||
|
||||
input:not([type=image]), textarea { box-sizing: border-box; }
|
||||
|
||||
|
||||
img[align=left i] { margin-right: 3px; }
|
||||
img[align=right i] { margin-left: 3px; }
|
||||
190
resources/quotes.css
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
|
||||
https://html.spec.whatwg.org/multipage/#quotes
|
||||
|
||||
> This block is automatically generated from the Unicode Common Locale Data Repository.
|
||||
> http://cldr.unicode.org/
|
||||
>
|
||||
> User agents are expected to use either the block [from the spec]
|
||||
> (which will be regularly updated)
|
||||
> or to automatically generate their own copy directly from the source material.
|
||||
> The language codes are derived from the CLDR file names.
|
||||
> The quotes are derived from the delimiter blocks,
|
||||
> with fallback handled as specified in the CLDR documentation.
|
||||
|
||||
*/
|
||||
|
||||
@namespace url(http://www.w3.org/1999/xhtml);
|
||||
|
||||
|
||||
:root { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(af), :not(:lang(af)) > :lang(af) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(agq), :not(:lang(agq)) > :lang(agq) { quotes: '\201e' '\201d' '\201a' '\2019' } /* „ ” ‚ ’ */
|
||||
:root:lang(ak), :not(:lang(ak)) > :lang(ak) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(am), :not(:lang(am)) > :lang(am) { quotes: '\00ab' '\00bb' '\2039' '\203a' } /* « » ‹ › */
|
||||
:root:lang(ar), :not(:lang(ar)) > :lang(ar) { quotes: '\201d' '\201c' '\2019' '\2018' } /* ” “ ’ ‘ */
|
||||
:root:lang(asa), :not(:lang(asa)) > :lang(asa) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(ast), :not(:lang(ast)) > :lang(ast) { quotes: '\00ab' '\00bb' '\201c' '\201d' } /* « » “ ” */
|
||||
:root:lang(az), :not(:lang(az)) > :lang(az) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(az-Cyrl), :not(:lang(az-Cyrl)) > :lang(az-Cyrl) { quotes: '\00ab' '\00bb' '\2039' '\203a' } /* « » ‹ › */
|
||||
:root:lang(bas), :not(:lang(bas)) > :lang(bas) { quotes: '\00ab' '\00bb' '\201e' '\201c' } /* « » „ “ */
|
||||
:root:lang(bem), :not(:lang(bem)) > :lang(bem) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(bez), :not(:lang(bez)) > :lang(bez) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(bg), :not(:lang(bg)) > :lang(bg) { quotes: '\201e' '\201c' '\201e' '\201c' } /* „ “ „ “ */
|
||||
:root:lang(bm), :not(:lang(bm)) > :lang(bm) { quotes: '\00ab' '\00bb' '\201c' '\201d' } /* « » “ ” */
|
||||
:root:lang(bn), :not(:lang(bn)) > :lang(bn) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(br), :not(:lang(br)) > :lang(br) { quotes: '\00ab' '\00bb' '\2039' '\203a' } /* « » ‹ › */
|
||||
:root:lang(brx), :not(:lang(brx)) > :lang(brx) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(bs), :not(:lang(bs)) > :lang(bs) { quotes: '\201e' '\201c' '\2018' '\2019' } /* „ “ ‘ ’ */
|
||||
:root:lang(bs-Cyrl), :not(:lang(bs-Cyrl)) > :lang(bs-Cyrl) { quotes: '\201e' '\201c' '\201a' '\2018' } /* „ “ ‚ ‘ */
|
||||
:root:lang(ca), :not(:lang(ca)) > :lang(ca) { quotes: '\00ab' '\00bb' '\201c' '\201d' } /* « » “ ” */
|
||||
:root:lang(cgg), :not(:lang(cgg)) > :lang(cgg) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(chr), :not(:lang(chr)) > :lang(chr) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(cs), :not(:lang(cs)) > :lang(cs) { quotes: '\201e' '\201c' '\201a' '\2018' } /* „ “ ‚ ‘ */
|
||||
:root:lang(cy), :not(:lang(cy)) > :lang(cy) { quotes: '\2018' '\2019' '\201c' '\201d' } /* ‘ ’ “ ” */
|
||||
:root:lang(da), :not(:lang(da)) > :lang(da) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(dav), :not(:lang(dav)) > :lang(dav) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(de), :not(:lang(de)) > :lang(de) { quotes: '\201e' '\201c' '\201a' '\2018' } /* „ “ ‚ ‘ */
|
||||
:root:lang(de-CH), :not(:lang(de-CH)) > :lang(de-CH) { quotes: '\00ab' '\00bb' '\2039' '\203a' } /* « » ‹ › */
|
||||
:root:lang(dje), :not(:lang(dje)) > :lang(dje) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(dsb), :not(:lang(dsb)) > :lang(dsb) { quotes: '\201e' '\201c' '\201a' '\2018' } /* „ “ ‚ ‘ */
|
||||
:root:lang(dua), :not(:lang(dua)) > :lang(dua) { quotes: '\00ab' '\00bb' '\2018' '\2019' } /* « » ‘ ’ */
|
||||
:root:lang(dyo), :not(:lang(dyo)) > :lang(dyo) { quotes: '\00ab' '\00bb' '\201c' '\201d' } /* « » “ ” */
|
||||
:root:lang(dz), :not(:lang(dz)) > :lang(dz) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(ebu), :not(:lang(ebu)) > :lang(ebu) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(ee), :not(:lang(ee)) > :lang(ee) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(el), :not(:lang(el)) > :lang(el) { quotes: '\00ab' '\00bb' '\0022' '\0022' } /* « » " " */
|
||||
:root:lang(en), :not(:lang(en)) > :lang(en) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(es), :not(:lang(es)) > :lang(es) { quotes: '\00ab' '\00bb' '\201c' '\201d' } /* « » “ ” */
|
||||
:root:lang(et), :not(:lang(et)) > :lang(et) { quotes: '\201e' '\201c' '\201a' '\2018' } /* „ “ ‚ ‘ */
|
||||
:root:lang(eu), :not(:lang(eu)) > :lang(eu) { quotes: '\0022' '\0022' '\0022' '\0022' } /* " " " " */
|
||||
:root:lang(ewo), :not(:lang(ewo)) > :lang(ewo) { quotes: '\00ab' '\00bb' '\201c' '\201d' } /* « » “ ” */
|
||||
:root:lang(fa), :not(:lang(fa)) > :lang(fa) { quotes: '\00ab' '\00bb' '\2039' '\203a' } /* « » ‹ › */
|
||||
:root:lang(ff), :not(:lang(ff)) > :lang(ff) { quotes: '\201e' '\201d' '\201a' '\2019' } /* „ ” ‚ ’ */
|
||||
:root:lang(fi), :not(:lang(fi)) > :lang(fi) { quotes: '\201d' '\201d' '\2019' '\2019' } /* ” ” ’ ’ */
|
||||
:root:lang(fil), :not(:lang(fil)) > :lang(fil) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(fr), :not(:lang(fr)) > :lang(fr) { quotes: '\00ab' '\00bb' '\00ab' '\00bb' } /* « » « » */
|
||||
:root:lang(fr-CA), :not(:lang(fr-CA)) > :lang(fr-CA) { quotes: '\00ab' '\00bb' '\2039' '\203a' } /* « » ‹ › */
|
||||
:root:lang(fr-CH), :not(:lang(fr-CH)) > :lang(fr-CH) { quotes: '\00ab' '\00bb' '\2039' '\203a' } /* « » ‹ › */
|
||||
:root:lang(ga), :not(:lang(ga)) > :lang(ga) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(gd), :not(:lang(gd)) > :lang(gd) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(gl), :not(:lang(gl)) > :lang(gl) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(gsw), :not(:lang(gsw)) > :lang(gsw) { quotes: '\00ab' '\00bb' '\2039' '\203a' } /* « » ‹ › */
|
||||
:root:lang(gu), :not(:lang(gu)) > :lang(gu) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(guz), :not(:lang(guz)) > :lang(guz) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(ha), :not(:lang(ha)) > :lang(ha) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(he), :not(:lang(he)) > :lang(he) { quotes: '\05f4' '\05f4' '\05f3' '\05f3' } /* ״ ״ ׳ ׳ */
|
||||
:root:lang(hi), :not(:lang(hi)) > :lang(hi) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(hr), :not(:lang(hr)) > :lang(hr) { quotes: '\201e' '\201c' '\201a' '\2018' } /* „ “ ‚ ‘ */
|
||||
:root:lang(hsb), :not(:lang(hsb)) > :lang(hsb) { quotes: '\201e' '\201c' '\201a' '\2018' } /* „ “ ‚ ‘ */
|
||||
:root:lang(hu), :not(:lang(hu)) > :lang(hu) { quotes: '\201e' '\201d' '\00bb' '\00ab' } /* „ ” » « */
|
||||
:root:lang(hy), :not(:lang(hy)) > :lang(hy) { quotes: '\00ab' '\00bb' '\00ab' '\00bb' } /* « » « » */
|
||||
:root:lang(id), :not(:lang(id)) > :lang(id) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(ig), :not(:lang(ig)) > :lang(ig) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(is), :not(:lang(is)) > :lang(is) { quotes: '\201e' '\201c' '\201a' '\2018' } /* „ “ ‚ ‘ */
|
||||
:root:lang(it), :not(:lang(it)) > :lang(it) { quotes: '\00ab' '\00bb' '\201c' '\201d' } /* « » “ ” */
|
||||
:root:lang(ja), :not(:lang(ja)) > :lang(ja) { quotes: '\300c' '\300d' '\300e' '\300f' } /* 「 」 『 』 */
|
||||
:root:lang(jgo), :not(:lang(jgo)) > :lang(jgo) { quotes: '\00ab' '\00bb' '\2039' '\203a' } /* « » ‹ › */
|
||||
:root:lang(jmc), :not(:lang(jmc)) > :lang(jmc) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(ka), :not(:lang(ka)) > :lang(ka) { quotes: '\201e' '\201c' '\00ab' '\00bb' } /* „ “ « » */
|
||||
:root:lang(kab), :not(:lang(kab)) > :lang(kab) { quotes: '\00ab' '\00bb' '\201c' '\201d' } /* « » “ ” */
|
||||
:root:lang(kam), :not(:lang(kam)) > :lang(kam) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(kde), :not(:lang(kde)) > :lang(kde) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(kea), :not(:lang(kea)) > :lang(kea) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(khq), :not(:lang(khq)) > :lang(khq) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(ki), :not(:lang(ki)) > :lang(ki) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(kk), :not(:lang(kk)) > :lang(kk) { quotes: '\201c' '\0022' '\2018' '\2019' } /* “ " ‘ ’ */
|
||||
:root:lang(kkj), :not(:lang(kkj)) > :lang(kkj) { quotes: '\00ab' '\00bb' '\2039' '\203a' } /* « » ‹ › */
|
||||
:root:lang(kln), :not(:lang(kln)) > :lang(kln) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(km), :not(:lang(km)) > :lang(km) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(kn), :not(:lang(kn)) > :lang(kn) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(ko), :not(:lang(ko)) > :lang(ko) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(ksb), :not(:lang(ksb)) > :lang(ksb) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(ksf), :not(:lang(ksf)) > :lang(ksf) { quotes: '\00ab' '\00bb' '\2018' '\2019' } /* « » ‘ ’ */
|
||||
:root:lang(ky), :not(:lang(ky)) > :lang(ky) { quotes: '\00ab' '\00bb' '\201e' '\201c' } /* « » „ “ */
|
||||
:root:lang(lag), :not(:lang(lag)) > :lang(lag) { quotes: '\201d' '\201d' '\2019' '\2019' } /* ” ” ’ ’ */
|
||||
:root:lang(lb), :not(:lang(lb)) > :lang(lb) { quotes: '\201e' '\201c' '\201a' '\2018' } /* „ “ ‚ ‘ */
|
||||
:root:lang(lg), :not(:lang(lg)) > :lang(lg) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(ln), :not(:lang(ln)) > :lang(ln) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(lo), :not(:lang(lo)) > :lang(lo) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(lt), :not(:lang(lt)) > :lang(lt) { quotes: '\201e' '\201c' '\201e' '\201c' } /* „ “ „ “ */
|
||||
:root:lang(lu), :not(:lang(lu)) > :lang(lu) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(luo), :not(:lang(luo)) > :lang(luo) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(luy), :not(:lang(luy)) > :lang(luy) { quotes: '\201e' '\201c' '\201a' '\2018' } /* „ “ ‚ ‘ */
|
||||
:root:lang(lv), :not(:lang(lv)) > :lang(lv) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(mas), :not(:lang(mas)) > :lang(mas) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(mer), :not(:lang(mer)) > :lang(mer) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(mfe), :not(:lang(mfe)) > :lang(mfe) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(mg), :not(:lang(mg)) > :lang(mg) { quotes: '\00ab' '\00bb' '\201c' '\201d' } /* « » “ ” */
|
||||
:root:lang(mgo), :not(:lang(mgo)) > :lang(mgo) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(mk), :not(:lang(mk)) > :lang(mk) { quotes: '\201e' '\201c' '\201a' '\2018' } /* „ “ ‚ ‘ */
|
||||
:root:lang(ml), :not(:lang(ml)) > :lang(ml) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(mn), :not(:lang(mn)) > :lang(mn) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(mr), :not(:lang(mr)) > :lang(mr) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(ms), :not(:lang(ms)) > :lang(ms) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(mt), :not(:lang(mt)) > :lang(mt) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(mua), :not(:lang(mua)) > :lang(mua) { quotes: '\00ab' '\00bb' '\201c' '\201d' } /* « » “ ” */
|
||||
:root:lang(my), :not(:lang(my)) > :lang(my) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(naq), :not(:lang(naq)) > :lang(naq) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(nb), :not(:lang(nb)) > :lang(nb) { quotes: '\00ab' '\00bb' '\2018' '\2019' } /* « » ‘ ’ */
|
||||
:root:lang(nd), :not(:lang(nd)) > :lang(nd) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(ne), :not(:lang(ne)) > :lang(ne) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(nl), :not(:lang(nl)) > :lang(nl) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(nmg), :not(:lang(nmg)) > :lang(nmg) { quotes: '\201e' '\201d' '\00ab' '\00bb' } /* „ ” « » */
|
||||
:root:lang(nn), :not(:lang(nn)) > :lang(nn) { quotes: '\00ab' '\00bb' '\2018' '\2019' } /* « » ‘ ’ */
|
||||
:root:lang(nnh), :not(:lang(nnh)) > :lang(nnh) { quotes: '\00ab' '\00bb' '\201c' '\201d' } /* « » “ ” */
|
||||
:root:lang(nus), :not(:lang(nus)) > :lang(nus) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(nyn), :not(:lang(nyn)) > :lang(nyn) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(pa), :not(:lang(pa)) > :lang(pa) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(pl), :not(:lang(pl)) > :lang(pl) { quotes: '\201e' '\201d' '\00ab' '\00bb' } /* „ ” « » */
|
||||
:root:lang(pt), :not(:lang(pt)) > :lang(pt) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(pt-PT), :not(:lang(pt-PT)) > :lang(pt-PT) { quotes: '\00ab' '\00bb' '\201c' '\201d' } /* « » “ ” */
|
||||
:root:lang(rn), :not(:lang(rn)) > :lang(rn) { quotes: '\201d' '\201d' '\2019' '\2019' } /* ” ” ’ ’ */
|
||||
:root:lang(ro), :not(:lang(ro)) > :lang(ro) { quotes: '\201c' '\201d' '\00ab' '\00bb' } /* “ ” « » */
|
||||
:root:lang(rof), :not(:lang(rof)) > :lang(rof) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(ru), :not(:lang(ru)) > :lang(ru) { quotes: '\00ab' '\00bb' '\201e' '\201c' } /* « » „ “ */
|
||||
:root:lang(rw), :not(:lang(rw)) > :lang(rw) { quotes: '\00ab' '\00bb' '\2018' '\2019' } /* « » ‘ ’ */
|
||||
:root:lang(rwk), :not(:lang(rwk)) > :lang(rwk) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(saq), :not(:lang(saq)) > :lang(saq) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(sbp), :not(:lang(sbp)) > :lang(sbp) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(seh), :not(:lang(seh)) > :lang(seh) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(ses), :not(:lang(ses)) > :lang(ses) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(sg), :not(:lang(sg)) > :lang(sg) { quotes: '\00ab' '\00bb' '\201c' '\201d' } /* « » “ ” */
|
||||
:root:lang(shi), :not(:lang(shi)) > :lang(shi) { quotes: '\00ab' '\00bb' '\201e' '\201d' } /* « » „ ” */
|
||||
:root:lang(shi-Latn), :not(:lang(shi-Latn)) > :lang(shi-Latn) { quotes: '\00ab' '\00bb' '\201e' '\201d' } /* « » „ ” */
|
||||
:root:lang(si), :not(:lang(si)) > :lang(si) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(sk), :not(:lang(sk)) > :lang(sk) { quotes: '\201e' '\201c' '\201a' '\2018' } /* „ “ ‚ ‘ */
|
||||
:root:lang(sl), :not(:lang(sl)) > :lang(sl) { quotes: '\201e' '\201c' '\201a' '\2018' } /* „ “ ‚ ‘ */
|
||||
:root:lang(sn), :not(:lang(sn)) > :lang(sn) { quotes: '\201d' '\201d' '\2019' '\2019' } /* ” ” ’ ’ */
|
||||
:root:lang(so), :not(:lang(so)) > :lang(so) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(sq), :not(:lang(sq)) > :lang(sq) { quotes: '\00ab' '\00bb' '\201c' '\201d' } /* « » “ ” */
|
||||
:root:lang(sr), :not(:lang(sr)) > :lang(sr) { quotes: '\201e' '\201c' '\2018' '\2018' } /* „ “ ‘ ‘ */
|
||||
:root:lang(sr-Latn), :not(:lang(sr-Latn)) > :lang(sr-Latn) { quotes: '\201e' '\201c' '\2018' '\2018' } /* „ “ ‘ ‘ */
|
||||
:root:lang(sv), :not(:lang(sv)) > :lang(sv) { quotes: '\201d' '\201d' '\2019' '\2019' } /* ” ” ’ ’ */
|
||||
:root:lang(sw), :not(:lang(sw)) > :lang(sw) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(swc), :not(:lang(swc)) > :lang(swc) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(ta), :not(:lang(ta)) > :lang(ta) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(te), :not(:lang(te)) > :lang(te) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(teo), :not(:lang(teo)) > :lang(teo) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(th), :not(:lang(th)) > :lang(th) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(ti-ER), :not(:lang(ti-ER)) > :lang(ti-ER) { quotes: '\2018' '\2019' '\201c' '\201d' } /* ‘ ’ “ ” */
|
||||
:root:lang(to), :not(:lang(to)) > :lang(to) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(tr), :not(:lang(tr)) > :lang(tr) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(twq), :not(:lang(twq)) > :lang(twq) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(tzm), :not(:lang(tzm)) > :lang(tzm) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(uk), :not(:lang(uk)) > :lang(uk) { quotes: '\00ab' '\00bb' '\201e' '\201c' } /* « » „ “ */
|
||||
:root:lang(ur), :not(:lang(ur)) > :lang(ur) { quotes: '\201d' '\201c' '\2019' '\2018' } /* ” “ ’ ‘ */
|
||||
:root:lang(ur-IN), :not(:lang(ur-IN)) > :lang(ur-IN) { quotes: '\0022' '\0022' '\0027' '\0027' } /* " " ' ' */
|
||||
:root:lang(uz), :not(:lang(uz)) > :lang(uz) { quotes: '\0022' '\0022' '\0027' '\0027' } /* " " ' ' */
|
||||
:root:lang(uz-Cyrl), :not(:lang(uz-Cyrl)) > :lang(uz-Cyrl) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(vai), :not(:lang(vai)) > :lang(vai) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(vai-Latn), :not(:lang(vai-Latn)) > :lang(vai-Latn) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(vi), :not(:lang(vi)) > :lang(vi) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(vun), :not(:lang(vun)) > :lang(vun) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(xog), :not(:lang(xog)) > :lang(xog) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(yav), :not(:lang(yav)) > :lang(yav) { quotes: '\00ab' '\00bb' '\00ab' '\00bb' } /* « » « » */
|
||||
:root:lang(yo), :not(:lang(yo)) > :lang(yo) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(zgh), :not(:lang(zgh)) > :lang(zgh) { quotes: '\00ab' '\00bb' '\201e' '\201d' } /* « » „ ” */
|
||||
:root:lang(zh), :not(:lang(zh)) > :lang(zh) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
:root:lang(zh-Hant), :not(:lang(zh-Hant)) > :lang(zh-Hant) { quotes: '\300c' '\300d' '\300e' '\300f' } /* 「 」 『 』 */
|
||||
:root:lang(zu), :not(:lang(zu)) > :lang(zu) { quotes: '\201c' '\201d' '\2018' '\2019' } /* “ ” ‘ ’ */
|
||||
BIN
resources/rippy.png
Normal file
|
After Width: | Height: | Size: 253 B |
23
resources/self_signed_certificate_for_testing.crt
Normal file
@@ -0,0 +1,23 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDxjCCAq6gAwIBAgIJAIPzh6mmZhGkMA0GCSqGSIb3DQEBCwUAMHgxCzAJBgNV
|
||||
BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg
|
||||
Q29tcGFueSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDEgMB4GCSqGSIb3DQEJARYR
|
||||
dGVzdGluZ0BzZXJ2by5vcmcwHhcNMTcxMjI0MTQxMjA0WhcNMjcxMjIyMTQxMjA0
|
||||
WjB4MQswCQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQK
|
||||
DBNEZWZhdWx0IENvbXBhbnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QxIDAeBgkq
|
||||
hkiG9w0BCQEWEXRlc3RpbmdAc2Vydm8ub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||
AQ8AMIIBCgKCAQEA8CYQ9pOb0TsChmRWzlUoZ8dJ7m8lKz103tsJ1HFOj699qoLi
|
||||
Dphw/k7bUhVZlOU1XtFJTFJG8kYxjC84Ln/xKNBaFCh9hm1KUtO4RssXKlEHfg8x
|
||||
VmGOIaC61oXfiAj3q+2PTgv6olDoL7z+4Y2gSP/zg1szdAGWwfksgX0atIWy8lCB
|
||||
bX8VeNlBAvKkM0CLavXhjUmmpbzPGj/Pl2bv+HdXZZd7t6sHQ6AuzPaBDAnph2Pj
|
||||
unZ2iVLt/PrONZ8mrUkiqg5GURQ7x/EwhO/j0EQEZDd6rOexHsnmCoUsF2KYMzgu
|
||||
D9/A6Q+ruo3BE6UGb2Sb9seGfrJa1uPMzttb3QIDAQABo1MwUTAdBgNVHQ4EFgQU
|
||||
rzyrtcToBhyprrcbQ698ysb4mqIwHwYDVR0jBBgwFoAUrzyrtcToBhyprrcbQ698
|
||||
ysb4mqIwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAffZmMwi5
|
||||
ljhc0pM2DUWjfari4pvXzRpPUErTtMxuXQVijPhwOOWFI91xoEZUojxhOr+0Ran9
|
||||
7OulbgaTN0xMNwSs5cdS/KLY/nuIz0J8zYeW/VfIm+9fAKxt0cqORQppd6nTnfhl
|
||||
Sfr7MBE9su3fMq142voUgEwb4zxKq/tnlCzkWMAju+EPdTHW+HRhz8nEy/DVThiY
|
||||
2gTQG5tajQV7XZyWBozLiCjx0I2sidC7uxoy9o9yQRXvikeNxLdiOZlBP25IHTM+
|
||||
57uYE15RiCOOQB5vYH4L8ISRxDmNRYBSi5HFc68URqOuakpmGDJ8HNMJRb0m8PbR
|
||||
zDwuZIy1uC+UBg==
|
||||
-----END CERTIFICATE-----
|
||||
273
resources/servo.css
Normal file
@@ -0,0 +1,273 @@
|
||||
button {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
background: white;
|
||||
min-height: 1.0em;
|
||||
padding: 0em;
|
||||
padding-left: 0.25em;
|
||||
padding-right: 0.25em;
|
||||
border: solid lightgrey 1px;
|
||||
color: black;
|
||||
font-family: sans-serif;
|
||||
font-size: 0.8333em;
|
||||
text-align: left;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
textarea {
|
||||
background: white;
|
||||
min-height: 1.0em;
|
||||
padding: 0em;
|
||||
padding-left: 0.25em;
|
||||
padding-right: 0.25em;
|
||||
border: solid lightgrey 1px;
|
||||
color: black;
|
||||
font-family: sans-serif;
|
||||
font-size: 0.8333em;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
input::selection,
|
||||
textarea::selection {
|
||||
background: rgba(176, 214, 255, 1.0);
|
||||
color: black;
|
||||
}
|
||||
|
||||
button,
|
||||
input[type="button"],
|
||||
input[type="submit"],
|
||||
input[type="reset"] {
|
||||
background: lightgrey;
|
||||
border-top: solid 1px #EEEEEE;
|
||||
border-left: solid 1px #CCCCCC;
|
||||
border-right: solid 1px #999999;
|
||||
border-bottom: solid 1px #999999;
|
||||
text-align: center;
|
||||
color: black;
|
||||
}
|
||||
|
||||
input[type="hidden"] { display: none !important }
|
||||
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
font-family: monospace !important;
|
||||
border: none !important;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
input[type="checkbox"]::before {
|
||||
display: inline-block;
|
||||
border: solid currentcolor 1px;
|
||||
content: "";
|
||||
padding: 0;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked::before { content: "✓"; }
|
||||
input[type="checkbox"]:indeterminate::before { content: "-"; }
|
||||
|
||||
input[type="radio"]::before {
|
||||
display: inline-block;
|
||||
border: solid currentcolor 1px;
|
||||
content: "";
|
||||
padding: 0;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input[type="radio"]:checked::before { content: "●"; line-height: 1em; }
|
||||
|
||||
input[type="file"]::before {
|
||||
content: "Choose File";
|
||||
}
|
||||
|
||||
input[type="file"] {
|
||||
background: lightgrey;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
color: black;
|
||||
}
|
||||
|
||||
select {
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
select[multiple] { padding: 0em 0.25em; }
|
||||
select:not([multiple]) { padding: 0.25em 0.5em; border-radius: 6px; }
|
||||
|
||||
select:not([multiple])::after {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
border-width: 5.2px 3px 0 3px;
|
||||
border-style: solid;
|
||||
border-color: currentcolor transparent transparent transparent;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
select:not([multiple]) option { display: none !important; }
|
||||
select:not([multiple]) option[selected] { display: inline !important; }
|
||||
select[multiple] option { display: block !important; }
|
||||
select[multiple] option[selected] { background-color: grey; color: white; }
|
||||
select[multiple]:focus option[selected] { background-color: darkblue; }
|
||||
|
||||
td[align="left"] { text-align: left; }
|
||||
td[align="center"] { text-align: center; }
|
||||
td[align="right"] { text-align: right; }
|
||||
|
||||
center { text-align: -servo-center; }
|
||||
|
||||
label { cursor: default; }
|
||||
|
||||
input:not([type=radio i]):not([type=checkbox i]):not([type=reset i]):not([type=button i]):not([type=submit i]),
|
||||
textarea {
|
||||
cursor: text;
|
||||
overflow: hidden;
|
||||
-servo-overflow-clip-box: content-box;
|
||||
}
|
||||
|
||||
input:not([type=radio i]):not([type=checkbox i]):not([type=reset i]):not([type=button i]):not([type=submit i]) {
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements */
|
||||
details {
|
||||
display: block;
|
||||
}
|
||||
|
||||
details::-servo-details-summary {
|
||||
margin-left: 40px;
|
||||
display: list-item;
|
||||
list-style: disclosure-closed;
|
||||
}
|
||||
|
||||
details[open]::-servo-details-summary {
|
||||
list-style: disclosure-open;
|
||||
}
|
||||
|
||||
*|*::-servo-details-content {
|
||||
margin-left: 40px;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Until servo supports svg properly, make sure to at least prevent svg
|
||||
* children from being layed out and rendered like usual html.
|
||||
* https://github.com/servo/servo/issues/10646
|
||||
*/
|
||||
svg > * {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*
|
||||
* For most (but not all) anon-boxes, we inherit all values from the
|
||||
* parent.
|
||||
*
|
||||
* Anonymous table flows shouldn't inherit their parents properties in order
|
||||
* to avoid doubling up styles such as transformations. Same for text and such.
|
||||
*
|
||||
* For tables, we do want style to inherit, because TableWrapper is
|
||||
* responsible for handling clipping and scrolling, while Table is
|
||||
* responsible for creating stacking contexts.
|
||||
*
|
||||
* StackingContextCollectionFlags makes sure this is processed
|
||||
* properly.
|
||||
*
|
||||
* FIXME(emilio): inheriting all is a very strong hammer, and it's likely
|
||||
* broken for stuff like table backgrounds and such. Gecko explicitly inherits
|
||||
* what it wants, which seems a bit better off-hand.
|
||||
*/
|
||||
*|*::-servo-legacy-anonymous-table,
|
||||
*|*::-servo-legacy-anonymous-table-wrapper,
|
||||
*|*::-servo-legacy-table-wrapper,
|
||||
*|*::-servo-legacy-anonymous-block,
|
||||
*|*::-servo-legacy-inline-block-wrapper,
|
||||
*|*::-servo-legacy-inline-absolute {
|
||||
all: inherit;
|
||||
}
|
||||
|
||||
/* style for text node. */
|
||||
*|*::-servo-legacy-text {
|
||||
text-overflow: inherit;
|
||||
overflow: inherit;
|
||||
}
|
||||
|
||||
*|*::-servo-legacy-table-wrapper {
|
||||
display: table;
|
||||
border: none;
|
||||
}
|
||||
|
||||
*|*::-servo-legacy-anonymous-table-wrapper {
|
||||
position: static;
|
||||
margin: 0;
|
||||
counter-increment: none;
|
||||
|
||||
/* We don't want anonymous table parts to inherit hidden overflow, because
|
||||
* they will create extra unnecessary ClipScrollNodes which also throws
|
||||
* off assignment of contained flows. */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
*|*::-servo-legacy-anonymous-table {
|
||||
display: table;
|
||||
position: static;
|
||||
border: none;
|
||||
padding: 0;
|
||||
counter-increment: none;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
*|*::-servo-anonymous-table-row {
|
||||
display: table-row;
|
||||
position: static;
|
||||
border: none;
|
||||
counter-increment: none;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
*|*::-servo-anonymous-table-cell {
|
||||
display: table-cell;
|
||||
position: static;
|
||||
border: none;
|
||||
counter-increment: none;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
*|*::-servo-legacy-anonymous-block {
|
||||
display: block;
|
||||
position: static;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* The outer fragment wrapper of an inline-block. */
|
||||
*|*::-servo-legacy-inline-block-wrapper {
|
||||
position: static;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* The outer fragment wrapper of an inline absolute hypothetical fragment. */
|
||||
*|*::-servo-legacy-inline-absolute {
|
||||
clip: auto;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
BIN
resources/servo.icns
Normal file
BIN
resources/servo.ico
Normal file
|
After Width: | Height: | Size: 29 KiB |
21
resources/servo.svg
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 63 (92445) - https://sketch.com -->
|
||||
<title>GitHub - Page</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="GitHub---Page" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Group" transform="translate(10.000000, 10.000000)">
|
||||
<g id="Icon---Color">
|
||||
<g id="servo">
|
||||
<g id="o" transform="translate(0.316840, 0.042007)">
|
||||
<path d="M16.7017651,48.9189873 L36.8920575,63.6007304 L36.8920575,78.4179 C21.2468561,77.5053684 8.0646958,67.4117359 2.64512247,53.4412684 L16.7017651,48.9189873 Z" id="Path" fill="#1192E8"></path>
|
||||
<path d="M61.7159566,48.9189873 L75.7727775,53.4412684 C70.3532248,67.4116829 57.171144,77.5052918 41.5261865,78.4178896 L41.5258425,63.6007304 L61.7159566,48.9189873 Z" id="Path" fill="#1192E8"></path>
|
||||
<path d="M14.3755249,8.8729592 L23.0441693,20.8542542 L15.3761462,44.473866 L1.2281496,49.0256385 C0.477806609,46.0989575 0.0562613624,43.0403504 0.00525287907,39.8915922 L1.14815464e-13,39.2426433 C1.14815464e-13,27.0016423 5.59987531,16.0696862 14.3755249,8.8729592 Z" id="Path" fill="#009D9A"></path>
|
||||
<path d="M64.0423751,8.8729592 C72.8180247,16.0696862 78.4179,27.0016423 78.4179,39.2426433 C78.4179,42.6202989 77.9915412,45.8982875 77.1897504,49.0256385 L63.0415756,44.473866 L55.3735525,20.8542542 L64.0423751,8.8729592 Z" id="Path" fill="#009D9A"></path>
|
||||
<path d="M39.20895,-5.62820901e-15 C46.969487,-5.62820901e-15 54.2034284,2.25656145 60.2914837,6.14986174 L51.5922057,18.1734465 L26.825516,18.1734465 L18.1264163,6.14986174 C24.0445944,2.3651976 31.0456026,0.12720565 38.5605583,0.00525739302 L39.20895,-5.62820901e-15 Z" id="Path" fill="#42BE65"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
BIN
resources/servo_1024.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
resources/servo_64.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/tumbeast.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
6
resources/user-agent-js/00.example.js
Normal file
@@ -0,0 +1,6 @@
|
||||
// Keep files in this directory which you would like executed before
|
||||
// any other script when servo is run with `--userscripts`
|
||||
// Files are sorted alphabetically. When committing polyfills
|
||||
// order them with numbers, e.g. `01.innerhtml.js` will be executed before
|
||||
// `05.jquery.js`
|
||||
onunhandledrejection = (e) => console.error("xxxjdm error: " + JSON.stringify(e.reason));
|
||||
316
resources/user-agent.css
Normal file
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
https://html.spec.whatwg.org/multipage/#form-controls
|
||||
*/
|
||||
|
||||
@namespace url(http://www.w3.org/1999/xhtml);
|
||||
|
||||
/*
|
||||
FIXME: Uncomment this when :lang() is supported, or do something equivalent.
|
||||
@import url(quotes.css);
|
||||
*/
|
||||
|
||||
[hidden], area, base, basefont, datalist, head, link, menu[type=popup i], meta,
|
||||
noembed, noframes, param, rp, script, source, style, template, track, title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
embed[hidden] { display: inline; height: 0; width: 0; }
|
||||
|
||||
/* FIXME: only if scripting is enabled */
|
||||
noscript { display: none !important; }
|
||||
|
||||
input[type=hidden i] { display: none !important; }
|
||||
|
||||
|
||||
html, body { display: block; }
|
||||
|
||||
body { margin: 8px; }
|
||||
|
||||
|
||||
address, blockquote, center, div, figure, figcaption, footer, form, header, hr,
|
||||
legend, listing, main, p, plaintext, pre, summary, xmp {
|
||||
display: block;
|
||||
}
|
||||
|
||||
blockquote, figure, listing, p, plaintext, pre, xmp {
|
||||
margin-top: 1em; margin-bottom: 1em;
|
||||
}
|
||||
|
||||
blockquote, figure { margin-left: 40px; margin-right: 40px; }
|
||||
|
||||
address { font-style: italic; }
|
||||
listing, plaintext, pre, xmp {
|
||||
font-family: monospace; white-space: pre;
|
||||
}
|
||||
|
||||
dialog:not([open]) { display: none; }
|
||||
dialog {
|
||||
position: absolute;
|
||||
left: 0; right: 0;
|
||||
/* FIXME: support fit-content */
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
margin: auto;
|
||||
border: solid;
|
||||
padding: 1em;
|
||||
background: white;
|
||||
color: black;
|
||||
}
|
||||
/* FIXME: support ::backdrop */
|
||||
dialog::backdrop {
|
||||
position: fixed;
|
||||
top: 0; right: 0; bottom: 0; left: 0;
|
||||
background: rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* for small devices, modal dialogs go full-screen */
|
||||
@media screen and (max-width: 540px) {
|
||||
/* FIXME: support :modal */
|
||||
dialog:modal {
|
||||
top: 0;
|
||||
width: auto;
|
||||
margin: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cite, dfn, em, i, var { font-style: italic; }
|
||||
b, strong { font-weight: bolder; }
|
||||
code, kbd, samp, tt { font-family: monospace; }
|
||||
big { font-size: larger; }
|
||||
small { font-size: smaller; }
|
||||
|
||||
sub { vertical-align: sub; }
|
||||
sup { vertical-align: super; }
|
||||
sub, sup { line-height: normal; font-size: smaller; }
|
||||
|
||||
ruby { display: ruby; }
|
||||
rt { display: ruby-text; }
|
||||
|
||||
/*
|
||||
* All tag names that can be links are listed here, because applying pseudo-class selectors
|
||||
* disables style sharing, so we want to apply pseudo-class selectors to as few elements as
|
||||
* possible.
|
||||
*/
|
||||
a:link, area:link, link:link { color: #0000EE; }
|
||||
a:visited, area:visited, link:visited { color: #551A8B; }
|
||||
a:link, a:visited,
|
||||
area:link, area:visited,
|
||||
link:link, link:visited { text-decoration: underline; cursor: pointer; }
|
||||
a:link[rel~=help], a:visited[rel~=help],
|
||||
area:link[rel~=help], area:visited[rel~=help],
|
||||
link:link[rel~=help], link:visited[rel~=help] { cursor: help; }
|
||||
|
||||
/*
|
||||
* FIXME: use `outline: auto;`
|
||||
*/
|
||||
a:focus, area:focus,
|
||||
input:focus, textarea:focus, button:focus { outline: thin dotted; }
|
||||
|
||||
mark { background: yellow; color: black; }
|
||||
|
||||
abbr[title], acronym[title] { text-decoration: dotted underline; }
|
||||
ins, u { text-decoration: underline; }
|
||||
del, s, strike { text-decoration: line-through; }
|
||||
blink { text-decoration: blink; }
|
||||
|
||||
q::before { content: open-quote; }
|
||||
q::after { content: close-quote; }
|
||||
|
||||
/*br { display-outside: newline; } /* this also has bidi implications */
|
||||
br::before { content: "\A"; white-space: pre }
|
||||
|
||||
nobr { white-space: nowrap; }
|
||||
wbr { display-outside: break-opportunity; } /* this also has bidi implications */
|
||||
nobr wbr { white-space: normal; }
|
||||
|
||||
|
||||
[dir]:dir(ltr), bdi:dir(ltr), input[type=tel]:dir(ltr) { direction: ltr; }
|
||||
[dir]:dir(rtl), bdi:dir(rtl) { direction: rtl; }
|
||||
|
||||
address, blockquote, center, div, figure, figcaption, footer, form, header, hr,
|
||||
legend, listing, main, p, plaintext, pre, summary, xmp, article, aside, h1, h2,
|
||||
h3, h4, h5, h6, hgroup, nav, section, table, caption, colgroup, col, thead,
|
||||
tbody, tfoot, tr, td, th, dir, dd, dl, dt, menu, ol, ul, li, bdi, output,
|
||||
[dir=ltr i], [dir=rtl i], [dir=auto i] {
|
||||
unicode-bidi: isolate;
|
||||
}
|
||||
|
||||
bdo, bdo[dir] { unicode-bidi: isolate-override; }
|
||||
|
||||
textarea[dir=auto i], pre[dir=auto i] { unicode-bidi: plaintext; }
|
||||
|
||||
|
||||
article, aside, h1, h2, h3, h4, h5, h6, hgroup, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
h1 { margin-top: 0.67em; margin-bottom: 0.67em; font-size: 2.00em; font-weight: bold; }
|
||||
h2 { margin-top: 0.83em; margin-bottom: 0.83em; font-size: 1.50em; font-weight: bold; }
|
||||
h3 { margin-top: 1.00em; margin-bottom: 1.00em; font-size: 1.17em; font-weight: bold; }
|
||||
h4 { margin-top: 1.33em; margin-bottom: 1.33em; font-size: 1.00em; font-weight: bold; }
|
||||
h5 { margin-top: 1.67em; margin-bottom: 1.67em; font-size: 0.83em; font-weight: bold; }
|
||||
h6 { margin-top: 2.33em; margin-bottom: 2.33em; font-size: 0.67em; font-weight: bold; }
|
||||
|
||||
:matches(article, aside, nav, section) h1 { margin-top: 0.83em; margin-bottom: 0.83em; font-size: 1.50em; }
|
||||
:matches(article, aside, nav, section) :matches(article, aside, nav, section) h1 { margin-top: 1.00em; margin-bottom: 1.00em; font-size: 1.17em; }
|
||||
:matches(article, aside, nav, section) :matches(article, aside, nav, section) :matches(article, aside, nav, section) h1 { margin-top: 1.33em; margin-bottom: 1.33em; font-size: 1.00em; }
|
||||
:matches(article, aside, nav, section) :matches(article, aside, nav, section) :matches(article, aside, nav, section) :matches(article, aside, nav, section) h1 { margin-top: 1.67em; margin-bottom: 1.67em; font-size: 0.83em; }
|
||||
:matches(article, aside, nav, section) :matches(article, aside, nav, section) :matches(article, aside, nav, section) :matches(article, aside, nav, section) :matches(article, aside, nav, section) h1 { margin-top: 2.33em; margin-bottom: 2.33em; font-size: 0.67em; }
|
||||
|
||||
:matches(article, aside, nav, section) hgroup > h1 ~ h2 { margin-top: 1.00em; margin-bottom: 1.00em; font-size: 1.17em; }
|
||||
:matches(article, aside, nav, section) :matches(article, aside, nav, section) hgroup > h1 ~ h2 { margin-top: 1.33em; margin-bottom: 1.33em; font-size: 1.00em; }
|
||||
:matches(article, aside, nav, section) :matches(article, aside, nav, section) :matches(article, aside, nav, section) hgroup > h1 ~ h2 { margin-top: 1.67em; margin-bottom: 1.67em; font-size: 0.83em; }
|
||||
:matches(article, aside, nav, section) :matches(article, aside, nav, section) :matches(article, aside, nav, section) :matches(article, aside, nav, section) hgroup > h1 ~ h2 { margin-top: 2.33em; margin-bottom: 2.33em; font-size: 0.67em; }
|
||||
|
||||
:matches(article, aside, nav, section) hgroup > h1 ~ h3 { margin-top: 1.33em; margin-bottom: 1.33em; font-size: 1.00em; }
|
||||
:matches(article, aside, nav, section) :matches(article, aside, nav, section) hgroup > h1 ~ h3 { margin-top: 1.67em; margin-bottom: 1.67em; font-size: 0.83em; }
|
||||
:matches(article, aside, nav, section) :matches(article, aside, nav, section) :matches(article, aside, nav, section) hgroup > h1 ~ h3 { margin-top: 2.33em; margin-bottom: 2.33em; font-size: 0.67em; }
|
||||
|
||||
:matches(article, aside, nav, section) hgroup > h1 ~ h4 { margin-top: 1.67em; margin-bottom: 1.67em; font-size: 0.83em; }
|
||||
:matches(article, aside, nav, section) :matches(article, aside, nav, section) hgroup > h1 ~ h4 { margin-top: 2.33em; margin-bottom: 2.33em; font-size: 0.67em; }
|
||||
|
||||
:matches(article, aside, nav, section) hgroup > h1 ~ h5 { margin-top: 2.33em; margin-bottom: 2.33em; font-size: 0.67em; }
|
||||
|
||||
|
||||
dir, dd, dl, dt, menu, ol, ul { display: block; }
|
||||
li { display: list-item; }
|
||||
|
||||
dir, dl, menu, ol, ul { margin-top: 1em; margin-bottom: 1em; }
|
||||
|
||||
:matches(dir, dl, menu, ol, ul) :matches(dir, dl, menu, ol, ul) {
|
||||
margin-top: 0; margin-bottom: 0;
|
||||
}
|
||||
|
||||
dd { margin-left: 40px; } /* FIXME: use margin-inline-start when supported */
|
||||
dir, menu, ol, ul { padding-left: 40px; } /* FIXME: use padding-inline-start when supported */
|
||||
|
||||
ol { list-style-type: decimal; }
|
||||
|
||||
dir, menu, ul { list-style-type: disc; }
|
||||
|
||||
:matches(dir, menu, ol, ul) :matches(dir, menu, ul) {
|
||||
list-style-type: circle;
|
||||
}
|
||||
|
||||
:matches(dir, menu, ol, ul) :matches(dir, menu, ol, ul) :matches(dir, menu, ul) {
|
||||
list-style-type: square;
|
||||
}
|
||||
|
||||
|
||||
table { display: table; }
|
||||
caption { display: table-caption; }
|
||||
colgroup, colgroup[hidden] { display: table-column-group; }
|
||||
col, col[hidden] { display: table-column; }
|
||||
thead, thead[hidden] { display: table-header-group; }
|
||||
tbody, tbody[hidden] { display: table-row-group; }
|
||||
tfoot, tfoot[hidden] { display: table-footer-group; }
|
||||
tr, tr[hidden] { display: table-row; }
|
||||
td, th, td[hidden], th[hidden] { display: table-cell; }
|
||||
|
||||
colgroup[hidden], col[hidden], thead[hidden], tbody[hidden],
|
||||
tfoot[hidden], tr[hidden], td[hidden], th[hidden] {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
table {
|
||||
box-sizing: border-box;
|
||||
border-spacing: 2px;
|
||||
border-collapse: separate;
|
||||
text-indent: initial;
|
||||
}
|
||||
td, th { padding: 1px; }
|
||||
th { font-weight: bold; }
|
||||
|
||||
thead, tbody, tfoot, table > tr { vertical-align: middle; }
|
||||
tr, td, th { vertical-align: inherit; }
|
||||
|
||||
|
||||
table, td, th { border-color: gray; }
|
||||
thead, tbody, tfoot, tr { border-color: inherit; }
|
||||
table:matches(
|
||||
[rules=none i], [rules=groups i], [rules=rows i],
|
||||
[rules=cols i], [rules=all i],
|
||||
[frame=void i], [frame=above i], [frame=below i],
|
||||
[frame=hsides i], [frame=lhs i], [frame=rhs i],
|
||||
[frame=vsides i], [frame=box i], [frame=border i]
|
||||
),
|
||||
table:matches(
|
||||
[rules=none i], [rules=groups i], [rules=rows i],
|
||||
[rules=cols i], [rules=all i]
|
||||
) > tr > :matches(td, th),
|
||||
table:matches(
|
||||
[rules=none i], [rules=groups i], [rules=rows i],
|
||||
[rules=cols i], [rules=all i]
|
||||
) > :matches(thead, tbody, tfoot) > tr > :matches(td, th) {
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
|
||||
:matches(table, thead, tbody, tfoot, tr) > form {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
input, select, option, optgroup, button, textarea {
|
||||
text-indent: initial;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
textarea { white-space: pre-wrap; }
|
||||
|
||||
input[type="radio"], input[type="checkbox"], input[type="reset"], input[type="button"],
|
||||
input[type="submit"], select, button {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input, textarea, select, button { display: inline-block; }
|
||||
|
||||
hr { color: gray; border-style: inset; border-width: 1px; margin: 0.5em auto; }
|
||||
|
||||
|
||||
fieldset {
|
||||
display: block; /* https://www.w3.org/Bugs/Public/show_bug.cgi?id=27018 */
|
||||
margin-left: 2px; margin-right: 2px;
|
||||
border: groove 2px;
|
||||
border-color: ThreeDFace; /* FIXME: system color */
|
||||
padding: 0.35em 0.625em 0.75em;
|
||||
min-width: min-content;
|
||||
}
|
||||
|
||||
legend {
|
||||
padding-left: 2px; padding-right: 2px;
|
||||
}
|
||||
|
||||
iframe:not([seamless]) { border: 2px inset; }
|
||||
iframe[seamless] { display: block; }
|
||||
video { object-fit: contain; }
|
||||
|
||||
|
||||
textarea { white-space: pre-wrap; }
|
||||
|
||||
*|*:not(:root):fullscreen {
|
||||
position:fixed !important;
|
||||
top:0 !important; right:0 !important; bottom:0 !important; left:0 !important;
|
||||
margin:0 !important;
|
||||
box-sizing:border-box !important;
|
||||
min-width:0 !important;
|
||||
max-width:none !important;
|
||||
min-height:0 !important;
|
||||
max-height:none !important;
|
||||
width:100% !important;
|
||||
height:100% !important;
|
||||
transform:none !important;
|
||||
|
||||
/* intentionally not !important */
|
||||
object-fit:contain;
|
||||
|
||||
/* The internal-only -servo-top-layer property is used
|
||||
to implement https://fullscreen.spec.whatwg.org/#top-layer */
|
||||
-servo-top-layer: top;
|
||||
}
|
||||
|
||||
iframe:fullscreen {
|
||||
border:none !important;
|
||||
padding:0 !important;
|
||||
}
|
||||
13
rust-toolchain.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[toolchain]
|
||||
channel = "1.74"
|
||||
components = [
|
||||
# For support/crown
|
||||
"llvm-tools",
|
||||
# For support/crown
|
||||
"rustc-dev",
|
||||
# For docs building
|
||||
"rust-docs",
|
||||
# For formatting
|
||||
"rustfmt",
|
||||
]
|
||||
profile = "minimal"
|
||||
15
rustfmt.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
max_width = 100
|
||||
hard_tabs = false
|
||||
tab_spaces = 2
|
||||
newline_style = "Unix"
|
||||
use_small_heuristics = "Default"
|
||||
reorder_imports = true
|
||||
reorder_modules = true
|
||||
remove_nested_parens = true
|
||||
edition = "2018"
|
||||
merge_derives = true
|
||||
use_try_shorthand = false
|
||||
use_field_init_shorthand = false
|
||||
force_explicit_abi = true
|
||||
imports_granularity = "Crate"
|
||||
#license_template_path = ".license_template"
|
||||
61
src/error.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
/// Convenient type alias of Result type for wry.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Errors returned by wry.
|
||||
#[non_exhaustive]
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[cfg(gtk)]
|
||||
#[error(transparent)]
|
||||
GlibError(#[from] gtk::glib::Error),
|
||||
#[cfg(gtk)]
|
||||
#[error(transparent)]
|
||||
GlibBoolError(#[from] gtk::glib::BoolError),
|
||||
#[cfg(gtk)]
|
||||
#[error("Fail to fetch security manager")]
|
||||
MissingManager,
|
||||
#[cfg(gtk)]
|
||||
#[error("Couldn't find X11 Display")]
|
||||
X11DisplayNotFound,
|
||||
#[cfg(gtk)]
|
||||
#[error(transparent)]
|
||||
XlibError(#[from] x11_dl::error::OpenError),
|
||||
#[error("Failed to initialize the script")]
|
||||
InitScriptError,
|
||||
#[error("Bad RPC request: {0} ((1))")]
|
||||
RpcScriptError(String, String),
|
||||
#[error(transparent)]
|
||||
NulError(#[from] std::ffi::NulError),
|
||||
#[error(transparent)]
|
||||
ReceiverError(#[from] std::sync::mpsc::RecvError),
|
||||
#[error(transparent)]
|
||||
SenderError(#[from] std::sync::mpsc::SendError<String>),
|
||||
#[error("Failed to send the message")]
|
||||
MessageSender,
|
||||
#[error(transparent)]
|
||||
Json(#[from] serde_json::Error),
|
||||
#[error(transparent)]
|
||||
UrlError(#[from] url::ParseError),
|
||||
#[error("IO error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
#[cfg(target_os = "windows")]
|
||||
#[error("WebView2 error: {0}")]
|
||||
WebView2Error(webview2_com::Error),
|
||||
#[error("Duplicate custom protocol registered: {0}")]
|
||||
DuplicateCustomProtocol(String),
|
||||
#[error(transparent)]
|
||||
HttpError(#[from] http::Error),
|
||||
#[error("Infallible error, something went really wrong: {0}")]
|
||||
Infallible(#[from] std::convert::Infallible),
|
||||
#[cfg(target_os = "android")]
|
||||
#[error(transparent)]
|
||||
JniError(#[from] jni::errors::Error),
|
||||
#[error("Failed to create proxy endpoint")]
|
||||
ProxyEndpointCreationFailed,
|
||||
#[error(transparent)]
|
||||
WindowHandleError(#[from] raw_window_handle::HandleError),
|
||||
#[error("the window handle kind is not supported")]
|
||||
UnsupportedWindowHandle,
|
||||
#[error(transparent)]
|
||||
Utf8Error(#[from] std::str::Utf8Error),
|
||||
}
|
||||
1573
src/lib.rs
Normal file
76
src/main.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2020-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use winit::{
|
||||
dpi::PhysicalSize,
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
use wry::{WebViewBuilder, WebViewBuilderExtServo, WebViewExtServo};
|
||||
|
||||
/* window decoration */
|
||||
#[cfg(target_os = "macos")]
|
||||
use cocoa::appkit::{NSView, NSWindow};
|
||||
#[cfg(target_os = "macos")]
|
||||
use cocoa::appkit::{NSWindowStyleMask, NSWindowTitleVisibility};
|
||||
#[cfg(target_os = "macos")]
|
||||
use objc::{msg_send, runtime::Object, sel, sel_impl};
|
||||
#[cfg(target_os = "macos")]
|
||||
use raw_window_handle::{AppKitWindowHandle, HasRawWindowHandle, RawWindowHandle};
|
||||
#[cfg(target_os = "macos")]
|
||||
use winit::dpi::LogicalPosition;
|
||||
#[cfg(target_os = "macos")]
|
||||
use winit::platform::macos::WindowBuilderExtMacOS;
|
||||
|
||||
fn main() -> wry::Result<()> {
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
let window = WindowBuilder::new()
|
||||
.with_inner_size(PhysicalSize::new(1000, 500))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
unsafe {
|
||||
let rwh = window.raw_window_handle();
|
||||
if let RawWindowHandle::AppKit(AppKitWindowHandle { ns_window, .. }) = rwh {
|
||||
decorate_window(ns_window as *mut Object, LogicalPosition::new(8.0, 40.0));
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut builder = WebViewBuilder::new_servo(window, event_loop.create_proxy());
|
||||
let mut webview = builder.build()?;
|
||||
|
||||
event_loop
|
||||
.run(move |event, evl| {
|
||||
if !evl.exiting() && webview.servo().is_shutdown() {
|
||||
if let Some(servo) = webview.servo().servo_client().take() {
|
||||
servo.deinit();
|
||||
}
|
||||
evl.exit();
|
||||
} else {
|
||||
webview.servo().set_control_flow(&event, evl);
|
||||
webview.servo().handle_winit_event(event);
|
||||
webview.servo().handle_servo_messages();
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub unsafe fn decorate_window(window: *mut Object, position: LogicalPosition<f64>) {
|
||||
NSWindow::setTitlebarAppearsTransparent_(window, true);
|
||||
NSWindow::setTitleVisibility_(window, NSWindowTitleVisibility::NSWindowTitleHidden);
|
||||
NSWindow::setStyleMask_(
|
||||
window,
|
||||
NSWindowStyleMask::NSTitledWindowMask
|
||||
| NSWindowStyleMask::NSFullSizeContentViewWindowMask
|
||||
| NSWindowStyleMask::NSClosableWindowMask
|
||||
| NSWindowStyleMask::NSResizableWindowMask
|
||||
| NSWindowStyleMask::NSMiniaturizableWindowMask,
|
||||
);
|
||||
}
|
||||
15
src/proxy.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProxyEndpoint {
|
||||
/// Proxy server host (e.g. 192.168.0.100, localhost, example.com, etc.)
|
||||
pub host: String,
|
||||
/// Proxy server port (e.g. 1080, 3128, etc.)
|
||||
pub port: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ProxyConfig {
|
||||
/// Connect to proxy server via HTTP CONNECT
|
||||
Http(ProxyEndpoint),
|
||||
/// Connect to proxy server via SOCKSv5
|
||||
Socks5(ProxyEndpoint),
|
||||
}
|
||||
310
src/servo/embedder.rs
Normal file
@@ -0,0 +1,310 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use servo::{
|
||||
compositing::{
|
||||
windowing::{EmbedderEvent, EmbedderMethods, MouseWindowEvent},
|
||||
CompositeTarget,
|
||||
},
|
||||
embedder_traits::{Cursor, EmbedderMsg, EventLoopWaker},
|
||||
euclid::{Point2D, Size2D},
|
||||
script_traits::{TouchEventType, WheelDelta, WheelMode},
|
||||
servo_url::ServoUrl,
|
||||
webrender_api::{
|
||||
units::{DeviceIntPoint, DevicePoint, LayoutVector2D},
|
||||
ScrollLocation,
|
||||
},
|
||||
BrowserId, Servo,
|
||||
};
|
||||
use winit::{
|
||||
dpi::PhysicalPosition,
|
||||
event::{ElementState, Event, TouchPhase, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoopProxy, EventLoopWindowTarget},
|
||||
window::CursorIcon,
|
||||
};
|
||||
|
||||
use super::window::WebView;
|
||||
|
||||
/// The Servo embedder to communicate with servo instance.
|
||||
pub struct Embedder {
|
||||
servo: Option<Servo<WebView>>,
|
||||
// TODO TopLevelBrowsingContextId
|
||||
browser_id: Option<BrowserId>,
|
||||
webview: Rc<WebView>,
|
||||
events: Vec<EmbedderEvent>,
|
||||
mouse_position: PhysicalPosition<f64>,
|
||||
is_shutdown: bool,
|
||||
}
|
||||
|
||||
impl Embedder {
|
||||
pub fn new(webview: WebView, callback: EmbedderWaker) -> Self {
|
||||
let webview = Rc::new(webview);
|
||||
let mut init_servo = Servo::new(
|
||||
Box::new(callback),
|
||||
webview.clone(),
|
||||
Some(String::from(
|
||||
"Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0",
|
||||
)),
|
||||
CompositeTarget::Window,
|
||||
);
|
||||
|
||||
let demo_path = std::env::current_dir().unwrap().join("demo.html");
|
||||
let url = ServoUrl::from_file_path(demo_path.to_str().unwrap()).unwrap();
|
||||
init_servo
|
||||
.servo
|
||||
.handle_events(vec![EmbedderEvent::NewBrowser(url, init_servo.browser_id)]);
|
||||
init_servo.servo.setup_logging();
|
||||
Embedder {
|
||||
servo: Some(init_servo.servo),
|
||||
webview,
|
||||
events: vec![],
|
||||
mouse_position: PhysicalPosition::default(),
|
||||
is_shutdown: false,
|
||||
browser_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_control_flow(&self, event: &Event<()>, evl: &EventLoopWindowTarget<()>) {
|
||||
let control_flow = if !self.webview.is_animating() || *event == Event::Suspended {
|
||||
ControlFlow::Wait
|
||||
} else {
|
||||
ControlFlow::Poll
|
||||
};
|
||||
evl.set_control_flow(control_flow);
|
||||
log::trace!("Servo embedder sets control flow to: {control_flow:?}");
|
||||
}
|
||||
|
||||
pub fn handle_winit_event(&mut self, event: Event<()>) {
|
||||
log::trace!("Servo embedder is creating ebedder event from: {event:?}");
|
||||
match event {
|
||||
Event::Suspended => {}
|
||||
Event::Resumed | Event::UserEvent(()) => {
|
||||
self.events.push(EmbedderEvent::Idle);
|
||||
}
|
||||
Event::WindowEvent {
|
||||
window_id: _,
|
||||
event,
|
||||
} => match event {
|
||||
WindowEvent::RedrawRequested => {
|
||||
let Some(servo) = self.servo.as_mut() else {
|
||||
return;
|
||||
};
|
||||
|
||||
servo.recomposite();
|
||||
servo.present();
|
||||
self.events.push(EmbedderEvent::Idle);
|
||||
}
|
||||
WindowEvent::Resized(size) => {
|
||||
let size = Size2D::new(size.width, size.height);
|
||||
let _ = self.webview.resize(size.to_i32());
|
||||
self.events.push(EmbedderEvent::Resize);
|
||||
}
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
let event: DevicePoint = DevicePoint::new(position.x as f32, position.y as f32);
|
||||
self.mouse_position = position;
|
||||
self
|
||||
.events
|
||||
.push(EmbedderEvent::MouseWindowMoveEventClass(event));
|
||||
}
|
||||
WindowEvent::MouseInput { state, button, .. } => {
|
||||
let button: servo::script_traits::MouseButton = match button {
|
||||
winit::event::MouseButton::Left => servo::script_traits::MouseButton::Left,
|
||||
winit::event::MouseButton::Right => servo::script_traits::MouseButton::Right,
|
||||
winit::event::MouseButton::Middle => servo::script_traits::MouseButton::Middle,
|
||||
_ => {
|
||||
log::warn!("Servo embedder hasn't supported this mouse button yet: {button:?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let position = Point2D::new(self.mouse_position.x as f32, self.mouse_position.y as f32);
|
||||
|
||||
let event: MouseWindowEvent = match state {
|
||||
ElementState::Pressed => MouseWindowEvent::MouseDown(button, position),
|
||||
ElementState::Released => MouseWindowEvent::MouseUp(button, position),
|
||||
};
|
||||
self
|
||||
.events
|
||||
.push(EmbedderEvent::MouseWindowEventClass(event));
|
||||
|
||||
// winit didn't send click event, so we send it after mouse up
|
||||
if state == ElementState::Released {
|
||||
let event: MouseWindowEvent = MouseWindowEvent::Click(button, position);
|
||||
self
|
||||
.events
|
||||
.push(EmbedderEvent::MouseWindowEventClass(event));
|
||||
}
|
||||
}
|
||||
WindowEvent::TouchpadMagnify { delta, .. } => {
|
||||
self.events.push(EmbedderEvent::Zoom(1.0 + delta as f32));
|
||||
}
|
||||
WindowEvent::MouseWheel { delta, phase, .. } => {
|
||||
// FIXME: Pixels per line, should be configurable (from browser setting?) and vary by zoom level.
|
||||
const LINE_HEIGHT: f32 = 38.0;
|
||||
|
||||
let (mut x, mut y, mode) = match delta {
|
||||
winit::event::MouseScrollDelta::LineDelta(x, y) => {
|
||||
(x as f64, (y * LINE_HEIGHT) as f64, WheelMode::DeltaLine)
|
||||
}
|
||||
winit::event::MouseScrollDelta::PixelDelta(position) => {
|
||||
let position = position.to_logical::<f64>(self.webview.window.scale_factor());
|
||||
(position.x, position.y, WheelMode::DeltaPixel)
|
||||
}
|
||||
};
|
||||
|
||||
// Wheel Event
|
||||
self.events.push(EmbedderEvent::Wheel(
|
||||
WheelDelta { x, y, z: 0.0, mode },
|
||||
DevicePoint::new(self.mouse_position.x as f32, self.mouse_position.y as f32),
|
||||
));
|
||||
|
||||
// Scroll Event
|
||||
// Do one axis at a time.
|
||||
if y.abs() >= x.abs() {
|
||||
x = 0.0;
|
||||
} else {
|
||||
y = 0.0;
|
||||
}
|
||||
|
||||
let phase: TouchEventType = match phase {
|
||||
TouchPhase::Started => TouchEventType::Down,
|
||||
TouchPhase::Moved => TouchEventType::Move,
|
||||
TouchPhase::Ended => TouchEventType::Up,
|
||||
TouchPhase::Cancelled => TouchEventType::Cancel,
|
||||
};
|
||||
|
||||
self.events.push(EmbedderEvent::Scroll(
|
||||
ScrollLocation::Delta(LayoutVector2D::new(x as f32, y as f32)),
|
||||
DeviceIntPoint::new(self.mouse_position.x as i32, self.mouse_position.y as i32),
|
||||
phase,
|
||||
));
|
||||
}
|
||||
WindowEvent::CloseRequested => {
|
||||
self.events.push(EmbedderEvent::Quit);
|
||||
}
|
||||
e => log::warn!("Servo embedder hasn't supported this window event yet: {e:?}"),
|
||||
},
|
||||
e => log::warn!("Servo embedder hasn't supported this event yet: {e:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_servo_messages(&mut self) {
|
||||
let Some(servo) = self.servo.as_mut() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut need_present = false;
|
||||
|
||||
servo.get_events().into_iter().for_each(|(w, m)| {
|
||||
log::trace!("Servo embedder is handling servo message: {m:?} with browser id: {w:?}");
|
||||
match m {
|
||||
EmbedderMsg::BrowserCreated(w) => {
|
||||
if self.browser_id.is_none() {
|
||||
self.browser_id = Some(w);
|
||||
}
|
||||
self.events.push(EmbedderEvent::SelectBrowser(w));
|
||||
}
|
||||
EmbedderMsg::ReadyToPresent => {
|
||||
need_present = true;
|
||||
}
|
||||
EmbedderMsg::SetCursor(cursor) => {
|
||||
let winit_cursor = match cursor {
|
||||
Cursor::Default => CursorIcon::Default,
|
||||
Cursor::Pointer => CursorIcon::Pointer,
|
||||
Cursor::ContextMenu => CursorIcon::ContextMenu,
|
||||
Cursor::Help => CursorIcon::Help,
|
||||
Cursor::Progress => CursorIcon::Progress,
|
||||
Cursor::Wait => CursorIcon::Wait,
|
||||
Cursor::Cell => CursorIcon::Cell,
|
||||
Cursor::Crosshair => CursorIcon::Crosshair,
|
||||
Cursor::Text => CursorIcon::Text,
|
||||
Cursor::VerticalText => CursorIcon::VerticalText,
|
||||
Cursor::Alias => CursorIcon::Alias,
|
||||
Cursor::Copy => CursorIcon::Copy,
|
||||
Cursor::Move => CursorIcon::Move,
|
||||
Cursor::NoDrop => CursorIcon::NoDrop,
|
||||
Cursor::NotAllowed => CursorIcon::NotAllowed,
|
||||
Cursor::Grab => CursorIcon::Grab,
|
||||
Cursor::Grabbing => CursorIcon::Grabbing,
|
||||
Cursor::EResize => CursorIcon::EResize,
|
||||
Cursor::NResize => CursorIcon::NResize,
|
||||
Cursor::NeResize => CursorIcon::NeResize,
|
||||
Cursor::NwResize => CursorIcon::NwResize,
|
||||
Cursor::SResize => CursorIcon::SResize,
|
||||
Cursor::SeResize => CursorIcon::SeResize,
|
||||
Cursor::SwResize => CursorIcon::SwResize,
|
||||
Cursor::WResize => CursorIcon::WResize,
|
||||
Cursor::EwResize => CursorIcon::EwResize,
|
||||
Cursor::NsResize => CursorIcon::NsResize,
|
||||
Cursor::NeswResize => CursorIcon::NeswResize,
|
||||
Cursor::NwseResize => CursorIcon::NwseResize,
|
||||
Cursor::ColResize => CursorIcon::ColResize,
|
||||
Cursor::RowResize => CursorIcon::RowResize,
|
||||
Cursor::AllScroll => CursorIcon::AllScroll,
|
||||
Cursor::ZoomIn => CursorIcon::ZoomIn,
|
||||
Cursor::ZoomOut => CursorIcon::ZoomOut,
|
||||
_ => CursorIcon::Default,
|
||||
};
|
||||
self.webview.window.set_cursor_icon(winit_cursor);
|
||||
}
|
||||
EmbedderMsg::AllowNavigationRequest(pipeline_id, _url) => {
|
||||
if w.is_some() {
|
||||
self
|
||||
.events
|
||||
.push(EmbedderEvent::AllowNavigationResponse(pipeline_id, true));
|
||||
}
|
||||
}
|
||||
EmbedderMsg::CloseBrowser => {
|
||||
self.events.push(EmbedderEvent::Quit);
|
||||
}
|
||||
EmbedderMsg::Shutdown => {
|
||||
self.is_shutdown = true;
|
||||
}
|
||||
e => {
|
||||
log::warn!("Servo embedder hasn't supported handling this message yet: {e:?}")
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
log::trace!(
|
||||
"Servo embedder is handling embedder events: {:?}",
|
||||
self.events
|
||||
);
|
||||
if servo.handle_events(self.events.drain(..)) {
|
||||
servo.repaint_synchronously();
|
||||
servo.present();
|
||||
} else if need_present {
|
||||
self.webview.request_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_shutdown(&self) -> bool {
|
||||
self.is_shutdown
|
||||
}
|
||||
|
||||
pub fn servo_client(&mut self) -> &mut Option<Servo<WebView>> {
|
||||
&mut self.servo
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EmbedderWaker(pub EventLoopProxy<()>);
|
||||
|
||||
impl EmbedderMethods for EmbedderWaker {
|
||||
fn create_event_loop_waker(&mut self) -> Box<dyn EventLoopWaker> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopWaker for EmbedderWaker {
|
||||
fn clone_box(&self) -> Box<dyn EventLoopWaker> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn wake(&self) {
|
||||
if let Err(e) = self.0.send_event(()) {
|
||||
log::error!(
|
||||
"Servo waker failed to send wake up event to servo embedder: {}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
133
src/servo/mod.rs
Normal file
@@ -0,0 +1,133 @@
|
||||
use raw_window_handle::HasRawWindowHandle;
|
||||
use url::Url;
|
||||
use winit::{event_loop::EventLoopProxy, window::Window};
|
||||
|
||||
use crate::{Rect, Result, WebContext, WebViewAttributes, WebViewBuilder, RGBA};
|
||||
|
||||
use self::{
|
||||
embedder::{Embedder, EmbedderWaker},
|
||||
window::WebView,
|
||||
};
|
||||
|
||||
mod embedder;
|
||||
mod prefs;
|
||||
mod resources;
|
||||
mod window;
|
||||
|
||||
pub(crate) struct InnerWebView {
|
||||
embedder: Embedder,
|
||||
}
|
||||
|
||||
impl InnerWebView {
|
||||
pub fn new_servo(
|
||||
window: Window,
|
||||
proxy: EventLoopProxy<()>,
|
||||
attributes: WebViewAttributes,
|
||||
pl_attrs: super::PlatformSpecificWebViewAttributes,
|
||||
web_context: Option<&mut WebContext>,
|
||||
) -> Result<Self> {
|
||||
resources::init(web_context);
|
||||
prefs::init();
|
||||
|
||||
let webview = WebView::new(window);
|
||||
let callback = EmbedderWaker(proxy);
|
||||
let embedder = Embedder::new(webview, callback);
|
||||
|
||||
Ok(Self { embedder })
|
||||
}
|
||||
|
||||
pub fn new<W: HasRawWindowHandle>(
|
||||
_window: &W,
|
||||
_attributes: WebViewAttributes,
|
||||
_pl_attrs: super::PlatformSpecificWebViewAttributes,
|
||||
_web_context: Option<&mut WebContext>,
|
||||
) -> Result<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn new_as_child<W: HasRawWindowHandle>(
|
||||
_parent: &W,
|
||||
_attributes: WebViewAttributes,
|
||||
_pl_attrs: super::PlatformSpecificWebViewAttributes,
|
||||
_web_context: Option<&mut WebContext>,
|
||||
) -> Result<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn print(&self) {}
|
||||
|
||||
pub fn url(&self) -> Url {
|
||||
Url::parse("").unwrap()
|
||||
}
|
||||
|
||||
pub fn eval(
|
||||
&self,
|
||||
js: &str,
|
||||
callback: Option<impl FnOnce(String) + Send + 'static>,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(debug_assertions, feature = "devtools"))]
|
||||
pub fn open_devtools(&self) {}
|
||||
|
||||
#[cfg(any(debug_assertions, feature = "devtools"))]
|
||||
pub fn close_devtools(&self) {}
|
||||
|
||||
#[cfg(any(debug_assertions, feature = "devtools"))]
|
||||
pub fn is_devtools_open(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub fn zoom(&self, scale_factor: f64) {}
|
||||
|
||||
pub fn set_background_color(&self, background_color: RGBA) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_url(&self, url: &str) {}
|
||||
|
||||
pub fn load_url_with_headers(&self, url: &str, headers: http::HeaderMap) {}
|
||||
|
||||
pub fn clear_all_browsing_data(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_bounds(&self, bounds: Rect) {}
|
||||
|
||||
pub fn set_visible(&self, visible: bool) {}
|
||||
|
||||
pub fn focus(&self) {}
|
||||
}
|
||||
|
||||
pub fn platform_webview_version() -> Result<String> {
|
||||
Ok(String::from(""))
|
||||
}
|
||||
|
||||
pub trait WebViewBuilderExtServo {
|
||||
fn new_servo(window: Window, proxy: EventLoopProxy<()>) -> Self;
|
||||
}
|
||||
|
||||
impl WebViewBuilderExtServo for WebViewBuilder<'_> {
|
||||
fn new_servo(window: Window, proxy: EventLoopProxy<()>) -> Self {
|
||||
Self {
|
||||
attrs: WebViewAttributes::default(),
|
||||
window: None,
|
||||
as_child: false,
|
||||
#[allow(clippy::default_constructed_unit_structs)]
|
||||
platform_specific: super::PlatformSpecificWebViewAttributes::default(),
|
||||
web_context: None,
|
||||
winit: Some((window, proxy)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WebViewExtServo {
|
||||
fn servo(&mut self) -> &mut Embedder; // TODO expose method instead.
|
||||
}
|
||||
|
||||
impl WebViewExtServo for super::WebView {
|
||||
fn servo(&mut self) -> &mut Embedder {
|
||||
&mut self.webview.embedder
|
||||
}
|
||||
}
|
||||
32
src/servo/prefs.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use std::{collections::HashMap, env, fs::File, io::Read};
|
||||
|
||||
use getopts::Options;
|
||||
use servo::config::{basedir, opts, prefs};
|
||||
|
||||
pub fn init() {
|
||||
// Reads opts first and then prefs.
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let opts = Options::new();
|
||||
// FIXME: Most results are not handled. Better wait for user feedback to handle each config.
|
||||
let _ = opts::from_cmdline_args(opts, &args);
|
||||
|
||||
let user_prefs_path = opts::get()
|
||||
.config_dir
|
||||
.clone()
|
||||
.or_else(basedir::default_config_dir)
|
||||
.map(|path| path.join("prefs.json"))
|
||||
.filter(|path| path.exists());
|
||||
|
||||
let userprefs = if let Some(path) = user_prefs_path {
|
||||
let mut file = File::open(path).expect("Error opening user prefs");
|
||||
let mut txt = String::new();
|
||||
file
|
||||
.read_to_string(&mut txt)
|
||||
.expect("Can't read user prefs file");
|
||||
prefs::read_prefs_map(&txt).expect("Can't parse user prefs file")
|
||||
} else {
|
||||
HashMap::new()
|
||||
};
|
||||
|
||||
prefs::add_user_prefs(userprefs);
|
||||
}
|
||||
46
src/servo/resources.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use std::{env, fs, path::PathBuf};
|
||||
|
||||
use servo::embedder_traits::resources::{self, Resource, ResourceReaderMethods};
|
||||
|
||||
use crate::WebContext;
|
||||
|
||||
struct ResourceReader(PathBuf);
|
||||
|
||||
pub fn init(context: Option<&mut WebContext>) {
|
||||
match context {
|
||||
Some(c) if c.data_directory().is_some() => resources::set(Box::new(ResourceReader(
|
||||
c.data_directory().unwrap().to_path_buf(),
|
||||
))),
|
||||
_ => resources::set(Box::new(ResourceReader(resources_dir_path()))),
|
||||
}
|
||||
}
|
||||
|
||||
impl ResourceReaderMethods for ResourceReader {
|
||||
fn read(&self, res: Resource) -> Vec<u8> {
|
||||
let mut path = self.0.clone();
|
||||
path.push(res.filename());
|
||||
fs::read(path).expect("Can't read file")
|
||||
}
|
||||
|
||||
fn sandbox_access_files(&self) -> Vec<PathBuf> {
|
||||
vec![self.0.clone()]
|
||||
}
|
||||
|
||||
fn sandbox_access_files_dirs(&self) -> Vec<PathBuf> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
fn resources_dir_path() -> PathBuf {
|
||||
// Try ./resources relative to the directory containing the
|
||||
// canonicalised executable path, then each of its ancestors.
|
||||
let mut path = env::current_exe().unwrap().canonicalize().unwrap();
|
||||
while path.pop() {
|
||||
path.push("resources");
|
||||
if path.is_dir() {
|
||||
return path;
|
||||
}
|
||||
path.pop();
|
||||
}
|
||||
panic!("Can not find the resources directory. Please specify it in WebContext instead.");
|
||||
}
|
||||
167
src/servo/window.rs
Normal file
@@ -0,0 +1,167 @@
|
||||
use std::cell::Cell;
|
||||
|
||||
use raw_window_handle::HasRawWindowHandle;
|
||||
use servo::{
|
||||
compositing::windowing::{AnimationState, EmbedderCoordinates, WindowMethods},
|
||||
config::pref,
|
||||
euclid::{Point2D, Scale, Size2D, UnknownUnit},
|
||||
webrender_api::units::DeviceIntRect,
|
||||
webrender_surfman::WebrenderSurfman,
|
||||
};
|
||||
use surfman::{Connection, GLApi, GLVersion, SurfaceType};
|
||||
// FIXME servo should re-export this.
|
||||
use servo_media::player::context::{GlApi, GlContext, NativeDisplay};
|
||||
use winit::window::Window;
|
||||
|
||||
/// This is the type for servo embedder. Not for public usage.
|
||||
pub struct WebView {
|
||||
pub webrender_surfman: WebrenderSurfman,
|
||||
animation_state: Cell<AnimationState>,
|
||||
pub window: Window,
|
||||
}
|
||||
|
||||
impl WebView {
|
||||
pub fn new(window: Window) -> Self {
|
||||
let connection = Connection::new().expect("Failed to create surfman connection");
|
||||
let adapter = connection
|
||||
.create_adapter()
|
||||
.expect("Failed to create surfman adapter");
|
||||
let native_widget = connection
|
||||
.create_native_widget_from_rwh(window.raw_window_handle())
|
||||
.expect("Failed to create surfman native widget");
|
||||
let surface_type = SurfaceType::Widget { native_widget };
|
||||
let webrender_surfman = WebrenderSurfman::create(&connection, &adapter, surface_type)
|
||||
.expect("Failed to create webrender surfman");
|
||||
log::trace!("Created webrender surfman for window {:?}", window);
|
||||
|
||||
Self {
|
||||
webrender_surfman,
|
||||
animation_state: Cell::new(AnimationState::Idle),
|
||||
window,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_animating(&self) -> bool {
|
||||
self.animation_state.get() == AnimationState::Animating
|
||||
}
|
||||
|
||||
pub fn resize(&self, size: Size2D<i32, UnknownUnit>) {
|
||||
let _ = self.webrender_surfman.resize(size);
|
||||
}
|
||||
|
||||
pub fn request_redraw(&self) {
|
||||
self.window.request_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for WebView {}
|
||||
unsafe impl Sync for WebView {}
|
||||
|
||||
impl WindowMethods for WebView {
|
||||
fn get_coordinates(&self) -> EmbedderCoordinates {
|
||||
let size = self.window.inner_size();
|
||||
let pos = Point2D::new(0, 0);
|
||||
let viewport = Size2D::new(size.width as i32, size.height as i32);
|
||||
|
||||
let size = self.window.current_monitor().unwrap().size();
|
||||
let screen = Size2D::new(size.width as i32, size.height as i32);
|
||||
EmbedderCoordinates {
|
||||
hidpi_factor: Scale::new(self.window.scale_factor() as f32),
|
||||
screen,
|
||||
screen_avail: screen,
|
||||
window: (viewport, pos),
|
||||
framebuffer: viewport,
|
||||
viewport: DeviceIntRect::new(pos, viewport),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_animation_state(&self, state: AnimationState) {
|
||||
self.animation_state.set(state);
|
||||
}
|
||||
|
||||
fn get_gl_context(&self) -> GlContext {
|
||||
if !pref!(media.glvideo.enabled) {
|
||||
return GlContext::Unknown;
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
let native_context = self.webrender_surfman.native_context();
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
return GlContext::Egl(native_context.egl_context as usize);
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
return {
|
||||
use surfman::platform::generic::multi::context::NativeContext;
|
||||
match native_context {
|
||||
NativeContext::Default(NativeContext::Default(native_context)) => {
|
||||
GlContext::Egl(native_context.egl_context as usize)
|
||||
}
|
||||
NativeContext::Default(NativeContext::Alternate(native_context)) => {
|
||||
GlContext::Egl(native_context.egl_context as usize)
|
||||
}
|
||||
NativeContext::Alternate(_) => unimplemented!(),
|
||||
}
|
||||
};
|
||||
|
||||
// @TODO(victor): https://github.com/servo/media/pull/315
|
||||
#[cfg(target_os = "macos")]
|
||||
#[allow(unreachable_code)]
|
||||
return unimplemented!();
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
|
||||
return unimplemented!();
|
||||
}
|
||||
|
||||
fn get_native_display(&self) -> NativeDisplay {
|
||||
if !pref!(media.glvideo.enabled) {
|
||||
return NativeDisplay::Unknown;
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
let native_connection = self.webrender_surfman.connection().native_connection();
|
||||
#[allow(unused_variables)]
|
||||
let native_device = self.webrender_surfman.native_device();
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
return NativeDisplay::Egl(native_device.egl_display as usize);
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
return {
|
||||
use surfman::platform::generic::multi::connection::NativeConnection;
|
||||
match native_connection {
|
||||
NativeConnection::Default(NativeConnection::Default(conn)) => {
|
||||
NativeDisplay::Egl(conn.0 as usize)
|
||||
}
|
||||
NativeConnection::Default(NativeConnection::Alternate(conn)) => {
|
||||
NativeDisplay::X11(conn.x11_display as usize)
|
||||
}
|
||||
NativeConnection::Alternate(_) => unimplemented!(),
|
||||
}
|
||||
};
|
||||
|
||||
// @TODO(victor): https://github.com/servo/media/pull/315
|
||||
#[cfg(target_os = "macos")]
|
||||
#[allow(unreachable_code)]
|
||||
return unimplemented!();
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
|
||||
return unimplemented!();
|
||||
}
|
||||
|
||||
fn get_gl_api(&self) -> GlApi {
|
||||
let api = self.webrender_surfman.connection().gl_api();
|
||||
let attributes = self.webrender_surfman.context_attributes();
|
||||
let GLVersion { major, minor } = attributes.version;
|
||||
match api {
|
||||
GLApi::GL if major >= 3 && minor >= 2 => GlApi::OpenGL3,
|
||||
GLApi::GL => GlApi::OpenGL,
|
||||
GLApi::GLES if major > 1 => GlApi::Gles2,
|
||||
GLApi::GLES => GlApi::Gles1,
|
||||
}
|
||||
}
|
||||
|
||||
fn webrender_surfman(&self) -> WebrenderSurfman {
|
||||
self.webrender_surfman.clone()
|
||||
}
|
||||
}
|
||||
93
src/web_context.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright 2020-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#[cfg(gtk)]
|
||||
use crate::webkitgtk::WebContextImpl;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// A context that is shared between multiple [`WebView`]s.
|
||||
///
|
||||
/// A browser would have a context for all the normal tabs and a different context for all the
|
||||
/// private/incognito tabs.
|
||||
///
|
||||
/// # Warning
|
||||
/// If [`WebView`] is created by a WebContext. Dropping `WebContext` will cause [`WebView`] lose
|
||||
/// some actions like custom protocol on Mac. Please keep both instances when you still wish to
|
||||
/// interact with them.
|
||||
///
|
||||
/// [`WebView`]: crate::WebView
|
||||
#[derive(Debug)]
|
||||
pub struct WebContext {
|
||||
data: WebContextData,
|
||||
#[allow(dead_code)] // It's not needed on Windows and macOS.
|
||||
pub(crate) os: WebContextImpl,
|
||||
}
|
||||
|
||||
impl WebContext {
|
||||
/// Create a new [`WebContext`].
|
||||
///
|
||||
/// `data_directory`:
|
||||
/// * Whether the WebView window should have a custom user data path. This is useful in Windows
|
||||
/// when a bundled application can't have the webview data inside `Program Files`.
|
||||
pub fn new(data_directory: Option<PathBuf>) -> Self {
|
||||
let data = WebContextData { data_directory };
|
||||
let os = WebContextImpl::new(&data);
|
||||
Self { data, os }
|
||||
}
|
||||
|
||||
#[cfg(gtk)]
|
||||
pub(crate) fn new_ephemeral() -> Self {
|
||||
let data = WebContextData::default();
|
||||
let os = WebContextImpl::new_ephemeral();
|
||||
Self { data, os }
|
||||
}
|
||||
|
||||
/// A reference to the data directory the context was created with.
|
||||
pub fn data_directory(&self) -> Option<&Path> {
|
||||
self.data.data_directory()
|
||||
}
|
||||
|
||||
/// Set if this context allows automation.
|
||||
///
|
||||
/// **Note:** This is currently only enforced on Linux, and has the stipulation that
|
||||
/// only 1 context allows automation at a time.
|
||||
pub fn set_allows_automation(&mut self, flag: bool) {
|
||||
self.os.set_allows_automation(flag);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WebContext {
|
||||
fn default() -> Self {
|
||||
let data = WebContextData::default();
|
||||
let os = WebContextImpl::new(&data);
|
||||
Self { data, os }
|
||||
}
|
||||
}
|
||||
|
||||
/// Data that all [`WebContext`] share regardless of platform.
|
||||
#[derive(Default, Debug)]
|
||||
pub struct WebContextData {
|
||||
data_directory: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl WebContextData {
|
||||
/// A reference to the data directory the context was created with.
|
||||
pub fn data_directory(&self) -> Option<&Path> {
|
||||
self.data_directory.as_deref()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(gtk))]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct WebContextImpl;
|
||||
|
||||
#[cfg(not(gtk))]
|
||||
impl WebContextImpl {
|
||||
fn new(_data: &WebContextData) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn set_allows_automation(&mut self, _flag: bool) {}
|
||||
}
|
||||