- C++ 85.1%
- C 7.3%
- Shell 6.1%
- HTML 1.5%
Add mp-gpu-navigation.patch (posix_spawn, NavigationClient sequencing, QNX message-pump idle mitigations, EGL/qnx_screen GL), BerryShell launcher sources, MP test/deploy scripts, and updated mirror src/ copies. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|---|---|---|
| build | ||
| deploy | ||
| docs | ||
| patches | ||
| qnx_test_captures | ||
| scripts | ||
| src | ||
| .gitignore | ||
| browser-content-shell-131.zip | ||
| content-shell-term49.zip | ||
| README.md | ||
Chromium for BlackBerry 10 (QNX ARM32)
A port of Chromium's content_shell to QNX, targeting the BlackBerry Passport (Snapdragon 801, ARMv7-A). This brings a modern (~2024-era) Chromium engine to a platform that was only ever shipped with an ancient WebKit fork.
What Works
- Headless browser running in single-process mode on the BlackBerry Passport
- HTML parsing and DOM construction via Blink
- V8 JavaScript engine initialized and running on ARM32 QNX
--dump-domoutput forabout:blank,data:, local HTTP, external HTTP, and HTTPS with clean exit- Local HTTP page loading -- full HTML + JavaScript rendering via
http://127.0.0.1 - External HTTP page loading -- fetching and rendering pages from the internet (e.g.
http://example.com) - HTTPS page loading -- TLS 1.3 via BoringSSL (e.g.
https://example.com) - Complex HTTPS pages --
https://www.google.comandhttps://en.wikipedia.org/wiki/QNXdump full DOM (~200KB+) - ICU internationalization, CSS default stylesheets, full DOM tree
$ ./run.sh https://example.com 2>/dev/null
<html><head><title>Example Domain</title>...
<h1>Example Domain</h1>
<p>This domain is for use in documentation examples...</p>
...</html>
$ ./run.sh 'data:text/html,<h1>Hello from BB10</h1>' 2>/dev/null
<html><head></head><body><h1>Hello from BB10</h1></body></html>
In Progress
- Feature hardening -- tiered re-enablement of HTTP/2, Viz, multi-process, GPU (see
deploy/HARDENING.md) - Ozone platform for QNX Screen -- windowed rendering backend using
screen_create_window()+ Skia software rasterizer - Browser chrome UI -- Skia-rendered toolbar with URL bar, back/forward/reload buttons
- BAR packaging -- native app packaging for BB10 launcher (currently crashes on launch, needs Ozone debugging)
- Root CA certificates -- device has no CA bundle, requires
--ignore-certificate-errorsfor HTTPS - HTTP/2 -- ALPN negotiation causes 400 responses, currently forced to HTTP/1.1
What Doesn't Work (Yet)
- HTTP/2 -- disabled via
--disable-http2(ALPN negotiation issue) - Certificate verification -- no root CA store on device, bypassed with
--ignore-certificate-errors - Multi-process mode -- QNX process model differences
- Service workers, web workers -- disabled
- Windowed mode -- Ozone
qnx_screenplatform needs debugging
Target Hardware
| Device | BlackBerry Passport (SQW100-1) |
| SoC | Qualcomm Snapdragon 801 |
| CPU | Quad-core Krait 400, ARMv7-A |
| RAM | 3 GB |
| OS | BlackBerry 10 (QNX 8.0.0) |
Base Chromium Commit
ad76543128c308a9afdfc1ecf5b3f714886446c1
All patches apply against this commit. Fetch Chromium source using depot_tools and check out this commit before applying.
Repository Structure
├── README.md
├── browser-content-shell-131.zip # pre-built binary package (72 MB)
├── patches/
│ └── qnx-port.patch # unified diff (~21k lines)
├── src/ # 42 new QNX-specific files
│ ├── base/qnx/ # platform abstractions (memory, process, files, threading)
│ ├── build/config/qnx/ # GN platform config (defines, sysroot, flags)
│ ├── build/toolchain/qnx/ # GN toolchain (Clang 17 compile + GCC 9.3 link)
│ ├── content/ # shell platform delegate, stubs, browser chrome
│ ├── gpu/qnx/ # GPU stubs
│ ├── sandbox/qnx/ # sandbox stubs
│ ├── ui/ozone/platform/qnx_screen/ # Ozone platform backend for QNX Screen
│ └── ...
├── deploy/
│ ├── README.md # usage instructions for the binary
│ ├── run.sh # convenience launcher script
│ ├── package.sh # build a deployable tarball
│ └── www/index.html # sample test page
├── qnx_test_captures/ # verified milestone outputs (stdout + stderr)
├── build/
│ ├── args.gn # GN build arguments
│ └── ld-wrapper.sh # linker wrapper (LLD + QNX libs)
├── scripts/
│ ├── apply-patches.sh # apply everything to a Chromium checkout
│ ├── build.sh # build content_shell
│ └── run-on-passport.sh # deploy + run on device
└── docs/
├── STATUS.md # detailed status and issue log
├── QNX_BB10_MILESTONES.md # milestone descriptions and test captures
└── TERM49-DEPLOY.md # quick deploy guide for Term49 userland
Quick Start (Pre-built Binary)
A ready-to-run zip is included in this repo: browser-content-shell-131.zip (~72 MB)
Install on BB10 device
# Copy zip to device (from host)
scp browser-content-shell-131.zip passport:/accounts/1000/shared/misc/
# On device (e.g. via Term49 shell)
cp /accounts/1000/shared/misc/browser-content-shell-131.zip .
unzip -o browser-content-shell-131.zip
chmod +x bin/content-shell bin/content_shell
Run
bin/content-shell https://example.com 2>/dev/null
bin/content-shell http://example.com 2>/dev/null
bin/content-shell 'data:text/html,<h1>Hello</h1>' 2>/dev/null
Output is the rendered DOM on stdout. Use 2>/dev/null to suppress stderr noise.
Add --qnx-trace to enable verbose debug traces (off by default):
bin/content-shell --qnx-trace https://example.com
See docs/TERM49-DEPLOY.md for full Term49 deployment details.
Building from Source
Prerequisites
- Linux host (tested on Ubuntu in Docker)
- Chromium source checked out at commit
ad76543128viadepot_tools - QNX SDP 8.0 (or compatible) with ARM cross-compilation toolchain at
/root/qnx800/ - Clang 17 (
/usr/bin/clang-17,/usr/bin/clang++-17) - LLD 17 (
/usr/bin/ld.lld-17) - patchelf for patching the ELF interpreter
- SSH access to a BlackBerry 10 device with developer mode enabled
Build Instructions
1. Fetch Chromium and check out the base commit
mkdir ~/chromium && cd ~/chromium
fetch --nohooks chromium
cd src
git checkout ad76543128c308a9afdfc1ecf5b3f714886446c1
gclient sync -D
2. Apply the QNX port
cd /path/to/chromium-for-bb10
./scripts/apply-patches.sh ~/chromium/src
This applies patches/qnx-port.patch and copies the 42 new files into place.
3. Configure the build
cd ~/chromium/src
mkdir -p out/qnx-arm
cp /path/to/chromium-for-bb10/build/args.gn out/qnx-arm/args.gn
gn gen out/qnx-arm
4. Set up the linker wrapper
The QNX GCC toolchain's ld must be replaced with the LLD wrapper:
cp /path/to/chromium-for-bb10/build/ld-wrapper.sh \
/root/qnx800/arm-blackberry-qnx8eabi/bin/ld
chmod +x /root/qnx800/arm-blackberry-qnx8eabi/bin/ld
5. Build
ninja -C out/qnx-arm content_shell
6. Deploy to device
# Patch the ELF interpreter for the device
patchelf --set-interpreter /accounts/devuser/berry-deploy/ldqnx.so.2 \
out/qnx-arm/content_shell
# Copy binary + resources to device
scp out/qnx-arm/content_shell out/qnx-arm/content_shell.pak \
out/qnx-arm/icudtl.dat out/qnx-arm/snapshot_blob.bin \
out/qnx-arm/shell_resources.pak out/qnx-arm/ui_resources_100_percent.pak \
passport:/accounts/devuser/berry-deploy/
# Also deploy QNX runtime libraries (ldqnx.so.2, libm.so.2, libgcc_s.so.1, libstdc++.so.6)
7. Package for deployment
# Build a self-contained tarball with the binary, runtime libs, and resources
./deploy/package.sh
# Creates deploy/bb10-content-shell.tar.gz (~60 MB)
8. Deploy and run
# Copy to device
scp -r deploy/bb10-content-shell/ passport:/accounts/devuser/bb10-content-shell/
# Run on device
ssh passport "cd /accounts/devuser/bb10-content-shell && ./run.sh http://example.com" 2>/dev/null
# Or with a data: URL
ssh passport "cd /accounts/devuser/bb10-content-shell && \
./run.sh 'data:text/html,<h1>Hello from BlackBerry</h1>'" 2>/dev/null
# Local HTTP (start python server first: python3.2 -m http.server 8001)
ssh passport "cd /accounts/devuser/bb10-content-shell && \
./run.sh http://127.0.0.1:8001/" 2>/dev/null
See deploy/README.md for detailed usage instructions.
Build Configuration
target_os = "qnx"
target_cpu = "arm"
is_debug = false
is_component_build = false
use_sysroot = false
treat_warnings_as_errors = false
clang_base_path = "/usr/lib/llvm-17"
clang_use_chrome_plugins = false
use_lld = false
enable_nacl = false
use_custom_libcxx = false
is_clang = true
use_glib = false
use_dbus = false
enable_rust = false
use_v8_context_snapshot = false
symbol_level = 1
Toolchain
| Component | Tool | Path |
|---|---|---|
| C compiler | Clang 17 | /usr/bin/clang-17 |
| C++ compiler | Clang 17 | /usr/bin/clang++-17 |
| Linker | LLD 17 (via wrapper) | /usr/bin/ld.lld-17 |
| Archiver | GCC 9.3 | $QNX_HOST/bin/arm-blackberry-qnx8eabi-ar |
| Sysroot | QNX SDP 8.0 | /root/qnx800/arm-blackberry-qnx8eabi/ |
Architecture Notes
The port works around several QNX-specific issues:
-
QNX ARM
std::stringbugs:find_first_of,find_first_not_of, andfind(char, pos)all return incorrect values (typically 0) on QNX ARM. Centralized safe replacements inbase/qnx_string_util.hused across HTTP parsing, MIME type detection, chunked transfer decoding, and Mojo IPC deserialization. -
Mojo IPC
HttpResponseHeadersdeserialization: TheHttpResponseHeaders::BuilderAPI hangs on QNX due to thestd::string::findbug. Bypassed by directly constructing from raw header strings. -
URL path parsing bug: QNX ARM codegen silently drops URL path components in
ParsePath(). Bypassed with direct path assignment inDoParseAfterScheme(). -
DOCTYPE crash: Lowercase
<!doctype html>causes SIGSEGV instrlenduring HTML tokenization on QNX ARM. Fixed by normalizing to uppercase<!DOCTYPEinHTMLDocumentParser::Append(). -
Clipboard hang:
ui::Clipboard::IsSupportedClipboardBuffer()hangs on QNX Ozone. Guarded with#if !BUILDFLAG(IS_QNX). -
TRACE_EVENT hangs: Perfetto tracing macros cause hangs on QNX. All critical paths guarded with
#if !BUILDFLAG(IS_QNX). -
Debug traces: ~350+
write(2,...)debug traces consolidated behindQNX_TRACE_MSG/QNX_TRACE_FMTmacros controlled by--qnx-traceruntime flag (off by default, zero overhead). -
libevent poll() broken:
poll()blocks indefinitely for external TCP sockets on QNX. Disabled poll backend, forcedselect()with 50ms timeout for zero-timeout bug and 200ms cap for idle. -
Socket readiness detection: QNX
select()with zero timeout doesn't detect TCP socket readiness. Thread-pool-offloadedselect()with retry mechanism (6x2s) used as workaround. -
Mojo
EnableBatchDispatch: Disabled on QNX to prevent message delivery delays inThrottlingURLLoader. -
Content encoding: Forced
Accept-Encoding: identityon QNX to avoid gzip/deflate decompression issues. -
Single-process only: Due to QNX process model differences, only
--single-processmode is supported. -
Platform stubs: Missing QNX APIs (sandboxing, GPU, UI platform delegate, etc.) are stubbed out.
See docs/STATUS.md for a detailed issue log.
SSH Configuration
To connect to a BlackBerry Passport via USB:
Host passport
HostName 169.254.0.1
User devuser
HostKeyAlgorithms +ssh-rsa
PubkeyAcceptedAlgorithms +ssh-rsa
License
The Chromium source code is licensed under the BSD license and other applicable licenses. See the Chromium LICENSE for details.