Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 76 additions & 43 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ name: CI
env:
DEBUG: napi:*
APP_NAME: webview
MACOSX_DEPLOYMENT_TARGET: "10.13"
CARGO_INCREMENTAL: "1"
MACOSX_DEPLOYMENT_TARGET: '10.13'
CARGO_INCREMENTAL: '1'
CARGO_TERM_COLOR: always

permissions:
Expand All @@ -16,20 +16,20 @@ on:
branches:
- main
tags-ignore:
- "**"
- '**'
paths-ignore:
- "**/*.md"
- '**/*.md'
- LICENSE
- "**/*.gitignore"
- '**/*.gitignore'
- .editorconfig
- docs/**
pull_request:
branches:
- main
paths-ignore:
- "**/*.md"
- '**/*.md'
- LICENSE
- "**/*.gitignore"
- '**/*.gitignore'
- .editorconfig
- docs/**

Expand All @@ -53,68 +53,100 @@ jobs:
build_script: |
export PKG_CONFIG_ALLOW_CROSS=1
bun run build --target aarch64-apple-darwin

# Windows
- os: windows-latest
target: x86_64-pc-windows-msvc
- os: windows-latest
target: i686-pc-windows-msvc
- os: windows-latest
target: aarch64-pc-windows-msvc

# Linux x86_64
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
sys_deps: pkg-config libwebkit2gtk-4.1-dev libsoup-3.0-dev libglib2.0-dev libcairo2-dev libpango1.0-dev libatk1.0-dev libgdk-pixbuf2.0-dev libgtk-3-dev

# Linux ARM64
- os: ubuntu-latest
- os: ubuntu-22.04
target: aarch64-unknown-linux-gnu
cross_arch: arm64
cross_sys_deps: libglib2.0-dev:arm64 libwebkit2gtk-4.1-dev:arm64 libsoup-3.0-dev:arm64 libcairo2-dev:arm64 libpango1.0-dev:arm64 libatk1.0-dev:arm64 libgdk-pixbuf2.0-dev:arm64 libgtk-3-dev:arm64
# Added X11, Wayland, and XKB dependencies required by tao/wry
cross_sys_deps: >-
libwebkit2gtk-4.1-dev:arm64
libsoup-3.0-dev:arm64
libgtk-3-dev:arm64
libx11-dev:arm64
libxkbcommon-dev:arm64
libwayland-dev:arm64
libxcb-shape0-dev:arm64
libxcb-xfixes0-dev:arm64
libglib2.0-dev:arm64
libcairo2-dev:arm64
libpango1.0-dev:arm64
libatk1.0-dev:arm64
libgdk-pixbuf2.0-dev:arm64
cross_gcc: gcc-aarch64-linux-gnu
cross_gpp: g++-aarch64-linux-gnu
cross_triplet: aarch64-linux-gnu
build_script: |
set -e
rustup target add aarch64-unknown-linux-gnu
export PKG_CONFIG_ALLOW_CROSS=1
export PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu
export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig
# In Ubuntu Multiarch, the sysroot is actually /
export PKG_CONFIG_SYSROOT_DIR=/
# Only use the target's pkgconfig directory to prevent host library leakage
export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
export CC=aarch64-linux-gnu-gcc
export CXX=aarch64-linux-gnu-g++
export AR=aarch64-linux-gnu-ar
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc
bun run build --target aarch64-unknown-linux-gnu

# Linux ARMv7
- os: ubuntu-latest
- os: ubuntu-22.04
target: armv7-unknown-linux-gnueabihf
cross_arch: armhf
cross_sys_deps: libglib2.0-dev:armhf libwebkit2gtk-4.1-dev:armhf libsoup-3.0-dev:armhf libcairo2-dev:armhf libpango1.0-dev:armhf libatk1.0-dev:armhf libgdk-pixbuf2.0-dev:armhf libgtk-3-dev:armhf
# Added X11, Wayland, and XKB dependencies required by tao/wry
cross_sys_deps: >-
libwebkit2gtk-4.1-dev:armhf
libsoup-3.0-dev:armhf
libgtk-3-dev:armhf
libx11-dev:armhf
libxkbcommon-dev:armhf
libwayland-dev:armhf
libxcb-shape0-dev:armhf
libxcb-xfixes0-dev:armhf
libglib2.0-dev:armhf
libcairo2-dev:armhf
libpango1.0-dev:armhf
libatk1.0-dev:armhf
libgdk-pixbuf2.0-dev:armhf
cross_gcc: gcc-arm-linux-gnueabihf
cross_gpp: g++-arm-linux-gnueabihf
cross_triplet: arm-linux-gnueabihf
build_script: |
set -e
rustup target add armv7-unknown-linux-gnueabihf
export PKG_CONFIG_ALLOW_CROSS=1
export PKG_CONFIG_SYSROOT_DIR=/usr/arm-linux-gnueabihf
export PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig:/usr/share/pkgconfig
# In Ubuntu Multiarch, sysroot is /
export PKG_CONFIG_SYSROOT_DIR=/
# Only use target's pkgconfig to prevent host library leakage
export PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig
export CC=arm-linux-gnueabihf-gcc
export CXX=arm-linux-gnueabihf-g++
export AR=arm-linux-gnueabihf-ar
export CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc
bun run build --target armv7-unknown-linux-gnueabihf

# Android
- os: ubuntu-latest
target: aarch64-linux-android
- os: ubuntu-latest
target: armv7-linux-androideabi

runs-on: ${{ matrix.settings.os }}

steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand Down Expand Up @@ -166,16 +198,17 @@ jobs:
uses: actions/cache@v4
with:
path: target/
key: ${{ matrix.settings.target }}-cargo-build-${{ github.sha }}
# Added ${{ matrix.settings.os }} to the key
key: ${{ matrix.settings.os }}-${{ matrix.settings.target }}-cargo-build-${{ github.sha }}
restore-keys: |
${{ matrix.settings.target }}-cargo-build-
${{ matrix.settings.os }}-${{ matrix.settings.target }}-cargo-build-

- name: Install system dependencies (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y pkg-config

- name: Install system dependencies (Linux x86_64)
if: runner.os == 'Linux' && matrix.settings.sys_deps
run: |
Expand All @@ -185,24 +218,24 @@ jobs:
if: runner.os == 'Linux' && matrix.settings.cross_arch
run: |
sudo dpkg --add-architecture ${{ matrix.settings.cross_arch }}
# Update existing sources to be amd64-only to avoid 404s on foreign arches
# Handle traditional .list files
# We use a more careful sed to avoid doubling up [arch=...] tags and causing URI parse errors
sudo find /etc/apt/sources.list.d/ -type f -name "*.list" -exec sed -i -E 's/^(deb(-src)? )([^[])/\1[arch=amd64] \3/' {} +
if [ -f /etc/apt/sources.list ]; then
sudo sed -i -E 's/^(deb(-src)? )([^[])/\1[arch=amd64] \3/' /etc/apt/sources.list
fi
# Handle Ubuntu 24.04+ .sources files (DEB822 format)
sudo find /etc/apt/sources.list.d/ /etc/apt/ -type f -name "*.sources" -exec sh -c 'grep -q "Architectures:" "$1" || sed -i "/^Types: deb/a Architectures: amd64" "$1"' _ {} \;

# Remove problematic microsoft-prod repo if it exists, as it often breaks multi-arch on Noble images
sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list

# Add ports for the cross-architecture
echo "deb [arch=${{ matrix.settings.cross_arch }}] http://ports.ubuntu.com/ubuntu-ports $(lsb_release -sc) main restricted universe multiverse" | sudo tee /etc/apt/sources.list.d/ports.list
echo "deb [arch=${{ matrix.settings.cross_arch }}] http://ports.ubuntu.com/ubuntu-ports $(lsb_release -sc)-updates main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ports.list
echo "deb [arch=${{ matrix.settings.cross_arch }}] http://ports.ubuntu.com/ubuntu-ports $(lsb_release -sc)-security main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ports.list


# 1. Fix existing sources to be amd64-only
# This handles traditional .list files (Jammy 22.04)
sudo find /etc/apt/sources.list /etc/apt/sources.list.d/ -name "*.list" -type f -exec \
sudo sed -i -E 's/^(deb(-src)? )([^[])/\1[arch=amd64] \3/' {} +

# This handles the new DEB822 .sources files (Noble 24.04+)
# It adds "Architectures: amd64" to blocks that don't have it
sudo find /etc/apt/sources.list.d/ /etc/apt/ -name "*.sources" -type f -exec \
sudo sh -c 'grep -q "Architectures:" "$1" || sed -i "/^Types: deb/a Architectures: amd64" "$1"' _ {} \;

# 2. Add the Ports repository for the cross-architecture
CODENAME=$(lsb_release -sc)
echo "deb [arch=${{ matrix.settings.cross_arch }}] http://ports.ubuntu.com/ubuntu-ports $CODENAME main restricted universe multiverse" | sudo tee /etc/apt/sources.list.d/ports.list
echo "deb [arch=${{ matrix.settings.cross_arch }}] http://ports.ubuntu.com/ubuntu-ports ${CODENAME}-updates main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ports.list
echo "deb [arch=${{ matrix.settings.cross_arch }}] http://ports.ubuntu.com/ubuntu-ports ${CODENAME}-security main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ports.list

# 3. Update and install
sudo apt-get update
sudo apt-get install -y \
${{ matrix.settings.cross_gcc }} \
Expand Down Expand Up @@ -281,7 +314,7 @@ jobs:
- build
# - build-freebsd
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand Down
10 changes: 8 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libgtk-3-dev libglib2.0-dev pkg-config
sudo apt-get install -y libwebkit2gtk-4.1-dev libgtk-3-dev libglib2.0-dev pkg-config xvfb

- name: Install dependencies
run: bun install
Expand All @@ -52,5 +52,11 @@ jobs:
- name: Cargo check
run: bun run check

- name: Build native binding
run: bun run build

- name: Run tests
run: bun run test
env:
NO_AT_BRIDGE: 1
GDK_BACKEND: x11
run: xvfb-run -a bun run test
9 changes: 3 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,11 @@ version = "0.1.0"
crate-type = ["cdylib"]

[dependencies]
napi = { version = "3.8.2", default-features = true, features = ["napi9"] }
napi = { version = "3.8.2", default-features = true, features = ["napi9", "compat-mode"] }
napi-derive = "3.5.1"
tao = { version = "0.34.5", features = ["rwh_06"] }
tao = "0.34.5"
wry = { version = "0.53.5", features = ["devtools", "fullscreen"] }

[target.'cfg(target_os = "linux")'.dependencies]
gtk = "0.18"
glib = "0.18"
serde_json = "1"

[build-dependencies]
napi-build = "2"
Expand Down
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,36 @@ npm install @webviewjs/webview
| armv7-linux-androideabi | ✅ |
| aarch64-pc-windows-msvc | ✅ |

# Important Notes

## GTK Singleton Limitation on Linux

On Linux, GTK is implemented as a singleton, which means you can only create **one `Application` instance**. If you try to create multiple `Application` instances, the second one will fail with an error like:

```
Failed to initialize gtk backend!: Ya existe un objeto exportado para la interfaz org.gtk.Application
```

**Correct approach for multiple windows:**

```js
// ✅ Correct: Single Application with multiple BrowserWindows
const app = new Application();
const window1 = app.createBrowserWindow();
const window2 = app.createBrowserWindow();
app.run();
```

**Incorrect approach:**

```js
// ❌ Incorrect: Multiple Application instances (will fail on Linux)
const app1 = new Application();
const app2 = new Application(); // This will crash!
```

This limitation is specific to Linux. On Windows and macOS, you can create multiple Application instances, though it's still recommended to use a single Application instance for consistency.

# Examples

## Load external url
Expand Down
67 changes: 67 additions & 0 deletions __test__/high_level.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
import { Application, ControlFlow, Theme, getWebviewVersion, ProgressBarStatus } from '../index';

// Shared application instance to avoid GTK singleton conflicts
let app: Application;

// Skip Application instantiation in CI because GTK initialization is very slow
const isInCI = process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true';

describe('High-Level API', () => {
test('getWebviewVersion returns a string', () => {
const version = getWebviewVersion();
console.log('Webview Version:', version);
expect(typeof version).toBe('string');
});

test.skipIf(isInCI)('Application instantiation', () => {
app = new Application({
controlFlow: ControlFlow.Poll,
});
expect(app).toBeDefined();
expect(typeof app.run).toBe('function');
});

test.skipIf(isInCI)('BrowserWindow creation and properties', () => {
const win = app.createBrowserWindow({
title: 'High-Level Test Window',
width: 1024,
height: 768,
resizable: true,
decorations: true,
});

expect(win).toBeDefined();
// These might return defaults if window isn't created yet
expect(typeof win.title).toBe('string');
expect(win.isResizable()).toBe(true);
expect(win.isDecorated()).toBe(true);
});

test.skipIf(isInCI)('Monitor API', () => {
const win = app.createBrowserWindow();
const primary = win.getPrimaryMonitor();
if (primary) {
expect(primary.scaleFactor).toBeGreaterThan(0);
expect(primary.size.width).toBeGreaterThan(0);
}

const available = win.getAvailableMonitors();
expect(Array.isArray(available)).toBe(true);
if (available.length > 0) {
expect(available[0].size.width).toBeGreaterThan(0);
}
});

test.skipIf(isInCI)('Window actions (setters)', () => {
const win = app.createBrowserWindow();

win.setTitle('New Title');

// Testing the merged ProgressBarState (interface + enum)
win.setProgressBar({
status: ProgressBarStatus.Normal,
progress: 50,
});
});
});
6 changes: 0 additions & 6 deletions __test__/webview.test.mjs

This file was deleted.

Loading