rename: att -> att_browser
This commit is contained in:
@@ -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:<file>`, 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.
|
||||
|
||||
+5
-5
@@ -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}
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
'';
|
||||
};
|
||||
});
|
||||
|
||||
+2
-2
@@ -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;
|
||||
};
|
||||
|
||||
+14
-6
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()},
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -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 {};
|
||||
}
|
||||
|
||||
+33
-4
@@ -9,6 +9,9 @@
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCommandLineParser>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QStandardPaths>
|
||||
#include <QWebEngineProfile>
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user