From bd8572420779a916969fc2286e04269616348f71 Mon Sep 17 00:00:00 2001 From: Martin Negyokru Date: Mon, 31 Jul 2023 15:08:09 +0200 Subject: Add QWebEngineSettings::ForceDarkMode Enable forcibly modifying content rendering to result in dark color scheme. [ChangeLog][Settings] ForceDarkMode added, disabled by default. Fixes: QTBUG-84484 Change-Id: I4b3512dc365c61da8f91d8dead0715dadce91f75 Reviewed-by: Allan Sandfeld Jensen --- src/core/api/qwebenginesettings.h | 1 + src/core/doc/src/qwebenginesettings_lgpl.qdoc | 3 + src/core/web_engine_settings.cpp | 3 + src/webenginequick/api/qquickwebenginesettings.cpp | 21 +++++++ src/webenginequick/api/qquickwebenginesettings_p.h | 4 ++ tests/auto/core/qwebenginesettings/CMakeLists.txt | 1 + .../qwebenginesettings/tst_qwebenginesettings.cpp | 65 ++++++++++++++++++++++ tests/auto/quick/publicapi/tst_publicapi.cpp | 2 + tests/auto/quick/qmltests/data/tst_settings.qml | 26 +++++++++ 9 files changed, 126 insertions(+) diff --git a/src/core/api/qwebenginesettings.h b/src/core/api/qwebenginesettings.h index 7919719a1..0d1ac5295 100644 --- a/src/core/api/qwebenginesettings.h +++ b/src/core/api/qwebenginesettings.h @@ -61,6 +61,7 @@ public: PdfViewerEnabled, NavigateOnDropEnabled, ReadingFromCanvasEnabled, + ForceDarkMode, }; enum FontSize { diff --git a/src/core/doc/src/qwebenginesettings_lgpl.qdoc b/src/core/doc/src/qwebenginesettings_lgpl.qdoc index 63e9c9710..a6ec8916e 100644 --- a/src/core/doc/src/qwebenginesettings_lgpl.qdoc +++ b/src/core/doc/src/qwebenginesettings_lgpl.qdoc @@ -172,6 +172,9 @@ This setting will have impact on all HTML5 canvas elements irrespective of origin, and can be disabled to prevent canvas fingerprinting. Enabled by default. (Added in Qt 6.6) + \value ForceDarkMode Specifies that all web contents will be rendered using a dark theme. + For more information, see \l{https://developer.chrome.com/blog/auto-dark-theme/}{Auto dark theme}. + Disabled by default. (Added in Qt 6.7) */ /*! diff --git a/src/core/web_engine_settings.cpp b/src/core/web_engine_settings.cpp index 23ba7a31a..8b916029e 100644 --- a/src/core/web_engine_settings.cpp +++ b/src/core/web_engine_settings.cpp @@ -264,6 +264,8 @@ void WebEngineSettings::initDefaults() bool noReadingFromCanvas = commandLine->HasSwitch(switches::kDisableReadingFromCanvas); s_defaultAttributes.insert(QWebEngineSettings::ReadingFromCanvasEnabled, !noReadingFromCanvas); + bool forceDarkMode = commandLine->HasSwitch(switches::kForceDarkMode); + s_defaultAttributes.insert(QWebEngineSettings::ForceDarkMode, forceDarkMode); } if (s_defaultFontFamilies.isEmpty()) { @@ -360,6 +362,7 @@ void WebEngineSettings::applySettingsToWebPreferences(blink::web_pref::WebPrefer prefs->fullscreen_supported = testAttribute(QWebEngineSettings::FullScreenSupportEnabled); prefs->accelerated_2d_canvas_enabled = testAttribute(QWebEngineSettings::Accelerated2dCanvasEnabled); + prefs->force_dark_mode_enabled = testAttribute(QWebEngineSettings::ForceDarkMode); prefs->webgl1_enabled = prefs->webgl2_enabled = testAttribute(QWebEngineSettings::WebGLEnabled); prefs->should_print_backgrounds = testAttribute(QWebEngineSettings::PrintElementBackgrounds); prefs->allow_running_insecure_content = diff --git a/src/webenginequick/api/qquickwebenginesettings.cpp b/src/webenginequick/api/qquickwebenginesettings.cpp index 2686a5774..b185f0b7a 100644 --- a/src/webenginequick/api/qquickwebenginesettings.cpp +++ b/src/webenginequick/api/qquickwebenginesettings.cpp @@ -448,6 +448,19 @@ bool QQuickWebEngineSettings::readingFromCanvasEnabled() const return d_ptr->testAttribute(QWebEngineSettings::ReadingFromCanvasEnabled); } +/*! + \qmlproperty bool WebEngineSettings::forceDarkMode + \since QtWebEngine 6.7 + + Automatically render all web contents using a dark theme. + + Disabled by default. + */ +bool QQuickWebEngineSettings::forceDarkMode() const +{ + return d_ptr->testAttribute(QWebEngineSettings::ForceDarkMode); +} + /*! \qmlproperty string WebEngineSettings::defaultTextEncoding \since QtWebEngine 1.2 @@ -739,6 +752,14 @@ void QQuickWebEngineSettings::setReadingFromCanvasEnabled(bool on) Q_EMIT readingFromCanvasEnabledChanged(); } +void QQuickWebEngineSettings::setForceDarkMode(bool on) +{ + bool wasOn = d_ptr->testAttribute(QWebEngineSettings::ForceDarkMode); + d_ptr->setAttribute(QWebEngineSettings::ForceDarkMode, on); + if (wasOn != on) + Q_EMIT forceDarkModeChanged(); +} + void QQuickWebEngineSettings::setUnknownUrlSchemePolicy(QQuickWebEngineSettings::UnknownUrlSchemePolicy policy) { QWebEngineSettings::UnknownUrlSchemePolicy oldPolicy = d_ptr->unknownUrlSchemePolicy(); diff --git a/src/webenginequick/api/qquickwebenginesettings_p.h b/src/webenginequick/api/qquickwebenginesettings_p.h index 53156da02..375f6544a 100644 --- a/src/webenginequick/api/qquickwebenginesettings_p.h +++ b/src/webenginequick/api/qquickwebenginesettings_p.h @@ -58,6 +58,7 @@ class Q_WEBENGINEQUICK_PRIVATE_EXPORT QQuickWebEngineSettings : public QObject { Q_PROPERTY(bool pdfViewerEnabled READ pdfViewerEnabled WRITE setPdfViewerEnabled NOTIFY pdfViewerEnabledChanged REVISION(1,8) FINAL) Q_PROPERTY(bool navigateOnDropEnabled READ navigateOnDropEnabled WRITE setNavigateOnDropEnabled NOTIFY navigateOnDropEnabledChanged REVISION(6,4) FINAL) Q_PROPERTY(bool readingFromCanvasEnabled READ readingFromCanvasEnabled WRITE setReadingFromCanvasEnabled NOTIFY readingFromCanvasEnabledChanged REVISION(6,6) FINAL) + Q_PROPERTY(bool forceDarkMode READ forceDarkMode WRITE setForceDarkMode NOTIFY forceDarkModeChanged REVISION(6,7) FINAL) QML_NAMED_ELEMENT(WebEngineSettings) QML_ADDED_IN_VERSION(1, 1) QML_EXTRA_VERSION(2, 0) @@ -106,6 +107,7 @@ public: bool pdfViewerEnabled() const; bool navigateOnDropEnabled() const; bool readingFromCanvasEnabled() const; + bool forceDarkMode() const; void setAutoLoadImages(bool on); void setJavascriptEnabled(bool on); @@ -140,6 +142,7 @@ public: void setPdfViewerEnabled(bool on); void setNavigateOnDropEnabled(bool on); void setReadingFromCanvasEnabled(bool on); + void setForceDarkMode(bool on); signals: void autoLoadImagesChanged(); @@ -175,6 +178,7 @@ signals: Q_REVISION(1,8) void pdfViewerEnabledChanged(); Q_REVISION(6,4) void navigateOnDropEnabledChanged(); Q_REVISION(6,6) void readingFromCanvasEnabledChanged(); + Q_REVISION(6,7) void forceDarkModeChanged(); private: explicit QQuickWebEngineSettings(QQuickWebEngineSettings *parentSettings = nullptr); diff --git a/tests/auto/core/qwebenginesettings/CMakeLists.txt b/tests/auto/core/qwebenginesettings/CMakeLists.txt index 44e8f5252..756b99bbb 100644 --- a/tests/auto/core/qwebenginesettings/CMakeLists.txt +++ b/tests/auto/core/qwebenginesettings/CMakeLists.txt @@ -8,5 +8,6 @@ qt_internal_add_test(tst_qwebenginesettings tst_qwebenginesettings.cpp LIBRARIES Qt::WebEngineCore + Qt::WebEngineWidgets Test::Util ) diff --git a/tests/auto/core/qwebenginesettings/tst_qwebenginesettings.cpp b/tests/auto/core/qwebenginesettings/tst_qwebenginesettings.cpp index 14492c728..e856dd094 100644 --- a/tests/auto/core/qwebenginesettings/tst_qwebenginesettings.cpp +++ b/tests/auto/core/qwebenginesettings/tst_qwebenginesettings.cpp @@ -27,6 +27,7 @@ #include #include +#include class tst_QWebEngineSettings: public QObject { Q_OBJECT @@ -40,6 +41,8 @@ private Q_SLOTS: void setInAcceptNavigationRequest(); void disableReadingFromCanvas_data(); void disableReadingFromCanvas(); + void forceDarkMode(); + void forceDarkModeMultiView(); }; void tst_QWebEngineSettings::resetAttributes() @@ -237,6 +240,68 @@ void tst_QWebEngineSettings::disableReadingFromCanvas() QCOMPARE(evaluateJavaScriptSync(&page, jsCode).toBool(), result); } +void tst_QWebEngineSettings::forceDarkMode() +{ + QWebEnginePage page; + QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); + page.settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, true); + + // based on: https://developer.chrome.com/blog/auto-dark-theme/#detecting-auto-dark-theme + page.setHtml("" + "
" + ""); + + const QString isAutoDark("(() => {" + " const detectionDiv = document.querySelector('#detection');" + " return getComputedStyle(detectionDiv).backgroundColor != 'rgb(255, 255, 255)';" + "})()"); + + QVERIFY(loadFinishedSpy.wait()); + QTRY_COMPARE(evaluateJavaScriptSync(&page, isAutoDark).toBool(), false); + page.settings()->setAttribute(QWebEngineSettings::ForceDarkMode, true); + QTRY_COMPARE(evaluateJavaScriptSync(&page, isAutoDark).toBool(), true); +} + +void tst_QWebEngineSettings::forceDarkModeMultiView() +{ + QWebEngineView view1; + QWebEngineView view2; + QWebEnginePage *page1 = view1.page(); + QWebEnginePage *page2 = view2.page(); + page1->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, true); + page2->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, true); + view1.resize(300,300); + view2.resize(300,300); + view1.show(); + view2.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view1)); + QVERIFY(QTest::qWaitForWindowExposed(&view2)); + + QSignalSpy loadFinishedSpy(page1, SIGNAL(loadFinished(bool))); + QSignalSpy loadFinishedSpy2(page2, SIGNAL(loadFinished(bool))); + QString html("" + "
" + ""); + + const QString isAutoDark("(() => {" + " const detectionDiv = document.querySelector('#detection');" + " return getComputedStyle(detectionDiv).backgroundColor != 'rgb(255, 255, 255)';" + "})()"); + + view1.setHtml(html); + QVERIFY(loadFinishedSpy.wait()); + view2.setHtml(html); + QVERIFY(loadFinishedSpy2.wait()); + + // both views has light color-scheme + QTRY_COMPARE(evaluateJavaScriptSync(page1, isAutoDark).toBool(), false); + QTRY_COMPARE(evaluateJavaScriptSync(page2, isAutoDark).toBool(), false); + view1.settings()->setAttribute(QWebEngineSettings::ForceDarkMode, true); + // dark mode should apply only for view1 + QTRY_COMPARE(evaluateJavaScriptSync(page1, isAutoDark).toBool(), true); + QTRY_COMPARE(evaluateJavaScriptSync(page2, isAutoDark).toBool(), false); +} + QTEST_MAIN(tst_QWebEngineSettings) #include "tst_qwebenginesettings.moc" diff --git a/tests/auto/quick/publicapi/tst_publicapi.cpp b/tests/auto/quick/publicapi/tst_publicapi.cpp index 581a77673..f97ceca69 100644 --- a/tests/auto/quick/publicapi/tst_publicapi.cpp +++ b/tests/auto/quick/publicapi/tst_publicapi.cpp @@ -401,6 +401,8 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineSettings.dnsPrefetchEnabledChanged() --> void" << "QQuickWebEngineSettings.errorPageEnabled --> bool" << "QQuickWebEngineSettings.errorPageEnabledChanged() --> void" + << "QQuickWebEngineSettings.forceDarkMode --> bool" + << "QQuickWebEngineSettings.forceDarkModeChanged() --> void" << "QQuickWebEngineSettings.focusOnNavigationEnabled --> bool" << "QQuickWebEngineSettings.focusOnNavigationEnabledChanged() --> void" << "QQuickWebEngineSettings.fullScreenSupportEnabled --> bool" diff --git a/tests/auto/quick/qmltests/data/tst_settings.qml b/tests/auto/quick/qmltests/data/tst_settings.qml index 6b351a293..f47674aa7 100644 --- a/tests/auto/quick/qmltests/data/tst_settings.qml +++ b/tests/auto/quick/qmltests/data/tst_settings.qml @@ -114,6 +114,32 @@ TestWebEngineView { }); tryVerify(function() { return isDataRead === data.result }); } + + function test_forceDarkMode() { + // based on: https://developer.chrome.com/blog/auto-dark-theme/#detecting-auto-dark-theme + webEngineView.loadHtml("" + + "
" + + ""); + const script = "(() => {" + + " const detectionDiv = document.querySelector('#detection');" + + " return getComputedStyle(detectionDiv).backgroundColor != 'rgb(255, 255, 255)';" + + "})()"; + verify(webEngineView.waitForLoadSucceeded()); + + var isAutoDark = true; + runJavaScript(script, result => isAutoDark = result); + tryVerify(() => {return !isAutoDark}); + + webEngineView.settings.forceDarkMode = true; + verify(webEngineView.settings.forceDarkMode == true) + + isAutoDark = false; + // the page is not updated immediately + tryVerify(function() { + runJavaScript(script, result => isAutoDark = result); + return isAutoDark; + }); + } } } -- cgit v1.2.3