Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Servo-kirja

API-dokumentaatio · Englanninkielinen alkuperä


Servo on Rust-ohjelmointikielellä kirjoitettu selainmoottori, jota kehitetään tällä hetkellä seuraaville alustoille:

  • 64-bittinen Linux
  • 64-bittinen macOS
  • 64-bittinen Windows
  • Android
  • OpenHarmony

Työtä webview-kirjastona käytettäväksi Servo jatkuvat. Tällä hetkellä suositeltu tapa ajaa Servoa selaimena on servoshell, winit- ja egui-pohjainen demoseilain. Jos haluat upottaa Servon omaan sovellukseesi, harkitse tauri-runtime-verso -ajoaikaa, mukautettua Tauri-ajoaikaa, tai servo-gtk -GTK4-pohjaista selainwidgetiä.

Kuvakaappaus servoshellistä

Tämä kirja opastaa servoshellin kääntämisessä ja ajamisessa, Servon kehittämisessä ja osallistumisessa, Servon arkkitehtuurissa sekä Servon ja sen kirjastojen käytössä.

Kirja on työn alla! Sisällysluettelossa * merkitsee lukuja, jotka on äskettäin lisätty tai tuotu vanhemmasta dokumentaatiosta ja jotka tarvitsevat vielä oikoluvun tai uudelleentyöstön.

Osallistuminen on aina tervetullutta. Ehdota muutoksia kunkin sivun oikean yläkulman kynäpainikkeella tai katso lisätietoja reposta Mikko-Huuskonen-Pro/Servo-kirja.

Tarvitsetko apua?

Liity Servo Zulipiin, jos sinulla on kysyttävää. Kaikki ovat tervetulleita!

Servoshellin hankkiminen

Servo-projekti tarjoaa pienen testiselaimen nimeltä servoshell, jonka avulla Servoa voi testata. servoshell ei ole täysiverinen selain, vaan tarkoitettu näkemään, miten Servo toimii järjestelmässäsi, ja testien ajamiseen. Voit ladata servoshellin uusimman valmiin version verkkosivumme latausosiosta. Voit ladata servoshellin myös alla olevista linkeistä:

Vanhemmat yökohtaiset julkaisut ovat saatavilla repossa servo/servo-nightly-builds.

Servoshellin käyttö

Oletetaan, että olet hakemistossa, jossa servo sijaitsee. Voit ajaa servoshellin komennolla:

$ ./servo [url] [options]

Käytä --help nähdäksesi käytettävissä olevat komentorivivalinnat:

$ ./servo --help

Kokeellisten web-alustan ominaisuuksien käyttöönotto

Servolla on käynnissä tuki monille web-alustan ominaisuuksille. Jotkut eivät ole vielä valmiita oletuksena käyttöön, mutta voit kokeilla niitä pref-asetuksella. Luettelo näistä ominaisuuksista ja niitä käyttöönotosta löytyy kohdasta Kokeelliset ominaisuudet. Lisäksi voit ottaa käyttöön hyödyllisen osajoukon näistä ominaisuuksista --enable-experimental-web-platform-features -komentoriviparametrilla tai servoshellin käyttöliittymän kautta.

Pikanäppäimet

  • Ctrl+Q (⌘Q macOS:llä) sulkee servoshellin
  • Ctrl+L (⌘L macOS:llä) kohdistaa osoitepalkkiin
  • Ctrl+R (⌘R macOS:llä) lataa sivun uudelleen
  • Alt+ (⌘← macOS:llä) siirtyy historiassa taaksepäin
  • Alt+ (⌘→ macOS:llä) siirtyy historiassa eteenpäin
  • Ctrl+= (⌘= macOS:llä) suurentaa sivun zoomia
  • Ctrl+- (⌘- macOS:llä) pienentää sivun zoomia
  • Ctrl+0 (⌘0 macOS:llä) palauttaa sivun zoomin
  • Esc poistuu koko näytön tilasta

Vianmääritys

servoshellin pitäisi toimia useimmilla järjestelmillä ilman erillisiä riippuvuuksia. Jos olet Linuxilla ja servoshell ilmoittaa puuttuvasta jaetusta kirjastosta, varmista että seuraavat paketit on asennettu:

  • GStreamer ≥ 1.18
  • gst-plugins-base ≥ 1.18
  • gst-plugins-good ≥ 1.18
  • gst-plugins-bad ≥ 1.18
  • gst-plugins-ugly ≥ 1.18
  • libXcursor
  • libXrandr
  • libXi
  • libxkbcommon
  • vulkan-loader

Lähdekoodin hankkiminen

Jos haluat tehdä muutoksia Servoon tai kääntää servoshellin itse, kloonaa päärepo Gitillä:

$ git clone https://github.com/servo/servo.git
$ cd servo

Servon repo on melko suuri! Jos verkkoyhteys on epävakaa tai levytila rajallinen, harkitse matalaa kloonausta.

Kun olet kloonannut Servon lähdekoodin, seuraava vaihe on riippuvuuksien asentaminen ja Servon kääntäminen.

Servon kääntäminen

Tällä sivulla on tarkempaa tietoa Servon kääntämisestä. Voit hypätä suoraan ohjeisiin omalle järjestelmällesi:

mach

Servon kääntämiseen tarvitset mach-työkalun. mach on Python-ohjelma, joka helpottaa Servon parissa työskentelyä monin tavoin: kääntäminen ja ajaminen, testien ajo ja riippuvuuksien päivitys.

Windows-käyttäjät: sinun täytyy korvata ./mach komennoissa merkkijonolla .\mach, jos käytät cmd:tä.

Käytä --help listataksesi alikomennot tai saadaksesi apua tiettyyn alikomentoon:

$ ./mach --help
$ ./mach build --help

Kun käytät machia ajamaan toista ohjelmaa, kuten servoshelliä, sillä ohjelmalla voi olla omia valintoja samoilla nimillä kuin machilla. Voit käyttää -- (välilyönneillä ympäröitynä) kertoaksesi machille, ettei se koske myöhempiin valintoihin vaan jättää ne toiselle ohjelmalle.

$ ./mach run --help         # Gets help for `mach run`.
$ ./mach run -d --help      # Still gets help for `mach run`.
$ ./mach run -d -- --help   # Gets help for the debug build of servoshell.

Tämä pätee myös Servon yksikkötesteihin, joissa on kolme valintakerrosta: mach-valinnat, cargo test -valinnat ja libtest-valinnat.

$ ./mach test-unit --help           # Gets help for `mach test-unit`.
$ ./mach test-unit -- --help        # Gets help for `cargo test`.
$ ./mach test-unit -- -- --help     # Gets help for the test harness (libtest).

Työtä jatketaan, jotta Servo voidaan kääntää ilman machia. Harkitse aina, voitko käyttää natiivia Cargo-toiminnallisuutta ennen kuin lisäät uutta toiminnallisuutta machiin.

Käännösprofiilit

On kolme pääkäännösprofiilia, joita voit kääntää ja käyttää toisistaan riippumatta:

  • debug-käännökset, joiden avulla voit käyttää debuggeria (lldb) (oletus, jos profiilia ei anneta)
  • release-käännökset, jotka kääntyvät hitaammin mutta ovat suorituskykyisempiä
  • production-käännökset, joita käytetään vain virallisissa julkaisuissa
debug release production
mach-valinta -d
--debug
-r
--release
--prod
--production
optimoitu? ei kyllä kyllä, enemmän kuin release
suurin RUST_LOG-taso trace info info
debug-väitteet? kylläkyllä(!)ei
debug-tiedot? kylläeiei
symbolit? kylläeikyllä
etsii resursseja
nykyisestä työhakemistosta?
kylläkylläei(!)

Lisäksi on kaksi erityistä production-käännöksen varianttia suorituskykyyn liittyviin käyttötarkoituksiin:

  • production-stripped -käännökset sopivat Servon benchmarkkaukseen ajan myötä, kun debug-symbolit on poistettu nopeampaa käynnistystä varten
  • profiling -käännökset sopivat profilointiin ja suorituskykyongelmien vianmääritykseen; ne käyttäytyvät kuin debug- tai release-käännös, mutta niiden suorituskyky on sama kuin production-käännöksellä
production production-stripped profiling
mach --profile production production-stripped profiling
debug-tiedot? eieikyllä
symbolit? kylläeikyllä
etsii resursseja
nykyisestä työhakemistosta?
eieikyllä(!)

Voit muuttaa näitä asetuksia servobuild-tiedostossa (katso servobuild.example) tai juuren Cargo.toml -tiedostossa.

Valinnaiset käännösasetukset

Joitakin käännösasetuksia voi ottaa käyttöön vain manuaalisesti:

  • AddressSanitizer-käännökset otetaan käyttöön komennolla ./mach build --with-asan
  • ThreadSanitizer-käännökset otetaan käyttöön komennolla ./mach build --with-tsan
  • crown-linttaus on suositeltava DOM-koodia muokattaessa, ja se otetaan käyttöön komennolla ./mach build --use-crown
  • SpiderMonkey debug -käännökset otetaan käyttöön komennolla ./mach build --debug-mozjs tai [build] debug-mozjs = true servobuild-tiedostossasi

Täydellisen argumenttilistan näet ajamalla ./mach build --help.

Servoshellin ajaminen

Kun käännät itse, servoshell on polussa target/debug/servo tai target/release/servo. Voit ajaa sen suoraan kuten yllä, mutta suosittelemme käyttämään machia sen sijaan.

Ajaaksesi servoshellin machilla, korvaa ./servo komennoilla ./mach run -d -- tai ./mach run -r -- riippuen käännösprofiilista, jonka haluat ajaa. Esimerkiksi molemmat alla olevat komennot ajavat debug-käännöksen servoshelliä samoilla valinnoilla:

$ target/debug/servo https://demo.servo.org
$ ./mach run -d -- https://demo.servo.org

Kääntäminen Linuxille

  • Asenna curl:
    • Arch: sudo pacman -S --needed curl
    • Debian, Ubuntu: sudo apt install curl
    • Fedora: sudo dnf install curl
    • Gentoo: sudo emerge net-misc/curl
  • Asenna uv: curl -LsSf https://astral.sh/uv/install.sh | sh
  • Asenna rustup: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  • Käynnistä shell uudelleen varmistaaksesi, että cargo on käytettävissä
  • Asenna muut riippuvuudet: ./mach bootstrap
  • Käännä servoshell: ./mach build

Tukemattomat jakelut

Jos ./mach boostrap ilmoittaa, että jakelusi ei ole tuettu, sinun täytyy asentaa riippuvuudet manuaalisesti. Alla on ohjeita käännösriippuvuuksien asentamiseen erilaisille jakelutyypeille. Jos jakelusi ei ole listalla, suositellaan sovittamaan lista järjestelmäsi pakettinimille. Päivitykset tähän listaan ovat erittäin tervetulleita!

Arch ja Manjaro

  • sudo pacman -S --needed curl

  • sudo pacman -S --needed base-devel git mesa cmake libxmu pkg-config ttf-fira-sans harfbuzz ccache llvm clang autoconf2.13 gstreamer gstreamer-vaapi gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly vulkan-icd-loader wireshark-cli

Debian-tyyppiset

(mukaan lukien elementary OS, KDE neon, Linux Mint, Pop!_OS, Raspbian, TUXEDO OS, Ubuntu)

  • sudo apt install curl
  • sudo apt install build-essential ca-certificates ccache clang cmake curl g++ git gperf gstreamer1.0-libav gstreamer1.0-plugins-bad gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-ugly gstreamer1.0-tools libdbus-1-dev libegl1-mesa-dev libfreetype6-dev libges-1.0-dev libgl1-mesa-dri libgles2-mesa-dev libglib2.0-dev libgstreamer-plugins-bad1.0-dev libgstreamer-plugins-base1.0-dev libgstrtspserver-1.0-dev libharfbuzz-dev liblzma-dev libudev-dev libunwind-dev libvulkan1 libx11-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-x11-0 libxkbcommon0 libxmu-dev libxmu6 llvm-dev m4 xorg-dev

Huom: Ubuntu-pohjaisissa jakelussa varmista, että sisällytät myös paketin libgstreamer-plugins-good1.0-dev yllä lueteltujen pakettien lisäksi.

Fedora-tyyppiset

  • sudo dnf install cabextract ccache clang clang-libs cmake dbus-devel expat-devel fontconfig-devel freetype-devel gcc-c++ glib2-devel gperf gstreamer1-devel gstreamer1-plugins-bad-free-devel gstreamer1-plugins-base-devel gstreamer1-plugins-good gstreamer1-plugins-ugly-free harfbuzz-devel libjpeg-turbo libjpeg-turbo-devel libtool libunwind-devel libX11-devel libXcursor-devel libXi-devel libxkbcommon libxkbcommon-x11 libXmu-devel libXrandr-devel llvm mesa-libEGL-devel mesa-libGL-devel ncurses-devel python3-devel rpm-build ttmkfdir vulkan-loader zlib-ng

Gentoo-tyyppiset

  • sudo emerge net-misc/curl media-libs/freetype media-libs/mesa dev-util/gperf dev-libs/openssl media-libs/harfbuzz dev-util/ccache sys-libs/libunwind x11-libs/libXmu x11-base/xorg-server sys-devel/clang media-libs/gstreamer media-libs/gst-plugins-base media-libs/gst-plugins-good media-libs/gst-plugins-bad media-libs/gst-plugins-ugly media-libs/vulkan-loader

openSUSE

  • sudo zypper install libX11-devel libexpat-devel Mesa-libEGL-devel Mesa-libGL-devel cabextract cmake dbus-1-devel fontconfig-devel freetype-devel gcc-c++ git glib2-devel gperf harfbuzz-devel libXcursor-devel libXi-devel libXmu-devel libXrandr-devel libopenssl-devel rpm-build ccache llvm-clang libclang autoconf213 gstreamer-devel gstreamer-plugins-base-devel gstreamer-plugins-good gstreamer-plugins-bad-devel gstreamer-plugins-ugly vulkan-loader libvulkan1

Void Linux

  • sudo xbps-install bzip2-devel cabextract ccache clang cmake cmake dbus-devel expat-devel fontconfig-devel freetype-devel gcc glib-devel glu-devel gperf gst-plugins-bad1-devel gst-plugins-base1-devel gst-plugins-good1 gst-plugins-ugly1 gstreamer1-devel harfbuzz-devel libtool libunwind-devel libX11-devel libXcursor-devel libXi-devel libxkbcommon libxkbcommon-x11 libXmu-devel libXrandr-devel MesaLib-devel ncurses-devel pkg-config vulkan-loader

Vianmääritys

Katso Yleinen vianmääritys -osio, jos käännöksessä on ongelmia eikä ongelmaasi ole listattu alla.

build: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.39' not found

Tämä kiertotie soveltuu, kun käännät Servoa nix:llä muilla Linux-jakelulla kuin NixOS:lla. Virhe tarkoittaa, että jakelun glibc-versio on vanhempi kuin nixpkgs-paketissa.

Muuta shell.nix-tiedoston loppuun rivi if ! [ -e /etc/NIXOS ]; then muotoon if false; then poistaaksesi shell.nix-tuen binääritulosteille, jotka eivät riipu nix-kaupasta.

Kääntäminen macOS:lle

  • Lataa ja asenna Xcode ja brew.
  • Asenna uv: curl -LsSf https://astral.sh/uv/install.sh | sh
  • Asenna rustup: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  • Käynnistä shell uudelleen varmistaaksesi, että cargo on käytettävissä
  • Asenna muut riippuvuudet: ./mach bootstrap
  • Käännä servoshell: ./mach build

Vianmääritys

Katso Yleinen vianmääritys -osio, jos käännöksessä on ongelmia.

Kääntäminen Windowsille

  • Lataa uv, choco ja rustup
    • Valitse Quick install via the Visual Studio Community installer
  • Varmista Visual Studio Installerissa, että seuraavat komponentit on asennettu:
    • Windows 10/11 SDK (anything >= 10.0.19041.0) (Microsoft.VisualStudio.Component.Windows{10, 11}SDK.{>=19041})
    • MSVC v143 - VS 2022 C++ x64/x86 build tools (Latest) (Microsoft.VisualStudio.Component.VC.Tools.x86.x64)
    • C++ ATL for latest v143 build tools (x86 & x64) (Microsoft.VisualStudio.Component.VC.ATL)
  • Käynnistä shell uudelleen varmistaaksesi, että cargo on käytettävissä
  • Asenna muut riippuvuudet: .\mach bootstrap
  • Käännä servoshell: .\mach build

Emme suosittele useamman kuin yhden Visual Studio -version asentamista. Servo yrittää etsiä oikean Visual Studio -version, mutta vain yhden version asentaminen vähentää virheiden riskiä.

Vianmääritys

Katso Yleinen vianmääritys -osio, jos käännöksessä on ongelmia eikä ongelmaasi ole listattu alla.

Cannot run mach in a path on a case-sensitive file system on Windows.
  • Avaa komentokehote tai PowerShell ylläpitäjänä (Win+X, A)
  • Poista kirjainkoon herkkyys Servo-repollesi:
    fsutil file SetCaseSensitiveInfo X:\path\to\servo disable
Could not find DLL dependency: api-ms-win-crt-runtime-l1-1-0.dll
DLL file `api-ms-win-crt-runtime-l1-1-0.dll` not found!

Etsi polku tiedostoon Redist\ucrt\DLLs\x64\api-ms-win-crt-runtime-l1-1-0.dll, esim. C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x64\api-ms-win-crt-runtime-l1-1-0.dll.

Aseta sitten WindowsSdkDir-ympäristömuuttuja polkuun, joka sisältää Redist, esim. C:\Program Files (x86)\Windows Kits\10.

thread 'main' panicked at 'Unable to find libclang: "couldn\'t find any valid shared libraries matching: [\'clang.dll\', \'libclang.dll\'], set the `LIBCLANG_PATH` environment variable to a path where one of these files can be found (invalid: [(C:\\Program Files\\LLVM\\bin\\libclang.dll: invalid DLL (64-bit))])"', C:\Users\me\.cargo\registry\src\...

rustup on ehkä asennettu 32-bittisellä oletusisännällä 64-bittisen oletusisännän sijaan, jota Servo tarvitsee. Tarkista oletusisäntä komennolla rustup show, aseta sitten oletusisäntä:

> rustup set default-host x86_64-pc-windows-msvc

ERROR: GetShortPathName returned a long path name: `C:/PROGRA~2/Windows Kits/10/`. Use `fsutil file setshortname' to create a short name for any components of this path that have spaces.

SpiderMonkey (mozjs) vaatii 8.3-tiedostonimet käyttöön Windowsilla (#26010).

  • Avaa komentokehote tai PowerShell ylläpitäjänä (Win+X, A)
  • Ota 8.3-tiedostonimien luonti käyttöön: fsutil behavior set disable8dot3 0
  • Poista ja asenna uudelleen mikä tahansa, joka sisältää epäonnistuvat polut, kuten Visual Studio tai Windows SDK — tämä on helpompaa kuin 8.3-tiedostonimien lisääminen käsin
= note: lld-link: error: undefined symbol: __std_search_1 >>> referenced by D:\a\mozjs\mozjs\mozjs-sys\mozjs\intl\components\src\NumberFormatterSkeleton.cpp:157

Tällaiset ongelmat voivat ilmetä mozjs:n päivityksen yhteydessä, koska päivitys voi riippua uudemmasta MSVC:stä (muista, että vaadimme “Latest” kohdassa set up your environment!). Ratkaise ongelma käynnistämällä Visual Studio Installer ja asentamalla kaikki saatavilla olevat päivitykset.

Kääntäminen NixOS:lle

  • Asenna Nix, paketinhallinta. Helpoin tapa on käyttää asennusohjelmaa monikäyttäjä- tai yksikäyttäjäasennuksella (valintasi mukaan).
  • Kerro mach:lle Nixin käytöstä: export MACH_USE_NIX=
  • Kirjoita nix-shell päästäksesi shelliin, jossa on kaikki tarvittavat työkalut ja riippuvuudet.
  • Asenna muut riippuvuudet: ./mach bootstrap
  • Käännä servoshell: ./mach build

Vianmääritys

Katso Yleinen vianmääritys -osio, jos käännöksessä on ongelmia eikä ongelmaasi ole listattu alla.

error: getting status of /nix/var/nix/daemon-socket/socket: Permission denied

Jos saat tämän virheen ja olet asentanut Nixin järjestelmän paketinhallinnalla:

  • Lisää itsesi nix-users-ryhmään
  • Kirjaudu ulos ja takaisin sisään
error: file 'nixpkgs' was not found in the Nix search path (add it using $NIX_PATH or -I)

Tämä virhe on vaaraton, mutta voit korjata sen näin:

  • Aja sudo nix-channel --add https://nixos.org/channels/nixpkgs-unstable nixpkgs
  • Aja sudo nix-channel --update

Kääntäminen WSL:llä

Servo voidaan kääntää WSL:llä kuten millä tahansa muulla Linux-jakelulla. Ajaaksesi ei-headless servoshelliä WSL:llä, tarvitset todennäköisesti Windows 10 Build 19044+ tai Windows 11, koska WSL v2 -pääsy tarvitaan graafiseen käyttöliittymään.

  1. Ota WSL v2 käyttöön. Katso Microsoftin ohjeet GUI-sovellusten käyttöön WSL:llä.
  2. Seuraa ohjeita Servon kääntämiseen ja ajamiseen käyttämälläsi WSL-jakelulla (esim. Ubuntu, OpenSuse jne.)

WSL v2:ssä on vastaavat sovittimet Wayland- ja X11-sovellusten näyttämiseen, vaikka se ei aina toimi suoraan Servon kanssa.

Vianmääritys

Katso Yleinen vianmääritys -osio, jos käännöksessä on ongelmia eikä ongelmaasi ole listattu alla.

Failed to create event loop

Jos kohtaat välittömän kaatumisen ajon jälkeen, joka viittaa winitiin ja sen alustatoteutukseen, WAYLAND_DISPLAY='' pysäyttää kaatumisen.

Failed to create events loop: Os(OsError { line: 81, file: "/home/astra/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winit-0.30.9/src/platform_impl/linux/wayland/event_loop/mod.rs", error: WaylandError(Connection(NoCompositor)) }) (thread main, at ports/servoshell/desktop/cli.rs:34)
   0: servoshell::backtrace::print
             at /home/astra/workspace/servo/ports/servoshell/backtrace.rs:18:5
  ...
  18: main
  19: <unknown>
  20: __libc_start_main
  21: _start
Servo was terminated by signal 11

Joko vie muuttuja ympäristöön tai aseta se ennen ajoa:

export WAYLAND_DISPLAY=''
./mach run

# or

WAYLAND_DISPLAY='' ./mach run

# optionally save the variable long term to your .bashrc profile

echo 'export WAYLAND_DISPLAY=""' >> ~/.bashrc
Library libxkbcommon-x11.so could not be loaded

Tämä voi johtua siitä, että jakelusi ei ole asentanut vaadittua kirjastoa. Aja seuraava komento (oletetaan WSL Debian/Ubuntu, säädä muille jakelulle):

sudo apt install libxkbcommon-x11-0
error: failed to run build command...

Jos kohtaat alla olevan kaltaisen virheen ./mach build -ajossa WSL:llä, syy voi olla muistin loppuminen (OOM), koska WSL:llä ei ole tarpeeksi RAMia Servon kääntämiseen. Sinun täytyy kasvattaa WSL:n muistirajaa ja swap-tiedostoa tai päivittää RAM.

yourusername@PC:~/servo$ ./mach build
No build type specified so assuming `--dev`.
Building `debug` build with crown disabled (no JS garbage collection linting).
   ...
   Compiling script v0.0.1 (/home/yourusername/servo/components/script)

error: failed to run build command...

Caused by:
  process didn't exit successfully: `/home/yourusername/.rustup/toolchains/1.91.0-x86_64-unknown-linux-gnu/bin/rustc --crate-name script --edition=2024 components/script/lib.rs...
  ...
warning: build failed, waiting for other jobs to finish...

Luo C:/user/yourusername/.wslconfig ja lisää seuraava:

[wsl2]
memory=6GB
swap=16GB
swapfile=C:\\Users\\yourusername\\swapfile.vhdx

Tallenna tiedosto ja käynnistä WSL uudelleen PowerShellissä:

wsl --shutdown

Kääntäminen Androidille

  • Varmista, että seuraavat ympäristömuuttujat on asetettu:
    • ANDROID_SDK_ROOT
    • ANDROID_NDK_ROOT: $ANDROID_SDK_ROOT/ndk/28.2.13676358/ ANDROID_SDK_ROOT voi olla mikä tahansa hakemisto (kuten ~/android-sdk). Kaikki Android-käännösriippuvuudet asennetaan sinne.
  • Asenna Android command-line tools -työkalujen uusin versio hakemistoon $ANDROID_SDK_ROOT/cmdline-tools/latest.
  • Asenna tarvittavat komponentit seuraavalla komennolla:
    sudo $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --install \
     "build-tools;36.0.0" \
     "emulator" \
     "ndk;28.2.13676358" \
     "platform-tools" \
     "platforms;android-37" \
     "system-images;android-37;google_apis;arm64-v8a"
    
  • Aja ./mach build --android

Huom: Tämä asentaa riippuvuudet ja kääntää Servon aarch64-linux-android -alustalle. Kääntääksesi Servon muille Android-kohteille, asenna sopivat järjestelmäkuvat sdkmanager:lla ja anna mach:lle --target Rust-yhteensopivalla kohteella --android:n sijaan.

Huom: Jos et käytä Android Studioa macOS:lla, sinun täytyy asentaa JDK. Käytä brew install opendjdk@21 asentaaksesi toimivan version; uudemmat versiot aiheuttavat java.lang.IllegalArgumentException: 25 Servon käännöksen gradle-vaiheessa.

Huom: Jos käytät Nixiä, sinun ei tarvitse asentaa työkaluja tai asettaa ANDROID_SDK_ROOT- ja ANDROID_NDK_ROOT -ympäristömuuttujia manuaalisesti. Ota Android-käännöstuki käyttöön ajamalla:

export SERVO_ANDROID_BUILD=1

shell-istunnossa ennen ./mach-komentoja

Kääntäminen Android Studiolla

