summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Varga <pvarga@inf.u-szeged.hu>2019-05-15 10:33:07 +0200
committerPeter Varga <pvarga@inf.u-szeged.hu>2019-05-27 10:49:59 +0200
commit2c86c348d7a94f27d1e9e7a71c2435565cbe6fb5 (patch)
tree574dd43d6739aa975f8a40e150ff32946fca5899
parente02bcb0855ebee0612cab0f3cd3f9fd494497336 (diff)
Disable edit actions when content has no focused frame
Task-number: QTBUG-75505 Change-Id: Ia1329ff554a86e307aa7995e9af1665ea6c5e64c Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
-rw-r--r--src/core/devtools_frontend_qt.cpp3
-rw-r--r--src/core/web_contents_adapter.cpp6
-rw-r--r--src/core/web_contents_adapter.h1
-rw-r--r--src/core/web_contents_adapter_client.h1
-rw-r--r--src/core/web_contents_delegate_qt.cpp59
-rw-r--r--src/core/web_contents_delegate_qt.h21
-rw-r--r--src/webengine/api/qquickwebengineaction.cpp3
-rw-r--r--src/webengine/api/qquickwebengineview.cpp22
-rw-r--r--src/webengine/api/qquickwebengineview_p_p.h1
-rw-r--r--src/webengine/doc/src/webengineview_lgpl.qdoc4
-rw-r--r--src/webenginewidgets/api/qwebenginepage.cpp22
-rw-r--r--src/webenginewidgets/api/qwebenginepage_p.h1
-rw-r--r--src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc8
-rw-r--r--tests/auto/quick/qmltests/data/tst_action.qml54
-rw-r--r--tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp95
15 files changed, 292 insertions, 9 deletions
diff --git a/src/core/devtools_frontend_qt.cpp b/src/core/devtools_frontend_qt.cpp
index bd9e0ebe7..fc91fd75f 100644
--- a/src/core/devtools_frontend_qt.cpp
+++ b/src/core/devtools_frontend_qt.cpp
@@ -78,7 +78,6 @@
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_fetcher_response_writer.h"
-#include <QDebug>
using namespace QtWebEngineCore;
namespace {
@@ -180,7 +179,7 @@ DevToolsFrontendQt *DevToolsFrontendQt::Show(QSharedPointer<WebContentsAdapter>
content::WebContents *contents = frontendAdapter->webContents();
if (contents == inspectedContents) {
- qWarning() << "You can not inspect youself";
+ LOG(WARNING) << "You can not inspect yourself";
return nullptr;
}
diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp
index 3e5003960..567a76637 100644
--- a/src/core/web_contents_adapter.cpp
+++ b/src/core/web_contents_adapter.cpp
@@ -1602,6 +1602,12 @@ bool WebContentsAdapter::isFindTextInProgress() const
return m_lastFindRequestId != m_webContentsDelegate->lastReceivedFindReply();
}
+bool WebContentsAdapter::hasFocusedFrame() const
+{
+ CHECK_INITIALIZED(false);
+ return m_webContents->GetFocusedFrame() != nullptr;
+}
+
WebContentsAdapterClient::RenderProcessTerminationStatus
WebContentsAdapterClient::renderProcessExitStatus(int terminationStatus) {
auto status = WebContentsAdapterClient::RenderProcessTerminationStatus(-1);
diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h
index e8e5359be..9f9d0ed03 100644
--- a/src/core/web_contents_adapter.h
+++ b/src/core/web_contents_adapter.h
@@ -217,6 +217,7 @@ public:
bool canViewSource();
void focusIfNecessary();
bool isFindTextInProgress() const;
+ bool hasFocusedFrame() const;
// meant to be used within WebEngineCore only
void initialize(content::SiteInstance *site);
diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h
index 75d010865..129e97c5b 100644
--- a/src/core/web_contents_adapter_client.h
+++ b/src/core/web_contents_adapter_client.h
@@ -466,6 +466,7 @@ public:
virtual void updateScrollPosition(const QPointF &position) = 0;
virtual void updateContentsSize(const QSizeF &size) = 0;
virtual void updateNavigationActions() = 0;
+ virtual void updateEditActions() = 0;
virtual void startDragging(const content::DropData &dropData, Qt::DropActions allowedActions,
const QPixmap &pixmap, const QPoint &offset) = 0;
virtual bool supportsDragging() const = 0;
diff --git a/src/core/web_contents_delegate_qt.cpp b/src/core/web_contents_delegate_qt.cpp
index 5cb94de5c..95a49f40a 100644
--- a/src/core/web_contents_delegate_qt.cpp
+++ b/src/core/web_contents_delegate_qt.cpp
@@ -63,6 +63,7 @@
#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
#include "components/web_cache/browser/web_cache_manager.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/invalidate_type.h"
@@ -102,6 +103,7 @@ WebContentsDelegateQt::WebContentsDelegateQt(content::WebContents *webContents,
, m_lastReceivedFindReply(0)
, m_faviconManager(new FaviconManager(webContents, adapterClient))
, m_lastLoadProgress(-1)
+ , m_frameFocusedObserver(adapterClient)
{
webContents->SetDelegate(this);
Observe(webContents);
@@ -253,11 +255,30 @@ void WebContentsDelegateQt::HandleKeyboardEvent(content::WebContents *, const co
m_viewClient->unhandledKeyEvent(reinterpret_cast<QKeyEvent *>(event.os_event));
}
+void WebContentsDelegateQt::RenderFrameCreated(content::RenderFrameHost *render_frame_host)
+{
+ content::FrameTreeNode *node = static_cast<content::RenderFrameHostImpl *>(render_frame_host)->frame_tree_node();
+ m_frameFocusedObserver.addNode(node);
+}
+
void WebContentsDelegateQt::RenderFrameDeleted(content::RenderFrameHost *render_frame_host)
{
m_loadingErrorFrameList.removeOne(render_frame_host->GetRoutingID());
}
+void WebContentsDelegateQt::RenderFrameHostChanged(content::RenderFrameHost *old_host, content::RenderFrameHost *new_host)
+{
+ if (old_host) {
+ content::FrameTreeNode *old_node = static_cast<content::RenderFrameHostImpl *>(old_host)->frame_tree_node();
+ m_frameFocusedObserver.removeNode(old_node);
+ }
+
+ if (new_host) {
+ content::FrameTreeNode *new_node = static_cast<content::RenderFrameHostImpl *>(new_host)->frame_tree_node();
+ m_frameFocusedObserver.addNode(new_node);
+ }
+}
+
void WebContentsDelegateQt::RenderViewHostChanged(content::RenderViewHost *, content::RenderViewHost *newHost)
{
if (newHost && newHost->GetWidget() && newHost->GetWidget()->GetView()) {
@@ -704,4 +725,42 @@ WebContentsAdapter *WebContentsDelegateQt::webContentsAdapter() const
return m_viewClient->webContentsAdapter();
}
+
+FrameFocusedObserver::FrameFocusedObserver(WebContentsAdapterClient *adapterClient)
+ : m_viewClient(adapterClient)
+{}
+
+void FrameFocusedObserver::addNode(content::FrameTreeNode *node)
+{
+ if (m_observedNodes.contains(node))
+ return;
+
+ node->AddObserver(this);
+ m_observedNodes.append(node);
+}
+
+void FrameFocusedObserver::removeNode(content::FrameTreeNode *node)
+{
+ node->RemoveObserver(this);
+ m_observedNodes.removeOne(node);
+}
+
+void FrameFocusedObserver::OnFrameTreeNodeFocused(content::FrameTreeNode *node)
+{
+ Q_UNUSED(node);
+ m_viewClient->updateEditActions();
+}
+
+void FrameFocusedObserver::OnFrameTreeNodeDestroyed(content::FrameTreeNode *node)
+{
+ m_observedNodes.removeOne(node);
+ m_viewClient->updateEditActions();
+}
+
+FrameFocusedObserver::~FrameFocusedObserver()
+{
+ for (content::FrameTreeNode *node : m_observedNodes)
+ node->RemoveObserver(this);
+}
+
} // namespace QtWebEngineCore
diff --git a/src/core/web_contents_delegate_qt.h b/src/core/web_contents_delegate_qt.h
index 2baa1176f..27aac2071 100644
--- a/src/core/web_contents_delegate_qt.h
+++ b/src/core/web_contents_delegate_qt.h
@@ -40,6 +40,7 @@
#ifndef WEB_CONTENTS_DELEGATE_QT_H
#define WEB_CONTENTS_DELEGATE_QT_H
+#include "content/browser/frame_host/frame_tree_node.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_observer.h"
#include "third_party/skia/include/core/SkColor.h"
@@ -70,6 +71,23 @@ class WebContentsAdapter;
class WebContentsAdapterClient;
class WebEngineSettings;
+class FrameFocusedObserver : public content::FrameTreeNode::Observer
+{
+public:
+ FrameFocusedObserver(WebContentsAdapterClient *adapterClient);
+ ~FrameFocusedObserver();
+ void addNode(content::FrameTreeNode *node);
+ void removeNode(content::FrameTreeNode *node);
+
+protected:
+ void OnFrameTreeNodeFocused(content::FrameTreeNode *node) override;
+ void OnFrameTreeNodeDestroyed(content::FrameTreeNode *node) override;
+
+private:
+ WebContentsAdapterClient *m_viewClient;
+ QVector<content::FrameTreeNode *> m_observedNodes;
+};
+
class SavePageInfo
{
public:
@@ -130,7 +148,9 @@ public:
bool TakeFocus(content::WebContents *source, bool reverse) override;
// WebContentsObserver overrides
+ void RenderFrameCreated(content::RenderFrameHost *render_frame_host) override;
void RenderFrameDeleted(content::RenderFrameHost *render_frame_host) override;
+ void RenderFrameHostChanged(content::RenderFrameHost *old_host, content::RenderFrameHost *new_host) override;
void RenderViewHostChanged(content::RenderViewHost *old_host, content::RenderViewHost *new_host) override;
void DidStartNavigation(content::NavigationHandle *navigation_handle) override;
void DidFinishNavigation(content::NavigationHandle *navigation_handle) override;
@@ -172,6 +192,7 @@ private:
QSharedPointer<FilePickerController> m_filePickerController;
QUrl m_initialTargetUrl;
int m_lastLoadProgress;
+ FrameFocusedObserver m_frameFocusedObserver;
QUrl m_url;
QString m_title;
diff --git a/src/webengine/api/qquickwebengineaction.cpp b/src/webengine/api/qquickwebengineaction.cpp
index 69a05f29b..77ac8d340 100644
--- a/src/webengine/api/qquickwebengineaction.cpp
+++ b/src/webengine/api/qquickwebengineaction.cpp
@@ -146,8 +146,7 @@ QString QQuickWebEngineAction::iconName() const
/*!
\qmlproperty bool WebEngineAction::enabled
- This property holds whether the action is enabled. Context-dependent
- actions are always enabled.
+ This property holds whether the action is enabled.
*/
bool QQuickWebEngineAction::isEnabled() const
{
diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp
index fd3cc8e82..c8ba64f49 100644
--- a/src/webengine/api/qquickwebengineview.cpp
+++ b/src/webengine/api/qquickwebengineview.cpp
@@ -931,6 +931,16 @@ void QQuickWebEngineViewPrivate::updateAction(QQuickWebEngineView::WebAction act
case QQuickWebEngineView::ViewSource:
enabled = adapter->canViewSource();
break;
+ case QQuickWebEngineView::Cut:
+ case QQuickWebEngineView::Copy:
+ case QQuickWebEngineView::Paste:
+ case QQuickWebEngineView::Undo:
+ case QQuickWebEngineView::Redo:
+ case QQuickWebEngineView::SelectAll:
+ case QQuickWebEngineView::PasteAndMatchStyle:
+ case QQuickWebEngineView::Unselect:
+ enabled = adapter->hasFocusedFrame();
+ break;
default:
break;
}
@@ -948,6 +958,18 @@ void QQuickWebEngineViewPrivate::updateNavigationActions()
updateAction(QQuickWebEngineView::ViewSource);
}
+void QQuickWebEngineViewPrivate::updateEditActions()
+{
+ updateAction(QQuickWebEngineView::Cut);
+ updateAction(QQuickWebEngineView::Copy);
+ updateAction(QQuickWebEngineView::Paste);
+ updateAction(QQuickWebEngineView::Undo);
+ updateAction(QQuickWebEngineView::Redo);
+ updateAction(QQuickWebEngineView::SelectAll);
+ updateAction(QQuickWebEngineView::PasteAndMatchStyle);
+ updateAction(QQuickWebEngineView::Unselect);
+}
+
QUrl QQuickWebEngineView::url() const
{
Q_D(const QQuickWebEngineView);
diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h
index 3c4189fd9..3b6300d5f 100644
--- a/src/webengine/api/qquickwebengineview_p_p.h
+++ b/src/webengine/api/qquickwebengineview_p_p.h
@@ -147,6 +147,7 @@ public:
void updateScrollPosition(const QPointF &position) override;
void updateContentsSize(const QSizeF &size) override;
void updateNavigationActions() override;
+ void updateEditActions() override;
void startDragging(const content::DropData &dropData, Qt::DropActions allowedActions,
const QPixmap &pixmap, const QPoint &offset) override;
bool supportsDragging() const override;
diff --git a/src/webengine/doc/src/webengineview_lgpl.qdoc b/src/webengine/doc/src/webengineview_lgpl.qdoc
index b294e1c4c..6759c1a36 100644
--- a/src/webengine/doc/src/webengineview_lgpl.qdoc
+++ b/src/webengine/doc/src/webengineview_lgpl.qdoc
@@ -756,6 +756,10 @@
Redo the last editing action.
\value WebEngineView.SelectAll
Select all content.
+ This action is only enabled when the page's content is focused.
+ The focus can be forced by the JavaScript \c{window.focus()} call, or the
+ \l{WebEngineSettings::focusOnNavigationEnabled} {focusOnNavigationEnabled} setting
+ should be enabled to get automatic focus.
\value WebEngineView.PasteAndMatchStyle
Paste content from the clipboard with current style.
\value WebEngineView.OpenLinkInThisWindow
diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp
index c33034015..78b89a53c 100644
--- a/src/webenginewidgets/api/qwebenginepage.cpp
+++ b/src/webenginewidgets/api/qwebenginepage.cpp
@@ -650,6 +650,16 @@ void QWebEnginePagePrivate::updateAction(QWebEnginePage::WebAction action) const
case QWebEnginePage::ViewSource:
enabled = adapter->canViewSource();
break;
+ case QWebEnginePage::Cut:
+ case QWebEnginePage::Copy:
+ case QWebEnginePage::Paste:
+ case QWebEnginePage::Undo:
+ case QWebEnginePage::Redo:
+ case QWebEnginePage::SelectAll:
+ case QWebEnginePage::PasteAndMatchStyle:
+ case QWebEnginePage::Unselect:
+ enabled = adapter->hasFocusedFrame();
+ break;
default:
break;
}
@@ -668,6 +678,18 @@ void QWebEnginePagePrivate::updateNavigationActions()
updateAction(QWebEnginePage::ViewSource);
}
+void QWebEnginePagePrivate::updateEditActions()
+{
+ updateAction(QWebEnginePage::Cut);
+ updateAction(QWebEnginePage::Copy);
+ updateAction(QWebEnginePage::Paste);
+ updateAction(QWebEnginePage::Undo);
+ updateAction(QWebEnginePage::Redo);
+ updateAction(QWebEnginePage::SelectAll);
+ updateAction(QWebEnginePage::PasteAndMatchStyle);
+ updateAction(QWebEnginePage::Unselect);
+}
+
#ifndef QT_NO_ACTION
void QWebEnginePagePrivate::_q_webActionTriggered(bool checked)
{
diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h
index 25ed89371..f8f67d341 100644
--- a/src/webenginewidgets/api/qwebenginepage_p.h
+++ b/src/webenginewidgets/api/qwebenginepage_p.h
@@ -144,6 +144,7 @@ public:
void updateScrollPosition(const QPointF &position) override;
void updateContentsSize(const QSizeF &size) override;
void updateNavigationActions() override;
+ void updateEditActions() override;
void startDragging(const content::DropData &dropData, Qt::DropActions allowedActions,
const QPixmap &pixmap, const QPoint &offset) override;
bool supportsDragging() const override;
diff --git a/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc b/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc
index 75b6c4f54..15e57e904 100644
--- a/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc
+++ b/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc
@@ -112,8 +112,7 @@
Actions only have an effect when they are applicable.
The availability of actions can be be determined by checking
- \l{QAction::}{isEnabled()} on the action returned by action(). Context-dependent
- actions are always enabled.
+ \l{QAction::}{isEnabled()} on the action returned by action().
\value NoWebAction No action is triggered.
\value Back Navigate back in the history of navigated links.
@@ -126,7 +125,10 @@
\value Paste Paste content from the clipboard.
\value Undo Undo the last editing action.
\value Redo Redo the last editing action.
- \value SelectAll Select all content.
+ \value SelectAll Select all content. This action is only enabled when the page's content is focused.
+ The focus can be forced by the JavaScript \c{window.focus()} call, or the
+ \l{QWebEngineSettings::FocusOnNavigationEnabled} {FocusOnNavigationEnabled} setting
+ should be enabled to get automatic focus.
\value PasteAndMatchStyle Paste content from the clipboard with current style.
\value OpenLinkInThisWindow Open the current link in the current window. (Added in Qt 5.6)
diff --git a/tests/auto/quick/qmltests/data/tst_action.qml b/tests/auto/quick/qmltests/data/tst_action.qml
index f6d8669fe..56a91d8d0 100644
--- a/tests/auto/quick/qmltests/data/tst_action.qml
+++ b/tests/auto/quick/qmltests/data/tst_action.qml
@@ -127,5 +127,59 @@ TestWebEngineView {
stopAction.trigger();
compare(stopTriggerSpy.count, 0);
}
+
+ function test_editActionsWithExplicitFocus() {
+ var webView = Qt.createQmlObject("TestWebEngineView { visible: false; }", webEngineView);
+ webView.settings.focusOnNavigationEnabled = false;
+
+ // The view is hidden and no focus on the page. Edit actions should be disabled.
+ var selectAllAction = webView.action(WebEngineView.SelectAll);
+ verify(selectAllAction);
+ verify(!selectAllAction.enabled);
+
+ var triggerSpy = createTemporaryObject(signalSpy, webEngineView, {target: selectAllAction, signalName: "triggered"});
+ var enabledSpy = createTemporaryObject(signalSpy, webEngineView, {target: selectAllAction, signalName: "enabledChanged"});
+
+ webView.loadHtml("<html><body><div>foo bar</div></body></html>");
+ verify(webView.waitForLoadSucceeded());
+
+ // Still no focus because focus on navigation is disabled. Edit actions don't do anything (should not crash).
+ verify(!selectAllAction.enabled);
+ compare(enabledSpy.count, 0);
+ selectAllAction.trigger();
+ compare(triggerSpy.count, 0);
+ compare(getTextSelection(), "");
+
+ // Focus content by focusing window from JavaScript. Edit actions should be enabled and functional.
+ webView.runJavaScript("window.focus();");
+ tryVerify(function() { return enabledSpy.count === 1 });
+ verify(selectAllAction.enabled);
+ selectAllAction.trigger();
+ compare(triggerSpy.count, 1);
+ tryVerify(function() { return webView.getTextSelection() === "foo bar" });
+ }
+
+ function test_editActionsWithInitialFocus() {
+ var webView = Qt.createQmlObject("TestWebEngineView { visible: false; }", webEngineView);
+ webView.settings.focusOnNavigationEnabled = true;
+
+ // The view is hidden and no focus on the page. Edit actions should be disabled.
+ var selectAllAction = webView.action(WebEngineView.SelectAll);
+ verify(selectAllAction);
+ verify(!selectAllAction.enabled);
+
+ var triggerSpy = createTemporaryObject(signalSpy, webEngineView, {target: selectAllAction, signalName: "triggered"});
+ var enabledSpy = createTemporaryObject(signalSpy, webEngineView, {target: selectAllAction, signalName: "enabledChanged"});
+
+ webView.loadHtml("<html><body><div>foo bar</div></body></html>");
+ verify(webView.waitForLoadSucceeded());
+
+ // Content gets initial focus.
+ tryVerify(function() { return enabledSpy.count === 1 });
+ verify(selectAllAction.enabled);
+ selectAllAction.trigger();
+ compare(triggerSpy.count, 1);
+ tryVerify(function() { return webView.getTextSelection() === "foo bar" });
+ }
}
}
diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
index 098bae9de..d73c8e80a 100644
--- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
+++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
@@ -227,6 +227,10 @@ private Q_SLOTS:
void triggerActionWithoutMenu();
void dynamicFrame();
+ void editActionsWithExplicitFocus();
+ void editActionsWithInitialFocus();
+ void editActionsWithFocusOnIframe();
+
private:
static QPoint elementCenter(QWebEnginePage *page, const QString &id);
@@ -1137,8 +1141,8 @@ void tst_QWebEnginePage::textSelection()
QCOMPARE(page->action(QWebEnginePage::SelectEndOfDocument)->isEnabled(), false);
#endif
- // ..but SelectAll is awalys enabled
- QCOMPARE(page->action(QWebEnginePage::SelectAll)->isEnabled(), true);
+ // ..but SelectAll is disabled because the page has no focus due to disabled FocusOnNavigationEnabled.
+ QCOMPARE(page->action(QWebEnginePage::SelectAll)->isEnabled(), false);
// Verify hasSelection returns false since there is no selection yet...
QCOMPARE(page->hasSelection(), false);
@@ -4503,6 +4507,93 @@ void tst_QWebEnginePage::dynamicFrame()
QCOMPARE(toPlainTextSync(&page).trimmed(), QStringLiteral("foo"));
}
+void tst_QWebEnginePage::editActionsWithExplicitFocus()
+{
+ QWebEngineView view;
+ QWebEnginePage *page = view.page();
+ view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false);
+
+ QSignalSpy loadFinishedSpy(page, &QWebEnginePage::loadFinished);
+ QSignalSpy selectionChangedSpy(page, &QWebEnginePage::selectionChanged);
+ QSignalSpy actionChangedSpy(page->action(QWebEnginePage::SelectAll), &QAction::changed);
+
+ // The view is hidden and no focus on the page. Edit actions should be disabled.
+ QVERIFY(!view.isVisible());
+ QVERIFY(!page->action(QWebEnginePage::SelectAll)->isEnabled());
+
+ page->setHtml(QString("<html><body><div>foo bar</div></body></html>"));
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
+
+ // Still no focus because focus on navigation is disabled. Edit actions don't do anything (should not crash).
+ QVERIFY(!page->action(QWebEnginePage::SelectAll)->isEnabled());
+ view.page()->triggerAction(QWebEnginePage::SelectAll);
+ QCOMPARE(selectionChangedSpy.count(), 0);
+ QCOMPARE(page->hasSelection(), false);
+
+ // Focus content by focusing window from JavaScript. Edit actions should be enabled and functional.
+ evaluateJavaScriptSync(page, "window.focus();");
+ QTRY_COMPARE(actionChangedSpy.count(), 1);
+ QVERIFY(page->action(QWebEnginePage::SelectAll)->isEnabled());
+ view.page()->triggerAction(QWebEnginePage::SelectAll);
+ QTRY_COMPARE(selectionChangedSpy.count(), 1);
+ QCOMPARE(page->hasSelection(), true);
+ QCOMPARE(page->selectedText(), QStringLiteral("foo bar"));
+}
+
+void tst_QWebEnginePage::editActionsWithInitialFocus()
+{
+ QWebEngineView view;
+ QWebEnginePage *page = view.page();
+ view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true);
+
+ QSignalSpy loadFinishedSpy(page, &QWebEnginePage::loadFinished);
+ QSignalSpy selectionChangedSpy(page, &QWebEnginePage::selectionChanged);
+ QSignalSpy actionChangedSpy(page->action(QWebEnginePage::SelectAll), &QAction::changed);
+
+ // The view is hidden and no focus on the page. Edit actions should be disabled.
+ QVERIFY(!view.isVisible());
+ QVERIFY(!page->action(QWebEnginePage::SelectAll)->isEnabled());
+
+ page->setHtml(QString("<html><body><div>foo bar</div></body></html>"));
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
+
+ // Content gets initial focus.
+ QTRY_COMPARE(actionChangedSpy.count(), 1);
+ QVERIFY(page->action(QWebEnginePage::SelectAll)->isEnabled());
+ view.page()->triggerAction(QWebEnginePage::SelectAll);
+ QTRY_COMPARE(selectionChangedSpy.count(), 1);
+ QCOMPARE(page->hasSelection(), true);
+ QCOMPARE(page->selectedText(), QStringLiteral("foo bar"));
+}
+
+void tst_QWebEnginePage::editActionsWithFocusOnIframe()
+{
+ QWebEngineView view;
+ QWebEnginePage *page = view.page();
+ view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false);
+
+ QSignalSpy loadFinishedSpy(page, &QWebEnginePage::loadFinished);
+ QSignalSpy selectionChangedSpy(page, &QWebEnginePage::selectionChanged);
+ QSignalSpy actionChangedSpy(page->action(QWebEnginePage::SelectAll), &QAction::changed);
+
+ // The view is hidden and no focus on the page. Edit actions should be disabled.
+ QVERIFY(!view.isVisible());
+ QVERIFY(!page->action(QWebEnginePage::SelectAll)->isEnabled());
+
+ page->load(QUrl("qrc:///resources/iframe2.html"));
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
+ QVERIFY(!page->action(QWebEnginePage::SelectAll)->isEnabled());
+
+ // Focusing an iframe.
+ evaluateJavaScriptSync(page, "document.getElementsByTagName('iframe')[0].contentWindow.focus()");
+ QTRY_COMPARE(actionChangedSpy.count(), 1);
+ QVERIFY(page->action(QWebEnginePage::SelectAll)->isEnabled());
+ view.page()->triggerAction(QWebEnginePage::SelectAll);
+ QTRY_COMPARE(selectionChangedSpy.count(), 1);
+ QCOMPARE(page->hasSelection(), true);
+ QCOMPARE(page->selectedText(), QStringLiteral("inner"));
+}
+
static QByteArrayList params = {QByteArrayLiteral("--use-fake-device-for-media-stream")};
W_QTEST_MAIN(tst_QWebEnginePage, params)