diff --git a/.qmake.conf b/.qmake.conf index b6a222b54b7..6b2ef0b4398 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -5,4 +5,4 @@ QTWEBENGINE_OUT_ROOT = $$shadowed($$PWD) load(qt_build_config) CONFIG += warning_clean -MODULE_VERSION = 5.12.3 +MODULE_VERSION = 5.12.12 diff --git a/configure.pri b/configure.pri index 26c57ce61fb..897bea54098 100644 --- a/configure.pri +++ b/configure.pri @@ -1,3 +1,5 @@ +include(src/core/config/functions.pri) + # this must be done outside any function QTWEBENGINE_SOURCE_TREE = $$PWD @@ -297,12 +299,12 @@ defineTest(qtConfTest_isWindowsHostCompiler64) { # Fixme QTBUG-71772 defineTest(qtConfTest_hasThumbFlag) { - FLAG = $$extractCFlag("-mthumb") + FLAG = $$qtwebengine_extractCFlag("-mthumb") !isEmpty(FLAG): return(true) - FLAG = $$extractCFlag("-marm") + FLAG = $$qtwebengine_extractCFlag("-marm") !isEmpty(FLAG): return(false) - MARCH = $$extractCFlag("-march=.*") + MARCH = $$qtwebengine_extractCFlag("-march=.*") MARMV = $$replace(MARCH, "armv",) !isEmpty(MARMV) { MARMV = $$split(MARMV,) diff --git a/dist/changes-5.12.10 b/dist/changes-5.12.10 new file mode 100644 index 00000000000..3f706d6b11c --- /dev/null +++ b/dist/changes-5.12.10 @@ -0,0 +1,91 @@ +Qt 5.12.10 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.12.9. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + + https://doc.qt.io/qt-5.12/index.html + +The Qt version 5.12 series is binary compatible with the 5.11.x series. +Applications compiled for 5.11 will continue to run with 5.12. + +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. + +**************************************************************************** +* Library * +**************************************************************************** + +General +------- + + - [QTBUG-75391] Fix pasting Unicode characters copied from WebEngine on macOS + - [QTBUG-83710] Fix input of keys requiring AltGr on Windows + - [QTBUG-84337] Fixed GPU switching with CoreProfile on macOS (for e.g. AirPlay) + - [QTBUG-86018] Fixed build with Bison 3.7 + - Fixed build with the linux-clang 10 spec + +Chromium +-------- + + - Security fixes from Chromium up to version 86.0.4240.111, including: + + - CVE-2020-6459 + - CVE-2020-6462: Use after free in task scheduling + - CVE-2020-6470 + - CVE-2020-6474 + - CVE-2020-6481 + - CVE-2020-6489 + - CVE-2020-6490 + - CVE-2020-6511: Side-channel information leakage in CSP + - CVE-2020-6512: Type Confusion in V8 + - CVE-2020-6513: Heap buffer overflow in PDFium + - CVE-2020-6514: Inappropriate implementation in WebRTC + - CVE-2020-6518: Use after free in developer tools + - CVE-2020-6523: Out of bounds write in Skia + - CVE-2020-6524: Heap buffer overflow in WebAudio + - CVE-2020-6529: Inappropriate implementation in WebRTC + - CVE-2020-6532: Use after free in SCTP + - CVE-2020-6535: Insufficient data validation in WebUI + - CVE-2020-6534: Heap buffer overflow in WebRTC + - CVE-2020-6540: Heap buffer overflow in Skia + - CVE-2020-6541: Use after free in WebUSB + - CVE-2020-6542: Use after free in ANGLE + - CVE-2020-6543: Use after free in task scheduling + - CVE-2020-6544: Use after free in media + - CVE-2020-6545: Use after free in audio + - CVE-2020-6548: Heap buffer overflow in Skia + - CVE-2020-6549: Use after free in media + - CVE-2020-6559: Use after free in presentation API + - CVE-2020-6561: Inappropriate implementation in Content Security Policy + - CVE-2020-6562: Insufficient policy enforcement in Blink + - CVE-2020-6569: Integer overflow in WebUSB + - CVE-2020-6571: Incorrect security UI in Omnibox + - CVE-2020-6573: Use after free in video + - CVE-2020-15962: Insufficient policy enforcement in serial + - CVE-2020-15964: Insufficient data validation in media + - CVE-2020-15965: Out of bounds write in V8 + - CVE-2020-15968: Use after free in Blink + - CVE-2020-15969: Use after free in WebRTC. + - CVE-2020-15999: Heap buffer overflow in freetype + - CVE-2020-16001: Use after free in media. + - CVE-2020-16002: Use after free in PDFium. + - CVE-2020-16003: Use after free in printing. + - Security Bug 1051439 + - Security bug 1052492 + - Security bug 1054229 + - Security Bug 1057369 + - Security Bug 1058515 + - Security bug 1065122 + - Security bug 1065731 + - Security bugs 1087629 and 1029569 + - Security issue 1098860 + - Security issue 1102137 + - Security bug 1102408 + - Security bug 1111149 + diff --git a/dist/changes-5.12.4 b/dist/changes-5.12.4 new file mode 100644 index 00000000000..986c6871bc6 --- /dev/null +++ b/dist/changes-5.12.4 @@ -0,0 +1,77 @@ +Qt 5.12.4 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.12.0 through 5.12.3. + +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.12 series is binary compatible with the 5.11.x series. +Applications compiled for 5.11 will continue to run with 5.12. + +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. + +**************************************************************************** +* Qt 5.12.4 Changes * +**************************************************************************** + +General +------- + + - [QTBUG-57729, QTBUG-75539] Fixed automatic NTLM authentication, and added + support for --auth-server-whitelist. + - [QTBUG-60203] Fixed setting referer HTTP header in request interceptor. + - [QTBUG-74251, QTBUG-74698] Normalize download paths on Windows. + - [QTBUG-74864] Fixed resolving relative URLs with custom schemes. + - [QTBUG-75092] Fixed printing in landscape orientation. + - [QTBUG-75212] Added support for building with VS 2019. + - [QTBUG-75304] Fixed running non-MainWorld DocumentCreations scripts when + JavaScript is disabled. + - [QTBUG-75465] Fixed -no-gui builds. + - [QTBUG-75629] Fixed crash on Linux when pulseaudio had no devices. + - [QTBUG-76045] Fixed desktop capture on macOS. + - [QTBUG-73799] Try different versions when creating a CoreProfile context on macOS + +Chromium +-------- + + - Security fixes from Chromium up to version 74.0.3729.157, including: + + - CVE-2019-5805 + - CVE-2019-5806 + - CVE-2019-5808 + - CVE-2019-5814 + - CVE-2019-5815 + - CVE-2019-5818 + - CVE-2019-5819 + - CVE-2019-5820 + - CVE-2019-5821 + - CVE-2019-5822 + - CVE-2019-5823 + - CVE-2019-5824 + - CVE-2019-5825 + - CVE-2019-5826 + - CVE-2019-5827 + - security issue 894933 + - security issue 908669 + - security issue 931949 + - security issue 937663 + - security issue 939316 + - security issue 940205 + - security issue 949015 + - security issue 951322 + +Qt WebEngine Widgets +-------------------- + + - [QTBUG-75131, QTBUG-75175] Fixed QWebEngineView::setPage not deleting + old page. + - [QTBUG-75547] Fixed crash on exit when page and profile were deleted in + the wrong order. + - [QTBUG-75566] Added path validation to QWebEngineDownloadItem::setPath(). diff --git a/dist/changes-5.12.5 b/dist/changes-5.12.5 new file mode 100644 index 00000000000..15f0bcde679 --- /dev/null +++ b/dist/changes-5.12.5 @@ -0,0 +1,80 @@ +Qt 5.12.5 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.12.0 through 5.12.4. + +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.12 series is binary compatible with the 5.11.x series. +Applications compiled for 5.11 will continue to run with 5.12. + +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. + +**************************************************************************** +* Qt 5.12.5 Changes * +**************************************************************************** + +General +------- + + - [QTBUG-62106] Fixed possible crash after rapid tapping. + - [QTBUG-75884] Fixed crash on setHttpUserAgent. + - [QTBUG-76249] Fixed user-agent on some new windows. + - [QTBUG-76268] Fixed tab key send on minimize. + - [QTBUG-76347] Fixed duplicate events being send from tablets. + - [QTBUG-76828] Clear shared context on exit. + - [QTBUG-76958] Fixed possible crash when loading in background. + +Chromium +-------- + + - Security fixes from Chromium up to version 76.0.3809.87, including: + + - CVE-2019-5829 + - CVE-2019-5831 + - CVE-2019-5832 + - CVE-2019-5837 + - CVE-2019-5839 + - CVE-2019-5842 + - CVE-2019-5851 + - CVE-2019-5852 + - CVE-2019-5854 + - CVE-2019-5855 + - CVE-2019-5856 + - CVE-2019-5857 + - CVE-2019-5860 + - CVE-2019-5861 + - CVE-2019-5862 + - CVE-2019-5865 + - Critical security issue 977057 + - Security bug 934161 + - Security bug 939644 + - Security bug 948172 + - Security bug 948228 + - Security bug 948944 + - Security bug 950005 + - Security bug 952849 + - Security bug 956625 + - Security bug 958457 + - Security bug 958689 + - Security bug 959193 + - Security bug 959518 + - Security bug 958717 + - Security bug 960785 + - Security bug 961674 + - Security bug 961597 + - Security bug 962083 + - Security bug 964002 + - Security bug 973893 + - Security bug 974627 + - Security bug 976050 + - Security bug 981602 + - Security bug 983850 + - Security bug 983938 diff --git a/dist/changes-5.12.6 b/dist/changes-5.12.6 new file mode 100644 index 00000000000..386eac2338d --- /dev/null +++ b/dist/changes-5.12.6 @@ -0,0 +1,64 @@ +Qt 5.12.6 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.12.0 through 5.12.5. + +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.12 series is binary compatible with the 5.11.x series. +Applications compiled for 5.11 will continue to run with 5.12. + +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. + +**************************************************************************** +* Qt 5.12.6 Changes * +**************************************************************************** + +General +------- + + - [QTBUG-77072,QTBUG-78084] Fixed flashing when closing popup on macOS. + - [QTBUG-78733] Fixed building with Visual Studio 2019. + - [QTBUG-77037] Fixed building with Pulseaudio 13. + - Fixed building with system ICU 65. + +Chromium +-------- + + - Security fixes from Chromium up to version 77.0.3865.120, including: + + - CVE-2019-5869 + - CVE-2019-5870 + - CVE-2019-5875 + - CVE-2019-5876 + - CVE-2019-13659 + - CVE-2019-13660 + - CVE-2019-13664 + - CVE-2019-13665 + - CVE-2019-13674 + - CVE-2019-13675 + - CVE-2019-13687 + - CVE-2019-13688 + - CVE-2019-13691 + - CVE-2019-13692 + - CVE-2019-13693 + - CVE-2019-13694 + - CVE-2019-13695 + - CVE-2019-13697 + - Security issue 946351 + - Security issue 957160 + - Security issue 960354 + - Security issue 964938 + - Security issue 979373 + - Security issue 981459 + - Security issue 986727 + - Security issue 990234 + + - Security fix from Chrome 78: CVE-2019-13720. diff --git a/dist/changes-5.12.7 b/dist/changes-5.12.7 new file mode 100644 index 00000000000..4fdbb17d08d --- /dev/null +++ b/dist/changes-5.12.7 @@ -0,0 +1,78 @@ +Qt 5.12.7 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.12.0 through 5.12.6. + +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.12 series is binary compatible with the 5.11.x series. +Applications compiled for 5.11 will continue to run with 5.12. + +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. + +**************************************************************************** +* Qt 5.12.7 Changes * +**************************************************************************** + +General +------- + + - [QTBUG-78280, QTBUG-80696] Fix pepper flash plugin permission + +Chromium +-------- + + - Security fixes from Chromium up to version 79.0.3945.130, including: + + - CVE-2019-13700 + - CVE-2019-13714 + - CVE-2019-13715 + - CVE-2019-13718 + - CVE-2019-13728 + - CVE-2019-13730 + - CVE-2019-13732 + - CVE-2019-13734 + - CVE-2019-13735 + - CVE-2019-13736 + - CVE-2019-13737 + - CVE-2019-13738 + - CVE-2019-13739 + - CVE-2019-13741 + - CVE-2019-13747 + - CVE-2019-13750 + - CVE-2019-13751 + - CVE-2019-13752 + - CVE-2019-13753 + - CVE-2019-13757 + - CVE-2019-13758 + - CVE-2019-13761 + - CVE-2019-13762 + - CVE-2019-13764 + - CVE-2019-15903 + - CVE-2020-0601 + - CVE-2020-6377 + - Security bug 889276 + - Security bug 955191 + - Security bug 993266 + - Security bug 1006544 + - Security bug 1011551 + - Security bug 1017020 + - Security bug 1017961 + - Security bug 1018406 + - Security bug 1025089 + - Security bug 1027905 + - Security bug 1028722 + - Security bug 1029002 + - Security bug 1029027 + - Security bug 1029210 + - Security bug 1029506 + - Security bug 1033260 + - Security bug 1034695 + - Security bug 1035371 diff --git a/dist/changes-5.12.8 b/dist/changes-5.12.8 new file mode 100644 index 00000000000..5a474e9c08c --- /dev/null +++ b/dist/changes-5.12.8 @@ -0,0 +1,73 @@ +Qt 5.12.8 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.12.0 through 5.12.7. + +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.12 series is binary compatible with the 5.11.x series. +Applications compiled for 5.11 will continue to run with 5.12. + +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. + +**************************************************************************** +* Qt 5.12.8 Changes * +**************************************************************************** + +General +------- + + - [QTBUG-81313] Enabled running with glibc 2.30 in Linux sandbox + - [QTBUG-81521] Update navigation actions when load finishes in a subframe + - [QTBUG-78490] Fixed crash when initiaing loads from urlChanged handlers + +Chromium +-------- + + - Security fixes from Chromium up to version 80.0.3987.149, including: + + - CVE-2019-18197 - Multiple vulnerabilities in XML + - CVE-2019-19923 - Out of bounds memory access in SQLite + - CVE-2019-19925 - Multiple vulnerabilities in SQLite + - CVE-2019-19926 - Inappropriate implementation in SQLite + - CVE-2019-20503 - Out of bounds read in usersctplib + - CVE-2020-6381 - Integer overflow in Javascript + - CVE-2020-6383 - Type confusion in V8 + - CVE-2020-6384 - Use after free in WebAudio + - CVE-2020-6388 - Out of bounds memory access in WebAudio + - CVE-2020-6389 - Out of bounds write in WebRTC + - CVE-2020-6391 - Insufficient validation of untrusted input in Blink + - CVE-2020-6393 - Insufficient policy enforcement in Blink + - CVE-2020-6394 - Insufficient policy enforcement in Blink + - CVE-2020-6398 - Uninitialized use in PDFium + - CVE-2020-6399 - Insufficient policy enforcement in AppCache + - CVE-2020-6401 + - CVE-2020-6405 - Out of bounds read in SQLite + - CVE-2020-6406 - Use after free in audio + - CVE-2020-6410 - Insufficient policy enforcement in navigation + - CVE-2020-6411 + - CVE-2020-6412 - Insufficient validation of untrusted input in Omnibox + - CVE-2020-6413 - Inappropriate implementation in Blink + - CVE-2020-6418 - Type confusion in V8 + - CVE-2020-6420 - Insufficient policy enforcement in media + - Security bug 1016038 + - Security bug 1016506 + - Security bug 1018629 + - Security bug 1020031 + - Security bug 1025442 + - Security bug 1026293 + - Security bug 1029865 + - Security bug 1031909 + - Security bug 1033461 + - Security bug 1035723 + - Security bug 1040700 + - Security bug 1044570 + - Security bug 1047097 + diff --git a/dist/changes-5.12.9 b/dist/changes-5.12.9 new file mode 100644 index 00000000000..57badb1a30d --- /dev/null +++ b/dist/changes-5.12.9 @@ -0,0 +1,55 @@ +Qt 5.12.9 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.12.0 through 5.12.8. + +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.12 series is binary compatible with the 5.11.x series. +Applications compiled for 5.11 will continue to run with 5.12. + +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. + +**************************************************************************** +* Qt 5.12.9 Changes * +**************************************************************************** + +General +------- + + - [QTBUG-83318] Fix macOS build after 10.15.4 + +Chromium +-------- + + - Security fixes from Chromium up to version 83.0.4103.97, including: + + - CVE-2020-6423 + - CVE-2020-6431 + - CVE-2020-6432 + - CVE-2020-6441 + - CVE-2020-6443 + - CVE-2020-6450 + - CVE-2020-6451 + - CVE-2020-6452 + - CVE-2020-6455 + - CVE-2020-6460 + - CVE-2020-6461 + - CVE-2020-6464 + - CVE-2020-6467 + - CVE-2020-6468 + - CVE-2020-6493 + - Security Bug 1025302 + - Security Bug 1025740 + - Security Bug 1050090 + - Security Bug 1065094 + - Security Bug 1066893 + - Security Bug 1070012 + - Security Bug 1075907 diff --git a/examples/webengine/quicknanobrowser/BrowserWindow.qml b/examples/webengine/quicknanobrowser/BrowserWindow.qml index 0ac69ef241c..17e7941bb70 100644 --- a/examples/webengine/quicknanobrowser/BrowserWindow.qml +++ b/examples/webengine/quicknanobrowser/BrowserWindow.qml @@ -115,7 +115,7 @@ ApplicationWindow { Action { shortcut: StandardKey.AddTab onTriggered: { - tabs.createEmptyTab(currentWebView.profile); + tabs.createEmptyTab(tabs.count != 0 ? currentWebView.profile : defaultProfile); tabs.currentIndex = tabs.count - 1; addressBar.forceActiveFocus(); addressBar.selectAll(); @@ -294,18 +294,22 @@ ApplicationWindow { id: offTheRecordEnabled text: "Off The Record" checkable: true - checked: currentWebView.profile === otrProfile + checked: currentWebView && currentWebView.profile === otrProfile onToggled: function(checked) { - currentWebView.profile = checked ? otrProfile : defaultProfile; + if (currentWebView) { + currentWebView.profile = checked ? otrProfile : defaultProfile; + } } } MenuItem { id: httpDiskCacheEnabled text: "HTTP Disk Cache" - checkable: !currentWebView.profile.offTheRecord - checked: (currentWebView.profile.httpCacheType === WebEngineProfile.DiskHttpCache) + checkable: currentWebView && !currentWebView.profile.offTheRecord + checked: currentWebView && (currentWebView.profile.httpCacheType === WebEngineProfile.DiskHttpCache) onToggled: function(checked) { - currentWebView.profile.httpCacheType = checked ? WebEngineProfile.DiskHttpCache : WebEngineProfile.MemoryHttpCache; + if (currentWebView) { + currentWebView.profile.httpCacheType = checked ? WebEngineProfile.DiskHttpCache : WebEngineProfile.MemoryHttpCache; + } } } MenuItem { @@ -373,6 +377,52 @@ ApplicationWindow { anchors.right: parent.right Component.onCompleted: createEmptyTab(defaultProfile) + // Add custom tab view style so we can customize the tabs to include a close button + style: TabViewStyle { + property color frameColor: "#999" + property color fillColor: "#eee" + property color nonSelectedColor: "#ddd" + frameOverlap: 1 + frame: Rectangle { + color: "#eee" + border.color: frameColor + } + tab: Rectangle { + id: tabRectangle + color: styleData.selected ? fillColor : nonSelectedColor + border.width: 1 + border.color: frameColor + implicitWidth: Math.max(text.width + 30, 80) + implicitHeight: Math.max(text.height + 10, 20) + Rectangle { height: 1 ; width: parent.width ; color: frameColor} + Rectangle { height: parent.height ; width: 1; color: frameColor} + Rectangle { x: parent.width - 2; height: parent.height ; width: 1; color: frameColor} + Text { + id: text + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 6 + text: styleData.title + elide: Text.ElideRight + color: styleData.selected ? "black" : frameColor + } + Button { + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: 4 + height: 12 + style: ButtonStyle { + background: Rectangle { + implicitWidth: 12 + implicitHeight: 12 + color: control.hovered ? "#ccc" : tabRectangle.color + Text {text: "x" ; anchors.centerIn: parent ; color: "gray"} + }} + onClicked: tabs.removeTab(styleData.index); + } + } + } + Component { id: tabComponent WebEngineView { diff --git a/mkspecs/features/configure.prf b/mkspecs/features/configure.prf index d7d382a4ff8..e407ede1763 100644 --- a/mkspecs/features/configure.prf +++ b/mkspecs/features/configure.prf @@ -4,6 +4,10 @@ load(functions) load(platform) defineTest(runConfigure) { + !qtHaveModule(gui) { + skipBuild("QtWebEngine requires QtGui.") + return(false) + } !exists(src/3rdparty/chromium) { skipBuild("Submodule qtwebengine-chromium does not exist. Run 'git submodule update --init'.") @@ -90,13 +94,9 @@ defineTest(runConfigure) { } } - !qtConfig(webengine-embedded-build): qtConfig(xcb) { - for(package, $$list("libdrm xcomposite xcursor xi xtst")) { - !qtConfig(webengine-system-$$package) { - skipBuild("A suitable version of $$package could not be found.") - return(false) - } - } + !qtConfig(webengine-embedded-build): qtConfig(xcb) : !qtConfig(webengine-ozone-x11) { + skipBuild("Could not find all necessary libraries for qpa-xcb support") + return(false) } } } diff --git a/mkspecs/features/functions.prf b/mkspecs/features/functions.prf index f6aeea21a79..d3eda85b2d5 100644 --- a/mkspecs/features/functions.prf +++ b/mkspecs/features/functions.prf @@ -10,15 +10,6 @@ defineReplace(getChromiumSrcDir) { return($$git_chromium_src_dir) } -defineReplace(extractCFlag) { - CFLAGS = $$QMAKE_CC $$QMAKE_CFLAGS - OPTION = $$find(CFLAGS, $$1) - OPTION = $$split(OPTION, =) - PARAM = $$member(OPTION, 1) - !isEmpty(PARAM): return ($$PARAM) - return ($$OPTION) -} - defineReplace(which) { out = $$1 win32 { @@ -130,11 +121,3 @@ defineReplace(pkgConfigHostExecutable) { return($$system_quote($$system_path($$wrapper_name))) } -defineTest(hasX11Dependencies) { - for(package, $$list("libdrm xcomposite xcursor xi xtst")) { - !qtConfig(webengine-system-$$package) { - return(false) - } - } - return(true) -} diff --git a/src/3rdparty b/src/3rdparty index 5bbb0fff1e6..b249cd9b645 160000 --- a/src/3rdparty +++ b/src/3rdparty @@ -1 +1 @@ -Subproject commit 5bbb0fff1e645bb29cd740767cda698e4e6d23e7 +Subproject commit b249cd9b6455cafacb8f7c99d300cefc59881cc2 diff --git a/src/core/api/qtwebenginecoreglobal.cpp b/src/core/api/qtwebenginecoreglobal.cpp index a415ade92a4..5a634641b23 100644 --- a/src/core/api/qtwebenginecoreglobal.cpp +++ b/src/core/api/qtwebenginecoreglobal.cpp @@ -79,6 +79,8 @@ static QOpenGLContext *shareContext; static void deleteShareContext() { + if (qt_gl_global_share_context() == shareContext) + qt_gl_set_global_share_context(nullptr); delete shareContext; shareContext = 0; } diff --git a/src/core/api/qwebengineurlscheme.cpp b/src/core/api/qwebengineurlscheme.cpp index 9b04f46c584..9cc5b5056be 100644 --- a/src/core/api/qwebengineurlscheme.cpp +++ b/src/core/api/qwebengineurlscheme.cpp @@ -378,6 +378,11 @@ void QWebEngineUrlScheme::registerScheme(const QWebEngineUrlScheme &scheme) return; } + if (url::IsStandard(scheme.d->name.data(), url::Component(0, scheme.d->name.size()))) { + qWarning() << "QWebEngineUrlScheme::registerScheme: Scheme" << scheme.name() << "is a standard scheme"; + return; + } + if (g_schemesLocked) { qWarning() << "QWebEngineUrlScheme::registerScheme: Too late to register scheme" << scheme.name(); return; diff --git a/src/core/certificate_error_controller.cpp b/src/core/certificate_error_controller.cpp index a747451df4a..3309db8f1ad 100644 --- a/src/core/certificate_error_controller.cpp +++ b/src/core/certificate_error_controller.cpp @@ -79,14 +79,14 @@ CertificateErrorControllerPrivate::CertificateErrorControllerPrivate(int cert_er const net::SSLInfo& ssl_info, const GURL &request_url, content::ResourceType resource_type, - bool _overridable, + bool fatal_error, bool strict_enforcement, const base::Callback& cb ) : certError(CertificateErrorController::CertificateError(cert_error)) , requestUrl(toQt(request_url)) , resourceType(CertificateErrorController::ResourceType(resource_type)) - , overridable(_overridable) + , fatalError(fatal_error) , strictEnforcement(strict_enforcement) , callback(cb) { @@ -118,7 +118,7 @@ QUrl CertificateErrorController::url() const bool CertificateErrorController::overridable() const { - return d->overridable; + return !d->fatalError && !d->strictEnforcement; } bool CertificateErrorController::strictEnforcement() const diff --git a/src/core/certificate_error_controller_p.h b/src/core/certificate_error_controller_p.h index abde9a7d541..3b4d0f3bf64 100644 --- a/src/core/certificate_error_controller_p.h +++ b/src/core/certificate_error_controller_p.h @@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE class CertificateErrorControllerPrivate { public: - CertificateErrorControllerPrivate(int cert_error, const net::SSLInfo& ssl_info, const GURL& request_url, content::ResourceType resource_type, bool overridable, bool strict_enforcement, const base::Callback& callback); + CertificateErrorControllerPrivate(int cert_error, const net::SSLInfo& ssl_info, const GURL& request_url, content::ResourceType resource_type, bool fatal_error, bool strict_enforcement, const base::Callback& callback); void accept(bool accepted); @@ -68,7 +68,7 @@ class CertificateErrorControllerPrivate { QDateTime validStart; QDateTime validExpiry; CertificateErrorController::ResourceType resourceType; - bool overridable; + bool fatalError; bool strictEnforcement; const base::Callback callback; }; diff --git a/src/core/clipboard_qt.cpp b/src/core/clipboard_qt.cpp index 44756bdfe93..c3724d4090f 100644 --- a/src/core/clipboard_qt.cpp +++ b/src/core/clipboard_qt.cpp @@ -235,7 +235,14 @@ void ClipboardQt::WriteText(const char* text_data, size_t text_len) void ClipboardQt::WriteHTML(const char* markup_data, size_t markup_len, const char* url_data, size_t url_len) { - getUncommittedData()->setHtml(QString::fromUtf8(markup_data, markup_len)); + QString markup_string = QString::fromUtf8(markup_data, markup_len); +#if defined (Q_OS_MACOS) + // We need to prepend the charset on macOS to prevent garbled Unicode characters + // when pasting to certain applications (e.g. Notes, TextEdit) + // Mirrors the behavior in ui/base/clipboard/clipboard_mac.mm in Chromium. + markup_string.prepend(QLatin1String("")); +#endif + getUncommittedData()->setHtml(markup_string); } void ClipboardQt::WriteRTF(const char* rtf_data, size_t data_len) diff --git a/src/core/config/common.pri b/src/core/config/common.pri index c8c3d55f6ed..42cf445dff3 100644 --- a/src/core/config/common.pri +++ b/src/core/config/common.pri @@ -114,3 +114,7 @@ qtConfig(webengine-kerberos) { } !msvc: gn_args += enable_iterator_debugging=false + +ccache { + gn_args += cc_wrapper=\"ccache\" +} diff --git a/src/core/config/functions.pri b/src/core/config/functions.pri new file mode 100644 index 00000000000..8c11faa16ad --- /dev/null +++ b/src/core/config/functions.pri @@ -0,0 +1,8 @@ +defineReplace(qtwebengine_extractCFlag) { + CFLAGS = $$QMAKE_CC $$QMAKE_CFLAGS + OPTION = $$find(CFLAGS, $$1) + OPTION = $$split(OPTION, =) + PARAM = $$member(OPTION, 1) + !isEmpty(PARAM): return ($$PARAM) + return ($$OPTION) +} diff --git a/src/core/config/linux.pri b/src/core/config/linux.pri index 9fc8c6e821a..eaecab3c99c 100644 --- a/src/core/config/linux.pri +++ b/src/core/config/linux.pri @@ -1,4 +1,10 @@ include(common.pri) +include(functions.pri) + +defineReplace(extractCFlag) { + return($$qtwebengine_extractCFlag($$1)) +} + QT_FOR_CONFIG += gui-private webenginecore-private gn_args += \ @@ -169,7 +175,7 @@ host_build { } !packagesExist(libpci): gn_args += use_libpci=false - qtConfig(webengine-system-x11): hasX11Dependencies() { + qtConfig(webengine-ozone-x11) { gn_args += ozone_platform_x11=true packagesExist(xscrnsaver): gn_args += use_xscrnsaver=true } diff --git a/src/core/config/mac_osx.pri b/src/core/config/mac_osx.pri index 4426901cf71..7b77a8bf7f4 100644 --- a/src/core/config/mac_osx.pri +++ b/src/core/config/mac_osx.pri @@ -9,6 +9,10 @@ isEmpty(QMAKE_MAC_SDK_VERSION) { isEmpty(QMAKE_MAC_SDK_VERSION): error("Could not resolve SDK version for \'$${QMAKE_MAC_SDK}\'") } +# chromium/build/mac/find_sdk.py expects the SDK version (mac_sdk_min) in Major.Minor format. +# If Patch version is provided it fails with "Exception: No Major.Minor.Patch+ SDK found" +QMAKE_MAC_SDK_VERSION_MAJOR_MINOR = $$section(QMAKE_MAC_SDK_VERSION, ".", 0, 1) + QMAKE_CLANG_DIR = "/usr" QMAKE_CLANG_PATH = $$eval(QMAKE_MAC_SDK.macx-clang.$${QMAKE_MAC_SDK}.QMAKE_CXX) !isEmpty(QMAKE_CLANG_PATH) { @@ -27,7 +31,7 @@ gn_args += \ clang_base_path=\"$${QMAKE_CLANG_DIR}\" \ clang_use_chrome_plugins=false \ mac_deployment_target=\"$${QMAKE_MACOSX_DEPLOYMENT_TARGET}\" \ - mac_sdk_min=\"$${QMAKE_MAC_SDK_VERSION}\" \ + mac_sdk_min=\"$${QMAKE_MAC_SDK_VERSION_MAJOR_MINOR}\" \ mac_views_browser=false \ toolkit_views=false \ use_external_popup_menu=false diff --git a/src/core/configure.json b/src/core/configure.json index 044d855274b..b9c92044434 100644 --- a/src/core/configure.json +++ b/src/core/configure.json @@ -5,11 +5,13 @@ "gui-private", "printsupport" ], + "condition": "module.gui", "testDir": "../../config.tests", "commandline": { "options": { "webengine-alsa": "boolean", "webengine-embedded-build": "boolean", + "webengine-full-debug-info": "boolean", "webengine-icu": { "type": "enum", "name": "webengine-system-icu", "values": { "system": "yes", "qt": "no" } }, "webengine-ffmpeg": { "type": "enum", "name": "webengine-system-ffmpeg", "values": { "system": "yes", "qt": "no" } }, "webengine-opus": { "type": "enum", "name": "webengine-system-opus", "values": { "system": "yes", "qt": "no" } }, @@ -326,6 +328,11 @@ "webengine-arm-thumb" : { "label": "thumb instruction set", "type": "hasThumbFlag" + }, + "webengine-noexecstack" : { + "label": "linker supports -z noexecstack", + "type": "linkerSupportsFlag", + "flag": "-z,noexecstack" } }, @@ -636,6 +643,17 @@ "condition": "config.unix && libs.webengine-x11", "output": [ "privateFeature" ] }, + "webengine-ozone-x11" : { + "label": "Support qpa-xcb", + "condition": "config.unix + && features.webengine-system-x11 + && features.webengine-system-libdrm + && features.webengine-system-xcomposite + && features.webengine-system-xcursor + && features.webengine-system-xi + && features.webengine-system-xtst", + "output": [ "privateFeature" ] + }, "webengine-sanitizer" : { "label": "Sanitizer", "autoDetect": "config.sanitizer && tests.webengine-sanitizer", @@ -657,6 +675,21 @@ "label": "Thumb instruction set", "condition": "config.linux && features.webengine-embedded-build && arch.arm && tests.webengine-arm-thumb", "output": [ "privateFeature" ] + }, + "webengine-full-debug-info": { + "label": "Full debug information", + "purpose": "Enables debug information for Blink and V8.", + "autoDetect": false, + "condition": "config.debug || features.debug_and_release || features.force_debug_info", + "output": [ + { "type": "privateConfig", "name": "v8base_debug" }, + { "type": "privateConfig", "name": "webcore_debug" } + ] + }, + "webengine-noexecstack": { + "label": "linker supports -z noexecstack", + "condition": "config.unix && tests.webengine-noexecstack", + "output": [ "privateFeature" ] } }, @@ -708,6 +741,7 @@ "section": "Qt WebEngine", "entries": [ "webengine-embedded-build", + "webengine-full-debug-info", "webengine-pepper-plugins", "webengine-printing-and-pdf", "webengine-proprietary-codecs", @@ -719,6 +753,11 @@ "webengine-webchannel", "webengine-v8-snapshot", "webengine-kerberos", + { + "type": "feature", + "args": "webengine-ozone-x11", + "condition": "config.unix" + }, { "type": "feature", "args": "webengine-v8-snapshot-support", @@ -791,8 +830,7 @@ "webengine-system-png", "webengine-system-jpeg", "webengine-system-harfbuzz", - "webengine-system-freetype", - "webengine-system-x11" + "webengine-system-freetype" ] }, { @@ -810,6 +848,7 @@ "section": "Required system libraries for qpa-xcb", "condition": "config.unix && !config.macos", "entries": [ + "webengine-system-x11", "webengine-system-libdrm", "webengine-system-xcomposite", "webengine-system-xcursor", diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp index 0a51cc2614f..acd652b15ce 100644 --- a/src/core/content_browser_client_qt.cpp +++ b/src/core/content_browser_client_qt.cpp @@ -355,7 +355,7 @@ void ContentBrowserClientQt::AllowCertificateError(content::WebContents *webCont ssl_info, request_url, resource_type, - !IsCertErrorFatal(cert_error), + IsCertErrorFatal(cert_error), strict_enforcement, callback))); contentsDelegate->allowCertificateError(errorController); diff --git a/src/core/content_client_qt.cpp b/src/core/content_client_qt.cpp index df1bc303de2..61194bae080 100644 --- a/src/core/content_client_qt.cpp +++ b/src/core/content_client_qt.cpp @@ -125,7 +125,8 @@ static QString getLocalAppDataDir() static const int32_t kPepperFlashPermissions = ppapi::PERMISSION_DEV | ppapi::PERMISSION_PRIVATE | ppapi::PERMISSION_BYPASS_USER_GESTURE | - ppapi::PERMISSION_FLASH; + ppapi::PERMISSION_FLASH | + ppapi::PERMISSION_SOCKET; namespace switches { const char kPpapiFlashPath[] = "ppapi-flash-path"; diff --git a/src/core/core_chromium.pri b/src/core/core_chromium.pri index e92dd60ffcb..5566dbef7b5 100644 --- a/src/core/core_chromium.pri +++ b/src/core/core_chromium.pri @@ -33,8 +33,6 @@ RCC_DIR = $$OUT_PWD/$$getConfigDir()/.rcc # whenever we are cross compiling. qtConfig(webengine-embedded-build): DEFINES += QTWEBENGINE_EMBEDDED_SWITCHES -qtConfig(egl): CONFIG += egl - INCLUDEPATH += $$PWD $$PWD/api SOURCES = \ @@ -221,7 +219,7 @@ HEADERS = \ web_event_factory.h -qtConfig(webengine-system-x11): hasX11Dependencies() { +qtConfig(webengine-ozone-x11) { HEADERS += ozone/gl_ozone_glx_qt.h \ ozone/gl_surface_glx_qt.h SOURCES += ozone/gl_surface_glx_qt.cpp \ diff --git a/src/core/core_common.pri b/src/core/core_common.pri index 5f9f3c4f615..01f40eb09e6 100644 --- a/src/core/core_common.pri +++ b/src/core/core_common.pri @@ -2,8 +2,8 @@ # gyp/ninja will take care of the compilation, qmake/make will finish with linking and install. TARGET = QtWebEngineCore -QT += qml quick -QT_PRIVATE += quick-private gui-private core-private webenginecoreheaders-private +QT += qml-private quick-private gui-private core-private +QT_PRIVATE += webenginecoreheaders-private qtConfig(webengine-geolocation): QT += positioning qtConfig(webengine-webchannel): QT += webchannel diff --git a/src/core/core_headers.pro b/src/core/core_headers.pro index 21b5d58c33d..cd5352eb7b1 100644 --- a/src/core/core_headers.pro +++ b/src/core/core_headers.pro @@ -1,5 +1,6 @@ TARGET = QtWebEngineCore CONFIG += no_private_module header_module internal_module no_plist +QT -= core gui MODULE = webenginecoreheaders MODULE_CFG_FILE = qtwebenginecore-config load(qt_module) diff --git a/src/core/core_module.pro b/src/core/core_module.pro index 3b5d37f299c..7bcd916a15f 100644 --- a/src/core/core_module.pro +++ b/src/core/core_module.pro @@ -10,8 +10,6 @@ linking_pri = $$OUT_PWD/$$getConfigDir()/$${TARGET}.pri error("Could not find the linking information that gn should have generated.") } -load(qt_module) - api_library_name = qtwebenginecoreapi$$qtPlatformTargetSuffix() api_library_path = $$OUT_PWD/api/$$getConfigDir() @@ -25,25 +23,34 @@ isEmpty(NINJA_LIBS): error("Missing library files from QtWebEngineCore linking p NINJA_OBJECTS = $$eval($$list($$NINJA_OBJECTS)) # Do manual response file linking for macOS and Linux -RSP_FILE = $$OUT_PWD/$$getConfigDir()/$${TARGET}.rsp -for(object, NINJA_OBJECTS): RSP_CONTENT += $$object -write_file($$RSP_FILE, RSP_CONTENT) -macos:LIBS_PRIVATE += -Wl,-filelist,$$shell_quote($$RSP_FILE) -linux:LIBS_PRIVATE += @$$RSP_FILE +RSP_OBJECT_FILE = $$OUT_PWD/$$getConfigDir()/$${TARGET}_o.rsp +for(object, NINJA_OBJECTS): RSP_O_CONTENT += $$object +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_FILE -linux: LIBS_PRIVATE += -Wl,--start-group $$NINJA_ARCHIVES -Wl,--end-group +win32:QMAKE_LFLAGS += @$${RSP_OBJECT_FILE} +linux:QMAKE_LFLAGS += -Wl,--start-group @$${RSP_ARCHIVE_FILE} -Wl,--end-group else: LIBS_PRIVATE += $$NINJA_ARCHIVES 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 +unix:qtConfig(webengine-noexecstack): \ + QMAKE_LFLAGS += -Wl,-z,noexecstack linux { - QMAKE_LFLAGS += -Wl,--gc-sections -Wl,-O1 -Wl,-z,now - # Embedded address sanitizer symbols are undefined and are picked up by the dynamic link loader - # at runtime. Thus we do not to pass the linker flag below, because the linker would complain - # about the undefined sanitizer symbols. - !sanitizer: QMAKE_LFLAGS += -Wl,-z,defs + # add chromium flags + for(flag, NINJA_LFLAGS) { + # filter out some flags + !contains(flag, .*noexecstack$): \ + !contains(flag, .*as-needed$): \ + !contains(flag, ^-B.*): \ + !contains(flag, ^-fuse-ld.*): \ + QMAKE_LFLAGS += $$flag + } } else { QMAKE_LFLAGS += $$NINJA_LFLAGS } @@ -71,7 +78,7 @@ osx { # API library as response file to the linker. QMAKE_LFLAGS += @$${api_library_path}$${QMAKE_DIR_SEP}$${api_library_name}.lib.objects } else { - LIBS_PRIVATE += -Wl,-whole-archive -l$$api_library_name -Wl,-no-whole-archive + QMAKE_LFLAGS += -Wl,-whole-archive -l$$api_library_name -Wl,-no-whole-archive } win32-msvc* { @@ -84,8 +91,6 @@ win32-msvc* { # and doesn't let Chromium get access to libc symbols through dlsym. CONFIG -= bsymbolic_functions -qtConfig(egl): CONFIG += egl - linux:qtConfig(separate_debug_info): QMAKE_POST_LINK="cd $(DESTDIR) && $(STRIP) --strip-unneeded $(TARGET)" REPACK_DIR = $$OUT_PWD/$$getConfigDir() @@ -154,3 +159,6 @@ OTHER_FILES = \ $$files(../3rdparty/chromium/*.gypi, true) \ $$files(../3rdparty/chromium/*.gn, true) \ $$files(../3rdparty/chromium/*.gni, true) + +load(qt_module) + diff --git a/src/core/delegated_frame_node.cpp b/src/core/delegated_frame_node.cpp index 8ac82dbf186..e2c6e316563 100644 --- a/src/core/delegated_frame_node.cpp +++ b/src/core/delegated_frame_node.cpp @@ -768,7 +768,7 @@ static bool areSharedQuadStatesEqual(const viz::SharedQuadState *layerState, // *structurally* equivalent to the one of the previous frame. // If it is, we will just reuse and update the old nodes where necessary. static bool areRenderPassStructuresEqual(viz::CompositorFrame *frameData, - viz::CompositorFrame *previousFrameData) + viz::CompositorFrame *previousFrameData, const gfx::Rect &viewportRect) { if (!previousFrameData) return false; @@ -776,6 +776,8 @@ static bool areRenderPassStructuresEqual(viz::CompositorFrame *frameData, if (previousFrameData->render_pass_list.size() != frameData->render_pass_list.size()) return false; + auto rootRenderPass = frameData->render_pass_list.back().get(); + for (unsigned i = 0; i < frameData->render_pass_list.size(); ++i) { viz::RenderPass *newPass = frameData->render_pass_list.at(i).get(); viz::RenderPass *prevPass = previousFrameData->render_pass_list.at(i).get(); @@ -786,6 +788,13 @@ static bool areRenderPassStructuresEqual(viz::CompositorFrame *frameData, if (newPass->quad_list.size() != prevPass->quad_list.size()) return false; + auto &&scissorRect = newPass == rootRenderPass ? viewportRect : newPass->output_rect; + auto &&prevScissorRect = newPass == rootRenderPass ? viewportRect : prevPass->output_rect; + if (newPass != rootRenderPass) { + if (scissorRect.IsEmpty() != prevScissorRect.IsEmpty()) + return false; + } + viz::QuadList::ConstBackToFrontIterator it = newPass->quad_list.BackToFrontBegin(); viz::QuadList::ConstBackToFrontIterator end = newPass->quad_list.BackToFrontEnd(); viz::QuadList::ConstBackToFrontIterator prevIt = prevPass->quad_list.BackToFrontBegin(); @@ -803,18 +812,25 @@ static bool areRenderPassStructuresEqual(viz::CompositorFrame *frameData, return false; #endif // GL_OES_EGL_image_external #endif // QT_NO_OPENGL - if (!areSharedQuadStatesEqual(quad->shared_quad_state, prevQuad->shared_quad_state)) + + auto sharedState = quad->shared_quad_state, prevSharedState = prevQuad->shared_quad_state; + if (!areSharedQuadStatesEqual(sharedState, prevSharedState)) + return false; + + auto &&transform = quad->shared_quad_state->quad_to_target_transform; + + gfx::Rect targetRect1 = cc::MathUtil::MapEnclosingClippedRect(transform, quad->visible_rect); + if (sharedState->is_clipped) + targetRect1.Intersect(sharedState->clip_rect); + targetRect1.Intersect(scissorRect); + + gfx::Rect targetRect2 = cc::MathUtil::MapEnclosingClippedRect(transform, prevQuad->visible_rect); + if (prevSharedState->is_clipped) + targetRect2.Intersect(prevSharedState->clip_rect); + targetRect2.Intersect(scissorRect); + + if (targetRect1.IsEmpty() != targetRect2.IsEmpty()) return false; - if (quad->shared_quad_state->is_clipped && quad->visible_rect != prevQuad->visible_rect) { - gfx::Rect targetRect1 = - cc::MathUtil::MapEnclosingClippedRect(quad->shared_quad_state->quad_to_target_transform, quad->visible_rect); - gfx::Rect targetRect2 = - cc::MathUtil::MapEnclosingClippedRect(quad->shared_quad_state->quad_to_target_transform, prevQuad->visible_rect); - targetRect1.Intersect(quad->shared_quad_state->clip_rect); - targetRect2.Intersect(quad->shared_quad_state->clip_rect); - if (targetRect1.IsEmpty() != targetRect2.IsEmpty()) - return false; - } } } return true; @@ -854,11 +870,20 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, } frameData->resource_list.clear(); - QScopedPointer nodeHandler; + + // The RenderPasses list is actually a tree where a parent RenderPass is connected + // to its dependencies through a RenderPassId reference in one or more RenderPassQuads. + // The list is already ordered with intermediate RenderPasses placed before their + // parent, with the last one in the list being the root RenderPass, the one + // that we displayed to the user. + // All RenderPasses except the last one are rendered to an FBO. + viz::RenderPass *rootRenderPass = frameData->render_pass_list.back().get(); const QSizeF viewportSizeInPt = apiDelegate->screenRect().size(); const QSizeF viewportSizeF = viewportSizeInPt * devicePixelRatio; const QSize viewportSize(std::ceil(viewportSizeF.width()), std::ceil(viewportSizeF.height())); + gfx::Rect viewportRect(toGfx(viewportSize)); + viewportRect += rootRenderPass->output_rect.OffsetFromOrigin(); // We first compare if the render passes from the previous frame data are structurally // equivalent to the render passes in the current frame data. If they are, we are going @@ -867,10 +892,11 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, // Additionally, because we clip (i.e. don't build scene graph nodes for) quads outside // of the visible area, we also have to rebuild the tree whenever the window is resized. const bool buildNewTree = - !areRenderPassStructuresEqual(frameData, &m_chromiumCompositorData->previousFrameData) || m_sceneGraphNodes.empty() || - viewportSize != m_previousViewportSize; + viewportSize != m_previousViewportSize || + !areRenderPassStructuresEqual(frameData, &m_chromiumCompositorData->previousFrameData, viewportRect); + QScopedPointer nodeHandler; m_chromiumCompositorData->previousFrameData = viz::CompositorFrame(); SGObjects previousSGObjects; QVector > textureStrongRefs; @@ -889,20 +915,12 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, qSwap(m_sgObjects.textureStrongRefs, textureStrongRefs); nodeHandler.reset(new DelegatedNodeTreeUpdater(&m_sceneGraphNodes)); } - // The RenderPasses list is actually a tree where a parent RenderPass is connected - // to its dependencies through a RenderPassId reference in one or more RenderPassQuads. - // The list is already ordered with intermediate RenderPasses placed before their - // parent, with the last one in the list being the root RenderPass, the one - // that we displayed to the user. - // All RenderPasses except the last one are rendered to an FBO. - viz::RenderPass *rootRenderPass = frameData->render_pass_list.back().get(); - gfx::Rect viewportRect(toGfx(viewportSize)); for (unsigned i = 0; i < frameData->render_pass_list.size(); ++i) { viz::RenderPass *pass = frameData->render_pass_list.at(i).get(); QSGNode *renderPassParent = 0; - gfx::Rect scissorRect; + auto &&scissorRect = pass != rootRenderPass ? pass->output_rect : viewportRect; if (pass != rootRenderPass) { QSharedPointer rpLayer; if (buildNewTree) { @@ -927,11 +945,8 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, rpLayer->setFormat(pass->has_transparent_background ? GL_RGBA : GL_RGB); rpLayer->setHasMipmaps(pass->generate_mipmap); rpLayer->setMirrorVertical(true); - scissorRect = pass->output_rect; } else { renderPassParent = this; - scissorRect = viewportRect; - scissorRect += rootRenderPass->output_rect.OffsetFromOrigin(); } if (scissorRect.IsEmpty()) { diff --git a/src/core/devtools_frontend_qt.cpp b/src/core/devtools_frontend_qt.cpp index bd9e0ebe7d4..fc91fd75fc8 100644 --- a/src/core/devtools_frontend_qt.cpp +++ b/src/core/devtools_frontend_qt.cpp @@ -78,7 +78,6 @@ #include "net/url_request/url_fetcher.h" #include "net/url_request/url_fetcher_response_writer.h" -#include using namespace QtWebEngineCore; namespace { @@ -180,7 +179,7 @@ DevToolsFrontendQt *DevToolsFrontendQt::Show(QSharedPointer content::WebContents *contents = frontendAdapter->webContents(); if (contents == inspectedContents) { - qWarning() << "You can not inspect youself"; + LOG(WARNING) << "You can not inspect yourself"; return nullptr; } diff --git a/src/core/net/network_delegate_qt.cpp b/src/core/net/network_delegate_qt.cpp index 1441ec336ed..f6202cf1be3 100644 --- a/src/core/net/network_delegate_qt.cpp +++ b/src/core/net/network_delegate_qt.cpp @@ -252,15 +252,22 @@ int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::Complet if (!infoPrivate->extraHeaders.isEmpty()) { auto end = infoPrivate->extraHeaders.constEnd(); - for (auto header = infoPrivate->extraHeaders.constBegin(); header != end; ++header) - request->SetExtraRequestHeaderByName(header.key().toStdString(), header.value().toStdString(), /* overwrite */ true); + for (auto header = infoPrivate->extraHeaders.constBegin(); header != end; ++header) { + std::string h = header.key().toStdString(); + if (base::LowerCaseEqualsASCII(h, "referer")) { + request->SetReferrer(header.value().toStdString()); + } else { + request->SetExtraRequestHeaderByName(h, header.value().toStdString(), /* overwrite */ true); + } + } } if (result != net::OK) return result; } - } else + } else { m_profileIOData->releaseInterceptor(); + } if (!resourceInfo) return net::OK; diff --git a/src/core/net/url_request_qrc_job_qt.cpp b/src/core/net/url_request_qrc_job_qt.cpp index a2712653dde..4ac45e7c8bf 100644 --- a/src/core/net/url_request_qrc_job_qt.cpp +++ b/src/core/net/url_request_qrc_job_qt.cpp @@ -120,14 +120,14 @@ void URLRequestQrcJobQt::startGetHead() QMimeType mimeType = mimeDatabase.mimeTypeForFile(qrcFileInfo); m_mimeType = mimeType.name().toStdString(); // Open file - if (m_file.open(QIODevice::ReadOnly)) { + if (m_file.open(QIODevice::ReadOnly) && m_file.size() > 0) { m_remainingBytes = m_file.size(); set_expected_content_size(m_remainingBytes); // Notify that the headers are complete NotifyHeadersComplete(); - } else { - NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, ERR_INVALID_URL)); + return; } + qWarning("Resource %s not found or is empty", qUtf8Printable(qrcFilePath)); + NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, ERR_INVALID_URL)); } - } // namespace QtWebEngineCore diff --git a/src/core/ozone/surface_factory_qt.cpp b/src/core/ozone/surface_factory_qt.cpp index 9570852c97a..f69520b70ac 100644 --- a/src/core/ozone/surface_factory_qt.cpp +++ b/src/core/ozone/surface_factory_qt.cpp @@ -41,7 +41,7 @@ #include "qtwebenginecoreglobal_p.h" #include "gl_context_qt.h" #include "gl_ozone_egl_qt.h" -#if QT_CONFIG(webengine_system_x11) +#if QT_CONFIG(webengine_ozone_x11) #include "gl_ozone_glx_qt.h" #endif @@ -58,7 +58,7 @@ namespace QtWebEngineCore { SurfaceFactoryQt::SurfaceFactoryQt() { Q_ASSERT(qApp); -#if QT_CONFIG(webengine_system_x11) +#if QT_CONFIG(webengine_ozone_x11) if (GLContextHelper::getGlXConfig()) { m_impl = gl::kGLImplementationDesktopGL; m_ozone.reset(new ui::GLOzoneGLXQt()); diff --git a/src/core/profile_adapter.cpp b/src/core/profile_adapter.cpp index 1b946949a5d..b04fa0d46f6 100644 --- a/src/core/profile_adapter.cpp +++ b/src/core/profile_adapter.cpp @@ -44,6 +44,7 @@ #include "content/public/browser/browsing_data_remover.h" #include "content/public/browser/download_manager.h" +#include "api/qwebengineurlscheme.h" #include "content_client_qt.h" #include "download_manager_delegate_qt.h" #include "net/url_request_context_getter_qt.h" @@ -440,16 +441,50 @@ bool ProfileAdapter::removeCustomUrlSchemeHandler(QWebEngineUrlSchemeHandler *ha QWebEngineUrlSchemeHandler *ProfileAdapter::takeCustomUrlSchemeHandler(const QByteArray &scheme) { - QWebEngineUrlSchemeHandler *handler = m_customUrlSchemeHandlers.take(scheme); + QWebEngineUrlSchemeHandler *handler = m_customUrlSchemeHandlers.take(scheme.toLower()); if (handler) updateCustomUrlSchemeHandlers(); return handler; } -void ProfileAdapter::addCustomUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler) +bool ProfileAdapter::addCustomUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler) { - m_customUrlSchemeHandlers.insert(scheme, handler); + static const QSet blacklist{ + QByteArrayLiteral("about"), + QByteArrayLiteral("blob"), + QByteArrayLiteral("data"), + QByteArrayLiteral("javascript"), + QByteArrayLiteral("qrc"), + // See also kStandardURLSchemes in url/url_util.cc (through url::IsStandard below) + }; + + static const QSet whitelist{ + QByteArrayLiteral("gopher"), + }; + + const QByteArray canonicalScheme = scheme.toLower(); + bool standardSyntax = url::IsStandard(canonicalScheme.data(), url::Component(0, canonicalScheme.size())); + bool customScheme = QWebEngineUrlScheme::schemeByName(canonicalScheme) != QWebEngineUrlScheme(); + bool blacklisted = blacklist.contains(canonicalScheme) || (standardSyntax && !customScheme); + bool whitelisted = whitelist.contains(canonicalScheme); + + if (blacklisted && !whitelisted) { + qWarning("Cannot install a URL scheme handler overriding internal scheme: %s", scheme.constData()); + return false; + } + + if (m_customUrlSchemeHandlers.value(canonicalScheme, handler) != handler) { + qWarning("URL scheme handler already installed for the scheme: %s", scheme.constData()); + return false; + } + + if (!whitelisted && !customScheme) + qWarning("Please register the custom scheme '%s' via QWebEngineUrlScheme::registerScheme() " + "before installing the custom scheme handler.", scheme.constData()); + + m_customUrlSchemeHandlers.insert(canonicalScheme, handler); updateCustomUrlSchemeHandlers(); + return true; } void ProfileAdapter::clearCustomUrlSchemeHandlers() diff --git a/src/core/profile_adapter.h b/src/core/profile_adapter.h index 7ed5c13f566..e92fb524b36 100644 --- a/src/core/profile_adapter.h +++ b/src/core/profile_adapter.h @@ -179,7 +179,7 @@ class QWEBENGINECORE_PRIVATE_EXPORT ProfileAdapter : public QObject const QHash &customUrlSchemeHandlers() const; const QList customUrlSchemes() const; void clearCustomUrlSchemeHandlers(); - void addCustomUrlSchemeHandler(const QByteArray &, QWebEngineUrlSchemeHandler *); + bool addCustomUrlSchemeHandler(const QByteArray &, QWebEngineUrlSchemeHandler *); bool removeCustomUrlSchemeHandler(QWebEngineUrlSchemeHandler *); QWebEngineUrlSchemeHandler *takeCustomUrlSchemeHandler(const QByteArray &); UserResourceControllerHost *userResourceController(); diff --git a/src/core/profile_io_data_qt.cpp b/src/core/profile_io_data_qt.cpp index 63585b2c3a3..99a6f6db0bc 100644 --- a/src/core/profile_io_data_qt.cpp +++ b/src/core/profile_io_data_qt.cpp @@ -48,6 +48,7 @@ #include "content/public/common/content_features.h" #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h" #include "chrome/browser/net/chrome_mojo_proxy_resolver_factory.h" +#include "chrome/common/chrome_switches.h" #include "net/cert/cert_verifier.h" #include "net/cert/ct_log_verifier.h" #include "net/cert/ct_policy_enforcer.h" @@ -88,13 +89,6 @@ namespace QtWebEngineCore { -static const char* const kDefaultAuthSchemes[] = { net::kBasicAuthScheme, - net::kDigestAuthScheme, -#if QT_CONFIG(webengine_kerberos) - net::kNegotiateAuthScheme, -#endif - net::kNtlmAuthScheme }; - static bool doNetworkSessionParamsMatch(const net::HttpNetworkSession::Params &first, const net::HttpNetworkSession::Params &second) { @@ -206,9 +200,11 @@ content::ResourceContext *ProfileIODataQt::resourceContext() void ProfileIODataQt::initializeOnIOThread() { m_networkDelegate.reset(new NetworkDelegateQt(this)); + m_hostResolver = net::HostResolver::CreateDefaultResolver(NULL); m_urlRequestContext.reset(new net::URLRequestContext()); m_urlRequestContext->set_network_delegate(m_networkDelegate.get()); m_urlRequestContext->set_enable_brotli(base::FeatureList::IsEnabled(features::kBrotliEncoding)); + m_urlRequestContext->set_host_resolver(m_hostResolver.get()); // this binds factory to io thread m_weakPtr = m_weakPtrFactory.GetWeakPtr(); QMutexLocker lock(&m_mutex); @@ -295,10 +291,15 @@ void ProfileIODataQt::generateStorage() ct_verifier->AddLogs(ct_logs); m_storage->set_cert_transparency_verifier(std::move(ct_verifier)); m_storage->set_ct_policy_enforcer(base::WrapUnique(new net::DefaultCTPolicyEnforcer())); - m_storage->set_host_resolver(net::HostResolver::CreateDefaultResolver(NULL)); m_storage->set_ssl_config_service(std::make_unique()); + if (!m_httpAuthPreferences) { + m_httpAuthPreferences.reset(new net::HttpAuthPreferences()); + std::string serverWhitelist = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switches::kAuthServerWhitelist); + m_httpAuthPreferences->SetServerWhitelist(serverWhitelist); + } m_storage->set_http_auth_handler_factory(net::HttpAuthHandlerFactory::CreateDefault( - m_urlRequestContext->host_resolver())); + m_urlRequestContext->host_resolver(), + m_httpAuthPreferences.get())); m_storage->set_transport_security_state(std::make_unique()); if (!m_dataPath.isEmpty()) { diff --git a/src/core/profile_io_data_qt.h b/src/core/profile_io_data_qt.h index 2d4706bf434..8ce6185b5ab 100644 --- a/src/core/profile_io_data_qt.h +++ b/src/core/profile_io_data_qt.h @@ -120,6 +120,7 @@ class ProfileIODataQt { std::unique_ptr m_httpAuthPreferences; std::unique_ptr m_jobFactory; std::unique_ptr m_transportSecurityPersister; + std::unique_ptr m_hostResolver; base::WeakPtr m_weakPtr; scoped_refptr m_cookieDelegate; content::URLRequestInterceptorScopedVector m_requestInterceptors; diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp index 3e500396064..f74f16bc3c9 100644 --- a/src/core/web_contents_adapter.cpp +++ b/src/core/web_contents_adapter.cpp @@ -653,19 +653,23 @@ void WebContentsAdapter::load(const QWebEngineHttpRequest &request) } } - auto navigate = [](WebContentsAdapter *adapter, const content::NavigationController::LoadURLParams ¶ms) { + auto navigate = [](QWeakPointer weakAdapter, const content::NavigationController::LoadURLParams ¶ms) { + WebContentsAdapter *adapter = weakAdapter.data(); + if (!adapter) + return; adapter->webContents()->GetController().LoadURLWithParams(params); // Follow chrome::Navigate and invalidate the URL immediately. adapter->m_webContentsDelegate->NavigationStateChanged(adapter->webContents(), content::INVALIDATE_TYPE_URL); adapter->focusIfNecessary(); }; + QWeakPointer weakThis(sharedFromThis()); if (resizeNeeded) { // Schedule navigation on the event loop. content::BrowserThread::PostTask( - content::BrowserThread::UI, FROM_HERE, base::BindOnce(navigate, this, std::move(params))); + content::BrowserThread::UI, FROM_HERE, base::BindOnce(navigate, std::move(weakThis), std::move(params))); } else { - navigate(this, params); + navigate(std::move(weakThis), params); } } @@ -987,6 +991,7 @@ quint64 WebContentsAdapter::findText(const QString &subString, bool caseSensitiv // waiting for it forever. // Assume that any unfinished find has been unsuccessful when a new one is started // to cover that case. + m_webContentsDelegate->setLastReceivedFindReply(m_lastFindRequestId); m_adapterClient->didFindText(m_lastFindRequestId, 0); } @@ -1007,6 +1012,7 @@ quint64 WebContentsAdapter::findText(const QString &subString, bool caseSensitiv void WebContentsAdapter::stopFinding() { CHECK_INITIALIZED(); + m_webContentsDelegate->setLastReceivedFindReply(m_lastFindRequestId); m_webContentsDelegate->setLastSearchedString(QString()); m_webContents->StopFinding(content::STOP_FIND_ACTION_KEEP_SELECTION); } @@ -1602,6 +1608,12 @@ bool WebContentsAdapter::isFindTextInProgress() const return m_lastFindRequestId != m_webContentsDelegate->lastReceivedFindReply(); } +bool WebContentsAdapter::hasFocusedFrame() const +{ + CHECK_INITIALIZED(false); + return m_webContents->GetFocusedFrame() != nullptr; +} + WebContentsAdapterClient::RenderProcessTerminationStatus WebContentsAdapterClient::renderProcessExitStatus(int terminationStatus) { auto status = WebContentsAdapterClient::RenderProcessTerminationStatus(-1); diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h index e8e5359beb7..9f9d0ed033f 100644 --- a/src/core/web_contents_adapter.h +++ b/src/core/web_contents_adapter.h @@ -217,6 +217,7 @@ class QWEBENGINECORE_PRIVATE_EXPORT WebContentsAdapter : public QEnableSharedFro bool canViewSource(); void focusIfNecessary(); bool isFindTextInProgress() const; + bool hasFocusedFrame() const; // meant to be used within WebEngineCore only void initialize(content::SiteInstance *site); diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h index 75d01086560..129e97c5b4f 100644 --- a/src/core/web_contents_adapter_client.h +++ b/src/core/web_contents_adapter_client.h @@ -466,6 +466,7 @@ class QWEBENGINECORE_PRIVATE_EXPORT WebContentsAdapterClient { virtual void updateScrollPosition(const QPointF &position) = 0; virtual void updateContentsSize(const QSizeF &size) = 0; virtual void updateNavigationActions() = 0; + virtual void updateEditActions() = 0; virtual void startDragging(const content::DropData &dropData, Qt::DropActions allowedActions, const QPixmap &pixmap, const QPoint &offset) = 0; virtual bool supportsDragging() const = 0; diff --git a/src/core/web_contents_delegate_qt.cpp b/src/core/web_contents_delegate_qt.cpp index 5cb94de5c4d..c840b7f43be 100644 --- a/src/core/web_contents_delegate_qt.cpp +++ b/src/core/web_contents_delegate_qt.cpp @@ -63,6 +63,7 @@ #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h" #include "components/web_cache/browser/web_cache_manager.h" +#include "content/browser/frame_host/render_frame_host_impl.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/invalidate_type.h" @@ -102,6 +103,7 @@ WebContentsDelegateQt::WebContentsDelegateQt(content::WebContents *webContents, , m_lastReceivedFindReply(0) , m_faviconManager(new FaviconManager(webContents, adapterClient)) , m_lastLoadProgress(-1) + , m_frameFocusedObserver(adapterClient) { webContents->SetDelegate(this); Observe(webContents); @@ -223,6 +225,11 @@ void WebContentsDelegateQt::AddNewContents(content::WebContents* source, std::un { Q_UNUSED(source) QSharedPointer newAdapter = createWindow(std::move(new_contents), disposition, initial_pos, user_gesture); + // Chromium can forget to pass user-agent override settings to new windows (see QTBUG-61774 and QTBUG-76249), + // so set it here. Note the actual value doesn't really matter here. Only the second value does, but we try + // to give the correct user-agent anyway. + if (newAdapter) + newAdapter->webContents()->SetUserAgentOverride(newAdapter->profileAdapter()->httpUserAgent().toStdString(), true); if (newAdapter && !newAdapter->isInitialized()) newAdapter->loadDefault(); if (was_blocked) @@ -253,11 +260,30 @@ void WebContentsDelegateQt::HandleKeyboardEvent(content::WebContents *, const co m_viewClient->unhandledKeyEvent(reinterpret_cast(event.os_event)); } +void WebContentsDelegateQt::RenderFrameCreated(content::RenderFrameHost *render_frame_host) +{ + content::FrameTreeNode *node = static_cast(render_frame_host)->frame_tree_node(); + m_frameFocusedObserver.addNode(node); +} + void WebContentsDelegateQt::RenderFrameDeleted(content::RenderFrameHost *render_frame_host) { m_loadingErrorFrameList.removeOne(render_frame_host->GetRoutingID()); } +void WebContentsDelegateQt::RenderFrameHostChanged(content::RenderFrameHost *old_host, content::RenderFrameHost *new_host) +{ + if (old_host) { + content::FrameTreeNode *old_node = static_cast(old_host)->frame_tree_node(); + m_frameFocusedObserver.removeNode(old_node); + } + + if (new_host) { + content::FrameTreeNode *new_node = static_cast(new_host)->frame_tree_node(); + m_frameFocusedObserver.addNode(new_node); + } +} + void WebContentsDelegateQt::RenderViewHostChanged(content::RenderViewHost *, content::RenderViewHost *newHost) { if (newHost && newHost->GetWidget() && newHost->GetWidget()->GetView()) { @@ -383,8 +409,10 @@ void WebContentsDelegateQt::DidFinishLoad(content::RenderFrameHost* render_frame return; } - if (render_frame_host->GetParent()) + if (render_frame_host->GetParent()) { + m_viewClient->updateNavigationActions(); return; + } if (!m_faviconManager->hasCandidate()) m_viewClient->iconChanged(QUrl()); @@ -484,7 +512,7 @@ void WebContentsDelegateQt::FindReply(content::WebContents *source, int request_ Q_UNUSED(source) Q_UNUSED(selection_rect) Q_UNUSED(active_match_ordinal) - if (final_update) { + if (final_update && request_id > m_lastReceivedFindReply) { m_lastReceivedFindReply = request_id; m_viewClient->didFindText(request_id, number_of_matches); } @@ -704,4 +732,42 @@ WebContentsAdapter *WebContentsDelegateQt::webContentsAdapter() const return m_viewClient->webContentsAdapter(); } + +FrameFocusedObserver::FrameFocusedObserver(WebContentsAdapterClient *adapterClient) + : m_viewClient(adapterClient) +{} + +void FrameFocusedObserver::addNode(content::FrameTreeNode *node) +{ + if (m_observedNodes.contains(node)) + return; + + node->AddObserver(this); + m_observedNodes.append(node); +} + +void FrameFocusedObserver::removeNode(content::FrameTreeNode *node) +{ + node->RemoveObserver(this); + m_observedNodes.removeOne(node); +} + +void FrameFocusedObserver::OnFrameTreeNodeFocused(content::FrameTreeNode *node) +{ + Q_UNUSED(node); + m_viewClient->updateEditActions(); +} + +void FrameFocusedObserver::OnFrameTreeNodeDestroyed(content::FrameTreeNode *node) +{ + m_observedNodes.removeOne(node); + m_viewClient->updateEditActions(); +} + +FrameFocusedObserver::~FrameFocusedObserver() +{ + for (content::FrameTreeNode *node : m_observedNodes) + node->RemoveObserver(this); +} + } // namespace QtWebEngineCore diff --git a/src/core/web_contents_delegate_qt.h b/src/core/web_contents_delegate_qt.h index 2baa1176fc8..b88b56dc117 100644 --- a/src/core/web_contents_delegate_qt.h +++ b/src/core/web_contents_delegate_qt.h @@ -40,6 +40,7 @@ #ifndef WEB_CONTENTS_DELEGATE_QT_H #define WEB_CONTENTS_DELEGATE_QT_H +#include "content/browser/frame_host/frame_tree_node.h" #include "content/public/browser/web_contents_delegate.h" #include "content/public/browser/web_contents_observer.h" #include "third_party/skia/include/core/SkColor.h" @@ -70,6 +71,23 @@ class WebContentsAdapter; class WebContentsAdapterClient; class WebEngineSettings; +class FrameFocusedObserver : public content::FrameTreeNode::Observer +{ +public: + FrameFocusedObserver(WebContentsAdapterClient *adapterClient); + ~FrameFocusedObserver(); + void addNode(content::FrameTreeNode *node); + void removeNode(content::FrameTreeNode *node); + +protected: + void OnFrameTreeNodeFocused(content::FrameTreeNode *node) override; + void OnFrameTreeNodeDestroyed(content::FrameTreeNode *node) override; + +private: + WebContentsAdapterClient *m_viewClient; + QVector m_observedNodes; +}; + class SavePageInfo { public: @@ -96,6 +114,7 @@ class WebContentsDelegateQt : public content::WebContentsDelegate QString lastSearchedString() const { return m_lastSearchedString; } void setLastSearchedString(const QString &s) { m_lastSearchedString = s; } int lastReceivedFindReply() const { return m_lastReceivedFindReply; } + void setLastReceivedFindReply(int id) { m_lastReceivedFindReply = id; } QUrl url() const { return m_url; } QString title() const { return m_title; } @@ -130,7 +149,9 @@ class WebContentsDelegateQt : public content::WebContentsDelegate bool TakeFocus(content::WebContents *source, bool reverse) override; // WebContentsObserver overrides + void RenderFrameCreated(content::RenderFrameHost *render_frame_host) override; void RenderFrameDeleted(content::RenderFrameHost *render_frame_host) override; + void RenderFrameHostChanged(content::RenderFrameHost *old_host, content::RenderFrameHost *new_host) override; void RenderViewHostChanged(content::RenderViewHost *old_host, content::RenderViewHost *new_host) override; void DidStartNavigation(content::NavigationHandle *navigation_handle) override; void DidFinishNavigation(content::NavigationHandle *navigation_handle) override; @@ -172,6 +193,7 @@ class WebContentsDelegateQt : public content::WebContentsDelegate QSharedPointer m_filePickerController; QUrl m_initialTargetUrl; int m_lastLoadProgress; + FrameFocusedObserver m_frameFocusedObserver; QUrl m_url; QString m_title; diff --git a/src/core/web_engine_library_info.cpp b/src/core/web_engine_library_info.cpp index 3899ced2529..75fd947a27b 100644 --- a/src/core/web_engine_library_info.cpp +++ b/src/core/web_engine_library_info.cpp @@ -290,6 +290,8 @@ QString resourcesDataPath() static QString potentialResourcesPath = #if defined(OS_MACOSX) && defined(QT_MAC_FRAMEWORK_BUILD) getResourcesPath(frameworkBundle()); +#elif defined(OS_MACOSX) + QLibraryInfo::location(QLibraryInfo::DataPath) % QLatin1String("/Resources"); #else QLibraryInfo::location(QLibraryInfo::DataPath) % QLatin1String("/resources"); #endif diff --git a/src/core/web_event_factory.cpp b/src/core/web_event_factory.cpp index fc6287dd9b1..8042c091478 100644 --- a/src/core/web_event_factory.cpp +++ b/src/core/web_event_factory.cpp @@ -171,8 +171,15 @@ static QString qtTextForKeyEvent(const QKeyEvent *ev, int qtKey, Qt::KeyboardMod { QString text = ev->text(); - if ((qtModifiers & Qt::ControlModifier) && keyboardDriver() == KeyboardDriver::Xkb) + 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(); + } return text; } diff --git a/src/webengine/api/qquickwebengineaction.cpp b/src/webengine/api/qquickwebengineaction.cpp index 69a05f29b37..77ac8d340d0 100644 --- a/src/webengine/api/qquickwebengineaction.cpp +++ b/src/webengine/api/qquickwebengineaction.cpp @@ -146,8 +146,7 @@ QString QQuickWebEngineAction::iconName() const /*! \qmlproperty bool WebEngineAction::enabled - This property holds whether the action is enabled. Context-dependent - actions are always enabled. + This property holds whether the action is enabled. */ bool QQuickWebEngineAction::isEnabled() const { diff --git a/src/webengine/api/qquickwebenginedownloaditem.cpp b/src/webengine/api/qquickwebenginedownloaditem.cpp index cdb95fa53e6..7d51ed21d4f 100644 --- a/src/webengine/api/qquickwebenginedownloaditem.cpp +++ b/src/webengine/api/qquickwebenginedownloaditem.cpp @@ -43,6 +43,8 @@ #include "profile_adapter.h" #include "qquickwebengineprofile_p.h" +#include "QFileInfo" + using QtWebEngineCore::ProfileAdapterClient; QT_BEGIN_NAMESPACE @@ -427,6 +429,16 @@ void QQuickWebEngineDownloadItem::setPath(QString path) return; } if (d->downloadPath != path) { + if (QFileInfo(path).fileName().isEmpty()) { + qWarning("The download path does not include file name."); + return; + } + + if (QFileInfo(path).isDir()) { + qWarning("The download path matches with an already existing directory path."); + return; + } + d->downloadPath = path; Q_EMIT pathChanged(); } diff --git a/src/webengine/api/qquickwebengineprofile.cpp b/src/webengine/api/qquickwebengineprofile.cpp index 73577a04ccc..f77f376aa35 100644 --- a/src/webengine/api/qquickwebengineprofile.cpp +++ b/src/webengine/api/qquickwebengineprofile.cpp @@ -847,20 +847,7 @@ void QQuickWebEngineProfile::setRequestInterceptor(QWebEngineUrlRequestIntercept const QWebEngineUrlSchemeHandler *QQuickWebEngineProfile::urlSchemeHandler(const QByteArray &scheme) const { const Q_D(QQuickWebEngineProfile); - if (d->profileAdapter()->customUrlSchemeHandlers().contains(scheme)) - return d->profileAdapter()->customUrlSchemeHandlers().value(scheme); - return 0; -} - -static bool checkInternalScheme(const QByteArray &scheme) -{ - static QSet internalSchemes; - if (internalSchemes.isEmpty()) { - internalSchemes << QByteArrayLiteral("qrc") << QByteArrayLiteral("data") << QByteArrayLiteral("blob") - << QByteArrayLiteral("http") << QByteArrayLiteral("https") << QByteArrayLiteral("ftp") - << QByteArrayLiteral("javascript"); - } - return internalSchemes.contains(scheme); + return d->profileAdapter()->customUrlSchemeHandlers().value(scheme.toLower()); } /*! @@ -873,23 +860,8 @@ void QQuickWebEngineProfile::installUrlSchemeHandler(const QByteArray &scheme, Q { Q_D(QQuickWebEngineProfile); Q_ASSERT(handler); - QByteArray canonicalScheme = scheme.toLower(); - if (checkInternalScheme(canonicalScheme)) { - qWarning("Cannot install a URL scheme handler overriding internal scheme: %s", scheme.constData()); - return; - } - - if (d->profileAdapter()->customUrlSchemeHandlers().contains(canonicalScheme)) { - if (d->profileAdapter()->customUrlSchemeHandlers().value(canonicalScheme) != handler) - qWarning("URL scheme handler already installed for the scheme: %s", scheme.constData()); + if (!d->profileAdapter()->addCustomUrlSchemeHandler(scheme, handler)) return; - } - - if (QWebEngineUrlScheme::schemeByName(canonicalScheme) == QWebEngineUrlScheme()) - qWarning("Please register the custom scheme '%s' via QWebEngineUrlScheme::registerScheme() " - "before installing the custom scheme handler.", scheme.constData()); - - d->profileAdapter()->addCustomUrlSchemeHandler(canonicalScheme, handler); connect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*))); } diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp index fd3cc8e8284..60aaead671a 100644 --- a/src/webengine/api/qquickwebengineview.cpp +++ b/src/webengine/api/qquickwebengineview.cpp @@ -294,7 +294,7 @@ void QQuickWebEngineViewPrivate::allowCertificateError(const QSharedPointernewQObject(quickController); Q_EMIT q->certificateError(quickController); - if (!quickController->deferred() && !quickController->answered()) + if (!quickController->overridable() || (!quickController->deferred() && !quickController->answered())) quickController->rejectCertificate(); else m_certificateErrorControllers.append(errorController); @@ -356,7 +356,9 @@ void QQuickWebEngineViewPrivate::urlChanged(const QUrl &url) { Q_Q(QQuickWebEngineView); Q_UNUSED(url); - Q_EMIT q->urlChanged(); + QTimer::singleShot(0, q, [q]() { + Q_EMIT q->urlChanged(); + }); } void QQuickWebEngineViewPrivate::iconChanged(const QUrl &url) @@ -931,6 +933,16 @@ void QQuickWebEngineViewPrivate::updateAction(QQuickWebEngineView::WebAction act case QQuickWebEngineView::ViewSource: enabled = adapter->canViewSource(); break; + case QQuickWebEngineView::Cut: + case QQuickWebEngineView::Copy: + case QQuickWebEngineView::Paste: + case QQuickWebEngineView::Undo: + case QQuickWebEngineView::Redo: + case QQuickWebEngineView::SelectAll: + case QQuickWebEngineView::PasteAndMatchStyle: + case QQuickWebEngineView::Unselect: + enabled = adapter->hasFocusedFrame(); + break; default: break; } @@ -948,6 +960,18 @@ void QQuickWebEngineViewPrivate::updateNavigationActions() updateAction(QQuickWebEngineView::ViewSource); } +void QQuickWebEngineViewPrivate::updateEditActions() +{ + updateAction(QQuickWebEngineView::Cut); + updateAction(QQuickWebEngineView::Copy); + updateAction(QQuickWebEngineView::Paste); + updateAction(QQuickWebEngineView::Undo); + updateAction(QQuickWebEngineView::Redo); + updateAction(QQuickWebEngineView::SelectAll); + updateAction(QQuickWebEngineView::PasteAndMatchStyle); + updateAction(QQuickWebEngineView::Unselect); +} + QUrl QQuickWebEngineView::url() const { Q_D(const QQuickWebEngineView); diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h index 3c4189fd9fb..3b6300d5f31 100644 --- a/src/webengine/api/qquickwebengineview_p_p.h +++ b/src/webengine/api/qquickwebengineview_p_p.h @@ -147,6 +147,7 @@ class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineViewPrivate : public QtWebEngine void updateScrollPosition(const QPointF &position) override; void updateContentsSize(const QSizeF &size) override; void updateNavigationActions() override; + void updateEditActions() override; void startDragging(const content::DropData &dropData, Qt::DropActions allowedActions, const QPixmap &pixmap, const QPoint &offset) override; bool supportsDragging() const override; diff --git a/src/webengine/doc/src/qtwebengine-platform-notes.qdoc b/src/webengine/doc/src/qtwebengine-platform-notes.qdoc index fefb477954e..5b7d750ffbb 100644 --- a/src/webengine/doc/src/qtwebengine-platform-notes.qdoc +++ b/src/webengine/doc/src/qtwebengine-platform-notes.qdoc @@ -69,7 +69,12 @@ \section2 Windows - On Windows, Visual Studio 2017 and Windows 10 SDK are required. + On Windows, the following additional tools are required: + + \list + \li Visual Studio 2017 version 15.8 or later + \li Windows 10 SDK + \endlist \QWE can only be built on 64-bit Windows, with a x64-bit toolchain. For building \QWE for x86 applications, you need to configure diff --git a/src/webengine/doc/src/webengineview_lgpl.qdoc b/src/webengine/doc/src/webengineview_lgpl.qdoc index 23d3049fd5f..a4f07ddd94b 100644 --- a/src/webengine/doc/src/webengineview_lgpl.qdoc +++ b/src/webengine/doc/src/webengineview_lgpl.qdoc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Qt Company Ltd. + * Copyright (C) 2019 The Qt Company Ltd. * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies) * Copyright (c) 2012 Hewlett-Packard Development Company, L.P. * @@ -376,7 +376,8 @@ which references HTML pages via URL. External objects, such as stylesheets or images referenced in the HTML - document, should be located relative to \a baseUrl. For example, if \a html + document, should be located relative to \a baseUrl. For external objects to + be loaded, \c baseUrl cannot be empty. For example, if \a html is retrieved from \c http://www.example.com/documents/overview.html, which is the base URL, then an image referenced with the relative URL, \c diagram.png, should be at \c{http://www.example.com/documents/diagram.png}. @@ -395,13 +396,19 @@ runJavaScript("document.title", function(result) { console.log(result); }); \endcode + Only plain data can be returned from JavaScript as the result value. + Supported data types include all of the JSON data types as well as, for + example, \c{Date} and \c{ArrayBuffer}. Unsupported data types include, for + example, \c{Function} and \c{Promise}. + The script will run in the same \e world as other scripts that are part of the loaded site. \warning Do not execute lengthy routines in the callback function, because it might block the rendering of the web content. - See WebEngineView::userScripts for an alternative API to inject scripts. + For more information about injecting scripts, see \l {Script Injection}. + For an alternative way to inject scripts, see WebEngineView::userScripts. */ /*! @@ -431,14 +438,13 @@ To clear the search highlight, just pass an empty string. - The \a resultCallback must take a boolean parameter. It will be called with - a value of true if the \a subString was found; otherwise the callback value - will be false. + The \a resultCallback must take an integer parameter. It will be called with + the number of found occurrences of the \a subString. \code - findText("Qt", WebEngineView.FindCaseSensitively, function(success) { - if (success) - console.log("Qt was found!"); + findText("Qt", WebEngineView.FindCaseSensitively, function(matchCount) { + if (matchCount > 0) + console.log("'Qt' tokens found:", matchCount); }); \endcode */ @@ -486,7 +492,8 @@ a web engine view can be used to create a UI element that should not get focus. This can be useful in a hybrid UI. - \sa activeFocusOnPressChanged, WebEngineSettings::focusOnNavigationEnabled + \sa activeFocusOnPress, activeFocusOnPressChanged, + WebEngineSettings::focusOnNavigationEnabled */ /*! @@ -524,7 +531,8 @@ \qmlsignal WebEngineView::certificateError(WebEngineCertificateError error) \since QtWebEngine 1.1 - This signal is emitted when an invalid certificate error is raised while loading a given request. + This signal is emitted when an invalid certificate error, \a error, is + raised while loading a given request. The certificate error can be handled by using the methods of the WebEngineCertificateError type. @@ -560,7 +568,7 @@ \qmlsignal WebEngineView::newViewRequested(WebEngineNewViewRequest request) \since QtWebEngine 1.1 - This signal is emitted when a page load is requested to happen in a separate + This signal is emitted when \a request is issued to load a page in a separate web engine view. This can either be because the current page requested it explicitly through a JavaScript call to \c window.open, or because the user clicked on a link while holding Shift, Ctrl, or a built-in combination that triggers the page to open @@ -579,8 +587,8 @@ \qmlsignal WebEngineView::fullScreenRequested(FullScreenRequest request) \since QtWebEngine 1.1 - This signal is emitted when the web page requests fullscreen mode through the - JavaScript API. + This signal is emitted when the web page issues the \a request for + fullscreen mode through the JavaScript API. \sa isFullScreen */ @@ -589,10 +597,10 @@ \qmlsignal WebEngineView::activeFocusOnPressChanged(bool activeFocusOnPress) \since QtWebEngine 1.2 - This signal is emitted when the ability of the web engine view to get focus when clicked - changes. + This signal is emitted when the value of \a activeFocusOnPress changes. + It specifies whether the view should gain active focus when pressed. - \sa setActiveFocusOnPress() + \sa activeFocusOnPress, setActiveFocusOnPress() */ /*! @@ -749,6 +757,10 @@ Redo the last editing action. \value WebEngineView.SelectAll Select all content. + This action is only enabled when the page's content is focused. + The focus can be forced by the JavaScript \c{window.focus()} call, or the + \l{WebEngineSettings::focusOnNavigationEnabled} {focusOnNavigationEnabled} setting + should be enabled to get automatic focus. \value WebEngineView.PasteAndMatchStyle Paste content from the clipboard with current style. \value WebEngineView.OpenLinkInThisWindow @@ -1107,7 +1119,8 @@ \qmlsignal WebEngineView::audioMutedChanged(bool muted) \since QtWebEngine 1.3 - This signal is emitted when the page's audio is (un)muted using audioMuted property. + This signal is emitted when the value of \a muted changes. The value is + specified using the \l audioMuted property. \note Not to be confused with a specific HTML5 audio / video element being muted. \sa audioMuted, recentlyAudibleChanged @@ -1125,10 +1138,10 @@ \qmlsignal WebEngineView::recentlyAudibleChanged(bool recentlyAudible) \since QtWebEngine 1.3 - This signal is emitted when the page's audible state is changed, due to audio - being played or stopped. + This signal is emitted when the page's audible state, specified by + \a recentlyAudible, is changed, due to audio being played or stopped. - \note The signal is also emitted when the audioMuted property changes. + \note The signal is also emitted when the \l audioMuted property changes. Also if the audio is paused, this signal is emitted with an approximate \b{two-second delay}, from the moment the audio is paused. @@ -1174,7 +1187,7 @@ PDF and returns immediately. To be informed about the result of the request, connect to the signal pdfPrintingFinished(). - If you leave out \a pageSizeID, it defaults to \c A4. If you leave out + If you leave out \a pageSizeId, it defaults to \c A4. If you leave out \a orientation, it defaults to \c Portrait. \sa pdfPrintingFinished() @@ -1190,7 +1203,7 @@ The \a resultCallback must take a string parameter. This string will contain the document's data upon successful printing and an empty string otherwise. - If you leave out \a pageSizeID, it defaults to \c A4. If you leave out + If you leave out \a pageSizeId, it defaults to \c A4. If you leave out \a orientation, it defaults to \c Portrait. */ @@ -1205,8 +1218,8 @@ \qmlsignal WebEngineView::wasRecentlyAudibleChanged(bool wasRecentlyAudible) \since QtWebEngine 1.3 - This signal is emitted when the page's audible state is changed, due to audio - being played or stopped. + This signal is emitted when the page's audible state, specified by + \a wasRecentlyAudible, is changed, due to audio being played or stopped. \note The signal is also emitted when calling the setAudioMuted method. Also if the audio is paused, this signal is emitted with an approximate \b{2 second @@ -1219,7 +1232,7 @@ This signal is emitted when an authentication dialog is requested. - The request can be handled by using the methods of the AuthenticationDialogRequest + The \a request can be handled by using the methods of the AuthenticationDialogRequest type. \note Signal handlers need to call \c{request.accepted = true} to prevent a @@ -1234,7 +1247,7 @@ This signal is emitted when a JavaScript dialog is requested. - The request can be handled by using the methods of the JavaScriptDialogRequest + The \a request can be handled by using the methods of the JavaScriptDialogRequest type. \note Signal handlers need to call \c{request.accepted = true} to prevent a @@ -1249,7 +1262,7 @@ This signal is emitted when a color picker dialog is requested. - The request can be handled by using the methods of the ColorDialogRequest + The \a request can be handled by using the methods of the ColorDialogRequest type. \note Signal handlers need to call \c{request.accepted = true} to prevent a @@ -1263,7 +1276,7 @@ This signal is emitted when a file picker dialog is requested. - The request error can be handled by using the methods of the FileDialogRequest + The \a request can be handled by using the methods of the FileDialogRequest type. \note Signal handlers need to call \c{request.accepted = true} to prevent a @@ -1285,7 +1298,7 @@ This signal is emitted when a context menu is requested. - The request can be handled by using the properties of the ContextMenuRequest + The \a request can be handled by using the properties of the ContextMenuRequest type. \note Signal handlers need to call \c{request.accepted = true} to prevent a @@ -1300,7 +1313,7 @@ \qmlsignal WebEngineView::quotaRequested(QuotaRequest request) \since QtWebEngine 1.7 - This signal is emitted when the web page requests larger persistent storage + This signal is emitted when the web page issues a \a request for a larger persistent storage than the application's current allocation in File System API. The default quota is 0 bytes. @@ -1359,7 +1372,7 @@ \since QtWebEngine 1.7 This signal is emitted when the web page tries to register a custom protocol - using the \l registerProtocolHandler API. + by issuing a \l registerProtocolHandler \a request. \sa RegisterProtocolHandlerRequest */ @@ -1455,7 +1468,7 @@ \qmlmethod WebEngineAction WebEngineView::action(WebAction action) \since 5.12 - Returns a \l WebEngineAction for the specified \l WebAction action. + Returns a \l WebEngineAction for the specified \l WebAction \a action. WebEngineView also takes care of implementing the action, so that upon triggering the corresponding action is performed on the view. diff --git a/src/webengine/render_widget_host_view_qt_delegate_quickwindow.cpp b/src/webengine/render_widget_host_view_qt_delegate_quickwindow.cpp index d3c88148eab..7db223ad9a8 100644 --- a/src/webengine/render_widget_host_view_qt_delegate_quickwindow.cpp +++ b/src/webengine/render_widget_host_view_qt_delegate_quickwindow.cpp @@ -47,7 +47,7 @@ namespace QtWebEngineCore { RenderWidgetHostViewQtDelegateQuickWindow::RenderWidgetHostViewQtDelegateQuickWindow(RenderWidgetHostViewQtDelegate *realDelegate) : m_realDelegate(realDelegate) { - setFlags(Qt::ToolTip | Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus); + setFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus); } RenderWidgetHostViewQtDelegateQuickWindow::~RenderWidgetHostViewQtDelegateQuickWindow() diff --git a/src/webengine/ui/ToolTip.qml b/src/webengine/ui/ToolTip.qml index 96033e8f1be..2bfe9eaf5b6 100644 --- a/src/webengine/ui/ToolTip.qml +++ b/src/webengine/ui/ToolTip.qml @@ -47,8 +47,9 @@ Item { visible: false property alias text: toolTip.text - property int delayTimerInterval: 1000 - property int hideTimerInterval: 1500 + property int delayTimerInterval: 500 + property int hideTimerInterval: 10000 + property int toolTipMaxWidth: 400 Timer { id: delayTimer @@ -77,7 +78,8 @@ Item { Text { id: toolTip anchors {fill: parent; margins: 5} - wrapMode: Text.WrapAnywhere + wrapMode: Text.Wrap + width: Math.min(toolTipMaxWidth, (toolTip.text.length +1) * 8) } } diff --git a/src/webenginewidgets/api/qwebenginedownloaditem.cpp b/src/webenginewidgets/api/qwebenginedownloaditem.cpp index 4575f292943..05c6956eabc 100644 --- a/src/webenginewidgets/api/qwebenginedownloaditem.cpp +++ b/src/webenginewidgets/api/qwebenginedownloaditem.cpp @@ -43,6 +43,7 @@ #include "profile_adapter.h" #include "qwebengineprofile_p.h" +#include "QFileInfo" QT_BEGIN_NAMESPACE @@ -534,6 +535,16 @@ void QWebEngineDownloadItem::setPath(QString path) return; } + if (QFileInfo(path).fileName().isEmpty()) { + qWarning("The download path does not include file name."); + return; + } + + if (QFileInfo(path).isDir()) { + qWarning("The download path matches with an already existing directory path."); + return; + } + d->downloadPath = path; } diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index 85a162383f0..aeed6ce85a9 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -650,6 +650,16 @@ void QWebEnginePagePrivate::updateAction(QWebEnginePage::WebAction action) const case QWebEnginePage::ViewSource: enabled = adapter->canViewSource(); break; + case QWebEnginePage::Cut: + case QWebEnginePage::Copy: + case QWebEnginePage::Paste: + case QWebEnginePage::Undo: + case QWebEnginePage::Redo: + case QWebEnginePage::SelectAll: + case QWebEnginePage::PasteAndMatchStyle: + case QWebEnginePage::Unselect: + enabled = adapter->hasFocusedFrame(); + break; default: break; } @@ -668,6 +678,18 @@ void QWebEnginePagePrivate::updateNavigationActions() updateAction(QWebEnginePage::ViewSource); } +void QWebEnginePagePrivate::updateEditActions() +{ + updateAction(QWebEnginePage::Cut); + updateAction(QWebEnginePage::Copy); + updateAction(QWebEnginePage::Paste); + updateAction(QWebEnginePage::Undo); + updateAction(QWebEnginePage::Redo); + updateAction(QWebEnginePage::SelectAll); + updateAction(QWebEnginePage::PasteAndMatchStyle); + updateAction(QWebEnginePage::Unselect); +} + #ifndef QT_NO_ACTION void QWebEnginePagePrivate::_q_webActionTriggered(bool checked) { @@ -746,24 +768,34 @@ void QWebEnginePagePrivate::bindPageAndView(QWebEnginePage *page, QWebEngineView auto oldView = page ? page->d_func()->view : nullptr; auto oldPage = view ? view->d_func()->page : nullptr; + bool ownNewPage = false; + bool deleteOldPage = false; + // Change pointers first. if (page && oldView != view) { - if (oldView) + if (oldView) { + ownNewPage = oldView->d_func()->m_ownsPage; oldView->d_func()->page = nullptr; + oldView->d_func()->m_ownsPage = false; + } page->d_func()->view = view; } if (view && oldPage != page) { - if (oldPage) - oldPage->d_func()->view = nullptr; + if (oldPage) { + if (oldPage->d_func()) + oldPage->d_func()->view = nullptr; + deleteOldPage = view->d_func()->m_ownsPage; + } + view->d_func()->m_ownsPage = ownNewPage; view->d_func()->page = page; } // Then notify. auto widget = page ? page->d_func()->widget : nullptr; - auto oldWidget = oldPage ? oldPage->d_func()->widget : nullptr; + auto oldWidget = (oldPage && oldPage->d_func()) ? oldPage->d_func()->widget : nullptr; if (page && oldView != view && oldView) { oldView->d_func()->pageChanged(page, nullptr); @@ -772,17 +804,15 @@ void QWebEnginePagePrivate::bindPageAndView(QWebEnginePage *page, QWebEngineView } if (view && oldPage != page) { - view->d_func()->pageChanged(oldPage, page); + if (oldPage && oldPage->d_func()) + view->d_func()->pageChanged(oldPage, page); + else + view->d_func()->pageChanged(nullptr, page); if (oldWidget != widget) view->d_func()->widgetChanged(oldWidget, widget); - - // At this point m_ownsPage should still refer to oldPage, - // it is only set for the new page after binding. - if (view->d_func()->m_ownsPage) { - delete oldPage; - view->d_func()->m_ownsPage = false; - } } + if (deleteOldPage) + delete oldPage; } void QWebEnginePagePrivate::bindPageAndWidget(QWebEnginePage *page, RenderWidgetHostViewQtDelegateWidget *widget) @@ -793,7 +823,7 @@ void QWebEnginePagePrivate::bindPageAndWidget(QWebEnginePage *page, RenderWidget // Change pointers first. if (widget && oldPage != page) { - if (oldPage) + if (oldPage && oldPage->d_func()) oldPage->d_func()->widget = nullptr; widget->m_page = page; } @@ -806,7 +836,7 @@ void QWebEnginePagePrivate::bindPageAndWidget(QWebEnginePage *page, RenderWidget // Then notify. - if (widget && oldPage != page && oldPage) { + if (widget && oldPage != page && oldPage && oldPage->d_func()) { if (auto oldView = oldPage->d_func()->view) oldView->d_func()->widgetChanged(widget, nullptr); } @@ -1757,11 +1787,9 @@ void QWebEnginePagePrivate::allowCertificateError(const QSharedPointererror(), controller->url(), controller->overridable() && !controller->strictEnforcement(), controller->errorString()); + QWebEngineCertificateError error(controller->error(), controller->url(), controller->overridable(), controller->errorString()); accepted = q->certificateError(error); - - if (error.isOverridable()) - controller->accept(accepted); + controller->accept(error.isOverridable() && accepted); } void QWebEnginePagePrivate::selectClientCert(const QSharedPointer &controller) @@ -1857,7 +1885,7 @@ void QWebEnginePagePrivate::setToolTip(const QString &toolTipText) } // Update tooltip if text was changed. - QString wrappedTip = QLatin1String("

