From 483b3f87baeecd45e3566503de34edcb1c8a9c2c Mon Sep 17 00:00:00 2001 From: Asmir A Date: Sat, 17 Oct 2026 11:38:55 +0200 Subject: [PATCH] rename: att -> att_browser --- CLAUDE.md | 15 ++++++++------- CMakeLists.txt | 10 +++++----- README.md | 35 ++++++++++++++++++++--------------- flake.nix | 16 ++++++++-------- package.nix | 4 ++-- src/browserwindow.cpp | 20 ++++++++++++++------ src/browserwindow.h | 1 + src/config.h | 4 ++++ src/greasemonkey.cpp | 2 +- src/hardening.cpp | 2 +- src/main.cpp | 37 +++++++++++++++++++++++++++++++++---- 11 files changed, 97 insertions(+), 49 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 31c393b..720ec89 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## What this is -`att` is a small, keyboard-driven QtWebEngine browser modelled on qutebrowser, in C++20 / Qt 6 (pinned nixpkgs ships Qt 6.11). The README is the user-facing reference for key bindings, modes and commands. When you add a binding, mode or command, update its tables there. +`att_browser` is a small, keyboard-driven QtWebEngine browser modelled on qutebrowser, in C++20 / Qt 6 (pinned nixpkgs ships Qt 6.11). The README is the user-facing reference for key bindings, modes and commands. When you add a binding, mode or command, update its tables there. ## Build and run @@ -12,12 +12,12 @@ Everything goes through the Nix flake: ```sh nix develop -c sh -c 'cmake -B build -G Ninja && cmake --build build' # dev build -nix develop -c ./build/att [url] # run the dev build +nix develop -c ./build/att_browser [url] # run the dev build nix build # package build (→ ./result) nix run . -- example.com ``` -Run `./build/att` from inside `nix develop`. The shell hook exports `QT_PLUGIN_PATH`, `QML2_IMPORT_PATH` and `QTWEBENGINEPROCESS_PATH`, because `wrapQtAppsHook` only wraps *installed* binaries. Run outside the dev shell, the binary can't find Qt's plugins. +Run `./build/att_browser` from inside `nix develop`. The shell hook exports `QT_PLUGIN_PATH`, `QML2_IMPORT_PATH` and `QTWEBENGINEPROCESS_PATH`, because `wrapQtAppsHook` only wraps *installed* binaries. Run outside the dev shell, the binary can't find Qt's plugins. `package.nix` uses a `lib.fileset` source: `CMakeLists.txt`, `src/` and the listed `adblock/` files. Every new source file must also be added to `qt_add_executable` in `CMakeLists.txt`, and `git add`ed: the flake only sees tracked files, so `nix build` fails on untracked sources (`nix build path:.` works around that). The crate's cargo dependencies are vendored from `adblock/Cargo.lock` (`importCargoLock` + `cargoSetupHook`) for the offline sandbox build, so commit `Cargo.lock` changes along with `Cargo.toml` ones. @@ -39,7 +39,7 @@ The only tests are the Rust crate's (`nix develop -c sh -c 'cd adblock && cargo - Open the command line only through `openCommandLine`, which hides the status bar *before* showing the command line. Showing a widget re-runs the layout at once, so the other order shrinks and regrows the page. - **`setMode` is where entering and leaving a mode takes effect.** It updates the status line and focus. Leaving `Hint` also runs the hint-clear script. - **Page interaction mostly goes through `page()->runJavaScript`**, except for input that should feel native: - - Scrolling (`scrollBy`, used by `j/k/h/l`, `Ctrl-d/u/f/b` and `gg/G`) sends a pixel-delta `QWheelEvent` to `m_view->focusProxy()` at the pointer, which is what a touchpad sends. Chromium then scrolls whatever is under the pointer. `window.scrollBy` only moves the document, so it did nothing on sites that scroll an inner element. + - Scrolling (`scrollBy`, used by `j/k/h/l`, `Ctrl-d/u/f/b` and `gg/G`) sends a pixel-delta `QWheelEvent` to `m_view->focusProxy()` at the pointer, which is what a touchpad sends. Chromium then scrolls whatever is under the pointer. Wheel deltas are screen pixels, so the page zoom shrinks them; `j`/`k` go through `scrollByCssPx`, which multiplies by the zoom so `config::kScrollStepPx` means CSS px like qutebrowser's `scroll-px`. `window.scrollBy` only moves the document, so it did nothing on sites that scroll an inner element. - Hint clicks are real mouse events (see below). - **Hint mode** (`src/hints.cpp`): - The state lives in JS (`window.__attHints`), injected into `QWebEngineScript::ApplicationWorld`. That world shares the DOM with the page but keeps its JS globals isolated from the page's scripts. @@ -48,6 +48,7 @@ The only tests are the Rust crate's (`nix develop -c sh -c 'cd adblock && cargo - Following a hint sends a real `QMouseEvent` press/release to `m_view->focusProxy()` (the render widget). The position is in CSS px multiplied by the zoom factor. - **`CommandLine`** keeps the leading `:` in its text; deleting it cancels. It emits `commandEntered(cmd)` without the `:`, and `BrowserWindow::runCommand` parses it as name plus the rest of the line. - **`urlutil::fromUserInput`** turns what the user typed into a URL. In order, it tries an existing local path, a full URL, then something that looks like a host (gets `https://`). Anything else becomes a DuckDuckGo search. +- **Data paths**: the application name is `att_browser` and there is deliberately no organization name, so `AppDataLocation` is `~/.local/share/att_browser` (and `CacheLocation` `~/.cache/att_browser`). `migrateOldDataDirs` in `main.cpp` moves the pre-rename `att/att` directories there once, before anything opens a file. - **The `QWebEngineProfile` is created in `main.cpp`** and passed into `BrowserWindow`. It is a named, on-disk profile with `ForcePersistentCookies`, because Qt 6's default profile is off-the-record. It is parented to the `QApplication` so it outlives the view's page. Qt warns if a profile is destroyed while a page still uses it. - **Adblocking** is split across two parts: - `adblock/` is a Rust staticlib exposing a hand-written C API (`att_adblock.h`, kept in sync with `src/lib.rs` by hand) over Brave's `adblock` crate. Corrosion builds it as part of `cmake --build`, always in release. Its `single-thread` default feature is off, which makes `Engine` `Send + Sync`. @@ -58,14 +59,14 @@ The only tests are the Rust crate's (`nix develop -c sh -c 'cd adblock && cargo - **`:open` completion:** `BrowserWindow::updateCompletion` runs on the command line's `textEdited` and on entering command mode. It parses `:open`/`:o` and shows `History::search` results in `CompletionView`, which never takes focus. `CommandLine::event` intercepts Tab/Backtab, because `QWidget::event` would otherwise turn them into focus changes. Picking an entry uses `setCommandText`, which doesn't emit `textEdited`, so it doesn't re-filter. - **Greasemonkey** (`Greasemonkey`, `src/greasemonkey.cpp`, part of the `Session`) inserts each `greasemonkey/*.js` into `profile->scripts()` as a `QWebEngineScript` named `att-greasemonkey:`, in `MainWorld`. - URL matching is left to QtWebEngine, which parses the `==UserScript==` block during `setSourceCode` and checks `@include/@exclude/@match` in Chromium. So the block must stay in the source. `setSourceCode` also overwrites the script's name (with `@name`) and injection point, so `setName` and `setInjectionPoint` must come *after* it. `install()` finds the old scripts by the `att-greasemonkey:` name prefix to remove them; a lost prefix makes scripts run twice after a reload. - - att's own `parseMetadata` is for run-at, noframes, `@require` and `GM_info`. A `@require` is cached by the SHA-1 of its URL. It must be `https`, and an optional `#sha256=` pin is checked against the cached file on every install (`requireProblem`). + - att_browser's own `parseMetadata` is for run-at, noframes, `@require` and `GM_info`. A `@require` is cached by the SHA-1 of its URL. It must be `https`, and an optional `#sha256=` pin is checked against the cached file on every install (`requireProblem`). - Scripts are wrapped in an IIFE (`kWrapperHead`/`kWrapperTail`) that defines the GM API as locals. - At `document-start`, `document.documentElement` is still null, which the GM helpers must tolerate. - **Hardening** (`src/hardening.cpp`): `applyToProfile` sets the restrictive `QWebEngineSettings` and the third-party cookie filter. The filter runs on the network thread and must stay a pure function. `main.cpp` applies it right after creating the profile, and warns in the first window if `sandboxDisabledReason` finds the sandbox off. `StatusBar::setUrl` takes a `QUrl` so it can mark plain-HTTP pages (`insecure` property). - **User agent** (`src/useragent.cpp`): `useragent::apply` sets the profile's UA string *and* its `QWebEngineClientHints`, so the two don't contradict each other. Chromium sends the low-entropy hints (brands, platform, mobile) even with `setAllClientHintsEnabled(false)`, so every preset sets a platform; the brand ("Google Chrome", "Microsoft Edge") goes in through `setFullVersionList`. Chrome-based presets use the engine's own major version. `:useragent` completion reuses `CompletionView` with name / string columns. -- **Settings** live as constants in `src/config.h` (`config::kZoomDefault`, `config::kZoomLevels`, `kBlockThirdPartyCookies`, `kPdfViewer`, `kUserAgent`), named after qutebrowser's settings, until there's a config file. Zoom is per window: `setZoom` / `zoomStep` in `BrowserWindow`, applied before the first load. QtWebEngine keeps a view's zoom across navigations, so it doesn't need re-applying. +- **Settings** live as constants in `src/config.h` (`config::kZoomDefault`, `config::kZoomLevels`, `kBlockThirdPartyCookies`, `kPdfViewer`, `kUserAgent`, `kScrollStepPx`), named after qutebrowser's settings, until there's a config file. Zoom is per window: `setZoom` / `zoomStep` in `BrowserWindow`, applied before the first load. QtWebEngine keeps a view's zoom across navigations, so it doesn't need re-applying. - The UI stylesheet is the `kStyleSheet` string in `src/main.cpp`. Widgets are styled by `objectName` and by dynamic properties: `StatusBar` toggles `error` and re-polishes the label to pick up the change. -**Windows, not tabs.** att deliberately has no tab bar, because the user's window manager does the tabbing. Map qutebrowser's tab features onto top-level windows: `d` closes the window, `O` / `:open -w` (or `-t`) opens a new one. Each `BrowserWindow` is one page, with `WA_DeleteOnClose`. All windows share a `Session` (profile, `AdBlocker`, `History`) that `main()` owns. After `app.exec()`, `main()` deletes any window still alive before the history and profile go away. `:quit` closes all windows; `:close` closes one. +**Windows, not tabs.** att_browser deliberately has no tab bar, because the user's window manager does the tabbing. Map qutebrowser's tab features onto top-level windows: `d` closes the window, `O` / `:open -w` (or `-t`) opens a new one. Each `BrowserWindow` is one page, with `WA_DeleteOnClose`. All windows share a `Session` (profile, `AdBlocker`, `History`) that `main()` owns. After `app.exec()`, `main()` deletes any window still alive before the history and profile go away. `:quit` closes all windows; `:close` closes one. `newWindowRequested` isn't handled yet, so `target=_blank` and `window.open` go nowhere. Hint mode rewrites link targets to `_self` to work around this. diff --git a/CMakeLists.txt b/CMakeLists.txt index 86b5f69..e9d23b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.21) -project(att +project(att_browser VERSION 0.1.0 DESCRIPTION "Minimal keyboard-driven QtWebEngine browser" LANGUAGES CXX @@ -26,7 +26,7 @@ corrosion_import_crate( PROFILE release ) -qt_add_executable(att +qt_add_executable(att_browser src/adblock.cpp src/adblock.h src/main.cpp @@ -53,9 +53,9 @@ qt_add_executable(att src/useragent.h ) -target_include_directories(att PRIVATE src adblock) +target_include_directories(att_browser PRIVATE src adblock) -target_link_libraries(att PRIVATE +target_link_libraries(att_browser PRIVATE att_adblock Qt6::Core Qt6::Gui @@ -67,6 +67,6 @@ target_link_libraries(att PRIVATE Qt6::WebEngineWidgets ) -install(TARGETS att +install(TARGETS att_browser RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) diff --git a/README.md b/README.md index 7b0d8bf..c0e972f 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# att +# att_browser A small, keyboard-driven QtWebEngine browser in the spirit of qutebrowser. One page per window, a qutebrowser-style status line and `:` command line, `f` link hints and adblocking. There is deliberately no tab bar: where qutebrowser -opens or closes a tab, att opens or closes a window, and your window manager +opens or closes a tab, att_browser opens or closes a window, and your window manager does the tabbing. No config file yet. ## Running @@ -20,7 +20,7 @@ nix run . -- news.ycombinator.com nix develop cmake -B build -G Ninja cmake --build build -./build/att +./build/att_browser ``` The dev shell exports `QT_PLUGIN_PATH`, `QML2_IMPORT_PATH` and @@ -45,7 +45,7 @@ Like qutebrowser, keys mean different things depending on the mode: leaves fullscreen when a page (e.g. a YouTube video) is fullscreen. The status bar is hidden while a page is fullscreen. -> Note: unlike qutebrowser, att does not yet auto-switch to insert mode when you +> Note: unlike qutebrowser, att_browser does not yet auto-switch to insert mode when you > click into a text field on a page — press `i` first. Doing it automatically > needs a JS focus-tracking script over QWebChannel; see *Next steps*. @@ -62,7 +62,8 @@ is hidden while a page is fullscreen. | `gi` | focus the first text field, insert mode | | `f` | hint mode: follow a link by its label | | `Escape` | back to normal mode | -| `h` `j` `k` `l`| scroll | +| `j` `k` | scroll down / up 25 CSS px (`scroll-px 0 ±25`) | +| `h` `l` | scroll left / right | | `Ctrl-d/u` | scroll half a page | | `Ctrl-f/b` | scroll a full page | | `gg` / `G` | jump to top / bottom | @@ -94,17 +95,20 @@ shadow DOM yet. ## Browsing data -Browsing history is kept in `~/.local/share/att/att/history.sqlite` (tables +Browsing history is kept in `~/.local/share/att_browser/history.sqlite` (tables `visits`, one row per page load, and `urls`, one row per URL for completion). A page is recorded once it finishes loading, so redirect hops and in-page `#anchor` jumps don't create entries. `:history-clear` empties it. Cookies, local storage and Chromium's own state are kept in a persistent QtWebEngine profile -under `~/.local/share/att/att/QtWebEngine/default` (the cache is in -`~/.cache/att/att/`). Session cookies are saved too, like qutebrowser's +under `~/.local/share/att_browser/QtWebEngine/default` (the cache is in +`~/.cache/att_browser/`). Session cookies are saved too, like qutebrowser's `content.cookies.store`, so you stay logged in across restarts. Delete that directory to start fresh. +Before the rename to att_browser, this data lived in `~/.local/share/att/att` and +`~/.cache/att/att`. The first start moves it to the new locations. + ## Adblocking Network requests are checked against EasyList and EasyPrivacy with Brave's @@ -114,15 +118,15 @@ no cosmetic filtering (element hiding), so ads served from the site's own URLs still get through. The lists are downloaded on first start and compiled into -`~/.local/share/att/att/adblock-cache.dat`, which later starts just load. +`~/.local/share/att_browser/adblock-cache.dat`, which later starts just load. `:adblock-update` re-downloads them. `:adblock-toggle` switches blocking off and on, for when a site breaks. ## Greasemonkey -Userscripts go in `~/.local/share/att/att/greasemonkey/` as `*.js` files. They +Userscripts go in `~/.local/share/att_browser/greasemonkey/` as `*.js` files. They are loaded at startup; after changing them, run `:greasemonkey-reload` and reload -the page. Like qutebrowser, att: +the page. Like qutebrowser, att_browser: - runs scripts in the page's own JavaScript context (so `unsafeWindow` is just `window`), in iframes too unless `@noframes` is set; @@ -144,7 +148,7 @@ supported. `GM_openInTab` does nothing until new-window requests are handled. ## Security -On top of Chromium's own protections, att: +On top of Chromium's own protections, att_browser: - blocks third-party cookies, which stops most cross-site tracking. A few embedded logins (e.g. some comment widgets) may stop working. @@ -186,13 +190,13 @@ and platform. Chrome and Edge claim the engine's real Chromium major version (as qutebrowser does), so sites don't use features the engine lacks. The Firefox and Safari -versions are pinned in `src/useragent.cpp`. Those presets only change what att +versions are pinned in `src/useragent.cpp`. Those presets only change what att_browser says, not how it behaves: Chromium's low-entropy client hints still name Chromium, and feature detection still sees Chromium. ## Settings -att has no config file yet. The settings live in `src/config.h`, named after +att_browser has no config file yet. The settings live in `src/config.h`, named after their qutebrowser equivalents; change them there and rebuild: | Setting | Default | Meaning | @@ -201,7 +205,8 @@ their qutebrowser equivalents; change them there and rebuild: | `zoom.levels` | 25%, 33%, 50%, … 125%, 150%, … 500% | the steps `+` / `-` move through | | `content.cookies.accept` | no third-party | `kBlockThirdPartyCookies` | | `content.pdfjs` | on | `kPdfViewer`: Chromium's PDF viewer | -| `content.headers.user_agent` | `chrome-linux` | `kUserAgent`: the preset att starts with | +| `j` / `k` step (`scroll-px`) | 25 CSS px | `kScrollStepPx`; grows with the zoom | +| `content.headers.user_agent` | `chrome-linux` | `kUserAgent`: the preset att_browser starts with | ## Commands diff --git a/flake.nix b/flake.nix index d139eb6..4e9a9b6 100644 --- a/flake.nix +++ b/flake.nix @@ -1,5 +1,5 @@ { - description = "att - a minimal, qutebrowser-like QtWebEngine browser"; + description = "att_browser - a minimal, qutebrowser-like QtWebEngine browser"; inputs = { # Pinned to the current NixOS stable release. @@ -14,21 +14,21 @@ in { packages = forAllSystems (pkgs: rec { - att = pkgs.callPackage ./package.nix { }; - default = att; + att_browser = pkgs.callPackage ./package.nix { }; + default = att_browser; }); apps = forAllSystems (pkgs: rec { - att = { + att_browser = { type = "app"; - program = "${self.packages.${pkgs.stdenv.hostPlatform.system}.att}/bin/att"; + program = "${self.packages.${pkgs.stdenv.hostPlatform.system}.att_browser}/bin/att_browser"; }; - default = att; + default = att_browser; }); devShells = forAllSystems (pkgs: { default = pkgs.mkShell { - inputsFrom = [ self.packages.${pkgs.stdenv.hostPlatform.system}.att ]; + inputsFrom = [ self.packages.${pkgs.stdenv.hostPlatform.system}.att_browser ]; packages = with pkgs; [ clang-tools @@ -42,7 +42,7 @@ export QT_PLUGIN_PATH="${qtbase}/${qtbase.qtPluginPrefix}:${qtwebengine}/${qtbase.qtPluginPrefix}" export QML2_IMPORT_PATH="${qtdeclarative}/${qtbase.qtQmlPrefix}:${qtwebengine}/${qtbase.qtQmlPrefix}" export QTWEBENGINEPROCESS_PATH="${qtwebengine}/libexec/QtWebEngineProcess" - echo "att dev shell - cmake -B build -G Ninja && cmake --build build && ./build/att" + echo "att_browser dev shell - cmake -B build -G Ninja && cmake --build build && ./build/att_browser" ''; }; }); diff --git a/package.nix b/package.nix index 8ed2fdc..ecd857e 100644 --- a/package.nix +++ b/package.nix @@ -10,7 +10,7 @@ }: stdenv.mkDerivation (finalAttrs: { - pname = "att"; + pname = "att_browser"; version = "0.1.0"; src = lib.fileset.toSource { @@ -49,7 +49,7 @@ stdenv.mkDerivation (finalAttrs: { meta = { description = "Minimal keyboard-driven QtWebEngine browser"; - mainProgram = "att"; + mainProgram = "att_browser"; platforms = lib.platforms.linux; license = lib.licenses.mit; }; diff --git a/src/browserwindow.cpp b/src/browserwindow.cpp index fa5e7b7..79ab8d1 100644 --- a/src/browserwindow.cpp +++ b/src/browserwindow.cpp @@ -88,7 +88,7 @@ BrowserWindow::BrowserWindow(const Session &session, const QUrl &startUrl, QWidg void BrowserWindow::setupUi() { - setWindowTitle(QStringLiteral("att")); + setWindowTitle(QStringLiteral("att_browser")); resize(1280, 800); auto *central = new QWidget(this); @@ -134,8 +134,8 @@ void BrowserWindow::connectView() setPageFullScreen(request.toggleOn()); }); connect(m_view, &QWebEngineView::titleChanged, this, [this](const QString &title) { - setWindowTitle(title.isEmpty() ? QStringLiteral("att") - : QStringLiteral("%1 - att").arg(title)); + setWindowTitle(title.isEmpty() ? QStringLiteral("att_browser") + : QStringLiteral("%1 - att_browser").arg(title)); m_session.history->updateTitle(m_view->url(), title); }); connect(m_view, &QWebEngineView::loadFinished, this, [this](bool ok) { @@ -344,11 +344,11 @@ bool BrowserWindow::handleNormalModeKey(QKeyEvent *event) return true; } if (text == QLatin1String("j")) { - scrollBy(0, kScrollStep); + scrollByCssPx(0, config::kScrollStepPx); return true; } if (text == QLatin1String("k")) { - scrollBy(0, -kScrollStep); + scrollByCssPx(0, -config::kScrollStepPx); return true; } if (text == QLatin1String("h")) { @@ -500,7 +500,7 @@ void BrowserWindow::runCommand(const QString &command) } else if (name == QLatin1String("version")) { // The Chromium security patch level is what says how far behind // Chrome's fixes this build is; "nix flake update" moves it forward. - m_status->showMessage(QStringLiteral("att %1, Qt %2, Chromium %3, security patches %4") + m_status->showMessage(QStringLiteral("att_browser %1, Qt %2, Chromium %3, security patches %4") .arg(QCoreApplication::applicationVersion(), QString::fromLatin1(qVersion()), QString::fromLatin1(qWebEngineChromiumVersion()), @@ -655,6 +655,14 @@ void BrowserWindow::scrollBy(int dx, int dy) QCoreApplication::sendEvent(target, &event); } +void BrowserWindow::scrollByCssPx(int dx, int dy) +{ + // Wheel deltas are screen pixels, which the page zoom shrinks (at 125% a + // 25px wheel moves 20 CSS px). qutebrowser's scroll-px counts CSS pixels. + const double zoom = m_view->zoomFactor(); + scrollBy(qRound(dx * zoom), qRound(dy * zoom)); +} + void BrowserWindow::scrollPages(double factor) { scrollBy(0, qRound(m_view->height() * factor)); diff --git a/src/browserwindow.h b/src/browserwindow.h index b9943f3..cff3f99 100644 --- a/src/browserwindow.h +++ b/src/browserwindow.h @@ -66,6 +66,7 @@ private: void placeCompletion(); void openUrl(const QString &input, bool newWindow); void scrollBy(int dx, int dy); + void scrollByCssPx(int dx, int dy); void scrollPages(double factor); void scrollToEnd(bool bottom); void focusView(); diff --git a/src/config.h b/src/config.h index a94fbd4..88c9ec0 100644 --- a/src/config.h +++ b/src/config.h @@ -17,6 +17,10 @@ inline constexpr std::array kZoomLevels = { 1.25, 1.50, 1.75, 2.00, 2.50, 3.00, 4.00, 5.00, }; +// What "j" / "k" do: qutebrowser's "scroll-px 0 25" / "scroll-px 0 -25". In +// CSS pixels, so the step grows with the zoom like qutebrowser's does. +inline constexpr int kScrollStepPx = 25; + // content.cookies.accept = no-3rdparty: refuse cookies a page's third-party // requests try to set or send. Blocks most cross-site tracking; a few embedded // logins (e.g. some comment widgets) stop working. diff --git a/src/greasemonkey.cpp b/src/greasemonkey.cpp index 00b0326..5ec004f 100644 --- a/src/greasemonkey.cpp +++ b/src/greasemonkey.cpp @@ -230,7 +230,7 @@ QString wrap(const Greasemonkey::Metadata &meta, const QString &requiredCode, co QJsonObject info{ {QStringLiteral("script"), script}, {QStringLiteral("scriptMetaStr"), meta.block}, - {QStringLiteral("scriptHandler"), QStringLiteral("att")}, + {QStringLiteral("scriptHandler"), QStringLiteral("att_browser")}, {QStringLiteral("version"), QCoreApplication::applicationVersion()}, }; diff --git a/src/hardening.cpp b/src/hardening.cpp index 8eb8702..6945c03 100644 --- a/src/hardening.cpp +++ b/src/hardening.cpp @@ -44,7 +44,7 @@ QString sandboxDisabledReason(int argc, char **argv) return QStringLiteral("QTWEBENGINE_CHROMIUM_FLAGS contains --no-sandbox"); for (int i = 1; i < argc; ++i) { if (QLatin1String(argv[i]) == QLatin1String("--no-sandbox")) - return QStringLiteral("att was started with --no-sandbox"); + return QStringLiteral("att_browser was started with --no-sandbox"); } return {}; } diff --git a/src/main.cpp b/src/main.cpp index 5758643..52dfd17 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,6 +9,9 @@ #include #include +#include +#include +#include #include namespace { @@ -48,18 +51,44 @@ constexpr auto kStyleSheet = R"( } )"; +// Before the rename, the browser was "att" with organization "att", so its +// data was in ~/.local/share/att/att and ~/.cache/att/att. Move those to the +// new locations once, before anything opens a file there, so cookies, history +// and userscripts survive. A new directory that already exists is left alone. +void migrateOldDataDirs() +{ + const auto migrate = [](QStandardPaths::StandardLocation generic, + QStandardPaths::StandardLocation app) { + const QString base = QStandardPaths::writableLocation(generic); + const QString oldPath = base + QStringLiteral("/att/att"); + const QString newPath = QStandardPaths::writableLocation(app); + if (!QFileInfo::exists(oldPath) || QFileInfo::exists(newPath)) + return; + QDir().mkpath(QFileInfo(newPath).path()); + if (QDir().rename(oldPath, newPath)) + QDir(base).rmdir(QStringLiteral("att")); // only if now empty + else + qWarning("att_browser: could not move %s to %s", qPrintable(oldPath), + qPrintable(newPath)); + }; + migrate(QStandardPaths::GenericDataLocation, QStandardPaths::AppDataLocation); + migrate(QStandardPaths::GenericCacheLocation, QStandardPaths::CacheLocation); +} + } // namespace int main(int argc, char *argv[]) { QApplication app(argc, argv); - QCoreApplication::setApplicationName(QStringLiteral("att")); + // No organization name, so the data lives in ~/.local/share/att_browser + // rather than a doubled att_browser/att_browser. + QCoreApplication::setApplicationName(QStringLiteral("att_browser")); QCoreApplication::setApplicationVersion(QStringLiteral("0.1.0")); - QCoreApplication::setOrganizationName(QStringLiteral("att")); + migrateOldDataDirs(); QCommandLineParser parser; parser.setApplicationDescription( - QStringLiteral("att - a minimal, keyboard-driven browser")); + QStringLiteral("att_browser - a minimal, keyboard-driven browser")); parser.addHelpOption(); parser.addVersionOption(); parser.addPositionalArgument(QStringLiteral("url"), QStringLiteral("URL to open.")); @@ -97,7 +126,7 @@ int main(int argc, char *argv[]) firstWindow->show(); const QString sandboxOff = hardening::sandboxDisabledReason(argc, argv); if (!sandboxOff.isEmpty()) { - qWarning("att: Chromium's sandbox is disabled: %s", qPrintable(sandboxOff)); + qWarning("att_browser: Chromium's sandbox is disabled: %s", qPrintable(sandboxOff)); firstWindow->showMessage(QStringLiteral("Sandbox disabled: %1").arg(sandboxOff), true); } // After the window exists, so its status bar shows their messages. The