Servon kääntäminen komentoriviltä on suositeltavaa Androidille, mutta voit kääntää sen myös Android Studiolla, jos haluat käyttää Android-IDE:tä.

  • Asenna Android Studio lataamalla sopiva versio viralliselta sivustolta ja seuraamalla asennusohjeita.
  • Asentaaksesi lisätyökaluja, käynnistä Android Studio, mene Settings-valikkoon ja kirjoita hakupalkkiin sdk.
  • Valitse SDK:
    • Klikkaa Android SDK`
    • Siirry kohtaan SDK Tools
    • Valitse Android SDK Command-line tools (latest): image
  • Valitse NDK. Huomaa, että Servo vaatii NDK:n version 28.
    • Osiossa SDK Tools valitse NDK (side by side)
    • Klikkaa show package details: image
    • Valitse NDK 28:n uusin versio: image
  • Klikkaa Ok asentaaksesi sekä NDK:n että SDK:n.
  • Etsi SDK:n polku kohdasta Languages & Frameworks sitten Android SDK Location: image. Varmista sitten, että seuraavat ympäristömuuttujat on asetettu:
    • ANDROID_SDK_ROOT: Yllä löytynyt polku.
    • ANDROID_NDK_ROOT: $ANDROID_SDK_ROOT/ndk/28.2.13676358/
  • Aja ./mach build --android

Ajaminen emulaattorissa

  1. Luo uusi AVD-kuva Servon ajamiseen:
    $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/avdmanager create avd \
        --name "Servo" \
        --device "pixel" \
        --package "system-images;android-33;google_apis;arm64-v8a" \
        --tag "google_apis" \
        --abi "arm64-v8a"
    
  2. Ota laitteiston näppäimistö käyttöön. Avaa ~/.android/avd/Servo.avd/config.ini ja muuta hw.keyboard = no muotoon hw.keyboard = yes.
  3. Käynnistä emulaattori
    $ANDROID_SDK_ROOT/emulator/emulator -avd Servo -netdelay none -no-snapshot
    
  4. Asenna Servo emulaattoriin:
     ./mach install --android
    
  5. Käynnistä Servo napauttamalla Servo-kuvaketta käynnistysohjelmassa.

Asentaminen fyysiselle laitteelle

  1. Valmistele laitteesi kehitystä varten.
  2. Käännä Servo kuten yllä, varmistaen että käännät sopivalle kohteelle laitteellesi.
  3. Asenna Servo laitteellesi ajamalla:
    ./mach install --android
    
  4. Käynnistä Servo napauttamalla Servo-kuvaketta käynnistysohjelmassa tai aja:
    ./mach run --android https://www.servo.org/
    

Voit pakottaa Servon pysähtymään ajamalla:

adb shell am force-stop org.servo.servoshell/org.servo.servoshell.MainActivity

Jos yllä oleva ei toimi, kokeile tätä:

adb shell am force-stop org.servo.servoshell

Voit poistaa Servon asennuksen ajamalla:

adb uninstall org.servo.servoshell

Vianmääritys

Katso Yleinen vianmääritys -osio, jos käännöksessä on ongelmia.

Kääntäminen OpenHarmonylle

OpenHarmony-tuki on parhaillaan kehitteillä, ja nämä ohjeet voivat muuttua ajoittain ja olla myös puutteellisia.

OpenHarmony-työkalujen hankkiminen

OpenHarmonylle kääntäminen vaatii seuraavat:

  1. OpenHarmony SDK. Tämä riittää Servon kääntämiseen jaetulla kirjastolla OpenHarmonylle.
  2. hvigor-käännöstyökalun sovelluksen kääntämiseen sovelluspaketiksi ja allekirjoittamiseen.

OpenHarmony SDK:n asennus

OpenHarmony SDK vaaditaan sovellusten kääntämiseen OpenHarmonylle. Servon tukeman SDK:n vähimmäisversio on v6.0.0 (API-20).

Lataus DevEco Studion kautta

DevEco Studio on IDE HarmonyOS NEXT- ja OpenHarmony-sovellusten kehittämiseen. Se tukee Windowsia ja macOS:ää. Voit hallita asennettuja OpenHarmony SDK:ita valitsemalla File->Settings ja “OpenHarmony SDK”. Kun olet asettanut sopivan asennuspolun, voit valita asennettavat komponentit kullekin saatavilla olevalle API-versiolle. DevEco Studio lataa ja asentaa komponentit automaattisesti.

OpenHarmony SDK:n manuaalinen asennus (esim. Linuxilla)

Ennen kuin lataat OH SDK:n giteestä kuten tässä kuvataan, huomaa että tarvitset myös hvigorin sovellusten kääntämiseen. hvigor suositellaan ladattavaksi HarmonyOS NEXT -komentorivityökalupaketin kautta, joka sisältää myös kopion OpenHarmony SDK:sta.
  1. Mene OpenHarmony release notes -sivulle ja valitse versio, jolle haluat kääntää.
  2. Vieritä osioon “Acquiring Source Code from Mirrors” ja klikkaa latauslinkkiä “Public SDK package for the standard system” -versiolle, joka vastaa isäntäjärjestelmääsi.
  3. Pura arkisto sopivaan sijaintiin.
  4. Siirry SDK-kansioon komennolla cd <sdk_folder>/<your_operating_system>.
  5. Luo alikansio samalla nimellä kuin API-versio (esim. 14 SDK v5.0.2:lle) ja siirry siihen.
  6. Pura yksittäisten komponenttien zip-tiedostot edellisessä vaiheessa luotuun kansioon. Käytä mieluiten unzip-komentoa komentorivillä, tai varmista manuaalisesti että puretut paketit ovat nimeltään esim. native eivätkä native-linux-x64-5.x.y.z.

Seuraava pätkä toimii viitteenä vaiheille 4–6:

cd ~/ohos-sdk/linux
for COMPONENT in "native toolchains ets js previewer" do
    echo "Extracting component ${COMPONENT}"
    unzip ${COMPONENT}-*.zip
    API_VERSION=$(cat ${COMPONENT}/oh-uni-package.json | jq -r '.apiVersion')
    mkdir -p ${API_VERSION}
    mv ${COMPONENT} "${API_VERSION}/"
done

Windowsilla on suositeltavaa käyttää 7zip:iä arkistojen purkamiseen, koska Windows Explorerin purkutyökalu on erittäin hidas.

HarmonyOS NEXT -komentorivityökalujen manuaalinen asennus

HarmonyOS NEXT commandline tools sisältää OpenHarmony SDK:n ja seuraavat lisätyökalut:

  • codelinter (linter)
  • hstack (crash dump stack analysis tool)
  • hvigor / hvigorw (build tool)
  • ohpm (package manager)

Tällä hetkellä komentorivityökalupaketti ei ole julkisesti saatavilla ja vaatii kiinalaisen Huawei-tilin lataamiseen.

hvigorin manuaalinen asennus ilman komentorivityökaluja

Tätä osiota ei ole täysin testattu, ja se voi muuttua käyttäjäpalautteen perusteella. Komentorivityökalupaketin asentaminen on suositeltavaa. Jos päätät asentaa manuaalisesti, sinun täytyy huolehtia hvigor-version asentamisesta, joka vastaa projektisi vaatimuksia.

hvigor (ei wrapper hvigorw) on saatavilla myös npm:n kautta.

  1. Asenna sama nodejs-versio kuin komentorivityökalut toimittavat. HarmonyOS NEXT:lle toimitetaan Node 18. Varmista, että node-binääri on PATH:ssa.

  2. Asenna Java suositellulla asennustavalla käyttöjärjestelmällesi. Käännösvaiheet toimivat tunnetusti OpenJDK v17, v21 ja v23:lla. macOS:lla, jos asennat Homebrew’n OpenJDK formula -kaavan, seuraava lisäkomento saattaa olla tarpeen asennuksen jälkeen:

    # For the system Java wrappers to find this JDK, symlink it with
    sudo ln -sfn $HOMEBREW_PREFIX/opt/openjdk/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk
    
  3. Muokkaa .npmrc-tiedostoasi sisältämään seuraava rivi:

    @ohos:registry=https://repo.harmonyos.com/npm/
    
  4. Asenna hvigor ja hvigor-ohos-plugin. Tämä luo node_modules-hakemiston nykyiseen hakemistoon.

    npm install @ohos/hvigor
    npm install @ohos/hvigor-ohos-plugin
    
  5. Nyt sinun pitäisi pystyä ajamaan hvigor.js OpenHarmony-projektissasi kääntääksesi hap-paketin:

    /path/to/node_modules/@ohos/hvigor/bin/hvigor.js assembleHap
    

hdc:n konfigurointi Linuxilla

hdc on OpenHarmony-laitteiden vastine adb:lle. Löydät sen SDK:n toolchains-hakemistosta. Käytännön syistä saatat haluta lisätä toolchainsin PATH:iin. Muun muassa hdc:llä voi avata shellin tai siirtää tiedostoja laitteen ja isäntäjärjestelmän välillä. hdc tarvitsee yhteyden fyysiseen laitteeseen usb:n kautta, mikä vaatii käyttäjältä oikeudet laitteen käyttöön.

On suositeltavaa lisätä udev-sääntö, jotta hdc pääsee laitteeseen ilman root-oikeuksia. Tämä stackoverflow-vastaus pätee myös hdc:hen. Aja lsusb ja tarkista laitteesi vendor id, luo sitten vastaava udev-sääntö. Huomaa, että käyttäjäsi tulee olla GROUP="xxx":llä määritetyn ryhmän jäsen. Jakelustasi riippuen saatat haluta käyttää eri ryhmää.

Tarkistaaksesi toimiiko hdc, aja hdc list targets — sen pitäisi näyttää laitteesi sarjanumero. Jos se ei toimi, kokeile uudelleenkäynnistystä.

Huomaa, että laitteesi täytyy olla “Developer mode” -tilassa USB-debuggaus päällä. Prosessi on täsmälleen sama kuin Androidilla:

  1. Napauta build-numeroa useita kertoja ottaaksesi kehittäjätilan käyttöön.
  2. Siirry sitten kehittäjäasetuksiin ja ota USB-debuggaus käyttöön.
  3. Kun yhdistät laitteen ensimmäistä kertaa, vahvista ponnahdusikkuna, jossa kysytään luotatko yhdistettävään tietokoneeseen.

Allekirjoitusasetukset

Useimmat laitteet vaativat, että HAP on digitaalisesti allekirjoitettu kehittäjän toimesta asennusta varten. hvigor-työkalulla tämä onnistuu asettamalla staattinen signingConfigs-objekti build-profile.json5-tiedostoon tai luomalla dynaamisesti signingConfigs-taulukko sovelluskontekstiobjektille hvigorfile.ts-skriptissä.

signingConfigs-ominaisuus on taulukko objekteja seuraavalla rakenteella:

[
    {
        "name": "default",
        "type": "<OpenHarmony or HarmonyOS>",
        "material": {
            "certpath": "/path/to/app-signing-certificate.cer",
            "storePassword": "<encrypted password>",
            "keyAlias": "debugKey",
            "keyPassword": "<encrypted password>",
            "profile": "/path/to/signed-profile-certificate.p7b",
            "signAlg": "SHA256withECDSA",
            "storeFile": "/path/to/java-keystore-file.p12"
        }
    }
]

Tässä <encrypted password> on salasanan plaintext-esityksen heksadesimaalimerkkijono salauksen jälkeen. Avain ja suola salasanojen salaamiseen luodaan DevEco Studio IDE:llä, ja ne tallennetaan levylle sertifikaattitiedostojen ja keystoren rinnalle, yleensä hakemistoon <USER HOME>/.ohos.

Luodaksesi salauksessa tarvittavat tiedot, vaaditut sovellus- ja profiilisertifikaattitiedostot ja itse keystoren, voit kloonata esimerkki-ArkTS-sovelluksen ja avata sen DevEco Studio IDE:llä. Huomaa, että koska allekirjoitustiedot sidotaan bundle-nimeen, kaikki ArkTS-sovellukset eivät toimi, ja siksi on erittäin suositeltavaa käyttää yllä mainittua esimerkki-ArkTS-sovellusta.

  1. Avaa Project Structure -valintaikkuna valikosta File > Project Structure.
  2. Välilehdellä ‘Signing Config’ ota käyttöön ‘Automatically generate signature’ -valintaruutu.

HUOM: Yllä automaattisesti luotu allekirjoitus on tarkoitettu vain kehitykseen ja testaukseen. Tuotantokäännöksiin ja jakeluun sovelluskaupan kautta tarvitaan asianmukainen konfiguraatio sovelluskaupan tarjoajalta.

Linux-käyttäjille DevEco Studio on saatavilla vain Windowsilla ja macOS:lla. Jatkaaksesi tarvitset toisen Windows- / macOS-koneen, jossa DevEco Studio IDE on asennettuna allekirjoitusavainten luomiseen. Jos kehität OpenHarmony-levyjä (kuten HopeRun-kehityslevy), voit nimetä SigningConfigs:in default. Muuten aseta se arvoon hos, jos kehität Servoa HarmonyOS-laitteille (kuten Huawei Mate -puhelinsarja).

Kun avaimet on luotu, sinun täytyy siirtää koko hakemisto, joka tallentaa avaimet (yleensä <USER HOME>/.ohos/), DevEco Studion luomalta Windows- / macOS-koneelta.

Lisäksi sinun täytyy kopioida SigningConfigs DevEco Studion luomasta build-profile.json5:stä Windows- / macOS-koneelta .json-tiedostoon Linux-koneellasi. Tämä toimii “signing material” -lähteenä, johon mach voi myöhemmin viitata.

Kun luotu, sinun täytyy osoittaa mach yllä olevaan “signing material” -konfiguraatioon SERVO_OHOS_SIGNING_CONFIG-ympäristömuuttujalla. Muuttujan arvon täytyy olla polku kelvolliseen .json-tiedostoon, jolla on sama rakenne kuin yllä olevalla signingConfigs-ominaisuudella, mutta jossa certPath, storeFile ja profile on annettu polkuina suhteessa json-tiedostoon absoluuttisten polkujen sijaan.

servoshellin kääntäminen

Ennen Servon kääntämistä sinun täytyy asettaa joitakin ympäristömuuttujia. direnv on kätevä työkalu, joka voi asettaa nämä muuttujat automaattisesti .envrc-tiedoston perusteella, mutta voit käyttää mitä tahansa muuta tapaa asettaaksesi vaaditut ympäristömuuttujat.

.envrc:

    export OHOS_SDK_NATIVE=/path/to/openharmony-sdk/platform/api-version/native

    # Required if the HAP must be signed. See the signing configuration section above.
    export SERVO_OHOS_SIGNING_CONFIG=/path/to/signing-configs.json

    # Required only when building for HarmonyOS:
    export DEVECO_SDK_HOME=/path/to/command-line-tools/sdk # OR /path/to/DevEcoStudio/sdk OR on MacOS /Applications/DevEco-Studio.app/Contents/sdk

    # Required only when building for OpenHarmony:
    # Note: The openharmony sdk is under ${DEVECO_SDK_HOME}/default/openharmony
    # Presumably you would need to replicate this directory structure
    export OHOS_BASE_SDK_HOME=/path/to/openharmony-sdk/platform

    # If you have the command-line tools installed:
    export PATH="${PATH}:/path/to/command-line-tools/bin/"
    export NODE_HOME=/path/to/command-line-tools/tool/node

    # Alternatively, if you do NOT have the command-line tools installed:
    export HVIGOR_PATH=/path/to/parent/directory/containing/node_modules  # Not required if `hvigorw` is in $PATH

Jos käytät direnv:iä ja .envrc-tiedostoa, muista ajaa direnv allow . .envrc-tiedoston muokkaamisen jälkeen. Muuten ympäristömuuttujia ei ladata.

Seuraavalla komennolla voit kääntää servoshell-sovelluksen 64-bittiselle ARM-laitteelle tai emulaattorille:

./mach build --ohos --release [--flavor=harmonyos]

Komennoissa mach build, mach install ja mach package --ohos on alias --target aarch64-unknown-linux-ohos:lle. Kääntääksesi emulaattorille, joka ajaa x86-64-isännällä, käytä --target x86_64-unknown-linux-ohos. Oletus ohos -käännös / pakkaus / asennus kohdistaa OpenHarmonyyn. Jos haluat kääntää HarmonyOS:lle, voit lisätä --flavor=harmonyos. Tarkista Allekirjoitusasetukset ja lisää konfiguraatio, jossa "name": "hos" ja "type": "HarmonyOS"" ja vastaavat allekirjoitussertifikaatit.

Asentaminen ja ajaminen laitteella

Seuraavalla komennolla voit asentaa aiemmin käännetyn servoshell-sovelluksen 64-bittiselle ARM-laitteelle tai emulaattorille:

./mach install --ohos --release [--flavor=harmonyos]

Lisälukemista

OpenHarmony Glossary

Vianmääritys

Katso Yleinen vianmääritys -osio, jos käännöksessä on ongelmia.

Offline-käännös

Servon julkaisut tarjoavat servo-<release-tag>-src-vendored.tar.gz -artifaktin, joka sisältää Servon lähdekoodin ja kaikki Rust-riippuvuudet vendoroituina. Tiedoston .cargo/config.toml sisältää tarvittavan konfiguraation Servon offline-kääntämiseen vendoroituja riippuvuuksia käyttäen.

Linux

Katso Linux-kääntämisohjeet lisätietoja käännösriippuvuuksien asentamisesta. Suosittelemme yleensä ./mach build -komentoa Servon kääntämiseen, mutta se vaatii uv:n asennuksen ja Python-riippuvuuksien synkronoinnin uv sync -komennolla etukäteen (muuten ./mach yrittää käyttää verkkoa Python-ympäristön asentamiseen).

# online pre-build environment (e.g. building a docker container)
uv sync
# offline build environment
./mach build --profile=production --frozen

Jos tämä on vaikeaa, voit kääntää Servon myös cargo:lla, jolloin mikä tahansa tuore Python-versio (>= 3.11) riittää (tätä ei testata CI:ssä). Huomaa, että ./mach build ottaa media-gstreamer -ominaisuuden oletuksena käyttöön — cargo:lla sinun täytyy ottaa tämä ominaisuus käyttöön manuaalisesti ja asettaa gstreamer-liittyvät ympäristömuuttujat. Gstreamer-ominaisuuden vaatimia ympäristömuuttujia ei ole dokumentoitu tässä, mutta dokumentaatioparannukset ./mach build -koodin pohjalta ovat tervetulleita.

# Build an offline release build with the production profile.
cargo build --profile=production --frozen

Windows ja macOS

Windowsilla ja macOS:lla ./mach bootstrap lataa lisäriippuvuuksia Servon kääntämiseen. Näitä ei tällä hetkellä toimiteta tarballissa. Jos offline-käännökset näille alustoille kiinnostavat, osallistuminen on tervetullutta (mutta ota ensin yhteyttä Zulipissa tai GitHub-issueissa).

Valmiiksi käännetyt SpiderMonkey-artifaktit

Online-käännökset käyttävät oletuksena valmiiksi käännetyitä SpiderMonkey-artifakteja, jotka on hostattu servo/mozjs -repon GitHub-julkaisuissa. Jos haluat yksinkertaistaa tai nopeuttaa käännösympäristöäsi, voit ladata nämä artifaktit itse ja käyttää MOZJS_ARCHIVE=path/to/libmozjs.tar.gz offline-käännöksissä. Voit varmistaa ladattujen artifaktien eheyden GitHub-attestointien avulla gh-työkalulla:

gh attestation verify path/to/libmozjs.tar.gz -R servo/mozjs

Yleinen vianmääritys

Jos Servon kääntämisessä ilmenee ongelmia, joita ei ole listattu muilla sivuilla, kokeile ensin alla olevia vaiheita:

  1. Varmista, että sinulla on kaikki listatut käännösvaatimukset. Erityisesti jos käytät harvinaista Linux-jakelua tai muuta Unix-tyyppistä järjestelmää, sinun täytyy ehkä selvittää tietyn riippuvuuden oikea nimi järjestelmässäsi.
  2. Tarkista uudelleen, että käännösvaatimukset on asennettu ja katso riippuvuusversiot.
  3. Aja ./mach boostrap tai .\mach boostrap Windowsilla. Joskus Servon kääntämiseen tarvittavat työkalut tai riippuvuudet muuttuvat. Komennon voi ajaa useamman kerran turvallisesti. Varmista, ettei suorituksen aikana ilmoitettu virheitä. Jos olet asentanut muita riippuvuuksia manuaalisesti, sinun täytyy ehkä ajaa ./mach bootstrap --skip-platform.
  4. Päivitä ympäristösi. Tämä voi tarkoittaa:
    • Shellin uudelleenkäynnistystä
    • Uloskirjautumista ja takaisin sisäänkirjautumista
    • Tietokoneen uudelleenkäynnistystä

Riippuvuusversiot

  • curl --version tulostaa version kuten 7.83.1 tai 8.4.0
    • Windowsilla kirjoita curl.exe --version PowerShellin Invoke-WebRequest -aliasin välttämiseksi
  • uv --version tulostaa version 0.4.30 tai uudemman
    • Servon mach-käännöstyökalu riippuu uv:sta kiinnitetyn Python-version (repossa .python-version-tiedostossa) tarjoamiseen ja paikallisen virtuaaliympäristön (.venv-hakemisto) luomiseen, johon Python-riippuvuusmoduulit asennetaan.
    • Jos järjestelmässä on jo vaaditun Python-version asennus, uv linkittää siihen levytilan säästämiseksi.
    • Jos versiot eivät täsmää tai Pythonia ei ole asennettuna, uv lataa tarvittavat binäärit.
    • Ulkoisesti hallitun Python-asennuksen käyttöä mach-skriptin ajamiseen ei tällä hetkellä tueta.
  • rustup --version tulostaa version kuten 1.26.0
  • Windows: choco --version tulostaa version kuten 2.2.2
  • macOS: brew --version tulostaa version kuten 4.2.17

Et ole yksin!

Jos Servon kääntämisessä on ongelmia, joita et ratkaise, voit aina pyytää apua build issues -keskustelussa Zulipissa.

Yleiskuva: Servon upottaminen

Servo on selainmoottori, jonka on tarkoitus olla helppo upottaa muihin sovelluksiin. Tällä hetkellä dokumentaatio Servon upottamisesta on niukkaa, ja tämä luku on aktiivisesti työn alla. Ota rohkeasti yhteyttä Servo Zulip -keskustelussa, jos sinulla on kysyttävää.

Servon kääntäminen ilman machia

Servo voidaan kääntää suoraan cargo:lla, vaikka joudut todennäköisesti toteuttamaan osan machin toiminnallisuudesta uudelleen. Alla on lyhyt yleiskuva asioista, joita kannattaa pitää mielessä.

Ympäristömuuttujat

./mach asettaa useita ympäristömuuttujia käännöksen ohjaamiseen, mikä on erityisen tarpeellista ristiinkäännöksessä esim. Androidille tai OpenHarmonylle. Voit ajaa ./mach print-env nähdäksesi mitkä muuttujat asetetaan, ja käyttää sitä käännöksen konfigurointiin.

Mediatuki

./mach build ottaa media-gstreamer -ominaisuuden oletuksena käyttöön, mikä ei päde tavallisiin käännöksiin. media-gstreamer vaaditaan mediatuelle yleisillä työpöytäalustoilla. Linuxilla sinun täytyy asentaa vaaditut gstreamer-kirjastot paketinhallinnan kautta (esim. ajamalla ./mach bootstrap). macOS:lla ./mach bootstrap asentaa vaaditut gstreamer-kirjastot asentamalla uusimmat paketit osoitteesta https://github.com/servo/servo-build-deps/releases/tag/macOS. Windowsilla voit asentaa gstreamer-kirjastot osoitteesta https://github.com/servo/servo-build-deps/releases/tag/msvc-deps (versio 1.22.8).

Resurssit

Servo tarvitsee resursseja ja tarjoaa oletusversiot servo-default-resources -craten kautta, jos baked-in-resources -ominaisuus on käytössä. Tämä toteutetaan upottamalla resurssit binääriin. Upottajat voivat kieltäytyä tästä poistamalla default-features -ominaisuudet käytöstä ja tarjoamalla oman resurssienlukumekanismin servo_embedder_traits::submit_resource_reader:n kautta. Tällöin ResourceReader-toteutuksen vastuulla on tarjota kaikki resurssit.

Esimerkki on OpenHarmony-portti, joka lukee kaikki resurssit tiedostojärjestelmästä: ohos/resources.rs.

Servon LTS-julkaisut

Kuukausittaisten julkaisujemme lisäksi Servo-yhteisö tarjoaa parhaan ponnistelun mukaisia pitkäaikaistuen (LTS) -julkaisuja. Koska Servon julkinen API on vielä kehittymässä, nämä LTS-julkaisut voivat sopia upottajille, joilla on rajalliset resurssit pysyä ajan tasalla uusimmasta Servo-julkaisusta. LTS-julkaisut antavat upottajille aikataulutetut päivitysikkunat ja hyödyn tietoturvakorjauksista, mukaan lukien JavaScript-moottorimme tietoturvakorjaukset.

LTS-julkaisujen laajuus

Servo toimitetaan SELLAISENAAN eikä sille anneta erityisiä takuita. LTS-julkaisut (ja alla olevat yksityiskohdat) tarjotaan parhaan ponnistelun mukaisesti. Toistaiseksi tämä tarkoittaa:

  • Uusi LTS-julkaisu / haara otetaan käyttöön 6 kuukauden välein, perustuen silloisen tavallisen julkaisun versioon.
  • Odotettu tukikesto on 9 kuukautta, mikä antaa upottajille aikaa siirtyä seuraavaan LTS-julkaisuun.
  • LTS-julkaisu saa vain tietoturvakorjauksia.
  • Korjausjulkaisut tehdään tarpeen mukaan; kiinteää aikataulua ei ole.
  • Laajuudessa on vain servo-kirjasto ja sen riippuvuudet. Selaindemo servoshell on nimenomaisesti laajuuden ulkopuolella.
  • Tuettua vähimmäis-Rust-versiota (MSRV) ei nosteta LTS-haarassa, mutta se voi nousta siirryttäessä seuraavaan LTS-versioon
  • Julkaisut julkaistaan crates.io:hon jos mahdollista, mutta upottajien tulee varautua siihen, että git-riippuvuuksia saatetaan vaatia.

CVE-korjausten paikkaaminen alavirta-cratesissa

Monet Rust-kirjastot eivät yleensä tuo CVE-korjauksia vanhempiin julkaisuihin / haaroihin. Koska MSRV-nostot käsitellään taaksepäin yhteensopimattomina muutoksina, tämä voi johtaa siihen, ettei voimme päivittää kirjaston uudempaan julkaistuun versioon (joka korjaa CVE:n). Nämä tilanteet käsitellään tapauskohtaisesti, ihanteellisesti yhteistyössä upstream-ylläpitäjän kanssa, ja ne sisältävät todennäköisesti kirjaston paikatun version hakemisen git:n kautta. Tämä tarkoittaa, että Servon LTS-korjausjulkaisuja ei pidä odottaa crates.io:ssa, koska niissä voi olla git-riippuvuuksia.

Rajoitukset

  • Servo toimitetaan SELLAISENAAN eikä sille anneta erityisiä takuita, mukaan lukien tietoturvatakuut. LTS-julkaisut tarjotaan parhaan ponnistelun mukaisesti kiinnostuneiden yhteisön jäsenten toimesta.
  • Kuten yllä mainittiin, Servolla ei ole vielä 1.0-julkaisua, mikä tarkoittaa että Servon tuotantokäyttöä tulee arvioida huolellisesti. Servon käyttö sovelluksessa tunnetun, luotetun sisällön renderöintiin on hyvin erilainen riskiprofiili kuin Servon käyttö selaimena mielivaltaisen sisällön renderöintiin.

LTS-julkaisujen ylläpitäjät

  • @jschwe (Jonathan Schwender)
  • TBD

Aloitus

Servo toivottaa kaikki avustajat tervetulleiksi. Verkkomoottorin parissa työskentely voi olla haastavaa ja joskus turhauttavaa, mutta se voi olla myös hyvin palkitsevaa ja hauskaa. Jos käytät aikaa ja vaivaa osallistumiseen, opit jatkuvasti ja kehityt kehittäjänä ja avoimen lähdekoodin avustajana. Aloita seuraavasti:

  1. Lue loput tästä sivusta saadaksesi perustiedot Servoon avustamisesta.
  2. Hae Servon repositorio onnistuneesti ja käännä Servo.
  3. Luo Servon fork ja opi Gitin perusteet seuraamalla Git-asetukset -luvun ohjeita.
  4. Opettele hieman Rustia. Verkossa on paljon resursseja, mutta yksi parhaista on virallinen Learning Rust -dokumentaatio. Jos tunnet muut imperatiiviset ohjelmointikielet, voit oppia Rustia työskennellessäsi Servon parissa, mutta perustuntemus kielestä on hyödyllinen alussa.
  5. Aseta editorisi niin, että se integroituu rust-analyzer-työkalun kanssa.
  6. Lue tyyliohjeemme ja tutustu odotuksiin tekemäsi koodin suhteen.
  7. Lue ja seuraa pull requestin tekemisen vaiheet.

Työskentely issuen parissa

Jos haluat työskennellä issuen parissa, jätä siihen kommentti ilmoittaaksesi, että otat sen työn alle. Näin vältetään päällekkäinen työ samassa issuessa.

Viesti “@servo-highfive assign me” osoittaa issuen sinulle.

Siirry Servo Starters -sivustolle löytääksesi hyviä tehtäviä aloittelijalle. Jos kohtaat outoja sanoja tai ammattislangia, tarkista ensin sanasto. Jos sopivaa merkintää ei ole, tee pull request ja lisää uusi merkintä sisällöllä TODO, jotta voimme korjata sen!

Tekoälyavustukset

Avustukset eivät saa sisältää suurten kielimallien tai muiden probabilististen työkalujen tuottamaa sisältöä, mukaan lukien mutta ei rajoittuen Copilotiin tai ChatGPT:hen. Tämä käytäntö kattaa koodin, dokumentaation, pull requestit, issuet, kommentit ja kaikki muut Servo-projektin avustukset.

Toistaiseksi suhtaudumme näihin työkaluihin varovaisesti niiden vaikutusten vuoksi — sekä tuntemattomien että havaittujen — projektin terveyteen ja ylläpitotaakkaan. Alue kehittyy nopeasti, joten olemme avoimia käytännön uudelleen arvioinnille myöhemmin, jos esitetään ehdotuksia työkaluista, jotka lieventävät näitä vaikutuksia. Perustelumme ovat seuraavat:

Ylläpitäjien taakka: Arvioijat luottavat siihen, että avustajat kirjoittavat ja testaavat koodinsa ennen lähettämistä. Olemme havainneet, että nämä työkalut helpottavat suurten määrien uskottavalta näyttävän koodin tuottamista, jota avustaja ei ymmärrä, jota usein ei ole testattu ja joka ei toimi oikein. Tämä kuormittaa arvioijiemme (jo valmiiksi rajallista) aikaa ja energiaa.

Oikeellisuus ja turvallisuus: Vaikka tekoälytyökalujen tuottama koodi näyttäisi toimivan, oikeellisuudesta ei ole takeita eikä merkkejä mahdollisista turvallisuusvaikutuksista. Selainmoottori on rakennettu toimimaan vihamielisissä suoritusympäristöissä, joten kaiken koodin on otettava huomioon mahdolliset turvallisuusongelmat. Avustajilla on suuri rooli näiden asioiden pohtimisessa avustuksia luodessaan — emme voi luottaa tekoälytyökalun tekevän tätä.

Tekijänoikeuskysymykset: Julkisesti saatavilla olevat mallit on koulutettu tekijänoikeudella suojatulla sisällöllä sekä vahingossa että tarkoituksella, ja niiden tuotos sisältää usein kyseistä sisältöä sanatarkasti. Koska tämän laillisuus on epävarmaa, avustukset voivat rikkoa tekijänoikeudella suojattujen teosten lisenssejä.

Eettiset kysymykset: Tekoälytyökalujen rakentaminen ja käyttö vaatii kohtuuttoman paljon energiaa ja vettä, niiden mallit rakennetaan raskaasti hyväksikäytetyillä työntekijöillä hyväksymättömissä työoloissa, ja niitä käytetään työvoiman heikentämiseen ja irtisanomisten oikeuttamiseen. Emme halua jatkaa näitä haittoja, edes epäsuorasti.

Huomaa, että koodin tai muiden avustusten tuottamisen lisäksi tekoälytyökalut voivat joskus vastata Servoa koskeviin kysymyksiisi, mutta olemme havainneet näiden vastausten olevan usein virheellisiä tai hyvin harhaanjohtavia.

Yleisesti ottaen älä oleta tekoälytyökalujen olevan totuuden lähde Servon toiminnasta. Harkitse kysymysten esittämistä Zulipissa sen sijaan.

Tekoälykäytännön FAQ

Voinko käyttää tekoälytyökaluja kääntäessäni äidinkielestäni englanniksi?

Kyllä.

Voinko käyttää tekoälytyökalua auttamaan bugien tai turvallisuusongelmien löytämisessä Servossa?

Kyllä, mutta sinun on varmennettava tekoälytyökalun tuotos ennen issuen avaamista Servo-projektia vastaan. Projektia vastaan avattujen issuen odotetaan noudattavan projektin tekoälykäytäntöä — sinun on pystyttävä toistamaan ja validoimaan havainnot, et pelkästään luottamaan työkalun tuotokseen.

Voinko käyttää tekoälytyökalua ymmärtääkseni Servon koodikantaa paremmin?

Kyllä, mutta katso aiempi varoitus näiden työkalujen luotettavuudesta.

Voinko lähettää pull requestin, joka sisältää tekoälytyökalujen tuottamaa koodia?

Et. Tämä kattaa (mutta ei rajoitu) kaikki generatiiviset tekoälytyökalut, kuten Clauden, Codexin, ChatGPT:n, Cursorin, Geminin ja Windsurfin.

Voinko käyttää tekoälytyökalua pull requestin tiivistämiseen?

Et. Tämä kattaa (mutta ei rajoitu) kaikki generatiiviset tekoälytyökalut, kuten Clauden, Codexin, ChatGPT:n, Cursorin, Geminin ja Windsurfin. Kuvaile pull requestisi noudattamalla projektin parhaita käytäntöjä.

Voinko käyttää tekoälyarvioijaa pull requestilleni?

Kyllä, mutta älä tee sitä. Tulokset ovat epäluotettavia ja meluisia.

Voinko käyttää tekoälytyökalua uuden issuen kuvauksen tai kommentin kirjoittamiseen?

Et. Tämä kattaa (mutta ei rajoitu) kaikki generatiiviset tekoälytyökalut, kuten Clauden, Codexin, ChatGPT:n, Cursorin, Geminin ja Windsurfin.

Mitä tapahtuu, jos rikon tätä tekoälykäytäntöä?

Servon ylläpitäjät pidättävät oikeuden sulkea minkä tahansa pull requestin, joka ei täytä tämän käytännön vaatimuksia. Kaikki kommentit, kuvaukset ja muut ei-koodiset artefaktit, joita aiemmat FAQ-kohdat eivät salli, on kirjoitettava uudelleen ilman tekoälytyökalujen käyttöä.

Käyttäytyminen

Servon käyttäytymissäännöt on julkaistu osoitteessa https://servo.org/coc/.

Hallinto

Servon hallinto määritellään Technical Steering Committee (TSC) -elimellä, ja se on dokumentoitu servo/project-repositoriossa.

Tehtävien löytäminen

Servon issue tracker sisältää paljon issueita, ja sopivan tehtävän etsiminen voi tuntua ylivoimaiselta. Tässä on hyödyllisiä vinkkejä issue-tunnisteista, joita kannattaa etsiä:

Vähemmän monimutkaiset issuet

E-less-complex -tunniste tarkoittaa, että joku katsoo issuen sopivan uudelle avustajalle. Näiden tunnisteiden issuen pitäisi sisältää selkeä kuvaus ongelmasta, selkeät vaiheet sen korjaamiseksi ja odotetut varmistusvaiheet.

Monimutkaisemmat issuet

E-more-complex -tunniste tarkoittaa, että joku katsoo issuen sopivan hieman kokemusta omaavalle. Kuten E-less-complex-issuet, myös näiden tunnisteiden issuen pitäisi sisältää selkeä kuvaus ongelmasta, selkeät vaiheet sen korjaamiseksi ja odotetut varmistusvaiheet. Ero kahden tunnisteen välillä on ratkaisuun odotettu vaadittu työmäärä, ja ne voivat vaatia lisätutkimusta.

Panicien korjaaminen minimoiduilla testitapauksilla

Servo voidaan saada panikoimaan monin tavoin, ja näitä löydetään usein fuzzaamalla Servoa. On paljon esimerkkejä issueista, joissa on minimoidut testitapaukset (eli pienin HTML/JS/CSS, jolla ongelma toistuu) ja jotka on merkitty tunnisteilla I-panic ja C-has-manual-testcase.

Nämä voivat olla hyviä issueita, koska panic-backtrace voi antaa vihjeitä relevantista koodista, ja voit keskittyä ymmärtämään panicin laukaisevat olosuhteet.

Testitapausten minimointi

On helppo avata issue siitä, että Servo renderöi verkkosivun väärin, mutta näihin issueihin on usein vaikea reagoida. On hyvin hyödyllistä etsiä issueita C-needs minimized testcase -tunnisteella ja työskennellä ongelman toistamiseen tarvittavan HTML/CSS/JS:n eristämiseksi. Katso opas vinkkejä ei-triviaalisten sivujen minimointiin.

Epäonnistuvien Web Platform Tests -testien diagnosointi

Web Platform Tests (WPT) ovat Servon pääasiallinen automatisoitujen yhteensopivuustestien lähde. On paljon testejä, joita Servo ei vielä läpäise, ja ne löytyvät testien metatietohakemistosta. Jokainen .ini-tiedosto kyseisessä hakemistossa vastaa testin lähdetiedostoa, ja voi olla hyödyllistä valita sinua kiinnostavasta hakemistosta tiedosto ja diagnosoida, mikä estää testin läpäisyn.

Voit myös tutkia kaikkia testejä, joita Servo epäonnistuu, verkkopohjaisessa wpt.fyi-käyttöliittymässä. Katso opas vinkkejä WPT-testien epäonnistumisten syiden tunnistamiseen.

Git-asetukset

Jos olet uusi Gitin tai hajautetun versionhallinnan parissa, on erittäin suositeltavaa käyttää aikaa perusteiden opetteluun ennen jatkamista. Verkossa on monia erinomaisia resursseja Gitin oppimiseen. Esimerkiksi Zulip-projekti ylläpitää hyvin kattavaa opasta Gitin käyttöön avoimen lähdekoodin projektiin avustamisessa. Tiedot ovat usein hyvin relevantteja myös Servon parissa työskentelyyn. Gitin hallinta on taito, josta on hyötyä monenlaisissa ohjelmistokehitystehtävissä, ja siihen käytetty aika on sen arvoista.

Kun kloonasit Servon alun perin, upstream-Servo-repositorio osoitteessa https://github.com/servo/servo oli upstreamisi. Voit pitää tuon konfiguraation, mutta suositeltu työnkulku on seuraava:

  1. Forkkaa upstream-Servo-repositorio.
  2. Checkoutaa klooni juuri forkkaamastasi Servo-kopiosta.
    git clone --depth 10 https://github.com/<username>/servo.git
    
    Huomaa, että --depth 10 -argumentit hylkäävät suurimman osan Servon commit-historiasta paremman suorituskyvyn vuoksi. Ne voidaan jättää pois.
  3. Lisää uusi remote nimeltä upstream, joka osoittaa servo/servo-repoon.
    git remote add upstream https://github.com/servo/servo.git
    

Uuden muutoksen aloittaminen

Kun haluat työskennellä uuden muutoksen parissa, älä tee sitä main-haarassa, koska siellä haluat pitää upstream-repositorion kopion ajan tasalla. Tee työsi sen sijaan haarassa.

  1. Päivitä main-haarasi upstreamin uusimpiin muutoksiin.
    git checkout main
    git pull origin main
    
  2. Luo uusi haara main-haaran pohjalta.
    git checkout -b issue-12345
    
  3. Tee muutoksesi ja commitoi ne kyseiselle haaralle. Muista myös allekirjoittaa jokainen commit!
    git commit --signoff -m "script: Add a stub interface for MessagePort"
    

Seuraavaksi haluat todennäköisesti tehdä pull requestin muutoksillasi.

Editorin asetukset

On erittäin suositeltavaa asettaa editorisi tukemaan rust-analyzer-työkalua. Vaikka se vaatii kohtuullisen määrän RAM-muistia ja CPU:ta, se parantaa Servon parissa työskentelyä merkittävästi, koska editorisi voi tarjota koodin täydentämistä, reaaliaikaisia käännösvirheitä ja varoituksia sekä helposti saatavilla olevan rustdoc-dokumentaation. Valitettavasti rust-analyzer yrittää ajaa cargo-komentoa ilman mach-komentoa, mikä aiheuttaa ongelmia Servon erityisen käännöskonfiguraation vuoksi. Käytä seuraavia ohjeita IDE:n konfigurointiin. Lisäykset ovat tervetulleita!

Visual Studio Code

On suositeltavaa lisätä seuraava projektikohtaisiin asetuksiin tiedostoon .vscode/settings.json:

{
    "rust-analyzer.rustfmt.overrideCommand": [ "./mach", "fmt" ],
    "rust-analyzer.check.overrideCommand": [
        "./mach",
        "clippy",
        "--message-format=json",
        "--target-dir",
        "target/lsp",
        "--features",
        "tracing,tracing-perfetto"
    ],
    "rust-analyzer.cargo.buildScripts.overrideCommand": [
        "./mach",
        "clippy",
        "--message-format=json",
        "--target-dir",
        "target/lsp",
        "--features",
        "tracing,tracing-perfetto"    ],
}

Huomioita:

  • Yllä olevassa katkelmassa kielipalvelin kääntää omaan target-hakemistoonsa target/lsp, jotta vältetään ei-toivotut uudelleenkäännökset. Jos haluat säästää levytilaa, voit poistaa --target-dir- ja target/lsp-argumentit, jolloin käytetään oletustarget-hakemistoa.
  • Ota valinnaiset käännösasetukset käyttöön lisäämällä ne konfiguraatiotiedoston build-argumenttilistaan.
  • Windows: Windowsissa sinun on käytettävä ./mach.bat-komentoa ./mach-komennon sijaan. Muuten saatat saada virheen, että suoritettu komento ei ole kelvollinen Win32-sovellus.
  • Ristikäännös: Jos ristikäännät, voit ohittaa cargo-käytettävän targetin lisäämällä "rust-analyzer.cargo.target": "aarch64-linux-android". Huomaa, että jotkin LSP-ominaisuudet eivät välttämättä toimi tässä konfiguraatiossa.

Zed

Jos käytät Zedia, sinun on tehtävä hyvin samanlaista kuin Visual Studio Code -kohdassa kuvataan, mutta Zed-konfiguraatiotiedosto odottaa hieman erilaista syntaksia. Tiedostossa ./zed/settings.json tarvitset jotain tällaista:

{
  "lsp": {
    "rust-analyzer": {
      "initialization_options": {
        "checkOnSave": true,
        "check": {
          "overrideCommand": [
            "./mach",
            "clippy",
            "--message-format=json",
            "--target-dir",
            "target/lsp",
            "--feature",
            "tracing,tracing-perfetto"
          ]
        },
        "cargo": {
          "allTargets": false,
          "buildScripts": {
            "overrideCommand": [
              "./mach",
              "clippy",
              "--message-format=json",
              "--target-dir",
              "target/lsp",
              "--feature",
              "tracing,tracing-perfetto"
            ]
          }
        },
        "rustfmt": {
          "extraArgs": [
            "--config",
            "unstable_features=true",
            "--config",
            "binop_separator=Back",
            "--config",
            "imports_granularity=Module",
            "--config",
            "group_imports=StdExternalCrate"
          ]
        }
      }
    }
  }
}

Python

Servo sisältää Python-skriptejä osana build-työkaluja ja tiettyjä testityyppejä. Zedin oletus-Python-konfiguraatio ei välttämättä tarjoa kunnollista staattista koodianalyysia.

Servo on konfiguroitu toimimaan Pyrefly-kielipalvelimen kanssa, jonka voit ottaa käyttöön alla olevan kaltaisella konfiguraatiolla. Tarvitset myös Pyrefly Zed-laajennuksen.

{
  "lsp": {
    "pyrefly": {
      "binary": {
        "path": ".venv/bin/pyrefly",
        "arguments": ["lsp"],
      },
      "settings": {
        "python": {
          "pythonPath": ".venv/bin/python",
        },
        "pyrefly": {
          "python_interpreter": ".venv/bin/python",
        },
      },
    },
  },
  "languages": {
    "Python": {
      "language_servers": ["pyrefly", "!pyright", "!pylsp"],
    },
  },
}

NixOS

NixOS:ssa saatat saada virheitä pkg-config- tai crown-paketeista:

thread ‘main’ panicked at ’called `Result::unwrap()` on an `Err` value: “Could not run `PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=\“1\” PKG_CONFIG_ALLOW_SYSTEM_LIBS=\“1\” \“pkg-config\” \“–libs\” \“–cflags\” \“fontconfig\”`

[ERROR rust_analyzer::main_loop] FetchWorkspaceError: rust-analyzer failed to load workspace: Failed to load the project at /path/to/servo/Cargo.toml: Failed to read Cargo metadata from Cargo.toml file /path/to/servo/Cargo.toml, Some(Version { major: 1, minor: 74, patch: 1 }): Failed to run `cd “/path/to/servo” && “cargo” “metadata” “–format-version” “1” “–manifest-path” “/path/to/servo/Cargo.toml” “–filter-platform” “x86_64-unknown-linux-gnu”`: `cargo metadata` exited with an error: error: could not execute process `crown -vV` (never executed)

mach välittää eri RUSTFLAGS-arvot Rust-kääntäjälle kuin pelkkä cargo, joten jos yrität kääntää Servon cargo-komennolla, se kumoaa mach-komennon työn (ja päinvastoin). Tämän vuoksi, ja koska Servo voidaan tällä hetkellä kääntää vain mach-komennolla, sinun on konfiguroitava rust-analyzer-laajennus käyttämään mach-komentoa tiedostossa .vscode/settings.json:

crown-käyttö

Jos käytät --use-crown-valitsinta, sinun pitäisi myös asettaa CARGO_BUILD_RUSTC tiedostossa .vscode/settings.json seuraavasti, jossa /nix/store/.../crown on komennon nix-shell --run 'command -v crown' tulos.

{
    "rust-analyzer.server.extraEnv": {
        "CARGO_BUILD_RUSTC": "/nix/store/.../crown",
    },
}

Näiden asetusten pitäisi riittää siihen, ettei sinun tarvitse ajaa code . nix-shell-ympäristöstä, mutta voit kokeilla sitä, jos ongelmia on yhä.

Ongelmia proc-makrojen kanssa

Kun otat rust-analyzerin proc-makrotuen käyttöön, saatat alkaa nähdä virheitä kuten

proc macro `MallocSizeOf` not expanded: Cannot create expander for /path/to/servo/target/debug/deps/libfoo-0781e5a02b945749.so: unsupported ABI `rustc 1.69.0-nightly (dc1d9d50f 2023-01-31)` rust-analyzer(unresolved-proc-macro)

Tämä tarkoittaa, että rust-analyzer käyttää väärää proc-makropalvelinta, ja sinun on konfiguroitava oikea manuaalisesti. Käytä mach-komentoa kysyäksesi nykyisen sysroot-polun ja kopioi tulosteen viimeinen rivi:

$ ./mach rustc --print sysroot
NOTE: Entering nix-shell /path/to/servo/shell.nix
info: component 'llvm-tools' for target 'x86_64-unknown-linux-gnu' is up to date
/home/me/.rustup/toolchains/nightly-2023-02-01-x86_64-unknown-linux-gnu

Konfiguroi sitten joko sysroot-polku tai proc-makropalvelimen polku tiedostossa .vscode/settings.json:

{
    "rust-analyzer.procMacro.enable": true,
    "rust-analyzer.cargo.sysroot": "[paste what you copied]",
    "rust-analyzer.procMacro.server": "[paste what you copied]/libexec/rust-analyzer-proc-macro-srv",
}

Emacs

Emacsissa on kaksi LSP-asiakasimplementaatiota: eglot, joka on Emacsin sisäänrakennettu paketti, ja emacs-lsp.

Eglot

Komentojen ohittamiseksi meidän on asetettava eglot-workspace-configuration. Luo tätä varten .dir-locals.el-tiedosto Servo-checkoutisi ylätason hakemistoon seuraavalla sisällöllä:

;;; Directory Local Variables         -*- no-byte-compile: t; -*-
;;; For more information see (info "(emacs) Directory Variables")

((nil . ((eglot-workspace-configuration
          . (:rust-analyzer
             (:rustfmt (:overrideCommand ["./mach" "fmt"])
                       :check (:overrideCommand
                               ["./mach" "clippy" "--message-format=json" "--target-dir" "target/lsp" "--features" "tracing,tracing-perfetto"])
                       :cargo (:buildScripts
                               (:overrideCommand
                                ["./mach" "clippy" "--message-format=json" "--target-dir" "target/lsp" "--features" "tracing,tracing-perfetto"]))))))))

Kun olet käynnistänyt eglotin (M-x eglot), voit tarkistaa käytössä olevan workspace-konfiguraation komennolla M-x eglot-show-workspace-configuration.

Tyyliohje

Suurin osa tyylisuosituksistamme pakotetaan automaattisesti lintereillämme. Tässä dokumentissa on ohjeita, joita on vaikeampi lintata.

Paljon Servon koodia kirjoitettiin ennen kuin nämä suositukset hyväksyttiin. Tervetulleita ovat pull requestit, jotka tuovat projektin koodin ajan tasalle nykyaikaisten ohjeiden mukaiseksi.

Rust

Yleisesti ottaen kaikki Servon Rust-koodi formatoidaan automaattisesti rustfmt-työkalulla, kun ajat ./mach fmt, mikä pitäisi kohdistaa koodisi viralliseen Rust Style Guide -oppaaseen. Lisäksi Rust-koodin pitäisi noudattaa Rust API naming conventions -käytäntöjä. Nimeämiskäytäntöoppaassa on muutamia epäilmeisiä kohtia, kuten:

  • get_-etuliitettä ei yleensä käytetä gettereissä Rust-koodissa. Poikkeus tähän sääntöön on, kun sitä käytetään get()-metodin variantille kuten std::cell::Cell::get_mut().
  • Camel case -kirjoituksessa lyhenteet ja yhdys-sanojen supistukset lasketaan yhdeksi sanaksi. Esimerkiksi structin pitäisi olla nimeltään HtmlParser, ei HTMLParser.

Sisennys

Monimutkaisten funktioiden ja metodien kulun seuraamisen helpottamiseksi pyrimme minimoimaan sisennystä käyttämällä varhaisia return-lauseita. Tämä vastaa myös monissa tapauksissa spesifikaatioissa käytettyä kieltä. Kun funktion logiikka saavuttaa 2 tai 3 sisennystasoa, tai kun lyhyt ehtolohko on poikkeustapaus funktion lopussa, harkitse varhaisen return-lauseen käyttöä. Varhaiset return-lauseet toimivat hyvin yhdistettynä Option- tai enum-varianttien purkamiseen. Suosi seuraavaa syntaksia, kun palaat varhaisesti, jos Option-arvo on None:

#![allow(unused)]
fn main() {
let Some(inner_value) = option_value else {
    return
};
}

Tämä toimii myös enum-varianttien kanssa:

#![allow(unused)]
fn main() {
enum Pet {
    Dog(usize),
    Cat(usize),
}

let pet = Pet::Dog(10);
let Pet::Dog(age) = pet else {
   return;
};
}

Lyhenteet

Servo noudattaa Google C++ -oppaan nimeämissääntöjä. Vältä lyhenteitä, joita joku projektin ulkopuolella ei tuntisi. Älä lyhennä poistamalla kirjaimia sanojen keskeltä.

Poikkeus:: Voit käyttää joitain yleisesti tunnettuja lyhenteitä, kuten i silmukkaindeksille. Voit myös käyttää yksittäisiä kirjaimia, kuten T Rust-tyyppiparametreille.

Enum-variantit

Luettavuuden vuoksi vältä enum-varianttien suoraa use-tuontia. Viittaa sen sijaan enumeihin täydellisellä nimellä (eli Enum::Variant). Nimeäristiriitojen välttämisen lisäksi tämä auttaa koodia tuntemattomia lukemaan helpommin koodissa käytetyn tyypin.

Kuollut koodi

Lähes kaikissa tapauksissa älä commitoi kuollutta koodia tai kommentoitua koodia repositorioon. Kommentoitu koodi voi bit rot -ilmiön vuoksi rappeutua helposti, eikä kuollutta koodia testata. Kun koodista tulee kuollutta, koska sitä ei käytetä lainkaan, se pitäisi poistaa.

Poikkeus: Poikkeus on, kun koodi on kuollutta vain joissakin käännöskonfiguraatioissa. Siinä tapauksessa käytät expect(dead_code) -kääntäjädirektiiviä konfiguraatioqualifierin kanssa. Esimerkiksi:

#![allow(unused)]
fn main() {
#[cfg_attr(any(target_os = "android", target_env = "ohos"), expect(dead_code))]
pub(crate) const LINE_HEIGHT: f32 = 76.0;
}

Tässä tapauksessa LINE_HEIGHT-vakio käännetyään, mutta sen odotetaan olevan kuollutta Android- tai OpenHarmony-käännöksessä.

unsafe-koodi

Pyri välttämään unsafe-koodia. Valitettavasti selainmoottori on monimutkainen, joten jonkin verran unsafe-koodia on Servossa väistämätöntä. Jos joudut tekemään unsafe-funktion, käytä Safety comments -kommentteja, jotka selittävät, miksi lohko on turvallinen ja mitkä turvallisuusinvariantit ovat voimassa. Käytä harkintaa siitä, milloin lisätä safety-kommentteja funktioiden sisällä oleviin unsafe-lohkoihin.

Assertiot

Kun Servon sisäisen logiikan pitäisi tehdä tietystä ehdosta mahdoton, käytä assert!- tai debug_assert!-lausetta varmistaaksesi sen. Ajattele assert!-lausetta sekä eräänlaisena testinä että dokumentaationa. Jos assertiossa ilmaistu invariantti joskus muuttuu epätodeksi, Servo voi alkaa kaatua testejä ajaessa, mikä estää logiikkavirheiden pääsyn koodiin. Lisäksi koodia lukevat voivat tietää vahvemmin kuin kommentti sallii, mitä kirjoittaja oletti todeksi tietyssä kohdassa. Jos tietty koodin osa on saavuttamaton, esimerkiksi jos enum-variantti on käsitelty aiemmin eikä sitä pitäisi kohdata myöhemmin samassa funktiossa, käytä unreachable!(), mutta täytä teksti aina sillä, miksi koodi on saavuttamaton.

Option::map ja Result::map/Result::map_err

map-API:ta pitäisi käyttää vain yhden tyypin muuntamiseen toiseen, ei ohjausvirran muodossa. Suosi match-, if let- tai let/else-rakenteita, kun kirjoitat koodia, joka vaikuttaa vain tiettyyn varianttiin.

unwrap() ja expect()

unwrap-kutsua Option- tai Result-tyypille ei pitäisi lähes koskaan käyttää. Käsittele sen sijaan None- tai Err-tapaus ja tee tarvittava virheenkäsittely. Servon ei pitäisi kaatua, kun se on mahdollista. Jos None tai Err on mahdoton Servon sisäisen logiikan vuoksi joka ei liity ulkoiseen syötteeseen tai crateihin, voit käyttää expect()-kutsua kuten assertiota. expect()-kutsulle annetun tekstin pitäisi ilmaista, miksi arvo ei voi olla None tai Err.

Poikkeus:: Kun käsitellään Rustin std::sync::Mutex-tyyppiä tai muita samanaikaisuusprimitiivejä, jotka käyttävät poisoning-mekanismia, unwrap-kutsu on sopiva.

todo!() ja unimplemented!()

Saavutettavassa koodissa älä käytä todo!- tai unimplemented!-makroja. Nämä makrot saavat Servon panikoimaan, eikä normaalin verkkosisällön pitäisi saada Servoa panikoimaan. Sen sijaan pyri tekemään tällaiset tapaukset saavuttamattomiksi ja palauttamaan asianmukaisia virhearvoja tai yksinkertaisesti jättämään koodin tekemättä mitään.

Makrot

Makrot peittävät toteutustiedot, ja kutsukohdat ovat usein vaikeampia lukea kuin inline Rust-koodi. Kun mahdollista, suosi geneerisiä/parametrisoituja funktioita macro_rules!-makrojen sijaan. Ilmoitetut makrot pitäisi yrittää näyttää mahdollisimman paljon inline-kelpoiselta Rust-koodilta.

Poikkeus: Kun on yleisiä ohjausvirran kuvioita (esim. virheen tai välimuistissa olevan arvon tarkistus ja varhainen paluu), joita ei voi toistaa Result/Option-tyypeillä ja ?-operaattorilla, makrot ovat yksi tapa vähentää toistuvaa boilerplate-koodia.

Poikkeus: Kun monta uniikkia tyyppiä pitää ilmoittaa identtisellä kuviolla, makrot ovat yksi tapa vähentää boilerplate-koodia.

Shell-skriptit

Shell-skriptit sopivat pieniin tehtäviin tai wrappereihin, mutta on parempi käyttää Pythonia kaikkeen, jossa on vähänkin monimutkaisuutta tai yleisesti ottaen.

Shell-skriptit kirjoitetaan bashilla, alkaen tällä shebangilla:

#!/usr/bin/env bash

Huomaa, että macOS:n oletusbash on melko vanha, joten ole varovainen uusien ominaisuuksien kanssa.

Skriptien alussa pitäisi ottaa käyttöön muutama valitsin vankkuuden vuoksi:

set -o errexit
set -o nounset
set -o pipefail

Muista lainata kaikki muuttujat täydellisessä muodossa: "${SOME_VARIABLE}".

Käytä "$(some-command)" backtickien sijaan komentokorvauksessa. Huomaa, että nämäkin pitäisi lainata.

Servo Book

  • Käytä permalinkkejä linkatessasi lähdekoodirepoihin — paina Y GitHubissa saadaksesi pysyvän URL-osoitteen

Markdown-lähde

  • Käytä yhtä lausetta per rivi ilman sarakeleveyttä, jotta diffit ja historia ovat helpompia ymmärtää

Lauserivien jakamiseen voit korvata ([.!?]) $1\n, mutta varo tapauksia kuten “e.g.”. Yksinkertaisten listojen sisennysten korjaamiseen voit korvata ^([*-] .*\n([* -].*\n)*)([^\n* -])$1 $3, mutta tämä ei toimi sisäkkäisille tai monimutkaisemmille listoille.

  • Johdonmukaisuuden vuoksi sisennä sisäkkäiset listat kahdella välilyönnillä ja käytä --merkkiä järjestämättömissä listoissa

Notaatio

  • Käytä lihavoitua tekstiä viitattaessa käyttöliittymäelementteihin kuten valikkovalintoihin, esim. “click Inspect
  • Käytä backtick-merkkejä viitattaessa yksikirjaimisiin näppäimiin, esim. “press A or Ctrl+A

Virheilmoitukset

  • Kun mahdollista, sisällytä aina linkki dokumentaatioon, Zulip-chatiin tai lähdekoodiin — tämä auttaa säilyttämään alkuperäisen kontekstin ja auttaa meitä tarkistamaan ja päivittämään neuvoamme ajan myötä

Loput virheilmoitusten säännöt on suunniteltu varmistamaan, että teksti on mahdollisimman luettavaa ja että lukija voi liittää virheilmoituksensa find-in-page -hakuun mahdollisimman vähällä väärillä osumilla, ilman että säännöt ovat liian hankalia noudattaa.

Kääri virheilmoitus <pre><samp>-elementteihin, <pre> rivin alussa (ei sisennettynä). Jos haluat tyylitellä virheilmoituksen lainauksena, kääri se <pre><blockquote><samp>-elementteihin.

<pre> käsittelee rivinvaihdot rivinvaihdoiksi, ja rivin alussa se estää Markdown-syntaksin vahingossa aktivoitumisen, kun virheilmoituksessa on tyhjiä rivejä.

<samp> merkitsee tekstin tietokoneen tuotokseksi, jossa meillä on CSS, joka saa sen rivittymään kuten terminaalissa. Koodilohkot (<pre><code>) eivät rivity, joten ne voivat tehdä pitkistä virheistä vaikeasti luettavia.

Korvaa jokainen & merkillä &amp;, sitten jokainen < merkillä &lt;. Teksti <pre>-elementin sisällä ei koskaan käsitellä Markdownina, mutta se on silti HTML-merkkausta, joten se on escapattava.

Tarkista aina renderöity tulos varmistaaksesi, että kaikki symbolit säilyivät. Saatat huomata, että sinun on yhä escapattava osaa Markdownista \-merkillä, jotta called `Result::unwrap()` on an `Err` value ei renderöidy muodossa called Result::unwrap() on an Err value.

Virheilmoitus Markdown
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: "Could not run `PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=\"1\" PKG_CONFIG_ALLOW_SYSTEM_LIBS=\"1\" \"pkg-config\" \"--libs\" \"--cflags\" \"fontconfig\"`
<pre><samp>thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: "Could not run `PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=\"1\" PKG_CONFIG_ALLOW_SYSTEM_LIBS=\"1\" \"pkg-config\" \"--libs\" \"--cflags\" \"fontconfig\"`</samp></pre>
error[E0765]: ...
 --> src/main.rs:2:14
  |
