webbrowser-1.0.5/.cargo_vcs_info.json 0000644 00000000136 00000000001 0013220 0 ustar {
"git": {
"sha1": "fbd294d7fc4f02ec9833c92a92181bd263628eda"
},
"path_in_vcs": ""
} webbrowser-1.0.5/.github/workflows/android.yaml 0000644 0000000 0000000 00000007522 10461020230 0017717 0 ustar 0000000 0000000 name: Android
on:
push:
branches-ignore: [ '**win**', '**linux**', '**macos**', '**bsd**', '**haiku**', '**wasm**', '**ios**' ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 0 * * SUN'
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
RUST_LOG: webbrowser=TRACE,tests=TRACE
CARGO_APK_VERSION: '0.9.7'
ANDROID_ARCH: x86_64
ANDROID_TARGET: x86_64-linux-android
jobs:
build:
name: Build
runs-on: ubuntu-latest
if: ${{ !contains(github.event.head_commit.message, '#build-') || contains(github.event.head_commit.message, '#build-android') }}
strategy:
matrix:
rust: [ stable ]
api-level: [ 34 ]
ndk-version: [ '27.1.12297006' ]
cmake-version: [ '3.30.4' ]
continue-on-error: ${{ matrix.rust != 'stable' && matrix.rust != 'beta' }}
steps:
- uses: actions/checkout@v3
name: Checkout
- name: Install rust version
run: |
rustup install ${{ matrix.rust }} --profile minimal
rustup component add --toolchain ${{ matrix.rust }} rustfmt clippy
rustup target add ${{ env.ANDROID_TARGET }}
# Install cargo-apk
- uses: actions/cache@v3
name: Fetch cargo-apk from cache
id: cargo-apk-cache
with:
path: ~/.cargo/bin/cargo-apk
key: cargo-apk-${{ env.CARGO_APK_VERSION }}
- name: Install cargo-apk
if: steps.cargo-apk-cache.outputs.cache-hit != 'true'
run: cargo install cargo-apk --version ${{ env.CARGO_APK_VERSION }}
# Pre-build
- name: Compile
run: rustup run ${{ matrix.rust }} cargo apk build --target ${{ env.ANDROID_TARGET }}
working-directory: tests/test-android-app
- name: Compile tests
run: rustup run ${{ matrix.rust }} cargo build --test test_android
# Check if AVD is in cache, else create AVD
- name: Enable KVM group perms
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: AVD cache
uses: actions/cache@v3
id: avd-cache
with:
path: |
~/.android/avd/*
~/.android/adb*
key: avd-${{ matrix.api-level }}
- name: Create AVD
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@v2
with:
arch: ${{ env.ANDROID_ARCH }}
api-level: ${{ matrix.api-level }}
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: false
script: echo "Generated AVD snapshot for caching."
# Run tests on emulator
- name: Run tests
uses: reactivecircus/android-emulator-runner@v2
with:
arch: ${{ env.ANDROID_ARCH }}
api-level: ${{ matrix.api-level }}
ndk: ${{ matrix.ndk-version }}
cmake: ${{ matrix.cmake-version }}
force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
script: bash -c 'ANDROID_TARGET=${{ env.ANDROID_TARGET }} ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/${{ matrix.ndk-version }} rustup run ${{ matrix.rust }} cargo test --test test_android -- --ignored'
# Code format, linting etc.
- name: Check Code Formatting
if: ${{ matrix.rust == 'stable' || matrix.rust == 'beta' }}
run: cargo +${{ matrix.rust }} fmt --all -- --check
- name: Lints
if: ${{ matrix.rust == 'stable' || matrix.rust == 'beta' }}
run: cargo +${{ matrix.rust }} clippy --target ${{ env.ANDROID_TARGET }} -- -D warnings
webbrowser-1.0.5/.github/workflows/ios.yaml 0000644 0000000 0000000 00000004316 10461020230 0017067 0 ustar 0000000 0000000 name: iOS
on:
push:
branches-ignore: [ '**win**', '**android**', '**linux**', '**bsd**', '**haiku**', '**wasm**', '**macos**' ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 0 1 * *'
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: full
RUST_LOG: webbrowser=TRACE
IOS_TARGET: aarch64-apple-ios-sim
jobs:
build:
name: Build
runs-on: macos-15
if: ${{ !contains(github.event.head_commit.message, '#build-') || contains(github.event.head_commit.message, '#build-ios') }}
strategy:
matrix:
rust: [stable]
continue-on-error: ${{ matrix.rust != 'stable' && matrix.rust != 'beta' }}
steps:
- uses: actions/checkout@v3
name: Checkout
- name: Select Xcode 16.2
run: sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
- name: Install rust version
run: |
rustup install ${{ matrix.rust }} --profile minimal
rustup component add --toolchain ${{ matrix.rust }} rustfmt clippy
rustup target add ${{ env.IOS_TARGET }}
- name: Configure and start iOS Simulator
run: |
set -e
IOSRUNTIME=$(xcrun simctl list runtimes | grep SimRuntime.iOS | awk '{ print $NF }' | egrep 'iOS-[0-9]{2}-' | sort | tail -n 1)
IOSDEV=$(xcrun simctl list 2>&1 | grep com.apple.CoreSimulator.SimDeviceType.iPhone | awk '{ print $NF }' | tr -d '()' | egrep 'iPhone-[0-9]{2}$' | sort | tail -n 1)
DEVID=$(xcrun simctl create iphone-latest $IOSDEV $IOSRUNTIME)
echo "==== using device $IOSDEV, $IOSRUNTIME ===="
xcrun simctl boot $DEVID
sleep 10
xcrun simctl list 2>&1
# Run tests
- name: Run tests
run: cargo +${{ matrix.rust }} test --verbose --test test_ios -- --include-ignored --nocapture
env:
TEST_REQ_TIMEOUT: '300'
# Code format, linting etc.
- name: Check Code Formatting
if: ${{ matrix.rust == 'stable' || matrix.rust == 'beta' }}
run: cargo +${{ matrix.rust }} fmt --all -- --check
- name: Lints
if: ${{ matrix.rust == 'stable' || matrix.rust == 'beta' }}
run: cargo +${{ matrix.rust }} clippy --target ${{ env.IOS_TARGET }} -- -D warnings
webbrowser-1.0.5/.github/workflows/linux.yaml 0000644 0000000 0000000 00000003303 10461020230 0017427 0 ustar 0000000 0000000 name: Linux
on:
push:
branches-ignore: [ '**win**', '**android**', '**macos**', '**bsd**', '**haiku**', '**wasm**', '**ios**' ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 0 * * SUN'
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
RUST_LOG: webbrowser=TRACE
jobs:
build:
name: Build
runs-on: ubuntu-latest
if: ${{ !contains(github.event.head_commit.message, '#build-') || contains(github.event.head_commit.message, '#build-linux') }}
strategy:
max-parallel: 1
matrix:
rust: [stable, beta, nightly]
continue-on-error: ${{ matrix.rust != 'stable' && matrix.rust != 'beta' }}
steps:
- uses: actions/checkout@v3
name: Checkout
- name: Install rust version
run: |
rustup install ${{ matrix.rust }} --profile minimal
rustup component add --toolchain ${{ matrix.rust }} rustfmt clippy
- name: Compile
run: cargo +${{ matrix.rust }} build
- name: Run Tests
env:
BROWSER: firefox --screenshot %s
run: cargo +${{ matrix.rust }} test --all --locked --verbose
- name: Run Tests (hardened)
run: cargo +${{ matrix.rust }} test --features hardened --locked --verbose --test test_unix tests::test_hardened_mode
- name: Check compilation with WSL disabled
run: cargo +${{ matrix.rust }} build --features disable-wsl
- name: Check Code Formatting
if: ${{ matrix.rust == 'stable' || matrix.rust == 'beta' }}
run: cargo +${{ matrix.rust }} fmt --all -- --check
- name: Lints
if: ${{ matrix.rust == 'stable' || matrix.rust == 'beta' }}
run: cargo +${{ matrix.rust }} clippy --all-targets -- -D warnings
webbrowser-1.0.5/.github/workflows/macos.yaml 0000644 0000000 0000000 00000003030 10461020230 0017367 0 ustar 0000000 0000000 name: MacOS
on:
push:
branches-ignore: [ '**win**', '**android**', '**linux**', '**bsd**', '**haiku**', '**wasm**', '**ios**' ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 0 1 * *'
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
RUST_LOG: webbrowser=TRACE
jobs:
build:
name: Build
runs-on: macos-latest
if: ${{ !contains(github.event.head_commit.message, '#build-') || contains(github.event.head_commit.message, '#build-macos') }}
strategy:
matrix:
rust: [stable]
continue-on-error: ${{ matrix.rust != 'stable' && matrix.rust != 'beta' }}
steps:
- uses: actions/checkout@v3
name: Checkout
- name: Install specific rust version
run: |
rustup install ${{ matrix.rust }} --profile minimal
rustup component add --toolchain ${{ matrix.rust }} rustfmt clippy
- name: Compile
run: cargo +${{ matrix.rust }} build
- name: Run Tests
run: cargo +${{ matrix.rust }} test --locked --verbose --test test_macos -- --include-ignored
- name: Run Tests (hardened)
run: cargo +${{ matrix.rust }} test --features hardened --locked --verbose --test test_macos tests::test_hardened_mode
- name: Check Code Formatting
if: ${{ matrix.rust == 'stable' || matrix.rust == 'beta' }}
run: cargo +${{ matrix.rust }} fmt --all -- --check
- name: Lints
if: ${{ matrix.rust == 'stable' || matrix.rust == 'beta' }}
run: cargo +${{ matrix.rust }} clippy --all-targets -- -D warnings
webbrowser-1.0.5/.github/workflows/release.yaml 0000644 0000000 0000000 00000002537 10461020230 0017720 0 ustar 0000000 0000000 name: Release
on:
release:
types: [ created ]
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
RUST_LOG: webbrowser=TRACE
jobs:
crates:
name: Crates Publish
runs-on: ubuntu-latest
strategy:
matrix:
rust: [stable]
continue-on-error: ${{ matrix.rust != 'stable' && matrix.rust != 'beta' }}
steps:
- uses: actions/checkout@v3
name: Checkout
- name: Install rust version
run: |
rustup install ${{ matrix.rust }} --profile minimal
rustup component add --toolchain ${{ matrix.rust }} rustfmt clippy
# Check compilation
- name: Compile
run: cargo +${{ matrix.rust }} build
# Run tests
- name: Run Tests
env:
BROWSER: firefox --screenshot %s
run: cargo +${{ matrix.rust }} test --all --locked --verbose
# Code format and lints
- name: Check Code Formatting
if: ${{ matrix.rust == 'stable' || matrix.rust == 'beta' }}
run: cargo +${{ matrix.rust }} fmt --all -- --check
- name: Lints
if: ${{ matrix.rust == 'stable' || matrix.rust == 'beta' }}
run: cargo +${{ matrix.rust }} clippy --all-targets -- -D warnings
# Publish to crates.io
- name: Publish
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_TOKEN }}
run: cargo publish --no-verify
webbrowser-1.0.5/.github/workflows/wasm.yaml 0000644 0000000 0000000 00000004417 10461020230 0017246 0 ustar 0000000 0000000 name: WASM
on:
push:
branches-ignore: [ '**win**', '**android**', '**linux**', '**bsd**', '**haiku**', '**macos**', '**ios**' ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 0 * * SUN'
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
RUST_LOG: webbrowser=TRACE
WASM_PACK_VERSION: '0.13.1'
jobs:
build:
name: Build
runs-on: ubuntu-latest
if: ${{ !contains(github.event.head_commit.message, '#build-') || contains(github.event.head_commit.message, '#build-wasm') }}
strategy:
max-parallel: 1
matrix:
rust: [stable, beta]
wasmarch: [wasm32-unknown-unknown]
continue-on-error: ${{ matrix.rust == 'beta' || matrix.wasmarch == 'wasm64-unknown-unknown' }}
steps:
- uses: actions/checkout@v3
name: Checkout
- name: Install rust version
run: |
rustup install ${{ matrix.rust }} --profile minimal
rustup component add --toolchain ${{ matrix.rust }} rustfmt clippy
rustup target add ${{ matrix.wasmarch }}
# Install wasm-pack
- uses: actions/cache@v3
name: Fetch wasm-pack from cache
id: wasm-pack-cache
with:
path: ~/.cargo/bin/wasm-pack
key: wasm-pack-${{ env.WASM_PACK_VERSION }}
- name: Install wasm-pack
if: steps.wasm-pack-cache.outputs.cache-hit != 'true'
run: cargo install wasm-pack --version ${{ env.WASM_PACK_VERSION }}
# Compile using WEBBROWSER_WASM_TARGET=_self so that we don't encounter popup blockers
- name: Compile
env:
RUST_LOG: info
WEBBROWSER_WASM_TARGET: _self
run: rustup run ${{ matrix.rust }} wasm-pack build --target web --dev
working-directory: tests/test-wasm-app
# Run tests
- name: Run tests
env:
BROWSER: firefox --screenshot %s
run: cargo +${{ matrix.rust }} test --test test_wasm -- --ignored
# Code format, linting etc.
- name: Check Code Formatting
if: ${{ matrix.rust == 'stable' || matrix.rust == 'beta' }}
run: cargo +${{ matrix.rust }} fmt --all -- --check
- name: Lints
if: ${{ matrix.rust == 'stable' || matrix.rust == 'beta' }}
run: cargo +${{ matrix.rust }} clippy --target ${{ matrix.wasmarch }} -- -D warnings
webbrowser-1.0.5/.github/workflows/windows.yaml 0000644 0000000 0000000 00000003035 10461020230 0017764 0 ustar 0000000 0000000 name: Windows
on:
push:
branches-ignore: [ '**wasm**', '**android**', '**linux**', '**bsd**', '**haiku**', '**macos**', '**ios**' ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 0 1 * *'
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
RUST_LOG: webbrowser=TRACE
jobs:
build:
name: Build
runs-on: windows-latest
if: ${{ !contains(github.event.head_commit.message, '#build-') || contains(github.event.head_commit.message, '#build-windows') }}
strategy:
max-parallel: 1
matrix:
rust: [stable, beta]
continue-on-error: ${{ matrix.rust != 'stable' && matrix.rust != 'beta' }}
steps:
- uses: actions/checkout@v3
name: Checkout
- name: Install specific rust version
run: |
rustup install ${{ matrix.rust }} --profile minimal
rustup component add --toolchain ${{ matrix.rust }} rustfmt clippy
- name: Compile
run: cargo +${{ matrix.rust }} build
- name: Run Tests
run: cargo +${{ matrix.rust }} test --all --locked --verbose
- name: Run Tests (hardened)
run: cargo +${{ matrix.rust }} test --features hardened --locked --verbose --test test_windows tests::test_hardened_mode
- name: Check Code Formatting
if: ${{ matrix.rust == 'stable' || matrix.rust == 'beta' }}
run: cargo +${{ matrix.rust }} fmt --all -- --check
- name: Lints
if: ${{ matrix.rust == 'stable' || matrix.rust == 'beta' }}
run: cargo +${{ matrix.rust }} clippy --all-targets -- -D warnings
webbrowser-1.0.5/.gitignore 0000644 0000000 0000000 00000000126 10461020230 0013777 0 ustar 0000000 0000000 /target/
/Cargo.lock
/.idea/
/*.code-workspace
/screenshot.png
**/.DS_Store
/.vscode/
webbrowser-1.0.5/CHANGELOG.md 0000644 0000000 0000000 00000030646 10461020230 0013632 0 ustar 0000000 0000000 # Changelog
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [1.0.5] - 2025-03-10
### Fixed
- Unix: Fix build on redox os. See PR #104
- macOS: Fix packed alignment of LSLaunchURLSpec. See PR #102
### Added
- WASM: support wasm64
## [1.0.4] - 2025-03-10
### Changed
- iOS: Return appropriate errors if called from non-main thread or before UIApplication initialization. See PRs #95 and #99
## [1.0.3] - 2024-11-28
### Added
- Unix: add support for solaris
## [1.0.2] - 2024-09-16
### Changed
- macOS: move to core-foundation 0.10
## [1.0.1] - 2024-05-06
### Added
- Support for visionOS. See PR #86 and #87
## [1.0.0] - 2024-04-20
### Added
- Move to 1.0! MSRV has now been defined too.
### Fixed
- Unix: default to standard unix implementation, instead of whitelisting each flavour of unix
- WASM: return an error on dry_run if window object isn't available
## [0.8.15] - 2024-04-10
### Fixed
- Fix crash on iOS. See #83 and #84
## [0.8.14] - 2024-04-06
### Added
- Illumos: experimental support. See PR #82
### Fixed
- Previously configured MSRV was stale (i.e. wouldn't have compiled), and has been modified to 1.60. This is being treated as bug, and as such not requiring a major release.
## [0.8.13] - 2024-03-05
### Added
- AIX: experimental support. See PR #79
## [0.8.12] - 2023-10-11
### Fixed
- Linux: fix message about BROWSER env variable. See PR #76
## [0.8.11] - 2023-08-17
### Fixed
- WSL: handle `&` in URL correctly in WSL environment. See issue #73 and PR #74
## [0.8.10] - 2023-04-12
### Changed
- Linux: move to `home` as a dependency, instead of `dirs`
## [0.8.9] - 2023-04-12
### Added
- Linux: add support for running under Flatpak sandboxes. See issue #67 and PR #70
### Fixed
- Windows: fix a bug where browser command parsing failed. See issue #68 and PR #69
## [0.8.8] - 2023-01-30
### Changed
- Android: bumped `jni` dependency version to `0.21`
## [0.8.7] - 2023-01-30
### Fixed
- Fixes a bug on WSL, when `xdg-settings` executes successfully but returns no default browser name. Thanks to [@krsh732](https://github.com/krsh732). See #64.
## [0.8.6] - 2023-01-26
### Fixed
- For Windows 32-bit, fix ABI to be used, which was broken in v0.8.5. Thanks to [@alula](https://github.com/alula). See #62 and #63.
## [0.8.5] - 2022-12-31
### Fixed
- For Windows platform, removes the `windows` crate dependency, relying on selective FFI bindings instead, thus avoiding the large dependency.
See #62. Thanks to [@Jake-Shadle](https://github.com/Jake-Shadle).
## [0.8.4] - 2022-12-31
### Fixed
- Urgent bug fix for windows, where rendering broke on Firefox & Chrome. See #60
## [0.8.3] - 2022-12-30
### Added
- Web browser is guaranteed to open for local files even if local file association was to a non-browser app (say an editor). This now is formally
incorporated as part of this crate's [Consistent Behaviour](https://github.com/amodm/webbrowser-rs/blob/main/README.md#consistent-behaviour)
- WSL support, thanks to [@Nachtalb](https://github.com/Nachtalb). This works even if `wslu` is not installed in WSL environments.
- A new feature `hardened` now available for applications which require only http(s) urls to be opened. This acts as a security feature.
### Changed
- On macOS, we now use `CoreFoundation` library instead of `open` command.
- On Linux/*BSD, we now parse xdg configuration to execute the command directly, instead of using `xdg-open` command. This allows us to open the
browser for local html files, even if the `.html` extension was associated with an edit (see #55)
### Fixed
- The guarantee of web browser being opened (instead of local file association), now solves for the scenario where the URL is crafted to be an
executable. This was reported privately by [@offalltn](https://github.com/offalltn).
## [0.8.2] - 2022-11-08
### Fixed
- Fix app crashes when running under termux on Android. See #53 and #54.
## [0.8.1] - 2022-11-01
### Fixed
- On Android, app crashes due to ndk-glue dependency. See #51 and #52. Thanks to [@rib](https://github.com/rib) for the fix.
## [0.8.0] - 2022-09-09
### Added
- Support for iOS is finally here. Thanks to [hakolao](https://github.com/hakolao) for this. See [PR #48](https://github.com/amodm/webbrowser-rs/pull/48)
### Changed
- Updated all dependencies to current versions
## [0.7.1] - 2022-04-27
### Added
- Introduce `Browser::is_available()` and `Browser::exists(&self)` to check availability of browsers without opening a URL
### Changed
- Modify `BrowserOptions` to be constructable only via the builder pattern, to prevent future API compatibility issues
## [0.7.0] - 2022-04-24
### Added
- Introduce way to provide a target hint to the browser via `BrowserOptions::target_hint` [PR #45](https://github.com/amodm/webbrowser-rs/pull/45)
### Changed
- Breaking API change for users of `BrowserOptions`. We've now shifted to a non-consuming builder pattern to avoid future breakages, as more items get added to `BrowserOptions`
## [0.6.0] - 2022-02-19
### Changed
- Define consistent non-blocking behaviour on all UNIX platforms. Now, unless it's specifically a text browser (like lynx etc.), we make sure that the browser is launched in a non-blocking way. See #18 and https://github.com/amodm/webbrowser-rs/commit/614cacf4a67ae0a75323768a1d70c16d792a760d
- Define default behaviour on all UNIX platforms to make sure that stdout/stderr are suppressed. See #20 and https://github.com/amodm/webbrowser-rs/commit/ecfbf66daa0cc139bd557bd7899a183bd6575990
- (Low probability) breaking change: All public functions now return a `Result<()>`. As almost all the uses of this library do a `.is_ok()` or equivalent, there should not be any breaks, but please report a bug if you do. See #42 and #43
- @VZout modified Android implementation to use JNI instead of `am start` because of permission issues in more recent Android.
- Define consistent behaviour for non-ascii URLs, where they're now encoded automatically before being invoked. See #34 and https://github.com/amodm/webbrowser-rs/commit/11789ddfe36264bbbe7d596ab61e3fff855c3adb
- Richer set of underlying commands used for UNIX to cater to different scenarios at runtime. See https://github.com/amodm/webbrowser-rs/commit/d09eeae4f2ab5664fc01f4dba4a409e1bc11f10e
### Fixed
- On WASM, by default URLs are opened with a target of `_blank`. See #39. Thanks to @vbeffa for pointing out the issue.
- @tokusumi fixed #41 where addition of `open` command (done for Haiku) was breaking things in some places.
## [0.5.5] - 2020-07-20
### Added
- Support for WASM [PR #26](https://github.com/amodm/webbrowser-rs/pull/26)
## [0.5.4] - 2020-06-09
### Fixed
- Fix README to reflect platform support for Android and Haiku
## [0.5.3] - 2020-06-09
### Changed
- Added support for Haiku (Untested right now!) [PR #21](https://github.com/amodm/webbrowser-rs/pull/21)
- Added support for Android [PR #19](https://github.com/amodm/webbrowser-rs/pull/19)
- Added support for kioclient and x-www-browser [PR #17](https://github.com/amodm/webbrowser-rs/pull/17)
## [0.5.2] - 2019-08-22
### Fixed
- Fix a COM leak bug on Windows [PR #15](https://github.com/amodm/webbrowser-rs/pull/15)
## [0.5.1] - 2019-04-01
### Fixed
- Fix the behaviour that open() was blocking on Linux and BSD family. [Issue #13](https://github.com/amodm/webbrowser-rs/issues/13)
- Fix tests on macos
## [0.5.0] - 2019-03-31
### Added
- Add BSD family to supported platforms. [PR #12](https://github.com/amodm/webbrowser-rs/pull/12)
## [0.4.0] - 2018-12-18
### Changed
- Use `ShellExecuteW` on Windows as the earlier approach of using cmd.exe was breaking on
special characters. [PR #11](https://github.com/amodm/webbrowser-rs/pull/11)
### Fixed
- Fixed Apache Licensing format
## [0.3.1] - 2018-06-22
### Fixed
- Fix broken examples header. [PR #7](https://github.com/amodm/webbrowser-rs/pull/7)
- Fix undeclared reference to `env` that breaks Linux. [PR #8](https://github.com/amodm/webbrowser-rs/pull/8)
## [0.3.0] - 2018-06-18
### Changed
- Change the OS test to use conditional complication and raise a compile error if the target OS is unsupported.
[PR #6](https://github.com/amodm/webbrowser-rs/pull/6)
- Implement useful trait from StdLib for Browser such as `Display`, `Default` and `FromStr`.
[PR #6](https://github.com/amodm/webbrowser-rs/pull/6)
### Fixed
- Fix the command in `open_on_windows` to use `cmd.exe` instead of `start`. [PR #5](https://github.com/amodm/webbrowser-rs/pull/5)
## [0.2.2] - 2017-01-23
### Fixed
- Honour the right syntax for `$BROWSER`. Closes [#3](https://github.com/amodm/webbrowser-rs/issues/3)
- Include `gvfs-open` and `gnome-open` for [#2](https://github.com/amodm/webbrowser-rs/issues/2)
## [0.2.1] - 2017-01-22
### Changed
- Honour `$BROWSER` env var on Linux, before choosing to fallback to `xdg-open`. [Issue #2](https://github.com/amodm/webbrowser-rs/issues/2)
## [0.1.3] - 2016-01-11
### Added
- Add Apache license [Issue #1](https://github.com/amodm/webbrowser-rs/issues/1)
## [0.1.2] - 2015-12-09
### Added
- Initial release.
[Unreleased]: https://github.com/amodm/webbrowser-rs/compare/v1.0.5...HEAD
[1.0.5]: https://github.com/amodm/webbrowser-rs/compare/v1.0.4...v1.0.5
[1.0.4]: https://github.com/amodm/webbrowser-rs/compare/v1.0.3...v1.0.4
[1.0.3]: https://github.com/amodm/webbrowser-rs/compare/v1.0.2...v1.0.3
[1.0.2]: https://github.com/amodm/webbrowser-rs/compare/v1.0.1...v1.0.2
[1.0.1]: https://github.com/amodm/webbrowser-rs/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/amodm/webbrowser-rs/compare/v0.8.15...v1.0.0
[0.8.15]: https://github.com/amodm/webbrowser-rs/compare/v0.8.14...v0.8.15
[0.8.14]: https://github.com/amodm/webbrowser-rs/compare/v0.8.13...v0.8.14
[0.8.13]: https://github.com/amodm/webbrowser-rs/compare/v0.8.12...v0.8.13
[0.8.12]: https://github.com/amodm/webbrowser-rs/compare/v0.8.11...v0.8.12
[0.8.11]: https://github.com/amodm/webbrowser-rs/compare/v0.8.10...v0.8.11
[0.8.10]: https://github.com/amodm/webbrowser-rs/compare/v0.8.9...v0.8.10
[0.8.9]: https://github.com/amodm/webbrowser-rs/compare/v0.8.8...v0.8.9
[0.8.8]: https://github.com/amodm/webbrowser-rs/compare/v0.8.7...v0.8.8
[0.8.7]: https://github.com/amodm/webbrowser-rs/compare/v0.8.6...v0.8.7
[0.8.6]: https://github.com/amodm/webbrowser-rs/compare/v0.8.5...v0.8.6
[0.8.5]: https://github.com/amodm/webbrowser-rs/compare/v0.8.4...v0.8.5
[0.8.4]: https://github.com/amodm/webbrowser-rs/compare/v0.8.3...v0.8.4
[0.8.3]: https://github.com/amodm/webbrowser-rs/compare/v0.8.2...v0.8.3
[0.8.2]: https://github.com/amodm/webbrowser-rs/compare/v0.8.1...v0.8.2
[0.8.1]: https://github.com/amodm/webbrowser-rs/compare/v0.8.0...v0.8.1
[0.8.0]: https://github.com/amodm/webbrowser-rs/compare/v0.7.1...v0.8.0
[0.7.1]: https://github.com/amodm/webbrowser-rs/compare/v0.7.0...v0.7.1
[0.7.0]: https://github.com/amodm/webbrowser-rs/compare/v0.6.0...v0.7.0
[0.6.0]: https://github.com/amodm/webbrowser-rs/compare/v0.5.5...v0.6.0
[0.5.5]: https://github.com/amodm/webbrowser-rs/compare/v0.5.4...v0.5.5
[0.5.4]: https://github.com/amodm/webbrowser-rs/compare/v0.5.3...v0.5.4
[0.5.3]: https://github.com/amodm/webbrowser-rs/compare/v0.5.2...v0.5.3
[0.5.2]: https://github.com/amodm/webbrowser-rs/compare/v0.5.1...v0.5.2
[0.5.1]: https://github.com/amodm/webbrowser-rs/compare/v0.5.0...v0.5.1
[0.5.0]: https://github.com/amodm/webbrowser-rs/compare/v0.4.0...v0.5.0
[0.4.0]: https://github.com/amodm/webbrowser-rs/compare/v0.3.1...v0.4.0
[0.3.1]: https://github.com/amodm/webbrowser-rs/compare/v0.3.0...v0.3.1
[0.3.0]: https://github.com/amodm/webbrowser-rs/compare/v0.2.2...v0.3.0
[0.2.2]: https://github.com/amodm/webbrowser-rs/compare/v0.2.1...v0.2.2
[0.2.1]: https://github.com/amodm/webbrowser-rs/compare/v0.1.3...v0.2.1
[0.1.3]: https://github.com/amodm/webbrowser-rs/compare/v0.1.2...v0.1.3
webbrowser-1.0.5/Cargo.lock 0000644 00000162470 00000000001 0011205 0 ustar # This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "actix-codec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a"
dependencies = [
"bitflags 2.9.1",
"bytes",
"futures-core",
"futures-sink",
"memchr",
"pin-project-lite",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "actix-files"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0773d59061dedb49a8aed04c67291b9d8cf2fe0b60130a381aab53c6dd86e9be"
dependencies = [
"actix-http",
"actix-service",
"actix-utils",
"actix-web",
"bitflags 2.9.1",
"bytes",
"derive_more 0.99.20",
"futures-core",
"http-range",
"log",
"mime",
"mime_guess",
"percent-encoding",
"pin-project-lite",
"v_htmlescape",
]
[[package]]
name = "actix-http"
version = "3.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44dfe5c9e0004c623edc65391dfd51daa201e7e30ebd9c9bedf873048ec32bc2"
dependencies = [
"actix-codec",
"actix-rt",
"actix-service",
"actix-utils",
"base64",
"bitflags 2.9.1",
"brotli",
"bytes",
"bytestring",
"derive_more 2.0.1",
"encoding_rs",
"flate2",
"foldhash",
"futures-core",
"h2",
"http",
"httparse",
"httpdate",
"itoa",
"language-tags",
"local-channel",
"mime",
"percent-encoding",
"pin-project-lite",
"rand 0.9.1",
"sha1",
"smallvec",
"tokio",
"tokio-util",
"tracing",
"zstd",
]
[[package]]
name = "actix-macros"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
dependencies = [
"quote",
"syn 2.0.103",
]
[[package]]
name = "actix-router"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8"
dependencies = [
"bytestring",
"cfg-if",
"http",
"regex",
"regex-lite",
"serde",
"tracing",
]
[[package]]
name = "actix-rt"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208"
dependencies = [
"futures-core",
"tokio",
]
[[package]]
name = "actix-server"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a65064ea4a457eaf07f2fba30b4c695bf43b721790e9530d26cb6f9019ff7502"
dependencies = [
"actix-rt",
"actix-service",
"actix-utils",
"futures-core",
"futures-util",
"mio",
"socket2",
"tokio",
"tracing",
]
[[package]]
name = "actix-service"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e46f36bf0e5af44bdc4bdb36fbbd421aa98c79a9bce724e1edeb3894e10dc7f"
dependencies = [
"futures-core",
"pin-project-lite",
]
[[package]]
name = "actix-utils"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8"
dependencies = [
"local-waker",
"pin-project-lite",
]
[[package]]
name = "actix-web"
version = "4.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a597b77b5c6d6a1e1097fddde329a83665e25c5437c696a3a9a4aa514a614dea"
dependencies = [
"actix-codec",
"actix-http",
"actix-macros",
"actix-router",
"actix-rt",
"actix-server",
"actix-service",
"actix-utils",
"actix-web-codegen",
"bytes",
"bytestring",
"cfg-if",
"cookie",
"derive_more 2.0.1",
"encoding_rs",
"foldhash",
"futures-core",
"futures-util",
"impl-more",
"itoa",
"language-tags",
"log",
"mime",
"once_cell",
"pin-project-lite",
"regex",
"regex-lite",
"serde",
"serde_json",
"serde_urlencoded",
"smallvec",
"socket2",
"time",
"tracing",
"url",
]
[[package]]
name = "actix-web-codegen"
version = "4.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8"
dependencies = [
"actix-router",
"proc-macro2",
"quote",
"syn 2.0.103",
]
[[package]]
name = "addr2line"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "alloc-no-stdlib"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
[[package]]
name = "alloc-stdlib"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
dependencies = [
"alloc-no-stdlib",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "backtrace"
version = "0.3.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets 0.52.6",
]
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "brotli"
version = "8.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
"brotli-decompressor",
]
[[package]]
name = "brotli-decompressor"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
]
[[package]]
name = "bumpalo"
version = "3.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee"
[[package]]
name = "bytes"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "bytestring"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e465647ae23b2823b0753f50decb2d5a86d2bb2cac04788fafd1f80e45378e5f"
dependencies = [
"bytes",
]
[[package]]
name = "cc"
version = "1.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
dependencies = [
"jobserver",
"libc",
"shlex",
]
[[package]]
name = "cesu8"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cfg-if"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "combine"
version = "4.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
dependencies = [
"bytes",
"memchr",
]
[[package]]
name = "convert_case"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "cookie"
version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
dependencies = [
"percent-encoding",
"time",
"version_check",
]
[[package]]
name = "core-foundation"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "darling"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 1.0.109",
]
[[package]]
name = "darling_macro"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
dependencies = [
"darling_core",
"quote",
"syn 1.0.109",
]
[[package]]
name = "dashmap"
version = "5.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
dependencies = [
"cfg-if",
"hashbrown 0.14.5",
"lock_api",
"once_cell",
"parking_lot_core",
]
[[package]]
name = "deranged"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
dependencies = [
"powerfmt",
]
[[package]]
name = "derive_more"
version = "0.99.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"rustc_version",
"syn 2.0.103",
]
[[package]]
name = "derive_more"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
dependencies = [
"derive_more-impl",
]
[[package]]
name = "derive_more-impl"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"unicode-xid",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "displaydoc"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
]
[[package]]
name = "encoding_rs"
version = "0.8.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
dependencies = [
"cfg-if",
]
[[package]]
name = "env_logger"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "flate2"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "form_urlencoded"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
[[package]]
name = "futures"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-executor"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-sink"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
[[package]]
name = "futures-task"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-util"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"libc",
"wasi 0.11.1+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi 0.14.2+wasi-0.2.4",
]
[[package]]
name = "gimli"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "h2"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
dependencies = [
"bytes",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http",
"indexmap",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "hashbrown"
version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "http"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "http-range"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573"
[[package]]
name = "httparse"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "httpdate"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "humantime"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f"
[[package]]
name = "icu_collections"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
dependencies = [
"displaydoc",
"potential_utf",
"yoke",
"zerofrom",
"zerovec",
]
[[package]]
name = "icu_locale_core"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
dependencies = [
"displaydoc",
"litemap",
"tinystr",
"writeable",
"zerovec",
]
[[package]]
name = "icu_normalizer"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
dependencies = [
"displaydoc",
"icu_collections",
"icu_normalizer_data",
"icu_properties",
"icu_provider",
"smallvec",
"zerovec",
]
[[package]]
name = "icu_normalizer_data"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
[[package]]
name = "icu_properties"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
dependencies = [
"displaydoc",
"icu_collections",
"icu_locale_core",
"icu_properties_data",
"icu_provider",
"potential_utf",
"zerotrie",
"zerovec",
]
[[package]]
name = "icu_properties_data"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
[[package]]
name = "icu_provider"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
dependencies = [
"displaydoc",
"icu_locale_core",
"stable_deref_trait",
"tinystr",
"writeable",
"yoke",
"zerofrom",
"zerotrie",
"zerovec",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
dependencies = [
"idna_adapter",
"smallvec",
"utf8_iter",
]
[[package]]
name = "idna_adapter"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
dependencies = [
"icu_normalizer",
"icu_properties",
]
[[package]]
name = "impl-more"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2"
[[package]]
name = "indexmap"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [
"equivalent",
"hashbrown 0.15.4",
]
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "jni"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
dependencies = [
"cesu8",
"cfg-if",
"combine",
"jni-sys",
"log",
"thiserror",
"walkdir",
"windows-sys 0.45.0",
]
[[package]]
name = "jni-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jobserver"
version = "0.1.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
dependencies = [
"getrandom 0.3.3",
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "language-tags"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.173"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb"
[[package]]
name = "litemap"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
[[package]]
name = "local-channel"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8"
dependencies = [
"futures-core",
"futures-sink",
"local-waker",
]
[[package]]
name = "local-waker"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487"
[[package]]
name = "lock_api"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "memchr"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mime_guess"
version = "2.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
dependencies = [
"mime",
"unicase",
]
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
]
[[package]]
name = "mio"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
dependencies = [
"libc",
"log",
"wasi 0.11.1+wasi-snapshot-preview1",
"windows-sys 0.59.0",
]
[[package]]
name = "ndk"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0"
dependencies = [
"bitflags 1.3.2",
"jni-sys",
"ndk-sys",
"num_enum",
"raw-window-handle",
"thiserror",
]
[[package]]
name = "ndk-context"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
[[package]]
name = "ndk-glue"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0434fabdd2c15e0aab768ca31d5b7b333717f03cf02037d5a0a3ff3c278ed67f"
dependencies = [
"libc",
"log",
"ndk",
"ndk-context",
"ndk-macro",
"ndk-sys",
"once_cell",
"parking_lot",
]
[[package]]
name = "ndk-macro"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c"
dependencies = [
"darling",
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "ndk-sys"
version = "0.4.1+23.1.7779620"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3"
dependencies = [
"jni-sys",
]
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num_enum"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
dependencies = [
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "objc2"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551"
dependencies = [
"objc2-encode",
]
[[package]]
name = "objc2-encode"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
[[package]]
name = "objc2-foundation"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c"
dependencies = [
"bitflags 2.9.1",
"objc2",
]
[[package]]
name = "object"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "parking_lot"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets 0.52.6",
]
[[package]]
name = "percent-encoding"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "potential_utf"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
dependencies = [
"zerovec",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro-crate"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [
"once_cell",
"toml_edit",
]
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha 0.3.1",
"rand_core 0.6.4",
]
[[package]]
name = "rand"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.3",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core 0.9.3",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.16",
]
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom 0.3.3",
]
[[package]]
name = "raw-window-handle"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
[[package]]
name = "redox_syscall"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
dependencies = [
"bitflags 2.9.1",
]
[[package]]
name = "regex"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-lite"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a"
[[package]]
name = "regex-syntax"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "rustc-demangle"
version = "0.1.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "semver"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
]
[[package]]
name = "serde_json"
version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serial_test"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c789ec87f4687d022a2405cf46e0cd6284889f1839de292cadeb6c6019506f2"
dependencies = [
"dashmap",
"futures",
"lazy_static",
"log",
"parking_lot",
"serial_test_derive",
]
[[package]]
name = "serial_test_derive"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b64f9e531ce97c88b4778aad0ceee079216071cffec6ac9b904277f8f92e7fe3"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "sha1"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook-registry"
version = "1.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "socket2"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "synstructure"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
]
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
]
[[package]]
name = "time"
version = "0.3.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
dependencies = [
"deranged",
"itoa",
"num-conv",
"powerfmt",
"serde",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
[[package]]
name = "time-macros"
version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
dependencies = [
"num-conv",
"time-core",
]
[[package]]
name = "tinystr"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
dependencies = [
"displaydoc",
"zerovec",
]
[[package]]
name = "tokio"
version = "1.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
dependencies = [
"backtrace",
"bytes",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.52.0",
]
[[package]]
name = "tokio-macros"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
]
[[package]]
name = "tokio-util"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
]
[[package]]
name = "toml_datetime"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
[[package]]
name = "toml_edit"
version = "0.19.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap",
"toml_datetime",
"winnow",
]
[[package]]
name = "tracing"
version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [
"log",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
]
[[package]]
name = "tracing-core"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
dependencies = [
"once_cell",
]
[[package]]
name = "typenum"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
[[package]]
name = "unicase"
version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "url"
version = "2.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
]
[[package]]
name = "urlencoding"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]]
name = "utf8_iter"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "v_htmlescape"
version = "0.15.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e8257fbc510f0a46eb602c10215901938b5c2a7d5e70fc11483b1d3c9b5b18c"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
dependencies = [
"cfg-if",
"once_cell",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
dependencies = [
"bumpalo",
"log",
"proc-macro2",
"quote",
"syn 2.0.103",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
dependencies = [
"unicode-ident",
]
[[package]]
name = "web-sys"
version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "webbrowser"
version = "1.0.5"
dependencies = [
"actix-files",
"actix-web",
"core-foundation",
"crossbeam-channel",
"env_logger",
"jni",
"log",
"ndk-context",
"ndk-glue",
"objc2",
"objc2-foundation",
"rand 0.8.5",
"serial_test",
"tokio",
"url",
"urlencoding",
"web-sys",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags 2.9.1",
]
[[package]]
name = "writeable"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
[[package]]
name = "yoke"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
dependencies = [
"serde",
"stable_deref_trait",
"yoke-derive",
"zerofrom",
]
[[package]]
name = "yoke-derive"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"synstructure",
]
[[package]]
name = "zerocopy"
version = "0.8.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
]
[[package]]
name = "zerofrom"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
dependencies = [
"zerofrom-derive",
]
[[package]]
name = "zerofrom-derive"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"synstructure",
]
[[package]]
name = "zerotrie"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
dependencies = [
"displaydoc",
"yoke",
"zerofrom",
]
[[package]]
name = "zerovec"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
dependencies = [
"yoke",
"zerofrom",
"zerovec-derive",
]
[[package]]
name = "zerovec-derive"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
]
[[package]]
name = "zstd"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "7.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
dependencies = [
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "2.0.15+zstd.1.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237"
dependencies = [
"cc",
"pkg-config",
]
webbrowser-1.0.5/Cargo.toml 0000644 00000005333 00000000001 0011222 0 ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.60"
name = "webbrowser"
version = "1.0.5"
authors = ["Amod Malviya @amodm"]
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Open URLs in web browsers available on a platform"
homepage = "https://github.com/amodm/webbrowser-rs"
documentation = "https://docs.rs/webbrowser"
readme = "README.md"
keywords = [
"webbrowser",
"browser",
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/amodm/webbrowser-rs"
[features]
disable-wsl = []
hardened = []
wasm-console = ["web-sys/console"]
[lib]
name = "webbrowser"
path = "src/lib.rs"
[[test]]
name = "common"
path = "tests/common.rs"
[[test]]
name = "test_android"
path = "tests/test_android.rs"
[[test]]
name = "test_ios"
path = "tests/test_ios.rs"
[[test]]
name = "test_macos"
path = "tests/test_macos.rs"
[[test]]
name = "test_unix"
path = "tests/test_unix.rs"
[[test]]
name = "test_wasm"
path = "tests/test_wasm.rs"
[[test]]
name = "test_windows"
path = "tests/test_windows.rs"
[dependencies.log]
version = "0.4"
[dependencies.url]
version = "2"
[dev-dependencies.actix-files]
version = "0.6"
[dev-dependencies.actix-web]
version = "4"
[dev-dependencies.crossbeam-channel]
version = "0.5"
[dev-dependencies.env_logger]
version = "0.9.0"
[dev-dependencies.rand]
version = "0.8"
[dev-dependencies.serial_test]
version = "0.10"
[dev-dependencies.tokio]
version = "1"
features = ["full"]
[dev-dependencies.urlencoding]
version = "2.1"
[target.'cfg(any(target_os = "ios", target_os = "tvos", target_os = "visionos"))'.dependencies.objc2]
version = "0.6"
[target.'cfg(any(target_os = "ios", target_os = "tvos", target_os = "visionos"))'.dependencies.objc2-foundation]
version = "0.3"
features = [
"std",
"NSDictionary",
"NSString",
"NSURL",
]
default-features = false
[target.'cfg(target_family = "wasm")'.dependencies.web-sys]
version = "0.3"
features = ["Window"]
[target.'cfg(target_os = "android")'.dependencies.jni]
version = "0.21"
[target.'cfg(target_os = "android")'.dependencies.ndk-context]
version = "0.1"
[target.'cfg(target_os = "android")'.dev-dependencies.ndk-glue]
version = ">= 0.3, <= 0.7"
[target.'cfg(target_os = "macos")'.dependencies.core-foundation]
version = "0.10"
webbrowser-1.0.5/Cargo.toml.orig 0000644 0000000 0000000 00000002513 10461020230 0014700 0 ustar 0000000 0000000 [package]
name = "webbrowser"
description = "Open URLs in web browsers available on a platform"
version = "1.0.5"
authors = ["Amod Malviya @amodm"]
documentation = "https://docs.rs/webbrowser"
homepage = "https://github.com/amodm/webbrowser-rs"
repository = "https://github.com/amodm/webbrowser-rs"
readme = "README.md"
keywords = ["webbrowser", "browser"]
license = "MIT OR Apache-2.0"
edition = "2021"
rust-version = "1.60"
[dependencies]
log = "0.4"
url = "2"
[target.'cfg(target_family = "wasm")'.dependencies.web-sys]
version = "0.3"
features = ['Window']
[features]
hardened = []
disable-wsl = []
wasm-console = ["web-sys/console"]
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.10"
[target.'cfg(target_os = "android")'.dependencies]
jni = "0.21"
ndk-context = "0.1"
[target.'cfg(any(target_os = "ios", target_os = "tvos", target_os = "visionos"))'.dependencies]
objc2 = "0.6"
objc2-foundation = { version = "0.3", default-features = false, features = [
"std",
"NSDictionary",
"NSString",
"NSURL",
] }
[dev-dependencies]
actix-web = "4"
actix-files = "0.6"
crossbeam-channel = "0.5"
env_logger = "0.9.0"
rand = "0.8"
serial_test = "0.10"
tokio = { version = "1", features = ["full"] }
urlencoding = "2.1"
[target.'cfg(target_os = "android")'.dev-dependencies]
ndk-glue = { version = ">= 0.3, <= 0.7" }
webbrowser-1.0.5/LICENSE-APACHE 0000644 0000000 0000000 00000024053 10461020230 0013740 0 ustar 0000000 0000000 Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright (c) 2015-2022 Amod Malviya
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
webbrowser-1.0.5/LICENSE-MIT 0000644 0000000 0000000 00000002074 10461020230 0013447 0 ustar 0000000 0000000 The MIT License (MIT)
Copyright (c) 2015-2022 Amod Malviya
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
webbrowser-1.0.5/README.md 0000644 0000000 0000000 00000010014 10461020230 0013263 0 ustar 0000000 0000000 # webbrowser
[](https://crates.io/crates/webbrowser)
[](https://crates.io/crates/webbrowser)
[](#license)






Rust library to open URLs and local files in the web browsers available on a platform, with guarantees of [Consistent Behaviour](#consistent-behaviour).
Inspired by the [webbrowser](https://docs.python.org/2/library/webbrowser.html) python library
## Documentation
- [API Reference](https://docs.rs/webbrowser)
- [Release Notes](CHANGELOG.md)
## Examples
```rust
use webbrowser;
if webbrowser::open("http://github.com").is_ok() {
// ...
}
```
## Platform Support
| Platform | Supported | Browsers | Test status |
|----------|-----------|----------|-------------|
| macos | ✅ | default + [others](https://docs.rs/webbrowser/latest/webbrowser/enum.Browser.html) | ✅ |
| windows | ✅ | default only | ✅ |
| linux/wsl | ✅ | default only (respects $BROWSER env var, so can be used with other browsers) | ✅ |
| android | ✅ | default only | ✅ |
| ios/tvOS/visionOS | ✅ | default only | ✅ |
| wasm | ✅ | default only | ✅ |
| unix (*bsd, aix etc.) | ✅ | default only (respects $BROWSER env var, so can be used with other browsers) | Manual |
## Consistent Behaviour
`webbrowser` defines consistent behaviour on all platforms as follows:
* **Browser guarantee** - This library guarantees that the browser is opened, even for local files - the only crate to make such guarantees
at the time of this writing. Alternative libraries rely on existing system commands, which may lead to an editor being opened (instead
of the browser) for local html files, leading to an inconsistent behaviour for users.
* **Non-Blocking** for GUI based browsers (e.g. Firefox, Chrome etc.), while **Blocking** for text based browser (e.g. lynx etc.)
* **Suppressed output** by default for GUI based browsers, so that their stdout/stderr don't pollute the main program's output. This can be
overridden by `webbrowser::open_browser_with_options`.
## Crate Features
`webbrowser` optionally allows the following features to be configured:
* `hardened` - this disables handling of non-http(s) urls (e.g. `file:///`) as a hard security precaution
* `disable-wsl` - this disables WSL `file` implementation (`http` still works)
* `wasm-console` - this enables logging to wasm console (valid only on wasm platform)
## Looking to contribute?
PRs invited for
* Bugs
* Supporting non-default browser invocation on any platform
Important note (while testing):
* This library requires availability of browsers and a graphical environment during runtime
* `cargo test` will actually open the browser locally
When contributing, please note that your work will be dual licensed as MIT + Apache-2.0 (see below).
## License
`SPDX-License-Identifier: Apache-2.0 OR MIT`
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any
additional terms or conditions.
webbrowser-1.0.5/SECURITY.md 0000644 0000000 0000000 00000001050 10461020230 0013575 0 ustar 0000000 0000000 # Security Policy
If you're looking to report a security vulnerability, please go to the
[Security -> Advisories](https://github.com/amodm/webbrowser-rs/security/advisories) section, and click on
the `Report a vulnerability` button
What to expect:
* 48hr turnaround to acknowledge the issue
* The closure ETA can be varied depending on the degree of severity and complexity of resolution
* I have nothing to offer as a reward, except a virtual hug :hugs: as thanks for your selfless effort, and
acknowledging your contribution on the release notes.
webbrowser-1.0.5/src/android.rs 0000644 0000000 0000000 00000010516 10461020230 0014570 0 ustar 0000000 0000000 use crate::{Browser, BrowserOptions, Error, ErrorKind, Result, TargetType};
use jni::objects::{JObject, JValue};
use std::process::{Command, Stdio};
/// Deal with opening of browsers on Android. Only [Browser::Default] is supported, and
/// in options, only [BrowserOptions::dry_run] is honoured.
pub(super) fn open_browser_internal(
browser: Browser,
target: &TargetType,
options: &BrowserOptions,
) -> Result<()> {
// ensure we're opening only http/https urls, failing otherwise
let url = target.get_http_url()?;
match browser {
Browser::Default => open_browser_default(url, options),
_ => Err(Error::new(
ErrorKind::NotFound,
"only default browser supported",
)),
}
}
/// Open the default browser
fn open_browser_default(url: &str, options: &BrowserOptions) -> Result<()> {
// always return true for a dry run
if options.dry_run {
return Ok(());
}
// first we try to see if we're in a termux env, because if we are, then
// the android context may not have been initialized, and it'll panic
if try_for_termux(url, options).is_ok() {
return Ok(());
}
// Create a VM for executing Java calls
let ctx = ndk_context::android_context();
let vm = unsafe { jni::JavaVM::from_raw(ctx.vm() as _) }.map_err(|_| {
Error::new(
ErrorKind::NotFound,
"Expected to find JVM via ndk_context crate",
)
})?;
let activity = unsafe { jni::objects::JObject::from_raw(ctx.context() as _) };
let mut env = vm
.attach_current_thread()
.map_err(|_| Error::new(ErrorKind::Other, "Failed to attach current thread"))?;
// Create ACTION_VIEW object
let intent_class = env
.find_class("android/content/Intent")
.map_err(|_| Error::new(ErrorKind::NotFound, "Failed to find Intent class"))?;
let action_view = env
.get_static_field(&intent_class, "ACTION_VIEW", "Ljava/lang/String;")
.map_err(|_| Error::new(ErrorKind::NotFound, "Failed to get intent.ACTION_VIEW"))?;
// Create Uri object
let uri_class = env
.find_class("android/net/Uri")
.map_err(|_| Error::new(ErrorKind::NotFound, "Failed to find Uri class"))?;
let url = env
.new_string(url)
.map_err(|_| Error::new(ErrorKind::Other, "Failed to create JNI string"))?;
let uri = env
.call_static_method(
&uri_class,
"parse",
"(Ljava/lang/String;)Landroid/net/Uri;",
&[JValue::Object(&JObject::from(url))],
)
.map_err(|_| Error::new(ErrorKind::Other, "Failed to parse JNI Uri"))?;
// Create new ACTION_VIEW intent with the uri
let intent = env
.alloc_object(&intent_class)
.map_err(|_| Error::new(ErrorKind::Other, "Failed to allocate intent"))?;
env.call_method(
&intent,
"",
"(Ljava/lang/String;Landroid/net/Uri;)V",
&[action_view.borrow(), uri.borrow()],
)
.map_err(|_| Error::new(ErrorKind::Other, "Failed to initialize intent"))?;
// Start the intent activity.
env.call_method(
&activity,
"startActivity",
"(Landroid/content/Intent;)V",
&[JValue::Object(&intent)],
)
.map_err(|_| Error::new(ErrorKind::Other, "Failed to start activity"))?;
Ok(())
}
/// Attemps to open a browser assuming a termux environment
///
/// See [issue #53](https://github.com/amodm/webbrowser-rs/issues/53)
fn try_for_termux(url: &str, options: &BrowserOptions) -> Result<()> {
use std::env;
if env::var("TERMUX_VERSION").is_ok() {
// return true on dry-run given that termux-open command is guaranteed to be present
if options.dry_run {
return Ok(());
}
let mut cmd = Command::new("termux-open");
cmd.arg(url);
if options.suppress_output {
cmd.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null());
}
cmd.status().and_then(|status| {
if status.success() {
Ok(())
} else {
Err(Error::new(
ErrorKind::Other,
"command present but exited unsuccessfully",
))
}
})
} else {
Err(Error::new(ErrorKind::Other, "Not a termux environment"))
}
}
webbrowser-1.0.5/src/common.rs 0000644 0000000 0000000 00000005217 10461020230 0014442 0 ustar 0000000 0000000 use super::{BrowserOptions, Error, ErrorKind, Result};
use log::debug;
use std::process::{Command, Stdio};
/// Parses `line` to find tokens (including quoted strings), and invokes `op`
/// on each token
pub(crate) fn for_each_token(line: &str, mut op: F)
where
F: FnMut(&str),
{
let mut start: Option = None;
let mut in_quotes = false;
let mut idx = 0;
for ch in line.chars() {
idx += 1;
match ch {
'"' => {
if let Some(start_idx) = start {
op(&line[start_idx..idx - 1]);
start = None;
in_quotes = false;
} else {
start = Some(idx);
in_quotes = true;
}
}
' ' => {
if !in_quotes {
if let Some(start_idx) = start {
op(&line[start_idx..idx - 1]);
start = None;
}
}
}
_ => {
if start.is_none() {
start = Some(idx - 1);
}
}
}
}
if let Some(start_idx) = start {
op(&line[start_idx..idx]);
}
}
/// Run the specified command in foreground/background
pub(crate) fn run_command(
cmd: &mut Command,
background: bool,
options: &BrowserOptions,
) -> Result<()> {
// if dry_run, we return a true, as executable existence check has
// already been done
if options.dry_run {
debug!("dry-run enabled, so not running: {:?}", &cmd);
return Ok(());
}
if background {
debug!("background spawn: {:?}", &cmd);
// if we're in background, set stdin/stdout to null and spawn a child, as we're
// not supposed to have any interaction.
if options.suppress_output {
cmd.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
} else {
cmd
}
.spawn()
.map(|_| ())
} else {
debug!("foreground exec: {:?}", &cmd);
// if we're in foreground, use status() instead of spawn(), as we'd like to wait
// till completion.
// We also specifically don't suppress anything here, because we're running here
// most likely because of a text browser
cmd.status().and_then(|status| {
if status.success() {
Ok(())
} else {
Err(Error::new(
ErrorKind::Other,
"command present but exited unsuccessfully",
))
}
})
}
}
webbrowser-1.0.5/src/ios.rs 0000644 0000000 0000000 00000005177 10461020230 0013751 0 ustar 0000000 0000000 use std::ffi::c_void;
use objc2::{class, msg_send, rc::Retained, Encode, Encoding, MainThreadMarker};
use objc2_foundation::{NSDictionary, NSObject, NSString, NSURL};
use crate::{Browser, BrowserOptions, Error, ErrorKind, Result, TargetType};
/// Returns `UIApplication`
#[allow(non_snake_case)]
fn sharedApplication(mtm: MainThreadMarker) -> Option> {
let _ = mtm;
// SAFETY: The signature is correct, and we hold `MainThreadMarker`, so we
// know we're on the main thread where it's safe to access the shared
// UIApplication.
//
// NOTE: `sharedApplication` is declared as returning non-NULL, but it
// will only do so inside `UIApplicationMain`; if called outside, the
// shared application is NULL.
unsafe { msg_send![class!(UIApplication), sharedApplication] }
}
/// Fake `block` to not have to depend on the `block2` crate just to set this to an empty/`None` block.
#[repr(transparent)]
struct FakeBlock(*const c_void);
// SAFETY: The type is `#[repr(transparent)]` over a pointer (same layout as `Option<&block::Block<...>>`).
unsafe impl Encode for FakeBlock {
const ENCODING: Encoding = Encoding::Block;
}
#[doc(alias = "openURL_options_completionHandler")]
fn open_url(app: &NSObject, url: &NSURL, options: &NSDictionary) {
let fake_handler = FakeBlock(std::ptr::null());
unsafe { msg_send![app, openURL: url, options: options, completionHandler: fake_handler] }
}
/// Deal with opening of browsers on iOS/tvOS/visionOS.
///
/// watchOS doesn't have a browser, so this won't work there.
pub(super) fn open_browser_internal(
_browser: Browser,
target: &TargetType,
options: &BrowserOptions,
) -> Result<()> {
// ensure we're opening only http/https urls, failing otherwise
let url = target.get_http_url()?;
// always return true for a dry run
if options.dry_run {
return Ok(());
}
let mtm = MainThreadMarker::new().ok_or(Error::new(
ErrorKind::Other,
"UIApplication must be retrieved on the main thread",
))?;
let app = sharedApplication(mtm).ok_or(Error::new(
ErrorKind::Other,
"UIApplication is NULL, perhaps UIApplicationMain has not been executed?",
))?;
// Create ns string class from our string
let url_string = NSString::from_str(url);
// Create NSURL object with given string
let url_object = unsafe { NSURL::URLWithString(&url_string) }.ok_or(Error::new(
ErrorKind::Other,
"Failed creating NSURL; is the URL valid?",
))?;
// empty options dictionary
let options = NSDictionary::new();
// Open url
open_url(&app, &url_object, &options);
Ok(())
}
webbrowser-1.0.5/src/lib.rs 0000644 0000000 0000000 00000032743 10461020230 0013724 0 ustar 0000000 0000000 //! Rust library to open URLs and local files in the web browsers available on a platform, with guarantees of [Consistent Behaviour](#consistent-behaviour).
//!
//! Inspired by the [webbrowser](https://docs.python.org/2/library/webbrowser.html) python library.
//!
//! ## Examples
//!
//! ```no_run
//! use webbrowser;
//!
//! if webbrowser::open("http://github.com").is_ok() {
//! // ...
//! }
//! ```
//!
//! ## Platform Support Status
//!
//! | Platform | Supported | Browsers | Test status |
//! |-----------------------|-----------|----------|-------------|
//! | macOS | ✅ | default + [others](https://docs.rs/webbrowser/latest/webbrowser/enum.Browser.html) | ✅ |
//! | windows | ✅ | default only | ✅ |
//! | linux/wsl | ✅ | default only (respects $BROWSER env var, so can be used with other browsers) | ✅ |
//! | android | ✅ | default only | ✅ |
//! | iOS/tvOS/visionOS | ✅ | default only | ✅ |
//! | wasm | ✅ | default only | ✅ |
//! | unix (*bsd, aix etc.) | ✅ | default only (respects $BROWSER env var, so can be used with other browsers) | Manual |
//!
//! ## Consistent Behaviour
//! `webbrowser` defines consistent behaviour on all platforms as follows:
//! * **Browser guarantee** - This library guarantees that the browser is opened, even for local files - the only crate to make such guarantees
//! at the time of this writing. Alternative libraries rely on existing system commands, which may lead to an editor being opened (instead
//! of the browser) for local html files, leading to an inconsistent behaviour for users.
//! * **Non-Blocking** for GUI based browsers (e.g. Firefox, Chrome etc.), while **Blocking** for text based browser (e.g. lynx etc.)
//! * **Suppressed output** by default for GUI based browsers, so that their stdout/stderr don't pollute the main program's output. This can be
//! overridden by `webbrowser::open_browser_with_options`.
//!
//! ## Crate Features
//! `webbrowser` optionally allows the following features to be configured:
//! * `hardened` - this disables handling of non-http(s) urls (e.g. `file:///`) as a hard security precaution
//! * `disable-wsl` - this disables WSL `file` implementation (`http` still works)
//! * `wasm-console` - this enables logging to wasm console (valid only on wasm platform)
#[cfg_attr(
any(target_os = "ios", target_os = "tvos", target_os = "visionos"),
path = "ios.rs"
)]
#[cfg_attr(target_os = "macos", path = "macos.rs")]
#[cfg_attr(target_os = "android", path = "android.rs")]
#[cfg_attr(target_family = "wasm", path = "wasm.rs")]
#[cfg_attr(windows, path = "windows.rs")]
#[cfg_attr(
all(
unix,
not(any(
target_os = "ios",
target_os = "tvos",
target_os = "visionos",
target_os = "macos",
target_os = "android",
target_family = "wasm",
windows,
)),
),
path = "unix.rs"
)]
mod os;
#[cfg(any(
windows,
all(
unix,
not(any(
target_os = "ios",
target_os = "tvos",
target_os = "visionos",
target_os = "macos",
target_os = "android",
target_family = "wasm",
)),
),
))]
pub(crate) mod common;
use std::fmt::Display;
use std::io::{Error, ErrorKind, Result};
use std::ops::Deref;
use std::str::FromStr;
use std::{error, fmt};
#[derive(Debug, Default, Eq, PartialEq, Copy, Clone, Hash)]
/// Browser types available
pub enum Browser {
///Operating system's default browser
#[default]
Default,
///Mozilla Firefox
Firefox,
///Microsoft's Internet Explorer
InternetExplorer,
///Google Chrome
Chrome,
///Opera
Opera,
///Mac OS Safari
Safari,
///Haiku's WebPositive
WebPositive,
}
impl Browser {
/// Returns true if there is likely a browser detected in the system
pub fn is_available() -> bool {
Browser::Default.exists()
}
/// Returns true if this specific browser is detected in the system
pub fn exists(&self) -> bool {
open_browser_with_options(
*self,
"https://rootnet.in",
BrowserOptions::new().with_dry_run(true),
)
.is_ok()
}
}
///The Error type for parsing a string into a Browser.
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
pub struct ParseBrowserError;
impl fmt::Display for ParseBrowserError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("Invalid browser given")
}
}
impl error::Error for ParseBrowserError {
fn description(&self) -> &str {
"invalid browser"
}
}
impl fmt::Display for Browser {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Browser::Default => f.write_str("Default"),
Browser::Firefox => f.write_str("Firefox"),
Browser::InternetExplorer => f.write_str("Internet Explorer"),
Browser::Chrome => f.write_str("Chrome"),
Browser::Opera => f.write_str("Opera"),
Browser::Safari => f.write_str("Safari"),
Browser::WebPositive => f.write_str("WebPositive"),
}
}
}
impl FromStr for Browser {
type Err = ParseBrowserError;
fn from_str(s: &str) -> ::std::result::Result {
match s {
"firefox" => Ok(Browser::Firefox),
"default" => Ok(Browser::Default),
"ie" | "internet explorer" | "internetexplorer" => Ok(Browser::InternetExplorer),
"chrome" => Ok(Browser::Chrome),
"opera" => Ok(Browser::Opera),
"safari" => Ok(Browser::Safari),
"webpositive" => Ok(Browser::WebPositive),
_ => Err(ParseBrowserError),
}
}
}
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
/// BrowserOptions to override certain default behaviour. Any option named as a `hint` is
/// not guaranteed to be honoured. Use [BrowserOptions::new()] to create.
///
/// e.g. by default, we suppress stdout/stderr, but that behaviour can be overridden here
pub struct BrowserOptions {
suppress_output: bool,
target_hint: String,
dry_run: bool,
}
impl fmt::Display for BrowserOptions {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_fmt(format_args!(
"BrowserOptions(supress_output={}, target_hint={}, dry_run={})",
self.suppress_output, self.target_hint, self.dry_run
))
}
}
impl std::default::Default for BrowserOptions {
fn default() -> Self {
let target_hint = String::from(option_env!("WEBBROWSER_WASM_TARGET").unwrap_or("_blank"));
BrowserOptions {
suppress_output: true,
target_hint,
dry_run: false,
}
}
}
impl BrowserOptions {
/// Create a new instance. Configure it with one of the `with_` methods.
pub fn new() -> Self {
Self::default()
}
/// Determines whether stdout/stderr of the appropriate browser command is suppressed
/// or not
pub fn with_suppress_output(&mut self, suppress_output: bool) -> &mut Self {
self.suppress_output = suppress_output;
self
}
/// Hint to the browser to open the url in the corresponding
/// [target](https://www.w3schools.com/tags/att_a_target.asp). Note that this is just
/// a hint, it may or may not be honoured (currently guaranteed only in wasm).
// TODO:remove this lint suppression once we're past the MSRV of 1.63 as that's when
// clone_into() became stable.
#[allow(clippy::all)]
pub fn with_target_hint(&mut self, target_hint: &str) -> &mut Self {
self.target_hint = target_hint.to_owned();
self
}
/// Do not do an actual execution, just return true if this would've likely
/// succeeded. Note the "likely" here - it's still indicative than guaranteed.
pub fn with_dry_run(&mut self, dry_run: bool) -> &mut Self {
self.dry_run = dry_run;
self
}
}
/// Opens the URL on the default browser of this platform
///
/// Returns Ok(..) so long as the browser invocation was successful. An Err(..) is returned in the
/// following scenarios:
/// * The requested browser was not found
/// * There was an error in opening the browser
/// * `hardened` feature is enabled, and the URL was not a valid http(s) url, say a `file:///`
/// * On ios/android/wasm, if the url is not a valid http(s) url
///
/// Equivalent to:
/// ```no_run
/// # use webbrowser::{Browser, open_browser};
/// # let url = "http://example.com";
/// open_browser(Browser::Default, url);
/// ```
///
/// # Examples
/// ```no_run
/// use webbrowser;
///
/// if webbrowser::open("http://github.com").is_ok() {
/// // ...
/// }
/// ```
pub fn open(url: &str) -> Result<()> {
open_browser(Browser::Default, url)
}
/// Opens the specified URL on the specific browser (if available) requested. Return semantics are
/// the same as for [open](fn.open.html).
///
/// # Examples
/// ```no_run
/// use webbrowser::{open_browser, Browser};
///
/// if open_browser(Browser::Firefox, "http://github.com").is_ok() {
/// // ...
/// }
/// ```
pub fn open_browser(browser: Browser, url: &str) -> Result<()> {
open_browser_with_options(browser, url, &BrowserOptions::default())
}
/// Opens the specified URL on the specific browser (if available) requested, while overriding the
/// default options.
///
/// Return semantics are
/// the same as for [open](fn.open.html).
///
/// # Examples
/// ```no_run
/// use webbrowser::{open_browser_with_options, Browser, BrowserOptions};
///
/// if open_browser_with_options(Browser::Default, "http://github.com", BrowserOptions::new().with_suppress_output(false)).is_ok() {
/// // ...
/// }
/// ```
pub fn open_browser_with_options(
browser: Browser,
url: &str,
options: &BrowserOptions,
) -> Result<()> {
let target = TargetType::try_from(url)?;
// if feature:hardened is enabled, make sure we accept only HTTP(S) URLs
#[cfg(feature = "hardened")]
if !target.is_http() {
return Err(Error::new(
ErrorKind::InvalidInput,
"only http/https urls allowed",
));
}
if cfg!(any(
target_os = "ios",
target_os = "tvos",
target_os = "visionos",
target_os = "macos",
target_os = "android",
target_family = "wasm",
windows,
unix,
)) {
os::open_browser_internal(browser, &target, options)
} else {
Err(Error::new(ErrorKind::NotFound, "unsupported platform"))
}
}
/// The link we're trying to open, represented as a URL. Local files get represented
/// via `file://...` URLs
struct TargetType(url::Url);
impl TargetType {
/// Returns true if this target represents an HTTP url, false otherwise
#[cfg(any(
feature = "hardened",
target_os = "android",
target_os = "ios",
target_os = "tvos",
target_os = "visionos",
target_family = "wasm"
))]
fn is_http(&self) -> bool {
matches!(self.0.scheme(), "http" | "https")
}
/// If `target` represents a valid http/https url, return the str corresponding to it
/// else return `std::io::Error` of kind `std::io::ErrorKind::InvalidInput`
#[cfg(any(
target_os = "android",
target_os = "ios",
target_os = "tvos",
target_os = "visionos",
target_family = "wasm"
))]
fn get_http_url(&self) -> Result<&str> {
if self.is_http() {
Ok(self.0.as_str())
} else {
Err(Error::new(ErrorKind::InvalidInput, "not an http url"))
}
}
#[cfg(not(target_family = "wasm"))]
fn from_file_path(value: &str) -> Result {
let pb = std::path::PathBuf::from(value);
let url = url::Url::from_file_path(if pb.is_relative() {
std::env::current_dir()?.join(pb)
} else {
pb
})
.map_err(|_| Error::new(ErrorKind::InvalidInput, "failed to convert path to url"))?;
Ok(Self(url))
}
}
impl Deref for TargetType {
type Target = str;
fn deref(&self) -> &Self::Target {
self.0.as_str()
}
}
impl Display for TargetType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(self as &str).fmt(f)
}
}
impl std::convert::TryFrom<&str> for TargetType {
type Error = Error;
#[cfg(target_family = "wasm")]
fn try_from(value: &str) -> Result {
url::Url::parse(value)
.map(|u| Ok(Self(u)))
.map_err(|_| Error::new(ErrorKind::InvalidInput, "invalid url for wasm"))?
}
#[cfg(not(target_family = "wasm"))]
fn try_from(value: &str) -> Result {
match url::Url::parse(value) {
Ok(u) => {
if u.scheme().len() == 1 && cfg!(windows) {
// this can happen in windows that C:\abc.html gets parsed as scheme "C"
Self::from_file_path(value)
} else {
Ok(Self(u))
}
}
Err(_) => Self::from_file_path(value),
}
}
}
#[test]
#[ignore]
fn test_open_firefox() {
assert!(open_browser(Browser::Firefox, "http://github.com").is_ok());
}
#[test]
#[ignore]
fn test_open_chrome() {
assert!(open_browser(Browser::Chrome, "http://github.com").is_ok());
}
#[test]
#[ignore]
fn test_open_safari() {
assert!(open_browser(Browser::Safari, "http://github.com").is_ok());
}
#[test]
#[ignore]
fn test_open_webpositive() {
assert!(open_browser(Browser::WebPositive, "http://github.com").is_ok());
}
webbrowser-1.0.5/src/macos.rs 0000644 0000000 0000000 00000017443 10461020230 0014260 0 ustar 0000000 0000000 use crate::{Browser, BrowserOptions, Error, ErrorKind, Result, TargetType};
use core_foundation::array::{CFArray, CFArrayRef};
use core_foundation::base::TCFType;
use core_foundation::error::{CFError, CFErrorRef};
use core_foundation::url::{CFURLRef, CFURL};
use std::os::raw::c_void;
/// Deal with opening of browsers on Mac OS X using Core Foundation framework
pub(super) fn open_browser_internal(
browser: Browser,
target: &TargetType,
options: &BrowserOptions,
) -> Result<()> {
// create the CFUrl for the browser
let browser_cf_url = match browser {
Browser::Firefox => create_cf_url("file:///Applications/Firefox.app/"),
Browser::Chrome => create_cf_url("file:///Applications/Google Chrome.app/"),
Browser::Opera => create_cf_url("file:///Applications/Opera.app/"),
Browser::Safari => create_cf_url("file:///Applications/Safari.app/"),
Browser::Default => {
if let Some(dummy_url) = create_cf_url("https://") {
let mut err: CFErrorRef = std::ptr::null_mut();
let result = unsafe {
LSCopyDefaultApplicationURLForURL(
dummy_url.as_concrete_TypeRef(),
LSROLE_VIEWER,
&mut err,
)
};
if result.is_null() {
log::error!("failed to get default browser: {}", unsafe {
CFError::wrap_under_create_rule(err)
});
create_cf_url(DEFAULT_BROWSER_URL)
} else {
let cf_url = unsafe { CFURL::wrap_under_create_rule(result) };
log::trace!("default browser is {:?}", &cf_url);
Some(cf_url)
}
} else {
create_cf_url(DEFAULT_BROWSER_URL)
}
}
_ => {
return Err(Error::new(
ErrorKind::NotFound,
"browser not supported on macos",
))
}
}
.ok_or_else(|| Error::new(ErrorKind::Other, "failed to create CFURL"))?;
let cf_url = create_cf_url(target.as_ref())
.ok_or_else(|| Error::new(ErrorKind::Other, "failed to create CFURL"))?;
let urls_v = [cf_url];
let urls_arr = CFArray::::from_CFTypes(&urls_v);
let spec = LSLaunchURLSpec {
app_url: browser_cf_url.as_concrete_TypeRef(),
item_urls: urls_arr.as_concrete_TypeRef(),
pass_thru_params: std::ptr::null(),
launch_flags: LS_LAUNCH_FLAG_DEFAULTS | LS_LAUNCH_FLAG_ASYNC,
async_ref_con: std::ptr::null(),
};
// handle dry-run scenario
if options.dry_run {
return if let Some(path) = browser_cf_url.to_path() {
if path.is_dir() {
log::debug!("dry-run: not actually opening the browser {}", &browser);
Ok(())
} else {
log::debug!("dry-run: browser {} not found", &browser);
Err(Error::new(ErrorKind::NotFound, "browser not found"))
}
} else {
Err(Error::new(
ErrorKind::Other,
"unable to convert app url to path",
))
};
}
// launch the browser
log::trace!("about to start browser: {} for {}", &browser, &target);
let mut launched_app: CFURLRef = std::ptr::null_mut();
let status = unsafe { LSOpenFromURLSpec(&spec, &mut launched_app) };
log::trace!("received status: {}", status);
if status == 0 {
Ok(())
} else {
Err(Error::from(LSError::from(status)))
}
}
/// Create a Core Foundation CFURL object given a rust-y `url`
fn create_cf_url(url: &str) -> Option {
let url_u8 = url.as_bytes();
let url_ref = unsafe {
core_foundation::url::CFURLCreateWithBytes(
std::ptr::null(),
url_u8.as_ptr(),
url_u8.len() as isize,
core_foundation::string::kCFStringEncodingUTF8,
std::ptr::null(),
)
};
if url_ref.is_null() {
None
} else {
Some(unsafe { CFURL::wrap_under_create_rule(url_ref) })
}
}
type OSStatus = i32;
/// A subset of Launch Services error codes as picked from (`Result Codes` section)
/// https://developer.apple.com/documentation/coreservices/launch_services?language=objc#1661359
enum LSError {
Unknown(OSStatus),
ApplicationNotFound,
NoLaunchPermission,
}
impl From for LSError {
fn from(status: OSStatus) -> Self {
match status {
// -43 is file not found, while -10814 is launch services err code
-43 | -10814 => Self::ApplicationNotFound,
-10826 => Self::NoLaunchPermission,
_ => Self::Unknown(status),
}
}
}
impl std::fmt::Display for LSError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Unknown(code) => write!(f, "ls_error: code {}", code),
Self::ApplicationNotFound => f.write_str("ls_error: application not found"),
Self::NoLaunchPermission => f.write_str("ls_error: no launch permission"),
}
}
}
impl std::fmt::Debug for LSError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self, f)
}
}
impl From for Error {
fn from(err: LSError) -> Self {
let kind = match err {
LSError::Unknown(_) => ErrorKind::Other,
LSError::ApplicationNotFound => ErrorKind::NotFound,
LSError::NoLaunchPermission => ErrorKind::PermissionDenied,
};
Error::new(kind, err.to_string())
}
}
type LSRolesMask = u32;
// as per https://developer.apple.com/documentation/coreservices/lsrolesmask/klsrolesviewer?language=objc
const LSROLE_VIEWER: LSRolesMask = 0x00000002;
// as per https://developer.apple.com/documentation/coreservices/lslaunchflags/klslaunchdefaults?language=objc
const LS_LAUNCH_FLAG_DEFAULTS: u32 = 0x00000001;
const LS_LAUNCH_FLAG_ASYNC: u32 = 0x00010000;
#[repr(C, packed(2))] // Header contains `#pragma pack(push, 2)`.
struct LSLaunchURLSpec {
app_url: CFURLRef,
item_urls: CFArrayRef,
pass_thru_params: *const c_void,
launch_flags: u32,
async_ref_con: *const c_void,
}
// Define the functions in CoreServices that we'll be using to open the browser
#[link(name = "CoreServices", kind = "framework")]
extern "C" {
/// Used to get the default browser configured for the user. See:
/// https://developer.apple.com/documentation/coreservices/1448824-lscopydefaultapplicationurlforur?language=objc
fn LSCopyDefaultApplicationURLForURL(
inURL: CFURLRef,
inRoleMask: LSRolesMask,
outError: *mut CFErrorRef,
) -> CFURLRef;
/// Used to launch the browser to open a url
/// https://developer.apple.com/documentation/coreservices/1441986-lsopenfromurlspec?language=objc
fn LSOpenFromURLSpec(
inLaunchSpec: *const LSLaunchURLSpec,
outLaunchedURL: *mut CFURLRef,
) -> OSStatus;
}
/// We assume Safari to be the default browser, if deductions fail for any reason
const DEFAULT_BROWSER_URL: &str = "file:///Applications/Safari.app/";
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn open_non_existing_browser() {
let _ = env_logger::try_init();
if let Err(err) = open_browser_internal(
Browser::Opera,
&TargetType::try_from("https://github.com").expect("failed to parse url"),
&BrowserOptions::default(),
) {
assert_eq!(err.kind(), ErrorKind::NotFound);
} else {
panic!("expected opening non-existing browser to fail");
}
}
#[test]
fn test_existence() {
let _ = env_logger::try_init();
assert!(Browser::Safari.exists());
assert!(!Browser::Opera.exists());
}
}
webbrowser-1.0.5/src/unix.rs 0000644 0000000 0000000 00000074270 10461020230 0014142 0 ustar 0000000 0000000 use crate::common::run_command;
use crate::{Browser, BrowserOptions, Error, ErrorKind, Result, TargetType};
use log::trace;
use std::io::{BufRead, BufReader};
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf, MAIN_SEPARATOR};
use std::process::{Command, Stdio};
macro_rules! try_browser {
( $options: expr, $name:expr, $( $arg:expr ),+ ) => {
for_matching_path($name, |pb| {
let mut cmd = Command::new(pb);
$(
cmd.arg($arg);
)+
run_command(&mut cmd, !is_text_browser(&pb), $options)
})
}
}
/// Deal with opening of browsers on Linux and *BSD - currently supports only the default browser
///
/// The mechanism of opening the default browser is as follows:
/// 1. Attempt to use $BROWSER env var if available
/// 2. Attempt to use xdg-open
/// 3. Attempt to use window manager specific commands, like gnome-open, kde-open etc. incl. WSL
/// 4. Fallback to x-www-browser
pub(super) fn open_browser_internal(
browser: Browser,
target: &TargetType,
options: &BrowserOptions,
) -> Result<()> {
match browser {
Browser::Default => open_browser_default(target, options),
_ => Err(Error::new(
ErrorKind::NotFound,
"only default browser supported",
)),
}
}
/// Open the default browser.
///
/// [BrowserOptions::dry_run] is handled inside [run_command], as all execution paths eventually
/// rely on it to execute.
fn open_browser_default(target: &TargetType, options: &BrowserOptions) -> Result<()> {
let url: &str = target;
// we first try with the $BROWSER env
try_with_browser_env(url, options)
// allow for haiku's open specifically
.or_else(|_| try_haiku(options, url))
// then we try with xdg configuration
.or_else(|_| try_xdg(options, url))
// else do desktop specific stuff
.or_else(|r| match guess_desktop_env() {
"kde" => try_browser!(options, "kde-open", url)
.or_else(|_| try_browser!(options, "kde-open5", url))
.or_else(|_| try_browser!(options, "kfmclient", "newTab", url)),
"gnome" => try_browser!(options, "gio", "open", url)
.or_else(|_| try_browser!(options, "gvfs-open", url))
.or_else(|_| try_browser!(options, "gnome-open", url)),
"mate" => try_browser!(options, "gio", "open", url)
.or_else(|_| try_browser!(options, "gvfs-open", url))
.or_else(|_| try_browser!(options, "mate-open", url)),
"xfce" => try_browser!(options, "exo-open", url)
.or_else(|_| try_browser!(options, "gio", "open", url))
.or_else(|_| try_browser!(options, "gvfs-open", url)),
"wsl" => try_wsl(options, target),
"flatpak" => try_flatpak(options, target),
_ => Err(r),
})
// at the end, we'll try x-www-browser and return the result as is
.or_else(|_| try_browser!(options, "x-www-browser", url))
// if all above failed, map error to not found
.map_err(|_| {
Error::new(
ErrorKind::NotFound,
"No valid browsers detected. You can specify one in BROWSER environment variable",
)
})
// and convert a successful result into a ()
.map(|_| ())
}
fn try_with_browser_env(url: &str, options: &BrowserOptions) -> Result<()> {
// $BROWSER can contain ':' delimited options, each representing a potential browser command line
for browser in std::env::var("BROWSER")
.unwrap_or_else(|_| String::from(""))
.split(':')
{
if !browser.is_empty() {
// each browser command can have %s to represent URL, while %c needs to be replaced
// with ':' and %% with '%'
let cmdline = browser
.replace("%s", url)
.replace("%c", ":")
.replace("%%", "%");
let cmdarr: Vec<&str> = cmdline.split_ascii_whitespace().collect();
let browser_cmd = cmdarr[0];
let env_exit = for_matching_path(browser_cmd, |pb| {
let mut cmd = Command::new(pb);
for arg in cmdarr.iter().skip(1) {
cmd.arg(arg);
}
if !browser.contains("%s") {
// append the url as an argument only if it was not already set via %s
cmd.arg(url);
}
run_command(&mut cmd, !is_text_browser(pb), options)
});
if env_exit.is_ok() {
return Ok(());
}
}
}
Err(Error::new(
ErrorKind::NotFound,
"No valid browser configured in BROWSER environment variable",
))
}
/// Check if we are inside WSL on Windows, and interoperability with Windows tools is
/// enabled.
fn is_wsl() -> bool {
// we should check in procfs only on linux, as for non-linux it will likely be
// a disk hit, which we should avoid.
if cfg!(target_os = "linux") {
// we check if interop with windows tools is allowed, as if it isn't, we won't
// be able to invoke windows commands anyways.
// See: https://learn.microsoft.com/en-us/windows/wsl/filesystems#disable-interoperability
if let Ok(s) = std::fs::read_to_string("/proc/sys/fs/binfmt_misc/WSLInterop") {
s.contains("enabled")
} else {
false
}
} else {
// we short-circuit and return false on non-linux
false
}
}
/// Check if we're running inside Flatpak
#[inline]
fn is_flatpak() -> bool {
std::env::var("container")
.map(|x| x.eq_ignore_ascii_case("flatpak"))
.unwrap_or(false)
}
/// Detect the desktop environment
fn guess_desktop_env() -> &'static str {
let unknown = "unknown";
let xcd: String = std::env::var("XDG_CURRENT_DESKTOP")
.unwrap_or_else(|_| unknown.into())
.to_ascii_lowercase();
let dsession: String = std::env::var("DESKTOP_SESSION")
.unwrap_or_else(|_| unknown.into())
.to_ascii_lowercase();
if is_flatpak() {
"flatpak"
} else if xcd.contains("gnome") || xcd.contains("cinnamon") || dsession.contains("gnome") {
// GNOME and its derivatives
"gnome"
} else if xcd.contains("kde")
|| std::env::var("KDE_FULL_SESSION").is_ok()
|| std::env::var("KDE_SESSION_VERSION").is_ok()
{
// KDE: https://userbase.kde.org/KDE_System_Administration/Environment_Variables#Automatically_Set_Variables
"kde"
} else if xcd.contains("mate") || dsession.contains("mate") {
// We'll treat MATE as distinct from GNOME due to mate-open
"mate"
} else if xcd.contains("xfce") || dsession.contains("xfce") {
// XFCE
"xfce"
} else if is_wsl() {
// WSL
"wsl"
} else {
// All others
unknown
}
}
/// Open browser in WSL environments
fn try_wsl(options: &BrowserOptions, target: &TargetType) -> Result<()> {
match target.0.scheme() {
"http" | "https" => {
let url: &str = target;
try_browser!(
options,
"cmd.exe",
"/c",
"start",
url.replace('^', "^^").replace('&', "^&")
)
.or_else(|_| {
try_browser!(
options,
"powershell.exe",
"Start",
url.replace('&', "\"&\"")
)
})
.or_else(|_| try_browser!(options, "wsl-open", url))
}
#[cfg(all(
target_os = "linux",
not(feature = "hardened"),
not(feature = "disable-wsl")
))]
"file" => {
// we'll need to detect the default browser and then invoke it
// with wsl translated path
let wc = wsl::get_wsl_win_config()?;
let mut cmd = if wc.powershell_path.is_some() {
wsl::get_wsl_windows_browser_ps(&wc, target)
} else {
wsl::get_wsl_windows_browser_cmd(&wc, target)
}?;
run_command(&mut cmd, true, options)
}
_ => Err(Error::new(ErrorKind::NotFound, "invalid browser")),
}
}
/// Open browser in Flatpak environments
fn try_flatpak(options: &BrowserOptions, target: &TargetType) -> Result<()> {
match target.0.scheme() {
"http" | "https" => {
let url: &str = target;
// we assume xdg-open to be present, given that it's a part of standard
// runtime & SDK of flatpak
try_browser!(options, "xdg-open", url)
}
// we support only http urls under Flatpak to adhere to the defined
// Consistent Behaviour, as effectively DBUS is used interally, and
// there doesn't seem to be a way for us to determine actual browser
_ => Err(Error::new(ErrorKind::NotFound, "only http urls supported")),
}
}
/// Handle Haiku explicitly, as it uses an "open" command, similar to macos
/// but on other Unixes, open ends up translating to shell open fd
fn try_haiku(options: &BrowserOptions, url: &str) -> Result<()> {
if cfg!(target_os = "haiku") {
try_browser!(options, "open", url).map(|_| ())
} else {
Err(Error::new(ErrorKind::NotFound, "Not on haiku"))
}
}
/// Dig into XDG settings (if xdg is available) to force it to open the browser, instead of
/// the default application
fn try_xdg(options: &BrowserOptions, url: &str) -> Result<()> {
// run: xdg-settings get default-web-browser
let browser_name_os = for_matching_path("xdg-settings", |pb| {
Command::new(pb)
.args(["get", "default-web-browser"])
.stdin(Stdio::null())
.stderr(Stdio::null())
.output()
})
.map_err(|_| Error::new(ErrorKind::NotFound, "unable to determine xdg browser"))?
.stdout;
// convert browser name to a utf-8 string and trim off the trailing newline
let browser_name = String::from_utf8(browser_name_os)
.map_err(|_| Error::new(ErrorKind::NotFound, "invalid default browser name"))?
.trim()
.to_owned();
if browser_name.is_empty() {
return Err(Error::new(ErrorKind::NotFound, "no default xdg browser"));
}
trace!("found xdg browser: {:?}", &browser_name);
// search for the config file corresponding to this browser name
let mut config_found = false;
let app_suffix = "applications";
for xdg_dir in get_xdg_dirs().iter_mut() {
let mut config_path = xdg_dir.join(app_suffix).join(&browser_name);
trace!("checking for xdg config at {config_path:?}");
let mut metadata = config_path.metadata();
if metadata.is_err() && browser_name.contains('-') {
// as per the spec, we need to replace '-' with /
let child_path = browser_name.replace('-', "/");
config_path = xdg_dir.join(app_suffix).join(child_path);
metadata = config_path.metadata();
}
if metadata.is_ok() {
// we've found the config file, so we try running using that
config_found = true;
match open_using_xdg_config(&config_path, options, url) {
Ok(x) => return Ok(x), // return if successful
Err(err) => {
// if we got an error other than NotFound, then we short
// circuit, and do not try any more options, else we
// continue to try more
if err.kind() != ErrorKind::NotFound {
return Err(err);
}
}
}
}
}
if config_found {
Err(Error::new(ErrorKind::Other, "xdg-open failed"))
} else {
Err(Error::new(ErrorKind::NotFound, "no valid xdg config found"))
}
}
/// Opens `url` using xdg configuration found in `config_path`
///
/// See https://specifications.freedesktop.org/desktop-entry-spec/latest for details
fn open_using_xdg_config(config_path: &PathBuf, options: &BrowserOptions, url: &str) -> Result<()> {
let file = std::fs::File::open(config_path)?;
let mut in_desktop_entry = false;
let mut hidden = false;
let mut cmdline: Option = None;
let mut requires_terminal = false;
// we capture important keys under the [Desktop Entry] section, as defined under:
// https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html
for line in BufReader::new(file).lines().map_while(Result::ok) {
if line == "[Desktop Entry]" {
in_desktop_entry = true;
} else if line.starts_with('[') {
in_desktop_entry = false;
} else if in_desktop_entry && !line.starts_with('#') {
if let Some(idx) = line.find('=') {
let key = &line[..idx];
let value = &line[idx + 1..];
match key {
"Exec" => cmdline = Some(value.to_owned()),
"Hidden" => hidden = value == "true",
"Terminal" => requires_terminal = value == "true",
_ => (), // ignore
}
}
}
}
if hidden {
// we ignore this config if it was marked hidden/deleted
return Err(Error::new(ErrorKind::NotFound, "xdg config is hidden"));
}
if let Some(cmdline) = cmdline {
// we have a valid configuration
let cmdarr: Vec<&str> = cmdline.split_ascii_whitespace().collect();
let browser_cmd = cmdarr[0];
for_matching_path(browser_cmd, |pb| {
let mut cmd = Command::new(pb);
let mut url_added = false;
for arg in cmdarr.iter().skip(1) {
match *arg {
"%u" | "%U" | "%f" | "%F" => {
url_added = true;
cmd.arg(url)
}
_ => cmd.arg(arg),
};
}
if !url_added {
// append the url as an argument only if it was not already set
cmd.arg(url);
}
run_command(&mut cmd, !requires_terminal, options)
})
} else {
// we don't have a valid config
Err(Error::new(ErrorKind::NotFound, "not a valid xdg config"))
}
}
/// Get the list of directories in which the desktop file needs to be searched
fn get_xdg_dirs() -> Vec {
let mut xdg_dirs: Vec = Vec::new();
#[allow(deprecated)]
let data_home = std::env::var("XDG_DATA_HOME")
.ok()
.map(PathBuf::from)
.filter(|path| path.is_absolute())
.or_else(|| std::env::home_dir().map(|path| path.join(".local/share")));
if let Some(data_home) = data_home {
xdg_dirs.push(data_home);
}
if let Ok(data_dirs) = std::env::var("XDG_DATA_DIRS") {
for d in data_dirs.split(':') {
xdg_dirs.push(PathBuf::from(d));
}
} else {
xdg_dirs.push(PathBuf::from("/usr/local/share"));
xdg_dirs.push(PathBuf::from("/usr/share"));
}
xdg_dirs
}
/// Returns true if specified command refers to a known list of text browsers
fn is_text_browser(pb: &Path) -> bool {
for browser in TEXT_BROWSERS.iter() {
if pb.ends_with(browser) {
return true;
}
}
false
}
fn for_matching_path(name: &str, op: F) -> Result
where
F: FnOnce(&PathBuf) -> Result,
{
let err = Err(Error::new(ErrorKind::NotFound, "command not found"));
// if the name already includes path separator, we should not try to do a PATH search on it
// as it's likely an absolutely or relative name, so we treat it as such.
if name.contains(MAIN_SEPARATOR) {
let pb = std::path::PathBuf::from(name);
if let Ok(metadata) = pb.metadata() {
if metadata.is_file() && metadata.permissions().mode() & 0o111 != 0 {
return op(&pb);
}
} else {
return err;
}
} else {
// search for this name inside PATH
if let Ok(path) = std::env::var("PATH") {
for entry in path.split(':') {
let mut pb = std::path::PathBuf::from(entry);
pb.push(name);
if let Ok(metadata) = pb.metadata() {
if metadata.is_file() && metadata.permissions().mode() & 0o111 != 0 {
return op(&pb);
}
}
}
}
}
// return the not found err, if we didn't find anything above
err
}
static TEXT_BROWSERS: [&str; 9] = [
"lynx", "links", "links2", "elinks", "w3m", "eww", "netrik", "retawq", "curl",
];
#[cfg(test)]
mod tests_xdg {
use super::*;
use std::fs::File;
use std::io::Write;
fn get_temp_path(name: &str, suffix: &str) -> String {
let pid = std::process::id();
std::env::temp_dir()
.join(format!("{name}.{pid}.{suffix}"))
.into_os_string()
.into_string()
.expect("failed to convert into string")
}
#[test]
fn test_xdg_open_local_file() {
let _ = env_logger::try_init();
// ensure flag file is not existing
let flag_path = get_temp_path("test_xdg", "flag");
let _ = std::fs::remove_file(&flag_path);
// create browser script
let txt_path = get_temp_path("test_xdf", "txt");
let browser_path = get_temp_path("test_xdg", "browser");
{
let mut browser_file =
File::create(&browser_path).expect("failed to create browser file");
let _ = browser_file.write_fmt(format_args!(
r#"#!/bin/bash
if [ "$1" != "p1" ]; then
echo "1st parameter should've been p1" >&2
exit 1
elif [ "$2" != "{}" ]; then
echo "2nd parameter should've been {}" >&2
exit 1
elif [ "$3" != "p3" ]; then
echo "3rd parameter should've been p3" >&2
exit 1
fi
echo "$2" > "{}"
"#,
&txt_path, &txt_path, &flag_path
));
let mut perms = browser_file
.metadata()
.expect("failed to get permissions")
.permissions();
perms.set_mode(0o755);
let _ = browser_file.set_permissions(perms);
}
// create xdg desktop config
let config_path = get_temp_path("test_xdg", "desktop");
{
let mut xdg_file =
std::fs::File::create(&config_path).expect("failed to create xdg desktop file");
let _ = xdg_file.write_fmt(format_args!(
r#"# this line should be ignored
[Desktop Entry]
Exec={} p1 %u p3
[Another Entry]
Exec=/bin/ls
# the above Exec line should be getting ignored
"#,
&browser_path
));
}
// now try opening browser using above desktop config
let result = open_using_xdg_config(
&PathBuf::from(&config_path),
&BrowserOptions::default(),
&txt_path,
);
// we need to wait until the flag file shows up due to the async
// nature of browser invocation
for _ in 0..10 {
if std::fs::read_to_string(&flag_path).is_ok() {
break;
}
std::thread::sleep(std::time::Duration::from_millis(500));
}
std::thread::sleep(std::time::Duration::from_millis(500));
// validate that the flag file contains the url we passed
assert_eq!(
std::fs::read_to_string(&flag_path)
.expect("flag file not found")
.trim(),
&txt_path,
);
assert!(result.is_ok());
// delete all temp files
let _ = std::fs::remove_file(&txt_path);
let _ = std::fs::remove_file(&flag_path);
let _ = std::fs::remove_file(&browser_path);
let _ = std::fs::remove_file(&config_path);
assert!(result.is_ok());
}
}
/// WSL related browser functionality.
///
/// We treat it as a separate submod, to allow for easy logical grouping
/// and to enable/disable based on some feature easily in future.
#[cfg(all(
target_os = "linux",
not(feature = "hardened"),
not(feature = "disable-wsl")
))]
mod wsl {
use crate::common::for_each_token;
use crate::{Result, TargetType};
use std::io::{Error, ErrorKind};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
pub(super) struct WindowsConfig {
root: PathBuf,
cmd_path: PathBuf,
pub(super) powershell_path: Option,
}
/// Returns a [WindowsConfig] by iterating over PATH entries. This seems to be
/// the fastest way to determine this.
pub(super) fn get_wsl_win_config() -> Result {
let err_fn = || Error::new(ErrorKind::NotFound, "invalid windows config");
if let Some(path_env) = std::env::var_os("PATH") {
let mut root: Option = None;
let mut cmd_path: Option = None;
let mut powershell_path: Option = None;
for path in std::env::split_paths(&path_env) {
let path_s = path.to_string_lossy().to_ascii_lowercase();
let path_s = path_s.trim_end_matches('/');
if path_s.ends_with("/windows/system32") {
root = Some(std::fs::canonicalize(path.join("../.."))?);
cmd_path = Some(path.join("cmd.exe"));
break;
}
}
if let Some(ref root) = root {
for path in std::env::split_paths(&path_env) {
if path.starts_with(root) {
let pb = path.join("powershell.exe");
if pb.is_file() {
powershell_path = Some(pb);
}
}
}
}
if let Some(root) = root {
let cmd_path = cmd_path.unwrap_or_else(|| (root).join("windows/system32/cmd.exe"));
Ok(WindowsConfig {
root,
cmd_path,
powershell_path,
})
} else {
Err(err_fn())
}
} else {
Err(err_fn())
}
}
/// Try to get default browser command from powershell.exe
pub(super) fn get_wsl_windows_browser_ps(
wc: &WindowsConfig,
url: &TargetType,
) -> Result {
let err_fn = || Error::new(ErrorKind::NotFound, "powershell.exe error");
let ps_exe = wc.powershell_path.as_ref().ok_or_else(err_fn)?;
let mut cmd = Command::new(ps_exe);
cmd.arg("-NoLogo")
.arg("-NoProfile")
.arg("-NonInteractive")
.arg("-Command")
.arg("-")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::null());
log::debug!("running command: ${:?}", &cmd);
let mut child = cmd.spawn()?;
let mut stdin = child.stdin.take().ok_or_else(err_fn)?;
std::io::Write::write_all(&mut stdin, WSL_PS_SCRIPT.as_bytes())?;
drop(stdin); // flush to stdin, and close
let output_u8 = child.wait_with_output()?;
let output = String::from_utf8_lossy(&output_u8.stdout);
let output = output.trim();
if output.is_empty() {
Err(err_fn())
} else {
parse_wsl_cmdline(wc, output, url)
}
}
/// Try to get default browser command from cmd.exe
pub(super) fn get_wsl_windows_browser_cmd(
wc: &WindowsConfig,
url: &TargetType,
) -> Result {
let err_fn = || Error::new(ErrorKind::NotFound, "cmd.exe error");
let mut cmd = Command::new(&wc.cmd_path);
cmd.arg("/Q")
.arg("/C")
.arg("ftype http")
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::null());
log::debug!("running command: ${:?}", &cmd);
let output_u8 = cmd.output()?;
let output = String::from_utf8_lossy(&output_u8.stdout);
let output = output.trim();
if output.is_empty() {
Err(err_fn())
} else {
parse_wsl_cmdline(wc, output, url)
}
}
/// Given the configured command line `cmdline` in registry, and the given `url`,
/// return the appropriate `Command` to invoke
fn parse_wsl_cmdline(wc: &WindowsConfig, cmdline: &str, url: &TargetType) -> Result {
let mut tokens: Vec = Vec::new();
let filepath = wsl_get_filepath_from_url(wc, url)?;
let fp = &filepath;
for_each_token(cmdline, |token: &str| {
if matches!(token, "%0" | "%1") {
tokens.push(fp.to_owned());
} else {
tokens.push(token.to_string());
}
});
if tokens.is_empty() {
Err(Error::new(ErrorKind::NotFound, "invalid command"))
} else {
let progpath = wsl_path_win2lin(wc, &tokens[0])?;
let mut cmd = Command::new(progpath);
if tokens.len() > 1 {
cmd.args(&tokens[1..]);
}
Ok(cmd)
}
}
fn wsl_get_filepath_from_url(wc: &WindowsConfig, target: &TargetType) -> Result {
let url = &target.0;
if url.scheme() == "file" {
if url.host().is_none() {
let path = url
.to_file_path()
.map_err(|_| Error::new(ErrorKind::NotFound, "invalid path"))?;
wsl_path_lin2win(wc, path)
} else {
Ok(format!("\\\\wsl${}", url.path().replace('/', "\\")))
}
} else {
Ok(url.as_str().to_string())
}
}
/// Converts a windows path to linux `PathBuf`
fn wsl_path_win2lin(wc: &WindowsConfig, path: &str) -> Result {
let err_fn = || Error::new(ErrorKind::NotFound, "invalid windows path");
if path.len() > 3 {
let pfx = &path[..3];
if matches!(pfx, "C:\\" | "c:\\") {
let win_path = path[3..].replace('\\', "/");
Ok(wc.root.join(win_path))
} else {
Err(err_fn())
}
} else {
Err(err_fn())
}
}
/// Converts a linux path to windows. We using `String` instead of `OsString` as
/// return type because the `OsString` will be different b/w Windows & Linux.
fn wsl_path_lin2win(wc: &WindowsConfig, path: impl AsRef) -> Result {
let path = path.as_ref();
if let Ok(path) = path.strip_prefix(&wc.root) {
// windows can access this path directly
Ok(format!("C:\\{}", path.as_os_str().to_string_lossy()).replace('/', "\\"))
} else {
// windows needs to access it via network
let wsl_hostname = get_wsl_distro_name(wc)?;
Ok(format!(
"\\\\wsl$\\{}{}",
&wsl_hostname,
path.as_os_str().to_string_lossy()
)
.replace('/', "\\"))
}
}
/// Gets the WSL distro name
fn get_wsl_distro_name(wc: &WindowsConfig) -> Result {
let err_fn = || Error::new(ErrorKind::Other, "unable to determine wsl distro name");
// mostly we should be able to get it from the WSL_DISTRO_NAME env var
if let Ok(wsl_hostname) = std::env::var("WSL_DISTRO_NAME") {
Ok(wsl_hostname)
} else {
// but if not (e.g. if we were running as root), we can invoke
// powershell.exe to determine pwd and from there infer the distro name
let psexe = wc.powershell_path.as_ref().ok_or_else(err_fn)?;
let mut cmd = Command::new(psexe);
cmd.arg("-NoLogo")
.arg("-NoProfile")
.arg("-NonInteractive")
.arg("-Command")
.arg("$loc = Get-Location\nWrite-Output $loc.Path")
.current_dir("/")
.stdin(Stdio::null())
.stderr(Stdio::null());
log::debug!("running command: ${:?}", &cmd);
let output_u8 = cmd.output()?.stdout;
let output = String::from_utf8_lossy(&output_u8);
let output = output.trim_end_matches('\\');
let idx = output.find("::\\\\").ok_or_else(err_fn)?;
Ok((output[idx + 9..]).trim().to_string())
}
}
/// Powershell script to get the default browser command.
///
/// Adapted from https://stackoverflow.com/a/60972216
const WSL_PS_SCRIPT: &str = r#"
$Signature = @"
using System;
using System.Runtime.InteropServices;
using System.Text;
public static class Win32Api
{
[DllImport("Shlwapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint AssocQueryString(AssocF flags, AssocStr str, string pszAssoc, string pszExtra,[Out] System.Text.StringBuilder pszOut, ref uint pcchOut);
public static string GetDefaultBrowser()
{
AssocF assocF = AssocF.IsProtocol;
AssocStr association = AssocStr.Command;
string assocString = "http";
uint length = 1024; // we assume 1k is sufficient memory to hold the command
var sb = new System.Text.StringBuilder((int) length);
uint ret = ret = AssocQueryString(assocF, association, assocString, null, sb, ref length);
return (ret != 0) ? null : sb.ToString();
}
[Flags]
internal enum AssocF : uint
{
IsProtocol = 0x1000,
}
internal enum AssocStr
{
Command = 1,
Executable,
}
}
"@
Add-Type -TypeDefinition $Signature
Write-Output $([Win32Api]::GetDefaultBrowser())
"#;
/*#[cfg(test)]
mod tests {
use crate::open;
#[test]
fn test_url() {
let _ = env_logger::try_init();
assert!(open("https://github.com").is_ok());
}
#[test]
fn test_linux_file() {
let _ = env_logger::try_init();
assert!(open("abc.html").is_ok());
}
#[test]
fn test_windows_file() {
let _ = env_logger::try_init();
assert!(open("/mnt/c/T/abc.html").is_ok());
}
}*/
}
webbrowser-1.0.5/src/wasm.rs 0000644 0000000 0000000 00000003332 10461020230 0014115 0 ustar 0000000 0000000 use crate::{Browser, BrowserOptions, Error, ErrorKind, Result, TargetType};
/// Deal with opening a URL in wasm. This implementation ignores the browser attribute
/// and always opens URLs in the same browser where wasm vm is running.
pub(super) fn open_browser_internal(
_: Browser,
target: &TargetType,
options: &BrowserOptions,
) -> Result<()> {
// ensure we're opening only http/https urls, failing otherwise
let url = target.get_http_url()?;
// always return true for a dry run
if options.dry_run {
if web_sys::window().is_some() {
return Ok(());
} else {
return Err(Error::new(ErrorKind::Other, "no browser window available"));
}
}
let window = web_sys::window();
match window {
Some(w) => match w.open_with_url_and_target(url, &options.target_hint) {
Ok(x) => match x {
Some(_) => Ok(()),
None => {
wasm_console_log(POPUP_ERR_MSG, options);
Err(Error::new(ErrorKind::Other, POPUP_ERR_MSG))
}
},
Err(_) => {
wasm_console_log("window error while opening url", options);
Err(Error::new(ErrorKind::Other, "error opening url"))
}
},
None => Err(Error::new(ErrorKind::Other, "no browser window available")),
}
}
/// Print to browser console
fn wasm_console_log(_msg: &str, _options: &BrowserOptions) {
#[cfg(all(debug_assertions, feature = "wasm-console"))]
if !_options.suppress_output {
web_sys::console::log_1(&format!("[webbrowser] {}", &_msg).into());
}
}
const POPUP_ERR_MSG: &str = "popup blocked? window detected, but open_url failed";
webbrowser-1.0.5/src/windows.rs 0000644 0000000 0000000 00000010530 10461020230 0014636 0 ustar 0000000 0000000 use crate::common::{for_each_token, run_command};
use crate::{Browser, BrowserOptions, Error, ErrorKind, Result, TargetType};
use log::trace;
use std::path::Path;
use std::process::Command;
const ASSOCF_IS_PROTOCOL: u32 = 0x00001000;
const ASSOCSTR_COMMAND: i32 = 1;
#[link(name = "shlwapi")]
extern "system" {
fn AssocQueryStringW(
flags: u32,
string: i32,
association: *const u16,
extra: *const u16,
out: *mut u16,
out_len: *mut u32,
) -> i32;
}
/// Deal with opening of browsers on Windows.
///
/// We first use [`AssocQueryStringW`](https://learn.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-assocquerystringw)
/// function to determine the default browser, and then invoke it with appropriate parameters.
///
/// We ignore BrowserOptions on Windows, except for honouring [BrowserOptions::dry_run]
pub(super) fn open_browser_internal(
browser: Browser,
target: &TargetType,
options: &BrowserOptions,
) -> Result<()> {
match browser {
Browser::Default => {
// always return true for a dry run for default browser
if options.dry_run {
return Ok(());
}
trace!("trying to figure out default browser command");
let cmdline = unsafe {
const BUF_SIZE: usize = 512;
let mut cmdline_u16 = [0_u16; BUF_SIZE];
let mut line_len = BUF_SIZE as u32;
if AssocQueryStringW(
ASSOCF_IS_PROTOCOL,
ASSOCSTR_COMMAND,
[0x68, 0x74, 0x74, 0x70, 0x0].as_ptr(), // http\0
std::ptr::null(),
cmdline_u16.as_mut_ptr(),
&mut line_len,
) != 0
{
return Err(Error::new(
ErrorKind::Other,
"failed to get default browser",
));
}
use std::os::windows::ffi::OsStringExt;
std::ffi::OsString::from_wide(&cmdline_u16[..(line_len - 1) as usize])
.into_string()
.map_err(|_err| {
Error::new(
ErrorKind::Other,
"The default web browser command contains invalid unicode characters",
)
})?
};
trace!("default browser command: {}", &cmdline);
let cmdline = ensure_cmd_quotes(&cmdline);
let mut cmd = get_browser_cmd(&cmdline, target)?;
run_command(&mut cmd, true, options)
}
_ => Err(Error::new(
ErrorKind::NotFound,
"Only the default browser is supported on this platform right now",
)),
}
}
/// It seems that sometimes browser exe paths which have spaces are not quoted, so we keep going over
/// each token, until we encounter what looks like a valid exe.
///
/// See https://github.com/amodm/webbrowser-rs/issues/68
fn ensure_cmd_quotes(cmdline: &str) -> String {
if !cmdline.starts_with('"') {
let mut end = 0;
for (idx, ch) in cmdline.char_indices() {
if ch == ' ' {
// does the path till now look like a valid exe?
let potential_exe = Path::new(&cmdline[..idx]);
if potential_exe.exists() {
end = idx;
break;
}
}
}
if end > 0 {
return format!("\"{}\"{}", &cmdline[..end], &cmdline[end..]);
}
}
// else we default to returning the original cmdline
cmdline.to_string()
}
/// Given the configured command line `cmdline` in registry, and the given `url`,
/// return the appropriate `Command` to invoke
fn get_browser_cmd(cmdline: &str, url: &TargetType) -> Result {
let mut tokens: Vec = Vec::new();
for_each_token(cmdline, |token: &str| {
if matches!(token, "%0" | "%1") {
tokens.push(url.to_string());
} else {
tokens.push(token.to_string());
}
});
if tokens.is_empty() {
Err(Error::new(ErrorKind::NotFound, "invalid command"))
} else {
let mut cmd = Command::new(&tokens[0]);
if tokens.len() > 1 {
cmd.args(&tokens[1..]);
}
Ok(cmd)
}
}
webbrowser-1.0.5/tests/common.rs 0000644 0000000 0000000 00000011750 10461020230 0015014 0 ustar 0000000 0000000 use actix_files as fs;
use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer, Responder};
use crossbeam_channel as cbc;
use rand::RngCore;
use std::{io::Write, path::PathBuf, sync::Arc};
use urlencoding::decode;
use webbrowser::{open_browser, Browser};
#[derive(Clone)]
struct AppState {
tx: Arc>,
}
async fn log_handler(req: HttpRequest, data: web::Data) -> impl Responder {
if data.tx.send(req.uri().to_string()).is_err() {
panic!("channel send failed");
}
let uri = req.uri();
if uri.path() == URI_PNG_1PX {
HttpResponse::Ok()
.content_type("image/png")
.body(PNG_1PX.to_vec())
} else {
HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(format!("URI: {}
", req.uri()))
}
}
async fn delayed_response(req: HttpRequest) -> impl Responder {
let qs = req.query_string();
let ms: u64 = qs
.replace("ms=", "")
.parse()
.expect("failed to parse millis");
tokio::time::sleep(tokio::time::Duration::from_millis(ms)).await;
HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(format!(
"Delayed by {qs}ms
"
))
}
pub async fn check_request_received_using(uri: String, host: &str, op: F)
where
F: FnOnce(&str, u16),
{
// initialize env logger
let _ = env_logger::try_init();
// start the server on a random port
let bind_addr = format!("{host}:0");
let (tx, rx) = cbc::bounded(2);
let data = AppState {
tx: Arc::new(tx.clone()),
};
let http_server = HttpServer::new(move || {
let wasm_pkg_dir = "tests/test-wasm-app/pkg";
let _ = std::fs::create_dir_all(std::path::Path::new(wasm_pkg_dir));
App::new()
.service(fs::Files::new("/static/wasm", wasm_pkg_dir))
.service(web::scope("/utils").route("/delay", web::get().to(delayed_response)))
.app_data(web::Data::new(data.clone()))
.default_service(web::to(log_handler))
})
.bind(&bind_addr)
.unwrap_or_else(|_| panic!("Can not bind to {}", &bind_addr));
let port = http_server
.addrs()
.first()
.expect("Failed to find bound address")
.port();
let server = http_server.run();
let server_handle = server.handle();
tokio::spawn(server);
// invoke the op
op(&format!("http://{}:{}{}", host, port, &uri), port);
// wait for the url to be hit
let timeout = option_env!("TEST_REQ_TIMEOUT")
.map(|s| s.parse().expect("failed to parse TEST_REQ_TIMEOUT"))
.unwrap_or(90);
match rx.recv_timeout(std::time::Duration::from_secs(timeout)) {
Ok(msg) => assert_eq!(decode(&msg).unwrap(), uri),
Err(_) => panic!("failed to receive uri data"),
}
// stop the server
server_handle.stop(true).await;
}
#[allow(dead_code)]
pub async fn check_request_received(browser: Browser, uri: String) {
check_request_received_using(uri, "127.0.0.1", |url, _port| {
open_browser(browser, url).expect("failed to open browser");
})
.await;
}
#[allow(dead_code)]
pub async fn check_local_file(browser: Browser, html_dir: Option, url_op: F)
where
F: FnOnce(&PathBuf) -> String,
{
let cwd = std::env::current_dir().expect("unable to determine current dir");
let tmpdir = cwd.join("target").join("tmp");
let html_dir = html_dir.unwrap_or(tmpdir);
let id = rand::thread_rng().next_u32();
let pb = html_dir.join(format!("test.{id}.html"));
let img_uri = format!("{URI_PNG_1PX}?r={id}");
check_request_received_using(img_uri, "127.0.0.1", |uri, _port| {
let url = url_op(&pb);
let mut html_file = std::fs::File::create(&pb).expect("failed to create html file");
html_file
.write_fmt(format_args!(
"html file: {}
url: {}
img:
",
&pb.as_os_str().to_string_lossy(),
url,
uri
))
.expect("failed to write html file");
drop(html_file);
open_browser(browser, &url).expect("failed to open browser");
})
.await;
let _ = std::fs::remove_file(&pb);
}
#[allow(dead_code)]
pub async fn check_browser(browser: Browser, platform: &str) {
check_request_received(browser, format!("/{platform}")).await;
check_request_received(browser, format!("/{platform}/😀😀😀")).await;
}
const URI_PNG_1PX: &str = "/img/1px.png";
// generated from https://shoonia.github.io/1x1/#ff4563ff
const PNG_1PX: [u8; 82] = [
137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 1, 0, 0, 0, 1, 8, 2, 0,
0, 0, 144, 119, 83, 222, 0, 0, 0, 1, 115, 82, 71, 66, 0, 174, 206, 28, 233, 0, 0, 0, 12, 73,
68, 65, 84, 24, 87, 99, 248, 239, 154, 12, 0, 3, 238, 1, 168, 16, 134, 253, 64, 0, 0, 0, 0, 73,
69, 78, 68, 174, 66, 96, 130,
];
webbrowser-1.0.5/tests/test-ios-app/.gitignore 0000644 0000000 0000000 00000000174 10461020230 0017471 0 ustar 0000000 0000000 build/
testglue/Cargo.lock
testglue/target/
test-ios-app.xcodeproj/xcuserdata/
test-ios-app.xcodeproj/project.xcworkspace/
webbrowser-1.0.5/tests/test-ios-app/test-ios-app/Assets.xcassets/AccentColor.colorset/Contents.json 0000644 0000000 0000000 00000000173 10461020230 0031640 0 ustar 0000000 0000000 {
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
webbrowser-1.0.5/tests/test-ios-app/test-ios-app/Assets.xcassets/AppIcon.appiconset/Contents.json 0000644 0000000 0000000 00000003067 10461020230 0031315 0 ustar 0000000 0000000 {
"images" : [
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
webbrowser-1.0.5/tests/test-ios-app/test-ios-app/Assets.xcassets/Contents.json 0000644 0000000 0000000 00000000077 10461020230 0025616 0 ustar 0000000 0000000 {
"info" : {
"author" : "xcode",
"version" : 1
}
}
webbrowser-1.0.5/tests/test-ios-app/test-ios-app/ContentView.swift 0000644 0000000 0000000 00000000671 10461020230 0023353 0 ustar 0000000 0000000 import SwiftUI
let SERVER_URL = "https://ip.rootnet.in"
struct ContentView: View {
var body: some View {
Text("Hello World!")
.padding()
.onAppear(perform: openBrowser)
}
private func openBrowser() {
let _ = TestGlueInterface.testOpenBrowser(url: SERVER_URL)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
././@LongLink 0000644 0000000 0000000 00000000147 00000000000 0007775 L ustar webbrowser-1.0.5/tests/test-ios-app/test-ios-app/Preview Content/Preview Assets.xcassets/Contents.json webbrowser-1.0.5/tests/test-ios-app/test-ios-app/Preview Content/Preview Assets.xcassets/Contents.js0000644 0000000 0000000 00000000077 10461020230 0031677 0 ustar 0000000 0000000 {
"info" : {
"author" : "xcode",
"version" : 1
}
}
webbrowser-1.0.5/tests/test-ios-app/test-ios-app/Test-Glue-Header.h 0000644 0000000 0000000 00000000364 10461020230 0023177 0 ustar 0000000 0000000 //
// Test-Glue-Header.h
// test-ios-app
//
// Created by Amod Malviya on 05/09/22.
//
#ifndef Test_Glue_Header_h
#define Test_Glue_Header_h
#include
void test_open_webbrowser(const char* url);
#endif /* Test_Glue_Header_h */
webbrowser-1.0.5/tests/test-ios-app/test-ios-app/TestGlueInterface.swift 0000644 0000000 0000000 00000000173 10461020230 0024460 0 ustar 0000000 0000000 class TestGlueInterface {
static func testOpenBrowser(url: String) -> Void {
test_open_webbrowser(url)
}
}
webbrowser-1.0.5/tests/test-ios-app/test-ios-app/test_ios_appApp.swift 0000644 0000000 0000000 00000000357 10461020230 0024241 0 ustar 0000000 0000000 //
// test_ios_appApp.swift
// test-ios-app
//
// Created by Amod Malviya on 05/09/22.
//
import SwiftUI
@main
struct test_ios_appApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
webbrowser-1.0.5/tests/test-ios-app/test-ios-app.xcodeproj/project.pbxproj 0000644 0000000 0000000 00000055622 10461020230 0025106 0 ustar 0000000 0000000 // !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 55;
objects = {
/* Begin PBXBuildFile section */
38CAD42128C5BA2800150BA6 /* test_ios_appApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38CAD42028C5BA2800150BA6 /* test_ios_appApp.swift */; };
38CAD42328C5BA2800150BA6 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38CAD42228C5BA2800150BA6 /* ContentView.swift */; };
38CAD42528C5BA2B00150BA6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 38CAD42428C5BA2B00150BA6 /* Assets.xcassets */; };
38CAD42828C5BA2B00150BA6 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 38CAD42728C5BA2B00150BA6 /* Preview Assets.xcassets */; };
38CAD44E28C5CB6500150BA6 /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 38CAD44D28C5CB5C00150BA6 /* libresolv.tbd */; };
38CAD45128C5CCF800150BA6 /* TestGlueInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38CAD45028C5CCF800150BA6 /* TestGlueInterface.swift */; };
38CAD45328C5CF6400150BA6 /* libtestglue.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 38CAD45228C5CF5500150BA6 /* libtestglue.a */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
38CAD42E28C5BA2B00150BA6 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 38CAD41528C5BA2800150BA6 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 38CAD41C28C5BA2800150BA6;
remoteInfo = "test-ios-app";
};
38CAD43828C5BA2B00150BA6 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 38CAD41528C5BA2800150BA6 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 38CAD41C28C5BA2800150BA6;
remoteInfo = "test-ios-app";
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
38CAD41D28C5BA2800150BA6 /* test-ios-app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "test-ios-app.app"; sourceTree = BUILT_PRODUCTS_DIR; };
38CAD42028C5BA2800150BA6 /* test_ios_appApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = test_ios_appApp.swift; sourceTree = ""; };
38CAD42228C5BA2800150BA6 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
38CAD42428C5BA2B00150BA6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
38CAD42728C5BA2B00150BA6 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
38CAD42D28C5BA2B00150BA6 /* test-ios-appTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "test-ios-appTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
38CAD43728C5BA2B00150BA6 /* test-ios-appUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "test-ios-appUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
38CAD44B28C5CB0800150BA6 /* libtestglue.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtestglue.a; path = testglue/target/target/debug/libtestglue.a; sourceTree = ""; };
38CAD44D28C5CB5C00150BA6 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; };
38CAD44F28C5CBE800150BA6 /* Test-Glue-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Test-Glue-Header.h"; sourceTree = ""; };
38CAD45028C5CCF800150BA6 /* TestGlueInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestGlueInterface.swift; sourceTree = ""; };
38CAD45228C5CF5500150BA6 /* libtestglue.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtestglue.a; path = testglue/target/target/debug/libtestglue.a; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
38CAD41A28C5BA2800150BA6 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
38CAD45328C5CF6400150BA6 /* libtestglue.a in Frameworks */,
38CAD44E28C5CB6500150BA6 /* libresolv.tbd in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
38CAD42A28C5BA2B00150BA6 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
38CAD43428C5BA2B00150BA6 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
38CAD41428C5BA2800150BA6 = {
isa = PBXGroup;
children = (
38CAD41F28C5BA2800150BA6 /* test-ios-app */,
38CAD41E28C5BA2800150BA6 /* Products */,
38CAD44A28C5CB0800150BA6 /* Frameworks */,
);
sourceTree = "";
};
38CAD41E28C5BA2800150BA6 /* Products */ = {
isa = PBXGroup;
children = (
38CAD41D28C5BA2800150BA6 /* test-ios-app.app */,
38CAD42D28C5BA2B00150BA6 /* test-ios-appTests.xctest */,
38CAD43728C5BA2B00150BA6 /* test-ios-appUITests.xctest */,
);
name = Products;
sourceTree = "";
};
38CAD41F28C5BA2800150BA6 /* test-ios-app */ = {
isa = PBXGroup;
children = (
38CAD45028C5CCF800150BA6 /* TestGlueInterface.swift */,
38CAD42028C5BA2800150BA6 /* test_ios_appApp.swift */,
38CAD42228C5BA2800150BA6 /* ContentView.swift */,
38CAD44F28C5CBE800150BA6 /* Test-Glue-Header.h */,
38CAD42428C5BA2B00150BA6 /* Assets.xcassets */,
38CAD42628C5BA2B00150BA6 /* Preview Content */,
);
path = "test-ios-app";
sourceTree = "";
};
38CAD42628C5BA2B00150BA6 /* Preview Content */ = {
isa = PBXGroup;
children = (
38CAD42728C5BA2B00150BA6 /* Preview Assets.xcassets */,
);
path = "Preview Content";
sourceTree = "";
};
38CAD44A28C5CB0800150BA6 /* Frameworks */ = {
isa = PBXGroup;
children = (
38CAD45228C5CF5500150BA6 /* libtestglue.a */,
38CAD44D28C5CB5C00150BA6 /* libresolv.tbd */,
38CAD44B28C5CB0800150BA6 /* libtestglue.a */,
);
name = Frameworks;
sourceTree = "";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
38CAD41C28C5BA2800150BA6 /* test-ios-app */ = {
isa = PBXNativeTarget;
buildConfigurationList = 38CAD44128C5BA2B00150BA6 /* Build configuration list for PBXNativeTarget "test-ios-app" */;
buildPhases = (
38CAD41928C5BA2800150BA6 /* Sources */,
38CAD41A28C5BA2800150BA6 /* Frameworks */,
38CAD41B28C5BA2800150BA6 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "test-ios-app";
productName = "test-ios-app";
productReference = 38CAD41D28C5BA2800150BA6 /* test-ios-app.app */;
productType = "com.apple.product-type.application";
};
38CAD42C28C5BA2B00150BA6 /* test-ios-appTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 38CAD44428C5BA2B00150BA6 /* Build configuration list for PBXNativeTarget "test-ios-appTests" */;
buildPhases = (
38CAD42928C5BA2B00150BA6 /* Sources */,
38CAD42A28C5BA2B00150BA6 /* Frameworks */,
38CAD42B28C5BA2B00150BA6 /* Resources */,
);
buildRules = (
);
dependencies = (
38CAD42F28C5BA2B00150BA6 /* PBXTargetDependency */,
);
name = "test-ios-appTests";
productName = "test-ios-appTests";
productReference = 38CAD42D28C5BA2B00150BA6 /* test-ios-appTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
38CAD43628C5BA2B00150BA6 /* test-ios-appUITests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 38CAD44728C5BA2B00150BA6 /* Build configuration list for PBXNativeTarget "test-ios-appUITests" */;
buildPhases = (
38CAD43328C5BA2B00150BA6 /* Sources */,
38CAD43428C5BA2B00150BA6 /* Frameworks */,
38CAD43528C5BA2B00150BA6 /* Resources */,
);
buildRules = (
);
dependencies = (
38CAD43928C5BA2B00150BA6 /* PBXTargetDependency */,
);
name = "test-ios-appUITests";
productName = "test-ios-appUITests";
productReference = 38CAD43728C5BA2B00150BA6 /* test-ios-appUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
38CAD41528C5BA2800150BA6 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1340;
LastUpgradeCheck = 1340;
TargetAttributes = {
38CAD41C28C5BA2800150BA6 = {
CreatedOnToolsVersion = 13.4.1;
};
38CAD42C28C5BA2B00150BA6 = {
CreatedOnToolsVersion = 13.4.1;
TestTargetID = 38CAD41C28C5BA2800150BA6;
};
38CAD43628C5BA2B00150BA6 = {
CreatedOnToolsVersion = 13.4.1;
TestTargetID = 38CAD41C28C5BA2800150BA6;
};
};
};
buildConfigurationList = 38CAD41828C5BA2800150BA6 /* Build configuration list for PBXProject "test-ios-app" */;
compatibilityVersion = "Xcode 13.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 38CAD41428C5BA2800150BA6;
productRefGroup = 38CAD41E28C5BA2800150BA6 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
38CAD41C28C5BA2800150BA6 /* test-ios-app */,
38CAD42C28C5BA2B00150BA6 /* test-ios-appTests */,
38CAD43628C5BA2B00150BA6 /* test-ios-appUITests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
38CAD41B28C5BA2800150BA6 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
38CAD42828C5BA2B00150BA6 /* Preview Assets.xcassets in Resources */,
38CAD42528C5BA2B00150BA6 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
38CAD42B28C5BA2B00150BA6 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
38CAD43528C5BA2B00150BA6 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
38CAD41928C5BA2800150BA6 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
38CAD45128C5CCF800150BA6 /* TestGlueInterface.swift in Sources */,
38CAD42328C5BA2800150BA6 /* ContentView.swift in Sources */,
38CAD42128C5BA2800150BA6 /* test_ios_appApp.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
38CAD42928C5BA2B00150BA6 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
38CAD43328C5BA2B00150BA6 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
38CAD42F28C5BA2B00150BA6 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 38CAD41C28C5BA2800150BA6 /* test-ios-app */;
targetProxy = 38CAD42E28C5BA2B00150BA6 /* PBXContainerItemProxy */;
};
38CAD43928C5BA2B00150BA6 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 38CAD41C28C5BA2800150BA6 /* test-ios-app */;
targetProxy = 38CAD43828C5BA2B00150BA6 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
38CAD43F28C5BA2B00150BA6 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.5;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
38CAD44028C5BA2B00150BA6 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.5;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
38CAD44228C5BA2B00150BA6 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"test-ios-app/Preview Content\"";
DEVELOPMENT_TEAM = "";
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/testglue/target/target/debug",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "in.rootnet.webbrowser.test-ios-app";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/test-ios-app/Test-Glue-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Debug;
};
38CAD44328C5BA2B00150BA6 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"test-ios-app/Preview Content\"";
DEVELOPMENT_TEAM = "";
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/testglue/target/target/debug",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "in.rootnet.webbrowser.test-ios-app";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/test-ios-app/Test-Glue-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Release;
};
38CAD44528C5BA2B00150BA6 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = X66SW5KC6C;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.5;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "in.rootnet.webbrowser.test-ios-appTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/test-ios-app.app/test-ios-app";
};
name = Debug;
};
38CAD44628C5BA2B00150BA6 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = X66SW5KC6C;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.5;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "in.rootnet.webbrowser.test-ios-appTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/test-ios-app.app/test-ios-app";
};
name = Release;
};
38CAD44828C5BA2B00150BA6 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = X66SW5KC6C;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "in.rootnet.webbrowser.test-ios-appUITests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = "test-ios-app";
};
name = Debug;
};
38CAD44928C5BA2B00150BA6 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = X66SW5KC6C;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "in.rootnet.webbrowser.test-ios-appUITests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = "test-ios-app";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
38CAD41828C5BA2800150BA6 /* Build configuration list for PBXProject "test-ios-app" */ = {
isa = XCConfigurationList;
buildConfigurations = (
38CAD43F28C5BA2B00150BA6 /* Debug */,
38CAD44028C5BA2B00150BA6 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
38CAD44128C5BA2B00150BA6 /* Build configuration list for PBXNativeTarget "test-ios-app" */ = {
isa = XCConfigurationList;
buildConfigurations = (
38CAD44228C5BA2B00150BA6 /* Debug */,
38CAD44328C5BA2B00150BA6 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
38CAD44428C5BA2B00150BA6 /* Build configuration list for PBXNativeTarget "test-ios-appTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
38CAD44528C5BA2B00150BA6 /* Debug */,
38CAD44628C5BA2B00150BA6 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
38CAD44728C5BA2B00150BA6 /* Build configuration list for PBXNativeTarget "test-ios-appUITests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
38CAD44828C5BA2B00150BA6 /* Debug */,
38CAD44928C5BA2B00150BA6 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 38CAD41528C5BA2800150BA6 /* Project object */;
}
webbrowser-1.0.5/tests/test_android.rs 0000644 0000000 0000000 00000006740 10461020230 0016206 0 ustar 0000000 0000000 mod common;
mod tests {
const TEST_PLATFORM: &str = "android";
use super::common::check_request_received_using;
use std::fs;
use std::path::PathBuf;
use std::process::Command;
use webbrowser::Browser;
// to run this test, run it as:
// cargo test --test test_android -- --ignored
//
// For this to run, we need ANDROID_NDK_ROOT env defined, e.g.
// ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/22.1.7171670
//
#[ignore]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_android() {
let mut app_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
app_dir.push("tests/test-android-app");
let uri = format!("/{TEST_PLATFORM}");
check_request_received_using(uri, "127.0.0.1", |url, port| {
// modify android app code to use the correct url
let mut lib_rs = PathBuf::from(&app_dir);
lib_rs.push("src/lib.rs");
let old_code =
fs::read_to_string(&lib_rs).expect("failed to read lib.rs for android app");
let new_code = old_code
.split('\n')
.map(|s| {
if s.starts_with("const SERVER_URL") {
format!("const SERVER_URL: &str = \"{url}\";")
} else {
s.into()
}
})
.collect::>()
.join("\n");
fs::write(&lib_rs, new_code).expect("failed to modify src/lib.rs");
log::debug!("modified src/lib.rs to use {url}");
// uninstall previous app version if existing
let mut adb_cmd = Command::new("adb");
adb_cmd.arg("uninstall").arg("rust.test_android_app");
if let Ok(status) = adb_cmd.current_dir(&app_dir).status() {
if status.success() {
log::info!("adb uninstall successful");
} else {
log::error!("adb uninstall failed");
}
} else {
log::error!("failed to run {adb_cmd:?}");
}
let adb_reverse_port = format!("tcp:{port}");
let mut adb_cmd = Command::new("adb");
adb_cmd
.arg("reverse")
.arg(&adb_reverse_port)
.arg(&adb_reverse_port);
assert!(
adb_cmd.status().expect("Failed to invoke").success(),
"Failed to run {adb_cmd:?}"
);
// invoke app in android
let mut apk_run_cmd = Command::new("cargo");
apk_run_cmd.arg("apk").arg("run");
if let Some(target) = option_env!("ANDROID_TARGET") {
apk_run_cmd.arg("--target").arg(target);
}
apk_run_cmd.arg("--no-logcat");
let apk_run_status = apk_run_cmd.current_dir(&app_dir).status();
// revert to the old code
fs::write(&lib_rs, &old_code).expect("failed to modify src/lib.rs");
// check for apk run status
assert!(
apk_run_status.expect("Failed to invoke").success(),
"failed to run {apk_run_cmd:?}"
);
})
.await;
}
#[test]
fn test_existence_default() {
assert!(Browser::is_available(), "should have found a browser");
}
#[test]
fn test_non_existence_safari() {
assert!(!Browser::Safari.exists(), "should not have found Safari");
}
}
webbrowser-1.0.5/tests/test_ios.rs 0000644 0000000 0000000 00000011722 10461020230 0015354 0 ustar 0000000 0000000 #[cfg(target_os = "macos")]
mod common;
#[cfg(target_os = "macos")]
mod tests {
const TEST_PLATFORM: &str = "ios";
use super::common::check_request_received_using;
use std::fs;
use std::path::PathBuf;
use std::process::{Command, ExitStatus, Stdio};
use webbrowser::Browser;
// to run this test, run it as:
// cargo test --test test_ios -- --ignored
//
// MAKE SURE: an iOS simulator instance is already running
#[ignore]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ios() {
let uri = format!("/{}", TEST_PLATFORM);
let ipv4 = get_ipv4_address();
let mut app_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
app_dir.push("tests/test-ios-app");
// build test glue code
let mut glue_dir = PathBuf::from(&app_dir);
glue_dir.push("testglue");
run_cmd(&glue_dir, &["./build"]).expect("glue code build failed");
let compile_app = || {
run_cmd(
&app_dir,
&[
"xcrun",
"xcodebuild",
"-project",
"test-ios-app.xcodeproj",
"-configuration",
"Debug",
"-sdk",
"iphonesimulator",
"-destination",
"platform=iOS Simulator,name=iphone-latest",
"-arch",
if cfg!(target_arch = "aarch64") {
"arm64"
} else {
"x86_64"
},
],
)
};
compile_app().expect("compilation warm up failed for the app");
// invoke server
check_request_received_using(uri, &ipv4, |url, _port| {
// modify ios app code to use the correct url
let mut swift_src = PathBuf::from(&app_dir);
swift_src.push("test-ios-app/ContentView.swift");
let old_code =
fs::read_to_string(&swift_src).expect("failed to read ContentView.swift");
let new_code = old_code
.split('\n')
.map(|s| {
if s.starts_with("let SERVER_URL") {
format!("let SERVER_URL = \"{}\"", url)
} else {
s.into()
}
})
.collect::>()
.join("\n");
fs::write(&swift_src, new_code).expect("failed to modify ContentView.swift");
let revert_code = || fs::write(&swift_src, &old_code).expect("failed to revert code");
let handle_exec_result = |result: std::io::Result, err_msg: &str| {
revert_code();
let success = match result {
Ok(status) => status.success(),
Err(_) => false,
};
if !success {
eprintln!("{err_msg}");
std::process::exit(1);
}
};
// build app
let exec_result = compile_app();
handle_exec_result(exec_result, "failed to build ios app");
// launch app on simulator
let exec_result = run_cmd(
&app_dir,
&[
"xcrun",
"simctl",
"install",
"booted",
"build/Debug-iphonesimulator/test-ios-app.app",
],
);
handle_exec_result(exec_result, "failed to install app on simulator");
let exec_result = run_cmd(
&app_dir,
&[
"xcrun",
"simctl",
"launch",
"booted",
"in.rootnet.webbrowser.test-ios-app",
],
);
handle_exec_result(exec_result, "failed to launch app on simulator");
// revert to the old code
revert_code();
})
.await;
}
fn get_ipv4_address() -> String {
let output = Command::new("sh")
.arg("-c")
.arg("ifconfig | grep 'inet ' | awk '{ print $2 }' | grep -v ^127.0.0")
.output()
.expect("failed to get non-local ipv4 address");
std::str::from_utf8(&output.stdout)
.expect("unable to parse output into utf8")
.split('\n')
.next()
.expect("no ip address found")
.into()
}
#[test]
fn test_existence_default() {
assert!(Browser::is_available(), "should have found a browser");
}
fn run_cmd(app_dir: &PathBuf, args: &[&str]) -> std::io::Result {
Command::new(args[0])
.args(&args[1..])
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.current_dir(app_dir)
.status()
}
}
webbrowser-1.0.5/tests/test_macos.rs 0000644 0000000 0000000 00000005245 10461020230 0015667 0 ustar 0000000 0000000 #[cfg(target_os = "macos")]
mod common;
#[cfg(target_os = "macos")]
mod tests {
const TEST_PLATFORM: &str = "macos";
use super::common::*;
use webbrowser::Browser;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_open_default() {
check_browser(Browser::Default, TEST_PLATFORM).await;
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_open_safari() {
check_browser(Browser::Safari, TEST_PLATFORM).await;
}
// #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
// #[ignore]
// async fn test_open_firefox() {
// check_browser(Browser::Firefox, TEST_PLATFORM).await;
// }
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
#[ignore]
async fn test_open_chrome() {
check_browser(Browser::Chrome, TEST_PLATFORM).await;
}
#[test]
fn test_existence_default() {
assert!(Browser::is_available(), "should have found a browser");
}
#[test]
fn test_existence_safari() {
assert!(Browser::Safari.exists(), "should have found Safari");
}
#[test]
fn test_non_existence_webpositive() {
assert!(
!Browser::WebPositive.exists(),
"should not have found WebPositive",
);
}
#[cfg(not(feature = "hardened"))]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_local_file_abs_path() {
check_local_file(Browser::Default, None, |pb| {
pb.as_os_str().to_string_lossy().into()
})
.await;
}
#[cfg(not(feature = "hardened"))]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_local_file_rel_path() {
let cwd = std::env::current_dir().expect("unable to get current dir");
check_local_file(Browser::Default, None, |pb| {
pb.strip_prefix(cwd)
.expect("strip prefix failed")
.as_os_str()
.to_string_lossy()
.into()
})
.await;
}
#[cfg(not(feature = "hardened"))]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_local_file_uri() {
check_local_file(Browser::Default, None, |pb| {
url::Url::from_file_path(pb)
.expect("failed to convert path to url")
.to_string()
})
.await;
}
#[cfg(feature = "hardened")]
#[test]
fn test_hardened_mode() {
let err = webbrowser::open("file:///etc/passwd")
.expect_err("expected non-http url to fail in hardened mode");
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
}
}
webbrowser-1.0.5/tests/test_unix.rs 0000644 0000000 0000000 00000004115 10461020230 0015543 0 ustar 0000000 0000000 #[cfg(all(unix, not(target_os = "macos")))]
mod common;
#[cfg(all(unix, not(target_os = "macos")))]
mod tests {
const TEST_PLATFORM: &str = "unix";
use super::common::*;
use serial_test::serial;
use webbrowser::Browser;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
#[serial]
async fn test_open_default() {
check_browser(Browser::Default, TEST_PLATFORM).await;
}
#[test]
fn test_existence_default() {
assert!(Browser::is_available(), "should have found a browser");
}
#[test]
fn test_non_existence_safari() {
assert!(!Browser::Safari.exists(), "should not have found Safari");
}
#[cfg(not(feature = "hardened"))]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
#[serial]
async fn test_local_file_abs_path() {
check_local_file(Browser::Default, None, |pb| {
pb.as_os_str().to_string_lossy().into()
})
.await;
}
#[cfg(not(feature = "hardened"))]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
#[serial]
async fn test_local_file_rel_path() {
let cwd = std::env::current_dir().expect("unable to get current dir");
check_local_file(Browser::Default, None, |pb| {
pb.strip_prefix(cwd)
.expect("strip prefix failed")
.as_os_str()
.to_string_lossy()
.into()
})
.await;
}
#[cfg(not(feature = "hardened"))]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
#[serial]
async fn test_local_file_uri() {
check_local_file(Browser::Default, None, |pb| {
url::Url::from_file_path(pb)
.expect("failed to convert path to url")
.to_string()
})
.await;
}
#[cfg(feature = "hardened")]
#[test]
fn test_hardened_mode() {
let err = webbrowser::open("file:///etc/passwd")
.expect_err("expected non-http url to fail in hardened mode");
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
}
}
webbrowser-1.0.5/tests/test_wasm.rs 0000644 0000000 0000000 00000003373 10461020230 0015534 0 ustar 0000000 0000000 #[cfg(any(target_os = "macos", target_os = "linux"))]
mod common;
#[cfg(any(target_os = "macos", target_os = "linux"))]
mod tests {
const TEST_PLATFORM: &str = "wasm32";
use super::common::check_request_received_using;
use std::fs;
use std::path::PathBuf;
// to run this test, run it as:
// cargo test --test test_wasm32 -- --ignored
//
#[ignore]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_wasm32() {
let uri = &format!("/{TEST_PLATFORM}");
let ipv4 = "127.0.0.1";
check_request_received_using(uri.into(), ipv4, |url, _port| {
// modify html to use the correct url
let mut app_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
app_dir.push("tests/test-wasm-app");
let mut src_html = PathBuf::from(&app_dir);
src_html.push("test.html");
let mut dst_html = PathBuf::from(&app_dir);
dst_html.push("pkg/test.html");
let old_html = fs::read_to_string(&src_html).expect("failed to read test.html");
let new_html = old_html.replace("DYNAMIC_URL_TBD", url);
fs::write(&dst_html, new_html).expect("failed to update dst test.html");
// ensure favicon is present
let mut favicon = PathBuf::from(&app_dir);
favicon.push("pkg/favicon.ico");
fs::write(&favicon, "").expect("failed to create favicon.ico");
// open browser
let html_url = url.replace(uri, "/static/wasm/test.html");
//println!("URL: {}", html_url);
let status = webbrowser::open(&html_url);
// check for browser run status
status.expect("browser open failed");
})
.await;
}
}
webbrowser-1.0.5/tests/test_windows.rs 0000644 0000000 0000000 00000004247 10461020230 0016260 0 ustar 0000000 0000000 #[cfg(target_os = "windows")]
mod common;
#[cfg(target_os = "windows")]
mod tests {
const TEST_PLATFORM: &str = "windows";
use super::common::*;
use webbrowser::Browser;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_open_default() {
check_browser(Browser::Default, TEST_PLATFORM).await;
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
#[ignore]
async fn test_open_internet_explorer() {
check_browser(Browser::InternetExplorer, TEST_PLATFORM).await;
}
#[test]
fn test_existence_default() {
assert!(Browser::is_available(), "should have found a browser");
}
#[test]
fn test_non_existence_safari() {
assert!(!Browser::Safari.exists(), "should not have found Safari");
}
#[cfg(not(feature = "hardened"))]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_local_file_abs_path() {
check_local_file(Browser::Default, None, |pb| {
pb.as_os_str().to_string_lossy().into()
})
.await;
}
#[cfg(not(feature = "hardened"))]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_local_file_rel_path() {
let cwd = std::env::current_dir().expect("unable to get current dir");
check_local_file(Browser::Default, None, |pb| {
pb.strip_prefix(cwd)
.expect("strip prefix failed")
.as_os_str()
.to_string_lossy()
.into()
})
.await;
}
#[cfg(not(feature = "hardened"))]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_local_file_uri() {
check_local_file(Browser::Default, None, |pb| {
url::Url::from_file_path(pb)
.expect("failed to convert path to url")
.to_string()
})
.await;
}
#[cfg(feature = "hardened")]
#[test]
fn test_hardened_mode() {
let err = webbrowser::open("file:///blah.html")
.expect_err("expected non-http url to fail in hardened mode");
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
}
}