From 0cc07329495a05fc5f8532c4420bd23db662fdb6 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Thu, 26 Oct 2017 15:11:20 +0200 Subject: Expose JavascriptCanPaste attribute Expose an attribute to enable the execCommand("paste") functionality, which is by default disabled due to security concerns. [ChangeLog][General] Added JavaScriptCanPaste attribute that enables JavaScript 'execCommand("paste")'. Task-number: QTBUG-64056 Change-Id: I1a414021e020473dd15946fff91fb103d871e961 Reviewed-by: Allan Sandfeld Jensen --- src/core/web_engine_settings.cpp | 2 + src/core/web_engine_settings.h | 1 + src/webengine/api/qquickwebenginesettings.cpp | 25 +++++++ src/webengine/api/qquickwebenginesettings_p.h | 4 ++ src/webenginewidgets/api/qwebenginesettings.cpp | 2 + src/webenginewidgets/api/qwebenginesettings.h | 1 + .../doc/src/qwebenginesettings_lgpl.qdoc | 5 ++ .../tst_qquickwebengineview.cpp | 66 +++++++++++++++++++ .../qwebenginesettings/tst_qwebenginesettings.cpp | 77 ++++++++++++++++++++++ tests/auto/widgets/widgets.pro | 2 +- 10 files changed, 184 insertions(+), 1 deletion(-) diff --git a/src/core/web_engine_settings.cpp b/src/core/web_engine_settings.cpp index f438091c6..b2b89c78d 100644 --- a/src/core/web_engine_settings.cpp +++ b/src/core/web_engine_settings.cpp @@ -297,6 +297,7 @@ void WebEngineSettings::initDefaults() playbackRequiresUserGesture = (commandLine->GetSwitchValueASCII(switches::kAutoplayPolicy) != switches::autoplay::kNoUserGestureRequiredPolicy); s_defaultAttributes.insert(PlaybackRequiresUserGesture, playbackRequiresUserGesture); s_defaultAttributes.insert(WebRTCPublicInterfacesOnly, false); + s_defaultAttributes.insert(JavascriptCanPaste, false); } if (s_defaultFontFamilies.isEmpty()) { @@ -388,6 +389,7 @@ void WebEngineSettings::applySettingsToWebPreferences(content::WebPreferences *p ? content::AutoplayPolicy::kUserGestureRequired : content::AutoplayPolicy::kNoUserGestureRequired; } + prefs->dom_paste_enabled = testAttribute(JavascriptCanPaste); // Fonts settings. prefs->standard_font_family_map[content::kCommonScript] = toString16(fontFamily(StandardFont)); diff --git a/src/core/web_engine_settings.h b/src/core/web_engine_settings.h index 06e6ac59c..eba9bf7ea 100644 --- a/src/core/web_engine_settings.h +++ b/src/core/web_engine_settings.h @@ -90,6 +90,7 @@ public: ShowScrollBars, PlaybackRequiresUserGesture, WebRTCPublicInterfacesOnly, + JavascriptCanPaste, }; // Must match the values from the public API in qwebenginesettings.h. diff --git a/src/webengine/api/qquickwebenginesettings.cpp b/src/webengine/api/qquickwebenginesettings.cpp index 71b0deeca..6203f20f1 100644 --- a/src/webengine/api/qquickwebenginesettings.cpp +++ b/src/webengine/api/qquickwebenginesettings.cpp @@ -130,6 +130,9 @@ bool QQuickWebEngineSettings::javascriptCanOpenWindows() const Allows JavaScript programs to read from or write to the clipboard. Writing to the clipboard is always allowed if it is specifically requested by the user. + To enable also the pasting of clipboard content from JavaScript, + use javascriptCanPaste. + Disabled by default. */ bool QQuickWebEngineSettings::javascriptCanAccessClipboard() const @@ -425,6 +428,20 @@ bool QQuickWebEngineSettings::webRTCPublicInterfacesOnly() const return d_ptr->testAttribute(WebEngineSettings::WebRTCPublicInterfacesOnly); } +/*! + \qmlproperty bool WebEngineSettings::javascriptCanPaste + \since QtWebEngine 1.7 + + Enables JavaScript \c{execCommand("paste")}. + This also requires enabling javascriptCanAccessClipboard. + + Disabled by default. +*/ +bool QQuickWebEngineSettings::javascriptCanPaste() const +{ + return d_ptr->testAttribute(WebEngineSettings::JavascriptCanPaste); +} + /*! \qmlproperty string WebEngineSettings::defaultTextEncoding \since QtWebEngine 1.2 @@ -667,6 +684,14 @@ void QQuickWebEngineSettings::setPlaybackRequiresUserGesture(bool on) Q_EMIT playbackRequiresUserGestureChanged(); } +void QQuickWebEngineSettings::setJavascriptCanPaste(bool on) +{ + bool wasOn = d_ptr->testAttribute(WebEngineSettings::JavascriptCanPaste); + d_ptr->setAttribute(WebEngineSettings::JavascriptCanPaste, on); + if (wasOn != on) + Q_EMIT javascriptCanPasteChanged(); +} + void QQuickWebEngineSettings::setUnknownUrlSchemePolicy(QQuickWebEngineSettings::UnknownUrlSchemePolicy policy) { WebEngineSettings::UnknownUrlSchemePolicy oldPolicy = d_ptr->unknownUrlSchemePolicy(); diff --git a/src/webengine/api/qquickwebenginesettings_p.h b/src/webengine/api/qquickwebenginesettings_p.h index 60baa7323..2911ee283 100644 --- a/src/webengine/api/qquickwebenginesettings_p.h +++ b/src/webengine/api/qquickwebenginesettings_p.h @@ -91,6 +91,7 @@ class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineSettings : public QObject { Q_PROPERTY(UnknownUrlSchemePolicy unknownUrlSchemePolicy READ unknownUrlSchemePolicy WRITE setUnknownUrlSchemePolicy NOTIFY unknownUrlSchemePolicyChanged REVISION 6 FINAL) Q_PROPERTY(bool playbackRequiresUserGesture READ playbackRequiresUserGesture WRITE setPlaybackRequiresUserGesture NOTIFY playbackRequiresUserGestureChanged REVISION 6 FINAL) Q_PROPERTY(bool webRTCPublicInterfacesOnly READ webRTCPublicInterfacesOnly WRITE setWebRTCPublicInterfacesOnly NOTIFY webRTCPublicInterfacesOnlyChanged REVISION 6 FINAL) + Q_PROPERTY(bool javascriptCanPaste READ javascriptCanPaste WRITE setJavascriptCanPaste NOTIFY javascriptCanPaste REVISION 6 FINAL) public: enum UnknownUrlSchemePolicy { @@ -131,6 +132,7 @@ public: UnknownUrlSchemePolicy unknownUrlSchemePolicy() const; bool playbackRequiresUserGesture() const; bool webRTCPublicInterfacesOnly() const; + bool javascriptCanPaste() const; void setAutoLoadImages(bool on); void setJavascriptEnabled(bool on); @@ -160,6 +162,7 @@ public: void setUnknownUrlSchemePolicy(UnknownUrlSchemePolicy policy); void setPlaybackRequiresUserGesture(bool on); void setWebRTCPublicInterfacesOnly(bool on); + void setJavascriptCanPaste(bool on); signals: void autoLoadImagesChanged(); @@ -190,6 +193,7 @@ signals: Q_REVISION(6) void unknownUrlSchemePolicyChanged(); Q_REVISION(6) void playbackRequiresUserGestureChanged(); Q_REVISION(6) void webRTCPublicInterfacesOnlyChanged(); + Q_REVISION(6) void javascriptCanPasteChanged(); private: explicit QQuickWebEngineSettings(QQuickWebEngineSettings *parentSettings = 0); diff --git a/src/webenginewidgets/api/qwebenginesettings.cpp b/src/webenginewidgets/api/qwebenginesettings.cpp index b829c5799..32f9b75cd 100644 --- a/src/webenginewidgets/api/qwebenginesettings.cpp +++ b/src/webenginewidgets/api/qwebenginesettings.cpp @@ -105,6 +105,8 @@ static WebEngineSettings::Attribute toWebEngineAttribute(QWebEngineSettings::Web return WebEngineSettings::PlaybackRequiresUserGesture; case QWebEngineSettings::WebRTCPublicInterfacesOnly: return WebEngineSettings::WebRTCPublicInterfacesOnly; + case QWebEngineSettings::JavascriptCanPaste: + return WebEngineSettings::JavascriptCanPaste; default: return WebEngineSettings::UnsupportedInCoreSettings; diff --git a/src/webenginewidgets/api/qwebenginesettings.h b/src/webenginewidgets/api/qwebenginesettings.h index 81b6c2937..1815396b6 100644 --- a/src/webenginewidgets/api/qwebenginesettings.h +++ b/src/webenginewidgets/api/qwebenginesettings.h @@ -94,6 +94,7 @@ public: ShowScrollBars, PlaybackRequiresUserGesture, WebRTCPublicInterfacesOnly, + JavascriptCanPaste, }; enum FontSize { diff --git a/src/webenginewidgets/doc/src/qwebenginesettings_lgpl.qdoc b/src/webenginewidgets/doc/src/qwebenginesettings_lgpl.qdoc index cde26f66b..a8ee59bf4 100644 --- a/src/webenginewidgets/doc/src/qwebenginesettings_lgpl.qdoc +++ b/src/webenginewidgets/doc/src/qwebenginesettings_lgpl.qdoc @@ -94,6 +94,8 @@ \value JavascriptCanAccessClipboard Allows JavaScript programs to read from and write to the clipboard. Writing to the clipboard is always allowed if it is specifically requested by the user. + See JavascriptCanPaste to also allow pasting the content of the clipboard content from + JavaScript. Disabled by default. \value LinksIncludedInFocusChain Includes hyperlinks in the keyboard focus chain. Enabled by default. @@ -173,6 +175,9 @@ Inhibits playback of media content until the user interacts with the page. This is similar to how Chrome on Android behaves, while the default behavior when it is disabled is similar to Chrome on desktops. + \value JavascriptCanPaste + Enables JavaScript \c{execCommand("paste")}. This also requires + enabling JavascriptCanAccessClipboard. Disabled by default. (Added in Qt 5.11) \value WebRTCPublicInterfacesOnly Limits WebRTC to public IP addresses only. When disabled WebRTC may also use diff --git a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp index a64197b1c..f8f742ae1 100644 --- a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp +++ b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp @@ -31,6 +31,8 @@ #include #include +#include +#include #include #include #include @@ -84,6 +86,8 @@ private Q_SLOTS: void changeLocale(); void userScripts(); + void javascriptClipboard_data(); + void javascriptClipboard(); private: inline QQuickWebEngineView *newWebEngineView(); @@ -860,5 +864,67 @@ void tst_QQuickWebEngineView::userScripts() list.clear(); } +void tst_QQuickWebEngineView::javascriptClipboard_data() +{ + QTest::addColumn("javascriptCanAccessClipboard"); + QTest::addColumn("javascriptCanPaste"); + QTest::addColumn("copyResult"); + QTest::addColumn("pasteResult"); + + QTest::newRow("default") << false << false << false << false; + QTest::newRow("canCopy") << true << false << true << false; + // paste command requires both permissions + QTest::newRow("canPaste") << false << true << false << false; + QTest::newRow("canCopyAndPaste") << true << true << true << true; +} + +void tst_QQuickWebEngineView::javascriptClipboard() +{ + QFETCH(bool, javascriptCanAccessClipboard); + QFETCH(bool, javascriptCanPaste); + QFETCH(bool, copyResult); + QFETCH(bool, pasteResult); + + // check defaults + QCOMPARE(webEngineView()->settings()->javascriptCanAccessClipboard(), false); + QCOMPARE(webEngineView()->settings()->javascriptCanPaste(), false); + + // check accessors + webEngineView()->settings()->setJavascriptCanAccessClipboard(javascriptCanAccessClipboard); + webEngineView()->settings()->setJavascriptCanPaste(javascriptCanPaste); + QCOMPARE(webEngineView()->settings()->javascriptCanAccessClipboard(), + javascriptCanAccessClipboard); + QCOMPARE(webEngineView()->settings()->javascriptCanPaste(), javascriptCanPaste); + + QQuickWebEngineView *view = webEngineView(); + view->loadHtml("" + "" + ""); + QVERIFY(waitForLoadSucceeded(view)); + + // make sure that 'OriginalText' is selected + evaluateJavaScriptSync(view, "document.getElementById('myInput').select()"); + QCOMPARE(evaluateJavaScriptSync(view, "window.getSelection().toString()").toString(), + "OriginalText"); + + // Check that the actual settings work by the + // - return value of queryCommandEnabled and + // - return value of execCommand + // - comparing the clipboard / input field + QGuiApplication::clipboard()->clear(); + QCOMPARE(evaluateJavaScriptSync(view, "document.queryCommandEnabled('copy')").toBool(), + copyResult); + QCOMPARE(evaluateJavaScriptSync(view, "document.execCommand('copy')").toBool(), copyResult); + QCOMPARE(QGuiApplication::clipboard()->text(), + (copyResult ? QString("OriginalText") : QString())); + + QGuiApplication::clipboard()->setText("AnotherText"); + QCOMPARE(evaluateJavaScriptSync(view, "document.queryCommandEnabled('paste')").toBool(), + pasteResult); + QCOMPARE(evaluateJavaScriptSync(view, "document.execCommand('paste')").toBool(), pasteResult); + QCOMPARE(evaluateJavaScriptSync(view, "document.getElementById('myInput').value").toString(), + (pasteResult ? QString("AnotherText") : QString("OriginalText"))); +} + QTEST_MAIN(tst_QQuickWebEngineView) #include "tst_qquickwebengineview.moc" diff --git a/tests/auto/widgets/qwebenginesettings/tst_qwebenginesettings.cpp b/tests/auto/widgets/qwebenginesettings/tst_qwebenginesettings.cpp index 5cbcf4ec0..9c008cb90 100644 --- a/tests/auto/widgets/qwebenginesettings/tst_qwebenginesettings.cpp +++ b/tests/auto/widgets/qwebenginesettings/tst_qwebenginesettings.cpp @@ -17,11 +17,17 @@ Boston, MA 02110-1301, USA. */ +#include "../util.h" + #include +#include #include #include +#include +#include + class tst_QWebEngineSettings: public QObject { Q_OBJECT @@ -29,6 +35,8 @@ private Q_SLOTS: void resetAttributes(); void defaultFontFamily_data(); void defaultFontFamily(); + void javascriptClipboard_data(); + void javascriptClipboard(); }; void tst_QWebEngineSettings::resetAttributes() @@ -85,6 +93,75 @@ void tst_QWebEngineSettings::defaultFontFamily() QVERIFY(!settings->fontFamily(static_cast(fontFamily)).isEmpty()); } +void tst_QWebEngineSettings::javascriptClipboard_data() +{ + QTest::addColumn("javascriptCanAccessClipboard"); + QTest::addColumn("javascriptCanPaste"); + QTest::addColumn("copyResult"); + QTest::addColumn("pasteResult"); + + QTest::newRow("default") << false << false << false << false; + QTest::newRow("canCopy") << true << false << true << false; + // paste command requires both permissions + QTest::newRow("canPaste") << false << true << false << false; + QTest::newRow("canCopyAndPaste") << true << true << true << true; +} + +void tst_QWebEngineSettings::javascriptClipboard() +{ + QFETCH(bool, javascriptCanAccessClipboard); + QFETCH(bool, javascriptCanPaste); + QFETCH(bool, copyResult); + QFETCH(bool, pasteResult); + + QWebEnginePage page; + + // check defaults + QCOMPARE(page.settings()->testAttribute(QWebEngineSettings::JavascriptCanAccessClipboard), + false); + QCOMPARE(page.settings()->testAttribute(QWebEngineSettings::JavascriptCanPaste), false); + + // check accessors + page.settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, + javascriptCanAccessClipboard); + page.settings()->setAttribute(QWebEngineSettings::JavascriptCanPaste, + javascriptCanPaste); + QCOMPARE(page.settings()->testAttribute(QWebEngineSettings::JavascriptCanAccessClipboard), + javascriptCanAccessClipboard); + QCOMPARE(page.settings()->testAttribute(QWebEngineSettings::JavascriptCanPaste), + javascriptCanPaste); + + QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); + page.setHtml("" + "" + ""); + QVERIFY(loadFinishedSpy.wait()); + + // make sure that 'OriginalText' is selected + evaluateJavaScriptSync(&page, "document.getElementById('myInput').select()"); + QCOMPARE(evaluateJavaScriptSync(&page, "window.getSelection().toString()").toString(), + "OriginalText"); + + // Check that the actual settings work by the + // - return value of queryCommandEnabled and + // - return value of execCommand + // - comparing the clipboard / input field + QGuiApplication::clipboard()->clear(); + QCOMPARE(evaluateJavaScriptSync(&page, "document.queryCommandEnabled('copy')").toBool(), + copyResult); + QCOMPARE(evaluateJavaScriptSync(&page, "document.execCommand('copy')").toBool(), copyResult); + QCOMPARE(QApplication::clipboard()->text(), + (copyResult ? QString("OriginalText") : QString())); + + + QGuiApplication::clipboard()->setText("AnotherText"); + QCOMPARE(evaluateJavaScriptSync(&page, "document.queryCommandEnabled('paste')").toBool(), + pasteResult); + QCOMPARE(evaluateJavaScriptSync(&page, "document.execCommand('paste')").toBool(), pasteResult); + QCOMPARE(evaluateJavaScriptSync(&page, "document.getElementById('myInput').value").toString(), + (pasteResult ? QString("AnotherText") : QString("OriginalText"))); +} + QTEST_MAIN(tst_QWebEngineSettings) #include "tst_qwebenginesettings.moc" diff --git a/tests/auto/widgets/widgets.pro b/tests/auto/widgets/widgets.pro index ba1b254f4..36dfaba9f 100644 --- a/tests/auto/widgets/widgets.pro +++ b/tests/auto/widgets/widgets.pro @@ -32,5 +32,5 @@ qtConfig(webengine-spellchecker):!cross_compile { boot2qt: SUBDIRS -= qwebengineaccessibility qwebenginedefaultsurfaceformat \ qwebenginefaviconmanager qwebenginepage qwebenginehistory \ qwebengineprofile qwebengineschemes qwebenginescript \ - qwebengineview qwebenginedownloads + qwebengineview qwebenginedownloads qwebenginesettings -- cgit v1.2.3