2 |       println!("```);
  |  ______________^
3 | | }
  | |__^
<pre><samp>error[E0765]: ...
 --> src/main.rs:2:14
  |
2 |       println!("```);
  |  ______________^
3 | | }
  | |__^</samp></pre>

Pull requestin tekeminen

Avustukset Servoon tai sen riippuvuuksiin tehdään GitHub pull request -muodossa. Jokainen pull request arvioi ydinavustaja (henkilö, jolla on oikeus landata patcheja), ja se joko landataan pääpuuhun tai siihen annetaan palautetta vaadituista muutoksista. Kaikkien avustusten pitäisi noudattaa tätä muotoa, myös ydinavustajien.

Pull request -tarkistuslista

  • Haaroita main-haarasta ja tarvittaessa rebasaa haarasi main-haaraan ennen pull requestin lähettämistä. Jos se ei yhdisty siististi main-haaraan, sinua voidaan pyytää rebasaamaan muutoksesi.

  • Aja ./mach fmt ja ./mach test-tidy muutoksellesi.

  • Commitit pitäisi olla mahdollisimman pieniä, mutta niin, että jokainen commit on itsenäisesti oikea (eli jokaisen commitin pitäisi kääntyä ja läpäistä testit).

  • Commiteihin liittyy Developer Certificate of Origin -allekirjoitus, joka osoittaa, että sinä (ja tarvittaessa työnantajasi) sitoudutte projektin lisenssin ehtoihin. Gitissä tämä on git commit-komennon -s-valitsin.

  • Jos patchiasi ei arvioida, tai tarvitset tietyn henkilön arvioivan sen, voit @-vastata arvioijalle pyytäen arviointia pull requestissa tai kommentissa, tai pyytää arviointia Servo-chatin kautta.

  • Lisää testit, jotka liittyvät korjattuun bugiin tai uuteen ominaisuuteen. DOM-muutoksessa tämä on yleensä web platform test; asettelussa reftest. Katso testausopas lisätietoja varten.

Pull requestin avaaminen

Ensin pushaa haarasi origin-remoteen. Seuraavaksi avaa joko onnistuneen pushin palauttama URL aloittaaksesi pull requestin, tai vieraile Servo-forkissasi selaimessa ja seuraa käyttöliittymän ohjeita.

jdm@pathfinder servo % git push origin issue-12345
Enumerating objects: 43, done.
Counting objects: 100% (43/43), done.
Delta compression using up to 12 threads
Compressing objects: 100% (29/29), done.
Writing objects: 100% (29/29), 5.13 KiB | 5.13 MiB/s, done.
Total 29 (delta 25), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (25/25), completed with 14 local objects.
remote:
remote: Create a pull request for 'issue-12345' on GitHub by visiting:
remote:      https://github.com/jdm/servo/pull/new/issue-12345
remote:

Kuvakaappaus GitHub-käyttöliittymän painikkeesta, jossa lukee “Compare & pull request”

Otsikko ja kuvaus

Servossa pull requestien commitit squashataan, ja pull requestin otsikko ja kuvaus käytetään lopullisen commit-viestin luomiseen (esimerkki). On tärkeää, että sekä otsikko että kuvaus kuvaavat muutoksen täysin, koska se tekee commitista hyödyllisen repositorion historian tarkastelijoille ja tekee Servosta terveemmän projektin kokonaisuutena.

Otsikon pitäisi tiiviisti kuvata, mitä muutos tekee. Muutamia vinkkejä:

  • Etuliite otsikossa on sen craten nimi pienillä kirjaimilla, jota työstät. Esimerkiksi jos muutos koskee “script”-cratea, etuliite on “script: “. Jos muutos vaikuttaa useisiin crateihin, tunnista, mikä crate on muutoksen ensisijainen “lähde”, tai jätä etuliite pois.
  • Otsikot kirjoitetaan imperatiivisena (pyyntö- tai käskylauseena) verbin kanssa. Esimerkiksi “layout: Skip box tree construction when possible.” Vältä geneerisiä verbejä kuten “fix”, “correct” tai “improve” ja kuvaile sen sijaan, mitä korjaus tekee. Koodia voidaan korjata useita kertoja, mutta viestin pitäisi tunnistaa muutos yksilöllisemmin.
  • Etuliitteet kirjoitetaan pienillä kirjaimilla, ja otsikon lopun osan pitäisi capitalisoida vain ensimmäinen sana ja erisnimet (kuten tietorakenteiden nimet tai spesifikaatiokäsitteet kuten WebDriver). Epävarmuuden vallitessa seuraa spesifikaatiossa kirjoitettua. Älä käytä “Title Casing for Commit Messages” -tyyliä.

Commit-kuvauksen pitäisi:

  • Kuvata alkuperäinen ongelma tai tilanne (linkaten tarvittaessa avoimiin bugeihin).
  • Kuvata, miten muutos korjaa ongelman, parantaa koodia tai valmistelee seuraavaa muutosta.
  • Selittää muutoksen varaukset, kuten uudet epäonnistuvat testit, suorituskyvyn heikkeneminen tai paljastumattomat reunatapaukset. Keskustele siitä, miten nämä voidaan käsitellä tulevaisuudessa.
  • Olla kirjoitettu usean lauseen kappaleina joko yhtenäisellä rivityksellä (80 merkkiä tai vähemmän) tai ilman rivitystä (GitHub tekee tämän automaattisesti).

Oletuspull request -malline sisältää useita kehotteita; täytä ne korvaamalla alkuperäiset ohjeet.

“Testing”-kehotus on erityisen tärkeä, koska se helpottaa arviointiprosessia. Se kysyy:

  • Onko muuttuvaa koodia jo kattavia automatisoituja testejä?
  • Jos ei, sisältääkö pull request uusia automatisoituja testejä?
  • Jos ei, mikä estää vähintään yhden uuden testin lisäämisen?

Jos et tiedä vastausta, kirjoita se ylös! Sen voi aina kirjoittaa uudelleen arvioijan palautteen perusteella.

Arviointikommenttien käsittely

Lisää uusia commiteja haaraan mieluummin kuin muokkaat olemassa olevia commiteja. Tämä helpottaa Servon arvioijien tarkastella vain muutoksia, mikä nopeuttaa pull requestien arviointia.

git commit -s -m "script: Fix deadlock with cross-origin iframes."
git push origin issue-12345

Yhdistämisristiriitojen käsittely

Kun pull requestissa on yhdistämisristiriitoja, yleisimmät tavat käsitellä ne ovat merge ja rebase. Älä paina pull requestin “Update branch” -painiketta; se suorittaa mergen ja estää osan Servon CI-toiminnallisuudesta toimimasta oikein.

Kuvakaappaus GitHub-käyttöliittymän painikkeesta, jossa lukee “Update branch”

Sen sijaan päivitä ensin paikallinen main-haarasi, rebasaa sitten feature-haarasi main-haaran päälle ja force pushaa muutokset.

git checkout issue-12345
git pull --rebase upstream main
git push -f origin issue-12345

Kun rebase kohtaa ristiriitoja, sinun on käsiteltävä ne:

jdm@pathfinder servo % git rebase main issue-12345
Auto-merging components/script/dom/bindings/root.rs
CONFLICT (content): Merge conflict in components/script/dom/bindings/root.rs
Auto-merging components/script/dom/node.rs
error: could not apply 932c8d3e97d... script: Remove unused field from nodes.
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".

Seuraa GitHubin dokumentaatiota ristiriitojen ratkaisemisesta.

Testien ajaminen pull requesteissa

Kun pushaat pull requestiin, GitHub tarkistaa automaattisesti, ettei muutoksissasi ole käännös-, lint- tai tidy-virheitä.

Ajaaksesi yksikkötestejä tai Web Platform Tests -testejä pull requestia vastaan, lisää yksi tai useampi alla olevista tunnisteista pull requestiisi. Jos sinulla ei ole oikeutta lisätä tunnisteita pull requestiisi, lisää kommentti bugiin pyytäen niiden lisäämistä.

TunnisteAjaa yksikkötestitAjaa web-testit
T-fullKaikilla alustoillaLinux
T-linux-wptLinuxLinux
T-macosmacOS(ei mitään)
T-windowsWindows(ei mitään)

DevTools-testit ovat kokeellisia eivätkä ole vielä oletuksena käytössä. On erittäin suositeltavaa, että pull requestit, jotka muokkaavat DevTools-koodia, ajavat ne. Lisää tätä varten T-linux-devtools-tunniste. Kuten Web Platform Tests -testeissä, jos sinulla ei ole oikeutta lisätä tunnisteita, pyydä kommentilla niiden lisäämistä.

Testaus

Tämä on tylsää. Mutta PR:ääsi ei hyväksytä ilman testiä.

Servossa on kolmea testityyppiä.

  • Koodin formatointitestit (./mach test-tidy).
  • Yksikkötestit (./mach test-unit).
  • Integraatiotestit (./mach test-wpt).

Tämän dokumentin painopiste on integraatiotesteissä, mutta haluamme mainita muut testit ensin.

Jokaisen lähetyksen on täytettävä koodin formatointitestit. Suurin osa formatoinnista voidaan tehdä automaattisesti komennolla ./mach fmt.

Yksikkötestit ovat eri tiedostoissa koko koodikannassa tyypillisillä rust #[cfg(test)]- ja #[test]-annotaatioilla. Yksikkötestit ajetaan komennolla ./mach test-unit. Esimerkkikutsuja:

# Run all unit tests in the net crate
./mach test-unit -p servo-net
# Run a specific unit test in the net crate
./mach test-unit -p servo-net test_fetch_response_is_not_network_error

Koko testisarjan ajaminen voi olla hyvin muistia kuluttavaa; voit lieventää tätä käyttäytymistä rajoittamalla käännettävien cratejen määrää komennolla ./mach test-unit -j 4.

Integraatiotestit

Testit sijaitsevat tests-hakemistossa. Huomaat, että siellä on paljon tiedostoja, joten oikean sijainnin löytäminen testillesi ei aina ole ilmeistä.

Katso ensin “Testing”-osio komennossa ./mach --help ymmärtääksesi eri testikategoriat. Löydät myös joitain update-*-komentoja. Niitä käytetään odotettujen tulosten listan päivittämiseen.

Testin ajaminen:

./mach test-wpt tests/wpt/yourtest

Uuden testin lisääminen

Jos sinun on luotava uusi testitiedosto, sen pitäisi sijaita hakemistossa tests/wpt/mozilla/tests tai hakemistossa tests/wpt/tests, jos se ei riipu vain Servo-ominaisuuksista. Sinun on sitten päivitettävä testien lista ja odotettujen tulosten lista:

./mach test-wpt --manifest-update

Testin debuggaus

Katso debuggausopas aloittaaksesi Servon debuggauksen.

Web Platform Tests (tests/wpt)

Tämä kansio sisältää Web Platform Tests -testit ja koodin, jota tarvitaan niiden integroimiseen Servoon. Lisäksi on WebGPU- ja WebGL-testejä, jotka on tuotu ja muunnettu Web Platform Test -tyylisiksi testeiksi.

tests/wpt-hakemiston sisältö

Erityisesti tämä kansio sisältää:

  • config.ini: konfiguraatio Web Platform Tests -testeille
  • include.ini: Web Platform Tests -testien osajoukko, jota ajamme tällä hetkellä
  • tests: Web Platform Tests -testien kopio repossa
  • meta: odotetut epäonnistumiset ajamillemme Web Platform Tests -testeille
  • mozilla: Web Platform Test -tyylisiä testejä, joita ei voi upstreamata
  • webgl: tuodut WebGL-testit
  • webgpu: tuodut WebGPU-testit (katso WebGPU -luku lisätietoja varten)

Web Platform Tests -testien ajaminen

Yksinkertaisin tapa ajaa Web Platform Tests -testit Servossa on ./mach test-wpt juurihakemistossa. Tämä ajaa include.ini-tiedostossa määritellyn JavaScript-testien osajoukon ja kirjaa tulosteen stdoutiin.