") + QString wrappedTip = QLatin1String("

") % toolTipText.toHtmlEscaped().left(MaxTooltipLength) % QLatin1String("

"); if (view->toolTip() != wrappedTip) @@ -2153,7 +2181,7 @@ void QWebEnginePage::runJavaScript(const QString& scriptSource, quint32 worldId, In addition, a page might also execute scripts added through QWebEngineProfile::scripts(). - \sa QWebEngineScriptCollection, QWebEngineScript + \sa QWebEngineScriptCollection, QWebEngineScript, {Script Injection} */ QWebEngineScriptCollection &QWebEnginePage::scripts() diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h index 25ed8937157..f8f67d3417f 100644 --- a/src/webenginewidgets/api/qwebenginepage_p.h +++ b/src/webenginewidgets/api/qwebenginepage_p.h @@ -144,6 +144,7 @@ class QWebEnginePagePrivate : public QtWebEngineCore::WebContentsAdapterClient void updateScrollPosition(const QPointF &position) override; void updateContentsSize(const QSizeF &size) override; void updateNavigationActions() override; + void updateEditActions() override; void startDragging(const content::DropData &dropData, Qt::DropActions allowedActions, const QPixmap &pixmap, const QPoint &offset) override; bool supportsDragging() const override; diff --git a/src/webenginewidgets/api/qwebengineprofile.cpp b/src/webenginewidgets/api/qwebengineprofile.cpp index 929c2aaa1a8..6644af3ffa8 100644 --- a/src/webenginewidgets/api/qwebengineprofile.cpp +++ b/src/webenginewidgets/api/qwebengineprofile.cpp @@ -593,7 +593,8 @@ bool QWebEngineProfile::visitedLinksContainsUrl(const QUrl &url) const Returns the collection of scripts that are injected into all pages that share this profile. - \sa QWebEngineScriptCollection, QWebEngineScript, QWebEnginePage::scripts() + \sa QWebEngineScriptCollection, QWebEngineScript, QWebEnginePage::scripts(), + {Script Injection} */ QWebEngineScriptCollection *QWebEngineProfile::scripts() const { @@ -688,20 +689,7 @@ QWebEngineSettings *QWebEngineProfile::settings() const const QWebEngineUrlSchemeHandler *QWebEngineProfile::urlSchemeHandler(const QByteArray &scheme) const { const Q_D(QWebEngineProfile); - if (d->profileAdapter()->customUrlSchemeHandlers().contains(scheme)) - return d->profileAdapter()->customUrlSchemeHandlers().value(scheme); - return 0; -} - -static bool checkInternalScheme(const QByteArray &scheme) -{ - static QSet internalSchemes; - if (internalSchemes.isEmpty()) { - internalSchemes << QByteArrayLiteral("qrc") << QByteArrayLiteral("data") << QByteArrayLiteral("blob") - << QByteArrayLiteral("http") << QByteArrayLiteral("https") << QByteArrayLiteral("ftp") - << QByteArrayLiteral("javascript"); - } - return internalSchemes.contains(scheme); + return d->profileAdapter()->customUrlSchemeHandlers().value(scheme.toLower()); } /*! @@ -716,23 +704,8 @@ void QWebEngineProfile::installUrlSchemeHandler(const QByteArray &scheme, QWebEn { Q_D(QWebEngineProfile); Q_ASSERT(handler); - QByteArray canonicalScheme = scheme.toLower(); - if (checkInternalScheme(canonicalScheme)) { - qWarning("Cannot install a URL scheme handler overriding internal scheme: %s", scheme.constData()); - return; - } - - if (d->profileAdapter()->customUrlSchemeHandlers().contains(canonicalScheme)) { - if (d->profileAdapter()->customUrlSchemeHandlers().value(canonicalScheme) != handler) - qWarning("URL scheme handler already installed for the scheme: %s", scheme.constData()); + if (!d->profileAdapter()->addCustomUrlSchemeHandler(scheme, handler)) return; - } - - if (QWebEngineUrlScheme::schemeByName(canonicalScheme) == QWebEngineUrlScheme()) - qWarning("Please register the custom scheme '%s' via QWebEngineUrlScheme::registerScheme() " - "before installing the custom scheme handler.", scheme.constData()); - - d->profileAdapter()->addCustomUrlSchemeHandler(canonicalScheme, handler); connect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*))); } diff --git a/src/webenginewidgets/api/qwebenginescript.cpp b/src/webenginewidgets/api/qwebenginescript.cpp index d1e996b3ac3..af6b9aa8d33 100644 --- a/src/webenginewidgets/api/qwebenginescript.cpp +++ b/src/webenginewidgets/api/qwebenginescript.cpp @@ -67,6 +67,8 @@ using QtWebEngineCore::UserScript; Use QWebEnginePage::scripts() and QWebEngineProfile::scripts() to access the collection of scripts associated with a single page or a number of pages sharing the same profile. + + \sa {Script Injection} */ /*! \enum QWebEngineScript::InjectionPoint diff --git a/src/webenginewidgets/api/qwebenginescriptcollection.cpp b/src/webenginewidgets/api/qwebenginescriptcollection.cpp index 5ef0ffd44cd..8cbeeb804cd 100644 --- a/src/webenginewidgets/api/qwebenginescriptcollection.cpp +++ b/src/webenginewidgets/api/qwebenginescriptcollection.cpp @@ -55,6 +55,8 @@ using QtWebEngineCore::UserScript; Use QWebEnginePage::scripts() and QWebEngineProfile::scripts() to access the collection of scripts associated with a single page or a number of pages sharing the same profile. + + \sa {Script Injection} */ /*! diff --git a/src/webenginewidgets/doc/src/qtwebkitportingguide.qdoc b/src/webenginewidgets/doc/src/qtwebkitportingguide.qdoc index e3fdc4ff106..9e8cc463c2d 100644 --- a/src/webenginewidgets/doc/src/qtwebkitportingguide.qdoc +++ b/src/webenginewidgets/doc/src/qtwebkitportingguide.qdoc @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the documentation of the Qt Toolkit. @@ -266,7 +266,6 @@ page.runJavaScript("document.documentElement.contentEditable = true"); \endcode - \section1 Unavailable Qt WebKit API The Qt \WebKit classes and methods in this list will not be available in \QWE. @@ -310,5 +309,11 @@ engine process them by overloading the QWebEnginePage::acceptNavigationRequest() function. This is necessary when an HTML document is used as part of the user interface, and not to display external data, for example, when displaying a list of results. + + \note \l{QWebEnginePage::}{acceptNavigationRequest()} starts the + loading process and emits the \l{QWebEnginePage::}{loadStarted()} + signal \e before the request is accepted or rejected. Therefore, a + \l{QWebEnginePage::}{loadFinished()} signal that returns \c false + is to be expected even after delegating the request. \endtable */ diff --git a/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc b/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc index 9aca48ebfc2..2bf4d041329 100644 --- a/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc +++ b/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc @@ -1,5 +1,5 @@ /* - Copyright (C) 2015 The Qt Company Ltd. + Copyright (C) 2019 The Qt Company Ltd. Copyright (C) 2008, 2009, 2012 Nokia Corporation and/or its subsidiary(-ies) Copyright (C) 2007 Staikos Computing Services Inc. Copyright (C) 2007 Apple Inc. @@ -112,8 +112,7 @@ Actions only have an effect when they are applicable. The availability of actions can be be determined by checking - \l{QAction::}{isEnabled()} on the action returned by action(). Context-dependent - actions are always enabled. + \l{QAction::}{isEnabled()} on the action returned by action(). \value NoWebAction No action is triggered. \value Back Navigate back in the history of navigated links. @@ -126,7 +125,10 @@ \value Paste Paste content from the clipboard. \value Undo Undo the last editing action. \value Redo Redo the last editing action. - \value SelectAll Select all content. + \value SelectAll Select all content. This action is only enabled when the page's content is focused. + The focus can be forced by the JavaScript \c{window.focus()} call, or the + \l{QWebEngineSettings::FocusOnNavigationEnabled} {FocusOnNavigationEnabled} setting + should be enabled to get automatic focus. \value PasteAndMatchStyle Paste content from the clipboard with current style. \value OpenLinkInThisWindow Open the current link in the current window. (Added in Qt 5.6) @@ -381,6 +383,11 @@ used as part of the user interface, and not to display external data, for example, when displaying a list of results. + \note The loading process is started and the loadStarted() signal is emitted + \e before the request is accepted or rejected. Therefore, a loadFinished() + signal that returns \c false is to be expected even after delegating the + request. + The \l{QWebEngineUrlRequestInterceptor} class offers further options for intercepting and manipulating requests. */ @@ -528,7 +535,7 @@ This signal is emitted when a page starts loading content. - \sa loadFinished() + \sa loadFinished(), acceptNavigationRequest() */ /*! @@ -548,7 +555,14 @@ is independent of script execution or page rendering. \a ok will indicate whether the load was successful or any error occurred. - \sa loadStarted() + \note Navigation requests can be delegated to the Qt application instead + of having the HTML handler engine process them by overloading the + acceptNavigationRequest() function. Because the loading process is started + and the loadStarted() signal is emitted \e before the request is accepted + or rejected, a \c loadFinished() signal that returns \c false is to be + expected even after delegating the request. + + \sa loadStarted(), acceptNavigationRequest() */ /*! @@ -745,10 +759,15 @@ \fn void QWebEnginePage::runJavaScript(const QString &scriptSource, const QWebEngineCallback &resultCallback) \since 5.7 - Runs the JavaScript code contained in \a scriptSource in the world specified by \a worldId. - The world ID values are the same as provided by QWebEngineScript::ScriptWorldId, and between \c 0 - and \c 256. Using the \e runJavaScript() versions without the world ID is the same as running the - script in the \c MainWorld. + Runs the JavaScript code contained in \a scriptSource without checking + whether the DOM of the page has been constructed. If you need more + control over how the script is run, consider using \l scripts() instead. + + To avoid conflicts with other scripts executed on the page, the world in + which the script is run is specified by \a worldId. The world ID values are + the same as provided by QWebEngineScript::ScriptWorldId, and between \c 0 + and \c 256. If you leave out the \c world ID, the script is run in the + \c MainWorld. When the script has been executed, \a resultCallback is called with the result of the last executed statement. \c resultCallback can be any of a function pointer, a functor or a lambda, @@ -758,6 +777,11 @@ page.runJavaScript("document.title", [](const QVariant &v) { qDebug() << v.toString(); }); \endcode + Only plain data can be returned from JavaScript as the result value. + Supported data types include all of the JSON data types as well as, for + example, \c{Date} and \c{ArrayBuffer}. Unsupported data types include, for + example, \c{Function} and \c{Promise}. + \warning Do not execute lengthy routines in the callback function, because it might block the rendering of the web engine page. @@ -765,9 +789,7 @@ during page destruction. When QWebEnginePage is deleted, the callback is triggered with an invalid value and it is not safe to use the corresponding QWebEnginePage or QWebEngineView instance inside it. - See scripts() for an alternative API to inject scripts. - - \sa QWebEngineScript::ScriptWorldId + \sa scripts(), QWebEngineScript::ScriptWorldId, {Script Injection} */ /*! diff --git a/src/webenginewidgets/doc/src/qwebengineview_lgpl.qdoc b/src/webenginewidgets/doc/src/qwebengineview_lgpl.qdoc index fce1e8d8f41..1b7812dffd1 100644 --- a/src/webenginewidgets/doc/src/qwebengineview_lgpl.qdoc +++ b/src/webenginewidgets/doc/src/qwebengineview_lgpl.qdoc @@ -1,5 +1,5 @@ /* - Copyright (C) 2015 The Qt Company Ltd. + Copyright (C) 2019 The Qt Company Ltd. Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) Copyright (C) 2008 Holger Hans Peter Freyther Copyright (C) 2009 Girish Ramakrishnan @@ -125,7 +125,11 @@ Sets the content of the web view to the specified \a html content. External objects, such as stylesheets or images referenced in the HTML - document, are located relative to \a baseUrl. + document, are located relative to \a baseUrl. For external objects to + be loaded, \c baseUrl cannot be empty. For example, if \a html + is retrieved from \c http://www.example.com/documents/overview.html, which + is the base URL, then an image referenced with the relative URL, \c diagram.png, + should be at \c{http://www.example.com/documents/diagram.png}. The HTML document is loaded immediately, whereas external objects are loaded asynchronously. @@ -156,6 +160,7 @@ is empty, it is assumed that the content is \c{text/plain,charset=US-ASCII}. External objects referenced in the content are located relative to \a baseUrl. + For external objects to be loaded, \c baseUrl cannot be empty. The data is loaded immediately; external objects are loaded asynchronously. 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 55bdb3a6251..18f1e97d00a 100644 --- a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp +++ b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp @@ -77,14 +77,6 @@ class RenderWidgetHostViewQuickItem : public QQuickItem { { m_client->forwardEvent(event); } - void keyPressEvent(QKeyEvent *event) override - { - m_client->forwardEvent(event); - } - void keyReleaseEvent(QKeyEvent *event) override - { - m_client->forwardEvent(event); - } void inputMethodEvent(QInputMethodEvent *event) override { m_client->forwardEvent(event); @@ -479,7 +471,7 @@ bool RenderWidgetHostViewQtDelegateWidget::event(QEvent *event) // where we can simply ignore the DblClick event. QMouseEvent *dblClick = static_cast(event); QMouseEvent press(QEvent::MouseButtonPress, dblClick->localPos(), dblClick->windowPos(), dblClick->screenPos(), - dblClick->button(), dblClick->buttons(), dblClick->modifiers()); + dblClick->button(), dblClick->buttons(), dblClick->modifiers(), dblClick->source()); press.setTimestamp(dblClick->timestamp()); handled = m_client->forwardEvent(&press); } else @@ -487,6 +479,8 @@ 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; } diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/qwebengineurlrequestinterceptor.pro b/tests/auto/core/qwebengineurlrequestinterceptor/qwebengineurlrequestinterceptor.pro index e99c7f4938c..9c239f1a7cd 100644 --- a/tests/auto/core/qwebengineurlrequestinterceptor/qwebengineurlrequestinterceptor.pro +++ b/tests/auto/core/qwebengineurlrequestinterceptor/qwebengineurlrequestinterceptor.pro @@ -1 +1,2 @@ include(../tests.pri) +include(../../shared/http.pri) diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp index 23bf88417c3..7c99100eb40 100644 --- a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp +++ b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp @@ -34,6 +34,9 @@ #include #include +#include +#include + class tst_QWebEngineUrlRequestInterceptor : public QObject { Q_OBJECT @@ -59,6 +62,7 @@ private Q_SLOTS: void requestInterceptorByResourceType_data(); void requestInterceptorByResourceType(); void firstPartyUrlHttp(); + void passRefererHeader(); }; tst_QWebEngineUrlRequestInterceptor::tst_QWebEngineUrlRequestInterceptor() @@ -79,6 +83,7 @@ void tst_QWebEngineUrlRequestInterceptor::cleanup() void tst_QWebEngineUrlRequestInterceptor::initTestCase() { + QSKIP("Interceptor has race condition in 5.12, skipping the test."); } void tst_QWebEngineUrlRequestInterceptor::cleanupTestCase() @@ -97,6 +102,9 @@ struct RequestInfo { int resourceType; }; +static const QByteArray kHttpHeaderReferrerValue = QByteArrayLiteral("http://somereferrer.com/"); +static const QByteArray kHttpHeaderRefererName = QByteArrayLiteral("referer"); + class TestRequestInterceptor : public QWebEngineUrlRequestInterceptor { public: @@ -112,6 +120,9 @@ class TestRequestInterceptor : public QWebEngineUrlRequestInterceptor if (shouldIntercept && info.requestUrl().toString().endsWith(QLatin1String("__placeholder__"))) info.redirect(QUrl("qrc:///resources/content.html")); + // Set referrer header + info.setHttpHeader(kHttpHeaderRefererName, kHttpHeaderReferrerValue); + requestInfos.append(info); } @@ -487,5 +498,38 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrlHttp() QCOMPARE(info.firstPartyUrl, firstPartyUrl); } +void tst_QWebEngineUrlRequestInterceptor::passRefererHeader() +{ + // Create HTTP Server to parse the request. + HttpServer httpServer; + + if (!httpServer.start()) + QSKIP("Failed to start http server"); + + bool succeeded = false; + connect(&httpServer, &HttpServer::newRequest, [&succeeded](HttpReqRep *rr) { + const QByteArray headerValue = rr->requestHeader(kHttpHeaderRefererName); + QCOMPARE(headerValue, kHttpHeaderReferrerValue); + succeeded = headerValue == kHttpHeaderReferrerValue; + rr->setResponseStatus(200); + rr->sendResponse(); + }); + + QWebEngineProfile profile; + TestRequestInterceptor interceptor(true); + profile.setRequestInterceptor(&interceptor); + + QWebEnginePage page(&profile); + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + QWebEngineHttpRequest httpRequest; + QUrl requestUrl = httpServer.url(); + httpRequest.setUrl(requestUrl); + page.load(httpRequest); + + QVERIFY(spy.wait()); + (void) httpServer.stop(); + QVERIFY(succeeded); +} + QTEST_MAIN(tst_QWebEngineUrlRequestInterceptor) #include "tst_qwebengineurlrequestinterceptor.moc" diff --git a/tests/auto/quick/dialogs/WebView.qml b/tests/auto/quick/dialogs/WebView.qml index 4f8b7a0ce52..01f4ac2974c 100644 --- a/tests/auto/quick/dialogs/WebView.qml +++ b/tests/auto/quick/dialogs/WebView.qml @@ -56,11 +56,12 @@ Window { WebEngineView { id: view anchors.fill: parent - onLoadingChanged: function(reqeust) { - if (reqeust.status === WebEngineView.LoadSucceededStatus) { + onLoadingChanged: function(request) { + if (request.status === WebEngineView.LoadSucceededStatus) { handler.ready = true - } else { - console.log("Wooohooo loading page from qrc failed !") + } else if (request.status === WebEngineView.LoadFailedStatus) { + console.log("Page was not successfully loaded from qrc! Status: " + request.status + + ", error [code: " + request.errorCode + "]: '" + request.errorString + "'") } } diff --git a/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp b/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp index 8e23e86e853..922c7769e93 100644 --- a/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp +++ b/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp @@ -167,7 +167,7 @@ void tst_InspectorServer::openRemoteDebuggingSession() // - The page list didn't return a valid inspector URL // - Or the front-end couldn't be loaded through the inspector HTTP server // - Or the web socket connection couldn't be established between the front-end and the page through the inspector server - QTRY_VERIFY(inspectorWebView->title().startsWith("DevTools -")); + QTRY_VERIFY_WITH_TIMEOUT(inspectorWebView->title().startsWith("DevTools -"), 20000); } QTEST_MAIN(tst_InspectorServer) diff --git a/tests/auto/quick/qmltests/BLACKLIST b/tests/auto/quick/qmltests/BLACKLIST index dfafbaea456..083c1598404 100644 --- a/tests/auto/quick/qmltests/BLACKLIST +++ b/tests/auto/quick/qmltests/BLACKLIST @@ -1,9 +1,3 @@ -[WebViewGeopermission::test_deniedGeolocationByUser] -osx - -[WebViewGeopermission::test_geoPermissionRequest] -osx - [WebEngineViewSingleFileUpload::test_acceptDirectory] * @@ -13,8 +7,5 @@ osx [WebEngineViewSingleFileUpload::test_acceptSingleFileSelection] * -[WebViewFindText::test_findTextInterruptedByLoad] -* - [WebEngineViewSource::test_viewSourceURL] * diff --git a/tests/auto/quick/qmltests/data/tst_action.qml b/tests/auto/quick/qmltests/data/tst_action.qml index f6d8669febd..56a91d8d0b0 100644 --- a/tests/auto/quick/qmltests/data/tst_action.qml +++ b/tests/auto/quick/qmltests/data/tst_action.qml @@ -127,5 +127,59 @@ TestWebEngineView { stopAction.trigger(); compare(stopTriggerSpy.count, 0); } + + function test_editActionsWithExplicitFocus() { + var webView = Qt.createQmlObject("TestWebEngineView { visible: false; }", webEngineView); + webView.settings.focusOnNavigationEnabled = false; + + // The view is hidden and no focus on the page. Edit actions should be disabled. + var selectAllAction = webView.action(WebEngineView.SelectAll); + verify(selectAllAction); + verify(!selectAllAction.enabled); + + var triggerSpy = createTemporaryObject(signalSpy, webEngineView, {target: selectAllAction, signalName: "triggered"}); + var enabledSpy = createTemporaryObject(signalSpy, webEngineView, {target: selectAllAction, signalName: "enabledChanged"}); + + webView.loadHtml("
foo bar
"); + verify(webView.waitForLoadSucceeded()); + + // Still no focus because focus on navigation is disabled. Edit actions don't do anything (should not crash). + verify(!selectAllAction.enabled); + compare(enabledSpy.count, 0); + selectAllAction.trigger(); + compare(triggerSpy.count, 0); + compare(getTextSelection(), ""); + + // Focus content by focusing window from JavaScript. Edit actions should be enabled and functional. + webView.runJavaScript("window.focus();"); + tryVerify(function() { return enabledSpy.count === 1 }); + verify(selectAllAction.enabled); + selectAllAction.trigger(); + compare(triggerSpy.count, 1); + tryVerify(function() { return webView.getTextSelection() === "foo bar" }); + } + + function test_editActionsWithInitialFocus() { + var webView = Qt.createQmlObject("TestWebEngineView { visible: false; }", webEngineView); + webView.settings.focusOnNavigationEnabled = true; + + // The view is hidden and no focus on the page. Edit actions should be disabled. + var selectAllAction = webView.action(WebEngineView.SelectAll); + verify(selectAllAction); + verify(!selectAllAction.enabled); + + var triggerSpy = createTemporaryObject(signalSpy, webEngineView, {target: selectAllAction, signalName: "triggered"}); + var enabledSpy = createTemporaryObject(signalSpy, webEngineView, {target: selectAllAction, signalName: "enabledChanged"}); + + webView.loadHtml("
foo bar
"); + verify(webView.waitForLoadSucceeded()); + + // Content gets initial focus. + tryVerify(function() { return enabledSpy.count === 1 }); + verify(selectAllAction.enabled); + selectAllAction.trigger(); + compare(triggerSpy.count, 1); + tryVerify(function() { return webView.getTextSelection() === "foo bar" }); + } } } diff --git a/tests/auto/quick/qmltests/data/tst_findText.qml b/tests/auto/quick/qmltests/data/tst_findText.qml index dfcfd586f9d..93aa48365de 100644 --- a/tests/auto/quick/qmltests/data/tst_findText.qml +++ b/tests/auto/quick/qmltests/data/tst_findText.qml @@ -43,13 +43,21 @@ TestWebEngineView { matchCount = -1 } + function findCallbackCalled() { return matchCount != -1 } + function findTextCallback(matchCount) { + // If this starts to fail then either clear was not called before findText + // or unexpected callback was triggered from some search. + // On c++ side callback id can be checked to verify + testcase.verify(!findCallbackCalled(), 'Unexpected callback call or uncleared state before findText call!') + webEngineView.matchCount = matchCount findFailed = matchCount == 0 } TestCase { + id: testcase name: "WebViewFindText" function getBodyInnerHTML() { @@ -207,13 +215,17 @@ TestWebEngineView { webEngineView.findText("hello", findFlags, webEngineView.findTextCallback); // This should not crash. - webEngineView.url = "https://www.qt.io/"; - if (!webEngineView.waitForLoadSucceeded(12000)) - skip("Couldn't load page from network, skipping test."); + webEngineView.loadHtml("New page with same hello text") + verify(webEngineView.waitForLoadSucceeded()) // The callback is not supposed to be called, see QTBUG-61506. - // Check whether the callback was called (-1 = no, other values = yes). - tryVerify(function() { return webEngineView.matchCount == -1; }, 20000); + expectFailContinue('', 'No unexpected findText callback calls occurred.') + tryVerify(function() { return webEngineView.findCallbackCalled() }) + verify(!webEngineView.findCallbackCalled()) + + webEngineView.clear(); + webEngineView.findText('New page', findFlags, webEngineView.findTextCallback) + tryCompare(webEngineView, 'matchCount', 1) } } } diff --git a/tests/auto/quick/qquickwebengineview/BLACKLIST b/tests/auto/quick/qquickwebengineview/BLACKLIST index 76cb18c1e45..5f46fa47eb6 100644 --- a/tests/auto/quick/qquickwebengineview/BLACKLIST +++ b/tests/auto/quick/qquickwebengineview/BLACKLIST @@ -1,14 +1,3 @@ -[transparentWebEngineViews] -windows - -[inputEventForwardingDisabledWhenActiveFocusOnPressDisabled] -* - -[transparentWebEngineViews] -* - -[basicRenderingSanity] -* [javascriptClipboard:default] opensuse-leap [javascriptClipboard:canPaste] diff --git a/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp b/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp index b30fc7258a1..9732de85c04 100644 --- a/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp +++ b/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp @@ -70,6 +70,7 @@ private Q_SLOTS: void downloadFileNot2(); void downloadDeleted(); void downloadDeletedByProfile(); + void downloadPathValidation(); private: void saveLink(QPoint linkPos); @@ -844,5 +845,127 @@ void tst_QWebEngineDownloadItem::downloadDeletedByProfile() QTRY_COMPARE(downloadItem.isNull(), true); } +void tst_QWebEngineDownloadItem::downloadPathValidation() +{ + const QString fileName = "test.txt"; + QString downloadPath; + QString originalDownloadPath; + + QTemporaryDir tmpDir; + QVERIFY(tmpDir.isValid()); + + // Set up HTTP server + ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) { + if (rr->requestMethod() == "GET" && rr->requestPath() == ("/" + fileName)) { + rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("application/octet-stream")); + rr->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment")); + rr->setResponseBody(QByteArrayLiteral("a")); + rr->sendResponse(); + } else { + rr->setResponseStatus(404); + rr->sendResponse(); + } + }); + + // Set up profile and download handler + QPointer downloadItem; + ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) { + downloadItem = item; + originalDownloadPath = item->path(); + + item->setPath(downloadPath); + // TODO: Do not cancel download from 5.13. This is for not messing up system download path. + // Use m_profile->setDownloadPath(tmpDir.path()) at initialization. + if (item->path() != downloadPath) + item->cancel(); + else + item->accept(); + + connect(item, &QWebEngineDownloadItem::stateChanged, [&, item](QWebEngineDownloadItem::DownloadState downloadState) { + if (downloadState == QWebEngineDownloadItem::DownloadInterrupted) { + item->cancel(); + } + }); + + connect(item, &QWebEngineDownloadItem::finished, [&, item]() { + QCOMPARE(item->isFinished(), true); + QCOMPARE(item->totalBytes(), item->receivedBytes()); + QVERIFY(item->receivedBytes() > 0); + QCOMPARE(item->page(), m_page); + }); + }); + + QString oldPath = QDir::currentPath(); + QDir::setCurrent(tmpDir.path()); + + // Set only the file name. + downloadItem.clear(); + originalDownloadPath = ""; + downloadPath = fileName; + m_page->setUrl(m_server->url("https://github.com/%22%20+%20fileName)); + QTRY_VERIFY(downloadItem); + QTRY_COMPARE(downloadItem->state(), QWebEngineDownloadItem::DownloadCompleted); + QCOMPARE(downloadItem->interruptReason(), QWebEngineDownloadItem::NoReason); + QCOMPARE(downloadItem->path(), fileName); + + // Set only the directory path. + downloadItem.clear(); + originalDownloadPath = ""; + downloadPath = tmpDir.path(); + m_page->setUrl(m_server->url("https://github.com/%22%20+%20fileName)); + QTRY_VERIFY(downloadItem); + QTRY_COMPARE(downloadItem->state(), QWebEngineDownloadItem::DownloadCancelled); + QCOMPARE(downloadItem->interruptReason(), QWebEngineDownloadItem::UserCanceled); + QCOMPARE(downloadItem->path(), originalDownloadPath); + + // Set only the directory path with separator. + downloadItem.clear(); + originalDownloadPath = ""; + downloadPath = tmpDir.path() + QDir::separator(); + m_page->setUrl(m_server->url("https://github.com/%22%20+%20fileName)); + QTRY_VERIFY(downloadItem); + QTRY_COMPARE(downloadItem->state(), QWebEngineDownloadItem::DownloadCancelled); + QCOMPARE(downloadItem->interruptReason(), QWebEngineDownloadItem::UserCanceled); + QCOMPARE(downloadItem->path(), originalDownloadPath); + + // Set only the directory with the current directory path without ending separator. + downloadItem.clear(); + originalDownloadPath = ""; + downloadPath = "."; + m_page->setUrl(m_server->url("https://github.com/%22%20+%20fileName)); + QTRY_VERIFY(downloadItem); + QTRY_COMPARE(downloadItem->state(), QWebEngineDownloadItem::DownloadCancelled); + QCOMPARE(downloadItem->interruptReason(), QWebEngineDownloadItem::UserCanceled); + QCOMPARE(downloadItem->path(), originalDownloadPath); + + // Set only the directory with the current directory path with ending separator. + downloadItem.clear(); + originalDownloadPath = ""; + downloadPath = "./"; + m_page->setUrl(m_server->url("https://github.com/%22%20+%20fileName)); + QTRY_VERIFY(downloadItem); + QTRY_COMPARE(downloadItem->state(), QWebEngineDownloadItem::DownloadCancelled); + QCOMPARE(downloadItem->interruptReason(), QWebEngineDownloadItem::UserCanceled); + QCOMPARE(downloadItem->path(), originalDownloadPath); + + + + downloadItem.clear(); + originalDownloadPath = ""; + downloadPath = "..."; + m_page->setUrl(m_server->url("https://github.com/%22%20+%20fileName)); + QTRY_VERIFY(downloadItem); + QTRY_COMPARE(downloadItem->state(), QWebEngineDownloadItem::DownloadCancelled); +#if !defined(Q_OS_WIN) + QCOMPARE(downloadItem->interruptReason(), QWebEngineDownloadItem::FileFailed); + QCOMPARE(downloadItem->path(), downloadPath); +#else + // Windows interprets the "..." path as a valid path. It will be the current path. + QCOMPARE(downloadItem->interruptReason(), QWebEngineDownloadItem::UserCanceled); + QCOMPARE(downloadItem->path(), originalDownloadPath); +#endif // !defined(Q_OS_WIN) + QDir::setCurrent(oldPath); +} + QTEST_MAIN(tst_QWebEngineDownloadItem) #include "tst_qwebenginedownloaditem.moc" diff --git a/tests/auto/widgets/qwebenginepage/BLACKLIST b/tests/auto/widgets/qwebenginepage/BLACKLIST index 228efd61c6f..1453f6e958e 100644 --- a/tests/auto/widgets/qwebenginepage/BLACKLIST +++ b/tests/auto/widgets/qwebenginepage/BLACKLIST @@ -10,9 +10,6 @@ osx [mouseMovementProperties] windows -[getUserMediaRequest] -windows - [getUserMediaRequestDesktopVideoManyPages] windows diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index 8c3b4002c17..080bba6434e 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -227,6 +227,12 @@ private Q_SLOTS: void triggerActionWithoutMenu(); void dynamicFrame(); + void editActionsWithExplicitFocus(); + void editActionsWithInitialFocus(); + void editActionsWithFocusOnIframe(); + + void customUserAgentInNewTab(); + private: static QPoint elementCenter(QWebEnginePage *page, const QString &id); @@ -1137,8 +1143,8 @@ void tst_QWebEnginePage::textSelection() QCOMPARE(page->action(QWebEnginePage::SelectEndOfDocument)->isEnabled(), false); #endif - // ..but SelectAll is awalys enabled - QCOMPARE(page->action(QWebEnginePage::SelectAll)->isEnabled(), true); + // ..but SelectAll is disabled because the page has no focus due to disabled FocusOnNavigationEnabled. + QCOMPARE(page->action(QWebEnginePage::SelectAll)->isEnabled(), false); // Verify hasSelection returns false since there is no selection yet... QCOMPARE(page->hasSelection(), false); @@ -1321,20 +1327,39 @@ void tst_QWebEnginePage::textEditing() void tst_QWebEnginePage::backActionUpdate() { QWebEngineView view; + view.resize(640, 480); + view.show(); + QWebEnginePage *page = view.page(); + QSignalSpy loadSpy(page, &QWebEnginePage::loadFinished); QAction *action = page->action(QWebEnginePage::Back); QVERIFY(!action->isEnabled()); - QSignalSpy loadSpy(page, SIGNAL(loadFinished(bool))); - QUrl url = QUrl("qrc:///resources/framedindex.html"); - page->load(url); - QTRY_COMPARE(loadSpy.count(), 1); + + page->load(QUrl("qrc:///resources/framedindex.html")); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); QVERIFY(!action->isEnabled()); - QTest::mouseClick(&view, Qt::LeftButton, 0, QPoint(10, 10)); - QEXPECT_FAIL("", "Behavior change: Load signals are emitted only for the main frame in QtWebEngine.", Continue); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 2, 100); - QEXPECT_FAIL("", "FIXME: Mouse events aren't passed from the QWebEngineView down to the RWHVQtDelegateWidget", Continue); - QVERIFY(action->isEnabled()); + auto firstAnchorCenterInFrame = [](QWebEnginePage *page, const QString &frameName) { + QVariantList rectList = evaluateJavaScriptSync(page, + "(function(){" + "var frame = document.getElementsByName('" + frameName + "')[0];" + "var anchor = frame.contentDocument.getElementsByTagName('a')[0];" + "var rect = anchor.getBoundingClientRect();" + "return [(rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2];" + "})()").toList(); + + if (rectList.count() != 2) { + qWarning("firstAnchorCenterInFrame failed."); + return QPoint(); + } + + return QPoint(rectList.at(0).toInt(), rectList.at(1).toInt()); + }; + + QVERIFY(evaluateJavaScriptSync(page, "document.getElementsByName('frame_b')[0].contentDocument == undefined").toBool()); + QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, firstAnchorCenterInFrame(page, "frame_c")); + QTRY_VERIFY(evaluateJavaScriptSync(page, "document.getElementsByName('frame_b')[0].contentDocument != undefined").toBool()); + QTRY_VERIFY(action->isEnabled()); } #if defined(QWEBENGINEPAGE_SETTINGS) @@ -1801,6 +1826,7 @@ void tst_QWebEnginePage::findText() QTRY_COMPARE(loadSpy.count(), 1); // Select whole page contents. + QTRY_VERIFY(m_view->page()->action(QWebEnginePage::SelectAll)->isEnabled()); m_view->page()->triggerAction(QWebEnginePage::SelectAll); QTRY_COMPARE(m_view->hasSelection(), true); @@ -2806,6 +2832,18 @@ void tst_QWebEnginePage::runJavaScript() JavaScriptCallbackUndefined callbackUndefined; page.runJavaScript("undefined", QWebEngineCallback(callbackUndefined)); + JavaScriptCallback callbackDate(QVariant(42.0)); + page.runJavaScript("new Date(42000)", QWebEngineCallback(callbackDate)); + + JavaScriptCallback callbackBlob(QVariant(QByteArray(8, 0))); + page.runJavaScript("new ArrayBuffer(8)", QWebEngineCallback(callbackBlob)); + + JavaScriptCallbackUndefined callbackFunction; + page.runJavaScript("(function(){})", QWebEngineCallback(callbackFunction)); + + JavaScriptCallback callbackPromise(QVariant(QVariantMap{})); + page.runJavaScript("new Promise(function(){})", QWebEngineCallback(callbackPromise)); + QVERIFY(watcher.wait()); } @@ -4491,6 +4529,156 @@ void tst_QWebEnginePage::dynamicFrame() QCOMPARE(toPlainTextSync(&page).trimmed(), QStringLiteral("foo")); } +void tst_QWebEnginePage::editActionsWithExplicitFocus() +{ + QWebEngineView view; + QWebEnginePage *page = view.page(); + view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false); + + QSignalSpy loadFinishedSpy(page, &QWebEnginePage::loadFinished); + QSignalSpy selectionChangedSpy(page, &QWebEnginePage::selectionChanged); + QSignalSpy actionChangedSpy(page->action(QWebEnginePage::SelectAll), &QAction::changed); + + // The view is hidden and no focus on the page. Edit actions should be disabled. + QVERIFY(!view.isVisible()); + QVERIFY(!page->action(QWebEnginePage::SelectAll)->isEnabled()); + + page->setHtml(QString("
foo bar
")); + QTRY_COMPARE(loadFinishedSpy.count(), 1); + + // Still no focus because focus on navigation is disabled. Edit actions don't do anything (should not crash). + QVERIFY(!page->action(QWebEnginePage::SelectAll)->isEnabled()); + view.page()->triggerAction(QWebEnginePage::SelectAll); + QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(page->hasSelection(), false); + + // Focus content by focusing window from JavaScript. Edit actions should be enabled and functional. + evaluateJavaScriptSync(page, "window.focus();"); + QTRY_COMPARE(actionChangedSpy.count(), 1); + QVERIFY(page->action(QWebEnginePage::SelectAll)->isEnabled()); + view.page()->triggerAction(QWebEnginePage::SelectAll); + QTRY_COMPARE(selectionChangedSpy.count(), 1); + QCOMPARE(page->hasSelection(), true); + QCOMPARE(page->selectedText(), QStringLiteral("foo bar")); +} + +void tst_QWebEnginePage::editActionsWithInitialFocus() +{ + QWebEngineView view; + QWebEnginePage *page = view.page(); + view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true); + + QSignalSpy loadFinishedSpy(page, &QWebEnginePage::loadFinished); + QSignalSpy selectionChangedSpy(page, &QWebEnginePage::selectionChanged); + QSignalSpy actionChangedSpy(page->action(QWebEnginePage::SelectAll), &QAction::changed); + + // The view is hidden and no focus on the page. Edit actions should be disabled. + QVERIFY(!view.isVisible()); + QVERIFY(!page->action(QWebEnginePage::SelectAll)->isEnabled()); + + page->setHtml(QString("
foo bar
")); + QTRY_COMPARE(loadFinishedSpy.count(), 1); + + // Content gets initial focus. + QTRY_COMPARE(actionChangedSpy.count(), 1); + QVERIFY(page->action(QWebEnginePage::SelectAll)->isEnabled()); + view.page()->triggerAction(QWebEnginePage::SelectAll); + QTRY_COMPARE(selectionChangedSpy.count(), 1); + QCOMPARE(page->hasSelection(), true); + QCOMPARE(page->selectedText(), QStringLiteral("foo bar")); +} + +void tst_QWebEnginePage::editActionsWithFocusOnIframe() +{ + QWebEngineView view; + QWebEnginePage *page = view.page(); + view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false); + + QSignalSpy loadFinishedSpy(page, &QWebEnginePage::loadFinished); + QSignalSpy selectionChangedSpy(page, &QWebEnginePage::selectionChanged); + QSignalSpy actionChangedSpy(page->action(QWebEnginePage::SelectAll), &QAction::changed); + + // The view is hidden and no focus on the page. Edit actions should be disabled. + QVERIFY(!view.isVisible()); + QVERIFY(!page->action(QWebEnginePage::SelectAll)->isEnabled()); + + page->load(QUrl("qrc:///resources/iframe2.html")); + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QVERIFY(!page->action(QWebEnginePage::SelectAll)->isEnabled()); + + // Focusing an iframe. + evaluateJavaScriptSync(page, "document.getElementsByTagName('iframe')[0].contentWindow.focus()"); + QTRY_COMPARE(actionChangedSpy.count(), 1); + QVERIFY(page->action(QWebEnginePage::SelectAll)->isEnabled()); + view.page()->triggerAction(QWebEnginePage::SelectAll); + QTRY_COMPARE(selectionChangedSpy.count(), 1); + QCOMPARE(page->hasSelection(), true); + QCOMPARE(page->selectedText(), QStringLiteral("inner")); +} + +void tst_QWebEnginePage::customUserAgentInNewTab() +{ + HttpServer server; + QByteArray lastUserAgent; + connect(&server, &HttpServer::newRequest, [&](HttpReqRep *rr) { + QCOMPARE(rr->requestMethod(), "GET"); + lastUserAgent = rr->requestHeader("user-agent"); + rr->setResponseBody(QByteArrayLiteral("Test")); + rr->sendResponse(); + }); + QVERIFY(server.start()); + + class Page : public QWebEnginePage { + public: + QWebEngineProfile *targetProfile = nullptr; + QScopedPointer newPage; + Page(QWebEngineProfile *profile) : QWebEnginePage(profile) {} + private: + QWebEnginePage *createWindow(WebWindowType) override + { + newPage.reset(new QWebEnginePage(targetProfile ? targetProfile : profile(), nullptr)); + return newPage.data(); + } + }; + QWebEngineProfile profile1, profile2; + profile1.setHttpUserAgent(QStringLiteral("custom 1")); + profile2.setHttpUserAgent(QStringLiteral("custom 2")); + Page page(&profile1); + QWebEngineView view; + view.resize(500, 500); + view.setPage(&page); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + QSignalSpy spy(&page, &QWebEnginePage::loadFinished); + + // First check we can get the user-agent passed through normally + page.setHtml(QString("link")); + QTRY_COMPARE(spy.count(), 1); + QVERIFY(spy.takeFirst().value(0).toBool()); + QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("navigator.userAgent")).toString(), profile1.httpUserAgent()); + QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, elementCenter(&page, "link")); + QTRY_VERIFY(page.newPage); + QTRY_VERIFY(!lastUserAgent.isEmpty()); + QCOMPARE(lastUserAgent, profile1.httpUserAgent().toUtf8()); + + // Now check we can get the new user-agent of the profile + page.newPage.reset(); + page.targetProfile = &profile2; + spy.clear(); + lastUserAgent = { }; + page.setHtml(QString("link")); + QTRY_COMPARE(spy.count(), 1); + QVERIFY(spy.takeFirst().value(0).toBool()); + QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, elementCenter(&page, "link")); + QTRY_VERIFY(page.newPage); + QTRY_VERIFY(!lastUserAgent.isEmpty()); + QCOMPARE(lastUserAgent, profile2.httpUserAgent().toUtf8()); +} + 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 d9505cbfd82..e4fc8ec532b 100644 --- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp +++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +44,7 @@ class tst_QWebEngineProfile : public QObject Q_OBJECT private Q_SLOTS: + void initTestCase(); void init(); void cleanup(); void privateProfile(); @@ -53,14 +55,33 @@ private Q_SLOTS: void urlSchemeHandlerFailRequest(); void urlSchemeHandlerFailOnRead(); void urlSchemeHandlerStreaming(); + void urlSchemeHandlerInstallation(); void customUserAgent(); void httpAcceptLanguage(); void downloadItem(); void changePersistentPath(); void initiator(); - void qtbug_72299(); // this should be the last test + void badDeleteOrder(); + void qtbug_71895(); // this should be the last test }; +void tst_QWebEngineProfile::initTestCase() +{ + QWebEngineUrlScheme foo("foo"); + QWebEngineUrlScheme stream("stream"); + QWebEngineUrlScheme letterto("letterto"); + QWebEngineUrlScheme aviancarrier("aviancarrier"); + foo.setSyntax(QWebEngineUrlScheme::Syntax::Host); + stream.setSyntax(QWebEngineUrlScheme::Syntax::HostAndPort); + stream.setDefaultPort(8080); + letterto.setSyntax(QWebEngineUrlScheme::Syntax::Path); + aviancarrier.setSyntax(QWebEngineUrlScheme::Syntax::Path); + QWebEngineUrlScheme::registerScheme(foo); + QWebEngineUrlScheme::registerScheme(stream); + QWebEngineUrlScheme::registerScheme(letterto); + QWebEngineUrlScheme::registerScheme(aviancarrier); +} + void tst_QWebEngineProfile::init() { //make sure defualt global profile is 'default' across all the tests @@ -82,6 +103,7 @@ void tst_QWebEngineProfile::cleanup() profile->setCachePath(QString()); profile->setPersistentStoragePath(QString()); profile->setHttpCacheType(QWebEngineProfile::DiskHttpCache); + profile->removeAllUrlSchemeHandlers(); } void tst_QWebEngineProfile::privateProfile() @@ -445,6 +467,46 @@ void tst_QWebEngineProfile::urlSchemeHandlerStreaming() QCOMPARE(toPlainTextSync(view.page()), QString::fromLatin1(result)); } +void tst_QWebEngineProfile::urlSchemeHandlerInstallation() +{ + FailingUrlSchemeHandler handler; + QWebEngineProfile profile; + + // Builtin schemes that *cannot* be overridden. + for (auto scheme : { "about", "blob", "data", "javascript", "qrc", "https", "http", "file", + "ftp", "wss", "ws", "filesystem", "FileSystem" }) { + QTest::ignoreMessage( + QtWarningMsg, + QRegularExpression("Cannot install a URL scheme handler overriding internal scheme.*")); + profile.installUrlSchemeHandler(scheme, &handler); + QCOMPARE(profile.urlSchemeHandler(scheme), nullptr); + } + + // Builtin schemes that *can* be overridden. + for (auto scheme : { "gopher", "GOPHER" }) { + profile.installUrlSchemeHandler(scheme, &handler); + QCOMPARE(profile.urlSchemeHandler(scheme), &handler); + profile.removeUrlScheme(scheme); + } + + // Other schemes should be registered with QWebEngineUrlScheme first, but + // handler installation still succeeds to preserve backwards compatibility. + QTest::ignoreMessage( + QtWarningMsg, + QRegularExpression("Please register the custom scheme.*")); + profile.installUrlSchemeHandler("tst", &handler); + QCOMPARE(profile.urlSchemeHandler("tst"), &handler); + + // Existing handler cannot be overridden. + FailingUrlSchemeHandler handler2; + QTest::ignoreMessage( + QtWarningMsg, + QRegularExpression("URL scheme handler already installed.*")); + profile.installUrlSchemeHandler("tst", &handler2); + QCOMPARE(profile.urlSchemeHandler("tst"), &handler); + profile.removeUrlScheme("tst"); +} + void tst_QWebEngineProfile::customUserAgent() { QString defaultUserAgent = QWebEngineProfile::defaultProfile()->httpUserAgent(); @@ -569,7 +631,25 @@ void tst_QWebEngineProfile::initiator() QCOMPARE(handler.initiator, QUrl()); } -void tst_QWebEngineProfile::qtbug_72299() +void tst_QWebEngineProfile::badDeleteOrder() +{ + QWebEngineProfile *profile = new QWebEngineProfile(); + QWebEngineView *view = new QWebEngineView(); + view->resize(640, 480); + view->show(); + QVERIFY(QTest::qWaitForWindowExposed(view)); + QWebEnginePage *page = new QWebEnginePage(profile, view); + view->setPage(page); + + QSignalSpy spyLoadFinished(page, SIGNAL(loadFinished(bool))); + page->setHtml(QStringLiteral("

