summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clang-format105
-rw-r--r--dist/changes-5.15.091
-rw-r--r--examples/pdf/pdfviewer/viewer.qml4
m---------src/3rdparty0
-rw-r--r--src/buildtools/config/linux.pri6
-rw-r--r--src/buildtools/configure.json21
-rw-r--r--src/core/accessibility_activation_observer.cpp4
-rw-r--r--src/core/accessibility_activation_observer.h5
-rw-r--r--src/core/accessibility_tree_formatter_qt.cpp6
-rw-r--r--src/core/api/qtwebenginecoreglobal.cpp12
-rw-r--r--src/core/browser_accessibility_manager_qt.cpp12
-rw-r--r--src/core/browser_accessibility_manager_qt.h7
-rw-r--r--src/core/browser_accessibility_qt.cpp4
-rw-r--r--src/core/browser_accessibility_qt.h4
-rw-r--r--src/core/compositor/delegated_frame_node.cpp46
-rw-r--r--src/core/content_browser_client_qt.cpp2
-rw-r--r--src/core/core_module.pro11
-rw-r--r--src/core/net/cookie_monster_delegate_qt.cpp18
-rw-r--r--src/core/net/cookie_monster_delegate_qt.h3
-rw-r--r--src/core/net/system_network_context_manager.cpp12
-rw-r--r--src/core/net/system_network_context_manager.h4
-rw-r--r--src/core/ozone/gl_context_qt.cpp6
-rw-r--r--src/core/ozone/gl_ozone_egl_qt.cpp6
-rw-r--r--src/core/permission_manager_qt.cpp7
-rw-r--r--src/core/render_widget_host_view_qt.cpp30
-rw-r--r--src/core/render_widget_host_view_qt.h3
-rw-r--r--src/core/render_widget_host_view_qt_delegate.h4
-rw-r--r--src/core/web_contents_adapter.cpp30
-rw-r--r--src/core/web_contents_adapter.h4
-rw-r--r--src/core/web_contents_adapter_client.h5
-rw-r--r--src/core/web_contents_delegate_qt.cpp24
-rw-r--r--src/core/web_contents_delegate_qt.h6
-rw-r--r--src/core/web_engine_context.cpp71
-rw-r--r--src/core/web_engine_context.h3
-rw-r--r--src/core/web_event_factory.cpp79
-rw-r--r--src/core/web_event_factory.h6
-rw-r--r--src/pdf/api/qpdfdocument.h2
-rw-r--r--src/pdf/api/qpdfdocument_p.h11
-rw-r--r--src/pdf/api/qpdflinkmodel_p_p.h2
-rw-r--r--src/pdf/api/qpdfselection.h8
-rw-r--r--src/pdf/api/qpdfselection_p.h10
-rw-r--r--src/pdf/api/qtpdfglobal.h2
-rw-r--r--src/pdf/qpdfdocument.cpp133
-rw-r--r--src/pdf/qpdfselection.cpp34
-rw-r--r--src/pdf/quick/plugin.cpp2
-rw-r--r--src/pdf/quick/qml/PdfMultiPageView.qml171
-rw-r--r--src/pdf/quick/qml/PdfScrollablePageView.qml49
-rw-r--r--src/pdf/quick/qquickpdfdocument.cpp1
-rw-r--r--src/pdf/quick/qquickpdflinkmodel.cpp1
-rw-r--r--src/pdf/quick/qquickpdfnavigationstack.cpp12
-rw-r--r--src/pdf/quick/qquickpdfnavigationstack_p.h2
-rw-r--r--src/pdf/quick/qquickpdfsearchmodel.cpp24
-rw-r--r--src/pdf/quick/qquickpdfsearchmodel_p.h3
-rw-r--r--src/pdf/quick/qquickpdfselection.cpp289
-rw-r--r--src/pdf/quick/qquickpdfselection_p.h39
-rw-r--r--src/pdf/quick/qquicktableviewextra.cpp193
-rw-r--r--src/pdf/quick/qquicktableviewextra_p.h92
-rw-r--r--src/pdf/quick/quick.pro8
-rw-r--r--src/webengine/api/qquickwebengineview.cpp27
-rw-r--r--src/webengine/api/qquickwebengineview_p_p.h6
-rw-r--r--src/webengine/doc/src/external-resources.qdoc8
-rw-r--r--src/webengine/doc/src/qtwebengine-features.qdoc28
-rw-r--r--src/webengine/render_widget_host_view_qt_delegate_quick.cpp2
-rw-r--r--src/webenginewidgets/api/qtwebenginewidgetsglobal.cpp6
-rw-r--r--src/webenginewidgets/api/qwebenginepage.cpp39
-rw-r--r--src/webenginewidgets/api/qwebenginepage_p.h8
-rw-r--r--src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc7
-rw-r--r--src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp9
-rw-r--r--src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h1
-rw-r--r--tests/auto/quick/qmltests/data/tst_audioMuted.qml63
-rw-r--r--tests/auto/quick/qmltests/qmltests.pro1
-rw-r--r--tests/auto/widgets/proxy/proxy_server.cpp18
-rw-r--r--tests/auto/widgets/proxy/proxy_server.h7
-rw-r--r--tests/auto/widgets/proxy/tst_proxy.cpp35
-rw-r--r--tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp190
-rw-r--r--tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp22
-rw-r--r--tests/auto/widgets/util.h13
-rw-r--r--tools/scripts/version_resolver.py2
78 files changed, 1891 insertions, 350 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 000000000..3399f428c
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,105 @@
+# Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
+#
+# You may use this file under the terms of the 3-clause BSD license.
+# See the file LICENSE from this package for details.
+
+# This is the clang-format configuration style to be used by Qt,
+# based on the rules from https://wiki.qt.io/Qt_Coding_Style and
+# https://wiki.qt.io/Coding_Conventions
+
+---
+# Webkit style was loosely based on the Qt style
+BasedOnStyle: WebKit
+
+Standard: Cpp11
+
+# Column width is limited to 100 in accordance with Qt Coding Style.
+# https://wiki.qt.io/Qt_Coding_Style
+# Note that this may be changed at some point in the future.
+ColumnLimit: 100
+# How much weight do extra characters after the line length limit have.
+# PenaltyExcessCharacter: 4
+
+# Disable reflow of qdoc comments: indentation rules are different.
+# Translation comments are also excluded.
+CommentPragmas: "^!|^:"
+
+# We want a space between the type and the star for pointer types.
+PointerBindsToType: false
+
+# We use template< without space.
+SpaceAfterTemplateKeyword: false
+
+# We want to break before the operators, but not before a '='.
+BreakBeforeBinaryOperators: NonAssignment
+
+# Braces are usually attached, but not after functions or class declarations.
+BreakBeforeBraces: Custom
+BraceWrapping:
+ AfterClass: true
+ AfterControlStatement: false
+ AfterEnum: false
+ AfterFunction: true
+ AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: true
+ AfterUnion: false
+ BeforeCatch: false
+ BeforeElse: false
+ IndentBraces: false
+
+# When constructor initializers do not fit on one line, put them each on a new line.
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+# Indent initializers by 4 spaces
+ConstructorInitializerIndentWidth: 4
+
+# Indent width for line continuations.
+ContinuationIndentWidth: 8
+
+# No indentation for namespaces.
+NamespaceIndentation: None
+
+# Allow indentation for preprocessing directives (if/ifdef/endif). https://reviews.llvm.org/rL312125
+IndentPPDirectives: AfterHash
+
+# Horizontally align arguments after an open bracket.
+# The coding style does not specify the following, but this is what gives
+# results closest to the existing code.
+AlignAfterOpenBracket: true
+AlwaysBreakTemplateDeclarations: true
+
+# Ideally we should also allow less short function in a single line, but
+# clang-format does not handle that.
+AllowShortFunctionsOnASingleLine: Inline
+
+# The coding style specifies some include order categories, but also tells to
+# separate categories with an empty line. It does not specify the order within
+# the categories. Since the SortInclude feature of clang-format does not
+# re-order includes separated by empty lines, the feature is not used.
+SortIncludes: false
+
+# macros for which the opening brace stays attached.
+ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ]
+
+# Break constructor initializers before the colon and after the commas.
+BreakConstructorInitializers: BeforeColon
+
+################################################################################
+# QtWebEngine specific changes to Qt style
+
+# We are using BeforeComma consistently so don't change it
+BreakConstructorInitializers: BeforeComma
+
+# Chromium macros
+MacroBlockBegin: "^\
+IPC_BEGIN_MESSAGE_MAP|\
+IPC_BEGIN_MESSAGE_MAP_WITH_PARAM|\
+IPC_STRUCT_BEGIN|\
+IPC_STRUCT_BEGIN_WITH_PARENT|\
+IPC_STRUCT_TRAITS_BEGIN|\
+PPAPI_BEGIN_MESSAGE_MAP$"
+MacroBlockEnd: "^\
+IPC_END_MESSAGE_MAP|\
+IPC_STRUCT_END|\
+IPC_STRUCT_TRAITS_END|\
+PPAPI_END_MESSAGE_MAP$"
diff --git a/dist/changes-5.15.0 b/dist/changes-5.15.0
new file mode 100644
index 000000000..630a70852
--- /dev/null
+++ b/dist/changes-5.15.0
@@ -0,0 +1,91 @@
+Qt 5.15 introduces many new features and improvements as well as bugfixes
+over the 5.14.x series. For more details, refer to the online documentation
+included in this distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.15 series is binary compatible with the 5.14.x series.
+Applications compiled for 5.14 will continue to run with 5.15.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+
+****************************************************************************
+* General *
+****************************************************************************
+
+Behavior Changes
+----------------
+
+ - XSS Auditing has been removed, and the XSSAuditingEnabled setting no
+ longer has any effect.
+ - [QTBUG-79864] The viz display compositor is now used by default on all
+ platforms, but can be disabled with --disable-viz-display-compositor.
+ - The network layer integration has been rewritten to use Chromium's network
+ service, and now runs in a separate sandboxed process by default.
+ - [QTBUG-83656] CTRL+mouse wheel page zoom fixed, and now works by default.
+
+
+Chromium Snapshot
+-----------------
+
+ - Updated the Chromium version to 80.0.3987.163
+ - Applied security fixes from Chrome up to version 81.0.4044.138
+
+
+General
+-------
+
+ - Fixed hardware accelerated video decoding on Windows and macOS.
+ - Updated where to look for Google Chrome's CDM plugin.
+ - [QTBUG-82390] Disabled picture-in-picture to avoid non-functional button
+ showing up.
+ - Command-line specified PAC files can now be loaded from QRC.
+ - [QTBUG-82012] Placeholder for missing PPAPI plugins added.
+
+
+Libraries
+---------
+
+ - Added a renderProcessPid() getter to QWebEnginePage and WebEngineView
+ which allows reading the process ID of the underlying render process.
+ - [QTBUG-83338] Avoid decoding HTML in default JavaScript message handlers.
+
+
+****************************************************************************
+* Qt PDF *
+****************************************************************************
+
+General
+-------
+
+ - The qt-labs/qtpdf module was using an out-of-date version of pdfium.
+ Development will now continue in the qtwebengine repository in order to
+ reuse Qt WebEngine's pdfium build system integration.
+ - QtPdf is still in Tech Preview.
+ - It's now possible to build QtPDF for iOS (even though Qt WebEngine does not).
+ - Added a Qt Quick API (import QtQuick.Pdf):
+ * Added an image plugin: Image { source: "my.pdf"; currentFrame: 2 } is
+ enough to view the third page in a PDF file.
+ * High-level API for full-featured viewers: PdfScrollablePageView shows
+ one page at a time; PdfMultiPageView allows flicking vertically from
+ page to page. These are implemented in QML and packaged with the module.
+ - Low-level QtQuick API:
+ * PdfDocument provides API for the document and its metadata.
+ * PdfSearchModel can find a text string and provides information about
+ the locations where it is found on each page, which can be listed in
+ a ListView and visualized by using a Repeater to instantiate
+ QtQuick.Shapes for the bounding boxes.
+ * PdfSelection allows selecting text and copying to the clipboard;
+ visualization of the selected region is done via QtQuick.Shapes.
+ * PdfLinkModel provides information about the links in the document;
+ visual feedback and clicking can be handled with QtQuick.Shapes
+ and TapHandler, respectively.
+ * PdfNavigationStack is a model for implementing forward/back navigation.
+
diff --git a/examples/pdf/pdfviewer/viewer.qml b/examples/pdf/pdfviewer/viewer.qml
index f4c913154..38d62740a 100644
--- a/examples/pdf/pdfviewer/viewer.qml
+++ b/examples/pdf/pdfviewer/viewer.qml
@@ -226,11 +226,11 @@ ApplicationWindow {
Drawer {
id: searchDrawer
edge: Qt.LeftEdge
- modal: false
+// modal: false
+// dim: false // commented out as workaround for QTBUG-83859
width: 300
y: root.header.height
height: view.height
- dim: false
clip: true
ListView {
id: searchResultsList
diff --git a/src/3rdparty b/src/3rdparty
-Subproject 757b9f459d3770644ad83d2faf26a7539c65023
+Subproject 623647821aa7c7565ed5153a27c5a1bb088efbe
diff --git a/src/buildtools/config/linux.pri b/src/buildtools/config/linux.pri
index c02af3909..455a2e3c6 100644
--- a/src/buildtools/config/linux.pri
+++ b/src/buildtools/config/linux.pri
@@ -192,15 +192,11 @@ host_build {
} else {
gn_args += use_alsa=false
}
- qtConfig(build-qtwebengine-core):qtConfig(webengine-system-xkbcommon) {
- gn_args += use_xkbcommon=true
- } else {
- gn_args += use_xkbcommon=false
- }
!packagesExist(libpci): gn_args += use_libpci=false
qtConfig(build-qtwebengine-core):qtConfig(webengine-ozone-x11) {
gn_args += ozone_platform_x11=true
+ gn_args += use_xkbcommon=true
packagesExist(xscrnsaver): gn_args += use_xscrnsaver=true
qtConfig(webengine-webrtc): gn_args += rtc_use_x11=true
}
diff --git a/src/buildtools/configure.json b/src/buildtools/configure.json
index 27bd3ebf8..f02ab8070 100644
--- a/src/buildtools/configure.json
+++ b/src/buildtools/configure.json
@@ -32,12 +32,6 @@
{ "type": "pkgConfig", "args": "libdrm" }
]
},
- "webengine-xkbcommon": {
- "label": "xkbcommon",
- "sources": [
- { "type": "pkgConfig", "args": "xkbcommon" }
- ]
- },
"webengine-xcomposite": {
"label": "xcomposite",
"sources": [
@@ -296,13 +290,6 @@
]
}
},
- "webengine-xkbcommon": {
- "label": "system xkbcommon",
- "type": "compile",
- "test": {
- "include": "xkbcommon/xkbcommon.h"
- }
- },
"webengine-ninja": {
"label": "system ninja",
"type": "detectNinja",
@@ -457,11 +444,6 @@
"condition": "libs.webengine-libdrm",
"output": [ "privateFeature" ]
},
- "webengine-system-xkbcommon": {
- "label": "xkbcommon",
- "condition": "libs.webengine-xkbcommon && tests.webengine-xkbcommon",
- "output": [ "privateFeature" ]
- },
"webengine-system-xcomposite": {
"label": "xcomposite",
"condition": "libs.webengine-xcomposite",
@@ -756,8 +738,7 @@
"webengine-system-png",
"webengine-system-jpeg",
"webengine-system-harfbuzz",
- "webengine-system-freetype",
- "webengine-system-xkbcommon"
+ "webengine-system-freetype"
]
}
]
diff --git a/src/core/accessibility_activation_observer.cpp b/src/core/accessibility_activation_observer.cpp
index 75ad90c54..833190844 100644
--- a/src/core/accessibility_activation_observer.cpp
+++ b/src/core/accessibility_activation_observer.cpp
@@ -39,7 +39,7 @@
#include "accessibility_activation_observer.h"
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
#include "content/browser/accessibility/browser_accessibility_state_impl.h"
@@ -86,4 +86,4 @@ void AccessibilityActivationObserver::accessibilityActiveChanged(bool active)
} // namespace QtWebEngineCore
-#endif // QT_NO_ACCESSIBILITY
+#endif // QT_CONFIG(accessibility)
diff --git a/src/core/accessibility_activation_observer.h b/src/core/accessibility_activation_observer.h
index e42c83eb5..23fd2101e 100644
--- a/src/core/accessibility_activation_observer.h
+++ b/src/core/accessibility_activation_observer.h
@@ -40,9 +40,10 @@
#ifndef ACCESSIBILITY_ACTIVATION_OBSERVER_H
#define ACCESSIBILITY_ACTIVATION_OBSERVER_H
-#ifndef QT_NO_ACCESSIBILITY
#include <QtGui/qaccessible.h>
+#if QT_CONFIG(accessibility)
+
namespace QtWebEngineCore {
class RenderWidgetHostViewQt;
@@ -58,6 +59,6 @@ public:
} // namespace QtWebEngineCore
-#endif // QT_NO_ACCESSIBILITY
+#endif // QT_CONFIG(accessibility)
#endif // ACCESSIBILITY_ACTIVATION_OBSERVER_H
diff --git a/src/core/accessibility_tree_formatter_qt.cpp b/src/core/accessibility_tree_formatter_qt.cpp
index 334759abb..081856b37 100644
--- a/src/core/accessibility_tree_formatter_qt.cpp
+++ b/src/core/accessibility_tree_formatter_qt.cpp
@@ -52,7 +52,7 @@
namespace content {
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
class AccessibilityTreeFormatterQt : public AccessibilityTreeFormatterBrowser {
public:
explicit AccessibilityTreeFormatterQt();
@@ -204,12 +204,12 @@ const std::string AccessibilityTreeFormatterQt::GetDenyNodeString()
return "@QT-DENY-NODE:";
}
-#endif // QT_NO_ACCESSIBILITY
+#endif // QT_CONFIG(accessibility)
// static
std::unique_ptr<AccessibilityTreeFormatter> AccessibilityTreeFormatter::Create()
{
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
return std::unique_ptr<AccessibilityTreeFormatter>(new AccessibilityTreeFormatterQt());
#else
return nullptr;
diff --git a/src/core/api/qtwebenginecoreglobal.cpp b/src/core/api/qtwebenginecoreglobal.cpp
index 9e2a4a5b1..ce4362741 100644
--- a/src/core/api/qtwebenginecoreglobal.cpp
+++ b/src/core/api/qtwebenginecoreglobal.cpp
@@ -40,7 +40,7 @@
#include "qtwebenginecoreglobal_p.h"
#include <QGuiApplication>
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
# include <QOpenGLContext>
#ifdef Q_OS_MACOS
#include <sys/types.h>
@@ -49,14 +49,14 @@
#endif
#include <QThread>
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
QT_BEGIN_NAMESPACE
Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context);
Q_GUI_EXPORT QOpenGLContext *qt_gl_global_share_context();
QT_END_NAMESPACE
#endif
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
#ifdef Q_OS_MACOS
static bool needsOfflineRendererWorkaround()
{
@@ -75,7 +75,7 @@ static bool needsOfflineRendererWorkaround()
#endif
namespace QtWebEngineCore {
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
static QOpenGLContext *shareContext;
static void deleteShareContext()
@@ -94,7 +94,7 @@ static void deleteShareContext()
Q_WEBENGINECORE_PRIVATE_EXPORT void initialize()
{
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
#ifdef Q_OS_WIN32
qputenv("QT_D3DCREATE_MULTITHREADED", "1");
#endif
@@ -134,6 +134,6 @@ Q_WEBENGINECORE_PRIVATE_EXPORT void initialize()
// Classes like QOpenGLWidget check for the attribute
app->setAttribute(Qt::AA_ShareOpenGLContexts);
-#endif // QT_NO_OPENGL
+#endif // QT_CONFIG(opengl)
}
} // namespace QtWebEngineCore
diff --git a/src/core/browser_accessibility_manager_qt.cpp b/src/core/browser_accessibility_manager_qt.cpp
index 8e3ee5940..5968bfd30 100644
--- a/src/core/browser_accessibility_manager_qt.cpp
+++ b/src/core/browser_accessibility_manager_qt.cpp
@@ -51,24 +51,24 @@ BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
BrowserAccessibilityDelegate* delegate,
BrowserAccessibilityFactory* factory)
{
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
return new BrowserAccessibilityManagerQt(nullptr, initialTree, delegate, factory);
#else
delete factory;
return nullptr;
-#endif // QT_NO_ACCESSIBILITY
+#endif // QT_CONFIG(accessibility)
}
BrowserAccessibility *BrowserAccessibility::Create()
{
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
return new BrowserAccessibilityQt();
#else
return nullptr;
-#endif // QT_NO_ACCESSIBILITY
+#endif // QT_CONFIG(accessibility)
}
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
BrowserAccessibilityManagerQt::BrowserAccessibilityManagerQt(
QObject *parentObject, const ui::AXTreeUpdate &initialTree,
BrowserAccessibilityDelegate* delegate, BrowserAccessibilityFactory* factory)
@@ -164,6 +164,6 @@ void BrowserAccessibilityManagerQt::FireGeneratedEvent(ui::AXEventGenerator::Eve
}
}
-#endif // QT_NO_ACCESSIBILITY
+#endif // QT_CONFIG(accessibility)
}
diff --git a/src/core/browser_accessibility_manager_qt.h b/src/core/browser_accessibility_manager_qt.h
index 16e2d1fe7..6d6086811 100644
--- a/src/core/browser_accessibility_manager_qt.h
+++ b/src/core/browser_accessibility_manager_qt.h
@@ -41,8 +41,11 @@
#define BROWSER_ACCESSIBILITY_MANAGER_QT_H
#include "content/browser/accessibility/browser_accessibility_manager.h"
-#ifndef QT_NO_ACCESSIBILITY
+
#include <QtCore/qobject.h>
+#include <QtGui/qtgui-config.h>
+
+#if QT_CONFIG(accessibility)
QT_BEGIN_NAMESPACE
class QAccessibleInterface;
@@ -74,5 +77,5 @@ private:
}
-#endif // QT_NO_ACCESSIBILITY
+#endif // QT_CONFIG(accessibility)
#endif
diff --git a/src/core/browser_accessibility_qt.cpp b/src/core/browser_accessibility_qt.cpp
index 6104fb1f8..de78eb85d 100644
--- a/src/core/browser_accessibility_qt.cpp
+++ b/src/core/browser_accessibility_qt.cpp
@@ -43,7 +43,7 @@
#include "browser_accessibility_qt.h"
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
#include "ui/accessibility/ax_enums.mojom.h"
@@ -996,4 +996,4 @@ void BrowserAccessibilityQt::modelChange(QAccessibleTableModelChangeEvent *)
} // namespace content
-#endif // QT_NO_ACCESSIBILITY
+#endif // QT_CONFIG(accessibility)
diff --git a/src/core/browser_accessibility_qt.h b/src/core/browser_accessibility_qt.h
index 4acac6aa7..19c7a1e54 100644
--- a/src/core/browser_accessibility_qt.h
+++ b/src/core/browser_accessibility_qt.h
@@ -43,7 +43,7 @@
#include <QtGui/qaccessible.h>
#include "content/browser/accessibility/browser_accessibility.h"
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
namespace content {
@@ -151,5 +151,5 @@ QAccessibleInterface *toQAccessibleInterface(BrowserAccessibility *acc);
} // namespace content
-#endif // QT_NO_ACCESSIBILITY
+#endif // QT_CONFIG(accessibility)
#endif
diff --git a/src/core/compositor/delegated_frame_node.cpp b/src/core/compositor/delegated_frame_node.cpp
index 4d74937d9..fcaae27bb 100644
--- a/src/core/compositor/delegated_frame_node.cpp
+++ b/src/core/compositor/delegated_frame_node.cpp
@@ -68,7 +68,7 @@
#include "components/viz/service/display/bsp_tree.h"
#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
# include <QOpenGLContext>
# include <QOpenGLFunctions>
# include <QSGFlatColorMaterial>
@@ -79,7 +79,7 @@
#include <QSGImageNode>
#include <QSGRectangleNode>
-#if !defined(QT_NO_EGL)
+#if QT_CONFIG(egl)
#include <EGL/egl.h>
#include <EGL/eglext.h>
#endif
@@ -108,14 +108,14 @@
#define GL_LINE_LOOP 0x0002
#endif
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
QT_BEGIN_NAMESPACE
Q_GUI_EXPORT QOpenGLContext *qt_gl_global_share_context();
QT_END_NAMESPACE
#endif
namespace QtWebEngineCore {
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
class MailboxTexture : public QSGTexture, protected QOpenGLFunctions {
public:
MailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target = -1);
@@ -141,7 +141,7 @@ private:
#endif
friend class DelegatedFrameNode;
};
-#endif // QT_NO_OPENGL
+#endif // QT_CONFIG(opengl)
class RectClipNode : public QSGClipNode
{
@@ -167,7 +167,7 @@ public:
QSGNode *) = 0;
virtual void setupSolidColorNode(const QRect &, const QColor &, QSGNode *) = 0;
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
virtual void setupDebugBorderNode(QSGGeometry *, QSGFlatColorMaterial *, QSGNode *) = 0;
virtual void setupYUVVideoNode(QSGTexture *, QSGTexture *, QSGTexture *, QSGTexture *,
const QRectF &, const QRectF &, const QSizeF &, const QSizeF &,
@@ -177,7 +177,7 @@ public:
virtual void setupStreamVideoNode(MailboxTexture *, const QRectF &,
const QMatrix4x4 &, QSGNode *) = 0;
#endif // GL_OES_EGL_image_external
-#endif // QT_NO_OPENGL
+#endif // QT_CONFIG(opengl)
protected:
QVector<QSGNode*> *m_sceneGraphNodes;
};
@@ -234,7 +234,7 @@ public:
if (rectangleNode->color() != color)
rectangleNode->setColor(color);
}
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
void setupDebugBorderNode(QSGGeometry *geometry, QSGFlatColorMaterial *material,
QSGNode *) override
{
@@ -259,7 +259,7 @@ public:
Q_UNREACHABLE();
}
#endif // GL_OES_EGL_image_external
-#endif // QT_NO_OPENGL
+#endif // QT_CONFIG(opengl)
private:
QVector<QSGNode*>::iterator m_nodeIterator;
@@ -314,7 +314,7 @@ public:
m_sceneGraphNodes->append(rectangleNode);
}
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
void setupDebugBorderNode(QSGGeometry *geometry, QSGFlatColorMaterial *material,
QSGNode *layerChain) override
{
@@ -363,7 +363,7 @@ public:
m_sceneGraphNodes->append(svideoNode);
}
#endif // GL_OES_EGL_image_external
-#endif // QT_NO_OPENGL
+#endif // QT_CONFIG(opengl)
private:
RenderWidgetHostViewQtDelegate *m_apiDelegate;
@@ -421,7 +421,7 @@ static QSGNode *buildLayerChain(QSGNode *chainParent, const viz::SharedQuadState
return layerChain;
}
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
MailboxTexture::MailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target)
: m_textureId(resource->texture_id)
, m_fence(resource->texture_fence)
@@ -476,7 +476,7 @@ void MailboxTexture::bind()
}
#endif
}
-#endif // !QT_NO_OPENGL
+#endif // QT_CONFIG(opengl)
RectClipNode::RectClipNode(const QRectF &rect)
: m_geometry(QSGGeometry::defaultAttributes_Point2D(), 4)
@@ -488,12 +488,12 @@ RectClipNode::RectClipNode(const QRectF &rect)
}
DelegatedFrameNode::DelegatedFrameNode()
-#if defined(USE_OZONE) && !defined(QT_NO_OPENGL)
+#if defined(USE_OZONE) && QT_CONFIG(opengl)
: m_contextShared(true)
#endif
{
setFlag(UsePreprocess);
-#if defined(USE_OZONE) && !defined(QT_NO_OPENGL)
+#if defined(USE_OZONE) && QT_CONFIG(opengl)
QOpenGLContext *currentContext = QOpenGLContext::currentContext() ;
QOpenGLContext *sharedContext = qt_gl_global_share_context();
if (currentContext && sharedContext && !QOpenGLContext::areSharing(currentContext, sharedContext)) {
@@ -569,14 +569,14 @@ static bool areRenderPassStructuresEqual(const viz::CompositorFrame *frameData,
const viz::DrawQuad *prevQuad = *prevIt;
if (quad->material != prevQuad->material)
return false;
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
if (quad->material == viz::DrawQuad::Material::kYuvVideoContent)
return false;
#ifdef GL_OES_EGL_image_external
if (quad->material == viz::DrawQuad::Material::kStreamVideoContent)
return false;
#endif // GL_OES_EGL_image_external
-#endif // QT_NO_OPENGL
+#endif // QT_CONFIG(opengl)
if (!areSharedQuadStatesEqual(quad->shared_quad_state, prevQuad->shared_quad_state))
return false;
if (quad->shared_quad_state->is_clipped && quad->visible_rect != prevQuad->visible_rect) {
@@ -680,7 +680,7 @@ void DelegatedFrameNode::commit(const viz::CompositorFrame &pendingFrame,
rpLayer->setSize(toQt(pass->output_rect.size()));
rpLayer->setFormat(pass->has_transparent_background ? GL_RGBA : GL_RGB);
rpLayer->setHasMipmaps(pass->generate_mipmap);
- rpLayer->setMirrorVertical(true);
+ rpLayer->setMirrorVertical(false);
scissorRect = pass->output_rect;
} else {
renderPassParent = this;
@@ -880,7 +880,7 @@ void DelegatedFrameNode::handleQuad(
Q_UNUSED(scquad->force_anti_aliasing_off);
nodeHandler->setupSolidColorNode(toQt(quad->rect), toQt(scquad->color), currentLayerChain);
break;
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
}
case viz::DrawQuad::Material::kDebugBorder: {
const viz::DebugBorderDrawQuad *dbquad = viz::DebugBorderDrawQuad::MaterialCast(quad);
@@ -913,7 +913,7 @@ void DelegatedFrameNode::handleQuad(
toQt(quad->rect), toQt(tquad->tex_coord_rect),
QSGImageNode::NoTransform, currentLayerChain);
break;
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
}
case viz::DrawQuad::Material::kYuvVideoContent: {
const viz::YUVVideoDrawQuad *vquad = viz::YUVVideoDrawQuad::MaterialCast(quad);
@@ -952,7 +952,7 @@ void DelegatedFrameNode::handleQuad(
nodeHandler->setupStreamVideoNode(texture, toQt(squad->rect), qMatrix, currentLayerChain);
break;
#endif // GL_OES_EGL_image_external
-#endif // QT_NO_OPENGL
+#endif // QT_CONFIG(opengl)
}
case viz::DrawQuad::Material::kSurfaceContent:
Q_UNREACHABLE();
@@ -1050,7 +1050,7 @@ QSharedPointer<QSGTexture> DelegatedFrameNode::createBitmapTexture(const Composi
QSharedPointer<MailboxTexture> DelegatedFrameNode::createMailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target)
{
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
return QSharedPointer<MailboxTexture>::create(resource, hasAlphaChannel, target);
#else
Q_UNREACHABLE();
@@ -1059,7 +1059,7 @@ QSharedPointer<MailboxTexture> DelegatedFrameNode::createMailboxTexture(const Co
void DelegatedFrameNode::copyMailboxTextures()
{
-#if !defined(QT_NO_OPENGL) && defined(USE_OZONE)
+#if QT_CONFIG(opengl) && defined(USE_OZONE)
// Workaround when context is not shared QTBUG-48969
// Make slow copy between two contexts.
if (!m_contextShared) {
diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp
index cac392182..05957c26f 100644
--- a/src/core/content_browser_client_qt.cpp
+++ b/src/core/content_browser_client_qt.cpp
@@ -312,7 +312,7 @@ private:
void ShareGroupQtQuick::AboutToAddFirstContext()
{
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
// This currently has to be setup by ::main in all applications using QQuickWebEngineView with delegated rendering.
QOpenGLContext *shareContext = qt_gl_global_share_context();
if (!shareContext) {
diff --git a/src/core/core_module.pro b/src/core/core_module.pro
index d7e2ab8da..5007012ac 100644
--- a/src/core/core_module.pro
+++ b/src/core/core_module.pro
@@ -29,12 +29,11 @@ write_file($$RSP_OBJECT_FILE, RSP_O_CONTENT)
RSP_ARCHIVE_FILE = $$OUT_PWD/$$getConfigDir()/$${TARGET}_a.rsp
for(archive, NINJA_ARCHIVES): RSP_A_CONTENT += $$archive
write_file($$RSP_ARCHIVE_FILE, RSP_A_CONTENT)
-macos:LIBS_PRIVATE += -Wl,-filelist,$$shell_quote($$RSP_OBJECT_FILE)
-linux:QMAKE_LFLAGS += @$${RSP_OBJECT_FILE}
-# QTBUG-58710 add main rsp file on windows
-win32:QMAKE_LFLAGS += @$${RSP_OBJECT_FILE}
-linux:QMAKE_LFLAGS += -Wl,--start-group @$${RSP_ARCHIVE_FILE} -Wl,--end-group
-else: LIBS_PRIVATE += $$NINJA_ARCHIVES
+
+macos:LIBS_PRIVATE += -Wl,-filelist,$$shell_quote($${RSP_OBJECT_FILE}) @$${RSP_ARCHIVE_FILE}
+linux:QMAKE_LFLAGS += @$${RSP_OBJECT_FILE} -Wl,--start-group @$${RSP_ARCHIVE_FILE} -Wl,--end-group
+win32:QMAKE_LFLAGS += @$${RSP_OBJECT_FILE} @$${RSP_ARCHIVE_FILE}
+
LIBS_PRIVATE += $$NINJA_LIB_DIRS $$NINJA_LIBS
# GN's LFLAGS doesn't always work across all the Linux configurations we support.
# The Windows and macOS ones from GN does provide a few useful flags however
diff --git a/src/core/net/cookie_monster_delegate_qt.cpp b/src/core/net/cookie_monster_delegate_qt.cpp
index cf114406b..d3157f760 100644
--- a/src/core/net/cookie_monster_delegate_qt.cpp
+++ b/src/core/net/cookie_monster_delegate_qt.cpp
@@ -223,20 +223,11 @@ void CookieMonsterDelegateQt::OnCookieChanged(const net::CookieChangeInfo &chang
m_client->d_func()->onCookieChanged(toQt(change.cookie), change.cause != net::CookieChangeCause::INSERTED);
}
-void CookieMonsterDelegateQt::GetAllCookiesCallbackOnUIThread(qint64 callbackId, const std::vector<net::CanonicalCookie> &cookies)
-{
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- QByteArray rawCookies;
- for (auto &&cookie : cookies)
- rawCookies += toQt(cookie).toRawForm() % QByteArrayLiteral("\n");
-
- GetAllCookiesResultOnUIThread(callbackId, rawCookies);
-}
-
-void CookieMonsterDelegateQt::GetAllCookiesResultOnUIThread(qint64 callbackId, const QByteArray &cookies)
+void CookieMonsterDelegateQt::GetAllCookiesCallbackOnUIThread(qint64 callbackId, const net::CookieList &cookies)
{
+ QByteArray rawCookies = QByteArray::fromStdString(net::CanonicalCookie::BuildCookieLine(cookies));
if (m_client)
- m_client->d_func()->onGetAllCallbackResult(callbackId, cookies);
+ m_client->d_func()->onGetAllCallbackResult(callbackId, rawCookies);
}
void CookieMonsterDelegateQt::SetCookieCallbackOnUIThread(qint64 callbackId, net::CanonicalCookie::CookieInclusionStatus status)
@@ -250,4 +241,5 @@ void CookieMonsterDelegateQt::DeleteCookiesCallbackOnUIThread(qint64 callbackId,
if (m_client)
m_client->d_func()->onDeleteCallbackResult(callbackId, numCookies);
}
-}
+
+} // namespace QtWebEngineCore
diff --git a/src/core/net/cookie_monster_delegate_qt.h b/src/core/net/cookie_monster_delegate_qt.h
index 6caaeea94..bcbbe4c52 100644
--- a/src/core/net/cookie_monster_delegate_qt.h
+++ b/src/core/net/cookie_monster_delegate_qt.h
@@ -115,8 +115,7 @@ public:
void OnCookieChanged(const net::CookieChangeInfo &change);
private:
- void GetAllCookiesCallbackOnUIThread(qint64 callbackId, const std::vector<net::CanonicalCookie> &cookies);
- void GetAllCookiesResultOnUIThread(qint64 callbackId, const QByteArray &cookies);
+ void GetAllCookiesCallbackOnUIThread(qint64 callbackId, const net::CookieList &cookies);
void SetCookieCallbackOnUIThread(qint64 callbackId, net::CanonicalCookie::CookieInclusionStatus status);
void DeleteCookiesCallbackOnUIThread(qint64 callbackId, uint numCookies);
};
diff --git a/src/core/net/system_network_context_manager.cpp b/src/core/net/system_network_context_manager.cpp
index 43b2b2557..29cc82abf 100644
--- a/src/core/net/system_network_context_manager.cpp
+++ b/src/core/net/system_network_context_manager.cpp
@@ -166,7 +166,8 @@ private:
network::mojom::NetworkContext *SystemNetworkContextManager::GetContext()
{
- if (!network_service_network_context_ || network_service_network_context_.encountered_error()) {
+ if (!network_service_network_context_ ||
+ !network_service_network_context_.is_connected()) {
// This should call into OnNetworkServiceCreated(), which will re-create
// the network service, if needed. There's a chance that it won't be
// invoked, if the NetworkContext has encountered an error but the
@@ -182,14 +183,14 @@ network::mojom::NetworkContext *SystemNetworkContextManager::GetContext()
network::mojom::URLLoaderFactory *SystemNetworkContextManager::GetURLLoaderFactory()
{
// Create the URLLoaderFactory as needed.
- if (url_loader_factory_ && !url_loader_factory_.encountered_error()) {
+ if (url_loader_factory_ && url_loader_factory_.is_connected()) {
return url_loader_factory_.get();
}
network::mojom::URLLoaderFactoryParamsPtr params = network::mojom::URLLoaderFactoryParams::New();
params->process_id = network::mojom::kBrowserProcessId;
params->is_corb_enabled = false;
- GetContext()->CreateURLLoaderFactory(mojo::MakeRequest(&url_loader_factory_), std::move(params));
+ GetContext()->CreateURLLoaderFactory(url_loader_factory_.BindNewPipeAndPassReceiver(), std::move(params));
return url_loader_factory_.get();
}
@@ -252,7 +253,10 @@ void SystemNetworkContextManager::OnNetworkServiceCreated(network::mojom::Networ
// The system NetworkContext must be created first, since it sets
// |primary_network_context| to true.
- network_service->CreateNetworkContext(MakeRequest(&network_service_network_context_), CreateNetworkContextParams());
+ network_service_network_context_.reset();
+ network_service->CreateNetworkContext(
+ network_service_network_context_.BindNewPipeAndPassReceiver(),
+ CreateNetworkContextParams());
// Configure the stub resolver. This must be done after the system
// NetworkContext is created, but before anything has the chance to use it.
diff --git a/src/core/net/system_network_context_manager.h b/src/core/net/system_network_context_manager.h
index e429453a2..5094008f2 100644
--- a/src/core/net/system_network_context_manager.h
+++ b/src/core/net/system_network_context_manager.h
@@ -167,12 +167,12 @@ private:
// NetworkContext using the network service, if the network service is
// enabled. nullptr, otherwise.
- network::mojom::NetworkContextPtr network_service_network_context_;
+ mojo::Remote<network::mojom::NetworkContext> network_service_network_context_;
// URLLoaderFactory backed by the NetworkContext returned by GetContext(), so
// consumers don't all need to create their own factory.
scoped_refptr<URLLoaderFactoryForSystem> shared_url_loader_factory_;
- network::mojom::URLLoaderFactoryPtr url_loader_factory_;
+ mojo::Remote<network::mojom::URLLoaderFactory> url_loader_factory_;
ProxyConfigMonitor proxy_config_monitor_;
diff --git a/src/core/ozone/gl_context_qt.cpp b/src/core/ozone/gl_context_qt.cpp
index e9337874a..10347bdc7 100644
--- a/src/core/ozone/gl_context_qt.cpp
+++ b/src/core/ozone/gl_context_qt.cpp
@@ -59,7 +59,7 @@ namespace {
inline void *resourceForContext(const QByteArray &resource)
{
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
QOpenGLContext *shareContext = qt_gl_global_share_context();
if (!shareContext) {
qFatal("QWebEngine: OpenGL resource sharing is not set up in QtQuick. Please make sure to call QtWebEngine::initialize() in your main() function.");
@@ -153,7 +153,7 @@ void* GLContextHelper::getNativeDisplay()
QFunctionPointer GLContextHelper::getGlXGetProcAddress()
{
QFunctionPointer get_proc_address = nullptr;
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
if (QOpenGLContext *context = qt_gl_global_share_context()) {
get_proc_address = context->getProcAddress("glXGetProcAddress");
}
@@ -164,7 +164,7 @@ QFunctionPointer GLContextHelper::getGlXGetProcAddress()
QFunctionPointer GLContextHelper::getEglGetProcAddress()
{
QFunctionPointer get_proc_address = nullptr;
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
if (QOpenGLContext *context = qt_gl_global_share_context()) {
get_proc_address = context->getProcAddress("eglGetProcAddress");
}
diff --git a/src/core/ozone/gl_ozone_egl_qt.cpp b/src/core/ozone/gl_ozone_egl_qt.cpp
index 2fa86d79b..c692920cf 100644
--- a/src/core/ozone/gl_ozone_egl_qt.cpp
+++ b/src/core/ozone/gl_ozone_egl_qt.cpp
@@ -55,9 +55,9 @@
#include <EGL/egl.h>
#include <dlfcn.h>
-#include <QtGui/qtgui-config.h> // for QT_NO_OPENGL
+#include <QtGui/qtgui-config.h>
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
#include <QOpenGLContext>
QT_BEGIN_NAMESPACE
Q_GUI_EXPORT QOpenGLContext *qt_gl_global_share_context();
@@ -88,7 +88,7 @@ bool GLOzoneEGLQt::LoadGLES2Bindings(gl::GLImplementation /*implementation*/)
reinterpret_cast<gl::GLGetProcAddressProc>(
base::GetFunctionPointerFromNativeLibrary(eglgles2Library,
"eglGetProcAddress"));
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
if (!get_proc_address) {
// QTBUG-63341 most likely libgles2 not linked with libegl -> fallback to qpa
if (QOpenGLContext *context = qt_gl_global_share_context()) {
diff --git a/src/core/permission_manager_qt.cpp b/src/core/permission_manager_qt.cpp
index 16a7b25bf..2f9543769 100644
--- a/src/core/permission_manager_qt.cpp
+++ b/src/core/permission_manager_qt.cpp
@@ -112,8 +112,13 @@ PermissionManagerQt::~PermissionManagerQt()
{
}
-void PermissionManagerQt::permissionRequestReply(const QUrl &origin, ProfileAdapter::PermissionType type, bool reply)
+void PermissionManagerQt::permissionRequestReply(const QUrl &url, ProfileAdapter::PermissionType type, bool reply)
{
+ // Normalize the QUrl to GURL origin form.
+ const GURL gorigin = toGurl(url).GetOrigin();
+ const QUrl origin = gorigin.is_empty() ? url : toQt(gorigin);
+ if (origin.isEmpty())
+ return;
QPair<QUrl, ProfileAdapter::PermissionType> key(origin, type);
m_permissions[key] = reply;
blink::mojom::PermissionStatus status = reply ? blink::mojom::PermissionStatus::GRANTED : blink::mojom::PermissionStatus::DENIED;
diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp
index 5887b356e..c4d6200b7 100644
--- a/src/core/render_widget_host_view_qt.cpp
+++ b/src/core/render_widget_host_view_qt.cpp
@@ -286,6 +286,7 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost *widget
, m_adapterClient(0)
, m_imeInProgress(false)
, m_receivedEmptyImeEvent(false)
+ , m_isMouseLocked(false)
, m_imState(0)
, m_anchorPositionWithinSelection(-1)
, m_cursorPositionWithinSelection(-1)
@@ -425,14 +426,14 @@ gfx::NativeViewAccessible RenderWidgetHostViewQt::GetNativeViewAccessible()
content::BrowserAccessibilityManager* RenderWidgetHostViewQt::CreateBrowserAccessibilityManager(content::BrowserAccessibilityDelegate* delegate, bool for_root_frame)
{
Q_UNUSED(for_root_frame); // FIXME
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
return new content::BrowserAccessibilityManagerQt(
m_adapterClient->accessibilityParentObject(),
content::BrowserAccessibilityManagerQt::GetEmptyDocument(),
delegate);
#else
return 0;
-#endif // QT_NO_ACCESSIBILITY
+#endif // QT_CONFIG(accessibility)
}
// Set focus to the associated View component.
@@ -448,6 +449,11 @@ bool RenderWidgetHostViewQt::HasFocus()
return m_delegate->hasKeyboardFocus();
}
+bool RenderWidgetHostViewQt::IsMouseLocked()
+{
+ return m_isMouseLocked;
+}
+
bool RenderWidgetHostViewQt::IsSurfaceAvailableForCopy()
{
if (m_enableViz)
@@ -520,6 +526,7 @@ bool RenderWidgetHostViewQt::LockMouse(bool)
{
m_previousMousePosition = QCursor::pos();
m_delegate->lockMouse();
+ m_isMouseLocked = true;
qApp->setOverrideCursor(Qt::BlankCursor);
return true;
}
@@ -528,6 +535,7 @@ void RenderWidgetHostViewQt::UnlockMouse()
{
m_delegate->unlockMouse();
qApp->restoreOverrideCursor();
+ m_isMouseLocked = false;
host()->LostMouseLock();
}
@@ -1530,7 +1538,21 @@ void RenderWidgetHostViewQt::WheelEventAck(const blink::WebMouseWheelEvent &even
m_mouseWheelPhaseHandler.AddPhaseIfNeededAndScheduleEndEvent(webEvent, false);
host()->ForwardWheelEvent(webEvent);
}
- // TODO: We could forward unhandled wheelevents to our parent.
+}
+
+void RenderWidgetHostViewQt::GestureEventAck(const blink::WebGestureEvent &event, content::InputEventAckState ack_result)
+{
+ // Forward unhandled scroll events back as wheel events
+ if (event.GetType() != blink::WebInputEvent::kGestureScrollUpdate)
+ return;
+ switch (ack_result) {
+ case content::INPUT_EVENT_ACK_STATE_NOT_CONSUMED:
+ case content::INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS:
+ WebEventFactory::sendUnhandledWheelEvent(event, delegate());
+ break;
+ default:
+ break;
+ }
}
content::MouseWheelPhaseHandler *RenderWidgetHostViewQt::GetMouseWheelPhaseHandler()
@@ -1770,6 +1792,8 @@ void RenderWidgetHostViewQt::handleFocusEvent(QFocusEvent *ev)
else if (ev->reason() == Qt::BacktabFocusReason)
viewHost->SetInitialFocus(true);
ev->accept();
+
+ m_adapterClient->webContentsAdapter()->handlePendingMouseLockPermission();
} else if (ev->lostFocus()) {
host()->SetActive(false);
host()->LostFocus();
diff --git a/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h
index 41ce50b34..453b90888 100644
--- a/src/core/render_widget_host_view_qt.h
+++ b/src/core/render_widget_host_view_qt.h
@@ -132,6 +132,7 @@ public:
gfx::NativeViewAccessible GetNativeViewAccessible() override;
void Focus() override;
bool HasFocus() override;
+ bool IsMouseLocked() override;
bool IsSurfaceAvailableForCopy() override;
void CopyFromSurface(const gfx::Rect &src_rect,
const gfx::Size &output_size,
@@ -155,6 +156,7 @@ public:
void DidCreateNewRendererCompositorFrameSink(viz::mojom::CompositorFrameSinkClient* renderer_compositor_frame_sink) override;
void SubmitCompositorFrame(const viz::LocalSurfaceId&, viz::CompositorFrame, base::Optional<viz::HitTestRegionList>) override;
void WheelEventAck(const blink::WebMouseWheelEvent &event, content::InputEventAckState ack_result) override;
+ void GestureEventAck(const blink::WebGestureEvent &event, content::InputEventAckState ack_result) override;
content::MouseWheelPhaseHandler *GetMouseWheelPhaseHandler() override;
viz::ScopedSurfaceIdAllocator DidUpdateVisualProperties(const cc::RenderFrameMetadata &metadata) override;
void OnDidUpdateVisualPropertiesComplete(const cc::RenderFrameMetadata &metadata);
@@ -288,6 +290,7 @@ private:
bool m_imeInProgress;
bool m_receivedEmptyImeEvent;
QPoint m_previousMousePosition;
+ bool m_isMouseLocked;
gfx::Vector2dF m_lastScrollOffset;
gfx::SizeF m_lastContentsSize;
diff --git a/src/core/render_widget_host_view_qt_delegate.h b/src/core/render_widget_host_view_qt_delegate.h
index 4ee790ce9..46f1802a6 100644
--- a/src/core/render_widget_host_view_qt_delegate.h
+++ b/src/core/render_widget_host_view_qt_delegate.h
@@ -58,12 +58,13 @@
QT_BEGIN_NAMESPACE
class QEvent;
+class QInputMethodEvent;
class QSGLayer;
class QSGNode;
class QSGRectangleNode;
class QSGTexture;
class QVariant;
-class QInputMethodEvent;
+class QWheelEvent;
class QSGImageNode;
@@ -111,6 +112,7 @@ public:
virtual void setInputMethodHints(Qt::InputMethodHints hints) = 0;
virtual void setClearColor(const QColor &color) = 0;
virtual bool copySurface(const QRect &, const QSize &, QImage &) = 0;
+ virtual void unhandledWheelEvent(QWheelEvent *) {}
};
} // namespace QtWebEngineCore
diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp
index 2d559bb38..0f2f21f83 100644
--- a/src/core/web_contents_adapter.cpp
+++ b/src/core/web_contents_adapter.cpp
@@ -1024,7 +1024,7 @@ QWebEngineUrlRequestInterceptor* WebContentsAdapter::requestInterceptor() const
return m_requestInterceptor;
}
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
QAccessibleInterface *WebContentsAdapter::browserAccessible()
{
CHECK_INITIALIZED(nullptr);
@@ -1040,7 +1040,7 @@ QAccessibleInterface *WebContentsAdapter::browserAccessible()
return content::toQAccessibleInterface(acc);
}
-#endif // QT_NO_ACCESSIBILITY
+#endif // QT_CONFIG(accessibility)
void WebContentsAdapter::runJavaScript(const QString &javaScript, quint32 worldId)
{
@@ -1372,20 +1372,40 @@ void WebContentsAdapter::runFeatureRequestCallback(const QUrl &securityOrigin, P
m_profileAdapter->permissionRequestReply(securityOrigin, feature, allowed);
}
-void WebContentsAdapter::grantMouseLockPermission(bool granted)
+void WebContentsAdapter::grantMouseLockPermission(const QUrl &securityOrigin, bool granted)
{
CHECK_INITIALIZED();
+ if (securityOrigin != toQt(m_webContents->GetLastCommittedURL().GetOrigin()))
+ return;
if (granted) {
- if (RenderWidgetHostViewQt *rwhv = static_cast<RenderWidgetHostViewQt *>(m_webContents->GetRenderWidgetHostView()))
+ if (RenderWidgetHostViewQt *rwhv = static_cast<RenderWidgetHostViewQt *>(m_webContents->GetRenderWidgetHostView())) {
rwhv->Focus();
- else
+ if (!rwhv->HasFocus()) {
+ // We tried to activate our RWHVQtDelegate, but we failed. This probably means that
+ // the permission was granted from a modal dialog and the windowing system is not ready
+ // to set focus on the originating view. Since pointer lock strongly requires it, we just
+ // wait until the next FocusIn event.
+ m_pendingMouseLockPermissions.insert(securityOrigin, granted);
+ return;
+ }
+ } else
granted = false;
}
m_webContents->GotResponseToLockMouseRequest(granted);
}
+void WebContentsAdapter::handlePendingMouseLockPermission()
+{
+ CHECK_INITIALIZED();
+ auto it = m_pendingMouseLockPermissions.find(toQt(m_webContents->GetLastCommittedURL().GetOrigin()));
+ if (it != m_pendingMouseLockPermissions.end()) {
+ m_webContents->GotResponseToLockMouseRequest(it.value());
+ m_pendingMouseLockPermissions.erase(it);
+ }
+}
+
void WebContentsAdapter::setBackgroundColor(const QColor &color)
{
CHECK_INITIALIZED();
diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h
index cc041ed55..66808ce5e 100644
--- a/src/core/web_contents_adapter.h
+++ b/src/core/web_contents_adapter.h
@@ -194,7 +194,8 @@ public:
void devToolsFrontendDestroyed(DevToolsFrontendQt *frontend);
void grantMediaAccessPermission(const QUrl &securityOrigin, WebContentsAdapterClient::MediaRequestFlags flags);
- void grantMouseLockPermission(bool granted);
+ void grantMouseLockPermission(const QUrl &securityOrigin, bool granted);
+ void handlePendingMouseLockPermission();
void runFeatureRequestCallback(const QUrl &securityOrigin, ProfileAdapter::PermissionType feature, bool allowed);
void setBackgroundColor(const QColor &color);
@@ -268,6 +269,7 @@ private:
#endif
WebContentsAdapterClient *m_adapterClient;
quint64 m_nextRequestId;
+ QMap<QUrl, bool> m_pendingMouseLockPermissions;
std::unique_ptr<content::DropData> m_currentDropData;
uint m_currentDropAction;
bool m_updateDragActionCalled;
diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h
index 250801f51..04df99f0e 100644
--- a/src/core/web_contents_adapter_client.h
+++ b/src/core/web_contents_adapter_client.h
@@ -469,7 +469,10 @@ public:
virtual void loadFinished(bool success, const QUrl &url, bool isErrorPage = false, int errorCode = 0, const QString &errorDescription = QString()) = 0;
virtual void focusContainer() = 0;
virtual void unhandledKeyEvent(QKeyEvent *event) = 0;
- virtual void adoptNewWindow(QSharedPointer<WebContentsAdapter> newWebContents, WindowOpenDisposition disposition, bool userGesture, const QRect & initialGeometry, const QUrl &targetUrl) = 0;
+ virtual QSharedPointer<WebContentsAdapter>
+ adoptNewWindow(QSharedPointer<WebContentsAdapter> newWebContents,
+ WindowOpenDisposition disposition, bool userGesture,
+ const QRect &initialGeometry, const QUrl &targetUrl) = 0;
virtual bool isBeingAdopted() = 0;
virtual void close() = 0;
virtual void windowCloseRejected() = 0;
diff --git a/src/core/web_contents_delegate_qt.cpp b/src/core/web_contents_delegate_qt.cpp
index fada874a3..bf0254e82 100644
--- a/src/core/web_contents_delegate_qt.cpp
+++ b/src/core/web_contents_delegate_qt.cpp
@@ -655,7 +655,7 @@ void WebContentsDelegateQt::RequestToLockMouse(content::WebContents *web_content
if (last_unlocked_by_target)
web_contents->GotResponseToLockMouseRequest(true);
else
- m_viewClient->runMouseLockPermissionRequest(toQt(web_contents->GetVisibleURL()));
+ m_viewClient->runMouseLockPermissionRequest(toQt(web_contents->GetLastCommittedURL().GetOrigin()));
}
void WebContentsDelegateQt::overrideWebPreferences(content::WebContents *webContents, content::WebPreferences *webPreferences)
@@ -663,14 +663,17 @@ void WebContentsDelegateQt::overrideWebPreferences(content::WebContents *webCont
m_viewClient->webEngineSettings()->overrideWebPreferences(webContents, webPreferences);
}
-QWeakPointer<WebContentsAdapter> WebContentsDelegateQt::createWindow(std::unique_ptr<content::WebContents> new_contents, WindowOpenDisposition disposition, const gfx::Rect& initial_pos, bool user_gesture)
+QSharedPointer<WebContentsAdapter>
+WebContentsDelegateQt::createWindow(std::unique_ptr<content::WebContents> new_contents,
+ WindowOpenDisposition disposition, const gfx::Rect &initial_pos,
+ bool user_gesture)
{
QSharedPointer<WebContentsAdapter> newAdapter = QSharedPointer<WebContentsAdapter>::create(std::move(new_contents));
- m_viewClient->adoptNewWindow(newAdapter, static_cast<WebContentsAdapterClient::WindowOpenDisposition>(disposition), user_gesture, toQt(initial_pos), m_initialTargetUrl);
-
- // If the client didn't reference the adapter, it will be deleted now, and the weak pointer zeroed.
- return newAdapter;
+ return m_viewClient->adoptNewWindow(
+ std::move(newAdapter),
+ static_cast<WebContentsAdapterClient::WindowOpenDisposition>(disposition), user_gesture,
+ toQt(initial_pos), m_initialTargetUrl);
}
void WebContentsDelegateQt::allowCertificateError(const QSharedPointer<CertificateErrorController> &errorController)
@@ -797,6 +800,15 @@ bool WebContentsDelegateQt::TakeFocus(content::WebContents *source, bool reverse
return m_viewClient->passOnFocus(reverse);
}
+void WebContentsDelegateQt::ContentsZoomChange(bool zoom_in)
+{
+ WebContentsAdapter *adapter = webContentsAdapter();
+ if (zoom_in)
+ adapter->setZoomFactor(adapter->currentZoomFactor() + 0.1f);
+ else
+ adapter->setZoomFactor(adapter->currentZoomFactor() - 0.1f);
+}
+
FaviconManager *WebContentsDelegateQt::faviconManager()
{
return m_faviconManager.data();
diff --git a/src/core/web_contents_delegate_qt.h b/src/core/web_contents_delegate_qt.h
index f32b02caf..bfef9a1df 100644
--- a/src/core/web_contents_delegate_qt.h
+++ b/src/core/web_contents_delegate_qt.h
@@ -148,6 +148,7 @@ public:
void RegisterProtocolHandler(content::WebContents* web_contents, const std::string& protocol, const GURL& url, bool user_gesture) override;
void UnregisterProtocolHandler(content::WebContents* web_contents, const std::string& protocol, const GURL& url, bool user_gesture) override;
bool TakeFocus(content::WebContents *source, bool reverse) override;
+ void ContentsZoomChange(bool zoom_in) override;
// WebContentsObserver overrides
void RenderFrameCreated(content::RenderFrameHost *render_frame_host) override;
@@ -200,7 +201,10 @@ public:
base::WeakPtr<WebContentsDelegateQt> AsWeakPtr() { return m_weakPtrFactory.GetWeakPtr(); }
private:
- QWeakPointer<WebContentsAdapter> createWindow(std::unique_ptr<content::WebContents> new_contents, WindowOpenDisposition disposition, const gfx::Rect& initial_pos, bool user_gesture);
+ QSharedPointer<WebContentsAdapter>
+ createWindow(std::unique_ptr<content::WebContents> new_contents,
+ WindowOpenDisposition disposition, const gfx::Rect &initial_pos,
+ bool user_gesture);
void EmitLoadStarted(const QUrl &url, bool isErrorPage = false);
void EmitLoadFinished(bool success, const QUrl &url, bool isErrorPage = false, int errorCode = 0, const QString &errorDescription = QString());
void EmitLoadCommitted();
diff --git a/src/core/web_engine_context.cpp b/src/core/web_engine_context.cpp
index 17742e1d7..a28e469a3 100644
--- a/src/core/web_engine_context.cpp
+++ b/src/core/web_engine_context.cpp
@@ -106,7 +106,7 @@
#include "base/mac/foundation_util.h"
#endif
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
#include "accessibility_activation_observer.h"
#endif
#include "api/qwebengineurlscheme.h"
@@ -125,7 +125,7 @@
#include <QGuiApplication>
#include <QMutex>
#include <QOffscreenSurface>
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
# include <QOpenGLContext>
#endif
#include <QQuickWindow>
@@ -138,7 +138,7 @@
using namespace QtWebEngineCore;
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
QT_BEGIN_NAMESPACE
Q_GUI_EXPORT QOpenGLContext *qt_gl_global_share_context();
QT_END_NAMESPACE
@@ -146,7 +146,7 @@ QT_END_NAMESPACE
namespace {
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
bool usingANGLE()
{
#if defined(Q_OS_WIN)
@@ -179,7 +179,7 @@ bool usingDefaultSGBackend()
return device.isEmpty();
}
-#endif //QT_NO_OPENGL
+#endif // QT_CONFIG(opengl)
#if QT_CONFIG(webengine_pepper_plugins)
void dummyGetPluginCallback(const std::vector<content::WebPluginInfo>&)
{
@@ -208,7 +208,7 @@ bool usingSoftwareDynamicGL()
{
if (QCoreApplication::testAttribute(Qt::AA_UseSoftwareOpenGL))
return true;
-#if defined(Q_OS_WIN) && !defined(QT_NO_OPENGL)
+#if defined(Q_OS_WIN) && QT_CONFIG(opengl)
HMODULE handle = static_cast<HMODULE>(QOpenGLContext::openGLModuleHandle());
wchar_t path[MAX_PATH];
DWORD size = GetModuleFileName(handle, path, MAX_PATH);
@@ -261,6 +261,52 @@ static void cleanupVizProcess()
vizCompositorThreadRunner->CleanupForShutdown(base::BindOnce(&completeVizCleanup));
}
+static QStringList parseEnvCommandLine(const QString &cmdLine)
+{
+ QString arg;
+ QStringList arguments;
+ enum { Parse, Quoted, Unquoted } state = Parse;
+ for (const QChar c : cmdLine) {
+ switch (state) {
+ case Parse:
+ if (c == '"') {
+ state = Quoted;
+ } else if (c != ' ' ) {
+ arg += c;
+ state = Unquoted;
+ }
+ // skips spaces
+ break;
+ case Quoted:
+ if (c == '"') {
+ DCHECK(!arg.isEmpty());
+ state = Unquoted;
+ } else {
+ // includes spaces
+ arg += c;
+ }
+ break;
+ case Unquoted:
+ if (c == '"') {
+ // skips quotes
+ state = Quoted;
+ } else if (c == ' ') {
+ arguments.append(arg);
+ arg.clear();
+ state = Parse;
+ } else {
+ arg += c;
+ }
+ break;
+ }
+ }
+ // last arg
+ if (!arg.isEmpty()) {
+ arguments.append(arg);
+ }
+ return arguments;
+}
+
scoped_refptr<QtWebEngineCore::WebEngineContext> WebEngineContext::m_handle;
bool WebEngineContext::m_destroyed = false;
@@ -559,7 +605,7 @@ WebEngineContext::WebEngineContext()
parsedCommandLine->AppendSwitch(switches::kDisableES3GLContext);
#endif
bool threadedGpu = false;
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
threadedGpu = QOpenGLContext::supportsThreadedOpenGL();
#if defined(Q_OS_MACOS)
// QtBase disabled it when building on 10.14+, unfortunately we still need it
@@ -595,6 +641,9 @@ WebEngineContext::WebEngineContext()
appendToFeatureList(disableFeatures, network::features::kDnsOverHttpsUpgrade.name);
+ // When enabled, event.movement is calculated in blink instead of in browser.
+ appendToFeatureList(disableFeatures, features::kConsolidatedMovementXY.name);
+
// Explicitly tell Chromium about default-on features we do not support
appendToFeatureList(disableFeatures, features::kBackgroundFetch.name);
appendToFeatureList(disableFeatures, features::kSmsReceiver.name);
@@ -643,7 +692,7 @@ WebEngineContext::WebEngineContext()
GLContextHelper::initialize();
const char *glType = 0;
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
const bool tryGL = (usingDefaultSGBackend() && !usingSoftwareDynamicGL() &&
QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL))
@@ -709,7 +758,7 @@ WebEngineContext::WebEngineContext()
qWarning("WebEngineContext used before QtWebEngine::initialize() or OpenGL context creation failed.");
}
}
-#endif
+#endif // QT_CONFIG(opengl)
if (glType) {
parsedCommandLine->AppendSwitchASCII(switches::kUseGL, glType);
@@ -796,7 +845,7 @@ WebEngineContext::WebEngineContext()
m_printJobManager.reset(new printing::PrintJobManager());
#endif
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
m_accessibilityActivationObserver.reset(new AccessibilityActivationObserver());
#endif
@@ -829,7 +878,7 @@ base::CommandLine* WebEngineContext::commandLine() {
QStringList appArgs = QCoreApplication::arguments();
if (qEnvironmentVariableIsSet(kChromiumFlagsEnv)) {
appArgs = appArgs.mid(0, 1); // Take application name and drop the rest
- appArgs.append(QString::fromLocal8Bit(qgetenv(kChromiumFlagsEnv)).split(' '));
+ appArgs.append(parseEnvCommandLine(QString::fromLocal8Bit(qgetenv(kChromiumFlagsEnv))));
}
#ifdef Q_OS_WIN
appArgs.removeAll(QStringLiteral("--enable-webgl-software-rendering"));
diff --git a/src/core/web_engine_context.h b/src/core/web_engine_context.h
index ac0536596..6cbd5c8e5 100644
--- a/src/core/web_engine_context.h
+++ b/src/core/web_engine_context.h
@@ -46,6 +46,7 @@
#include "base/memory/ref_counted.h"
#include "base/values.h"
+#include <QtGui/qtgui-config.h>
#include <QVector>
namespace base {
@@ -142,7 +143,7 @@ private:
std::unique_ptr<ProfileAdapter> m_defaultProfileAdapter;
std::unique_ptr<DevToolsServerQt> m_devtoolsServer;
QVector<ProfileAdapter*> m_profileAdapters;
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
std::unique_ptr<AccessibilityActivationObserver> m_accessibilityActivationObserver;
#endif
diff --git a/src/core/web_event_factory.cpp b/src/core/web_event_factory.cpp
index f37cce6c7..e1db69b16 100644
--- a/src/core/web_event_factory.cpp
+++ b/src/core/web_event_factory.cpp
@@ -71,6 +71,8 @@
#include "ui/events/keycodes/dom/keycode_converter.h"
#include "ui/events/keycodes/keyboard_code_conversion.h"
+#include "render_widget_host_view_qt_delegate.h"
+
#include <QtGui/private/qtgui-config_p.h>
#include <QCoreApplication>
@@ -84,6 +86,8 @@
#endif
#include <QWheelEvent>
+namespace QtWebEngineCore {
+
using namespace blink;
enum class KeyboardDriver { Unknown, Windows, Cocoa, Xkb, Evdev };
@@ -174,8 +178,13 @@ static QString qtTextForKeyEvent(const QKeyEvent *ev, int qtKey, Qt::KeyboardMod
{
QString text = ev->text();
- if ((qtModifiers & Qt::ControlModifier) &&
- (keyboardDriver() == KeyboardDriver::Xkb || keyboardDriver() == KeyboardDriver::Windows)) {
+ if (keyboardDriver() == KeyboardDriver::Xkb && (qtModifiers & Qt::ControlModifier)) {
+ text.clear();
+ }
+
+ // Keep text for Ctrl+Alt key combinations on Windows. It is an alternative for AltGr.
+ if (keyboardDriver() == KeyboardDriver::Windows
+ && (qtModifiers & Qt::ControlModifier) && !(qtModifiers & Qt::AltModifier)) {
text.clear();
}
@@ -1306,6 +1315,42 @@ static inline WebInputEvent::Modifiers modifiersForEvent(const QInputEvent* even
return (WebInputEvent::Modifiers)result;
}
+static inline Qt::KeyboardModifiers keyboardModifiersForModifier(unsigned int modifier)
+{
+ Qt::KeyboardModifiers modifiers = {};
+ if (modifier & WebInputEvent::kControlKey)
+ modifiers |= Qt::ControlModifier;
+ if (modifier & WebInputEvent::kMetaKey)
+ modifiers |= Qt::MetaModifier;
+ if (modifier & WebInputEvent::kShiftKey)
+ modifiers |= Qt::ShiftModifier;
+ if (modifier & WebInputEvent::kAltKey)
+ modifiers |= Qt::AltModifier;
+ if (modifier & WebInputEvent::kIsKeyPad)
+ modifiers |= Qt::KeypadModifier;
+
+ if (keyboardDriver() == KeyboardDriver::Cocoa && !qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) {
+ bool controlModifier = modifiers.testFlag(Qt::ControlModifier);
+ bool metaModifier = modifiers.testFlag(Qt::MetaModifier);
+ modifiers.setFlag(Qt::ControlModifier, metaModifier);
+ modifiers.setFlag(Qt::MetaModifier, controlModifier);
+ }
+
+ return modifiers;
+}
+
+static inline Qt::MouseButtons mouseButtonsForModifier(unsigned int modifier)
+{
+ Qt::MouseButtons buttons = {};
+ if (modifier & WebInputEvent::kLeftButtonDown)
+ buttons |= Qt::LeftButton;
+ if (modifier & WebInputEvent::kRightButtonDown)
+ buttons |= Qt::RightButton;
+ if (modifier & WebInputEvent::kMiddleButtonDown)
+ buttons |= Qt::MiddleButton;
+ return buttons;
+}
+
static WebInputEvent::Type webEventTypeForEvent(const QEvent* event)
{
switch (event->type()) {
@@ -1471,6 +1516,14 @@ static void setBlinkWheelEventDelta(blink::WebMouseWheelEvent &webEvent)
webEvent.delta_y = webEvent.wheel_ticks_y * wheelScrollLines * cDefaultQtScrollStep;
}
+static QPoint getWheelEventDelta(const blink::WebGestureEvent &webEvent)
+{
+ static const float cDefaultQtScrollStep = 20.f;
+ static const int wheelScrollLines = QGuiApplication::styleHints()->wheelScrollLines();
+ return QPoint(webEvent.data.scroll_update.delta_x * QWheelEvent::DefaultDeltasPerStep / (wheelScrollLines * cDefaultQtScrollStep),
+ webEvent.data.scroll_update.delta_y * QWheelEvent::DefaultDeltasPerStep / (wheelScrollLines * cDefaultQtScrollStep));
+}
+
blink::WebMouseWheelEvent::Phase toBlinkPhase(QWheelEvent *ev)
{
switch (ev->phase()) {
@@ -1552,6 +1605,26 @@ bool WebEventFactory::coalesceWebWheelEvent(blink::WebMouseWheelEvent &webEvent,
return true;
}
+static QPointF toQt(blink::WebFloatPoint p)
+{
+ return QPointF(p.x, p.y);
+}
+
+void WebEventFactory::sendUnhandledWheelEvent(const blink::WebGestureEvent &event,
+ RenderWidgetHostViewQtDelegate *delegate)
+{
+ Q_ASSERT(event.GetType() == blink::WebInputEvent::kGestureScrollUpdate);
+
+ QWheelEvent ev(toQt(event.PositionInWidget()),
+ toQt(event.PositionInScreen()),
+ QPoint(event.data.scroll_update.delta_x, event.data.scroll_update.delta_y),
+ getWheelEventDelta(event),
+ mouseButtonsForModifier(event.GetModifiers()),
+ keyboardModifiersForModifier(event.GetModifiers()),
+ Qt::NoScrollPhase, false);
+ delegate->unhandledWheelEvent(&ev);
+}
+
content::NativeWebKeyboardEvent WebEventFactory::toWebKeyboardEvent(QKeyEvent *ev)
{
content::NativeWebKeyboardEvent webKitEvent(reinterpret_cast<gfx::NativeEvent>(ev));
@@ -1686,3 +1759,5 @@ bool WebEventFactory::getEditCommand(QKeyEvent *event, std::string *editCommand)
return false;
}
+
+} // namespace QtWebEngineCore
diff --git a/src/core/web_event_factory.h b/src/core/web_event_factory.h
index 526202cfb..390502a9d 100644
--- a/src/core/web_event_factory.h
+++ b/src/core/web_event_factory.h
@@ -63,6 +63,10 @@ class QNativeGestureEvent;
#endif
QT_END_NAMESPACE
+namespace QtWebEngineCore {
+
+class RenderWidgetHostViewQtDelegate;
+
class WebEventFactory {
public:
@@ -77,9 +81,11 @@ public:
#endif
static blink::WebMouseWheelEvent toWebWheelEvent(QWheelEvent *);
static bool coalesceWebWheelEvent(blink::WebMouseWheelEvent &, QWheelEvent *);
+ static void sendUnhandledWheelEvent(const blink::WebGestureEvent &, RenderWidgetHostViewQtDelegate *);
static content::NativeWebKeyboardEvent toWebKeyboardEvent(QKeyEvent*);
static bool getEditCommand(QKeyEvent *event, std::string *editCommand);
};
+} // namespace QtWebEngineCore
#endif // WEB_EVENT_FACTORY_H
diff --git a/src/pdf/api/qpdfdocument.h b/src/pdf/api/qpdfdocument.h
index f80a7832b..54ca687fa 100644
--- a/src/pdf/api/qpdfdocument.h
+++ b/src/pdf/api/qpdfdocument.h
@@ -114,6 +114,7 @@ public:
QImage render(int page, QSize imageSize, QPdfDocumentRenderOptions options = QPdfDocumentRenderOptions());
Q_INVOKABLE QPdfSelection getSelection(int page, QPointF start, QPointF end);
+ Q_INVOKABLE QPdfSelection getSelectionAtIndex(int page, int startIndex, int maxLength);
Q_INVOKABLE QPdfSelection getAllText(int page);
Q_SIGNALS:
@@ -127,6 +128,7 @@ private:
friend class QPdfLinkModelPrivate;
friend class QPdfSearchModel;
friend class QPdfSearchModelPrivate;
+ friend class QQuickPdfSelection;
Q_PRIVATE_SLOT(d, void _q_tryLoadingWithSizeFromContentHeader())
Q_PRIVATE_SLOT(d, void _q_copyFromSequentialSourceDevice())
diff --git a/src/pdf/api/qpdfdocument_p.h b/src/pdf/api/qpdfdocument_p.h
index b69b6f19e..9a737766b 100644
--- a/src/pdf/api/qpdfdocument_p.h
+++ b/src/pdf/api/qpdfdocument_p.h
@@ -66,7 +66,7 @@ public:
QPdfMutexLocker();
};
-class QPdfDocumentPrivate: public FPDF_FILEACCESS, public FX_FILEAVAIL, public FX_DOWNLOADHINTS
+class Q_PDF_PRIVATE_EXPORT QPdfDocumentPrivate: public FPDF_FILEACCESS, public FX_FILEAVAIL, public FX_DOWNLOADHINTS
{
public:
QPdfDocumentPrivate();
@@ -106,6 +106,15 @@ public:
static void fpdf_AddSegment(struct _FX_DOWNLOADHINTS* pThis, size_t offset, size_t size);
void updateLastError();
QString getText(FPDF_TEXTPAGE textPage, int startIndex, int count);
+ QPointF getCharPosition(FPDF_TEXTPAGE textPage, double pageHeight, int charIndex);
+ QRectF getCharBox(FPDF_TEXTPAGE textPage, double pageHeight, int charIndex);
+
+ struct TextPosition {
+ QPointF position;
+ qreal height = 0;
+ int charIndex = -1;
+ };
+ TextPosition hitTest(int page, QPointF position);
};
QT_END_NAMESPACE
diff --git a/src/pdf/api/qpdflinkmodel_p_p.h b/src/pdf/api/qpdflinkmodel_p_p.h
index 3e44f1651..0454d6755 100644
--- a/src/pdf/api/qpdflinkmodel_p_p.h
+++ b/src/pdf/api/qpdflinkmodel_p_p.h
@@ -74,7 +74,7 @@ public:
// destination inside PDF
int page = -1; // -1 means look at the url instead
QPointF location;
- qreal zoom = 1;
+ qreal zoom = 0; // 0 means no specified zoom: don't change when clicking
// web destination
QUrl url;
diff --git a/src/pdf/api/qpdfselection.h b/src/pdf/api/qpdfselection.h
index 5a6a1cddc..9d91d46c7 100644
--- a/src/pdf/api/qpdfselection.h
+++ b/src/pdf/api/qpdfselection.h
@@ -53,7 +53,10 @@ class Q_PDF_EXPORT QPdfSelection
Q_GADGET
Q_PROPERTY(bool valid READ isValid)
Q_PROPERTY(QVector<QPolygonF> bounds READ bounds)
+ Q_PROPERTY(QRectF boundingRectangle READ boundingRectangle)
Q_PROPERTY(QString text READ text)
+ Q_PROPERTY(int startIndex READ startIndex)
+ Q_PROPERTY(int endIndex READ endIndex)
public:
~QPdfSelection();
@@ -65,13 +68,16 @@ public:
bool isValid() const;
QVector<QPolygonF> bounds() const;
QString text() const;
+ QRectF boundingRectangle() const;
+ int startIndex() const;
+ int endIndex() const;
#if QT_CONFIG(clipboard)
void copyToClipboard(QClipboard::Mode mode = QClipboard::Clipboard) const;
#endif
private:
QPdfSelection();
- QPdfSelection(const QString &text, QVector<QPolygonF> bounds);
+ QPdfSelection(const QString &text, QVector<QPolygonF> bounds, QRectF boundingRect, int startIndex, int endIndex);
QPdfSelection(QPdfSelectionPrivate *d);
friend class QPdfDocument;
friend class QQuickPdfSelection;
diff --git a/src/pdf/api/qpdfselection_p.h b/src/pdf/api/qpdfselection_p.h
index 37145f7f9..0577e5a31 100644
--- a/src/pdf/api/qpdfselection_p.h
+++ b/src/pdf/api/qpdfselection_p.h
@@ -46,12 +46,18 @@ class QPdfSelectionPrivate : public QSharedData
{
public:
QPdfSelectionPrivate() = default;
- QPdfSelectionPrivate(const QString &text, QVector<QPolygonF> bounds)
+ QPdfSelectionPrivate(const QString &text, QVector<QPolygonF> bounds, QRectF boundingRect, int startIndex, int endIndex)
: text(text),
- bounds(bounds) { }
+ bounds(bounds),
+ boundingRect(boundingRect),
+ startIndex(startIndex),
+ endIndex(endIndex) { }
QString text;
QVector<QPolygonF> bounds;
+ QRectF boundingRect;
+ int startIndex;
+ int endIndex;
};
QT_END_NAMESPACE
diff --git a/src/pdf/api/qtpdfglobal.h b/src/pdf/api/qtpdfglobal.h
index 223ec4bcb..8b4b0c206 100644
--- a/src/pdf/api/qtpdfglobal.h
+++ b/src/pdf/api/qtpdfglobal.h
@@ -53,6 +53,8 @@ QT_BEGIN_NAMESPACE
# endif
#endif
+#define Q_PDF_PRIVATE_EXPORT Q_PDF_EXPORT
+
QT_END_NAMESPACE
#endif // QTPDFGLOBAL_H
diff --git a/src/pdf/qpdfdocument.cpp b/src/pdf/qpdfdocument.cpp
index 89b27da8b..e4ec363ce 100644
--- a/src/pdf/qpdfdocument.cpp
+++ b/src/pdf/qpdfdocument.cpp
@@ -54,7 +54,7 @@ QT_BEGIN_NAMESPACE
// The library is not thread-safe at all, it has a lot of global variables.
Q_GLOBAL_STATIC_WITH_ARGS(QMutex, pdfMutex, (QMutex::Recursive));
static int libraryRefCount;
-static const double CharacterHitTolerance = 6.0;
+static const double CharacterHitTolerance = 16.0;
Q_LOGGING_CATEGORY(qLcDoc, "qt.pdf.document")
QPdfMutexLocker::QPdfMutexLocker()
@@ -402,6 +402,50 @@ QString QPdfDocumentPrivate::getText(FPDF_TEXTPAGE textPage, int startIndex, int
return QString::fromUtf16(buf.constData(), len - 1);
}
+QPointF QPdfDocumentPrivate::getCharPosition(FPDF_TEXTPAGE textPage, double pageHeight, int charIndex)
+{
+ double x, y;
+ int count = FPDFText_CountChars(textPage);
+ bool ok = FPDFText_GetCharOrigin(textPage, qMin(count - 1, charIndex), &x, &y);
+ if (!ok)
+ return QPointF();
+ return QPointF(x, pageHeight - y);
+}
+
+QRectF QPdfDocumentPrivate::getCharBox(FPDF_TEXTPAGE textPage, double pageHeight, int charIndex)
+{
+ double l, t, r, b;
+ bool ok = FPDFText_GetCharBox(textPage, charIndex, &l, &r, &b, &t);
+ if (!ok)
+ return QRectF();
+ return QRectF(l, pageHeight - t, r - l, t - b);
+}
+
+QPdfDocumentPrivate::TextPosition QPdfDocumentPrivate::hitTest(int page, QPointF position)
+{
+ const QPdfMutexLocker lock;
+ FPDF_PAGE pdfPage = FPDF_LoadPage(doc, page);
+ double pageHeight = FPDF_GetPageHeight(pdfPage);
+ FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage);
+ int hitIndex = FPDFText_GetCharIndexAtPos(textPage, position.x(), pageHeight - position.y(),
+ CharacterHitTolerance, CharacterHitTolerance);
+ if (hitIndex >= 0) {
+ QPointF charPos = getCharPosition(textPage, pageHeight, hitIndex);
+ if (!charPos.isNull()) {
+ QRectF charBox = getCharBox(textPage, pageHeight, hitIndex);
+ // If the given position is past the end of the line, i.e. if the right edge of the found character's
+ // bounding box is closer to it than the left edge is, we say that we "hit" the next character index after
+ if (qAbs(charBox.right() - position.x()) < qAbs(charPos.x() - position.x())) {
+ charPos.setX(charBox.right());
+ ++hitIndex;
+ }
+ qCDebug(qLcDoc) << "on page" << page << "@" << position << "got char position" << charPos << "index" << hitIndex;
+ return { charPos, charBox.height(), hitIndex };
+ }
+ }
+ return {};
+}
+
/*!
\class QPdfDocument
\since 5.10
@@ -748,29 +792,80 @@ QPdfSelection QPdfDocument::getSelection(int page, QPointF start, QPointF end)
if (startIndex >= 0 && endIndex != startIndex) {
if (startIndex > endIndex)
qSwap(startIndex, endIndex);
- int count = endIndex - startIndex + 1;
+
+ // If the given end position is past the end of the line, i.e. if the right edge of the last character's
+ // bounding box is closer to it than the left edge is, then extend the char range by one
+ QRectF endCharBox = d->getCharBox(textPage, pageHeight, endIndex);
+ if (qAbs(endCharBox.right() - end.x()) < qAbs(endCharBox.x() - end.x()))
+ ++endIndex;
+
+ int count = endIndex - startIndex;
QString text = d->getText(textPage, startIndex, count);
QVector<QPolygonF> bounds;
+ QRectF hull;
int rectCount = FPDFText_CountRects(textPage, startIndex, endIndex - startIndex);
for (int i = 0; i < rectCount; ++i) {
double l, r, b, t;
FPDFText_GetRect(textPage, i, &l, &t, &r, &b);
- QPolygonF poly;
- poly << QPointF(l, pageHeight - t);
- poly << QPointF(r, pageHeight - t);
- poly << QPointF(r, pageHeight - b);
- poly << QPointF(l, pageHeight - b);
- poly << QPointF(l, pageHeight - t);
- bounds << poly;
+ QRectF rect(l, pageHeight - t, r - l, t - b);
+ if (hull.isNull())
+ hull = rect;
+ else
+ hull = hull.united(rect);
+ bounds << QPolygonF(rect);
}
qCDebug(qLcDoc) << page << start << "->" << end << "found" << startIndex << "->" << endIndex << text;
- return QPdfSelection(text, bounds);
+ return QPdfSelection(text, bounds, hull, startIndex, endIndex);
}
qCDebug(qLcDoc) << page << start << "->" << end << "nothing found";
return QPdfSelection();
}
+/*!
+ Returns information about the text on the given \a page that can be found
+ beginning at the given \a startIndex with at most \l maxLength characters.
+*/
+QPdfSelection QPdfDocument::getSelectionAtIndex(int page, int startIndex, int maxLength)
+{
+
+ if (page < 0 || startIndex < 0 || maxLength < 0)
+ return {};
+ const QPdfMutexLocker lock;
+ FPDF_PAGE pdfPage = FPDF_LoadPage(d->doc, page);
+ double pageHeight = FPDF_GetPageHeight(pdfPage);
+ FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage);
+ int pageCount = FPDFText_CountChars(textPage);
+ if (startIndex >= pageCount)
+ return QPdfSelection();
+ QVector<QPolygonF> bounds;
+ QRectF hull;
+ int rectCount = 0;
+ QString text;
+ if (maxLength > 0) {
+ text = d->getText(textPage, startIndex, maxLength);
+ rectCount = FPDFText_CountRects(textPage, startIndex, text.length());
+ for (int i = 0; i < rectCount; ++i) {
+ double l, r, b, t;
+ FPDFText_GetRect(textPage, i, &l, &t, &r, &b);
+ QRectF rect(l, pageHeight - t, r - l, t - b);
+ if (hull.isNull())
+ hull = rect;
+ else
+ hull = hull.united(rect);
+ bounds << QPolygonF(rect);
+ }
+ }
+ if (bounds.isEmpty())
+ hull = QRectF(d->getCharPosition(textPage, pageHeight, startIndex), QSizeF());
+ qCDebug(qLcDoc) << "on page" << page << "at index" << startIndex << "maxLength" << maxLength
+ << "got" << text.length() << "chars," << rectCount << "rects within" << hull;
+ return QPdfSelection(text, bounds, hull, startIndex, startIndex + text.length());
+}
+
+/*!
+ Returns all the text and its bounds on the given \a page.
+*/
QPdfSelection QPdfDocument::getAllText(int page)
{
const QPdfMutexLocker lock;
@@ -782,20 +877,20 @@ QPdfSelection QPdfDocument::getAllText(int page)
return QPdfSelection();
QString text = d->getText(textPage, 0, count);
QVector<QPolygonF> bounds;
+ QRectF hull;
int rectCount = FPDFText_CountRects(textPage, 0, count);
for (int i = 0; i < rectCount; ++i) {
double l, r, b, t;
FPDFText_GetRect(textPage, i, &l, &t, &r, &b);
- QPolygonF poly;
- poly << QPointF(l, pageHeight - t);
- poly << QPointF(r, pageHeight - t);
- poly << QPointF(r, pageHeight - b);
- poly << QPointF(l, pageHeight - b);
- poly << QPointF(l, pageHeight - t);
- bounds << poly;
+ QRectF rect(l, pageHeight - t, r - l, t - b);
+ if (hull.isNull())
+ hull = rect;
+ else
+ hull = hull.united(rect);
+ bounds << QPolygonF(rect);
}
- qCDebug(qLcDoc) << "on page" << page << "got" << count << "chars" << rectCount << "rects";
- return QPdfSelection(text, bounds);
+ qCDebug(qLcDoc) << "on page" << page << "got" << count << "chars," << rectCount << "rects within" << hull;
+ return QPdfSelection(text, bounds, hull, 0, count);
}
QT_END_NAMESPACE
diff --git a/src/pdf/qpdfselection.cpp b/src/pdf/qpdfselection.cpp
index e334f0fb6..5f0ee3b20 100644
--- a/src/pdf/qpdfselection.cpp
+++ b/src/pdf/qpdfselection.cpp
@@ -67,8 +67,8 @@ QPdfSelection::QPdfSelection()
\a text string, and which take up space on the page within the polygon
regions given in \a bounds.
*/
-QPdfSelection::QPdfSelection(const QString &text, QVector<QPolygonF> bounds)
- : d(new QPdfSelectionPrivate(text, bounds))
+QPdfSelection::QPdfSelection(const QString &text, QVector<QPolygonF> bounds, QRectF boundingRect, int startIndex, int endIndex)
+ : d(new QPdfSelectionPrivate(text, bounds, boundingRect, startIndex, endIndex))
{
}
@@ -134,6 +134,36 @@ QString QPdfSelection::text() const
return d->text;
}
+/*!
+ \property rect QPdfSelection::boundingRectangle
+
+ This property holds the overall bounding rectangle (convex hull) around \l bounds.
+*/
+QRectF QPdfSelection::boundingRectangle() const
+{
+ return d->boundingRect;
+}
+
+/*!
+ \property int QPdfSelection::startIndex
+
+ This property holds the index at the beginning of \l text within the full text on the page.
+*/
+int QPdfSelection::startIndex() const
+{
+ return d->startIndex;
+}
+
+/*!
+ \property int QPdfSelection::endIndex
+
+ This property holds the index at the end of \l text within the full text on the page.
+*/
+int QPdfSelection::endIndex() const
+{
+ return d->endIndex;
+}
+
#if QT_CONFIG(clipboard)
/*!
Copies \l text to the \l {QGuiApplication::clipboard()}{system clipboard}.
diff --git a/src/pdf/quick/plugin.cpp b/src/pdf/quick/plugin.cpp
index 670fe0bf9..b082fcb4a 100644
--- a/src/pdf/quick/plugin.cpp
+++ b/src/pdf/quick/plugin.cpp
@@ -43,6 +43,7 @@
#include "qquickpdfnavigationstack_p.h"
#include "qquickpdfsearchmodel_p.h"
#include "qquickpdfselection_p.h"
+#include "qquicktableviewextra_p.h"
QT_BEGIN_NAMESPACE
@@ -89,6 +90,7 @@ public:
qmlRegisterType<QQuickPdfNavigationStack>(uri, 5, 15, "PdfNavigationStack");
qmlRegisterType<QQuickPdfSearchModel>(uri, 5, 15, "PdfSearchModel");
qmlRegisterType<QQuickPdfSelection>(uri, 5, 15, "PdfSelection");
+ qmlRegisterType<QQuickTableViewExtra>(uri, 5, 15, "TableViewExtra");
qmlRegisterType(QUrl("qrc:/qt-project.org/qtpdf/qml/PdfPageView.qml"), uri, 5, 15, "PdfPageView");
qmlRegisterType(QUrl("qrc:/qt-project.org/qtpdf/qml/PdfMultiPageView.qml"), uri, 5, 15, "PdfMultiPageView");
diff --git a/src/pdf/quick/qml/PdfMultiPageView.qml b/src/pdf/quick/qml/PdfMultiPageView.qml
index 70bb5454f..71485c214 100644
--- a/src/pdf/quick/qml/PdfMultiPageView.qml
+++ b/src/pdf/quick/qml/PdfMultiPageView.qml
@@ -48,15 +48,15 @@ Item {
property string selectedText
function selectAll() {
- var currentItem = tableView.itemAtPos(0, tableView.contentY + root.height / 2)
- if (currentItem !== null)
+ var currentItem = tableHelper.itemAtCell(tableHelper.cellAtPos(root.width / 2, root.height / 2))
+ if (currentItem)
currentItem.selection.selectAll()
}
function copySelectionToClipboard() {
- var currentItem = tableView.itemAtPos(0, tableView.contentY + root.height / 2)
+ var currentItem = tableHelper.itemAtCell(tableHelper.cellAtPos(root.width / 2, root.height / 2))
if (debug)
console.log("currentItem", currentItem, "sel", currentItem.selection.text)
- if (currentItem !== null)
+ if (currentItem)
currentItem.selection.copyToClipboard()
}
@@ -69,14 +69,19 @@ Item {
function goToPage(page) {
if (page === navigationStack.currentPage)
return
- goToLocation(page, Qt.point(0, 0), 0)
+ goToLocation(page, Qt.point(-1, -1), 0)
}
function goToLocation(page, location, zoom) {
- if (zoom > 0)
+ if (zoom > 0) {
+ navigationStack.jumping = true // don't call navigationStack.update() because we will push() instead
root.renderScale = zoom
- navigationStack.push(page, location, zoom)
- searchModel.currentPage = page
+ tableView.forceLayout() // but do ensure that the table layout is correct before we try to jump
+ navigationStack.jumping = false
+ }
+ navigationStack.push(page, location, zoom) // actually jump
}
+ property vector2d jumpLocationMargin: Qt.vector2d(10, 10) // px from top-left corner
+ property int currentPageRenderingStatus: Image.Null
// page scaling
property real renderScale: 1
@@ -115,29 +120,27 @@ Item {
id: tableView
anchors.fill: parent
anchors.leftMargin: 2
- model: root.document === undefined ? 0 : root.document.pageCount
+ model: modelInUse && root.document !== undefined ? root.document.pageCount : 0
+ // workaround to make TableView do scheduleRebuildTable(RebuildOption::All) in cases when forceLayout() doesn't
+ property bool modelInUse: true
+ function rebuild() {
+ modelInUse = false
+ modelInUse = true
+ }
+ // end workaround
rowSpacing: 6
property real rotationNorm: Math.round((360 + (root.pageRotation % 360)) % 360)
property bool rot90: rotationNorm == 90 || rotationNorm == 270
onRot90Changed: forceLayout()
property size firstPagePointSize: document === undefined ? Qt.size(0, 0) : document.pagePointSize(0)
- contentWidth: document === undefined ? 0 : (rot90 ? document.maxPageHeight : document.maxPageWidth) * root.renderScale + vscroll.width + 2
- // workaround for missing function (see https://codereview.qt-project.org/c/qt/qtdeclarative/+/248464)
- function itemAtPos(x, y, includeSpacing) {
- // we don't care about x (assume col 0), and assume includeSpacing is true
- var ret = null
- for (var i = 0; i < contentItem.children.length; ++i) {
- var child = contentItem.children[i];
- if (root.debug)
- console.log(child, "@y", child.y)
- if (child.y < y && (!ret || child.y > ret.y))
- ret = child
- }
- if (root.debug && ret !== null)
- console.log("given y", y, "found", ret, "@", ret.y)
- return ret // the delegate with the largest y that is less than the given y
- }
+ property real pageHolderWidth: Math.max(root.width, document === undefined ? 0 :
+ (rot90 ? document.maxPageHeight : document.maxPageWidth) * root.renderScale)
+ contentWidth: document === undefined ? 0 : pageHolderWidth + vscroll.width + 2
rowHeightProvider: function(row) { return (rot90 ? document.pagePointSize(row).width : document.pagePointSize(row).height) * root.renderScale }
+ TableViewExtra {
+ id: tableHelper
+ tableView: tableView
+ }
delegate: Rectangle {
id: pageHolder
color: root.debug ? "beige" : "transparent"
@@ -147,11 +150,8 @@ Item {
rotation: -90; text: pageHolder.width.toFixed(1) + "x" + pageHolder.height.toFixed(1) + "\n" +
image.width.toFixed(1) + "x" + image.height.toFixed(1)
}
- implicitWidth: Math.max(root.width, (tableView.rot90 ? document.maxPageHeight : document.maxPageWidth) * root.renderScale)
+ implicitWidth: tableView.pageHolderWidth
implicitHeight: tableView.rot90 ? image.width : image.height
- onImplicitWidthChanged: tableView.forceLayout()
- objectName: "page " + index
- property int delegateIndex: row // expose the context property for JS outside of the delegate
property alias selection: selection
Rectangle {
id: paper
@@ -177,6 +177,10 @@ Item {
paper.scale = 1
searchHighlights.update()
}
+ onStatusChanged: {
+ if (index === navigationStack.currentPage)
+ root.currentPageRenderingStatus = status
+ }
}
Shape {
anchors.fill: parent
@@ -276,9 +280,17 @@ Item {
target: null
}
TapHandler {
- id: tapHandler
+ id: mouseClickHandler
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
}
+ TapHandler {
+ id: touchTapHandler
+ acceptedDevices: PointerDevice.TouchScreen
+ onTapped: {
+ selection.clear()
+ selection.forceActiveFocus()
+ }
+ }
Repeater {
model: PdfLinkModel {
id: linkModel
@@ -290,6 +302,7 @@ Item {
y: rect.y * paper.pageScale
width: rect.width * paper.pageScale
height: rect.height * paper.pageScale
+ visible: image.status === Image.Ready
ShapePath {
strokeWidth: style.linkUnderscoreStrokeWidth
strokeColor: style.linkUnderscoreColor
@@ -320,17 +333,18 @@ Item {
}
}
}
- }
- PdfSelection {
- id: selection
- document: root.document
- page: image.currentFrame
- fromPoint: Qt.point(textSelectionDrag.centroid.pressPosition.x / paper.pageScale,
- textSelectionDrag.centroid.pressPosition.y / paper.pageScale)
- toPoint: Qt.point(textSelectionDrag.centroid.position.x / paper.pageScale,
- textSelectionDrag.centroid.position.y / paper.pageScale)
- hold: !textSelectionDrag.active && !tapHandler.pressed
- onTextChanged: root.selectedText = text
+ PdfSelection {
+ id: selection
+ anchors.fill: parent
+ document: root.document
+ page: image.currentFrame
+ renderScale: image.renderScale
+ fromPoint: textSelectionDrag.centroid.pressPosition
+ toPoint: textSelectionDrag.centroid.position
+ hold: !textSelectionDrag.active && !mouseClickHandler.pressed
+ onTextChanged: root.selectedText = text
+ focus: true
+ }
}
}
ScrollBar.vertical: ScrollBar {
@@ -338,42 +352,83 @@ Item {
property bool moved: false
onPositionChanged: moved = true
onActiveChanged: {
- var currentItem = tableView.itemAtPos(0, tableView.contentY + root.height / 2)
- var currentPage = currentItem.delegateIndex
- var currentLocation = Qt.point((tableView.contentX - currentItem.x + root.width / 2) / root.renderScale,
- (tableView.contentY - currentItem.y + root.height / 2) / root.renderScale)
+ var cell = tableHelper.cellAtPos(root.width / 2, root.height / 2)
+ var currentItem = tableHelper.itemAtCell(cell)
+ var currentLocation = Qt.point(0, 0)
+ if (currentItem) { // maybe the delegate wasn't loaded yet
+ currentLocation = Qt.point((tableView.contentX - currentItem.x + jumpLocationMargin.x) / root.renderScale,
+ (tableView.contentY - currentItem.y + jumpLocationMargin.y) / root.renderScale)
+ }
if (active) {
moved = false
- navigationStack.push(currentPage, currentLocation, root.renderScale)
+ // emitJumped false to avoid interrupting a pinch if TableView thinks it should scroll at the same time
+ navigationStack.push(cell.y, currentLocation, root.renderScale, false)
} else if (moved) {
- navigationStack.update(currentPage, currentLocation, root.renderScale)
+ navigationStack.update(cell.y, currentLocation, root.renderScale)
}
}
}
ScrollBar.horizontal: ScrollBar { }
}
onRenderScaleChanged: {
- tableView.forceLayout()
- var currentItem = tableView.itemAtPos(tableView.contentX + root.width / 2, tableView.contentY + root.height / 2)
- if (currentItem !== undefined)
- navigationStack.update(currentItem.delegateIndex, Qt.point(currentItem.x / renderScale, currentItem.y / renderScale), renderScale)
+ // if navigationStack.jumped changes the scale, don't turn around and update the stack again;
+ // and don't force layout either, because positionViewAtCell() will do that
+ if (navigationStack.jumping)
+ return
+ // make TableView rebuild from scratch, because otherwise it doesn't know the delegates are changing size
+ tableView.rebuild()
+ var cell = tableHelper.cellAtPos(root.width / 2, root.height / 2)
+ var currentItem = tableHelper.itemAtCell(cell)
+ if (currentItem) {
+ var currentLocation = Qt.point((tableView.contentX - currentItem.x + jumpLocationMargin.x) / root.renderScale,
+ (tableView.contentY - currentItem.y + jumpLocationMargin.y) / root.renderScale)
+ navigationStack.update(cell.y, currentLocation, renderScale)
+ }
}
PdfNavigationStack {
id: navigationStack
+ property bool jumping: false
+ property int previousPage: 0
onJumped: {
+ jumping = true
root.renderScale = zoom
- tableView.contentX = Math.max(0, location.x - root.width / 2) * root.renderScale
- tableView.contentY = tableView.originY + root.document.heightSumBeforePage(page, tableView.rowSpacing / root.renderScale) * root.renderScale
- if (root.debug) {
- console.log("going to page", page,
- "@y", root.document.heightSumBeforePage(page, tableView.rowSpacing / root.renderScale) * root.renderScale,
- "ended up @", tableView.contentY, "originY is", tableView.originY)
+ if (location.y < 0) {
+ // invalid to indicate that a specific location was not needed,
+ // so attempt to position the new page just as the current page is
+ var currentYOffset = 0
+ var previousPageDelegate = tableHelper.itemAtCell(0, previousPage)
+ if (previousPageDelegate)
+ currentYOffset = tableView.contentY - previousPageDelegate.y
+ tableHelper.positionViewAtRow(page, Qt.AlignTop, currentYOffset)
+ if (root.debug) {
+ console.log("going from page", previousPage, "to", page, "offset", currentYOffset,
+ "ended up @", tableView.contentX.toFixed(1) + ", " + tableView.contentY.toFixed(1))
+ }
+ } else {
+ // jump to a page and position the given location relative to the top-left corner of the viewport
+ var pageSize = root.document.pagePointSize(page)
+ pageSize.width *= root.renderScale
+ pageSize.height *= root.renderScale
+ var xOffsetLimit = Math.max(0, pageSize.width - root.width) / 2
+ var offset = Qt.point(Math.max(-xOffsetLimit, Math.min(xOffsetLimit,
+ location.x * root.renderScale - jumpLocationMargin.x)),
+ Math.max(0, location.y * root.renderScale - jumpLocationMargin.y))
+ tableHelper.positionViewAtCell(0, page, Qt.AlignLeft | Qt.AlignTop, offset)
+ if (root.debug) {
+ console.log("going to zoom", zoom, "loc", location, "on page", page,
+ "ended up @", tableView.contentX.toFixed(1) + ", " + tableView.contentY.toFixed(1))
+ }
}
+ jumping = false
+ previousPage = page
}
+ onCurrentPageChanged: searchModel.currentPage = currentPage
}
PdfSearchModel {
id: searchModel
document: root.document === undefined ? null : root.document
- onCurrentPageChanged: if (currentPage != navigationStack.currentPage) root.goToPage(currentPage)
+ // TODO maybe avoid jumping if the result is already fully visible in the viewport
+ onCurrentResultBoundingRectChanged: root.goToLocation(currentPage,
+ Qt.point(currentResultBoundingRect.x, currentResultBoundingRect.y), 0)
}
}
diff --git a/src/pdf/quick/qml/PdfScrollablePageView.qml b/src/pdf/quick/qml/PdfScrollablePageView.qml
index 6076e57df..51d9e530d 100644
--- a/src/pdf/quick/qml/PdfScrollablePageView.qml
+++ b/src/pdf/quick/qml/PdfScrollablePageView.qml
@@ -133,32 +133,29 @@ Flickable {
navigationStack.update(navigationStack.currentPage, currentLocation, root.renderScale)
}
- PdfSelection {
- id: selection
- document: root.document
- page: navigationStack.currentPage
- fromPoint: Qt.point(textSelectionDrag.centroid.pressPosition.x / image.pageScale,
- textSelectionDrag.centroid.pressPosition.y / image.pageScale)
- toPoint: Qt.point(textSelectionDrag.centroid.position.x / image.pageScale,
- textSelectionDrag.centroid.position.y / image.pageScale)
- hold: !textSelectionDrag.active && !tapHandler.pressed
- }
-
PdfSearchModel {
id: searchModel
document: root.document === undefined ? null : root.document
- onCurrentPageChanged: root.goToPage(currentPage)
+ // TODO maybe avoid jumping if the result is already fully visible in the viewport
+ onCurrentResultBoundingRectChanged: root.goToLocation(currentPage,
+ Qt.point(currentResultBoundingRect.x, currentResultBoundingRect.y), 0)
}
PdfNavigationStack {
id: navigationStack
onJumped: {
root.renderScale = zoom
- root.contentX = Math.max(0, location.x * root.renderScale - root.width / 2)
- root.contentY = Math.max(0, location.y * root.renderScale - root.height / 2)
- if (root.debug)
+ var dx = Math.max(0, location.x * root.renderScale - root.width / 2) - root.contentX
+ var dy = Math.max(0, location.y * root.renderScale - root.height / 2) - root.contentY
+ // don't jump if location is in the viewport already, i.e. if the "error" between desired and actual contentX/Y is small
+ if (Math.abs(dx) > root.width / 3)
+ root.contentX += dx
+ if (Math.abs(dy) > root.height / 3)
+ root.contentY += dy
+ if (root.debug) {
console.log("going to zoom", zoom, "loc", location,
"on page", page, "ended up @", root.contentX + ", " + root.contentY)
+ }
}
onCurrentPageChanged: searchModel.currentPage = currentPage
}
@@ -246,9 +243,29 @@ Flickable {
target: null
}
TapHandler {
- id: tapHandler
+ id: mouseClickHandler
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
}
+ TapHandler {
+ id: touchTapHandler
+ acceptedDevices: PointerDevice.TouchScreen
+ onTapped: {
+ selection.clear()
+ selection.focus = true
+ }
+ }
+ }
+
+ PdfSelection {
+ id: selection
+ anchors.fill: parent
+ document: root.document
+ page: navigationStack.currentPage
+ renderScale: image.pageScale
+ fromPoint: textSelectionDrag.centroid.pressPosition
+ toPoint: textSelectionDrag.centroid.position
+ hold: !textSelectionDrag.active && !mouseClickHandler.pressed
+ focus: true
}
PinchHandler {
diff --git a/src/pdf/quick/qquickpdfdocument.cpp b/src/pdf/quick/qquickpdfdocument.cpp
index 3d5f0fa10..ab5910523 100644
--- a/src/pdf/quick/qquickpdfdocument.cpp
+++ b/src/pdf/quick/qquickpdfdocument.cpp
@@ -38,7 +38,6 @@
#include <QQuickItem>
#include <QQmlEngine>
#include <QStandardPaths>
-#include <private/qguiapplication_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/pdf/quick/qquickpdflinkmodel.cpp b/src/pdf/quick/qquickpdflinkmodel.cpp
index f2ff3fd22..4f3958337 100644
--- a/src/pdf/quick/qquickpdflinkmodel.cpp
+++ b/src/pdf/quick/qquickpdflinkmodel.cpp
@@ -38,7 +38,6 @@
#include <QQuickItem>
#include <QQmlEngine>
#include <QStandardPaths>
-#include <private/qguiapplication_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/pdf/quick/qquickpdfnavigationstack.cpp b/src/pdf/quick/qquickpdfnavigationstack.cpp
index 7ba317557..044023ef6 100644
--- a/src/pdf/quick/qquickpdfnavigationstack.cpp
+++ b/src/pdf/quick/qquickpdfnavigationstack.cpp
@@ -90,6 +90,8 @@ void QQuickPdfNavigationStack::forward()
if (forwardAvailableWas != forwardAvailable())
emit forwardAvailableChanged();
m_changing = false;
+ qCDebug(qLcNav) << "forward: index" << m_currentHistoryIndex << "page" << currentPage()
+ << "@" << currentLocation() << "zoom" << currentZoom();
}
/*!
@@ -120,6 +122,8 @@ void QQuickPdfNavigationStack::back()
if (!forwardAvailableWas)
emit forwardAvailableChanged();
m_changing = false;
+ qCDebug(qLcNav) << "back: index" << m_currentHistoryIndex << "page" << currentPage()
+ << "@" << currentLocation() << "zoom" << currentZoom();
}
/*!
@@ -163,13 +167,14 @@ qreal QQuickPdfNavigationStack::currentZoom() const
\qmlmethod void PdfNavigationStack::push(int page, point location, qreal zoom)
Adds the given destination, consisting of \a page, \a location and \a zoom,
- to the history of visited locations.
+ to the history of visited locations. If \a emitJumped is \c false, the
+ \l jumped() signal will not be emitted.
If forwardAvailable is \c true, calling this function represents a branch
in the timeline which causes the "future" to be lost, and therefore
forwardAvailable will change to \c false.
*/
-void QQuickPdfNavigationStack::push(int page, QPointF location, qreal zoom)
+void QQuickPdfNavigationStack::push(int page, QPointF location, qreal zoom, bool emitJumped)
{
if (page == currentPage() && location == currentLocation() && zoom == currentZoom())
return;
@@ -192,7 +197,8 @@ void QQuickPdfNavigationStack::push(int page, QPointF location, qreal zoom)
emit backAvailableChanged();
if (forwardAvailableWas)
emit forwardAvailableChanged();
- emit jumped(page, location, zoom);
+ if (emitJumped)
+ emit jumped(page, location, zoom);
qCDebug(qLcNav) << "push: index" << m_currentHistoryIndex << "page" << page
<< "@" << location << "zoom" << zoom << "-> history" <<
[this]() {
diff --git a/src/pdf/quick/qquickpdfnavigationstack_p.h b/src/pdf/quick/qquickpdfnavigationstack_p.h
index 8d7102fb1..0d88d62fd 100644
--- a/src/pdf/quick/qquickpdfnavigationstack_p.h
+++ b/src/pdf/quick/qquickpdfnavigationstack_p.h
@@ -67,7 +67,7 @@ class QQuickPdfNavigationStack : public QObject
public:
explicit QQuickPdfNavigationStack(QObject *parent = nullptr);
- Q_INVOKABLE void push(int page, QPointF location, qreal zoom);
+ Q_INVOKABLE void push(int page, QPointF location, qreal zoom, bool emitJumped = true);
Q_INVOKABLE void update(int page, QPointF location, qreal zoom);
Q_INVOKABLE void forward();
Q_INVOKABLE void back();
diff --git a/src/pdf/quick/qquickpdfsearchmodel.cpp b/src/pdf/quick/qquickpdfsearchmodel.cpp
index a4b457841..1f62fbad0 100644
--- a/src/pdf/quick/qquickpdfsearchmodel.cpp
+++ b/src/pdf/quick/qquickpdfsearchmodel.cpp
@@ -116,6 +116,29 @@ QVector<QPolygonF> QQuickPdfSearchModel::currentResultBoundingPolygons() const
return ret;
}
+/*!
+ \qmlproperty point PdfSearchModel::currentResultBoundingRect
+
+ The bounding box containing all \l currentResultBoundingPolygons.
+
+ When this property changes, a scrollable view should automatically scroll
+ itself in such a way as to ensure that this region is visible; for example,
+ it could try to position the upper-left corner near the upper-left of its
+ own viewport, subject to the constraints of the scrollable area.
+*/
+QRectF QQuickPdfSearchModel::currentResultBoundingRect() const
+{
+ QRectF ret;
+ const auto &results = const_cast<QQuickPdfSearchModel *>(this)->resultsOnPage(m_currentPage);
+ if (m_currentResult < 0 || m_currentResult >= results.count())
+ return ret;
+ auto rects = results[m_currentResult].rectangles();
+ ret = rects.takeFirst();
+ for (auto rect : rects)
+ ret = ret.united(rect);
+ return ret;
+}
+
void QQuickPdfSearchModel::onResultsChanged()
{
emit currentPageBoundingPolygonsChanged();
@@ -266,6 +289,7 @@ void QQuickPdfSearchModel::setCurrentResult(int currentResult)
m_currentResult = currentResult;
emit currentResultChanged();
emit currentResultBoundingPolygonsChanged();
+ emit currentResultBoundingRectChanged();
}
/*!
diff --git a/src/pdf/quick/qquickpdfsearchmodel_p.h b/src/pdf/quick/qquickpdfsearchmodel_p.h
index 3e05f80e3..66fc583d9 100644
--- a/src/pdf/quick/qquickpdfsearchmodel_p.h
+++ b/src/pdf/quick/qquickpdfsearchmodel_p.h
@@ -64,6 +64,7 @@ class QQuickPdfSearchModel : public QPdfSearchModel
Q_PROPERTY(int currentResult READ currentResult WRITE setCurrentResult NOTIFY currentResultChanged)
Q_PROPERTY(QVector<QPolygonF> currentPageBoundingPolygons READ currentPageBoundingPolygons NOTIFY currentPageBoundingPolygonsChanged)
Q_PROPERTY(QVector<QPolygonF> currentResultBoundingPolygons READ currentResultBoundingPolygons NOTIFY currentResultBoundingPolygonsChanged)
+ Q_PROPERTY(QRectF currentResultBoundingRect READ currentResultBoundingRect NOTIFY currentResultBoundingRectChanged)
public:
explicit QQuickPdfSearchModel(QObject *parent = nullptr);
@@ -81,6 +82,7 @@ public:
QVector<QPolygonF> currentPageBoundingPolygons() const;
QVector<QPolygonF> currentResultBoundingPolygons() const;
+ QRectF currentResultBoundingRect() const;
signals:
void documentChanged();
@@ -88,6 +90,7 @@ signals:
void currentResultChanged();
void currentPageBoundingPolygonsChanged();
void currentResultBoundingPolygonsChanged();
+ void currentResultBoundingRectChanged();
private:
void updateResults();
diff --git a/src/pdf/quick/qquickpdfselection.cpp b/src/pdf/quick/qquickpdfselection.cpp
index 5371e85e5..23fbb80b9 100644
--- a/src/pdf/quick/qquickpdfselection.cpp
+++ b/src/pdf/quick/qquickpdfselection.cpp
@@ -37,13 +37,20 @@
#include "qquickpdfselection_p.h"
#include "qquickpdfdocument_p.h"
#include <QClipboard>
+#include <QGuiApplication>
+#include <QLoggingCategory>
#include <QQuickItem>
#include <QQmlEngine>
+#include <QRegularExpression>
#include <QStandardPaths>
-#include <private/qguiapplication_p.h>
+#include <QtPdf/private/qpdfdocument_p.h>
+
+Q_LOGGING_CATEGORY(qLcIm, "qt.pdf.im")
QT_BEGIN_NAMESPACE
+static const QRegularExpression WordDelimiter("\\s");
+
/*!
\qmltype PdfSelection
\instantiates QQuickPdfSelection
@@ -54,14 +61,29 @@ QT_BEGIN_NAMESPACE
PdfSelection provides the text string and its geometry within a bounding box
from one point to another.
+
+ To modify the selection using the mouse, bind \l fromPoint and \l toPoint
+ to the suitable properties of an input handler so that they will be set to
+ the positions where the drag gesture begins and ends, respectively; and
+ bind the \l hold property so that it will be set to \c true during the drag
+ gesture and \c false when the gesture ends.
+
+ PdfSelection also directly handles Input Method queries so that text
+ selection handles can be used on platforms such as iOS. For this purpose,
+ it must have keyboard focus.
*/
/*!
Constructs a SearchModel.
*/
-QQuickPdfSelection::QQuickPdfSelection(QObject *parent)
- : QObject(parent)
+QQuickPdfSelection::QQuickPdfSelection(QQuickItem *parent)
+ : QQuickItem(parent)
{
+#if QT_CONFIG(im)
+ setFlags(ItemIsFocusScope | ItemAcceptsInputMethod);
+ // workaround to get Copy instead of Paste on the popover menu (QTBUG-83811)
+ setProperty("qt_im_readonly", QVariant(true));
+#endif
}
QQuickPdfDocument *QQuickPdfSelection::document() const
@@ -124,6 +146,24 @@ QVector<QPolygonF> QQuickPdfSelection::geometry() const
return m_geometry;
}
+void QQuickPdfSelection::clear()
+{
+ m_hitPoint = QPointF();
+ m_fromPoint = QPointF();
+ m_toPoint = QPointF();
+ m_heightAtAnchor = 0;
+ m_heightAtCursor = 0;
+ m_fromCharIndex = -1;
+ m_toCharIndex = -1;
+ m_text.clear();
+ m_geometry.clear();
+ emit fromPointChanged();
+ emit toPointChanged();
+ emit textChanged();
+ emit selectedAreaChanged();
+ QGuiApplication::inputMethod()->update(Qt::ImQueryInput);
+}
+
void QQuickPdfSelection::selectAll()
{
QPdfSelection sel = m_document->m_doc.getAllText(m_page);
@@ -136,10 +176,172 @@ void QQuickPdfSelection::selectAll()
if (sel.bounds() != m_geometry) {
m_geometry = sel.bounds();
- emit geometryChanged();
+ emit selectedAreaChanged();
+ }
+#if QT_CONFIG(im)
+ m_fromCharIndex = sel.startIndex();
+ m_toCharIndex = sel.endIndex();
+ if (sel.bounds().isEmpty()) {
+ m_fromPoint = QPointF();
+ m_toPoint = QPointF();
+ } else {
+ m_fromPoint = sel.bounds().first().boundingRect().topLeft() * m_renderScale;
+ m_toPoint = sel.bounds().last().boundingRect().bottomRight() * m_renderScale - QPointF(0, m_heightAtCursor);
+ }
+
+ QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle | Qt::ImAnchorRectangle);
+#endif
+}
+
+#if QT_CONFIG(im)
+void QQuickPdfSelection::keyReleaseEvent(QKeyEvent *ev)
+{
+ qCDebug(qLcIm) << "release" << ev;
+ const auto &allText = pageText();
+ if (ev == QKeySequence::MoveToPreviousWord) {
+ // iOS sends MoveToPreviousWord first to get to the beginning of the word,
+ // and then SelectNextWord to select the whole word.
+ int i = allText.lastIndexOf(WordDelimiter, m_fromCharIndex - allText.length());
+ if (i < 0)
+ i = 0;
+ else
+ i += 1; // don't select the space before the word
+ auto sel = m_document->m_doc.getSelectionAtIndex(m_page, i, m_text.length() + m_fromCharIndex - i);
+ update(sel);
+ QGuiApplication::inputMethod()->update(Qt::ImAnchorRectangle);
+ } else if (ev == QKeySequence::SelectNextWord) {
+ int i = allText.indexOf(WordDelimiter, m_toCharIndex);
+ if (i < 0)
+ i = allText.length(); // go to the end of m_textAfter
+ auto sel = m_document->m_doc.getSelectionAtIndex(m_page, m_fromCharIndex, m_text.length() + i - m_toCharIndex);
+ update(sel);
+ QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle);
+ } else if (ev == QKeySequence::Copy) {
+ copyToClipboard();
+ }
+}
+
+void QQuickPdfSelection::inputMethodEvent(QInputMethodEvent *event)
+{
+ for (auto attr : event->attributes()) {
+ switch (attr.type) {
+ case QInputMethodEvent::Cursor:
+ qCDebug(qLcIm) << "QInputMethodEvent::Cursor: moved to" << attr.start << "len" << attr.length;
+ break;
+ case QInputMethodEvent::Selection: {
+ auto sel = m_document->m_doc.getSelectionAtIndex(m_page, attr.start, attr.length);
+ update(sel);
+ qCDebug(qLcIm) << "QInputMethodEvent::Selection: from" << attr.start << "len" << attr.length
+ << "result:" << m_fromCharIndex << "->" << m_toCharIndex << sel.boundingRectangle();
+ // the iOS plugin decided that it wanted to change the selection, but still has to be told to move the handles (!?)
+ QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle | Qt::ImAnchorRectangle);
+ break;
+ }
+ case QInputMethodEvent::Language:
+ case QInputMethodEvent::Ruby:
+ case QInputMethodEvent::TextFormat:
+ break;
+ }
}
}
+QVariant QQuickPdfSelection::inputMethodQuery(Qt::InputMethodQuery query, const QVariant &argument) const
+{
+ if (!argument.isNull()) {
+ qCDebug(qLcIm) << "IM query" << query << "with arg" << argument;
+ if (query == Qt::ImCursorPosition) {
+ // If it didn't move since last time, return the same result.
+ if (m_hitPoint == argument.toPointF())
+ return inputMethodQuery(query);
+ m_hitPoint = argument.toPointF();
+ auto tp = m_document->m_doc.d->hitTest(m_page, m_hitPoint / m_renderScale);
+ qCDebug(qLcIm) << "ImCursorPosition hit testing in px" << m_hitPoint << "pt" << (m_hitPoint / m_renderScale)
+ << "got char index" << tp.charIndex << "@" << tp.position << "pt," << tp.position * m_renderScale << "px";
+ if (tp.charIndex >= 0) {
+ m_toCharIndex = tp.charIndex;
+ m_toPoint = tp.position * m_renderScale - QPointF(0, m_heightAtCursor);
+ m_heightAtCursor = tp.height * m_renderScale;
+ if (qFuzzyIsNull(m_heightAtAnchor))
+ m_heightAtAnchor = m_heightAtCursor;
+ }
+ }
+ }
+ return inputMethodQuery(query);
+}
+
+QVariant QQuickPdfSelection::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ QVariant ret;
+ switch (query) {
+ case Qt::ImEnabled:
+ ret = true;
+ break;
+ case Qt::ImHints:
+ ret = QVariant(Qt::ImhMultiLine | Qt::ImhNoPredictiveText);
+ break;
+ case Qt::ImInputItemClipRectangle:
+ ret = boundingRect();
+ break;
+ case Qt::ImAnchorPosition:
+ ret = m_fromCharIndex;
+ break;
+ case Qt::ImAbsolutePosition:
+ ret = m_toCharIndex;
+ break;
+ case Qt::ImCursorPosition:
+ ret = m_toCharIndex;
+ break;
+ case Qt::ImAnchorRectangle:
+ ret = QRectF(m_fromPoint, QSizeF(1, m_heightAtAnchor));
+ break;
+ case Qt::ImCursorRectangle:
+ ret = QRectF(m_toPoint, QSizeF(1, m_heightAtCursor));
+ break;
+ case Qt::ImSurroundingText:
+ ret = QVariant(pageText());
+ break;
+ case Qt::ImTextBeforeCursor:
+ ret = QVariant(pageText().mid(0, m_toCharIndex));
+ break;
+ case Qt::ImTextAfterCursor:
+ ret = QVariant(pageText().mid(m_toCharIndex));
+ break;
+ case Qt::ImCurrentSelection:
+ ret = QVariant(m_text);
+ break;
+ case Qt::ImEnterKeyType:
+ break;
+ case Qt::ImFont: {
+ QFont font = QGuiApplication::font();
+ font.setPointSizeF(m_heightAtCursor);
+ ret = font;
+ break;
+ }
+ case Qt::ImMaximumTextLength:
+ break;
+ case Qt::ImPreferredLanguage:
+ break;
+ case Qt::ImPlatformData:
+ break;
+ case Qt::ImQueryInput:
+ case Qt::ImQueryAll:
+ qWarning() << "unexpected composite query";
+ break;
+ }
+ qCDebug(qLcIm) << "IM query" << query << "returns" << ret;
+ return ret;
+}
+#endif // QT_CONFIG(im)
+
+const QString &QQuickPdfSelection::pageText() const
+{
+ if (m_pageTextDirty) {
+ m_pageText = m_document->m_doc.getAllText(m_page).text();
+ m_pageTextDirty = false;
+ }
+ return m_pageText;
+}
+
void QQuickPdfSelection::resetPoints()
{
bool wasHolding = m_hold;
@@ -167,18 +369,42 @@ void QQuickPdfSelection::setPage(int page)
return;
m_page = page;
+ m_pageTextDirty = true;
emit pageChanged();
resetPoints();
}
/*!
+ \qmlproperty real PdfSelection::renderScale
+ \brief The ratio from points to pixels at which the page is rendered.
+
+ This is used to scale \l fromPoint and \l toPoint to find ranges of
+ selected characters in the document, because positions within the document
+ are always given in points.
+*/
+qreal QQuickPdfSelection::renderScale() const
+{
+ return m_renderScale;
+}
+
+void QQuickPdfSelection::setRenderScale(qreal scale)
+{
+ if (qFuzzyCompare(scale, m_renderScale))
+ return;
+
+ m_renderScale = scale;
+ emit renderScaleChanged();
+ updateResults();
+}
+
+/*!
\qmlproperty point PdfSelection::fromPoint
- The beginning location, in \l {https://en.wikipedia.org/wiki/Point_(typography)}{points}
- from the upper-left corner of the page, from which to find selected text.
- This can be bound to a scaled version of the \c centroid.pressPosition
- of a \l DragHandler to begin selecting text from the position where the user
- presses the mouse button and begins dragging, for example.
+ The beginning location, in pixels from the upper-left corner of the page,
+ from which to find selected text. This can be bound to the
+ \c centroid.pressPosition of a \l DragHandler to begin selecting text from
+ the position where the user presses the mouse button and begins dragging,
+ for example.
*/
QPointF QQuickPdfSelection::fromPoint() const
{
@@ -198,11 +424,10 @@ void QQuickPdfSelection::setFromPoint(QPointF fromPoint)
/*!
\qmlproperty point PdfSelection::toPoint
- The ending location, in \l {https://en.wikipedia.org/wiki/Point_(typography)}{points}
- from the upper-left corner of the page, from which to find selected text.
- This can be bound to a scaled version of the \c centroid.position
- of a \l DragHandler to end selection of text at the position where the user
- is currently dragging the mouse, for example.
+ The ending location, in pixels from the upper-left corner of the page,
+ from which to find selected text. This can be bound to the
+ \c centroid.position of a \l DragHandler to end selection of text at the
+ position where the user is currently dragging the mouse, for example.
*/
QPointF QQuickPdfSelection::toPoint() const
{
@@ -267,7 +492,13 @@ void QQuickPdfSelection::updateResults()
{
if (!m_document)
return;
- QPdfSelection sel = m_document->document().getSelection(m_page, m_fromPoint, m_toPoint);
+ QPdfSelection sel = m_document->document().getSelection(m_page,
+ m_fromPoint / m_renderScale, m_toPoint / m_renderScale);
+ update(sel, true);
+}
+
+void QQuickPdfSelection::update(const QPdfSelection &sel, bool textAndGeometryOnly)
+{
if (sel.text() != m_text) {
m_text = sel.text();
if (QGuiApplication::clipboard()->supportsSelection())
@@ -277,7 +508,33 @@ void QQuickPdfSelection::updateResults()
if (sel.bounds() != m_geometry) {
m_geometry = sel.bounds();
- emit geometryChanged();
+ emit selectedAreaChanged();
+ }
+
+ if (textAndGeometryOnly)
+ return;
+
+ m_fromCharIndex = sel.startIndex();
+ m_toCharIndex = sel.endIndex();
+ if (sel.bounds().isEmpty()) {
+ m_fromPoint = sel.boundingRectangle().topLeft() * m_renderScale;
+ m_toPoint = m_fromPoint;
+ } else {
+ Qt::InputMethodQueries toUpdate = {};
+ QRectF firstLineBounds = sel.bounds().first().boundingRect();
+ m_fromPoint = firstLineBounds.topLeft() * m_renderScale;
+ if (!qFuzzyCompare(m_heightAtAnchor, firstLineBounds.height())) {
+ m_heightAtAnchor = firstLineBounds.height() * m_renderScale;
+ toUpdate.setFlag(Qt::ImAnchorRectangle);
+ }
+ QRectF lastLineBounds = sel.bounds().last().boundingRect();
+ if (!qFuzzyCompare(m_heightAtCursor, lastLineBounds.height())) {
+ m_heightAtCursor = lastLineBounds.height() * m_renderScale;
+ toUpdate.setFlag(Qt::ImCursorRectangle);
+ }
+ m_toPoint = lastLineBounds.topRight() * m_renderScale;
+ if (toUpdate)
+ QGuiApplication::inputMethod()->update(toUpdate);
}
}
diff --git a/src/pdf/quick/qquickpdfselection_p.h b/src/pdf/quick/qquickpdfselection_p.h
index d231c0d11..ee7e1f85f 100644
--- a/src/pdf/quick/qquickpdfselection_p.h
+++ b/src/pdf/quick/qquickpdfselection_p.h
@@ -52,30 +52,35 @@
#include <QPolygonF>
#include <QVariant>
#include <QtQml/qqml.h>
+#include <QtQuick/qquickitem.h>
#include "qquickpdfdocument_p.h"
QT_BEGIN_NAMESPACE
+class QPdfSelection;
-class QQuickPdfSelection : public QObject
+class QQuickPdfSelection : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QQuickPdfDocument *document READ document WRITE setDocument NOTIFY documentChanged)
Q_PROPERTY(int page READ page WRITE setPage NOTIFY pageChanged)
+ Q_PROPERTY(qreal renderScale READ renderScale WRITE setRenderScale NOTIFY renderScaleChanged)
Q_PROPERTY(QPointF fromPoint READ fromPoint WRITE setFromPoint NOTIFY fromPointChanged)
Q_PROPERTY(QPointF toPoint READ toPoint WRITE setToPoint NOTIFY toPointChanged)
Q_PROPERTY(bool hold READ hold WRITE setHold NOTIFY holdChanged)
Q_PROPERTY(QString text READ text NOTIFY textChanged)
- Q_PROPERTY(QVector<QPolygonF> geometry READ geometry NOTIFY geometryChanged)
+ Q_PROPERTY(QVector<QPolygonF> geometry READ geometry NOTIFY selectedAreaChanged)
public:
- explicit QQuickPdfSelection(QObject *parent = nullptr);
+ explicit QQuickPdfSelection(QQuickItem *parent = nullptr);
QQuickPdfDocument *document() const;
void setDocument(QQuickPdfDocument * document);
int page() const;
void setPage(int page);
+ qreal renderScale() const;
+ void setRenderScale(qreal scale);
QPointF fromPoint() const;
void setFromPoint(QPointF fromPoint);
QPointF toPoint() const;
@@ -86,6 +91,7 @@ public:
QString text() const;
QVector<QPolygonF> geometry() const;
+ Q_INVOKABLE void clear();
Q_INVOKABLE void selectAll();
#if QT_CONFIG(clipboard)
Q_INVOKABLE void copyToClipboard() const;
@@ -94,24 +100,43 @@ public:
signals:
void documentChanged();
void pageChanged();
+ void renderScaleChanged();
void fromPointChanged();
void toPointChanged();
void holdChanged();
void textChanged();
- void geometryChanged();
+ void selectedAreaChanged();
+
+protected:
+#if QT_CONFIG(im)
+ void keyReleaseEvent(QKeyEvent *ev) override;
+ void inputMethodEvent(QInputMethodEvent *event) override;
+ Q_INVOKABLE QVariant inputMethodQuery(Qt::InputMethodQuery query, const QVariant &argument) const;
+ QVariant inputMethodQuery(Qt::InputMethodQuery query) const override;
+#endif
private:
void resetPoints();
void updateResults();
+ void update(const QPdfSelection &sel, bool textAndGeometryOnly = false);
+ const QString &pageText() const;
private:
QQuickPdfDocument *m_document = nullptr;
+ mutable QPointF m_hitPoint;
QPointF m_fromPoint;
- QPointF m_toPoint;
- QString m_text;
+ mutable QPointF m_toPoint;
+ qreal m_renderScale = 1;
+ mutable qreal m_heightAtAnchor = 0;
+ mutable qreal m_heightAtCursor = 0;
+ QString m_text; // selected text
+ mutable QString m_pageText; // all text on the page
QVector<QPolygonF> m_geometry;
int m_page = 0;
+ int m_fromCharIndex = -1; // same as anchor position
+ mutable int m_toCharIndex = -1; // same as cursor position
bool m_hold = false;
+ mutable bool m_pageTextDirty = true;
Q_DISABLE_COPY(QQuickPdfSelection)
};
@@ -119,5 +144,5 @@ private:
QT_END_NAMESPACE
QML_DECLARE_TYPE(QQuickPdfSelection)
-\
+
#endif // QQUICKPDFSELECTION_P_H
diff --git a/src/pdf/quick/qquicktableviewextra.cpp b/src/pdf/quick/qquicktableviewextra.cpp
new file mode 100644
index 000000000..2b59d6c6e
--- /dev/null
+++ b/src/pdf/quick/qquicktableviewextra.cpp
@@ -0,0 +1,193 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquicktableviewextra_p.h"
+#include <QtQml>
+#include <QQmlContext>
+
+Q_LOGGING_CATEGORY(qLcTVE, "qt.pdf.tableextra")
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+ \qmltype TableViewExtra
+ \instantiates QQuickTableViewExtra
+ \inqmlmodule QtQuick.Pdf
+ \ingroup pdf
+ \brief A helper class with missing TableView functions
+ \since 5.15
+
+ TableViewExtra provides equivalents for some functions that will be added
+ to TableView in Qt 6.
+*/
+
+QQuickTableViewExtra::QQuickTableViewExtra(QObject *parent) : QObject(parent)
+{
+}
+
+QPoint QQuickTableViewExtra::cellAtPos(qreal x, qreal y) const
+{
+ QPointF position(x, y);
+#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
+ return m_tableView->cellAtPos(position);
+#else
+ if (!m_tableView->boundingRect().contains(position))
+ return QPoint(-1, -1);
+
+ const QQuickItem *contentItem = m_tableView->contentItem();
+
+ for (const QQuickItem *child : contentItem->childItems()) {
+ const QPointF posInChild = m_tableView->mapToItem(child, position);
+ if (child->boundingRect().contains(posInChild)) {
+ const auto context = qmlContext(child);
+ const int column = context->contextProperty("column").toInt();
+ const int row = context->contextProperty("row").toInt();
+ return QPoint(column, row);
+ }
+ }
+
+ return QPoint(-1, -1);
+#endif
+}
+
+QQuickItem *QQuickTableViewExtra::itemAtCell(const QPoint &cell) const
+{
+#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
+ return m_tableView->itemAtCell(cell);
+#else
+ const QQuickItem *contentItem = m_tableView->contentItem();
+
+ for (QQuickItem *child : contentItem->childItems()) {
+ const auto context = qmlContext(child);
+ const int column = context->contextProperty("column").toInt();
+ const int row = context->contextProperty("row").toInt();
+ if (QPoint(column, row) == cell)
+ return child;
+ }
+
+ return nullptr;
+#endif
+}
+
+void QQuickTableViewExtra::positionViewAtCell(const QPoint &cell, Qt::Alignment alignment, const QPointF &offset)
+{
+#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
+ m_tableView->positionViewAtCell(cell, alignment, offset);
+#else
+ // Note: this fallback implementation assumes all cells to be of the same size!
+
+ if (cell.x() < 0 || cell.x() > m_tableView->columns() - 1)
+ return;
+ if (cell.y() < 0 || cell.y() > m_tableView->rows() - 1)
+ return;
+
+ Qt::Alignment verticalAlignment = alignment & (Qt::AlignTop | Qt::AlignVCenter | Qt::AlignBottom);
+ Qt::Alignment horizontalAlignment = alignment & (Qt::AlignLeft | Qt::AlignHCenter | Qt::AlignRight);
+
+ const QQuickItem *contentItem = m_tableView->contentItem();
+ const QQuickItem *randomChild = contentItem->childItems().first();
+ const qreal cellWidth = randomChild->width();
+ const qreal cellHeight = randomChild->height();
+
+ if (!verticalAlignment && !horizontalAlignment) {
+ qmlWarning(this) << "No valid alignment specified";
+ return;
+ }
+
+ if (horizontalAlignment) {
+ qreal newPosX = 0;
+ const qreal columnPosLeft = int(cell.x() * (cellWidth + m_tableView->columnSpacing()));
+ m_tableView->setContentX(0);
+ m_tableView->forceLayout();
+ m_tableView->setContentX(columnPosLeft);
+ m_tableView->forceLayout();
+
+ switch (horizontalAlignment) {
+ case Qt::AlignLeft:
+ newPosX = m_tableView->contentX() + offset.x();
+ break;
+ case Qt::AlignHCenter:
+ newPosX = m_tableView->contentX()
+ - m_tableView->width() / 2
+ + (cellWidth / 2)
+ + offset.x();
+ break;
+ case Qt::AlignRight:
+ newPosX = m_tableView->contentX()
+ - m_tableView->width()
+ + cellWidth
+ + offset.x();
+ break;
+ }
+
+ m_tableView->setContentX(newPosX);
+ m_tableView->forceLayout();
+ }
+
+ if (verticalAlignment) {
+ qreal newPosY = 0;
+ const qreal rowPosTop = int(cell.y() * (cellHeight + m_tableView->rowSpacing()));
+ m_tableView->setContentY(0);
+ m_tableView->forceLayout();
+ m_tableView->setContentY(rowPosTop);
+ m_tableView->forceLayout();
+
+ switch (verticalAlignment) {
+ case Qt::AlignTop:
+ newPosY = m_tableView->contentY() + offset.y();
+ break;
+ case Qt::AlignVCenter:
+ newPosY = m_tableView->contentY()
+ - m_tableView->height() / 2
+ + (cellHeight / 2)
+ + offset.y();
+ break;
+ case Qt::AlignBottom:
+ newPosY = m_tableView->contentY()
+ - m_tableView->height()
+ + cellHeight
+ + offset.y();
+ break;
+ }
+
+ m_tableView->setContentY(newPosY);
+ m_tableView->forceLayout();
+ }
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/pdf/quick/qquicktableviewextra_p.h b/src/pdf/quick/qquicktableviewextra_p.h
new file mode 100644
index 000000000..11b4955a1
--- /dev/null
+++ b/src/pdf/quick/qquicktableviewextra_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKTABLEVIEWEXTRA_P_H
+#define QQUICKTABLEVIEWEXTRA_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QPointF>
+#include <QPolygonF>
+#include <QVariant>
+#include <QtQml/qqml.h>
+#include <QtQuick/qquickitem.h>
+#include <QtQuick/private/qquicktableview_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickTableViewExtra : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickTableView *tableView READ tableView WRITE setTableView)
+
+public:
+ QQuickTableViewExtra(QObject *parent = nullptr);
+
+ QQuickTableView * tableView() const { return m_tableView; }
+ void setTableView(QQuickTableView * tableView) { m_tableView = tableView; }
+
+ Q_INVOKABLE QPoint cellAtPos(qreal x, qreal y) const;
+ Q_INVOKABLE QQuickItem *itemAtCell(int column, int row) const {
+ return itemAtCell(QPoint(column, row));
+ }
+ Q_INVOKABLE QQuickItem *itemAtCell(const QPoint &cell) const;
+ Q_INVOKABLE void positionViewAtCell(int column, int row, Qt::Alignment alignment, const QPointF &offset = QPointF()) {
+ positionViewAtCell(QPoint(column, row), alignment, offset);
+ }
+ Q_INVOKABLE void positionViewAtCell(const QPoint &cell, Qt::Alignment alignment, const QPointF &offset);
+ Q_INVOKABLE void positionViewAtRow(int row, Qt::Alignment alignment, qreal offset = 0) {
+ positionViewAtCell(QPoint(0, row), alignment & Qt::AlignVertical_Mask, QPointF(0, offset));
+ }
+
+private:
+ QQuickTableView *m_tableView = nullptr;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickTableViewExtra)
+
+#endif // QQUICKTABLEVIEWEXTRA_P_H
diff --git a/src/pdf/quick/quick.pro b/src/pdf/quick/quick.pro
index b62b80346..bd6bc8827 100644
--- a/src/pdf/quick/quick.pro
+++ b/src/pdf/quick/quick.pro
@@ -3,6 +3,10 @@ TARGET = pdfplugin
TARGETPATH = QtQuick/Pdf
IMPORT_VERSION = 1.0
+# qpdfdocument_p.h includes pdfium headers which we must find in order to use private API
+CHROMIUM_SRC_DIR = $$QTWEBENGINE_ROOT/$$getChromiumSrcDir()
+INCLUDEPATH += $$CHROMIUM_SRC_DIR
+
#QMAKE_DOCS = $$PWD/doc/qtquickpdf.qdocconf
PDF_QML_FILES = \
@@ -21,6 +25,7 @@ SOURCES += \
qquickpdfnavigationstack.cpp \
qquickpdfsearchmodel.cpp \
qquickpdfselection.cpp \
+ qquicktableviewextra.cpp \
HEADERS += \
qquickpdfdocument_p.h \
@@ -28,7 +33,8 @@ HEADERS += \
qquickpdfnavigationstack_p.h \
qquickpdfsearchmodel_p.h \
qquickpdfselection_p.h \
+ qquicktableviewextra_p.h \
-QT += pdf quick-private gui gui-private core core-private qml qml-private
+QT += pdf pdf-private gui core qml quick quick-private
load(qml_plugin)
diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp
index afb0fbd25..744891a61 100644
--- a/src/webengine/api/qquickwebengineview.cpp
+++ b/src/webengine/api/qquickwebengineview.cpp
@@ -129,6 +129,7 @@ QQuickWebEngineViewPrivate::QQuickWebEngineViewPrivate()
, devicePixelRatio(QGuiApplication::primaryScreen()->devicePixelRatio())
, m_webChannel(0)
, m_webChannelWorld(0)
+ , m_defaultAudioMuted(false)
, m_isBeingAdopted(false)
, m_backgroundColor(Qt::white)
, m_zoomFactor(1.0)
@@ -547,12 +548,13 @@ void QQuickWebEngineViewPrivate::unhandledKeyEvent(QKeyEvent *event)
QCoreApplication::sendEvent(q->parentItem(), event);
}
-void QQuickWebEngineViewPrivate::adoptNewWindow(QSharedPointer<WebContentsAdapter> newWebContents, WindowOpenDisposition disposition, bool userGesture, const QRect &, const QUrl &targetUrl)
+QSharedPointer<WebContentsAdapter>
+QQuickWebEngineViewPrivate::adoptNewWindow(QSharedPointer<WebContentsAdapter> newWebContents,
+ WindowOpenDisposition disposition, bool userGesture,
+ const QRect &, const QUrl &targetUrl)
{
Q_Q(QQuickWebEngineView);
QQuickWebEngineNewViewRequest request;
- // This increases the ref-count of newWebContents and will tell Chromium
- // to start loading it and possibly return it to its parent page window.open().
request.m_adapter = newWebContents;
request.m_isUserInitiated = userGesture;
request.m_requestedUrl = targetUrl;
@@ -575,6 +577,8 @@ void QQuickWebEngineViewPrivate::adoptNewWindow(QSharedPointer<WebContentsAdapte
}
Q_EMIT q->newViewRequested(&request);
+
+ return newWebContents;
}
bool QQuickWebEngineViewPrivate::isBeingAdopted()
@@ -669,11 +673,8 @@ void QQuickWebEngineViewPrivate::runMediaAccessPermissionRequest(const QUrl &sec
void QQuickWebEngineViewPrivate::runMouseLockPermissionRequest(const QUrl &securityOrigin)
{
-
- Q_UNUSED(securityOrigin);
-
// TODO: Add mouse lock support
- adapter->grantMouseLockPermission(false);
+ adapter->grantMouseLockPermission(securityOrigin, false);
}
void QQuickWebEngineViewPrivate::runQuotaRequest(QWebEngineQuotaRequest request)
@@ -906,6 +907,9 @@ void QQuickWebEngineViewPrivate::initializationFinished()
adapter->setWebChannel(m_webChannel, m_webChannelWorld);
#endif
+ if (m_defaultAudioMuted != adapter->isAudioMuted())
+ adapter->setAudioMuted(m_defaultAudioMuted);
+
if (devToolsView && devToolsView->d_ptr->adapter)
adapter->openDevToolsFrontend(devToolsView->d_ptr->adapter);
@@ -1426,15 +1430,18 @@ void QQuickWebEngineView::setBackgroundColor(const QColor &color)
bool QQuickWebEngineView::isAudioMuted() const
{
const Q_D(QQuickWebEngineView);
- return d->adapter->isAudioMuted();
+ if (d->adapter->isInitialized())
+ return d->adapter->isAudioMuted();
+ return d->m_defaultAudioMuted;
}
void QQuickWebEngineView::setAudioMuted(bool muted)
{
Q_D(QQuickWebEngineView);
- bool wasAudioMuted = d->adapter->isAudioMuted();
+ bool wasAudioMuted = isAudioMuted();
+ d->m_defaultAudioMuted = muted;
d->adapter->setAudioMuted(muted);
- if (wasAudioMuted != d->adapter->isAudioMuted())
+ if (wasAudioMuted != isAudioMuted())
Q_EMIT audioMutedChanged(muted);
}
diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h
index 5c884e36e..12a991ffa 100644
--- a/src/webengine/api/qquickwebengineview_p_p.h
+++ b/src/webengine/api/qquickwebengineview_p_p.h
@@ -121,7 +121,10 @@ public:
void loadFinished(bool success, const QUrl &url, bool isErrorPage = false, int errorCode = 0, const QString &errorDescription = QString()) override;
void focusContainer() override;
void unhandledKeyEvent(QKeyEvent *event) override;
- void adoptNewWindow(QSharedPointer<QtWebEngineCore::WebContentsAdapter> newWebContents, WindowOpenDisposition disposition, bool userGesture, const QRect &, const QUrl &targetUrl) override;
+ QSharedPointer<QtWebEngineCore::WebContentsAdapter>
+ adoptNewWindow(QSharedPointer<QtWebEngineCore::WebContentsAdapter> newWebContents,
+ WindowOpenDisposition disposition, bool userGesture, const QRect &,
+ const QUrl &targetUrl) override;
bool isBeingAdopted() override;
void close() override;
void windowCloseRejected() override;
@@ -214,6 +217,7 @@ public:
QPointer<QQuickWebEngineView> inspectedView;
QPointer<QQuickWebEngineView> devToolsView;
uint m_webChannelWorld;
+ bool m_defaultAudioMuted;
bool m_isBeingAdopted;
mutable QQuickWebEngineAction *actions[QQuickWebEngineView::WebActionCount];
QtWebEngineCore::RenderWidgetHostViewQtDelegateQuick *widget = nullptr;
diff --git a/src/webengine/doc/src/external-resources.qdoc b/src/webengine/doc/src/external-resources.qdoc
index acf63fb04..7878ed9f8 100644
--- a/src/webengine/doc/src/external-resources.qdoc
+++ b/src/webengine/doc/src/external-resources.qdoc
@@ -71,8 +71,8 @@
*/
/*!
- \externalpage http://www.widevine.com/wv_drm.html
- \title Widevine DRM
+ \externalpage http://www.widevine.com
+ \title Widevine CDM
*/
/*!
@@ -86,8 +86,8 @@
*/
/*!
- \externalpage https://shaka-player-demo.appspot.com/demo/
- \title Shaka Player
+ \externalpage https://bitmovin.com/demos/drm
+ \title Bitmovin Player
*/
/*!
diff --git a/src/webengine/doc/src/qtwebengine-features.qdoc b/src/webengine/doc/src/qtwebengine-features.qdoc
index 954992de1..431367765 100644
--- a/src/webengine/doc/src/qtwebengine-features.qdoc
+++ b/src/webengine/doc/src/qtwebengine-features.qdoc
@@ -181,8 +181,30 @@
\section1 HTML5 DRM
- \QWE supports viewing DRM protected videos if the \l{Widevine DRM}
- plugin has been installed.
+ \QWE supports viewing DRM protected videos if the \l{Widevine CDM} plugin has been installed.
+ CDM plugin is a replacement of Flash based plugins for displaying DRM-protected content.
+ It comes only in a binary format, so it can hide DRM decryption implementation details.
+ It can be obtained from a third party or from a Google Chrome installation.
+
+ \QWE on startup looks for the \l{Widevine CDM} plugin in well know locations, like
+ default Google Chrome installation directory or Linux distro specific paths. However, plugin
+ location can be also passed with \c {QTWEBENGINE_CHROMIUM_FLAGS} using \c {widevine-path}.
+
+ On Windows:
+ \code
+ set QTWEBENGINE_CHROMIUM_FLAGS=--widevine-path="C:/some path/widevinecdm.dll"
+ \endcode
+
+ On Linux:
+ \code
+ export QTWEBENGINE_CHROMIUM_FLAGS=--widevine-path="/some path/libwidevinecdm.so"
+ \endcode
+
+ On macOS:
+ \code
+ export QTWEBENGINE_CHROMIUM_FLAGS=--widevine-path="/some path/libwidevinecdm.dylib"
+ \endcode
+
The video format most commonly used by DRM services, H.264, requires
proprietary audio and video codecs. For more information about enabling the
@@ -190,7 +212,7 @@
This feature can be tested by playing a video in \l{WebEngine Widgets Simple Browser
Example}{Simple Browser} or \l{WebEngine Quick Nano Browser}{Nano Browser}
- from \l{castLabs}, \l{Swank Motion Pictures, Inc.}, or \l{Shaka Player}.
+ from \l{castLabs}, \l{Swank Motion Pictures, Inc.}, or \l{Bitmovin Player}.
Support for this feature was added in Qt 5.7.0.
diff --git a/src/webengine/render_widget_host_view_qt_delegate_quick.cpp b/src/webengine/render_widget_host_view_qt_delegate_quick.cpp
index cb41c8706..30f50d7c1 100644
--- a/src/webengine/render_widget_host_view_qt_delegate_quick.cpp
+++ b/src/webengine/render_widget_host_view_qt_delegate_quick.cpp
@@ -64,7 +64,7 @@ RenderWidgetHostViewQtDelegateQuick::RenderWidgetHostViewQtDelegateQuick(RenderW
setFocus(true);
setActiveFocusOnTab(true);
-#if defined(Q_OS_MACOS) && !defined(QT_NO_OPENGL)
+#if defined(Q_OS_MACOS) && QT_CONFIG(opengl)
// Check that the default QSurfaceFormat OpenGL profile is compatible with the global OpenGL
// shared context profile, otherwise this could lead to a nasty crash.
QOpenGLContext *globalSharedContext = QOpenGLContext::globalShareContext();
diff --git a/src/webenginewidgets/api/qtwebenginewidgetsglobal.cpp b/src/webenginewidgets/api/qtwebenginewidgetsglobal.cpp
index a39c0e483..5949f3d6e 100644
--- a/src/webenginewidgets/api/qtwebenginewidgetsglobal.cpp
+++ b/src/webenginewidgets/api/qtwebenginewidgetsglobal.cpp
@@ -49,13 +49,13 @@ namespace QtWebEngineCore
QT_BEGIN_NAMESPACE
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
Q_GUI_EXPORT QOpenGLContext *qt_gl_global_share_context();
#endif
static void initialize()
{
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
if (QCoreApplication::instance()) {
//On window/ANGLE, calling QtWebEngine::initialize from DllMain will result in a crash.
if (!qt_gl_global_share_context()) {
@@ -67,7 +67,7 @@ static void initialize()
}
//QCoreApplication is not yet instantiated, ensuring the call will be deferred
qAddPreRoutine(QtWebEngineCore::initialize);
-#endif // QT_NO_OPENGL
+#endif // QT_CONFIG(opengl)
}
Q_CONSTRUCTOR_FUNCTION(initialize)
diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp
index b267c5dd1..dc06e48be 100644
--- a/src/webenginewidgets/api/qwebenginepage.cpp
+++ b/src/webenginewidgets/api/qwebenginepage.cpp
@@ -348,7 +348,10 @@ void QWebEnginePagePrivate::unhandledKeyEvent(QKeyEvent *event)
QGuiApplication::sendEvent(view->parentWidget(), event);
}
-void QWebEnginePagePrivate::adoptNewWindow(QSharedPointer<WebContentsAdapter> newWebContents, WindowOpenDisposition disposition, bool userGesture, const QRect &initialGeometry, const QUrl &targetUrl)
+QSharedPointer<WebContentsAdapter>
+QWebEnginePagePrivate::adoptNewWindow(QSharedPointer<WebContentsAdapter> newWebContents,
+ WindowOpenDisposition disposition, bool userGesture,
+ const QRect &initialGeometry, const QUrl &targetUrl)
{
Q_Q(QWebEnginePage);
Q_UNUSED(userGesture);
@@ -356,27 +359,11 @@ void QWebEnginePagePrivate::adoptNewWindow(QSharedPointer<WebContentsAdapter> ne
QWebEnginePage *newPage = q->createWindow(toWindowType(disposition));
if (!newPage)
- return;
+ return nullptr;
- if (newPage->d_func() == this) {
- // If createWindow returns /this/ we must delay the adoption.
- Q_ASSERT(q == newPage);
- // WebContents might be null if we just opened a new page for navigation, in that case
- // avoid referencing newWebContents so that it is deleted and WebContentsDelegateQt::OpenURLFromTab
- // will fall back to navigating current page.
- if (newWebContents->webContents()) {
- QTimer::singleShot(0, q, [this, newPage, newWebContents, initialGeometry] () {
- adoptNewWindowImpl(newPage, newWebContents, initialGeometry);
- });
- }
- } else {
- adoptNewWindowImpl(newPage, newWebContents, initialGeometry);
- }
-}
+ if (!newWebContents->webContents())
+ return newPage->d_func()->adapter; // Reuse existing adapter
-void QWebEnginePagePrivate::adoptNewWindowImpl(QWebEnginePage *newPage,
- const QSharedPointer<WebContentsAdapter> &newWebContents, const QRect &initialGeometry)
-{
// Mark the new page as being in the process of being adopted, so that a second mouse move event
// sent by newWebContents->initialize() gets filtered in RenderWidgetHostViewQt::forwardEvent.
// The first mouse move event is being sent by q->createWindow(). This is necessary because
@@ -394,6 +381,8 @@ void QWebEnginePagePrivate::adoptNewWindowImpl(QWebEnginePage *newPage,
if (!initialGeometry.isEmpty())
emit newPage->geometryChangeRequested(initialGeometry);
+
+ return newWebContents;
}
bool QWebEnginePagePrivate::isBeingAdopted()
@@ -1147,9 +1136,11 @@ bool QWebEnginePage::isAudioMuted() const {
void QWebEnginePage::setAudioMuted(bool muted) {
Q_D(QWebEnginePage);
+ bool wasAudioMuted = isAudioMuted();
d->defaultAudioMuted = muted;
- if (d->adapter->isInitialized())
- d->adapter->setAudioMuted(muted);
+ d->adapter->setAudioMuted(muted);
+ if (wasAudioMuted != isAudioMuted())
+ Q_EMIT audioMutedChanged(muted);
}
/*!
@@ -1971,7 +1962,7 @@ void QWebEnginePage::setFeaturePermission(const QUrl &securityOrigin, QWebEngine
d->adapter->runFeatureRequestCallback(securityOrigin, ProfileAdapter::GeolocationPermission, true);
break;
case MouseLock:
- d->adapter->grantMouseLockPermission(true);
+ d->adapter->grantMouseLockPermission(securityOrigin, true);
break;
case Notifications:
d->adapter->runFeatureRequestCallback(securityOrigin, ProfileAdapter::NotificationPermission, true);
@@ -1990,7 +1981,7 @@ void QWebEnginePage::setFeaturePermission(const QUrl &securityOrigin, QWebEngine
d->adapter->runFeatureRequestCallback(securityOrigin, ProfileAdapter::GeolocationPermission, false);
break;
case MouseLock:
- d->adapter->grantMouseLockPermission(false);
+ d->adapter->grantMouseLockPermission(securityOrigin, false);
break;
case Notifications:
d->adapter->runFeatureRequestCallback(securityOrigin, ProfileAdapter::NotificationPermission, false);
diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h
index f37413b8e..b4424ec4b 100644
--- a/src/webenginewidgets/api/qwebenginepage_p.h
+++ b/src/webenginewidgets/api/qwebenginepage_p.h
@@ -112,10 +112,10 @@ public:
void loadFinished(bool success, const QUrl &url, bool isErrorPage = false, int errorCode = 0, const QString &errorDescription = QString()) override;
void focusContainer() override;
void unhandledKeyEvent(QKeyEvent *event) override;
- void adoptNewWindow(QSharedPointer<QtWebEngineCore::WebContentsAdapter> newWebContents, WindowOpenDisposition disposition, bool userGesture, const QRect &initialGeometry, const QUrl &targetUrl) override;
- void adoptNewWindowImpl(QWebEnginePage *newPage,
- const QSharedPointer<QtWebEngineCore::WebContentsAdapter> &newWebContents,
- const QRect &initialGeometry);
+ QSharedPointer<QtWebEngineCore::WebContentsAdapter>
+ adoptNewWindow(QSharedPointer<QtWebEngineCore::WebContentsAdapter> newWebContents,
+ WindowOpenDisposition disposition, bool userGesture,
+ const QRect &initialGeometry, const QUrl &targetUrl) override;
bool isBeingAdopted() override;
void close() override;
void windowCloseRejected() override;
diff --git a/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc b/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc
index 7701b9b3d..9d5c41713 100644
--- a/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc
+++ b/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc
@@ -805,9 +805,10 @@
Sets the permission for the web site identified by \a securityOrigin to use \a feature to \a policy.
- \note Call this method on the featurePermissionRequested() signal, as it is
- meant to serve pending feature requests only. Setting feature permissions
- ahead of a request has no effect.
+ \note This method is primarily for calling after a featurePermissionRequested() signal has been emitted
+ to trigger the feature permission response. It can also be called before a request has been emitted,
+ but will only set a granted permission for passive checks, mainly for Notification APIs that can check
+ if permission has already been granted before explicitly requesting it.
\sa featurePermissionRequested(), featurePermissionRequestCanceled()
*/
diff --git a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp
index 2c7cc83d1..08c471763 100644
--- a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp
+++ b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp
@@ -106,7 +106,7 @@ RenderWidgetHostViewQtDelegateWidget::RenderWidgetHostViewQtDelegateWidget(Rende
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
-#ifndef QT_NO_OPENGL
+#if QT_CONFIG(opengl)
QOpenGLContext *globalSharedContext = QOpenGLContext::globalShareContext();
if (globalSharedContext) {
QSurfaceFormat sharedFormat = globalSharedContext->format();
@@ -473,11 +473,16 @@ bool RenderWidgetHostViewQtDelegateWidget::event(QEvent *event)
if (!handled)
return QQuickWidget::event(event);
- // Most events are accepted by default, but tablet events are not:
event->accept();
return true;
}
+void RenderWidgetHostViewQtDelegateWidget::unhandledWheelEvent(QWheelEvent *ev)
+{
+ if (QWidget *p = parentWidget())
+ qApp->sendEvent(p, ev);
+}
+
void RenderWidgetHostViewQtDelegateWidget::onWindowPosChanged()
{
m_client->visualPropertiesChanged();
diff --git a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h
index c7783117a..034fdd65c 100644
--- a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h
+++ b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h
@@ -91,6 +91,7 @@ public:
void setInputMethodHints(Qt::InputMethodHints) override;
void setClearColor(const QColor &color) override;
bool copySurface(const QRect &, const QSize &, QImage &) override;
+ void unhandledWheelEvent(QWheelEvent *ev) override;
protected:
bool event(QEvent *event) override;
diff --git a/tests/auto/quick/qmltests/data/tst_audioMuted.qml b/tests/auto/quick/qmltests/data/tst_audioMuted.qml
new file mode 100644
index 000000000..c626d07a0
--- /dev/null
+++ b/tests/auto/quick/qmltests/data/tst_audioMuted.qml
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtTest 1.0
+import QtWebEngine 1.4
+
+TestWebEngineView {
+ id: view
+ width: 400
+ height: 400
+
+ SignalSpy {
+ id: spy
+ target: view
+ signalName: "audioMutedChanged"
+ }
+
+ TestCase {
+ id: test
+ name: "WebEngineViewAudioMuted"
+
+ function test_audioMuted() {
+ compare(view.audioMuted, false);
+ view.audioMuted = true;
+ view.url = "about:blank";
+ verify(view.waitForLoadSucceeded());
+ compare(view.audioMuted, true);
+ compare(spy.count, 1);
+ compare(spy.signalArguments[0][0], true);
+ view.audioMuted = false;
+ compare(view.audioMuted, false);
+ compare(spy.count, 2);
+ compare(spy.signalArguments[1][0], false);
+ }
+ }
+}
+
diff --git a/tests/auto/quick/qmltests/qmltests.pro b/tests/auto/quick/qmltests/qmltests.pro
index 0b3ff7c7e..5c57f7ad9 100644
--- a/tests/auto/quick/qmltests/qmltests.pro
+++ b/tests/auto/quick/qmltests/qmltests.pro
@@ -31,6 +31,7 @@ OTHER_FILES += \
$$PWD/data/titleupdate.js \
$$PWD/data/tst_action.qml \
$$PWD/data/tst_activeFocusOnPress.qml \
+ $$PWD/data/tst_audioMuted.qml \
$$PWD/data/tst_contextMenu.qml \
$$PWD/data/tst_desktopBehaviorLoadHtml.qml \
$$PWD/data/tst_download.qml \
diff --git a/tests/auto/widgets/proxy/proxy_server.cpp b/tests/auto/widgets/proxy/proxy_server.cpp
index 55f014914..3bf915609 100644
--- a/tests/auto/widgets/proxy/proxy_server.cpp
+++ b/tests/auto/widgets/proxy/proxy_server.cpp
@@ -42,8 +42,16 @@ void ProxyServer::setCredentials(const QByteArray &user, const QByteArray passwo
m_auth.append(QChar(':'));
m_auth.append(password);
m_auth = m_auth.toBase64();
+ m_authenticate = true;
}
+void ProxyServer::setCookie(const QByteArray &cookie)
+{
+ m_cookie.append(QByteArrayLiteral("Cookie: "));
+ m_cookie.append(cookie);
+}
+
+
bool ProxyServer::isListening()
{
return m_server.isListening();
@@ -75,7 +83,7 @@ void ProxyServer::handleReadReady()
if (!m_data.endsWith("\r\n\r\n"))
return;
- if (!m_data.contains(QByteArrayLiteral("Proxy-Authorization: Basic"))) {
+ if (m_authenticate && !m_data.contains(QByteArrayLiteral("Proxy-Authorization: Basic"))) {
socket->write("HTTP/1.1 407 Proxy Authentication Required\nProxy-Authenticate: "
"Basic realm=\"Proxy requires authentication\"\r\n"
"content-length: 0\r\n"
@@ -83,8 +91,12 @@ void ProxyServer::handleReadReady()
return;
}
- if (m_data.contains(m_auth)) {
- emit success();
+ if (m_authenticate && m_data.contains(m_auth)) {
+ emit authenticationSuccess();
+ }
+
+ if (m_data.contains(m_cookie)) {
+ emit cookieMatch();
}
m_data.clear();
}
diff --git a/tests/auto/widgets/proxy/proxy_server.h b/tests/auto/widgets/proxy/proxy_server.h
index cb7c30600..7bc7b100b 100644
--- a/tests/auto/widgets/proxy/proxy_server.h
+++ b/tests/auto/widgets/proxy/proxy_server.h
@@ -39,6 +39,7 @@ class ProxyServer : public QObject
public:
explicit ProxyServer(QObject *parent = nullptr);
void setCredentials(const QByteArray &user, const QByteArray password);
+ void setCookie(const QByteArray &cookie);
bool isListening();
public slots:
@@ -49,11 +50,15 @@ private slots:
void handleReadReady();
signals:
- void success();
+ void authenticationSuccess();
+ void cookieMatch();
+
private:
QByteArray m_data;
QTcpServer m_server;
QByteArray m_auth;
+ QByteArray m_cookie;
+ bool m_authenticate = false;
};
#endif // PROXY_SERVER_H
diff --git a/tests/auto/widgets/proxy/tst_proxy.cpp b/tests/auto/widgets/proxy/tst_proxy.cpp
index 5f5dec016..c3e3c88a4 100644
--- a/tests/auto/widgets/proxy/tst_proxy.cpp
+++ b/tests/auto/widgets/proxy/tst_proxy.cpp
@@ -32,6 +32,17 @@
#include <QNetworkProxy>
#include <QWebEnginePage>
#include <QWebEngineView>
+#include <QWebEngineUrlRequestInterceptor>
+
+
+struct Interceptor : public QWebEngineUrlRequestInterceptor
+{
+ Interceptor(const QByteArray cookie):m_cookie(cookie){};
+ void interceptRequest(QWebEngineUrlRequestInfo &info) override {
+ info.setHttpHeader(QByteArray("Cookie"), m_cookie);
+ };
+ QByteArray m_cookie;
+};
class tst_Proxy : public QObject {
@@ -41,8 +52,10 @@ public:
private slots:
void proxyAuthentication();
+ void forwardCookie();
};
+
void tst_Proxy::proxyAuthentication()
{
QByteArray user(QByteArrayLiteral("test"));
@@ -59,11 +72,31 @@ void tst_Proxy::proxyAuthentication()
server.run();
QTRY_VERIFY2(server.isListening(), "Could not setup authentication server");
QWebEnginePage page;
- QSignalSpy successSpy(&server, &ProxyServer::success);
+ QSignalSpy successSpy(&server, &ProxyServer::authenticationSuccess);
page.load(QUrl("http://www.qt.io"));
QTRY_VERIFY2(successSpy.count() > 0, "Could not get authentication token");
}
+void tst_Proxy::forwardCookie()
+{
+ QNetworkProxy proxy;
+ proxy.setType(QNetworkProxy::HttpProxy);
+ proxy.setHostName("localhost");
+ proxy.setPort(5555);
+ QNetworkProxy::setApplicationProxy(proxy);
+ ProxyServer server;
+ QByteArray cookie("foo=bar; sessionToken=123");
+ server.setCookie(cookie);
+ server.run();
+ QTRY_VERIFY2(server.isListening(), "Could not setup proxy server");
+ Interceptor interceptor(cookie);
+ QWebEnginePage page;
+ page.setUrlRequestInterceptor(&interceptor);
+ QSignalSpy cookieSpy(&server, &ProxyServer::cookieMatch);
+ page.load(QUrl("http://www.qt.io"));
+ QTRY_VERIFY2(cookieSpy.count() > 0, "Could not get cookie");
+}
+
#include "tst_proxy.moc"
QTEST_MAIN(tst_Proxy)
diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
index 098656390..92ee791db 100644
--- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
+++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
@@ -197,6 +197,8 @@ private Q_SLOTS:
void dataURLFragment();
void devTools();
void openLinkInDifferentProfile();
+ void openLinkInNewPage_data();
+ void openLinkInNewPage();
void triggerActionWithoutMenu();
void dynamicFrame();
@@ -204,6 +206,7 @@ private Q_SLOTS:
void notificationRequest();
void sendNotification();
void contentsSize();
+ void notificationPermission();
void setLifecycleState();
void setVisible();
@@ -227,6 +230,7 @@ private Q_SLOTS:
void renderProcessCrashed();
void renderProcessPid();
void backgroundColor();
+ void audioMuted();
private:
static QPoint elementCenter(QWebEnginePage *page, const QString &id);
@@ -3378,6 +3382,162 @@ void tst_QWebEnginePage::openLinkInDifferentProfile()
QVERIFY(spy2.takeFirst().value(0).toBool());
}
+// What does createWindow do?
+enum class OpenLinkInNewPageDecision {
+ // Returns nullptr,
+ ReturnNull,
+ // Returns this,
+ ReturnSelf,
+ // Returns page != this
+ ReturnOther,
+};
+
+// What causes createWindow to be called?
+enum class OpenLinkInNewPageCause {
+ // User clicks on a link with target=_blank.
+ TargetBlank,
+ // User clicks with MiddleButton.
+ MiddleClick,
+};
+
+// What happens after createWindow?
+enum class OpenLinkInNewPageEffect {
+ // The navigation request disappears into the ether.
+ Blocked,
+ // The navigation request becomes a navigation in the original page.
+ LoadInSelf,
+ // The navigation request becomes a navigation in a different page.
+ LoadInOther,
+};
+
+Q_DECLARE_METATYPE(OpenLinkInNewPageCause)
+Q_DECLARE_METATYPE(OpenLinkInNewPageDecision)
+Q_DECLARE_METATYPE(OpenLinkInNewPageEffect)
+
+void tst_QWebEnginePage::openLinkInNewPage_data()
+{
+ using Decision = OpenLinkInNewPageDecision;
+ using Cause = OpenLinkInNewPageCause;
+ using Effect = OpenLinkInNewPageEffect;
+
+ QTest::addColumn<Decision>("decision");
+ QTest::addColumn<Cause>("cause");
+ QTest::addColumn<Effect>("effect");
+
+ // Note that the meaning of returning nullptr from createWindow is not
+ // consistent between the TargetBlank and MiddleClick scenarios.
+ //
+ // With TargetBlank, the open-in-new-page disposition comes from the HTML
+ // target attribute; something the user is probably not aware of. Returning
+ // nullptr is interpreted as a decision by the app to block an unwanted
+ // popup.
+ //
+ // With MiddleClick, the open-in-new-page disposition comes from the user's
+ // explicit intent. Returning nullptr is then interpreted as a failure by
+ // the app to fulfill this intent, which we try to compensate by ignoring
+ // the disposition and performing the navigation request normally.
+
+ QTest::newRow("BlockPopup") << Decision::ReturnNull << Cause::TargetBlank << Effect::Blocked;
+ QTest::newRow("IgnoreIntent") << Decision::ReturnNull << Cause::MiddleClick << Effect::LoadInSelf;
+ QTest::newRow("OverridePopup") << Decision::ReturnSelf << Cause::TargetBlank << Effect::LoadInSelf;
+ QTest::newRow("OverrideIntent") << Decision::ReturnSelf << Cause::MiddleClick << Effect::LoadInSelf;
+ QTest::newRow("AcceptPopup") << Decision::ReturnOther << Cause::TargetBlank << Effect::LoadInOther;
+ QTest::newRow("AcceptIntent") << Decision::ReturnOther << Cause::MiddleClick << Effect::LoadInOther;
+}
+
+void tst_QWebEnginePage::openLinkInNewPage()
+{
+ using Decision = OpenLinkInNewPageDecision;
+ using Cause = OpenLinkInNewPageCause;
+ using Effect = OpenLinkInNewPageEffect;
+
+ class Page : public QWebEnginePage
+ {
+ public:
+ Page *targetPage = nullptr;
+ QSignalSpy spy{this, &QWebEnginePage::loadFinished};
+ Page(QWebEngineProfile *profile) : QWebEnginePage(profile) {}
+ private:
+ QWebEnginePage *createWindow(WebWindowType) override { return targetPage; }
+ };
+
+ class View : public QWebEngineView
+ {
+ public:
+ View(Page *page)
+ {
+ resize(500, 500);
+ setPage(page);
+ }
+ };
+
+ QFETCH(Decision, decision);
+ QFETCH(Cause, cause);
+ QFETCH(Effect, effect);
+
+ QWebEngineProfile profile;
+ Page page1(&profile);
+ Page page2(&profile);
+ View view1(&page1);
+ View view2(&page2);
+
+ view1.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view1));
+
+ page1.setHtml("<html><body>"
+ "<a id='link' href='data:,hello' target='_blank'>link</a>"
+ "</body></html>");
+ QTRY_COMPARE(page1.spy.count(), 1);
+ QVERIFY(page1.spy.takeFirst().value(0).toBool());
+
+ switch (decision) {
+ case Decision::ReturnNull:
+ page1.targetPage = nullptr;
+ break;
+ case Decision::ReturnSelf:
+ page1.targetPage = &page1;
+ break;
+ case Decision::ReturnOther:
+ page1.targetPage = &page2;
+ break;
+ }
+
+ Qt::MouseButton button;
+ switch (cause) {
+ case Cause::TargetBlank:
+ button = Qt::LeftButton;
+ break;
+ case Cause::MiddleClick:
+ button = Qt::MiddleButton;
+ break;
+ }
+ QTest::mouseClick(view1.focusProxy(), button, {}, elementCenter(&page1, "link"));
+
+ switch (effect) {
+ case Effect::Blocked:
+ // Nothing to test
+ break;
+ case Effect::LoadInSelf:
+ QTRY_COMPARE(page1.spy.count(), 1);
+ QVERIFY(page1.spy.takeFirst().value(0).toBool());
+ QCOMPARE(page2.spy.count(), 0);
+ if (decision == Decision::ReturnSelf && cause == Cause::TargetBlank)
+ // History was discarded due to AddNewContents
+ QCOMPARE(page1.history()->count(), 1);
+ else
+ QCOMPARE(page1.history()->count(), 2);
+ QCOMPARE(page2.history()->count(), 0);
+ break;
+ case Effect::LoadInOther:
+ QTRY_COMPARE(page2.spy.count(), 1);
+ QVERIFY(page2.spy.takeFirst().value(0).toBool());
+ QCOMPARE(page1.spy.count(), 0);
+ QCOMPARE(page1.history()->count(), 1);
+ QCOMPARE(page2.history()->count(), 1);
+ break;
+ }
+}
+
void tst_QWebEnginePage::triggerActionWithoutMenu()
{
// Calling triggerAction should not crash even when for
@@ -3455,6 +3615,18 @@ void tst_QWebEnginePage::notificationRequest()
QCOMPARE(page.getPermission(), permission);
}
+void tst_QWebEnginePage::notificationPermission()
+{
+ QWebEngineProfile otr;
+ QWebEnginePage page(&otr, nullptr);
+ QSignalSpy spy(&page, &QWebEnginePage::loadFinished);
+ page.setHtml(QString("<html><body>Test</body></html>"), QUrl("https://www.example.com"));
+ QTRY_COMPARE(spy.count(), 1);
+ QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), QLatin1String("default"));
+ page.setFeaturePermission(QUrl("https://www.example.com"), QWebEnginePage::Notifications, QWebEnginePage::PermissionGrantedByUser);
+ QTRY_COMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), QLatin1String("granted"));
+}
+
void tst_QWebEnginePage::sendNotification()
{
NotificationPage page(QWebEnginePage::PermissionGrantedByUser);
@@ -4432,6 +4604,24 @@ void tst_QWebEnginePage::backgroundColor()
QTRY_COMPARE(view.grab().toImage().pixelColor(center), Qt::green);
}
+void tst_QWebEnginePage::audioMuted()
+{
+ QWebEngineProfile profile;
+ QWebEnginePage page(&profile);
+ QSignalSpy spy(&page, &QWebEnginePage::audioMutedChanged);
+
+ QCOMPARE(page.isAudioMuted(), false);
+ page.setAudioMuted(true);
+ loadSync(&page, QUrl("about:blank"));
+ QCOMPARE(page.isAudioMuted(), true);
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy[0][0], QVariant(true));
+ page.setAudioMuted(false);
+ QCOMPARE(page.isAudioMuted(), false);
+ QCOMPARE(spy.count(), 2);
+ QCOMPARE(spy[1][0], QVariant(false));
+}
+
static QByteArrayList params = {QByteArrayLiteral("--use-fake-device-for-media-stream")};
W_QTEST_MAIN(tst_QWebEnginePage, params)
diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
index 6350c8510..00d4bae5a 100644
--- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
+++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
@@ -222,18 +222,6 @@ private:
}
};
-static bool loadSync(QWebEnginePage *page, const QUrl &url, bool ok = true)
-{
- QSignalSpy spy(page, &QWebEnginePage::loadFinished);
- page->load(url);
- return (!spy.empty() || spy.wait(20000)) && (spy.front().value(0).toBool() == ok);
-}
-
-static bool loadSync(QWebEngineView *view, const QUrl &url, bool ok = true)
-{
- return loadSync(view->page(), url, ok);
-}
-
void tst_QWebEngineProfile::clearDataFromCache()
{
TestServer server;
@@ -258,7 +246,7 @@ void tst_QWebEngineProfile::clearDataFromCache()
QTest::qWait(1000);
QVERIFY(sizeBeforeClear > totalSize(cacheDir));
- QVERIFY(server.stop());
+ (void)server.stop();
}
void tst_QWebEngineProfile::disableCache()
@@ -283,7 +271,7 @@ void tst_QWebEngineProfile::disableCache()
QVERIFY(loadSync(&page, server.url("/hedgehog.html")));
QVERIFY(cacheDir.exists("Cache"));
- QVERIFY(server.stop());
+ (void)server.stop();
}
class RedirectingUrlSchemeHandler : public QWebEngineUrlSchemeHandler
@@ -876,7 +864,7 @@ void tst_QWebEngineProfile::changePersistentPath()
QVERIFY(loadSync(&page, server.url("/hedgehog.html")));
QVERIFY(dataDir2.exists());
- QVERIFY(server.stop());
+ (void)server.stop();
}
void tst_QWebEngineProfile::changeHttpUserAgent()
@@ -949,7 +937,7 @@ void tst_QWebEngineProfile::changeUseForGlobalCertificateVerification()
page.reset(new QWebEnginePage(&profile));
QVERIFY(loadSync(page.get(), server.url("/hedgehog.html")));
// Don't check for error: there can be disconnects during GET hedgehog.png.
- server.stop();
+ (void)server.stop();
}
void tst_QWebEngineProfile::changePersistentCookiesPolicy()
@@ -973,7 +961,7 @@ void tst_QWebEngineProfile::changePersistentCookiesPolicy()
QVERIFY(loadSync(&page, server.url("/hedgehog.html")));
QVERIFY(dataDir.exists("Cookies"));
- QVERIFY(server.stop());
+ (void)server.stop();
}
class InitiatorSpy : public QWebEngineUrlSchemeHandler
diff --git a/tests/auto/widgets/util.h b/tests/auto/widgets/util.h
index ca03c5833..cb58f4243 100644
--- a/tests/auto/widgets/util.h
+++ b/tests/auto/widgets/util.h
@@ -36,6 +36,7 @@
#include <QSignalSpy>
#include <QTimer>
#include <qwebenginepage.h>
+#include <qwebengineview.h>
#if !defined(TESTS_SOURCE_DIR)
#define TESTS_SOURCE_DIR ""
@@ -160,6 +161,18 @@ static inline QUrl baseUrlSync(QWebEnginePage *page)
return spy.waitForResult().toUrl();
}
+static inline bool loadSync(QWebEnginePage *page, const QUrl &url, bool ok = true)
+{
+ QSignalSpy spy(page, &QWebEnginePage::loadFinished);
+ page->load(url);
+ return (!spy.empty() || spy.wait(20000)) && (spy.front().value(0).toBool() == ok);
+}
+
+static inline bool loadSync(QWebEngineView *view, const QUrl &url, bool ok = true)
+{
+ return loadSync(view->page(), url, ok);
+}
+
#define W_QSKIP(a, b) QSKIP(a)
#define W_QTEST_MAIN(TestObject, params) \
diff --git a/tools/scripts/version_resolver.py b/tools/scripts/version_resolver.py
index 20141e4b2..b3181db10 100644
--- a/tools/scripts/version_resolver.py
+++ b/tools/scripts/version_resolver.py
@@ -38,7 +38,7 @@ import json
import urllib2
import git_submodule as GitSubmodule
-chromium_version = '80.0.3987.136'
+chromium_version = '80.0.3987.163'
chromium_branch = '3987'
ninja_version = 'v1.8.2'