Testien osajoukkoa voi ajaa antamalla positionaalisia argumentteja mach-komennolle joko tiedostojärjestelmäpolkuina tai testi-URL:ina, esim.

./mach test-wpt tests/wpt/tests/dom/historical.html

ajaaksesi testin dom/historical.html, tai

./mach test-wpt dom

ajaaksesi kaikki DOM-testit.

Test harness hyväksyy myös suuren määrän komentorivivalitsimia; ne on dokumentoitu ajamalla --help.

WPT-testien ajaminen debug-käännöksellä johtaa usein timeouteihin. Sen sijaan harkitse kääntämistä komennolla mach build -r ja testaamista komennolla mach test-wpt -r.

Web Platform Tests -testien ajaminen GitHub-forkissasi

Vaihtoehtoisesti voit suorittaa testit GitHubin isännöimillä runnereilla komennolla mach try. Yleensä mach try linux-wpt (kaikki testit, linux) riittää.

Voit tarkastella ajotuloksia forkissasi “Actions”-välilehdeltä. Epäonnistuneet tehtävät sisältävät listan vakaista odottamattomista tuloksista lokin lopussa. Odottamattomat tulokset, jotka ovat tunnetusti intermittentejä, voidaan todennäköisesti jättää huomiotta.

Kun avaat PR:n, voit sisällyttää linkin ajoon. Muuten arvioijat ajavat testit uudelleen.

Web Platform Test -odotusten päivittäminen

Kun korjaat bugin, joka muuttaa testin tulosta, kyseisen testin odotetut tulokset on muutettava. Tämä voidaan tehdä manuaalisesti muokkaamalla .ini-tiedostoa meta-kansiossa, joka vastaa testiä. Poista tässä tapauksessa viittaukset testeihin, joiden odotus on nyt PASS, ja poista .ini-tiedostot, jotka eivät enää sisällä odotuksia.

Kun tarvitaan suurempi määrä muutoksia, prosessi voidaan automatisoida:

./mach test-wpt --update-expectations path/to/tests/

Voit myös päivittää testiodotukset CI:llä suoritetuista Try-ajoista. Jokainen CI-ajo tallentaa automaattisesti .log-tiedoston, jonka voit antaa update-wpt-komennolle:

./mach update-wpt https://github.com/servo/servo/actions/runs/<ID-OF-CI-RUN>

Kaikkien Web Platform Tests -testien ajaminen paikallisesti kestää kauan ja aiheuttaa usein liittymättömiä epäonnistumisia (kuten runnerin ylittäessä järjestelmän maksimimäärän avoimia tiedostoja). Testiodotukset asetetaan myös Servon CI-koneiden tulosten perusteella, joten ympäristösi erot voivat aiheuttaa epäonnistumisia.

Sinulla on yleensä karkea käsitys siitä, missä muutoksiisi liittyvät testit ovat. Esimerkiksi lähes kaikki SubtleCrypto -koodin testit ovat WebCryptoAPI -hakemistossa. Tässä tapauksessa voit ajaa vain nämä testit komennolla ./mach test-wpt WebCryptoAPI, ja sitten ./mach update-wpt kuten yllä kuvataan. Varmistaaksesi, ettei muita testejä rikkoutunut, tee sen jälkeen try run.

WebGPU-odotusten päivittämiseen katso WebGPU -luku.

Web Platform Tests -testien muokkaaminen

Katso Web Platform Test -testien Writing Tests guide testien kirjoittamiseen. Yleisesti hyvä tapa aloittaa on seurata näitä vaiheita:

  1. Etsi hakemisto, joka testaa haluamaasi ominaisuutta.
  2. Etsi hakemistosta testi, joka testaa samankaltaista käyttäytymistä, kopioi testi sopivalla nimellä ja tee muutoksesi.
  3. Jos testi on reference test, kopioi myös referenssi, jos sen pitää muuttua, seuraten muokkaamasi hakemiston nimeämiskäytäntöä.
  4. Aja ./mach update-manifest päivittääksesi WPT-manifestin uusilla testeillä.

Sinun on myös ajettava ./mach update-manifest aina, kun muokkaat testiä, referenssiä tai tukitiedostoa. Kaikki muutokset repossa oleviin Web Platform Tests -testeihin upstreamataan automaattisesti, kun PR:si mergeataan.

Servo-spesifiset testit

mozilla-hakemisto sisältää testejä, joita ei voi upstreamata jostain syystä (esim. koska ne riippuvat Servo-spesifisistä API:sta), sekä joitain legacy-testejä, jotka pitäisi upstreamata jossain vaiheessa. Kun ne ajetaan, ne mountataan palvelimelle polkuun /_mozilla/.

Reftest-tulosten analysointi

Reftest-tuloksia voi analysoida raakalokitiedostosta. Luo se ajamalla --log-raw-valitsimella, esim.

./mach test-wpt --log-raw wpt.log

Tämän tiedoston voi syöttää reftest analyzer -työkalulle, joka näyttää kaikki epäonnistuneet testit (ei vain niitä, joilla on odottamaton tulos). Huomaa, että tämä syö lokit eri muodossa kuin työkalun alkuperäinen versio, joka on kirjoitettu gecko-reftesteille.

Reftest analyzer mahdollistaa testin ja referenssin kuvakaappausten vertailun pikselitasolla. Testit, jotka sekä epäonnistuvat että joilla on odottamaton tulos, on merkitty !-merkillä.

WPT-manifestin päivittäminen

MANIFEST.json voidaan regeneroida automaattisesti mach-komennolla update-manifest, esim.

./mach update-manifest

Tämä on ekvivalentti ajolle

./mach test-wpt --manifest-update SKIP_TESTS

DevTools-testien ajaminen

Yksinkertaisin tapa ajaa DevTools-testit Servossa on ./mach test-devtools juurihakemistosta. Tämä ajaa kaikki devtools_tests/test_*.py-tiedostoissa määritellyt testit. Kaikki DevTools-liittyvät testitiedostot ovat hakemistossa python/servo/devtools_tests.

Vaihtoehtoja web platform tests -testien ajamiseen

Web platform tests -testejä voi ajaa myös muilla tavoilla. Näitä ei suositella tyypillisissä kehitystyönkuluissa, koska ne vaativat mukautetun asennuksen. Joskus näitä ajoja tarvitaan kuitenkin monimutkaisten vuorovaikutusten debuggaamiseen.

Web Platform Tests -testien ajaminen ulkoisella palvelimella

Normaalisti wptrunner käynnistää oman WPT-palvelimensa, mutta joskus saatat haluta ajaa useita mach test-wpt -instansseja, esimerkiksi debugatessasi yhtä testiä samalla kun ajat koko sarjaa taustalla, tai kun ajat yhtä testiä monta kertaa rinnakkain (–processes toimii vain eri testien välillä).

Tämä johtaisi “Failed to start HTTP server” -virheisiin, koska vain yksi WPT-palvelin voi olla kerrallaan käynnissä. Korjaa näin:

  1. Seuraa vaiheita kohdassa Web-testien manuaalinen ajaminen
  2. Lisää break kohtaan start_servers in serve.py seuraavasti:
--- a/tests/wpt/tests/tools/serve/serve.py
+++ b/tests/wpt/tests/tools/serve/serve.py
@@ -746,6 +746,7 @@ def start_servers(logger, host, ports, paths, routes, bind_address, config,
                   mp_context, log_handlers, **kwargs):
     servers = defaultdict(list)
     for scheme, ports in ports.items():
+        break
         assert len(ports) == {"http": 2, "https": 2}.get(scheme, 1)

         # If trying to start HTTP/2.0 server, check compatibility
  1. Aja mach test-wpt niin monta kertaa kuin tarvitset

Jos saat odottamattomia TIMEOUT-virheitä testharness-testeissä, custom testharnessreport.js on ehkä asennettu väärin (katso Web-testien manuaalinen ajaminen lisätietoja varten).

Web Platform Tests -testien manuaalinen ajaminen

(Katso myös upstream README:n relevantti osio.)

On hyödyllistä ajaa testi ilman test runnerin häiriötä, esimerkiksi käyttäessä debuggeria kuten gdb. Tätä varten WPT-palvelin on käynnistettävä manuaalisesti, mikä vaatii lisäkonfiguraatiota.

Lisää ensin seuraava järjestelmän hosts-tiedostoon:

127.0.0.1   www.web-platform.test
127.0.0.1   www1.web-platform.test
127.0.0.1   www2.web-platform.test
127.0.0.1   web-platform.test
127.0.0.1   xn--n8j6ds53lwwkrqhv28a.web-platform.test
127.0.0.1   xn--lve-6lad.web-platform.test

Siirry hakemistoon tests/wpt/web-platform-tests tämän osion loppuosaa varten.

Normaalisti wptrunner asentaa Servon version testharnessreport.js:stä, mutta kun WPT-palvelin käynnistetään manuaalisesti, saamme oletusversion, joka ei raportoi testituloksia oikein. Korjaa näin:

  1. Luo hakemisto local-resources
  2. Kopioi tools/wptrunner/wptrunner/testharnessreport-servo.jslocal-resources/testharnessreport.js
  3. Muokkaa local-resources/testharnessreport.js korvaten muuttujat seuraavasti:
  • %(output)d
    • 1 jos haluat leikkiä testillä interaktiivisesti (≈ pause-after-test)
    • 0 jos et välitä siitä (vaikka 1 on ok aina)
  • %(debug)strue
  1. Luo ./config.json seuraavasti (katso tools/wave/config.default.json oletuksia varten):
{"aliases": [{
    "url-path": "/resources/testharnessreport.js",
    "local-dir": "local-resources"
}]}

Käynnistä palvelin komennolla ./wpt serve. Tarkistaaksesi, asennettiinko testharnessreport.js oikein:

  • Komennon curl http://web-platform.test:8000/resources/testharnessreport.js standarditulosteen pitäisi näyttää testharnessreport-servo.js:ltä, ei the default testharnessreport.js:ltä
  • Komennon target/release/servo http://web-platform.test:8000/css/css-pseudo/highlight-pseudos-computed.html standardituloste (tai mikä tahansa testharness test) pitäisi sisältää rivit, jotka alkavat:
    • TEST START
    • TEST STEP
    • TEST DONE
    • ALERT: RESULT:

Estääksesi selaimen SSL-varoitukset HTTPS-testejä ajaessa paikallisesti, sinun on ajettava Servo valitsimella --certificate-path resources/cert-wpt-only.

Web Platform Tests -testien ajaminen Firefoxissa

Testien parissa työskennellessä saatat haluta vertailla Servon tulosta Firefoxiin. Voit antaa --product firefox sekä polun Firefox-binääriin (sekä muutamia muita asioita) ajaaaksesi testejä Firefoxissa Servo-checkoutistasi:

GECKO="$HOME/projects/mozilla/gecko"
GECKO_BINS="$GECKO/obj-firefox-release-artifact/dist/Nightly.app/Contents/MacOS"
./mach test-wpt dom --product firefox --binary $GECKO_BINS/firefox --certutil-binary $GECKO_BINS/certutil --prefs-root $GECKO/testing/profiles

Debuggaus

Yksi yksinkertaisimmista tavoista debugata Servoa on tulostaa kiinnostavia muuttujia println!-, eprintln!- tai dbg!-makroilla. Yleensä niitä tulisi käyttää vain tilapäisesti; sinun täytyy poistaa ne tai muuntaa ne kunnolliseksi debug-lokituksesi ennen kuin pull request yhdistetään.

Debug-lokitus log-kirjastolla ja RUST_LOG-muuttujalla

Servo käyttää log-kirjastoa pitkäaikaiseen debug-lokitukseen ja virheviesteihin:

#![allow(unused)]
fn main() {
log::error!("hello");
log::warn!("hello");
log::info!("hello");
log::debug!("hello");
log::trace!("hello");
}

Toisin kuin println!-kaltaiset makrot, log lisää aikaleiman ja kertoo, mistä viesti tuli:

[2024-05-01T09:07:42Z ERROR servoshell::app] hello
[2024-05-01T09:07:42Z WARN  servoshell::app] hello
[2024-05-01T09:07:42Z INFO  servoshell::app] hello
[2024-05-01T09:07:42Z DEBUG servoshell::app] hello
[2024-05-01T09:07:42Z TRACE servoshell::app] hello

Voit suodattaa log-kirjaston tulostetta RUST_LOG-muuttujalla tason (off, error, warn, info, debug, trace) ja/tai viestin lähteen, eli niin sanotun „targetin”, mukaan. Yleensä target on Rust-moduulin polku kuten servoshell::app, mutta on myös erityisiä targeteja (ks. § Tapahtumien jäljittäminen). Aseta RUST_LOG lisäämällä se komennon eteen tai käyttämällä export-komentoa:

$ RUST_LOG=warn ./mach run -d test.html     # Uses the prepended RUST_LOG.
$ export RUST_LOG=warn
$ ./mach run -d test.html                   # Uses the exported RUST_LOG.

Katso env_logger-dokumentaatiosta lisätietoja, mutta tässä muutamia esimerkkejä:

  • ota käyttöön kaikki viestit debug-tasolle asti, mutta ei trace-tasoa:
    RUST_LOG=debug
  • ota käyttöön kaikki viestit kohteista servo::*, servoshell::* tai mistä tahansa targetista, joka alkaa servo:
    RUST_LOG=servo=trace (tai pelkkä RUST_LOG=servo)
  • ota käyttöön kaikki viestit kohteista, jotka alkavat style, mutta vain error- ja warn-viestit kohteesta style::rule_tree:
    RUST_LOG=style,style::rule_tree=warn

Huomaa, että vaikka lokiviesti suodatetaan pois, se voi silti vaikuttaa suorituskykyyn, vaikkakin vain hieman. Eräät käännökset Servosta, mukaan lukien viralliset yöjulkaisut, poistavat DEBUG- ja TRACE-viestit käännösaikana, joten niiden ottaminen käyttöön RUST_LOG-muuttujalla ei vaikuta mihinkään.

Tapahtumien jäljittäminen

Constellationissa, compositorissa ja servoshellissä lokataan muihin komponentteihin lähetetyt ja niistä vastaanotetut viestit targeteilla muodossa component>other@Event tai component<other@Event. Tämä tarkoittaa, että voit valita ajonaikaisesti, mitä tapahtumatyyppejä lokataan RUST_LOG-muuttujalla!

Esimerkiksi constellationissa (lisätietoja):

  • jäljitä vain scriptin tapahtumat:
    RUST_LOG='constellation<=off,constellation<script@'
  • jäljitä kaikki tapahtumat paitsi ReadyToPresent-tapahtumat:
    RUST_LOG='constellation<,constellation<compositor@ReadyToPresent=off'
  • jäljitä vain scriptin InitiateNavigateRequest-tapahtumat:
    RUST_LOG='constellation<=off,constellation<script@InitiateNavigateRequest'

Compositorissa (lisätietoja):

  • jäljitä vain MoveResizeWebView-tapahtumat:
    RUST_LOG='compositor<constellation@MoveResizeWebView'
  • jäljitä kaikki tapahtumat paitsi Forwarded-tapahtumat:
    RUST_LOG=compositor<,compositor<constellation@Forwarded=off

Servoshellissä (lisätietoja):

  • jäljitä vain servon tapahtumat:
    RUST_LOG='servoshell<=off,servoshell>=off,servoshell<servo@'
  • jäljitä kaikki tapahtumat paitsi AxisMotion-tapahtumat:
    RUST_LOG='servoshell<,servoshell>,servoshell<winit@WindowEvent(AxisMotion)=off'
  • jäljitä vain winitin ikkunan siirto -tapahtumat:
    RUST_LOG='servoshell<=off,servoshell>=off,servoshell<winit@WindowEvent(Moved)'

Tapahtumien jäljittäminen voi tuottaa valtavan määrän tulostetta. Yleensä suosittelemme seuraavaa konfiguraatiota, jotta tuloste pysyy käyttökelpoisena:

  • constellation<,constellation>,constellation<compositor@ForwardEvent(MouseMoveEvent)=off,constellation<compositor@LogEntry=off,constellation<compositor@ReadyToPresent=off,constellation<script@LogEntry=off
  • compositor<,compositor>
  • servoshell<,servoshell>,servoshell<winit@DeviceEvent=off,servoshell<winit@MainEventsCleared=off,servoshell<winit@NewEvents(WaitCancelled)=off,servoshell<winit@RedrawEventsCleared=off,servoshell<winit@RedrawRequested=off,servoshell<winit@UserEvent(WakerEvent)=off,servoshell<winit@WindowEvent(CursorMoved)=off,servoshell<winit@WindowEvent(AxisMotion)=off

Muu debug-lokitus

mach run tekee tämän automaattisesti, mutta tulosta backtrace, kun Servo panikoi:

$ RUST_BACKTRACE=1 target/debug/servo test.html

Käytä -Z (-- --debug) ottaaksesi debug-asetukset käyttöön. Esimerkiksi tulosta stacking context -puu jokaisen layoutin jälkeen tai hae apua näihin asetuksiin:

$ ./mach run -Z stacking-context-tree test.html
$ ./mach run -Z help            # Lists available debug options.
$ ./mach run -- --debug help    # Same as above: lists available debug options.
$ ./mach run --debug            # Not the same! This just chooses target/debug.

Vaihtoehtoisesti voit käyttää SERVO_DIAGNOSTICS-ympäristömuuttujaa diagnostiikka-asetusten määrittämiseen.

$ SERVO_DIAGNOSTICS=style-tree ./mach run test.html
$ SERVO_DIAGNOSTICS=help ./mach run    # Lists available debug options.
$ export SERVO_DIAGNOSTICS=style-tree,display-list
$ ./mach run test.html            # Uses the exported SERVO_DIAGNOSTICS.

SERVO_DIAGNOSTICS-ympäristömuuttuja hyväksyy pilkuilla erotetut diagnostiikka-asetukset, samat kuin -Z-valinnan kautta saatavilla olevat. Tämä ominaisuus on saatavilla vain debug- ja release-käännöksissä, ei production-käännöksissä.

macOS:llä voit myös lisätä Cocoa-spesifisiä debug-asetuksia ylimääräisen ---merkitsimen jälkeen:

$ ./mach run -- test.html -- -NSShowAllViews YES

Servoshellin ajaminen debuggerilla

Aja servoshell debuggerilla käyttämällä --debugger-cmd-valintaa. Huomaa, että jos valitset gdb tai lldb, käytämme automaattisesti rust-gdb:tä ja rust-lldb:tä.

$ ./mach run --debugger-cmd=gdb test.html   # Same as `--debugger-cmd=rust-gdb`.
$ ./mach run --debugger-cmd=lldb test.html  # Same as `--debugger-cmd=rust-lldb`.

Välittääksesi debuggerille lisäasetuksia sinun täytyy ajaa debugger itse:

$ ./mach run --debugger-cmd=gdb -ex=r test.html         # Passes `-ex=r` to servoshell.
$ rust-gdb -ex=r --args target/debug/servo test.html    # Passes `-ex=r` to gdb.

$ ./mach run --debugger-cmd=lldb -o r test.html         # Passes `-o r` to servoshell.
$ rust-lldb -o r -- target/debug/servo test.html        # Passes `-o r` to lldb.

$ ./mach run --debugger-cmd=rr -M test.html             # Passes `-M` to servoshell.
$ rr record -M target/debug/servo test.html             # Passes `-M` to rr.

Monet debuggerit tarvitsevat lisäasetuksia erottamaan servoshellin argumentit omista valinnoistaan, ja --debugger-cmd välittää nämä asetukset automaattisesti muutamille debuggereille, mukaan lukien gdb ja lldb. Muille debuggereille --debugger-cmd toimii vain, jos debugger ei tarvitse ylimääräisiä asetuksia:

$ ./mach run --debugger-cmd=rr test.html                    # Good, because it’s...
#  servoshell arguments        ^^^^^^^^^
$ rr target/debug/servo test.html                           # equivalent to this.
#  servoshell arguments ^^^^^^^^^

$ ./mach run --debugger-cmd=renderdoccmd capture test.html  # Bad, because it’s...
#                renderdoccmd arguments? ^^^^^^^
#                  servoshell arguments          ^^^^^^^^^
$ renderdoccmd target/debug/servo capture test.html         # equivalent to this.
# => target/debug/servo is not a valid command.

$ renderdoccmd capture target/debug/servo test.html         # Good.
#              ^^^^^^^ renderdoccmd arguments
#                    servoshell arguments ^^^^^^^^^

Debuggaus gdb:llä tai lldb:llä

Etsi funktio nimen tai regexin perusteella:

(lldb) image lookup -r -n <name>
(gdb) info functions <name>

Listaa käynnissä olevat säikeet:

(lldb) thread list
(lldb) info threads

Muita gdb- tai lldb-komentoja:

(gdb) b a_servo_function    # Add a breakpoint.
(gdb) run                   # Run until breakpoint is reached.
(gdb) bt                    # Print backtrace.
(gdb) frame n               # Choose the stack frame by its number in `bt`.
(gdb) next                  # Run one line of code, stepping over function calls.
(gdb) step                  # Run one line of code, stepping into function calls.
(gdb) print varname         # Print a variable in the current scope.

Katso tätä gdb-oppasta tai tätä lldb-oppasta lisätietoja varten.

Voit tarkastella muuttujia lldb:ssä myös kirjoittamalla gui ja käyttämällä nuolinäppäimiä muuttujien laajentamiseen:

(lldb) gui
┌──<Variables>───────────────────────────────────────────────────────────────────────────┐
│ ◆─(&mut gfx::paint_task::PaintTask<Box<CompositorProxy>>) self = 0x000070000163a5b0    │
│ ├─◆─(msg::constellation_msg::PipelineId) id                                            │
│ ├─◆─(url::Url) _url                                                                    │
│ │ ├─◆─(collections::string::String) scheme                                             │
│ │ │ └─◆─(collections::vec::Vec<u8>) vec                                                │
│ │ ├─◆─(url::SchemeData) scheme_data                                                    │
│ │ ├─◆─(core::option::Option<collections::string::String>) query                        │
│ │ └─◆─(core::option::Option<collections::string::String>) fragment                     │
│ ├─◆─(std::sync::mpsc::Receiver<gfx::paint_task::LayoutToPaintMsg>) layout_to_paint_port│
│ ├─◆─(std::sync::mpsc::Receiver<gfx::paint_task::ChromeToPaintMsg>) chrome_to_paint_port│
└────────────────────────────────────────────────────────────────────────────────────────┘

Jos lldb kaatuu tietyillä riveillä, joissa on profile()-funktio, et ole ainoa. Kommentoi profilointikoodi pois ja pidä vain sisäinen funktio — sen pitäisi riittää.

Kääntävä debuggaus rr:llä (vain Linux)

rr on kuin gdb, mutta sen avulla voit kelata taaksepäin. Aloita ajamalla servoshell rr:n kautta:

$ ./mach run --debugger=rr test.html    # Either this...
$ rr target/debug/servo test.html       # ...or this.

Toista sitten jälki ja käytä gdb- tai rr-komentoja:

$ rr replay
(rr) continue
(rr) reverse-cont

Aja yksi tai useampi testi toistuvasti, kunnes tulos on odottamaton:

$ ./mach test-wpt --chaos path/to/test [path/to/test ...]

rr:n tallentamat jäljet voivat viedä paljon tilaa. Poista ne hakemistosta ~/.local/share/rr.

OpenGL-debuggaus RenderDocilla (vain Linux tai Windows)

RenderDoc mahdollistaa Servon OpenGL-toiminnan debuggauksen. Aloita ajamalla servoshell renderdoccmd:n kautta:

$ renderdoccmd capture -d . target/debug/servo test.html

Kun servoshell on käynnissä, aja qrenderdoc ja valitse File > Attach to Running Instance. Kun yhteys on muodostettu, voit painaa F12 tai Print Screen kaapataksesi ruudun.

Debuggaus OpenHarmonylla

On suositeltavaa lukea ensin yleiset ohjeet Debuggaus-osiosta. Tämä osio käsittelee vain OpenHarmonyyn liittyviä eroja.

Servoshellin käynnistäminen komentoriviltä

Debuggauksessa on usein hyödyllistä käynnistää servo komentoriviltä sovelluskuvakkeen sijaan, koska voimme välittää parametreja komentoriviltä.

# Run this command to see which parameters can be passed to an ohos app
hdc shell aa start --help
# Start servo from the commandline
hdc shell aa start -a EntryAbility -b org.servo.servo
# --ps=<arg> <value> can be used to pass arguments with values to servoshell.
# The space between arg and value is mandatory when using `--ps`, meaning `--ps=--log-filter warn` is
# correct while `--ps=--log-filter=warn` is not.
# For pure flags without a value use `--psn <flag>`
hdc shell aa start -a EntryAbility -b org.servo.servo --ps=--log-filter "warn"
# Use `-U <url>` to let servo load a custom URL.
hdc shell aa start -a EntryAbility -b org.servo.servo -U https://servo.org

Lokitus

OpenHarmony-laitteilla lokiviestit voidaan tallentaa ja hakea hilog-palvelun kautta:

# See the hilog help for a full list of arguments supported by hilog
hdc shell hilog --help
# View all logs (very verbose, includes all other apps)
hdc shell hilog
# All servo and Spidermonkey related logs
hdc shell hilog --domain=0xE0C3,0xE0C4
# Only Rust code from servo
hdc shell hilog --domain=0xE0C3
# Only spidermonkey C++ code
hdc shell hilog --domain=0xE0C4
# Filter by domain and log level
hdc shell hilog --domain=0xE0C3 --level=ERROR

Lokitaso

Riippuu useista ehdoista, näkyvätkö lokiviestit:

  1. log-kirjaston käännösaikainen maksimilokitaso. Katso log compile-time filters. Huom: log-kirjaston release_max_level_<level>-ominaisuudet tarkistavat, onko debug_assertions = false asetettu määrittääkseen, sovelletaanko release-suodattimia.
  2. Ajonaikainen log-kirjaston globaali suodatin moduuleittain, joka on asetettu servoshellissä. Koska ympäristömuuttujat eivät ole vaihtoehto lokitason mukauttamiseen, servoshell:lla on --log-filter-valinta ohos-kohteilla, joka mahdollistaa log-kirjaston lokisuodattimen mukauttamisen. Oletuksena servoshell asettaa lokisuodattimen, joka piilottaa lokiviestit monista crateista, joten todennäköisesti joudut asettamaan mukautetun log-filterin, jos et näe debuggaamasi craten lokeja.
  3. hilog-palvelun peruslokisuodatin. hdc shell hilog --base-level=<log_level>. Voidaan yhdistää --domain- ja --tag- valintoihin mukauttaaksesi mitkä lokit tallennetaan.
  4. hilog-palvelun lokisuodatin lokeja näytettäessä: hdc shell hilog --level=<level>

Useimmiten vaihtoehtoja 2 ja/tai 4 tulisi käyttää, koska ne mahdollistavat nopeat muutokset ilman uudelleenkääntämistä.

Hilog-domainit

hilog mahdollistaa mukautetun kokonaisluvun „domain” (välillä 0–0xFFFF) asettamisen lokittaessa, mikä helpottaa lokien suodattamista domainin mukaan. Servon Rust-koodissa, joka lokataan log-kirjaston kautta, käytämme domainia 0xE0C3 ja Spidermonkeyn C++-koodissa domainia 0xE0C4. Nämä arvot on valittu melko mielivaltaisesti.

Hilog-yksityisyysominaisuus

hilog:lla on yksityisyysominaisuus, joka oletuksena piilottaa arvot lokeissa (esim. %d- tai %s-korvausten kohdalla). Rustin lokiviestit eivät yleensä ole tämän vaikutuksen alaisia, koska merkkijonojen muotoilu tehdään Rust-puolella. Jos kohtaat tämän ongelman C/C++-lokeja katsellessa, voit poistaa yksityisyysominaisuuden väliaikaisesti käytöstä ajamalla:

hdc shell hilog -p off

DevTools ja porttien välitys

Voit ottaa DevTools-palvelimen käyttöön ja yhdistää siihen etänä Firefoxilla. Helpoin tapa on komentorivi: hdc shell aa start -a EntryAbility -b org.servo.servo --psn=--devtools=6080 Yhdistääksesi Servo-instanssiin sinun täytyy välittää portti komennolla hdc fport tcp:6080 tcp:6080. Sinun pitäisi nähdä viesti, että välitys onnistui. Nyt voit yhdistää DevTools-palvelimeen osoitteella localhost:6080.

Profilointi

Kun profiloit Servoa tai vianmetsästät suorituskykyongelmia, varmista, että käännöksesi on optimoitu mutta silti sallii tarkan profilointidatan.

./mach build --profile profiling --with-frame-pointer
  • –profile profiling kääntää Servon profilointikonfiguraatiollamme
  • –with-frame-pointer kääntää Servon pinot-kehyspointtereilla kaikilla alustoilla

Vastaava profiili on valittava Servoa ajettaessa:

./mach run --profile profiling http://example.org

Useita tapoja saada profilointitietoa Servon ajoista:

Tracing Perfetton kanssa

Tracing toimii instrumentoimalla tiettyjä funktioita (tai koodin osia) eksplisiittisillä annotaatioilla kuten time_profile! ja servo_tracing::*. Se tallentaa deterministisesti jokaisen kutsun instrumentoidun koodin läpi, mutta ei tarjoa näkyvyyttä koodiin, jota ei ole instrumentoitu.

Sitä vastoin sampling-profilerit näkevät kaiken, mutta vain probabilistisesti, ja tarkentuvat siten pidemmillä silmukoilla.

Tracingin käyttöön ota käyttöön liittyvät compile-time-ominaisuudet:

./mach build --profile profiling --features tracing tracing-perfetto

Aja sitten Servo SERVO_TRACING-ympäristömuuttujalla, joka on asetettu EnvFilter directives -direktiiveihin valitaksesi, mitkä trace:t otetaan käyttöön:

SERVO_TRACING=… ./mach run --profile profiling http://example.org

Esimerkiksi:

  • SERVO_TRACING=off poistaa kaiken tracingin käytöstä (tämä on oletus)
  • SERVO_TRACING=trace ottaa kaiken tracingin käyttöön (tuottaa valtavia trace-tiedostoja compositing-moduulin vuoksi)
  • SERVO_TRACING=[{servo_profiling}] tekee saman, koska suodatamme implisiittisesti servo_profiling-arvolla
  • SERVO_TRACING=info ottaisi käyttöön vain info-tason ja yläpuolella olevat, mutta emme vielä käytä tasoja
  • SERVO_TRACING=layout ottaa tracingin käyttöön vain layout-cratessa
  • SERVO_TRACING=trace,compositing=off ottaa kaiken tracingin käyttöön paitsi compositing-cratessa
  • SERVO_TRACING=[handle_reflow] ottaa tracingin käyttöön spaneissa nimeltä “handle_reflow” tai niiden jälkeläisissä

Tämä luo servo.pftrace-tiedoston nykyiseen hakemistoon, jota voi visualisoida osoitteessa ui.perfetto.dev.

Interval Profiling

Käyttämällä -p-valitsinta ja sen jälkeen numeroa (aikajakso sekunteina) voit tulostaa profilointitietoa terminaaliin säännöllisesti. Tätä varten aja Servo halutulla sivustolla (URL-osoitteet ja paikalliset tiedostopolut molemmat tuettuja) profilointi käytössä:

./mach run --profile profiling http://example.org -p 5

Yllä olevassa esimerkissä, kun Servo on yhä käynnissä (JA käsittelee uusia pass-vaiheita), profilointitieto tulostetaan terminaaliin 5 sekunnin välein.

Kun sivu on ladattu, paina ESC (tai sulje sovellus) poistuaksesi. Profilointituloste tarjotaan jaoteltuna selainmoottorin alueisiin ja URL:ään. Esimerkiksi saatat saada alla olevan muotoisen tulosteen:

_category_                          _incremental?_ _iframe?_             _url_                  _mean (ms)_   _median (ms)_      _min (ms)_      _max (ms)_       _events_ 
Painting                                N/A          N/A                  N/A                        6.8177          0.9512          0.0035         30.7573               6
Layout                                  yes          yes     http://example.org/                     0.0016          0.0016          0.0016          0.0016               1
Layout                                  no           yes     http://example.org/                    14.4966         14.4966         14.4966         14.4966               1
ScriptParseHTML                         no           yes     http://example.org/                     0.8507          1.7009          0.0004          1.7009               2
TimeToFirstPaint                        no           yes     http://example.org/                     0.0000          0.0000          0.0000          0.0000               1
TimeToFirstContentfulPaint              no           yes     http://example.org/                     0.0000          0.0000          0.0000          0.0000               1

_url_   _blocked layout queries_

Tässä esimerkissä sivun latauksessa suoritimme yhden täyden asettelun ja yhden inkrementaalisen asettelun.

TSV Profiling

Käyttämällä -p-valitsinta ja sen jälkeen tiedostonimeä voit tulostaa Servon suorituksen profilointitiedot TSV-tiedostoon (tab-separated, koska tietyt url:t sisältävät pilkkuja). Tiedot kirjoitetaan tiedostoon vasta Servon lopettamisen yhteydessä. Tämä toimii hyvin -x-, -z- ja -o-valitsimien kanssa, jotta suorituskykytietoa voidaan kerätä automatisoiduissa ajoissa. Esimerkkikäyttö:

./mach run --profile profiling http://example.org -zxo out.png -p out.tsv

Interval- ja TSV Profiling -valintojen profilointitiedon formaatit ovat käytännössä samat; url-nimiä ei katkaista TSV Profiling -valinnossa.

