fix(browser): clear browsing data via BrowserProfileUtils

Desktop clearBrowsingData was stuck because QML WebEngineProfile has no
cookie API and the old clearCache path threw before the fallback timer.
Add BrowserProfileUtils (QtWebEngine-only) to clear HTTP cache + cookies,
and align the QML API with the UI (clearBrowsingData / clearSiteData).
This commit is contained in:
Andrey Bocharnikov
2026-07-17 17:00:51 +04:00
parent b720f21fe9
commit d38dfef5cd
16 changed files with 191 additions and 44 deletions
+4
View File
@@ -408,6 +408,10 @@ target_link_libraries(StatusQ PRIVATE
if(NOT (IOS OR ANDROID))
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS WebEngineQuick QUIET)
if(TARGET Qt::WebEngineQuick)
target_sources(StatusQ PRIVATE
include/StatusQ/browserprofileutils.h
src/browserprofileutils.cpp
)
target_link_libraries(StatusQ PRIVATE Qt::WebEngineQuick)
target_compile_definitions(StatusQ PRIVATE STATUSQ_HAS_QTWEBENGINE=1)
endif()
@@ -0,0 +1,22 @@
#pragma once
#include <QObject>
// Desktop-only helper exposing WebEngineProfile clearing that the QML
// WebEngineProfile type does not expose (cookie store). Registered only when
// StatusQ is built with Qt WebEngine (STATUSQ_HAS_QTWEBENGINE).
class BrowserProfileUtils : public QObject
{
Q_OBJECT
public:
explicit BrowserProfileUtils(QObject *parent = nullptr);
// Clears profile-wide browsing data: HTTP cache and all cookies.
// `profile` must be a QML WebEngineProfile (QQuickWebEngineProfile);
// no-op with a warning otherwise. The profile's clearHttpCacheCompleted
// signal still fires, so QML can drive completion/reload handling.
// Note: global DOM storage / visited links are not clearable via Qt's
// public API and are therefore left untouched.
Q_INVOKABLE void clearBrowsingData(QObject *profile);
};
+26
View File
@@ -0,0 +1,26 @@
#include "StatusQ/browserprofileutils.h"
#include <QtWebEngineQuick/qquickwebengineprofile.h>
#include <QtWebEngineCore/QWebEngineCookieStore>
#include <QLoggingCategory>
BrowserProfileUtils::BrowserProfileUtils(QObject *parent)
: QObject(parent)
{
}
void BrowserProfileUtils::clearBrowsingData(QObject *profile)
{
auto *webProfile = qobject_cast<QQuickWebEngineProfile*>(profile);
if (!webProfile) {
qWarning("BrowserProfileUtils::clearBrowsingData: expected a WebEngineProfile");
return;
}
if (auto *cookieStore = webProfile->cookieStore())
cookieStore->deleteAllCookies();
// Async; the profile emits clearHttpCacheCompleted when done.
webProfile->clearHttpCache();
}
+11
View File
@@ -34,6 +34,10 @@
#include "MobileWebView/mobilewebviewbackend.h"
#endif
#if defined(STATUSQ_HAS_QTWEBENGINE)
#include "StatusQ/browserprofileutils.h"
#endif
// Forward declare platform-specific registration functions
// These are implemented in the respective platform files
#if defined(Q_OS_IOS) || defined(Q_OS_ANDROID) || defined(Q_OS_MACOS)
@@ -161,6 +165,13 @@ void registerStatusQTypes() {
#if defined(STATUSQ_HAS_MOBILEWEBVIEW)
qmlRegisterType<MobileWebViewBackend>("StatusQ.CustomWebView", 1, 0, "MobileWebViewBackend");
#endif
#if defined(STATUSQ_HAS_QTWEBENGINE)
qmlRegisterSingletonType<BrowserProfileUtils>(
"StatusQ.Internal", 0, 1, "BrowserProfileUtils", [](QQmlEngine*, QJSEngine*) {
return new BrowserProfileUtils;
});
#endif
// Register NativeSwipeHandler + NativeIndicator (native on iOS/Android/macOS)
#if defined(Q_OS_IOS) || defined(Q_OS_ANDROID) || defined(Q_OS_MACOS)
registerNativeSwipeHandlerItemType();
+3 -3
View File
@@ -689,11 +689,11 @@ StatusSectionLayout {
incognitoMode: _internal.currentTabIncognito
zoomFactor: _internal.currentWebView?.zoomFactor ?? 1
browserSettings: localAccountSensitiveSettings
clearingCache: _internal.currentWebView?.clearing ?? false
clearingBrowsingData: _internal.currentWebView?.clearing ?? false
clearSiteDataSupported: _internal.currentWebView?.clearSiteDataSupported ?? true
onForceReload: webViewContext.forceReloadCurrent()
onClearSiteData: webViewContext.clearSiteDataCurrent()
onClearCache: webViewContext.clearCacheCurrent()
onClearBrowsingData: webViewContext.clearBrowsingDataCurrent()
onAddNewTab: _internal.addNewEmptyTab()
onAddNewDownloadTab: _internal.addNewDownloadTab()
onGoIncognito: (checked) => root.applyIncognitoMode(checked)
@@ -736,7 +736,7 @@ StatusSectionLayout {
clearing: _internal.currentWebView?.clearing ?? false
onForceReload: webViewContext.forceReloadCurrent()
onClearSiteData: webViewContext.clearSiteDataCurrent()
onClearCache: webViewContext.clearCacheCurrent()
onClearBrowsingData: webViewContext.clearBrowsingDataCurrent()
onGoIncognito: checked => root.applyIncognitoMode(checked)
onSettingsRequested: Global.changeAppSectionBySectionType(Constants.appSection.profile, Constants.settingsSubsection.browserSettings)
@@ -122,7 +122,7 @@ Item {
function clearSiteData() { console.warn("AbstractWebView: clearSiteData not implemented") }
// Clear browsing data (profile-wide cache, cookies, DOM storage), then reload.
function clearCache() { console.warn("AbstractWebView: clearCache not implemented") }
function clearBrowsingData() { console.warn("AbstractWebView: clearBrowsingData not implemented") }
function findText(text, flags) {}
function showFindPanel() {}
@@ -101,10 +101,10 @@ AbstractWebView {
if (loader.item)
loader.item.clearSiteData()
}
function clearCache() {
function clearBrowsingData() {
ensureLoaded()
if (loader.item)
loader.item.clearCache()
loader.item.clearBrowsingData()
}
function findText(text, flags){ if (loader.item) loader.item.findText(text, flags) }
function showFindPanel() { if (loader.item) loader.item.showFindPanel() }
@@ -117,7 +117,7 @@ AbstractWebView {
// Profile-wide "clear browsing data": HTTP cache + cookies + DOM storage.
// Reloads the current view once clearProfileDataCompleted fires.
function clearCache() {
function clearBrowsingData() {
backend.clearProfileData()
}
@@ -3,6 +3,7 @@ import QtQml
import QtWebEngine
import StatusQ.Core.Theme
import StatusQ.Internal
import AppLayouts.Browser.views
@@ -51,20 +52,23 @@ AbstractWebView {
function clearSiteData() {
webView.runJavaScript("window.StatusSiteUtils && window.StatusSiteUtils.clearSiteDataAndReload()")
}
// Profile-wide "clear browsing data": HTTP cache, cookies and visited links.
function clearCache() {
// Profile-wide "clear browsing data": HTTP cache + cookies, via the native
// BrowserProfileUtils (the QML WebEngineProfile exposes no cookie API).
// Global DOM storage isn't clearable via Qt's public API, so it's left as-is
// (unlike mobile's clearProfileData). The profile's clearHttpCacheCompleted
// drives _finishClearBrowsingData(); start the fallback timer first so `clearing`
// can never stick if the call fails.
function clearBrowsingData() {
if (root.clearing || !root.profile)
return
root.clearing = true
root.profile.cookieStore.deleteAllCookies()
root.profile.clearAllVisitedLinks()
root.profile.clearHttpCache() // async; completion drives _finishClearCache()
_clearCacheFallbackTimer.restart()
_clearBrowsingDataFallbackTimer.restart()
BrowserProfileUtils.clearBrowsingData(root.profile)
}
function _finishClearCache() {
function _finishClearBrowsingData() {
if (!root.clearing)
return
_clearCacheFallbackTimer.stop()
_clearBrowsingDataFallbackTimer.stop()
root.clearing = false
webView.reload()
}
@@ -232,15 +236,15 @@ AbstractWebView {
return
root.downloadRequested(download)
}
function onClearHttpCacheCompleted() { root._finishClearCache() }
function onClearHttpCacheCompleted() { root._finishClearBrowsingData() }
}
// Fallback in case clearHttpCacheCompleted doesn't fire (e.g. empty cache).
Timer {
id: _clearCacheFallbackTimer
id: _clearBrowsingDataFallbackTimer
interval: 1000
repeat: false
onTriggered: root._finishClearCache()
onTriggered: root._finishClearBrowsingData()
}
WebEngineView {
@@ -27,9 +27,9 @@ StatusMenu {
signal launchBrowserSettings()
signal forceReload()
signal clearSiteData()
signal clearCache()
signal clearBrowsingData()
property bool clearingCache: false
property bool clearingBrowsingData: false
property bool clearSiteDataSupported: true
background: Rectangle {
@@ -158,16 +158,16 @@ StatusMenu {
}
StatusMenuItem {
text: root.clearingCache ? qsTr("Clearing cache...") : qsTr("Clear cache")
text: root.clearingBrowsingData ? qsTr("Clearing browsing data...") : qsTr("Clear browsing data")
icon.name: "broom"
icon.color: Theme.palette.primaryColor1
enabled: !root.clearingCache
enabled: !root.clearingBrowsingData
visibleOnDisabled: true
onTriggered: clearCache()
onTriggered: clearBrowsingData()
StatusToolTip {
visible: parent.hovered
text: qsTr("Clears cached files, cookies, and history for the entire browser. Browsing is paused until it is done.")
text: qsTr("Clears the cache and cookies for the entire browser. Browsing is paused until it is done.")
}
}
@@ -30,7 +30,7 @@ StatusDialog {
signal resetZoomFactor
signal forceReload
signal clearSiteData
signal clearCache
signal clearBrowsingData
signal settingsRequested
title: qsTr("Browser")
@@ -123,11 +123,11 @@ StatusDialog {
}
}
SettingsListItem {
title: root.clearing ? qsTr("Clearing cache...") : qsTr("Clear cache")
title: root.clearing ? qsTr("Clearing browsing data...") : qsTr("Clear browsing data")
asset.name: "broom"
enabled: !root.clearing
onClicked: {
root.clearCache()
root.clearBrowsingData()
root.close()
}
}
@@ -206,10 +206,10 @@ QtObject {
currentWebView.clearSiteData()
}
function clearCacheCurrent() {
function clearBrowsingDataCurrent() {
if (!currentWebView)
return
currentWebView.clearCache()
currentWebView.clearBrowsingData()
}
function findTextCurrent(text, backward = false) {
+23 -3
View File
@@ -2582,6 +2582,10 @@ Do you wish to override the security check and continue?</source>
<source>Developer Tools</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Force reload</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear site data</source>
<translation type="unfinished"></translation>
@@ -2591,15 +2595,15 @@ Do you wish to override the security check and continue?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clearing cache...</source>
<source>Clearing browsing data...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear cache</source>
<source>Clear browsing data</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clears cached files, cookies, and history for the entire browser. Browsing is paused until it is done.</source>
<source>Clears the cache and cookies for the entire browser. Browsing is paused until it is done.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -11315,6 +11319,22 @@ to load</source>
<source>Zoom Fit</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Force reload</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear site data</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clearing browsing data...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear browsing data</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Settings</source>
<translation type="unfinished"></translation>
+26 -6
View File
@@ -2591,6 +2591,10 @@ Přejete si obejít bezpečnostní kontrolu a pokračovat?</translation>
<source>Developer Tools</source>
<translation>Vývojářské nástroje</translation>
</message>
<message>
<source>Force reload</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear site data</source>
<translation>Vyčistit data stránky</translation>
@@ -2600,16 +2604,16 @@ Přejete si obejít bezpečnostní kontrolu a pokračovat?</translation>
<translation>Použijte k obnovení aktuální stránky, pokud se nenačítá nebo nefunfuje správně.</translation>
</message>
<message>
<source>Clearing cache...</source>
<translation>Čistí se cache...</translation>
<source>Clearing browsing data...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear cache</source>
<translation>Vyčistit cache</translation>
<source>Clear browsing data</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clears cached files, cookies, and history for the entire browser. Browsing is paused until it is done.</source>
<translation>Vyčistí soubory, cookies a historii celého prohlížeče. Prohlížení je pozastaveno, dokud není hotovo.</translation>
<source>Clears the cache and cookies for the entire browser. Browsing is paused until it is done.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Settings</source>
@@ -11382,6 +11386,22 @@ selhalo</translation>
<source>Zoom Fit</source>
<translation type="unfinished">Přizpůsobit velikost</translation>
</message>
<message>
<source>Force reload</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear site data</source>
<translation type="unfinished">Vyčistit data stránky</translation>
</message>
<message>
<source>Clearing browsing data...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear browsing data</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Settings</source>
<translation type="unfinished">Nastavení</translation>
+23 -3
View File
@@ -2588,6 +2588,10 @@ Do you wish to override the security check and continue?</source>
<source>Developer Tools</source>
<translation>Herramientas de desarrollo</translation>
</message>
<message>
<source>Force reload</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear site data</source>
<translation type="unfinished"></translation>
@@ -2597,15 +2601,15 @@ Do you wish to override the security check and continue?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clearing cache...</source>
<source>Clearing browsing data...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear cache</source>
<source>Clear browsing data</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clears cached files, cookies, and history for the entire browser. Browsing is paused until it is done.</source>
<source>Clears the cache and cookies for the entire browser. Browsing is paused until it is done.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -11328,6 +11332,22 @@ al cargar</translation>
<source>Zoom Fit</source>
<translation type="unfinished">Ajustar zoom</translation>
</message>
<message>
<source>Force reload</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear site data</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clearing browsing data...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear browsing data</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Settings</source>
<translation type="unfinished">Ajustes</translation>
+23 -3
View File
@@ -2572,6 +2572,10 @@ Do you wish to override the security check and continue?</source>
<source>Developer Tools</source>
<translation> </translation>
</message>
<message>
<source>Force reload</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear site data</source>
<translation type="unfinished"></translation>
@@ -2581,15 +2585,15 @@ Do you wish to override the security check and continue?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clearing cache...</source>
<source>Clearing browsing data...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear cache</source>
<source>Clear browsing data</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clears cached files, cookies, and history for the entire browser. Browsing is paused until it is done.</source>
<source>Clears the cache and cookies for the entire browser. Browsing is paused until it is done.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -11275,6 +11279,22 @@ to load</source>
<source>Zoom Fit</source>
<translation type="unfinished"> </translation>
</message>
<message>
<source>Force reload</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear site data</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clearing browsing data...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear browsing data</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Settings</source>
<translation type="unfinished"></translation>