- C++ 85.1%
- C 7.3%
- Shell 6.1%
- HTML 1.5%
Adds releases/BerryBrowserV3-3.0.2-build84.bar (installable app), documents what works in the current app vs the original headless port, and points users at the berry-v3 branch for full source. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|---|---|---|
| build | ||
| deploy | ||
| docs | ||
| patches | ||
| qnx_test_captures | ||
| releases | ||
| 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 / BlackBerry 10 — now shipping as
Berry Browser, a full windowed browser app for BB10 phones. This brings a
modern (~2024-era) Chromium engine to a platform that only ever shipped with an
ancient WebKit fork.
What started as a headless --dump-dom experiment (still documented below) is
today an installable .bar with GPU rendering, a Skia-drawn toolbar, HTTPS
with a bundled root store, working YouTube playback, WhatsApp Web, ad/tracker
blocking, and an Ubuntu-styled home screen.
Download — Berry Browser (V3)
Latest: v3.0.2 build 84 — releases/BerryBrowserV3-3.0.2-build84.bar (~60 MB)
Install like any BB10 sideload:
- Enable Development Mode on the phone (Settings > Security and Privacy).
- Sideload the
.barwith Sachesi or DDPB from a computer — or copy it to the device and install with an on-device file manager that supports.bar. - Launch Berry Browser from the home screen.
New in build 84: the app auto-detects the panel size at launch, so touch is correctly calibrated on every BB10 model — not just the Passport. Earlier builds needed Settings > Device on non-Passport phones.
What works in Berry Browser
- Windowed GPU rendering (EGL + Skia), 45 fps default cap, resolution tiers
- HTTPS everywhere via bundled root CA store (BoringSSL, TLS 1.3, QUIC)
- YouTube: watch pages + search through a lightweight built-in player (progressive MP4 — no SABR/kevlar player)
- WhatsApp Web, Facebook Messenger, DuckDuckGo / Bing / Google search
- Ad & tracker blocking at the network layer
- Pin sites to the BB10 home screen, BB10 Share integration,
mailto:/tel:/sms:handoff to native apps - In-app Settings (resolution, frame rate, dark mode, device profile) and persistent disk cache
Device compatibility
| Device | Panel | Status |
|---|---|---|
| Passport | 1440×1440 | Primary target — fully tested |
| Classic (Q20) / Q10 / Q5 | 720×720 | Auto-detected since build 84 (fixes touch calibration) |
| Z10 | 768×1280 | Auto-detected; less tested |
| Z30 / Z3 / Leap | 720×1280 | Auto-detected; less tested |
Requires BB10 10.3.x. Manual device selection in Settings still overrides
auto-detect; berry-device-rotation marker corrects orientation if needed.
Branches
| Branch | Contents |
|---|---|
berry-v3 (active) |
Full Chromium source of the current Berry Browser app — all engine work, shims, launcher, packaging |
qnx-bb10-v3 |
Kept in sync with berry-v3 (same commits) |
qnx-bb10 |
Earlier V2 line (pre-Berry-Browser save point) |
main |
This snapshot: docs, patches, and prebuilt binaries against the base commit |
Recent berry-v3 milestones: YouTube watch/search shims (builds 43–49),
stability/video/login fixes (50–72), Ubuntu-themed chrome + BB10 share +
Android-first YouTube playback (82), modern start page (83), device panel
auto-detect (84).
Target Hardware
| Primary 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. The berry-v3 branch already contains everything applied.
Original headless port (historical)
The sections below document the original bring-up: a headless content_shell
that could --dump-dom over SSH. Still useful for understanding the port's
foundations and for building from source.
What Worked (headless era)
- 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- Complex HTTPS pages --
https://www.google.comandhttps://en.wikipedia.org/wiki/QNXdump full DOM (~200KB+)
$ ./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>
Everything listed as "in progress" back then — windowed Ozone rendering, the
Skia toolbar, .bar packaging, root CAs, GPU — has since landed on berry-v3.
Repository Structure
├── README.md
├── releases/
│ └── BerryBrowserV3-3.0.2-build84.bar # installable Berry Browser app (~60 MB)
├── browser-content-shell-131.zip # pre-built headless 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.