Generating Timelines

Lisää --profiler-trace-path /timeline/output/path.html -lippu tulostaaksesi profilointidatan itse sisältävänä HTML-aikajanana. Koska se on itse sisältävä tiedosto (kaikki CSS ja JS inline), sitä on helppo jakaa, ladata tai linkittää bugiraportteihin.

./mach run --profile profiling http://example.org -p 5 --profiler-trace-path trace.html

Usage:

  • Käytä hiiren rullaa tai trackpadin scrollausta, hiiren ollessa aikajanan yläosassa, zoomataksesi näkymää sisään tai ulos.

  • Tartu valittuun alueeseen yläosassa ja vedä vasemmalle tai oikealle sivusuuntaiseen scrollaukseen.

  • Vie hiiri trace:n päälle nähdäksesi lisätietoja.

Hacking

Aikajanan JS, CSS ja HTML tulee reposta fitzgen/servo-trace-dump, ja siellä on skripti Servon kopion päivittämiseen.

Kaikki muu koodi on hakemistossa components/profile/.

Sampling profiler

Servo sisältää sampling-profilerin, joka tuottaa profiileja, jotka voidaan avata Gecko profiling tools -työkaluissa. Käyttö:

  1. Aja Servo lataamalla profiloitava sivu
  2. Paina Ctrl+P (tai Cmd+P macOS:ssa) käynnistääksesi profilerin (konsolin pitäisi näyttää “Enabling profiler”)
  3. Paina Ctrl+P (tai Cmd+P macOS:ssa) pysäyttääksesi profilerin (konsolin pitäisi näyttää “Stopping profiler”)
  4. Pidä Servo käynnissä, kunnes symbolien resoluutio on valmis (konsolin pitäisi näyttää lopuksi “Resolving N/N”)
  5. Aja python etc/profilicate.py samples.json >gecko_samples.json muuntaaksesi profiilin muotoon, jonka Gecko profiler ymmärtää
  6. Lataa gecko_samples.json osoitteeseen https://profiler.firefox.com/

Tulostetiedoston nimen hallintaan aseta PROFILE_OUTPUT-ympäristömuuttuja. Näytteenottotaajuuden hallintaan (oletus 10 ms) aseta SAMPLING_RATE-ympäristömuuttuja.