Badly handled page!

")); + QTRY_COMPARE(spyLoadFinished.count(), 1); + + delete profile; + delete view; +} + +void tst_QWebEngineProfile::qtbug_71895() { QWebEngineView view; QSignalSpy loadSpy(view.page(), SIGNAL(loadFinished(bool))); @@ -579,8 +659,9 @@ void tst_QWebEngineProfile::qtbug_72299() view.page()->profile()->setHttpCacheType(QWebEngineProfile::NoCache); view.page()->profile()->cookieStore()->deleteAllCookies(); view.page()->profile()->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); - QVERIFY(loadSpy.front().front().toBool()); + bool gotSignal = loadSpy.count() || loadSpy.wait(20000); + if (!gotSignal) + QSKIP("Couldn't load page from network, skipping test."); } diff --git a/tests/auto/widgets/qwebengineview/BLACKLIST b/tests/auto/widgets/qwebengineview/BLACKLIST index 7c86a72d681..9087067f550 100644 --- a/tests/auto/widgets/qwebengineview/BLACKLIST +++ b/tests/auto/widgets/qwebengineview/BLACKLIST @@ -1,6 +1,3 @@ -[doNotSendMouseKeyboardEventsWhenDisabled] -windows - [microFocusCoordinates] osx diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index 6ab63b54e61..f579506bfca 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp @@ -196,6 +196,7 @@ private Q_SLOTS: void closeOpenerTab(); void switchPage(); void setPageDeletesImplicitPage(); + void setPageDeletesImplicitPage2(); void setViewDeletesImplicitPage(); void setPagePreservesExplicitPage(); void setViewPreservesExplicitPage(); @@ -3074,7 +3075,7 @@ void tst_QWebEngineView::webUIURLs_data() QTest::newRow("usb-internals") << QUrl("chrome://usb-internals") << false; QTest::newRow("user-actions") << QUrl("chrome://user-actions") << false; QTest::newRow("version") << QUrl("chrome://version") << false; - QTest::newRow("webrtc-internals") << QUrl("chrome://webrtc-internals") << true; + QTest::newRow("webrtc-internals") << QUrl("chrome://webrtc-internals") << false; QTest::newRow("webrtc-logs") << QUrl("chrome://webrtc-logs") << false; } @@ -3210,6 +3211,20 @@ void tst_QWebEngineView::setPageDeletesImplicitPage() QVERIFY(!implicitPage); // should be deleted } +void tst_QWebEngineView::setPageDeletesImplicitPage2() +{ + QWebEngineView view1; + QWebEngineView view2; + QPointer implicitPage = view1.page(); + view2.setPage(view1.page()); + QVERIFY(implicitPage); + QVERIFY(view1.page() != implicitPage); + QWebEnginePage explicitPage; + view2.setPage(&explicitPage); + QCOMPARE(view2.page(), &explicitPage); + QVERIFY(!implicitPage); // should be deleted +} + void tst_QWebEngineView::setViewDeletesImplicitPage() { QWebEngineView view; diff --git a/tests/auto/widgets/spellchecking/tst_spellchecking.cpp b/tests/auto/widgets/spellchecking/tst_spellchecking.cpp index b6582083dd4..d02fc78b9eb 100644 --- a/tests/auto/widgets/spellchecking/tst_spellchecking.cpp +++ b/tests/auto/widgets/spellchecking/tst_spellchecking.cpp @@ -174,14 +174,41 @@ void tst_Spellchecking::spellcheck() QString result = evaluateJavaScriptSync(m_view->page(), "text();").toString(); QVERIFY(result == text); - // open menu on misspelled word - m_view->activateMenu(m_view->focusWidget(), rect.center()); - QSignalSpy spyMenuReady(m_view, &WebView::menuReady); - QVERIFY(spyMenuReady.wait()); - - // check if menu is valid - QVERIFY(m_view->data().isValid()); - QVERIFY(m_view->data().isContentEditable()); + bool gotMisspelledWord = false; // clumsy QTRY_VERIFY still execs expr after first success + QString detail; + + // check that spellchecker has done text processing and filled misspelled word + QTRY_VERIFY2([&] () { + detail.clear(); + if (gotMisspelledWord) + return true; + + // open menu on misspelled word + m_view->activateMenu(m_view->focusWidget(), rect.center()); + QSignalSpy spyMenuReady(m_view, &WebView::menuReady); + if (!spyMenuReady.wait()) { + detail = "menu was not shown"; + return false; + } + + if (!m_view->data().isValid()) { + detail = "invalid data"; + return false; + } + + if (!m_view->data().isContentEditable()) { + detail = "content is not editable"; + return false; + } + + if (m_view->data().misspelledWord().isEmpty()) { + detail = "no misspelled word"; + return false; + }; + + gotMisspelledWord = true; + return true; + } (), qPrintable(QString("Context menu: %1").arg(detail))); // check misspelled word QCOMPARE(m_view->data().misspelledWord(), QStringLiteral("lowe"));