Memory Profiling

  • Aja Servoshell normaalisti
  • Avaa uusi välilehti
  • Navigoi osoitteeseen about:memory
  • Napsauta Measure
  • Näet raportin, joka muistuttaa tätä:
  115.15 MiB -- explicit
     101.15 MiB -- jemalloc-heap-unclassified
      14.00 MiB -- url(http://example.org/)
         10.01 MiB -- layout-thread
            10.00 MiB -- font-context
             0.00 MiB -- stylist
             0.00 MiB -- display-list
          4.00 MiB -- js
             2.75 MiB -- malloc-heap
             1.00 MiB -- gc-heap
                0.56 MiB -- decommitted
                0.35 MiB -- used
                0.06 MiB -- unused
                0.02 MiB -- admin
             0.25 MiB -- non-heap
       0.00 MiB -- memory-cache
          0.00 MiB -- private
          0.00 MiB -- public

  121.89 MiB -- jemalloc-heap-active
  111.16 MiB -- jemalloc-heap-allocated
  203.02 MiB -- jemalloc-heap-mapped
  272.61 MiB -- resident

Using macOS Instruments

Xcodessa on instruments -työkalu helppoon profilointiin.

Ensin asenna Xcode instruments:

xcode-select --install

Toiseksi asenna cargo-instruments Homebrewin kautta:

brew install cargo-instruments

Sitten voit ajaa sen suoraan CLI:stä:

cargo instruments -t Allocations

Tässä on linkkejä ja resursseja Instrumentsin käyttöön (jotkut streamaavat vain Safarissa):

Profiling WebRender

Kun ajat Servoshelliä, paina CTRL+F12 näyttääksesi (tai piilottaaksesi) WebRender-overlayn.

Webpage snapshots

On mahdollista käyttää mitmproxy-työkalua Servon liikenteen sieppaamiseen ja luoda paikallinen snapshot (dump) mielivaltaisesta verkkosivusta, jota voidaan sitten tarjoilla paikallisesti profilointitarkoituksiin.

mitmproxy tukee useita tapoja siepata liikennettä, mukaan lukien proxy-tila portissa :8080, joten voit asettaa selaimen vain:

./target/release/servo \
--pref=network_http_proxy_uri=http://127.0.0.1:8080 \
--ignore-certificate-errors 

[!info] The default mitmproxy certs are in the ~/.mitmproxy or you can generate some using mitmproxy, but I have just set my browser to ignore cert errors

[!warning] ignoring certs is easy, but be cautious of risks

Default mitmproxy

Oletusverkossa mitmproxy luo paikallisen proxy-palvelimen porttiin :8080, ja asettamalla sen selaimessa tai antamalla http_proxy=localhost:8080 ja/tai https_proxy=localhost:8080 (ja valinnaisesti poistamalla no_proxy-asetuksen) voit dumppata ja tarjoilla liikennettä.

Creating a dump

mitmproxy -w <dumpfile>

Serving a dump

mitmproxy --server-replay <dumpfile>

Tuloksena oleva dump-tiedosto on noin ~5MB per sivu, joten se kasvaa nopeasti suureksi, koska työkalu on hyvin verbose ja voi tallentaa kuvia.

Chain-proxy

Toisen ensisijaisen proxy-yhteyden tapauksessa meidän on välitettävä upstream pääproxy:lle mitmproxy:sta, ja jos pääproxyllä on myös custom-sertifikaatteja, on tärkeää välittää ne tai jättää ne huomiotta

[!warning] ignoring certs is easy, but be cautious of risks

Creating a dump

mitmproxy  --mode upstream:${http_proxy} -w <dump-path>\
--set ssl_insecure=true
#### Serving the dump
```bash
mitmproxy -v  --server-replay ~/dev/recodings/servo_org_3.dump \
--set server_replay_extra=404 \
--set server_replay_ignore_host=true \
--set connection_strategy=lazy \
--set server_replay_reuse=true

[!info] the replay_extra and replay_reuse are optional, and may cause unexpected behaviour

OpenHarmony

Työkalua voi käyttää etäpuhelimen liikenteen sieppaamiseen, mukaan lukien OpenHarmony-kohteet. Avaa käänteinen proxy-portti hdc:llä ja aja sitten servo proxy- ja sertifikaattiasetuksilla.

reverse port

hdc rport tcp:8080 tcp:8080

run with args

hdc shell aa start -a EntryAbility \
-b org.servo.servo -U https://servo.org \
--psn=--pref=network_http_proxy_uri=http://127.0.0.1:8080 \
--psn=--ignore-certificate-errors

Crate-riippuvuudet

Rust-kirjastoa kutsutaan crateksi. Servo käyttää paljon crateja. Nämä cratet ovat riippuvuuksia. Ne on listattu tiedostoissa nimeltä Cargo.toml. Servo on jaettu komponentteihin ja portteihin (katso components- ja ports-hakemistot). Jokaisella on omat riippuvuutensa ja oma Cargo.toml-tiedostonsa.

Cargo.toml-tiedostot listaavat riippuvuudet. Voit muokata tätä tiedostoa.

Esimerkiksi components/net_traits/Cargo.toml sisältää:

 [dependencies.stb_image]
 git = "https://github.com/servo/rust-stb-image"

Mutta koska rust-stb-image-API voi muuttua ajan myötä, ei ole turvallista kääntää rust-stb-image-repon HEAD-versiota vastaan. Cargo.lock-tiedosto on Cargo.toml-tiedoston tilannekuva, joka sisältää viitteen tarkkaan revisioon ja varmistaa, että kaikki kääntävät aina samalla konfiguraatiolla:

[[package]]
name = "stb_image"
source = "git+https://github.com/servo/rust-stb-image#f4c5380cd586bfe16326e05e2518aa044397894b"

Tätä tiedostoa ei pidä muokata käsin. Normaalissa Rust-projektissa git-revisiota päivittäisi komennolla cargo update -p stb_image, mutta Servossa käytä ./mach cargo-update -p stb_image. Myös muut cargo-argumentit ymmärretään, esim. käytä –precise ‘0.2.3’ päivittääksesi kyseisen craten versioon 0.2.3.

Katso Cargon dokumentaatio Cargo.toml- ja Cargo.lock-tiedostoista.

Työskentely craten parissa

Kuten yllä selitetään, Servo riippuu monista kirjastoista, mikä tekee siitä hyvin modulaarisen. Työskennellessäsi Servon bugiin päädyt usein johonkin sen riippuvuuksista. Haluat silloin kääntää oman versionsi riippuvuudesta (ja ehkä kääntäminen kirjaston HEAD-versiota vastaan korjaa ongelman!).

Esimerkiksi yritän tuoda joitain cocoa-tapahtumia Servoon. Servon ikkuna työpöydällä rakennetaan kirjastolla nimeltä winit. winit riippuu cocoa-kirjastosta nimeltä cocoa-rs. Kun rakennat Servon, kaikki nämä riippuvuudet ladataan ja käännätään automaattisesti. Mutta koska haluan työskennellä tämän cocoa-tapahtumaominaisuuden parissa, haluan Servon käyttävän omaa versiotani winit- ja cocoa-rs-kirjastoista.

Projektini on järjestetty näin:

~/my-projects/servo/
~/my-projects/cocoa-rs/

Molemmat kansiot ovat git-repositorioita.

Jotta Servo käyttäisi polkua ~/my-projects/cocoa-rs/, selvitä ensin, mitä versiota cratesta Servo käyttää ja onko se git- vai crates.io-riippuvuus.

Molemmat tiedot löytyvät komennolla, tässä esimerkissä, cargo pkgid cocoa (cocoa on paketin nimi, joka ei välttämättä vastaa repo-kansion nimeä).

Jos tulos on muodossa https://github.com/servo/cocoa-rs#cocoa:0.0.0, kyseessä on git-riippuvuus ja sinun on muokattava tiedostoa ~/my-projects/servo/Cargo.toml ja lisättävä loppuun:

[patch]
"https://github.com/servo/cocoa-rs#cocoa:0.0.0" = { path = '../cocoa-rs' }

Jos tulos on muodossa https://github.com/rust-lang/crates.io-index#cocoa#0.0.0, kyseessä on crates.io-riippuvuus ja sinun on muokattava tiedostoa ~/my-projects/servo/Cargo.toml seuraavasti:

[patch]
"cocoa:0.0.0" = { path = '../cocoa-rs' }

Molemmat kertovat mille tahansa cargo-projektille, ettei se käytä riippuvuuden verkkoversiota vaan paikallista klooniasi.

Lisätietoja riippuvuuksien ohittamisesta: Cargon dokumentaatio.

Crate-julkaisujen pyytäminen

Servon selainmoottorin luomisen lisäksi Servo-projekti julkaisee modulaarisia komponentteja, kun ne voivat hyödyttää laajempaa Rust-kehittäjäyhteisöä. Esimerkki tällaisesta cratesta on rust-url. Pyrimme olemaan hyviä ylläpitäjiä, mutta selainmoottorin ja ulkoisten kirjastojen kokoelman hallinta voi olla paljon työtä, joten emme takaa säännöllisiä julkaisuja näille modulaarisille crateille.

Jos koet, että jonkin näistä crateista julkaisu on ajankohtainen, vastaamme pyyntöihin uusista julkaisuista. Uuden julkaisun pyytämisen prosessi on:

  1. Luo yksi tai useampi pull request, joka valmistelee craten uutta julkaisua varten.
  2. Luo pull request, joka nostaa versionumeron repositoriossa, ollen tarkkana siitä, mikä version osa pitäisi kasvaa edellisestä julkaisusta. Tämä tarkoittaa, että sinun on ehkä merkittävä, onko mukana breaking change -muutoksia.
  3. Pyydä pull requestissa uuden version julkaisemista. Muutoksen landaava henkilö on vastuussa uuden version julkaisemisesta tai selittää, miksi sitä ei voida julkaista pull requestin landauksen yhteydessä.

DevTools

Firefox DevTools on joukko web-kehittäjätyökaluja, joilla voi tarkastella, muokata ja debugata verkkosivun HTML:ää, CSS:ää ja JavaScriptiä. Servo tukee osajoukkoa DevTools-toiminnallisuudesta, mikä mahdollistaa yksinkertaisen debuggauksen.

Yhdistäminen Servoon

  1. Aja servoshell DevTools-palvelin käytössä. Luku devtools-parametrin jälkeen on palvelimen käyttämä portti.
./mach run --devtools=6080
  1. Avaa Firefox ja siirry osoitteeseen about:debugging. Jos käytät DevTools-integraatiota ensimmäistä kertaa, siirry Setup-välilehdelle ja lisää localhost:6080 verkkosijainniksi. Porttinumeron on oltava sama kuin edellisessä vaiheessa.

  2. Napsauta sivupalkissa Connect kohdan localhost:6080 vieressä.

Portin asettaminen Firefoxissa

  1. Palaa Firefoxissa, valitse webview ja napsauta Inspect. Uuden ikkunan pitäisi avautua sivun inspectorilla.

Välilehden tarkastelu

Inspectorin käyttö

Inspector-ikkuna on jaettu eri välilehtiin, joilla on eri työtiloja. Tällä hetkellä Inspector ja Console toimivat.

Inspector-välilehdellä on kolme saraketta. Vasemmalta oikealle:

  • HTML tree näyttää dokumentin solmut. Tämän avulla voit nähdä, lisätä tai muokata attribuutteja kaksoisnapsauttamalla tagin nimeä tai attribuuttia.
  • style inspector näyttää valitun elementin CSS-tyylit. Merkinnät tulevat elementin style-attribuutista, vastaavista tyylitiedoston säännöistä tai perittynä muilta elementeiltä. Tyylejä voi lisätä tai muokata napsauttamalla selektoria tai ominaisuutta, tai napsauttamalla tyhjää tilaa alapuolella.
  • extra column sisältää lisähyödyllisiä työkaluja:
    • Layout sisältää tietoa elementin box model -ominaisuuksista. Huomaa, että flex ja grid eivät vielä toimi.
    • Computed, joka sisältää kaikki CSS-computed values suhteellisten yksiköiden kaltaisten asioiden ratkaisemisen jälkeen.

Inspector

Console-välilehti sisältää JavaScript-konsolin, joka liittyy Servossa näytettävään verkkosivustoon. Virheet, varoitukset ja tiedot, joita verkkosivusto tuottaa, kirjataan tänne. Sitä voi käyttää myös JavaScript-koodin suorittamiseen suoraan verkkosivustolla, esimerkiksi dokumentin sisällön muuttamiseen tai sivun uudelleenlataamiseen:

document.write("Hello, Servo!")
location.reload()

DevTools-ominaisuuksien tuki on yhä kesken, ja se voi rikkoutua tulevissa Firefox-versioissa, jos viestintäprotokollaan tulee muutoksia.

DevToolsin kehittäminen

Lue täydellinen protokollakuvaus syvällistä katsausta varten tärkeistä käsitteistä.

  • Client: Frontend, joka sisältää eri työkalupaneelit (Inspector, Debugger, Console, …) ja lähettää pyyntöjä palvelimelle. Tällä hetkellä tämä on Firefoxin about:debugging-sivu.
  • Server: Selain, jota client tarkastelee. Vastaanottaa viestit ja välittää ne sopivalle actorille, jotta se voi vastata.
  • Actor: Koodi palvelimella, joka voi vaihtaa viestejä clientin kanssa.
  • Message: JSON-paketti, jota vaihdetaan palvelimen ja clientin välillä.
    • Clientin viesteissä on oltava to-kenttä actorin nimellä, jolle ne on suunnattu, ja type-kenttä, joka määrittää paketin tyypin.
    • Palvelimen viesteissä on oltava from-kenttä actorin nimellä, joka lähettää ne.
sequenceDiagram
    participant Client
    participant Server
    actor Actor1
    Client->>Server: {"to": "Actor1", "type": "SayHi"}
    Server-->>Actor1: {"to": "Actor1", "type": "SayHi"}
    Actor1-->>Server: {"from": "Actor1", "content": "hi!"}
    Server->>Client: {"from": "Actor1", "content": "hi!"}

Protokollaliikenteen näyttäminen

Siirry Protokollaliikenteen kaappaus ja käsittely -osioon hyödyllisempää työkalua lokianalyysiin varten.

Servo ↔ Firefox

Servo voi näyttää DevTools-palvelimelle lähetetyt ja sieltä vastaanotetut viestit. Ota käyttöön oikea lokitaso devtools-moduulille:

RUST_LOG="error,devtools=debug" ./mach run --devtools=6080

Tulosteessa lähetetyt viestit on etuliitteellä <- ja vastaanotetut viestit ilman etuliitettä. Tässä näemme, miten Servo lähettää alkuyhteyden tiedot ja Firefox vastaa yhteyspyynnöllä ja versionumerollaan.

[2025-11-07T11:37:35Z INFO  devtools] Connection established to 127.0.0.1:47496
[2025-11-07T11:37:35Z DEBUG devtools::protocol] <- {"from":"root","applicationType":"browser","traits":{"sources":false,"highlightable":true,"customHighlighters":true,"networkMonitor":true}}
[2025-11-07T11:37:35Z DEBUG devtools::protocol] {"type":"connect","frontendVersion":"144.0.2","to":"root"}
[2025-11-07T11:37:35Z DEBUG devtools::protocol] <- {"from":"root"}

Firefox ↔ Firefox

Paljon työtä Servon kehittäjätyökalujen parantamiseksi vaatii reverse-engineering-työtä Firefoxin toimivasta toteutuksesta. Yksi tehokkaimmista tavoista on tarkkailla onnistunutta sessiota Firefoxissa ja tallentaa kaksisuuntaista protokollaliikennettä palvelimen ja clientin välillä.

Ensimmäisellä ajolla

  1. Luo uusi Firefox-profiili komennolla firefox --createprofile devtools-testing.
  2. Käynnistä Firefox komennolla firefox --new-instance -P devtools-testing.
  3. Avaa about:config ja napsauta “Accept the Risk and Continue”.
  4. Muuta seuraavat asetukset:
# To see logs in the terminal window
browser.dom.window.dump.enabled = true
devtools.debugger.log = true
devtools.debugger.log.verbose = true
# To enable debugging
devtools.chrome.enabled = true
devtools.debugger.remote-enabled = true
# Optional, avoids having to confirm every time there is a connection
devtools.debugger.prompt-connection = false

Kun Firefox on konfiguroitu, se voidaan käynnistää terminaalista DevTools-palvelin päällä:

firefox --new-instance --start-debugger-server 6080 -P devtools-testing
# (on macOS you may need `/Applications/Firefox.app/Contents/MacOS/firefox`)

Tässä tapauksessa voit käyttää samaa Firefox-instanssia sekä clientina että palvelimena. “This Firefox” -vaihtoehtoa ei kuitenkaan suositella, koska se ei anna pääsyä välilehtiin ja viestit voivat olla erilaisia. Sen sijaan, käytitpä samaa vai eri instanssia, seuraa Yhdistäminen Servoon -osion ohjeita ohittaen ensimmäinen vaihe.

Terminaali-ikkuna sisältää nyt täydet debug-palvelinlokit; kopioi ne johonkin myöhempää analyysia varten.

Protokollaliikenteen kaappaus ja käsittely

Olemme nähneet yksinkertaisen tavan hankkia viestilokeja Servosta ja Firefoxista. Tämä kuitenkin monimutkaistuu nopeasti, kun halutaan vertailla lokia näiden välillä eri formaattien vuoksi tai suorittaa kyselyitä niihin. Prosessin helpottamiseen on pieni skripti: etc/devtools_parser.py.

Se perustuu Wireshark-verkkopakettianalysaattoriin; tarkemmin sen CLI-työkaluun tshark. Se on konfiguroitu lokittamaan paikallisverkossa lähetetyt paketit portissa, jossa DevTools-palvelin pyörii. Se voi lukea näiden pakettien payloadit, jotka ovat pieniä paloja JSON DevTools -protokollasta.

tshark on asennettava, jotta skripti toimii. Asenna se paketinhallinnalla tai hanki täydellinen Wireshark-julkaisu viralliselta sivustolta.

# Linux (Debian based)
sudo apt install tshark
# Linux (Arch based)
sudo pacman -S wireshark-cli
# Linux (Fedora)
sudo dnf install wireshark-cli
# MacOS (With homebrew):
brew install --cask wireshark
# Windows (With chocolatey):
choco install wireshark

Saatat joutua lisäämään käyttäjäsi wireshark-ryhmään rootless-kaappausten sallimiseksi. Käytä usermod -a -G wireshark $USER.

Varmista lopuksi Firefox-profiilin asettaminen debuggausta varten.

Sessio kaappaus

  1. Aja joko Servo tai Firefox DevTools-palvelin käytössä:
./mach run --devtools 6080
firefox --new-instance --start-debugger-server 6080 -P devtools-testing
  1. Toisessa terminaalissa käynnistä skripti capture-tilassa (-w) määrittäen saman portin kuin aiemmin:
./etc/devtools_parser.py -p 6080 -w capture.pcap
  1. Yhdistä about:debugging-sivulta seuraamalla samoja ohjeita.
  2. Suorita kaikki toiminnot, jotka haluat tallentaa.
  3. Paina Ctrl-C parseria ajavassa terminaalissa lopettaaksesi tallennuksen. Tämä tekee kaksi asiaa:
    • Tallentaa tulokset .pcap-tiedostoon, jonka määritit -w-lipulla. Tämä on Wiresharkin binääritiedostomuoto, mutta voimme lukea sen myöhemmin samalla työkalulla.
    • Tulostaa viestilokin. On kaksi tilaa: tavallinen, jossa viestit tulostetaan ystävällisessä muodossa, ja --json, joka tuottaa rivierotetun JSONin jokaisesta viestistä.
  4. Voit nyt sulkea Servon tai Firefoxin.

Kaappauksen lukeminen

On hyödyllistä tallentaa useita kaappauksia ja vertailla niitä myöhemmin. Vaikka tshark tallentaa ne oletuksena .pcap-muodossa, voimme käyttää samaa skriptiä parempaan tulostukseen. Tässä --json-valitsin on hyvin hyödyllinen, koska se mahdollistaa työkalujen kuten jq tai nushell käytön datan kyselyyn ja manipulointiin.

# Pretty print the messages
./etc/devtools_parser.py -r capture.pcap
# Save the capture in an NDJSON format
./etc/devtools_parser.py -r capture.pcap --json > capture.json
# Example of a query with jq to get unique message types
./etc/devtools_parser.py -r capture.pcap --json | jq -cs 'map({actor: (.from//.to) | gsub("[0-9]";""), type: .type} | select(.type != null)) | .[]' | sort -u

JSON-kaappaus voidaan tallentaa alusta asti komennolla ./etc/devtools_parser.py -w capture.pcap --json > capture.json.

Tässä on katkelma kaappauksen tulosteesta:

{"to": "root", "type": "getRoot"}
{"from": "root", "deviceActor": "device1", "performanceActor": "performance0", "preferenceActor": "preference2", "selected": 0}
{"to": "device1", "type": "getDescription"}
{"from": "device1", "value": {"apptype": "servo", "version": "0.0.1", "appbuildid": "20251106175140", "platformversion": "133.0", "brandName": "Servo"}}

Oppaat

Tämä osio sisältää muutamia oppaita yleisiin tehtäviin Servon parissa työskennellessä.

Verkkosisällön bugien korjaaminen

Servossa havaittavissa on pääasiassa kahta web-yhteensopivuusongelman luokkaa. Visuaaliset bugit johtuvat usein puuttuvista ominaisuuksista tai bugeista Servon CSS- ja layout-tuessa, kun taas interaktiivisuusongelmat ja rikkinäinen sisältö johtuvat usein bugeista tai puuttuvista ominaisuuksista Servon DOM- ja JavaScript-toteutuksessa.

Uusien ominaisuuksien lisääminen

Ominaisuuksien lisääminen ei ole hyvä tehtävä uudelle kontribuuttorille, koska web-moottorissa ominaisuuden täydellinen toteuttaminen vaatii usein pitkän sarjan muutoksia. Aloita lukemalla DOM-rajapinnan toteuttaminen.

Minimaalinen toistettava testitapaus

Jotta voit tunnistaa tarkalleen, mikä sivulla menee pieleen, on erittäin tärkeää osata luoda minimaalinen toistettava testitapaus. Vaikka et aikoisikaan korjata ongelmaa itse, testitapauksen tarjoaminen helpottaa muiden korjaustyötä huomattavasti ja säilyttää epäonnistumisen, vaikka alkuperäinen sivu muuttuisi. Testitapauksen luominen on melko systemaattista prosessia, jonka voi tehdä jopa hyvin uusi web-alustan kehittäjä. Minimaalisesti toistettava testitapaus on lähes aina ensimmäinen askel ongelman korjaamisessa, ja ne voidaan usein helposti muuntaa Web Platform Testeiksi.

Peruslähestymistapa

Peruslähestymistapa minimaalisen toistettavan testitapauksen luomiseen on poistaa vähitellen tarpeetonta sisältöä sivulta, kunnes jäljellä on vain ongelmallinen osa. Tämä tarkoittaa, että alkuperäinen layout-ongelma, DOM-virhe tai kaatuminen tapahtuu edelleen, vaikka lähdekoodi on paljon pienempi. On mahdollista, ettei virhe näytä tai toimi täsmälleen samalta testitapauksessa, mutta sen pitäisi silti tuottaa huonoja tuloksia verrattuna muihin selaimiin, kaatua tai tuottaa virheen. Tärkeä vaihe on vertailla tuloksia useampaan muuhun selainmoottoriin nähdäksesi, onko kyseessä oikeasti spesifikaatio-ongelma. On suositeltavaa ajaa testitapaus myös Chromessa, Firefoxissa ja WebKit-pohjaisessa selaimessa kuten Safarissa.

Minimointi

Luodaksesi minimaalisen toistettavan testitapauksen tarvitset ensin kopion sivusta tietokoneellesi. Chromessa tallenna ongelmallinen sivu valinnalla „Webpage, Complete.” Näin varmistat, että kaikki kuvat, CSS- ja JavaScript-tiedostot tallentuvat myös tietokoneellesi. Seuraavaksi lataa tallennettu HTML-tiedosto Chromessa ja Servossa varmistaaksesi, että sivu toimii edelleen ja bugi näkyy Servossa. Nyt on aika pienentää! Voit käyttää muutamia tekniikoita:

  • Etsi sivun osa, joka ei liity ongelmaan, kuten ylä- tai alatunniste. Poista ne ladatusta HTML-tiedostosta Web Inspectorissa, esimerkiksi korostamalla elementit ja painamalla Delete-näppäintä. Tallenna sivu uudelleen ja varmista, että bugi on edelleen olemassa lataamalla uudelleen tallennettu sivu. Jatka!
  • Jos ongelma on layout-ongelma, kokeile poistaa kaikki sivulla ladattu JavaScript. Jos JavaScriptin poistaminen poistaa ongelman, peru muutos ja kokeile poistaa muuta JavaScriptiä.
  • Kokeile poistaa viittaukset ulkoisiin tyylitiedostoihin. Jos tyylitiedoston poistaminen poistaa myös bugin, upota tyylitiedosto inline-muotoon ja poista vähitellen asiaankuulumattomia sääntöjä. Tarvittaessa voit käyttää tyyppistä binääristä eliminointia tyylisäännöille.
  • Korvaa ladatut kuvat yksinkertaisella image-tagilla, jossa on width ja height. Ulkoisen resurssin lataamatta jättäminen helpottaa testitapauksen ymmärtämistä huomattavasti.
  • Kun sivulla on vain muutama elementti jäljellä, kokeile poistaa class-, id- tai muita tarpeettomia attribuutteja.

Jatka pienentämistä, kunnes testitapaus on mahdollisimman pieni. Usein tämän aikana bugin luonne selviää, ja olet jo puolivälissä korjausta.

Lithium

Lithium on työkalu yllä kuvatun prosessin automatisointiin. Se toimii erityisen hyvin kaatumisten kanssa ja kestää ei-deterministiset bugit.

DOM-rajapinnan toteuttaminen

Osa 1: Web API:n perusasetukset

  1. Lue relevantti spesifikaatio.
  2. Lisää .webidl-tiedosto(t) tähän hakemistoon jokaiselle toteutettavalle rajapinnalle. Jos tiedosto on jo olemassa, lisää siihen puuttuvat osat.
  3. Jokaiselle rajapinnalle tämä luo traitin nimeltä {interface_name}Methods, johon pääsee käsiksi komennolla use crate::dom::bindings::codegen::Bindings::{interface_name}Binding.
  4. Käytä traitia:
    • Lisäämällä vastaavan structin #[dom_struct]-attribuutilla
    • Lisäämällä metodeja, joiden runko on todo!.
  5. Tässä vaiheessa structilla voi olla vain yksi jäsen: reflector_: Reflector,
    • Struct pitää dokumentoida linkillä sen rajapintaan spesifikaatiossa,
    • Trait-metodit pitää dokumentoida linkillä niiden määritelmiin rajapinnassa.
    • Esimerkkitulos.
  6. Palaa spesifikaatioon,
    • etsi structillesi lisäjäseniä.
    • Näitä kutsutaan yleensä „internal sloteiksi” (esimerkki), tai joksikin, mikä on „associated” rajapinnan kanssa (esimerkki).
  7. Lue DOM-rajapinnat.
  8. Käyttäen oppimaasi, jokaiselle rajapinnan internal slotille:
    • Lisää sopiva jäsen kohdassa 4 lisättyihin structeihin.
    • Jos tämä vaatii muiden structien tai enumien määrittelyä, niiden pitää derivoida JSTraceable ja MallocSizeOf (esimerkki).
    • Kaikki yllä lisätyt JSTraceable-structit, jotka sisältävät jäseniä, jotka täytyy rootata koska ne ovat joko JS-arvoja tai DOM-objekteja, pitää merkitä #[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]. Esimerkki, jossa lintti tarvitaan JSVal-jäsenen vuoksi.
    • Jos tällainen struct assignataan muuttujaan, tee impl js::gc::Rootable structille ja käytä rooted! rootataksesi muuttujan (esimerkki).
    • Kaikki tämä voidaan muuttaa myöhemmin, joten käytä parasta arvostelukykyäsi tässä vaiheessa.
    • Lisää metodeja rakentamiseen (älä sekoita Web API:n osana olevaa Constructor-metodia).
    • Esimerkkitulos.

Osa 2: Ensimmäisen luonnoksen kirjoittaminen

  1. Jokaiselle osan 1 kohdassa 3 mainitun bindings-traitin metodille:
    • Yleensä seuraa spesifikaation rakennetta: jos metodi kutsuu toista nimettyä algoritmia, toteuta se erillisenä structin yksityisenä metodina, jota trait-metodi kutsuu. Jos myöhemmin huomaat, että tätä yksityistä metodia voi käyttää muista structeista, tee siitä pub(crate).
    • Jokaiselle spesifikaation algoritmivaiheelle:
      • Kopioi rivi spesifikaatiosta.
      • Toteuta spesifikaatio koodissa (voi vaatia useamman rivin ja lisäkommentteja).
  2. Huom: tietyt asiat tarvitaan usein algoritmin osana:
    • JSContext tai CurrentRealm, jotka ovat toisensa poissulkevia, saadaan generoidun trait-metodin argumenttina tämän konfiguraatiotiedoston avulla
    • GlobalScope: saadaan self.global()-kutsulla dom_struct-structilla tai GlobalScope::from_current_realm-kutsulla
    • On parasta hakea ne mahdollisimman aikaisin, esimerkiksi trait-metodin toteutuksen alussa, ja välittää ne alaspäin ref:inä (useimmissa tapauksissa JSContext/CurrentRealm täytyy olla &mut-viitteen takana)
    • On suositeltavaa, että JSContext/CurrentRealm on ensimmäinen argumentti, sen jälkeen &GlobalScope tarvittaessa, ja muut tarvittavat argumentit perässä
  3. Tämän pitäisi antaa täydellinen ensimmäinen luonnos.

Osa 3: Testien ajaminen ja bugien korjaaminen

  1. Nyt on aika tunnistaa, mitä WPT-testiä ajetaan ensimmäistä luonnosta vasten. Löydät ne täältä.
  2. Testi voi epäonnistua, koska:
    • Koodissa on bugi. Nämä pitää korjata.
    • Testi käyttää muita API:ja, joita ei vielä tueta (yleensä ERROR).
  3. Bugit pitää korjata. Copilotista on tässä vähän hyötyä.
  4. Odotetut epäonnistumiset voidaan merkitä sellaisiksi tässä kuvatussa prosessissa.
  5. Tämä osa on valmis, kun odottamattomia testituloksia ei enää ole.
  6. Joskus reviewaajan neuvosta voit tehdä issuen ja kuvata epäonnistumisen, jota et voi korjata, merkitä testin epäonnistumiseksi ja jättää sen seuraavaan työvaiheeseen.

Osa 4: Refaktorointi ja lopullisen reviewn pyytäminen

  1. Voit pyytää reviewa milloin tahansa, jos olet jumissa, mutta nyt on aika katsoa koodia viimeisen kerran ja päättää, haluatko refaktoroida jotain.
  2. Jos olet tyytyväinen, nyt on aika pyytää lopullinen review.
  3. Onnittelut, toteuttamasi uusi Web API pitäisi pian yhdistää.

Virheiden diagnosointi

DOM-virheiden diagnosointi

Seuraavan kaltaiset virheviestit kertovat selvästi, että tiettyä DOM-rajapintaa ei ole vielä toteutettu Servossa:

[2024-08-16T01:56:15Z ERROR script::dom::bindings::error] Error at https://github.githubassets.com/assets/vendors-node_modules_github_mini-throttle_dist_index_js-node_modules_smoothscroll-polyfill_di-75db2e-686488490524.js:1:9976 AbortSignal is not defined

Seuraavan kaltaiset virheviestit eivät kuitenkaan anna paljon ohjausta:

[2024-08-16T01:58:25Z ERROR script::dom::bindings::error] Error at https://github.githubassets.com/assets/react-lib-7b7b5264f6c1.js:25:12596 e is undefined

Virheviestissä linkitetyn JS-tiedoston avaaminen näyttää usein minifioidun, obfusoidun JS-skriptin, jota on lähes mahdotonta lukea. Aloitetaan minifioidun koodin purkamisella. Varmista, että js-beautify-binääri on polussasi, tai asenna se komennolla:

npm install -g js-beautify

Aja ongelmasivu Servossa uudelleen sisäänrakennetulla minifiointipalautuksella käytössä:

./mach run https://github.com/servo/servo/activity --unminify-js

Tämä luo unminified-js-hakemiston Servo-repositorion juureen ja tallentaa automaattisesti minifioidut kopiot jokaisesta ulkoisesta JS-skriptistä, joka haetaan sivun elinkaaren aikana. Servo myös evaluoi skriptien minifioidut versiot, joten rivi- ja sarakenumerot virheviesteissä muuttuvat:

[2024-08-16T02:05:34Z ERROR script::dom::bindings::error] Error at https://github.githubassets.com/assets/react-lib-7b7b5264f6c1.js:3377:66 e is undefined

Löydät react-lib-7b7b5264f6c1.js-tiedoston hakemistosta ./unminified-js/github.githubassets.com/assets/, ja jos katsot riviä 3377, voit alkaa lukea ympäröivää koodia selvittääksesi (toivottavasti), mikä sivulla menee pieleen. Jos koodin tarkastelu ei riitä, Servo tukee myös minifioidun JS:n muokkaamista paikallisessa välimuistissa!

./mach run https://github.com/servo/servo/activity --local-script-source unminified-js

Kun --local-script-source-argumentti on käytössä, Servo etsii JS-tiedostoja annetusta hakemistosta ensin ennen kuin yrittää hakea niitä internetistä. Tämä mahdollistaa console.log(..)-lauseiden ja muiden hyödyllisten debuggaustekniikoiden lisäämisen ymmärtääksesi, mitä oikeat verkkosivut havaitsevat. Jos sinun täytyy palata alkuperäiseen sivulähteeseen, aja uudelleen --unminify-js-argumentilla korvataksesi ne uusilla minifioiduilla lähdekooditiedostoilla.

Vakaiden WPT-virheiden diagnosointi

FAIL-tuloksen diagnosointi

Aloita avaamalla testitiedoston .ini-tiedosto, joka sisältää odotetut epäonnistumiset. Se löytyy hakemistosta tests/wpt/meta/ rinnakkaisessa hakemistopuussa suhteessa tests/wpt/tests/-hakemistoon.

WPT-testikehys suppressoi suurimman osan .ini-tiedostossa odotetuiksi merkittyihin epäonnistumisiin liittyvästä tulosteesta.

Nähdäksesi kaikki testitiedoston epäonnistumiset, muuta ylätason [filename.html]-kohtaa niin, ettei se vastaa todellista tiedostonimeä; tämä saa kehyksen jättämään .ini-tiedoston kokonaan huomiotta.

Nähdäksesi lisätietoja yhdestä alitestistä, poista se .ini-tiedostosta ja aja testi uudelleen. Kehys näyttää epäonnistuvat testiväitteet sekä JS-stack tracen, kun epäonnistumiset tapahtuvat.

ERROR-tuloksen diagnosointi

ERROR-tulokset syntyvät, kun poikkeus heitetään ilman että sitä käsitellään. Testikehyksen stack tracen pitäisi näyttää alitesti, jossa käsittelemätön poikkeus havaittiin, sekä virheen tyyppi.

Jos poikkeus tulee Rustissa toteutetun API-metodin kutsumisesta, sinun täytyy löytää kyseinen metoditoteutus ja etsiä koodia, joka palauttaa vastaavan Error-variantin.

TIMEOUT-tuloksen diagnosointi

Testien timeout tapahtuu, kun async/promise-alitesti on määritelty mutta ei koskaan valmistu. Sinun täytyy tunnistaa kaksi asiaa:

  1. viimeinen testikoodi, joka suoritetaan onnistuneesti
  2. miksi seuraavaa koodia, joka pitäisi suorittaa, ei koskaan ajeta

Nopein tapa selvittää ensimmäinen tietopiste on lisätä console.log-lauseita todistamaan, että koodipolut suoritetaan.

Koodi, jota ei suoriteta, on yleensä:

  • tapahtumakäsittelijä (joko tapahtumaa ei koskaan laukaista, tai käsittelijässä on suodatuslogiikka, joka aktivoituu)
  • promise-käsittelijä (promisea ei koskaan resolvata/rejectata)
  • await-lause (promisea ei koskaan resolvata/rejectata)

Kussakin tapauksessa on hyödyllistä aloittaa koodista, jonka on tarkoitus laukaista puuttuva vaihe (esim. relevantti event.fire(..) Rust-koodissa), ja työskennellä taaksepäin selvittääksesi, miksi sitä ei koskaan suoriteta.

NOTRUN-tuloksen diagnosointi

Nämä epäonnistumiset tapahtuvat, kun async-alitesti on määritelty (esim. let some_test = async_test("frobbing the whatsit");) mutta sitä ei koskaan suoriteta (esim. some_test.step(() => ...)). Tämä tapahtuu yleensä, kun testitiedosto määrittää monta alitestiä ja yrittää ajaa ne peräkkäin, mutta kohtaa poikkeuksen tai timeoutin, joka estää jatkosuorituksen.

Nämä tulokset ovat yleensä oire jostain muusta ongelmasta, jonka testitiedosto paljastaa, ja näitä muita ongelmia pitäisi tutkia ensin.

Reftest-epäonnistumisen diagnosointi

Nähdäksesi visuaalisen esityksen eroista testitiedoston ja sen referenssitiedoston välillä, voit käyttää reftest-analysaattoria.

Voit nopeasti tarkastella reftestin ulkoasua ajamalla tiedoston suoraan (./mach run tests/wpt/tests/css/CSS2/some-file.html).

Jos tiedosto riippuu muista resursseista testisuitesta, sinun täytyy ehkä käynnistää WPT-web-palvelin ensin:

  • cd tests/wpt/tests; ./wpt serve
  • ./mach run http://localhost:8000/css/CSS2/some-file.html

Debuggerin käyttö Web Platform Testeissä

Liittääksesi debuggerin Servoon WPT-testiä ajaessasi, lisää --debugger-lippu ./mach test-wpt-komentoon. On kaksi toimintatapaa:

  1. Kun käytät servodriver-testikehystä (oletus), sinun täytyy ajaa komento debuggerin liittämiseksi toisessa terminaalissa. Esimerkkikomento annetaan ./mach test-wpt-komennon tulosteessa; testikehys odottaa sitten 30 sekuntia ennen suorituksen jatkamista.
  2. Kun käytät servo-testikehystä (--product=servo), debugger liitetään automaattisesti. Jatkaaksesi testikehyksen suoritusta, käytä debuggerin kehotteessa run-komentoa.

Satunnaisten WPT-virheiden diagnosointi

Yleisimmät satunnaisten epäonnistumisten lähteet Web Platform Testeissä ovat:

  • Ajoitus (esim. useita säikeitä ajetaan samanaikaisesti tai rinnakkain)
  • Käyttöjärjestelmävuorovaikutukset (esim. socket read/write verkkopyynnöissä)
  • Ei-deterministinen tehtävän valinta

Jokainen näistä voi muuttaa tapahtumien järjestystä Servossa ei-deterministisesti, paljastaen odottamatonta tai suunnittelematonta käyttäytymistä moottorissa.

Servossa tämä ei-determinismi ilmenee usein koodissa, joka:

  • käyttää mutexeja tai jaettua muistia kanavien sijaan.
  • jonottaa useita tehtäviä, jotka ajetaan yhdellä säikeellä mutta eri task source -lähteistä.
  • käyttää timeouteja havaitakseen, tapahtuiko tapahtuma.

Epäonnistumisen toistaminen

Vaikka tietylle satunnaiselle epäonnistumiselle voi keksiä teorian, on hyödyllistä näyttää ennen/jälkeen-epäonnistumisprosentti yritykselle tehdä korjaus.

Strategioita satunnaisten epäonnistumisten toistamiseen:

  • Aja --repeat-until-unexpected-valinnalla toistaaksesi testin, kunnes raportoidaan odottamaton tulos
  • Aja testi suuren kuormituksen alla (esim. aja puhdas release-käännös toisessa terminaalissa testiä toistuvasti ajaessasi)
  • Kokeile eri käännös- tai ajonaikaisia konfiguraatioita:
    • ./mach build --dev
    • ./mach build --release
    • ./mach build --debug-mozjs
    • ./mach test-wpt ... --binary-args=--force-ipc
    • ./mach test-wpt ... --binary-args=--multiprocess

Varmista, että test-wpt-komentosi käyttää samaa ajonaikaista binääriä kuin tekemäsi käännös!

Ongelman diagnosointi

Kun olet toistanut epäonnistumisen, sinun täytyy selvittää, mikä eroaa ajoissa, jotka raportoivat eri tuloksia. Testatessasi muutoksia pidä mielessä satunnaisten tulosten esiintymistiheys. Älä hyppää johtopäätöksiin odottamatta tarpeeksi kauan!

Aloita kommentoimalla pois niin paljon testistä kuin voit ja silti toistaen epäonnistumisen.

Kokeile lisätä console.log-kutsuja näyttääksesi eri tapahtumien järjestyksen, ja vertaa tulostetta yleiseen tapaukseen ja satunnaiseen tapaukseen. Saatat joutua asettamaan RUST_LOG-ympäristömuuttujan nähdäksesi relevanttien cratejen ja moduulien sisäiset Servo-jäljitet lokit.

Lisää eksplisiittisiä viiveitä testin osiin havaitaksesi, tapahtuvatko epäonnistumiset todennäköisemmin vai harvemmin:

  • siirrä osa testistä closureen, joka suoritetaan kiinteän viiveen jälkeen, kuten test_object.step_timeout(() => ..., 1000)
  • lisää viive verkkopyynnön vastaukseen

Layout-testien ongelmien diagnosointi

Jos testi varmistaa layout-ominaisuuksia (joko reftest tai testi, joka käyttää layout-API:ja kuten getBoundingClientRect(), scrollTop, offsetParent jne.), yleisiä satunnaisten tulosten lähteitä ovat:

  1. inkrementaalinen layout toimii väärin tietylle muutokselle, mutta tämä peittyy toisella async-operaatiolla, joka laukaisee lisälayoutin
  2. kuvakaappaus otetaan liian aikaisin/myöhään suhteessa johonkin muuhun muutokseen (esim. web-fontin lataus)

Tehdäksesi inkrementaalisen layoutin ongelmista näkyvämpiä, kokeile:

  • viivästää sivun muokkausta, kunnes kaikki muut sivupäivitykset on valmis (kokeile hyvin viivästettyä setTimeout-kutsua).
  • ajaa testi oikealla ikkunalla (--no-headless).
  • olla koskematta hiireen, kunnes sivun muokkaus tapahtuu, ja sitten muuttaa ikkunan kokoa.

Selvittääksesi, onko kuvakaappauksen ajoitus ongelma, käytä reftest-analysaattoria nähdäksesi testikehyksen vastaanottaman kuvakaappauksen. Jos se ei vastaa tulosta, jonka näet testitiedostoa ajaessasi, kuvakaappauksen ajoitus voi olla syypää.

Julkaisun tekeminen

Uuden Servo-version julkaiseminen

Servo-version nostaminen main-haarassa

Tällä hetkellä julkaisemme uuden Servo-version kuukausittain. Kuukauden lopussa valmistele haara viimeisimmän main-haaran pohjalta. Aja ./mach release X.Y.Z nostaaksesi versionumerot ja commitoi muutokset. Avaa pull request servossa yhdistääksesi haaran main-haaraan. Ihannetapauksessa pull request yhdistetään ajoissa kuukauden viimeisenä tai seuraavan kuukauden ensimmäisenä päivänä, jotta versionumeron nosto korreloi läheisesti blogipostin aikavälin kanssa. Blogipostin aikaväli on viimeisen kuukauden yöjulkaisun päähän asti (mukaan lukien). Versionoston pitäisi yhdistyä sen jälkeen, jotta kaikki blogipostissa mainitut muutokset sisältyvät julkaisuun.

Julkaisuhaaran luominen

Luo haara nimellä release/vX.Y.Z versionumeron nostaneen commitin pohjalta (main-haarassa). Jos yhdistämispäivä oli (merkittävästi) myöhemmin ja merkittäviä muutoksia tehtiin, julkaisuhaara voidaan perustaa myös aiempaan committiin ja versionumeron nosto backportata / tehdä uudelleen. Haara pitää pushata upstream servo -repositorioon.

Luonnosjulkaisun luominen testausta varten

Siirry servo-repositorion actions-välilehteen ja valitse Release-workflow. Valitse Run workflow -painike oikeasta yläkulmasta. Valitse haara release/vX.Y.Z (jonka juuri pushasit) workflow ajettavaksi. Jätä valintaruutu valitsematta, jotta julkaisu luodaan nightly-releases-repositorioon, koska se mahdollistaa ei-maintainerien auttamisen julkaisun testauksessa. Anna tag-arvoksi vX.Y.Z-beta1 luodaksesi esijulkaisun testausta varten. Klikkaa Run workflow. Luotu workflow ajaa ja luo (julkisen) julkaisun nightly-releases-repositorioon. Kun julkaisu on luotu, voit avata ketjun Zulipissa ja tehdä kutsun eri artefaktien testaamiseen.

Muutosten backporttaaminen julkaisuhaaraan

Jos julkaisuhaaraan täytyy soveltaa kriittisiä korjauksia (esim. manuaalisessa testauksessa havaittujen kaatumisten korjaus), PR pitää avata haaraa vastaan tavanomaisen review-prosessin mukaisesti. Tämän jälkeen pitäisi suorittaa uusi testauskierros, joten backporttaa vain välttämättömissä tapauksissa.

Julkaisun valmistelu

Kun testijulkaisu nightly-repositoriossa on testattu manuaalisesti, voit valmistella julkaisun ajamalla release-workflow uudelleen, mutta tällä kertaa valitsemalla ruudun luodaksesi julkaisun upstream servo -repositorioon. Tämä luo vain luonnosjulkaisun upstream servo -repositorioon. Kun artefaktit on ladattu julkaisuun, klikkaa muokkauspainiketta muokataksesi luonnosjulkaisua. Tagin pitäisi jo oikein olla vX.Y.Z. Muuta target arvosta main arvoon release/vX.Y.Z. Klikkaa Generate release notes ja kääri sitten generoidut release notes seuraavaan lohkoon:

<details>
  <summary>Generated Release notes</summary>
  Release notes here.
</details>

Ota yhteyttä macOS-artefaktin allekirjoittajaan ja pyydä häntä allekirjoittamaan julkaisu. Tämä voi kestää jonkin aikaa, joten se pitäisi tehdä pari päivää ennen suunniteltua julkaisupäivää.

Lopuksi lisää tavanomainen release notes -yhteenvetomme, jossa linkki blogipostiin ja yleisiin ongelmiin (katso aiempien release notes -kohtien esimerkit). Kun blogiposti on julkaistu, julkaisemme julkaisun.

Arkkitehtuuri

Servo on projekti uuden verkkoselaimen moottorin kehittämiseksi. Tavoitteemme on luoda arkkitehtuuri, joka hyödyntää rinnakkaisuutta monilla tasoilla samalla kun eliminoimme yleisiä virheiden ja tietoturva-aukkojen lähteitä, jotka liittyvät virheelliseen muistinhallintaan ja datakilpailuihin.

Koska C++ sopii huonosti näiden ongelmien estämiseen, Servo on kirjoitettu Rustilla, modernilla kielellä, joka on suunniteltu erityisesti Servon vaatimusten huomioon ottaen. Rust tarjoaa tehtäväpohjaisen rinnakkaisuusinfrastruktuurin ja vahvan tyyppijärjestelmän, joka pakottaa muistiturvallisuuden ja datan kilpailuttamattomuuden.

flowchart TB
    subgraph Web-sisällön prosessi
    ScriptA[Script-säie]-->PipelineA[Pipeline A]
    PipelineA
    PipelineA-->ImageA[Kuvavälimuisti]
    PipelineA-->FontA[Fonttivälimuisti]
    PipelineA-->LayoutA[Layout]
    LayoutA-->ImageA
    LayoutA-->FontA

    ScriptA[Script-säie]-->PipelineB[Pipeline B]
    PipelineB
    PipelineB-->ImageB[Kuvavälimuisti]
    PipelineB-->FontB[Fonttivälimuisti]
    PipelineB-->LayoutB[Layout]
    LayoutB-->ImageB
    LayoutB-->FontB
    end

    subgraph Upotusprosessi
    direction TB
    Embedder-->Constellation[Constellation-säie]
    Embedder<-->Renderer[Renderer]
    Constellation<-->Renderer
    Renderer-->WebRender
    Constellation-->SystemFont[Järjestelmäfonttivälimuisti]
    Constellation-->Resource[Resurssienhallinta]
    end

    Constellation<-->ScriptA
    FontA-->SystemFont
    PipelineA-->Renderer
    FontB-->SystemFont
    PipelineB-->Renderer

Tämä kaavio näyttää moniprosessisen Servon arkkitehtuurin, kun se ajetaan yhdellä web-sisällön prosessilla. Kun moniprosessitila on käytössä, jokainen script-säie ajaa omassa web-sisällön prosessissaan. Kun moniprosessitila on pois päältä, kaikki script-säieet ajavat upotusprosessissa. Jokaisella script-säieellä on joukko viestintäkanavia constellationin ja upotusprosessin upotus-API-osien kanssa. Yhtenäiset viivat osoittavat viestintäkanavia tai API-kutsuja.

Constellation, script-säieet ja pipeline:t

Jokaisella Servo-instanssilla on yksi constellation, joka hallinnoi web-sisällön prosesseja kaikille kehyksille kaikissa WebView-instansseissa. Web-sisällön prosessin script-säie voi hallita useita pipelineja, yhden jokaiselle <iframe>-elementille tai WebView-instanssin pääkehykselle. Script-säieen pipeline vastaa syötteen vastaanottamisesta, JavaScriptin suorittamisesta DOM:ia vasten, layoutin suorittamisesta, display listien rakentamisesta ja display listien lähettämisestä rendererille. Servo-instanssilla on yksi renderer koko instanssille, ja se hallinnoi useita WebRender-instansseja, jotka renderöivät erilaisiin RenderingContext-instansseihin (käytännössä OpenGL-konteksteihin alustan pinnoilla).

Pipeline koostuu kolmesta pääosasta:

  • Script: Scriptin ensisijainen tehtävä on luoda ja omistaa DOM ja suorittaa JavaScript-moottoria. Se vastaanottaa tapahtumia useista lähteistä, mukaan lukien navigointitapahtumat, ja reitittää ne tarpeen mukaan.
  • Layout: Layout käynnistyy aluksi samalla säieellä kuin Script, mutta se voi käyttää worker-säieitä sivun layoutin rinnakkaiseen suorittamiseen. Se laskee tyylit ja rakentaa kaksi päälayout-tietorakennetta, box tree ja fragment tree. Fragment treeä käytetään solmujen muuntamattomien sijaintien määrittämiseen ja siitä rakennetaan display list, joka lähetetään rendererille.
  • Renderer: Renderer (tunnetaan myös compositorina) välittää display listit WebRenderille, joka on sisällön rasterointi- ja näyttömoottori, jota sekä Servo että Firefox käyttävät. Se käyttää GPU:ta sivun lopullisen kuvan renderöintiin. Upottajan käyttöliittymäsäieellä ajettava renderer vastaanottaa myös ensimmäisenä syöte-tapahtumat, jotka yleensä lähetetään heti constellationille ja sitten script-säieelle käsittelyä varten. Jotkin tapahtumat, kuten scroll- ja kosketustapahtumat, voidaan käsitellä aluksi rendererissä responsiivisuuden vuoksi.

Rinnakkaisuus ja rinnakkaisuus (concurrency vs parallelism)

Rinnakkaisuus (concurrency) on tehtävien erottelua vuorottelevan suorituksen tarjoamiseksi. Rinnakkaisuus (parallelism) on useiden työpalojen samanaikaista suorittamista nopeuden lisäämiseksi. Näin hyödynnämme molempia:

  • Tehtäväpohjainen arkkitehtuuri: Järjestelmän pääkomponentit tulisi jakaa näyttelijöiksi (actors) eristetyillä keoilla, selkeillä raja-arvoilla virheille ja palautumiselle. Tämä kannustaa myös löyhään kytkentään koko järjestelmässä, mikä mahdollistaa komponenttien vaihtamisen kokeilu- ja tutkimustarkoituksiin.
  • Rinnakkainen renderöinti: Renderöinti on erillinen säie, joka on irrotettu layoutista responsiivisuuden ylläpitämiseksi. Renderer-säie hallitsee muistiaan manuaalisesti välttääkseen roskienkeruun (garbage collection) tauot.
  • Selektorien täsmäytys: Tämä on helposti rinnakkaistettava ongelma. Kuten Gecko, Servo tekee selektorien täsmäytyksen erillisessä läpäisyssä flow tree -rakentamisen sijaan, jotta se on helpompi rinnakkaistaa.
  • Rinnakkainen layout: Rakennamme flow tree:n rinnakkaisella DOM-käynnillä, joka kunnioittaa elementtien, kuten floatien, luomia peräkkäisiä riippuvuuksia.
  • Jäsennys: Olemme kirjoittaneet uuden HTML-parserin Rustilla, keskittyen sekä turvallisuuteen että spesifikaation noudattamiseen. Emme ole vielä lisänneet spekulatiivista jäsennystä tai rinnakkaisuutta parseriin.
  • Kuvien dekoodaus: Useiden kuvien dekoodaus rinnakkain on suoraviivaista.
  • Muiden resurssien dekoodaus: Tämä on todennäköisesti vähemmän tärkeää kuin kuvien dekoodaus, mutta kaikki, mitä sivun täytyy ladata, voidaan tehdä rinnakkain, esim. kokonaisten tyylitiedostojen jäsentäminen tai videoiden dekoodaus. Tyylitiedostot jäsennetään rinnakkain aina kun mahdollista.

Haasteet

  • Rinnakkaisuutta vastustavat kirjastot: Jotkin tarvitsemamme kolmannen osapuolen kirjastot eivät toimi hyvin monisäieisissä ympäristöissä. Fontit ovat olleet erityisen hankalia. Vaikka kirjastot olisivat teknisesti säie-turvallisia, säie-turvallisuus saavutetaan usein kirjaston laajuisella mutex-lukolla, mikä heikentää rinnakkaisuusmahdollisuuksiamme.
  • Liian monta säiettä: Jos heitämme maksimaalisen rinnakkaisuuden ja samanaikaisuuden kaikkeen, ylikuormitamme järjestelmän liian monella säieellä.
  • Liian monta avointa tiedostokahvaa: IPC-viestintä vaatii yleensä tiedostokahvan avaamisen järjestelmässä. Olemme törmänneet ongelmiin (#23910, #33672, #23905 tiedostokahvojen loppumiseen IPC-mekanismien liiallisen käytön vuoksi.

JavaScript ja DOM-bindings

Käytämme tällä hetkellä SpiderMonkeyta, vaikka vaihdettavat moottorit ovat pitkän aikavälin, matalan prioriteetin tavoite. Jokainen web-sisällön prosessi saa oman JavaScript-runtimeensa. DOM-bindings käyttävät natiivia JavaScript-moottorin API:a XPCOM:n sijaan, ja ne generoidaan automaattisesti WebIDL:n kautta.

Moniprosessiarkkitehtuuri

Kuten Chromiumissa ja WebKit2:ssa, tarkoituksemme on luoda luotettu upotusprosessi ja useita vähemmän luotettavia web-sisällön prosesseja. Korkean tason API on IPC-pohjainen, ei-IPC-toteutuksilla testausta ja yksiprosessikäyttötapauksia varten, vaikka odotetaankin, että useimmat vakavat käyttötapaukset käyttävät useita prosesseja. Moottoriprosessit käyttävät käyttöjärjestelmän sandboxointimahdollisuuksia rajoittaakseen pääsyä järjestelmäresursseihin.

Rustin tyyppijärjestelmä lisää myös merkittävän puolustuskerroksen muistiturvallisuusaukkoja vastaan. Tämä yksinään ei tee sandboxista vähemmän tärkeää turvallisen koodin, tyyppijärjestelmän virheiden ja kolmannen osapuolen/isäntäkirjastojen puolustamiseksi, mutta se pienentää Servon hyökkäyspintaa merkittävästi verrattuna muihin selainmoottoreihin. Lisäksi meillä on suorituskykyyn liittyviä huolia joistakin sandboxointitekniikoista (esimerkiksi kaikkien OpenGL-kutsujen välittäminen erilliseen prosessiin).

I/O ja resurssienhallinta

Verkkosivut riippuvat laajasta valikoimasta ulkoisia resursseja, joilla on monia hakemiseen ja dekoodaukseen liittyviä mekanismeja. Nämä resursseja välimuistitetaan useilla tasoilla — levylle, muistiin ja/tai dekoodatussa muodossa. Rinnakkaisessa selainympäristössä nämä resurssit on jaettava samanaikaisille worker-säieille.

Perinteisesti selaimet ovat olleet yksisäieisiä, suorittaen I/O:n “pääsäieellä”, jossa suurin osa laskennasta tapahtuu. Tämä johtaa viiveongelmiin. Servossa ei ole “pääsäiettä”, ja kaikkien ulkoisten resurssien latauksen hoitaa yksi resource manager -tehtävä.

Selaimilla on monia välimuisteja, ja Servon tehtäväpohjainen arkkitehtuuri tarkoittaa, että niitä on todennäköisesti enemmän kuin olemassa olevissa selainmoottoreissa (esim. meillä voi olla sekä globaali tehtäväpohjainen välimuisti että tehtäväkohtainen välimuisti, joka tallentaa tuloksia globaalista välimuistista säästääkseen schedulerin kierroksen). Servolla tulisi olla yhtenäinen välimuistitarina, säädettävillä välimuisteilla, jotka toimivat hyvin vähämuistisissa ympäristöissä.

Viitteet

Tärkeää tutkimusta ja kertynyttä tietoa selaimen toteutuksesta, rinnakkaisesta layoutista jne.:

Projektin rakenne

  • components
    • bluetooth — Bluetooth-säieen toteutus.
    • canvas — 2D- ja WebGL-canvasien maalaussäieiden toteutus.
    • compositing — Integraatio OS:n ikkunointi/renderöinti- ja tapahtumasilmukkaan.
    • constellation — Resurssien hallinta ylätason selauskontekstille (ts. välilehdelle).
    • devtools — Prosessin sisäinen palvelin selaininstanssien manipulointiin etä-Firefox-kehittäjätyökaluasiakkaan kautta.
    • fonts — Koodi fonttien ja tekstin muotoilun käsittelyyn.
    • layout — Muuntaa sivun sisällön sijoitetuiksi, tyylitetyiksi laatikoiksi ja välittää tuloksen rendererille.
    • layout_thread — Ajaa layout-säieitä, viestii script-säieen kanssa ja kutsuu layout-cratea layoutin suorittamiseksi.
    • msg — Jaetut API:t viestintään tiettyjen säieiden ja cratejen välillä.
    • net — Verkkoprotokollien toteutukset sekä tilan ja resurssien hallinta (välimuisti, evästeet jne.).
    • plugins — Syntaksilaajennukset, mukautetut attribuutit ja lintit.
    • profile — Muistin ja ajan profilointityökalut.
    • script — DOM:n toteutus (natiivi Rust-koodi ja bindings SpiderMonkeyhin).
    • script_bindings - Tukikoodi ja WebIDL-tiedostoista generoidut bindings. Bindings koostuvat trait:eista, jotka edustavat WebIDL-rajapintoja, ja liimakoodista SpiderMonkey JavaScript -moottorille. Varsinaiset trait-toteutukset sijaitsevat script-crate:ssa. Nämä on jaettu kahteen crateen inkrementaalisten käännösten nopeuttamiseksi.
    • script_layout_interface — API, jonka script-crate tarjoaa layout-cratelle.
    • selectors — CSS-selektorien täsmäytys.
    • servo — Entry pointit servo-sovellukselle ja libservo-upotuskirjastolle.
    • shared — Jaetut trait:t/koodi, joita useat komponentit käyttävät ilman riippuvuutta pääcrateen käännösnopeussyistä.
    • style — API:t CSS:n jäsentämiseen ja tyylitiedostojen ja tyylitettyjen elementtien käsittelyyn.
    • util — Sekalaisia apumetodeja ja -tyyppejä, joita käytetään laajasti projektissa.
    • webdriver_server — Prosessin sisäinen palvelin selaininstanssien manipulointiin WebDriver-asiakkaan kautta.
    • webgpu — WebGPU API:n säieiden toteutus.
  • etc — Hyödyllisiä työkaluja ja skriptejä kehittäjille.
  • ports
    • servoshell — Esimerkkiselain, joka käyttää servoa.
  • python
    • mach — Komentorivityökalu kehittäjätehtävien helpottamiseen.
    • servo — Servo-spesifisten mach-komentojen toteutukset.
    • tidy — Python-paketti koodilinteistä, jotka ajetaan automaattisesti ennen muutosten yhdistämistä.
  • resources — Ajonaikaiset tiedostot. Ne täytyy jotenkin sisällyttää, kun binäärikäännöksiä jaetaan.
  • support
    • android — Kirjastot, jotka vaativat erityiskäsittelyä Android-alustoille kääntäessä
  • target
    • debug./mach build --debug -komennolla generoidut käännösartefaktit.
    • doc — Dokumentaatio generoidaan tänne rustdoc-työkalulla ajettaessa ./mach doc
    • release./mach build --release -komennolla generoidut käännösartefaktit.
  • tests
    • dromaeo — Harness Dromaeo-testisarjan automaattiseen ajoon.
    • html — Manuaaliset testit ja kokeilut.
    • jquery — Harness jQuery-testisarjan automaattiseen ajoon.
    • power — Työkalut virrankulutuksen mittaamiseen.
    • unit — Yksikkötestit rustc:n sisäänrakennetulla test harnessilla.
    • wpt — W3C web-platform-tests ja csswg-tests työkaluineen niiden ajamiseen ja odotettuihin epäonnistumisiin.

Repositoriot

Servo-projekti ylläpitää useita repositorioita, jotka joko julkaistaan Servosta riippumatta tai on forkattu upstream-projektista.

Laajasti käytetty Rust-ekosysteemissä

Forkit

  • mozjs: Servon fork SpiderMonkeysta ja Rust-bindingsista
  • stylo: Servon CSS-toteutus säännöllisellä synkronoinnilla upstream-versioon Gecko-repositoriossa
  • webrender: Firefoxin WebRenderin fork pienillä Servo-spesifisillä muutoksilla

Servo-sisäiset

  • book: Tämä kirja!
  • ci-runners: Skriptit ja työkalut Servon CI:lle (continuous integration)
  • malloc_size_of: Arvojen ajonaikaisen koon mittaus
  • media: Mediat backend, jota Servo käyttää, tällä hetkellä vain GStreamer
  • servo: Päärepositorio Servo web platform -moottorille
  • surfman: Matalan tason cross-platform Rust-kirjasto graafisten pintojen hallintaan
  • wpt: Servon fork Web Platform Testsistä

Kokeelliset web-alustan ominaisuudet

Tämä on luettelo web-alustan ominaisuuksista, joilla on osittainen toteutus Servossa ja jotka on rajattu valinnaisen pref-asetuksen taakse.

Seuraavat ominaisuudet otetaan käyttöön kokeellisella renderöintitilalla tai --enable-experimental-web-platform-features -lipulla.

OminaisuusSeuranta-issuePref
CSS Font Loading API#29376dom_fontface_enabled
CSS Grid#34479layout_grid_enabled
CSS Multicol#22397layout_columns_enabled
Clipboard API#36084dom_async_clipboard_enabled
Document.execCommand#25005dom_exec_command_enabled
IndexedDB#6963dom_indexeddb_enabled
IntersectionObserver#35767dom_intersection_observer_enabled
Navigator.registerProtocolHandler()#40615dom_navigator_protocol_handlers_enabled
Notification API#34841dom_notification_enabled
OffscreenCanvas#34111dom_offscreen_canvas_enabled
Permissions API#31235dom_permissions_enabled
Sanitizer API#43948dom_sanitizer_enabled
Storage API#43976dom_storage_manager_api_enabled
Variable fonts#38800layout_variable_fonts_enabled
WebGL2#41394dom_webgl2_enabled
WebGPU#24706dom_webgpu_enabled

Seuraavat ominaisuudet ovat oletuksena pois päältä, mutta ne voi kytkeä päälle komentorivillä (esim. --pref dom_webgpu_enabled).

OminaisuusSeuranta-issuePref
Adopted Stylesheet#38132dom_adoptedstylesheet_enabled
CSS writing modes#2560layout_writing_mode_enabled
CookieStore#37674dom_cookiestore_enabled
Credential Management API#38788dom_credential_management_enabled
File and Directory Entries API#45653dom_entries_api_enabled
Geolocation API#38903dom_geolocation_enabled
Largest Contentful Paint#42000largest_contentful_paint_enabled
Media Capture and Streams API#26861dom_canvas_capture_enabled
Screen Wake Lock API#43615dom_wakelock_enabled
ServiceWorker#36538dom_serviceworker_enabled
VisualViewport#41341dom_visual_viewport_enabled
Web Animations#36950dom_web_animations_enabled
WebRTC#41396dom_webrtc_enabled
WebRTC Transceiver#41396dom_webrtc_transceiver_enabled
WebVTT#22312dom_webvtt_enabled
WebXR Layers#27468dom_webxr_layers_enabled

Käytössä olevat web-alustan ominaisuudet

Tämä on luettelo web-alustan ominaisuuksista, joiden toteutus on riittävän valmis oletuskäyttöön. Ne voidaan silti poistaa käytöstä valinnaisella pref-asetuksella (esim. --pref dom_webgpu_enabled=false).

OminaisuusSeuranta-issuePref
AbortController#34866dom_abort_controller_enabled
Gamepad API#10977dom_gamepad_enabled
MutationObserver#6633dom_mutation_observer_enabled
ResizeObserver#39790dom_resize_observer_enabled
SharedWorker#7458dom_sharedworker_enabled
WebCrypto API#40687dom_crypto_subtle_enabled

Script

Tämän dokumentin pitäisi tuoda sinut ajan tasalle Script Threadistä, rootingista ja Gc:stä. Se ei millään tavoin ole täydellinen.

Servo on ainutlaatuinen siinä, että se käyttää roskienkeruuta (garbage collection) joihinkin asioihin, jotka eivät ole ilmeisiä. Esimerkiksi jokainen DOM-objekti (struct, jossa on #[dom_struct]) on SpiderMonkeyn roskienkeruun omistama. Tämä edellyttää, että moottorin API:t, jotka vuorovaikuttavat näiden objektien kanssa, ovat turvallisia, kun roskienkeruu voi tapahtua monissa ohjelman kohdissa. Vaikka roskienkeruujärjestelmä (GC) on monimutkainen ja sillä on useita tiloja, voimme olettaa, että aina kun GC ajetaan, Rust-ohjelma ei aja.

Rooting ja rooted-tyypit

Tärkeä osa roskienkeruuta on objektigraafin juurten määrittäminen. Juuri kertoo roskienkeruulle kaksi asiaa:

  1. Älä poista mitään arvoa, johon tästä juuresta on transitiivisesti pääsy.
  2. Jos roskienkeruu siirtää tämän juuritetun arvon muistissa, kaikki osoittimet tähän arvoon päivitetään.

Servon koodissa juuret luodaan automaattisesti käyttämällä DomRoot<T>, Root<T> ja Rooted<T> -tyyppejä. Katso moduulin dokumentaatio lisätietoja eroista. Nämä tyypit toteuttavat Deref:in, joten voimme käsitellä niitä tavallisina viitteinä.

Seuraavat esimerkit sisältävät yksinkertaistuksia oikeista Servo-koodimalleista helpottamaan ymmärtämistä. Oletetaan seuraava koodi, joka määrittelee Kittens-tyypin, joka sisältää osoittimia Cat-tyyppeihin, ja kaikki nämä ovat roskienkeruun omistamia:

#![allow(unused)]
fn main() {
#[dom_struct]
struct Kittens {
    children: Vec<Dom<Cat>>
}

fn play_with_kittens(kittens: &Kittens) {
    let children = & cats.children;
    play_with(children)
}
}

Ilman rootingia GC voisi keskeyttää ohjelman sen jälkeen, kun olemme saaneet viitteen children:iin, siirtää Kittens-structin ympäriinsä ja meillä olisi virheellinen pääsy leikkiessämme niiden kanssa! Tämä pätee, vaikka Kittens olisi juuritettu jollakin muulla mekanismilla aiemmin kutsuketjussa. Muista, että roskienkeruu ei voi muuttaa paikallisen muuttujamme ‘children’ osoittimia, jos se ei tiedä niistä.

Siksi meidän täytyy muuttaa koodi muotoon

#![allow(unused)]
fn main() {
#[dom_struct]
struct Kittens {
    children: Vec<Dom<Cat>>
}

fn play_with_kittens(cats: &Kittens) {
    let children: Vec<DomRoot<Cat>> = cats.children.iter().map(|cat| cat.as_rooted()).collect();
    play_with(children)
}
}

Oletamme, että kissaviitteen hankkiminen ja juurittaminen on yksi atomitoiminto. Juuren poisto (käänteinen operaatio) tapahtuu automaattisesti DomRoot<T>:n Drop-toteutuksen kautta.

Varmistaakseen, että web API -toteutukset Servossa ovat turvallisia, moottorikoodi on konservatiivinen ja palauttaa yleensä juuritettyjä tyyppejä (kuten DomRoot<Node>). Tämä on hyväksyttävä kompromissi turvallisuuden ja suorituskyvyn välillä, koska juurittaminen ja juuren poisto ovat tehokkaita; ne käytännössä vain push:avat ja pop:avat vektorin elementtejä.

Crown ja CanGc

On helppoa unohtaa juurittamiseen liittyvät huolenaiheet toteuttaessa uusia API:ja. Crown on vastaus (jos työskentelet Script-asioissa, sinun tulisi ajaa ./mach build --use-crown varmistaaksesi, että se tarkistaa asiat). Pohjimmiltaan Crown vain tarkistaa, ettei juurittamista ole unohdettu, ja joskus näet koodissa tiettyjä linttejä, jotka viittaavat crown:iin, kuten #[cfg_attr(crown, allow(crown::unrooted_must_root))]. Nämä käytännössä poistavat crown-tarkistuksen käytöstä ja niitä tulisi käyttää vain hyvin erityisissä olosuhteissa.

Mutta on toinenkin palanen palapelissä, joka liittyy sivuttain. Vaikka juurittaminen antaa meille tavan pakottaa GC:n käyttäytymään kunnolla Rust-osoittimiemme kanssa, meillä on myös Rust RefCell:it. Vaikka näiden osoittimet käsitellään nyt oikein GC:n toimesta, GC:n täytyy lainata RefCell:iä testatakseen saavutettavuutta ja ohjatakseen osoitinta uudelleen. Mutta voimme lainata RefCell:iä vain ei samanaikaisesti. Mitä tapahtuu, jos GC keskeyttää ohjelman, kun meillä on cell-laina aktiivisena?

#![allow(unused)]
fn main() {
struct CatCarrier {
    cat: RefCell<Dom<Cat>>,
}

fn some_function(carrier: &CatCarrier) {
    let mutable_cat = carrier.inner_cat.borrow_mut().as_rooted();
    play_with_cat_mutably(mutable_cat);
    cleanup_playspace();
}
}

Oletetaan nyt, että cleanup_playspace voi kutsua GC:tä. Silloin voi tapahtua, että

  • Pidämme mutable-lainaa kissalle carrier:in sisällä.
  • Siivoamme kissan jälkeen.
  • GC keskeyttää ja yrittää jäljittää juuritetun mutable_cat:in lainaamalla RefCell:iä.
  • Panikoi, koska RefCell:iä voi lainata mutably vain kerran!

Kutsumme tätä borrow hazard -tilanteeksi. Estääksemme tämän, meillä on seuraava ratkaisu.

#![allow(unused)]
fn main() {
fn some_function(carrier: &CatCarrier, can_gc: CanGc) {
    {
        let mutable_cat = carrier.inner_cat.borrow_mut().as_rooted();
        play_with_cat_mutably(mutable_cat);
    }
    cleanup_playspace(can_gc);
}
}

Nyt tämä esimerkki ei ole täydellinen ja on täällä vain seuraavan pointin havainnollistamiseksi. CanGC (joka on triviaali tyyppi ja helposti kopioitavissa) otettiin käyttöön, ei muodollisena borrow hazard -estona vaan muistutuksena ohjelmoijalle. Se sanoo: Ole varovainen, jos haluat mutable-lainan, ettei se ylitä funktion rajoja, joka ottaa CanGc:n.

Lisätietoja siitä, miten borrow hazard -tilanteet tunnistetaan ja käsitellään, on täällä. Haluamme käyttää tätä vain motivoivana esimerkkinä seuraavalle osiolle &JSContext ja &mut JSContext.

Käynnissä oleva esimerkkimme näyttäisi sitten suunnilleen tältä:

#![allow(unused)]
fn main() {
fn some_function(cats: &Kittens, can_gc: CanGc) {
    let children: Vec<DomRoot<Cat>> = cats.children.iter().map(|cat| cat.as_rooted()).collect();
    play_with_cats(children);
    cleanup_after_cats(children, can_gc);
}
}

Tarkka käsitys on, että voimme olla varmoja, ettei GC tapahdu, kun kutsumme play_with_cats:ia, mutta GC voi tapahtua, kun kutsumme cleanup_after_cats:ia. Syy siihen, miksi jotkin metodit voivat aiheuttaa GC:n ja jotkin eivät, on syvällä SpiderMonkey-yhteyksissä servoon, eikä se yleensä ole ilmeistä.

Mitä tämä tarkoittaa sinulle? Pohjimmiltaan, jos kutsumasi metodi tarvitsee CanGc:n, metodisi tulisi käyttää CanGc:ä, jotta kaikki voivat muistaa olla varovaisia borrow hazard -tilanteiden suhteen koodin ympärillä.

JSContext, &mut JSContext

Mutta kuten näet, tämä voi olla vaarallista. Entä jos joku unohti käyttää CanGc-attribuuttia funktiokutsussaan, mutta itse asiassa kutsuu GC:tä jossain syvällä kutsupinossa? Silloin loimme borrow hazard -tilanteen, joka johtaa kaatumisiin, joita emme ehkä ymmärrä. Ratkaistaksemme tämän otamme käyttöön JSContext:in ja &mut JSContext:in. Toisin kuin CanGc, nämä tyypit käyttävät Rust-kääntäjän lainaus-sääntöjä ja SpiderMonkey API -wrappeja varmistaakseen, että mikä tahansa API, joka voi käynnistää roskienkeruun, vaatii &mut JSContext -argumentin. Koska nämä arvot ovat uniikkeja, kutsujien täytyy välittää ne. Vastaavasti mikä tahansa API-funktio, joka ei voi GC:ttä mutta tarvitsee silti pääsyn JS-kontekstiin, ottaa vain &JSContext -argumentin.

On myös NoGc-tyyppi, joka voidaan konstruoida JSContext:ista. Koska tämä tyyppi lainaa &mut JSContext:ia, se tekee mahdottomaksi kutsua mitään koodia, joka vaatii &mut JSContext -argumentin (ts. voi käynnistää GC-operaation), kun NoGc-arvo on olemassa.

Huomautus: Tällä hetkellä Servo-koodikanta muuttuu aiemmasta CanGc-lähestymistavasta JSContext-lähestymistapaan. Saatat nähdä molempia koodikannassa sekä useita escape hatch -ratkaisuja.

Rootingin suorituskyky

#![allow(unused)]
fn main() {
#[dom_struct]
struct Kittens {
    children: Vec<Cat>
}

impl Kittens {
    fn children(&self) -> Vec<DomRoot<Cat>> {
        self.children.iter().map(|cat| cat.as_rooted()).collect()
    }
}

fn some_function(cats: &Kittens) {
    let happy = cats.children().iter().any(|cat| cat.is_purring());
    if happy {
        println!("Happy cat found!");
    }
}
}

Tämä API kissojen kanssa vuorovaikutukseen on turvallisesti juuritettu, mutta kun lapsia on paljon, juurittaminen ja juuren poisto voivat kasaantua. Voimme käyttää JSContext-tyyppejä varmistaaksemme, ettei rooting-sääntöjä rikkova koodi käynnistä GC-operaatioita, jotka voisivat havaita nämä rikkomukset.

Koska &mut on uniikki lainaus, voimme esitellä uuden tyypin:

#![allow(unused)]
fn main() {
struct UnrootedDom<'a, T> {
    inner_cat: Dom<T>,
    js_context: &mut 'a JSContext,
}
}

Sitten aina kun meillä on käsissä UnrootedDom, tiedämme, että niin kauan kuin se elää, emme voi kutsua mitään metodeja, joilla on GC. Seuraava on virheellistä koodia.

#![allow(unused)]
fn main() {
#[dom_struct]
struct Kittens {
    children: Vec<UnrootedDom<'_, Cat>>,
}

fn make_cats(cx: &mut JSContext) -> Kittens {}

fn play_with_cat(cx: &JSContext, cat: &Cat) {}

fn cleanup_after_cats(cx: &mut JSContext, cat: &Cat) {}

fn some_function(cx: &mut JSContext) {
    let cats = make_cats(cx);
    for cat in cats.children {
        play_with_cat(cx, cat.inner_cat)
    }
    for cat in cats.children {
        cleanup_after_cats(cx, cat)
    }
}
}

Saamme kääntäjävirheen, että cx on lainattu mutably kerran ‘make_cats’:issa ja kerran ‘cleanup_after_cats’:issa. Mutta huomaa, että kutsu ‘play_with_cat’:iin on täysin ok.

Tämä on ‘UnrootedDomNode’:in ja vastaavien ‘Unrooted’-metodien ydin. Jotkut näistä voivat myös ottaa ‘NoGC’-argumentin

#![allow(unused)]
fn main() {
fn play_with_cats<'a>(no_gc: &'a NoGC, cat: Cat) {}

fn some_function(cx: &mut JSContext, cat: &Cat) {
    play_with_cats(cx.no_gc(), cat);
}
}

Lisätietoja

TODO:

SpiderMonkey

Servon SpiderMonkey-integraation nykytila ja näkymät: https://github.com/gterzian/spidermonkey_servo

Script Thread

DOM Bindings

DOM-bindings ovat WebIDL -rajapintojen toteutuksia natiivissa Rust-koodissa. Koodigeneraattori tuottaa liimakoodia, joka altistaa nämä natiivi Rust -toteutukset JavaScriptille SpiderMonkey API:n kautta. WebIDL-tiedostot sijaitsevat components/script_bindings/webidls/ -hakemistossa. Nämä tiedostot sisältävät kunkin rajapinnan määrittelyn, mukaan lukien niiden nimet, attribuutit ja metodit. Näiden rajapintojen Rust-toteutukset sijaitsevat components/script/dom -hakemistossa. Jokainen Rust-toteutus on erityinen Rust struct, joka sisältää kunkin DOM-objektin tilan.

Layout Wrappers

JavaScript ajaa yhdellä säieellä, eikä DOM-rajapinnat ole säie-turvallisia. Layoutin täytyy päästä käsiksi DOM:iin, mutta sen odotetaan ajavan eri säieillä. Teoriassa tämä voi toimia, kunhan säie ei yritä lukea tai mutatoida DOM-objektia, kun toinen säie mutatoi samaa objektia. Yritämme tehdä tästä unsafety-osiosta helpommin hallittavan rajoittamalla, miten layout voi käyttää DOM-objekteja wrapper struct:in kautta, joka altistaa rajoitetun, mutta yhteensopivan, toiminnallisuuden.

On kaksi käyttömallia:

  1. Layout olettaa itse, että vain yksi säie “omistaa” DOM-wrapperin jokaiselle solmulle, joten DOM:iin kirjoittaminen pitäisi olla turvallista. Solmun lapset voidaan käsitellä muilla säieillä samanaikaisesti. Toisaalta vanhempaan pääsy on erittäin vaarallista, koska toinen säie voi kirjoittaa ja lukea vanhempasolmua.
  2. stylo ja selectors olettavat, että solmuihin pääsee mistä tahansa säieestä, mutta vain yksi säie kirjoittaa solmuun kerrallaan. Tämä tarkoittaa, että vanhempiin ja lapsiin pääsy on turvallista, mutta solmuun kirjoittaminen on erittäin vaarallista.

Lisäksi layout riippuu script:ista, joten DOM-instanssien välittäminen suoraan script:ista layout:iin ei ole mahdollista, muuten meillä olisi riippuvuussykli. Sen sijaan layout-api altistaa trait-pohjaisen rajapinnan ja script toteuttaa sen. Tämä mahdollistaa Layout-rajapinnan käsittelemään solmuja suoraan.

Neljä trait:ia, joita altistamme, ovat:

  • LayoutNode: Tämä on perusrajapinta DOM-solmulle, jota käytetään layoutissa.
  • DangerousStyleNode: Tämä on rajapinta, joka toteuttaa stylo- ja selectors-trait:t solmujen kanssa vuorovaikutukseen. Tämä voidaan luoda LayoutNode:sta kutsumalla unsafe-metodia LayoutNode::dangerous_style_node(). Yleensä näitä solmuja ei tulisi käyttää layout-koodissa, ellei niitä välitetä suoraan stylo- tai selectors-kutsuun.
  • LayoutElement: Tämä on perusrajapinta DOM-elementille, jota käytetään layoutissa.
  • DangerousStyleElement: Tämä on rajapinta, joka toteuttaa stylo- ja selectors-trait:t elementtien kanssa vuorovaikutukseen. Tämä voidaan luoda LayoutElement:ista kutsumalla unsafe-metodia LayoutElement::dangerous_style_element(). Yleensä näitä elementtejä ei tulisi käyttää layout-koodissa, ellei niitä välitetä suoraan stylo- tai selectors-kutsuun.

script toteuttaa nämä trait:t tyypeillä ServoLayoutNode, ServoDangerousStyleNode, ServoLayoutElement ja ServoDangerousLayoutElement. Lisäksi script altistaa kaksi muuta struct:ia, jotka toteuttavat stylo-trait:t: ServoDangerousStyleDocument ja ServoDangerousStyleShadowRoot.

Säännöt Layout Wrappers -käytölle

  • Yksinkertaisuuden ja nopeampien käännösaikojen vuoksi trait-määrittelyissä (LayoutNode ja LayoutElement) ei tulisi olla oletusmetodeja. Kaikki toteutuskoodi tulisi olla script:issa.
  • Layout ei saisi käyttää DangerousStyleNode:a ja DangerousStyleElement:iä, ellei se kutsu stylo:on tai selectors:iin. Tällä hetkellä on muutamia poikkeuksia, mutta ne poistetaan vähitellen.
  • Layout ei saisi luottaa metodeihin, jotka on määritelty vain ServoLayoutNode:ssa ja ServoLayoutElement:issa. Sen sijaan uutta toiminnallisuutta tulisi lisätä LayoutNode- tai LayoutElement-trait:eihin ja sitten toteuttaa ServoLayoutNode:ssa tai ServoLayoutElement:issa. Tämä mahdollistaa TrustedNodeAddress:in poistamisen tulevaisuudessa ja LayoutNode:ien välittämisen suoraan layoutille, poistaen unsafe-koodin lähteen.

Microtasks

HTML-spesifikaation mukaan microtask on: “arkikielinen tapa viitata tehtävään, joka luotiin queue a microtask algorithm -algoritmin kautta” (lähde). Jokaisella event-loopilla — eli window, worker tai worklet — on oma microtask-jononsa. Jonoon lisätyt tehtävät ajetaan osana perform a microtask checkpoint -algoritmia, jota kutsutaan useista paikoista, pääasiallisesti tehtävän ajamisen jälkeen tehtäväjonosta, joka ei ole microtask-jono, ja jokainen kutsu tähän algoritmiin tyhjentää microtask-jonon — ajamalla kaikki siihen asti jonoon lisätyt tehtävät (ilman uudelleensisääntuloisuutta).

Microtask-jono Servossa

MicroTaskQueue on suoraviivainen spesifikaatioon perustuva toteutus: tehtävälista ja boolean uudelleensisääntuloisuuden estämiseksi checkpointissa. Yksi luodaan jokaiselle runtime:lle, mikä vastaa spesifikaatiota, koska runtime luodaan per event-loop. Window event-loopille, joka voi sisältää useita window-objekteja, jono jaetaan kaikkien sen sisältämien GlobalScope:ien kesken. Dedicated workerit käyttävät child-runtime:a, mutta sillä on silti oma microtask-jononsa.

Microtask-jonotus

Tehtävä voidaan jonottaa microtask-jonoon sekä Rustista että JS-moottorista.

  • JS:stä: JS-moottori kutsuu enqueue_promise_job aina, kun sen täytyy jonottaa microtask promise-käsittelijöiden kutsumiseksi. Tämä callback-mekanismi asetetaan kerran per runtime. Tämä tarkoittaa, että promisen ratkaiseminen, joko Rustista tai JS:stä, johtaa tähän callbackiin kutsumiseen ja microtaskin jonotukseen. Tiukasti ottaen microtask jonotetaan silti Rustista.
  • Rustista on useita paikkoja, joista microtaskit jonotetaan eksplisiittisesti “natiivista” Rustista:
    • await a stable state -algoritmin toteuttamiseen script-threadin kautta, ilmeisesti vain script-threadin kautta, mikä tarkoittaa, että worker event-loopit eivät koskaan käytä tätä algoritmia.
    • dom-queuemicrotask -algoritmin toteuttamiseen sekä window- että worker event-loopeissa.
    • Ja useissa muissa paikoissa DOM:issa, jotka kaikki voidaan jäljittää Microtask -enumin variantteihin
    • Microtask voidaan jonottaa vain vaiheista, jotka ajetaan tehtävän sisällä, ei koskaan vaiheista, jotka ajetaan “in-parallel” event-loopin kanssa.

Microtask Checkpointien ajaminen

perform-a-microtask-checkpoint vastaa MicrotaskQueue::checkpoint -metodia, ja sitä kutsutaan useissa kohdissa:

Roskienkeruu ja RefCell

Servon integraatio SpiderMonkeyn roskienkeruujärjestelmään ja Rustin malli jaetulle omistajuudelle vuorovaikuttavat hienovaraisesti. Koska DOM-objektit Servossa eivät ole uniikisti omistettuja, meidän täytyy käyttää RefCell/DomRefCell:iä jäsenille, joita voidaan mutatoida.

Kun roskienkeruu (GC) käynnistetään SpiderMonkeysta, jokainen DOM-objekti jäljitetään löytääkseen saavutettavat JS-arvot. Tämä jäljitys on toteutettu JSTraceable-derive:llä, joka kutsuu JSTraceable::trace:a jokaiselle DOM-objektin jäsenelle (ellei sitä ole annotoitu #[no_trace]-attribuutilla).

Koska RefCell:in JSTraceable-toteutus lainaa solua, tämä tarkoittaa, että mikä tahansa DOM-objektin jäsenen mutable-laina aiheuttaa paniikin, jos GC tapahtuu, kun laina on vielä aktiivinen. Kutsumme tätä usein Servossa borrow hazard -tilanteeksi.

Borrow hazard -tilanteiden tunnistaminen: CanGc

Servossa on tyyppi nimeltä CanGc, jota käytetään osoittamaan, milloin GC voi tapahtua ennen kuin kutsuttu funktio palaa. On yksi sääntö: kun kutsutaan funktiota, joka hyväksyy CanGc-argumentin, kutsujan täytyy myös hyväksyä CanGc-argumentti.

Tähän sääntöön on poikkeuksia:

  • trait-metodit, jotka on määritelty script/script_bindings-cratejen ulkopuolella, eivät voi propagoida CanGc:ä, joten toteutusten täytyy käyttää CanGc::note():a, jos kutsuttu funktio vaatii CanGc-argumentin
  • asynkronisten tehtävien täytyy käyttää CanGc::note():a, koska ne suoritetaan riippumatta kutsujan stack frame:sta
  • extern "C"-funktioiden täytyy käyttää CanGc::note():a, koska ne vaativat vastaavan allekirjoituksen ulkoiselle kirjastolle

Kun CanGc propagoidaan oikein koodin läpi, borrow hazard -tilanteet voidaan tunnistaa etsimällä borrow_mut()-käyttöjä can_gc:n käytön läheltä. Erityisesti, kun borrow_mut():n palautusarvo tallennetaan muuttujaan, ja muuttuja on vielä elossa, kun funktiokutsu sisältää can_gc-argumentin, on erittäin todennäköistä, että kyseessä on paniikki odottamassa laukeamistaan!

Katso esimerkki-issue, joka korostaa borrow hazard -tilannetta. Lue lisää alkuperäisestä issue:sta, joka ehdotti staattista analyysiä.

Borrow hazard -tilanteiden varmentaminen

Varmistaaksemme, että tietty mutable-laina voi laukaista paniikin GC:n tapahtuessa, tarvitsemme 1) deterministisen roskienkeruun, 2) tavan ajaa epäilyttävä koodi.

Tehdäksesi roskienkeruusta deterministisen, sinun täytyy ensin kääntää Servo --debug-mozjs:llä, sitten ajaa se --pref js_mem_gc_zeal_level=2 --pref js_mem_gc_zeal_frequency=1 -asetuksilla. Tämä ottaa käyttöön tilan, jossa roskienkeruu ajetaan aina JS-allokaation tapahtuessa, ja se varmasti laukaisee kaikki piilevät borrow hazard -tilanteet. Se on myös erittäin hidas, joten testitapauksen minimointi säästää aikaa.

Jos et ole varma, miten epäilyttävä koodi laukaistaan, lisää siihen paniikki ja aja WPT-testejä sopivasta hakemistosta, kunnes löydät testitiedoston, joka panikoi.

Mallit borrow hazard -tilanteiden korjaamiseen

  • Pakota laina pudotettavaksi aiemmin scopettamalla se ({ ... })
  • Kloonaa väliaikainen arvo lainatusta arvosta, jotta laina voidaan pudottaa aiemmin
  • Sen sijaan, että käytät RefCell<SomeStruct>:ia, tee SomeStruct:in jäsenet käyttämään RefCell/Cell:iä
  • Jaa sekoitettu immutable/mutable-laina useiksi scopatuiksi immutable-lainauksiksi ja käytä mutable-lainauksia vain mutaation tapahtuessa

Esimerkkejä borrow hazard -tilanteiden korjaamisesta

  • https://github.com/servo/servo/pull/40139
  • https://github.com/servo/servo/pull/40138

Esimerkkejä CanGc-argumenttien propagoinnista

  • https://github.com/servo/servo/pull/40033
  • https://github.com/servo/servo/pull/36180
  • https://github.com/servo/servo/pull/40325

CanGc-argumenttien lisääminen generoituihin DOM-metoditrait:eihin

WebIDL-metodien ylimääräiset argumentit kontrolloidaan Bindings.conf -tiedostolla. CanGc-argumentit kontrolloidaan erityisesti canGc-avaimella tietylle rajapinnalle. Jos rajapintaa ei ole vielä listattu tiedostossa, voit vapaasti lisätä sen.

Servon tyylisysteemin yleiskatsaus

Tämä dokumentti tarjoaa yleiskatsauksen Servon tyylisysteemistä. Laajempia yksityiskohtia varten katso style doc comments tai wikin Styling Overview, joka sisältää keskustelun Boris Zbarskyn ja Patrick Waltonin välillä siitä, miten tyylien jakaminen toimii.

Selektoritoteutus

Stylo-yhteensopivuuden varmistamiseksi (projekti, joka integroi Servon tyylisysteemin Geckoon) selektorien täytyy olla yhdenmukaisia.

Yhdenmukaisuus on toteutettu selectors’ SelectorImpl -kohdassa, joka sisältää logiikan pseudo-elementtien ja muiden pseudo-luokkien jäsentämiseen puurakenteellisten pseudo-luokkien lisäksi.

Servo laajentaa selektoritoteutuksen trait:ia salliakseen muutamien asioiden jakamisen Stylon ja Servon välillä.

Servon pääasiallinen toteutus (jota käytetään tavallisissa käännöksissä) on SelectorImpl.

DOM-liima

DOM:n, layoutin ja tyylin pitäminen eri moduuleissa vaatii muutamia trait:eja.

Stylen dom-trait:t (TDocument, TElement, TNode, TRestyleDamage) ovat pääasiallinen “muuri” layoutin ja tyylin välillä.

Layoutin wrapper-moduuli varmistaa, että layout-trait:eilla on vaaditut trait:t toteutettuina.

Stylist

stylist-rakenne sisältää kaikki selektorit ja laitteen ominaisuudet tietylle dokumentille.

Tyylitiedostojen CSS-säännöt muunnetaan Rule-objekteiksi. Ne sijoitetaan sitten SelectorMap -rakenteeseen pseudo-elementin (katso PerPseudoElementSelectorMap), tyylitiedoston originin (katso PerOriginSelectorMap) ja prioriteetin (katso normal- ja important-kentät PerOriginSelectorMap -rakenteessa) mukaan.

Tämä rakenne luodaan käytännössä kerran per pipeline, vastaavassa LayoutThread:ssä.

properties-moduuli

properties module on mako-malli. Sen monimutkaisuus johtuu koodista, joka tallentaa ominaisuuksia, cascade-funktiosta ja palautetun arvon laskentalogiikasta, joka on altistettu pääfunktiossa.

Layout

Servon nykyinen layout-järjestelmä tunnetaan myös nimellä Layout 2020. Se korvasi alkuperäisen layout-järjestelmän, joka tunnettiin myös nimellä Layout 2013.

Layout tapahtuu kolmessa vaiheessa: box tree -rakentaminen, fragment tree -rakentaminen ja display list -rakentaminen. Kun display list on generoitu, se lähetetään WebRenderille renderöintiä varten. Aina kun mahdollista puurakentamisen aikana, layout yrittää käyttää rinnakkaisuutta Rayonin avulla. Tietyt CSS-ominaisuudet estävät rinnakkaisuuden, kuten floatit tai laskurit. Sama koodi käytetään sekä rinnakkaisessa että peräkkäisessä layoutissa.

Box Tree

Box tree on puu, joka edustaa sisäkkäisiä formatting contexteja CSS-spesifikaatiossa kuvatulla tavalla. Formatting contexteja on erilaisia, kuten block formatting context (block flow:lle), inline formatting context (inline flow:lle), table formatting context ja flex formatting context. Jokaisella formatting contextilla on eri säännöt laatikoiden sijoittelulle kontekstin sisällä. Servo edustaa tätä kontekstipuuta sisäkkäisillä enum:eilla, jotka varmistavat, että kontekstin sisältö voi olla vain sellaista sisältöä, jota spesifikaatio kuvaa.

Box tree on vain layout-tilan alkuperäinen esitys, ja yleisesti ottaen seuraava vaihe on ajaa layout-algoritmi box tree:llä ja tuottaa fragment tree. CSS:n fragmentit ovat tuloksia elementtien jakamisesta box tree:ssä useisiin fragmentteihin rivinvaihtojen, sarakkeiden ja sivutuksen vuoksi. Lisäksi layout-vaiheessa Servo sijoittaa ja mitoittaa tuloksena olevat fragmentit suhteessa niiden containing blockeihin. Muunnos tapahtuu yleensä funktiossa layout(...) eri box tree -tietorakenteilla.

Box tree:n layout fragment tree:ksi tehdään rinnakkain, kunnes tullaan puun osaan, jossa on floatit. Näissä osissa tehdään peräkkäinen läpäisy, ja rinnakkainen layout voi jatkua uudelleen, kun layout-algoritmi siirtyy floatit sisältävän block formatting contextin rajojen yli, joko laskeutumalla riippumattomaan formatting contextiin tai viimeistelemällä float-kontin layoutin.

Fragment Tree

Layout-vaiheen tuote on fragment tree. Tässä puussa elementeillä, jotka on jaettu eri paloihin rivinvaihtojen, sarakkeiden tai sivutuksen vuoksi, on fragmentti jokaiselle palalle. Lisäksi jokainen fragmentti on sijoitettu suhteessa fragmenttiin, joka vastaa sen containing blockia. Positionoituja fragmentteja varten alkuperäiseen puun sijaintiin jätetään ylimääräinen placeholder-fragmentti AbsoluteOrFixedPositioned. Tätä placeholderia käytetään display listin rakentamiseen oikeassa järjestyksessä CSS:n maalausjärjestyksen mukaan.

Display List -rakentaminen

Kun layout on luonut fragment tree:n, se voi siirtyä renderöinnin seuraavaan vaiheeseen, joka on display listin tuottaminen puulle. Tässä vaiheessa fragment tree muunnetaan WebRender -display listiksi, joka koostuu display list -kohteista (suorakulmiot, viivat, kuvat, tekstijaksot, varjot jne.). WebRender ei tarvitse suurta valikoimaa display list -kohteita web-sisällön edustamiseen.

Normaalien display list -kohteiden lisäksi WebRender käyttää spatial node -puuta muunnosten, scrollattavien alueiden ja sticky-sisällön edustamiseen. Tämä puu on käytännössä kuvaus siitä, miten post-layout-muunnoksia sovelletaan display list -kohteisiin. Kun sivua scrollataan, juuren scrollaus-solmun offsetia voidaan säätää ilman välitöntä layoutia. Samoin WebRender voi soveltaa muunnoksia, mukaan lukien 3D-muunnoksia, web-sisältöön spatial node -tyypillä, jota kutsutaan reference frame:ksi.

Leikkaus, olipa se CSS-leikkauksesta tai CSS:n overflow-ominaisuuden tuomasta leikkauksesta, käsitellään toisella clip node -puulla. Näille solmuille on myös määritetty spatial node:t, jotta leikkaukset pysyvät synkassa muun web-sisällön kanssa. WebRender päättää, miten parhaiten soveltaa sarjan leikkauksia jokaiseen kohteeseen.

Kun display list on rakennettu, se lähetetään compositorille, joka välittää sen WebRenderille.

Compositor

TODO: Katso https://github.com/servo/servo/blob/main/components/compositing/compositor.rs

Canvas

Servo tukee neljää canvas-kontekstityyppiä:

Jokainen canvas-konteksti toteuttaa CanvasContext-traitin, joka vaatii kontekstien toteuttavan joitakin yhteisiä ominaisuuksia yhtenäisellä tavalla:

  • context_id
  • resize: tämä metodi tyhjentää maalarin kuvan asettamalla sen läpinäkyväksi alfaksi (kaikki tavut nolliksi)
  • get_image_data: käytetään canvas-kuvan hankkimiseen, yleensä kutsumalla toDataUrl, toBlob, createImageBitmap canvas-elementillä tai epäsuorasti piirtämällä yhden canvasin toiseen
  • update_the_rendering: renderöinnin päivityksen käynnistämiseen (yleensä vaihtamalla screen-buffer ja back-buffer)
  • canvas: hanki kytketty canvas-elementti (tämä voi olla HTMLCanvasElement tai OffscreenCanvas, joka voidaan myös kytkeä HTMLCanvasElement-elementtiin kontekstilla placeholder) samalla tarjoten hyviä oletustoteutuksia (onscreen, origin_is_clean, size, mark_as_dirty). mark_as_dirty kutsutaan funktioista, jotka vaikuttavat maalarin kuvaan, ja se kertoo layoutille renderöimään canvas-elementin uudelleen (merkitsemällä HTMLCanvasElement likaiseksi solmuksi).

HTML-tapahtumasilmukka ja renderöinti

flowchart TB
    subgraph Sisältöprosessi
        subgraph Script-kulku
            JS-->utr[Päivitä renderöinti]-->Layout
        end
    end

    subgraph Pääprosessi
        subgraph Maalarit
            WGPU[WGPU-säie]
            WEBGL[WebGL-säie]
            CPT[Canvas Paint Thread]
        end

        %% actual update in painters
        Painters--CreateImage-->Compositor
        Painters--UpdateImage-->Compositor

        Compositor-->WR

        WR[WebRender]--lock,unlock-->Painters
    end

    %% init canvas
    JS--create context-->Painters--ImageKey, CanvasId-->JS

    %% update canvas rendering
    utr<--Update rendering-->Painters

    %% rendering
    Layout--DisplayList (contains ImageKey)-->Compositor

HTML-tapahtumasilmukan osana script-säie suorittaa tehtävän (jäsentäminen, skriptin evaluointi, callbackit, tapahtumat, …) ja sen jälkeen se suorittaa microtask-checkpointin, joka tyhjentää microtask-jonon. Window event loopissa jonotamme globaalin tehtävän renderöinnin päivittämiseen, jos on rendering opportunity (yleensä compositorin ohjaama laitteistopäivitystaajuuden perusteella). Servossa emme itse asiassa jonota tehtävää, vaan ajamme update the rendering -algoritmin ScriptThreadin IPC-viestien jälkeen ja sitten suoritamme myös microtask-checkpointin, kuten tapahtumasilmukka olisi tehnyt tehtävän valmistuttua. Update the rendering suorittaa erilaisia resize-, scroll- ja animaatiovaiheita (mukaan lukien microtask-checkpointin avoimien promisejen ratkaisemiseksi) ja sitten ajaa animation frame -callbackit (callbackit, jotka on lisätty requestAnimationFrame -kutsulla). Tässä vaiheessa piirtokomennot lähetetään maalareille uuden animaatiokehyksen luomiseksi. Lopuksi käynnistämme reflow’n (layout), joka ensin päivittää canvasien renderöinnin (fluskaamalla likaiset canvasit) ja animoidut kuvat, sitten käy DOM:n ja sen tyylit läpi, rakentaa DisplayList:in ja lähettää sen WebRenderille renderöintiä varten.

Kun canvas-kontekstin luontia pyydetään (canvas.getContext('2d')), script-säie blokkaa maalarisäieellä sen alustuessa ja luodessa uuden WebRender-kuvan (CreateImage), ja lopulta lähettää siihen liittyvän ImageKey:n takaisin scriptille.

sequenceDiagram
    Script->>Constellation: Create Context
    Constellation->>Painter: Create Context

    Painter->>Compositor: GenerateImageKey
    Compositor->>WebRender: GenerateImageKey

    opt 
        Painter<<->>Compositor: ExternalImageId
    end

    WebRender->>Compositor: ImageKey
    Compositor->>Painter: ImageKey

    Painter->>Compositor: CreateImage
    Compositor->>WebRender: CreateImage

    Painter->>Script: PainterIPCSender, ImageKey, CanvasId

Jokainen canvas-konteksti toteuttaa LayoutCanvasRenderingContextHelpers, joka palauttaa ImageKey:n, jota layout käyttää DisplayList:issään, tai None, jos canvas on tyhjennetty tai muuten ei maalattavissa koon vuoksi. WebRender lukee tuloksena olevan kuvadatan renderöinnin yhteydessä annetun ImageKey:n perusteella. WebGL- ja WebGPU-maalareissa tämä tehdään toteuttamalla mukautettu WebrenderExternalImageApi; se tarjoaa lock- ja unlock-metodit WebRenderille varsinaisen kuvadatan hankkimiseksi. 2D-canvaksissa kuvadata toimitetaan suoraan CreateImage- ja UpdateImage-IPC-viestien kautta.

sequenceDiagram
    Script->>Painter:Update rendering (flush)
    Painter->>Compositor:UpdateImage
    Compositor->>WebRender: UpdateImage
    opt
        Painter->>Script: Done
    end
    
    Note over Script: Layout
    Script->>Compositor: DisplayList
    Compositor->>WebRender: DisplayList
    opt
        Compositor<<->>WebRender: Query ExternalImage Registery
        WebRender->>+Painter: lock ExternalImage
        WebRender->>Painter: unlock ExternalImage
        deactivate Painter
    end

2D-canvas-konteksti

flowchart LR
    CanvasRenderingContext2d --- HTMLCanvasContext
    subgraph OffscreenCanvasRenderingContext2D
        subgraph CanvasRenderingContext2d
            CS'[CanvasState]
        end
    end
    OffscreenCanvasRenderingContext2D --- OffscreenCanvas
    PaintRenderingContext2D --- PaintWorklet
    subgraph PaintRenderingContext2D
        CS''[CanvasState]
    end

Useimmat canvasit käyttävät samaa DOM-tyyppiä onscreen- ja offscreen-konteksteilleen, mutta 2D-canvaksissa näin ei ole niiden pitkän historian vuoksi. Web-standardit määrittelevät kolme 2D-canvas-kontekstityyppiä:

  • CanvasRenderingContext2D (kytketty HTMLCanvasContext-elementtiin)
  • OffscreenCanvasRenderingContext2D (kytketty OffscreenCanvas-elementtiin)
  • PaintRenderingContext2D (saatavilla vain PaintWorklet:issä)

CanvasRenderingContext2D ja PaintRenderingContext2D on toteutettu wrapper:eina CanvasState:n ympärille, kun taas OffscreenCanvasRenderingContext2D on toteutettu wrapper:ina CanvasRenderingContext2D:n ympärille samankaltaisen logiikan vuoksi duplikaation välttämiseksi.

flowchart LR
    HTMLCanvasElement --getContext('2d')--> CanvasRenderingContext2d
    CanvasRenderingContext2d --strokeRect--> CanvasState
    CanvasState --IPC
    strokeRect--> CanvasPaintThread
    CanvasPaintThread --Done--> CanvasState

CanvasState toteuttaa varsinaisen 2D-piirron logiikan asettamalla sopivan tilan ja lähettämällä IPC-viestejä Canvas Paint Threadille. Jotkin komennot muuttavat vain sisäistä tilaa, mutta eivät lähetä viestejä ennen kuin on varsinainen piirtokomento.

Kaikki “likaiset” 2D-canvasit tallennetaan Document:iin ja flusataan reflow’n aikana lähettämällä IPC-viestejä, jotka käynnistävät update_the_rendering-metodin jokaiselle canvasille.

Kun piirretään yksi 2D-canvas toiseen 2D-canvasiin, lähetämme DrawImageInOther-viestin, erityisen IPC-viestin, joka välttää bitmapin kopioimisen canvas paint thread -säieeltä ulos.

WebGL-canvas-konteksti

flowchart LR
    WebGLRenderingContext --- c["HTMLCanvasElement
    OffscreenCanvas"]
    subgraph WebGL2RenderingContext
        WebGLRenderingContext
    end

WebGL(2)-canvas-kontekstit ovat WebGLRenderingContext tai WebGL2RenderingContext, ja Servossa WebGL2RenderingContext wrap:aa ja laajentaa WebGLRenderingContext:ia. Nämä kontekstit tallentavat tilaa ja lähettävät IPC-viestejä WebGL-säieelle, joka suorittaa varsinaiset OpenGL- (tai OpenGL ES) -komennot ja palauttaa tulokset IPC:n kautta. Script-säie blokkaa WebGL-säieellä odottaen jokaisen operaation valmistumista.

Kaikki “likaiset” WebGL-canvasit tallennetaan Document:iin ja flusataan reflow’n osana lähettämällä yksi IPC-viesti, joka sisältää kaikki likaiset kontekstitunnukset, ja sitten blokkaamalla WebGL-säieellä, kunnes kaikki canvasit on flusattu. Fluskaus vaihtaa framebufferin, joista toinen on esitystä varten (WebRender lukee sen) ja toista käytetään piirtämiseen GL-komentojen kohteena.

WebGPU-canvas-konteksti

WebGPU-esitys on erityisin, koska se on täysin asynkroninen (ei-blokkaava). Lisätietoa siitä, miten asynkronisuus toteutetaan WebGPU:ssa, löytyy WebGPU-luvusta.

sequenceDiagram
    loop Context Creation
        Script->>WGPU: CreteContext
        WGPU->>WebRender: CreateImage
        WebRender->>WGPU: ImageKey
        WGPU->>Script: ImageKey
    end

    alt animation Callback
        Note over Script: getCurrentTexture
        Script-)WGPU:CreateTexture
        activate Script
        Note over Script: draw operations into current texture
    else update the rendering
    Note over Script: expire current texture
        Script-)WGPU:SwapchainPresent
        WGPU-)WGPU: Copy texture to one of staging buffer
        WGPU-)+WGPU poller: Map stagging buffer to CPU as GPUPresentationBuffer
        opt presentationId is newer than existing
            WGPU poller -)-WebRender: UpdateImage
        end
        WGPU poller -)WGPU poller: Unmap GPUPresentationBuffer

        Script-)WGPU:DestroyTexture
        deactivate Script

    end
    loop rendering
        WebRender<<->>+WGPU: lock ExternalImage and read GPUPresentationBuffer
        WebRender->>WGPU: unlock ExternalImage
        deactivate WGPU
    end

Kaikilla onscreen WebGPU-konteksteilla update_the_rendering suoritetaan osana renderöinnin päivitystä HTML-tapahtumasilmukassa. Tämä vanhentaa (tuhoaa) current texture -objektin, mutta ennen sitä lähetämme SwapChainPresent-pyynnön, joka kopioi tekstuuridatan yhteen 10 esityspuskurista GPU:lla. Kopioinnin valmistuttua map:ataan uusi puskuri asynkronisesti CPU:lle. Koska prosessi on asynkroninen, merkitsemme jokaisen esityspuskurin kasvavalla u64-tunnuksella, ja korvaamme aktiivisen esityspuskurin vain, jos puskurimme tunnus on uudempi. Epäaktiivinen esityspuskuri unmap:ataan.

flowchart TD
    S[Staging Presentation Buffer] --copy_texture_to_buffer, mapAsync-->
    Mapping --mapAsync done-->
    UpdateWR --yes-->
    Mapped[Mapped, Unmapped old]

    UpdateWR--else unmap-->S

Tämä on mallinnettu myös TLA+:ssa: https://gist.github.com/gterzian/aa5d96a89db280017b04917eee67f6ac

Sekä WebRenderin lock että get_image_data käyttävät aktiivisen esityspuskurin sisältöä.

Resurssit

Miten WebXR toimii Servossa

Terminologia

Servon WebXR-toteutus koostuu kolmesta pääkomponentista:

  1. Script-säie (ajaa kaiken JS:n sivulle)
  2. WebGL-säie (ylläpitää WebGL-canvas-datan ja kutsuu GL-operaatioita, jotka vastaavat WebGL API:ja)
  3. Compositor (eli pääsäie)

Lisäksi on useita WebXR-spesifisiä käsitteitä:

Lopuksi on grafiikkaan liittyviä käsitteitä, jotka ovat tärkeitä WebXR-renderöinnin matalan tason yksityiskohtien kannalta:

  • Surfman on crate, joka abstrahoi alustakohtaiset yksityiskohdat OpenGL-laitteistokiihdytetystä renderöinnistä.
  • surface on laitteistopuskuri, joka on sidottu tiettyyn OpenGL-kontekstiin.
  • surface texture on OpenGL-tekstuuri, joka wrap:aa pinnan. Surface textureja voidaan jakaa OpenGL-kontekstien välillä.
  • surfman context edustaa tiettyä OpenGL-kontekstia, ja sen taustalla on alustakohtaisia toteutuksia (kuten EGL Unix-pohjaisilla alustoilla).
  • ANGLE on OpenGL-toteutus Direct3D:n päällä, jota Servo käyttää tarjotakseen yhtenäisen OpenGL-backendin Windows-alustoilla.

Miten Servon compositor käynnistyy

Upottaja on vastuussa ikkunan luomisesta ja renderöintikontekstin luonnin käynnistämisestä asianmukaisesti. Servo luo renderöintikontekstin luomalla surfman-kontekstin, jota compositor käyttää kaikkiin web-sisällön renderöintitoimintoihin.

Miten sessio käynnistyy

Kun verkkosivu kutsuu navigator.xr.requestSession(..) JS:n kautta, tämä vastaa Servon XrSystem::RequestSession -metodia. Tämä metodi lähettää viestin WebXR-viestinkäsittelijälle, joka elää pääsäieellä compositorin hallinnassa.

WebXR-viestinkäsittelijä käy läpi kaikki tunnetut discovery objectit ja yrittää pyytää sessiota jokaiselta. Discovery objectit kapseloivat session luomisen jokaiselle tuetulle backendille.

Heinäkuun 19, 2024 tilanteessa on kolme WebXR-backendiä:

  • headless - tukee ikkunatonta, laitteetonta laitetta automatisoiduille testeille
  • glwindow - tukee GL-pohjaista ikkunaa manuaaliseen testaukseen työpöytäympäristöissä ilman oikeita laitteita
  • openxr - tukee laitteita, jotka toteuttavat OpenXR-standardin

WebXR-sessioiden täytyy luoda layer manager jossain vaiheessa, jotta ne voivat luoda ja renderöidä WebXR-kerroksiin. Tämä tapahtuu useassa vaiheessa:

  1. Jotakin alustusta tapahtuu pääsäieellä
  2. Pääsäie lähettää synkronisen viestin WebGL-säieelle
  3. WebGL-säie vastaanottaa viestin
  4. Backend-spesifistä, grafiikka-spesifistä alustusta tapahtuu WebGL-säieellä, layer manager factory -abstraktion takana
  5. Uusi layer manager tallennetaan WebGL-säieelle
  6. Pääsäie vastaanottaa yksilöllisen tunnisteen uudelle layer managerille

Tämä säieiden välinen tanssi on tärkeä, koska renderöintiä suorittavalla laitteella on usein tiukat vaatimukset yhteensopivuudelle kaikille renderöintiin käytetyille WebGL-konteksteille, ja suurin osa GL-tilasta on havaittavissa vain säieellä, joka loi sen.

Miten OpenXR-sessio luodaan

OpenXR-discovery-prosessi alkaa OpenXrDiscovery::request_session -kohdasta. Discovery objectilla on pääsy vain siihen tilaan, joka välitettiin sen konstruktorissa, sekä SessionBuilder -objektiin, joka sisältää uuden session luomiseen tarvittavat arvot.

OpenXR-session luominen luo ensin OpenXR-instanssin, joka mahdollistaa käytössä olevien laajennusten konfiguroinnin. Eri alustoilla OpenXR:n alustukseen käytetään eri laajennuksia; Windowsilla käytetään XR_KHR_D3D11_enable extension, koska Servo luottaa ANGLEen OpenGL-toteutuksessaan.

Kun OpenXR-instanssi on olemassa, session builderia käytetään uuden WebXR-session luomiseen, joka ajaa omassa säieessään. Kaikki WebXR-sessiot voivat joko ajaa säieessä tai Servo voi ajaa ne pääsäieellä. Tällä valinnalla on vaikutuksia siihen, miten WebXR-session grafiikka voidaan konfiguroida, sen perusteella, mitä GL-tilaa täytyy olla saatavilla jaettavaksi.

OpenXR:n uusi session-säie alustaa OpenXR-laitteen, joka on vastuussa varsinaisen OpenXR-session luomisesta. Tämä session-objekti luodaan WebGL-säieellä osana OpenXR layer managerin luomista, koska se luottaa WebGL-säieen käyttämän GPU-laitteen jakamiseen.

Kun session-objekti on luotu, pääsäie voi hankkia kopion ja jatkaa uuden laitteen jäljellä olevien ominaisuuksien alustusta.

WebGPU

Servon WebGPU-toteutus perustuu wgpu(-core) -kirjastoon, ja se koostuu kahdesta osasta:

  • DOM-toteutus
  • WGPU-toteutus
flowchart LR
    subgraph Sisältöprosessi
        Script[Script-säie]
    end

    subgraph Pääprosessi
        WGPU[WGPU-säie]
        poller[WGPU poller]
        WebRender
        Constellation
    end

    %% init
    Script--RequestAdapter-->Constellation
    Constellation--RequestAdapter/WebRender-->WGPU
    Script--WebGPURequest-->WGPU
    WGPU--Adapter/WebGPU chan-->Script

    %% normal operation
    WGPU--token(), wake()-->poller
    WGPU--Create/Update Image-->WebRender
    WGPU--WebGPUResponse/WebGPUMsg-->Script

DOM-toteutus

DOM-toteutus sijaitsee components/script/dom/webgpu -hakemistossa, ja se toteuttaa JS-rajapinnat, jotka on määritelty WebGPU IDL -tiedostossa ja jotka on altistettu web-alustalle.

Täällä toteutamme vain logiikan, joka on kuvattu WebGPU-spesifikaatiossa content timeline -osiossa. Tämä sisältää pääasiassa JS-tyyppien muuntamisen wgpu-types-kuvauksiksi, jotka lähetetään WGPU-säieelle IPC-viesteillä, jotka on määritelty https://github.com/servo/servo/blob/main/components/shared/webgpu/messages/recv.rs. WebGPU on suunniteltu asynkroniseksi, joten WGPU-säieen operaatioiden valmistumista ei tarvitse odottaa. Tämä tehdään tallentamalla tunnuksia DOM WebGPU -objekteihin, jotka linkittävät WGPU-säieellä eläviin WGPU-objekteihin (joita wgpu-core tarjoaa). Lisätietoa tästä suunnittelusta on saatavilla wgpu-repositoriossa.

WGPU-toteutus

Varsinainen käsittely tapahtuu kahdella omistetulla säieellä pääselainprosessissa: yksi WGPU:lle ja yksi WGPU poller:ille. Nämä säieet käynnistetään laiskasti ensimmäisellä adapter-pyynnöllä.

WGPU-säie toteuttaa device timeline -osiossa määritellyt vaiheet dispatch:amalla wgpu-core-funktioita vastauksena scriptiltä tuleviin IPC-viesteihin. Jotkin kutsut herättävät myös WGPU poller -säieen, joka ajaa tehokkaasti queue timeline -osioita kutsumalla poll_all_devices wgpu-coresta.

WebGPU CTS -odotusten päivittäminen

WebGPU CTS -odotukset ovat valtavia, joten mach update-wpt:n sijaan käytämme moz-webgpu-cts -työkalun forkkia odotusten päivittämiseen.

Odotusten päivittämiseksi sinun täytyy hankkia ajon wptreport-loki joko käynnistämällä täysi CTS-ajo CI:ssä (mach try webgpu) tai ajamalla tiettyjä testejä paikallisesti:

$ mach test-wpt -r --log-wptreport report.json [tests ...]

Huomaa, että odotukset asetetaan release- ja production-käännöksille, koska debug-käännökset ovat liian hitaita. Päivitä odotukset seuraavasti:

$ moz-webgpu-cts --servo process-reports --preset set report.json

Voit myös testata Servoa live CTS:ää vastaan:

$ mach run -r --pref dom.webgpu.enabled 'https://gpuweb.github.io/cts/standalone/?runnow=1&q=<test>'

Tällä hetkellä meillä on useita epävakaita tehtäviä webgpu:shader,execution,expression -kategoriassa, joita seurataan #31397 -issue:ssa. Ne voidaan jättää huomiotta, jos ne ilmestyvät try-ajojesi odottamattomiin tuloksiin.

Työskentely upstream gfx-rs/wgpu -projektissa

Servoa voidaan käyttää wgpu-muutosten testaamiseen WebGPU CTS:ää vasten seuraavasti:

  1. Päivitä wgpu Servon Cargo.toml:ssa uusimpaan committiin (valinnainen, mutta suositeltavaa jos Servo on vanhentunut)
  2. Perusta wgpu-muutoksesi samaan wgpu-committiin, jota Servo käyttää
  3. Päivitä wgpu Servon Cargo.toml:ssa haaraan, jossa muutoksesi ovat
  4. Testaa muutoksesi käynnistämällä täysi CTS-ajo: mach try webgpu
  5. Sisällytä linkki testituloksiin wgpu-pull requestiisi

Lisäresurssit

Servodriver-testiharness

Servodriver on wptrunner-kehyksen ja WebDriver-palvelimen päälle rakennettu testiharness. Se ei ole vielä oletuksena käytössä, mutta sen voi ajaa lisäämällä --product servodriver -argumentin mihin tahansa test-wpt-komentoon. Servodriver koostuu kolmesta pääkomponentista: Python-testiharness, joka orkestroi selainta, Servon sisäinen web-palvelin, joka toteuttaa WebDriver-spesifikaation, ja testisivujen sisällä ladattavat skriptit.

wptrunner-harness

Wptrunner on harness, joka käyttää executor- ja browser-yhdistelmää tuen tuotteen käynnistämiseen. browser määrittelee Python-luokat instanssien luomiseen ja metodit tuettujen konfiguraatioiden kutsumiseen sekä argumentit Servo-binääriä käynnistettäessä. Se delegoi kaiken WebDriver-spesifisen logiikan perus WebDriverBrowser-luokalle, joka on yhteinen kaikille WebDriveriin luottaville selaimille.

Servodriverin executor määrittelee Servo-spesifisen testien alustuksen, tulosten käsittelyn ja testien välisen tilan hallinnan. Esimerkiksi Servo määrittelee WebDriver-laajennusmetodit preferenssien hallintaan, ja näitä kutsutaan testien välillä varmistaakseen, että jokainen testi ajaa tarkoitetulla konfiguraatiollamme.

Servodriver-executormme delegoi paljon logiikkaa yhteiselle WebDriverTestharnessExecutor -luokalle. Tämä executor on vastuussa testdriver.js-harnessin suorittamisesta ja mahdollistaa Python-harnessin hakevan testitulokset selaimesta.

Servon WebDriver-toteutus

Tässä toteutuksessa on kolme pääkomponenttia: server handler, script handler ja input handler.

Server handler

Kun wptrunner yhdistää WebDriver-palvelimeen uudessa Servo-instanssissa, se luo uuden session -instanssin. Tämä sessio säilyttää tilaa WebDriver API -kutsujen välillä. Selaimen kanssa vuorovaikuttavat API-kutsut reititetään constellationin kautta ConstellationMsg::WebDriverCommand -viesteinä, ja ne, joiden täytyy vuorovaikuttaa suoraan dokumentin sisällön kanssa, kuuluvat WebDriverScriptCommand -enum:iin. Monet näistä API:sta vaativat synkronisen vastauksen IpcSender-kanavan kautta, jota Servon WebDriver-palvelin käyttää odottaakseen vastausta. Tapaukset kuten navigointi ja asynkronisten skriptien suoritus sisältävät kuitenkin lisäsynkronointia.

Kun navigointia pyydetään, constellation vastaanottaa IpcSenderin, jonka se tallentaa. Kun navigointi valmistuu, constellation tarkistaa, vastaako pipeline WebDriverin navigointia, ja ilmoittaa WebDriver-palvelimelle alkuperäistä kanavaa käyttäen.

Asynkroniset skriptit

Kun asynkronisen skriptin suoritus aloitetaan, WebDriver-spesifikaatio tarjoaa tavan skriptien kommunikoida tulosta palvelimelle. Skripti kutsutaan anonyyminä funktiona lisäfunktioargumentilla, joka lähettää vastauksen palvelimelle.

Script handler

Kun WebDriver-viesti, joka kohdistuu tiettyyn selauskontekstiin, vastaanotetaan, ScriptThread, joka sisältää aktiivisen dokumentin, käsittelee sen. Kunkin komennon käsittelylogiikka sijaitsee webdriver_handlers.rs -tiedostossa. Mikä tahansa komento, joka palauttaa web-sisällöstä johdetun arvon, täytyy serialisoida JS-arvot arvoiksi, jotka WebDriver-palvelin voi muuntaa API-arvotyypeiksi.

Altistamme kaksi webistä saavutettavaa metodia asynkronisten tulosten kommunikointiin WebDriver-palvelimelle: Window.webdriverCallback ja Window.webdriverTimeout.

Input handler

Kun WebDriver-palvelin vastaanottaa syöte-toimintokomentoja (esim. osoitin- tai hiiritoiminnot), se luo toimintosekvenssin ja dispatch:aa ne asteittain compositorille constellationin kautta. Compositor käsittelee nämä tapahtumat samalla tavalla kuin upottajalta vastaanotetut syöte-tapahtumat (poislukien tunnetut bugit).

Testisivujen skriptit

testdriver-harness yhdistää kaikki nämä elementit yhteen. Executor avaa uuden ikkunan ja kutsuu asynkronista skriptiä, joka asettaa testdriver-callbackin arvoon Window.webdriverCallback (arguments[arguments.length - 1]). Tämä ikkuna luo viestijonon, jota executor-harness lukee. Myöhemmin jokainen ajettava testi avataan uutena ikkunana; nämä ikkunat voivat käyttää opener.postMessage kommunikoidakseen alkuperäisen ikkunan kanssa, mikä mahdollistaa testdriver API:en lähettää viestejä, jotka edustavat WebDriver API -kutsuja.

Debuggausvinkit

Aloita aina seuraavalla RUST_LOG:lla:

RUST_LOG=webdriver_server,webdriver,script::webdriver_handlers,constellation

Tämä yleensä tekee selväksi, käsitelläänkö odotetut API-kutsut.

Kun debuggaat syöte-ongelmia, lisää JS-debug-rivit, jotka loggaavat klikattavan elementin getBoundingClientRect()-arvon, ja vertaa compositorin vastaanottamia koordinaatteja manuaalisessa klikkauksessa vs. WebDriver-palvelimen lähettämiä koordinaatteja.

Android-sovelluksen arkkitehtuuri

Android-sovelluksen toteutus on jaettu useisiin komponentteihin:

  • jaettu Android/OpenHarmony-toteutus servoshell:lle (ports/servoshell/egl)
  • Android-spesifinen servoshell-integraatio (ports/servoshell/egl/android.rs)
  • pääaktiviteetti (support/android/apk/servoapp/src/main/java/org/servoshell)
  • ServoView-komponentti (support/android/apk/servoview/src/main/java/org/servo/servoview)
    • Android SurfaceView (ServoView.java)
    • Servo-moottorin wrapper (Servo.java)
    • JNI servoshell-integraatio (JNIServo.java)

Ohjauskulku

Upottaja -> Servo

Sovelluksen sisällä alkavat tapahtumat käynnistyvät joko natiivin sovellus-UI:n toimesta (esim. URL:n lataus) tai osana ServoView-komponenttia (esim. kosketussyöte). Kaikki natiivi sovellus-UI määritellään pääaktiviteetissa ja käyttää ServoView-komponentin julkista API:a vuorovaikutukseen taustalla olevan Servo-instanssin kanssa.

ServoView on vastuussa koordinoinnista kahden säieen välillä — GL-säie, joka ajaa varsinaista Servo-instanssia, ja UI-säie. Se on myös vastuussa syöte-tapahtumien välittämisestä moottorille ja alustatapahtumien, kuten pinnan koon muutoksen, käsittelystä.

Servo -> Upottaja

Moottorin sisällä alkavat tapahtumat täytyy saada upotussovelluksen eri kerroksiin. Alin taso on Servo-moottorin wrapper, joka toteuttaa JNIServo.Callbacks-rajapinnan. Nämä callbackit mahdollistavat moottorin wrapperin käynnistää callbackit UI- ja GL-säieillä tarvittavan toiminnon mukaan. UI-säieellä tapahtuvat toiminnot sisältävät kehotteen näyttämisen ja näppäimistön käynnistämisen, kun taas GL-säie-toiminnot sisältävät Servo-tapahtumasilmukan pyörittämisen ja GL-renderöintitoiminnot. Servo-komponentti altistaa Client-rajapinnan UI-säie-toiminnoille, mahdollistaen viestinnän upotussovelluksen ylimpien kerrosten kanssa.

JNI-integraatio

Jokainen JNIServo-luokan native-jäsen on toteutettu android.rs-tiedostossa #[unsafe(no_mangle)]-funktiona, jolla on vastaava nimi. jni-rs-craten dokumentaatio sisältää lisätietoa tästä integraatiosta.

Dokumentaatio vanhoille versioille

Osa tästä kirjasta ei välttämättä päde vanhoihin Servo-versioihin.

Jos sinun täytyy työskennellä hyvin vanhan Servo-version kanssa, näistä dokumenteista voi olla apua:

TODO: docs/glossary.md

Sanaston käyttö

Tämä on kokoelma yleisiä termejä, joilla on erityinen merkitys Servo-projektin kontekstissa. Tavoitteena on tarjota korkean tason määritelmiä ja hyödyllisiä linkkejä lisälukemiseen, eikä täydellistä dokumentaatiota koodin yksittäisistä osista.

Jos Servon koodissa, issue trackerissa, mailing listillä jne. on sana tai lause, joka on hämmentävä, tee pull request, joka lisää sen tähän tiedostoon rungolla TODO. Tämä signaaloi asiantuntijoille lisäämään merkityksellisemmän määritelmän.

Sanasto

Compositor

Säie, joka vastaanottaa syötetapahtumat käyttöjärjestelmältä ja välittää ne constellationille. Se vastaa myös web-sisällön valmiiden renderöintien compositoinnista ja niiden näyttämisestä ruudulla mahdollisimman nopeasti.

Constellation

Säie, joka hallitsee joukkoa toisiinsa liittyvää web-sisältöä. Tätä voi ajatella yksittäisen välilehden omistajana välilehdellisessä web-selaimessa; se kapseloi istuntohistorian, tietää kaikista kehysten puussa olevista kehyksistä ja omistaa pipeline:n jokaiselle sisältyvälle kehykselle.

Display list

Lista konkreettisia renderöintiohjeita. Display list on layoutin jälkeen, joten kaikilla kohteilla on stacking context -suhteiset pikselisijainnit, ja z-index on jo sovellettu, joten myöhemmät kohteet display listissä ovat aina aiempien päällä.

Layout thread

Säie, joka vastaa DOM-puun layoutoinnista laatikoiden kerroksiin tietylle dokumentille. Vastaanottaa komentoja script-säikeeltä layoutoida sivu ja joko luo uuden display listin renderer-säikeen käyttöön tai palauttaa sivun layoutin kyselyn tulokset scriptin käyttöön.

Pipeline

Yksikkö, joka kapseloi viestintätavan script-, layout- ja renderer-säikeiden kanssa tietylle dokumentille. Jokaisella pipelinella on globaalisti yksilöllinen id, jolla siihen pääsee käsiksi constellationista.

Renderer thread (vaihtoehtoisesti paint thread)

Säie, joka kääntää display listin sarjaksi piirtokomentoja, jotka renderöivät liittyvän dokumentin sisällön puskuriin, joka lähetetään sitten compositorille.

Script thread (vaihtoehtoisesti script task)

Säie, joka suorittaa JavaScriptiä ja tallentaa DOM-esityksen kaikista dokumenteista, joilla on yhteinen origin. Tämä säie kääntää constellationilta vastaanotetut syötetapahtumat DOM-tapahtumiksi spesifikaation mukaisesti, kutsuu HTML-parserin, kun uutta sivun sisältöä vastaanotetaan, ja evaluoi JS:ää tapahtumille kuten timereille ja <script>-elementeille.