diff --git a/.cmake.conf b/.cmake.conf
index 8c2e0e6728c..81507b42a70 100644
--- a/.cmake.conf
+++ b/.cmake.conf
@@ -1,5 +1,6 @@
-set(QT_REPO_MODULE_VERSION "6.8.0")
+set(QT_REPO_MODULE_VERSION "6.8.3")
 set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1")
 set(QT_SUPPORTED_MIN_CMAKE_VERSION_FOR_BUILDING_WEBENGINE "3.19")
 set(QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_AS_CONST=1")
 list(APPEND QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_FOREACH=1")
+list(APPEND QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_QSNPRINTF=1")
diff --git a/.gitreview b/.gitreview
new file mode 100644
index 00000000000..a17ec896fca
--- /dev/null
+++ b/.gitreview
@@ -0,0 +1,4 @@
+[gerrit]
+host=codereview.qt-project.org
+project=qt/qtwebengine
+defaultbranch=dev
diff --git a/CHROMIUM_VERSION b/CHROMIUM_VERSION
index 8bdc0df63b6..d5745c8155d 100644
--- a/CHROMIUM_VERSION
+++ b/CHROMIUM_VERSION
@@ -1,3 +1,3 @@
 
-Based on Chromium version:                                      118.0.5993.220
-Patched with security patches up to Chromium version:           122.0.6261.128
+Based on Chromium version:                                      122.0.6261.171
+Patched with security patches up to Chromium version:           137.0.7151.68
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7d4638139cb..ee7fbca49ad 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,6 +13,12 @@ include(cmake/Functions.cmake)
 project(QtWebEngineDummy)
 find_package(Qt6 6.5 CONFIG REQUIRED COMPONENTS BuildInternals Core)
 
+# Sepcial case of just doing gn build
+if(DEFINED BUILD_ONLY_GN)
+    qt_webengine_build_and_install_gn()
+    return()
+endif()
+
 project(QtWebEngine
     VERSION ${Qt6Core_VERSION}
     DESCRIPTION "QtWebEngine and QtPdf modules"
@@ -26,7 +32,7 @@ find_package(Qt6 ${PROJECT_VERSION} CONFIG QUIET OPTIONAL_COMPONENTS
     WebChannel WebChannelQuick Positioning QuickControls2
     Test QuickWidgets QuickTest WebSockets Designer
     JpegPrivate PngPrivate HarfbuzzPrivate FreetypePrivate ZlibPrivate
-    HttpServer
+    HttpServer Svg
 )
 
 if(MATRIX_BUILD AND NOT MATRIX_SUBBUILD AND NOT QT_SUPERBUILD)
@@ -39,4 +45,7 @@ if(MATRIX_BUILD AND NOT MATRIX_SUBBUILD AND NOT QT_SUPERBUILD)
     return()
 endif()
 
+# Don't auto create the SBOM projects, they will be manually created
+# for each sub-project.
+set(QT_SKIP_SBOM_AUTO_PROJECT TRUE)
 qt_build_repo()
diff --git a/LICENSES/Apache-2.0.txt b/LICENSES/Apache-2.0.txt
new file mode 100644
index 00000000000..136d9004566
--- /dev/null
+++ b/LICENSES/Apache-2.0.txt
@@ -0,0 +1,61 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+    1. Definitions.
+
+        "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+        "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+        "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+        "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+        "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+        "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+        "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+        "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+        "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+        "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+    2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+    3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+    4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+        (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
+        (b) You must cause any modified files to carry prominent notices stating that You changed the files; and
+        (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+        (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
+
+        You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+    5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+    6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+    7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+    8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+    9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/LICENSES/CC0-1.0.txt b/LICENSES/CC0-1.0.txt
new file mode 100644
index 00000000000..0e259d42c99
--- /dev/null
+++ b/LICENSES/CC0-1.0.txt
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+    HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+  i. the right to reproduce, adapt, distribute, perform, display,
+     communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+     likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+     subject to the limitations in paragraph 4(a), below;
+  v. rights protecting the extraction, dissemination, use and reuse of data
+     in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+     European Parliament and of the Council of 11 March 1996 on the legal
+     protection of databases, and under any national implementation
+     thereof, including any amended or successor version of such
+     directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+     world based on applicable law or treaty, and any national
+     implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+    surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+    warranties of any kind concerning the Work, express, implied,
+    statutory or otherwise, including without limitation warranties of
+    title, merchantability, fitness for a particular purpose, non
+    infringement, or the absence of latent or other defects, accuracy, or
+    the present or absence of errors, whether or not discoverable, all to
+    the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+    that may apply to the Work or any use thereof, including without
+    limitation any person's Copyright and Related Rights in the Work.
+    Further, Affirmer disclaims responsibility for obtaining any necessary
+    consents, permissions or other rights required for any use of the
+    Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+    party to this document and has no duty or obligation with respect to
+    this CC0 or use of the Work.
diff --git a/LICENSES/LGPL-2.0-or-later.txt b/LICENSES/LGPL-2.0-or-later.txt
new file mode 100644
index 00000000000..7d446668174
--- /dev/null
+++ b/LICENSES/LGPL-2.0-or-later.txt
@@ -0,0 +1,324 @@
+
+
+GNU LIBRARY GENERAL PUBLIC LICENSE
+
+Version 2, June 1991
+
+Copyright (C) 1991 Free Software Foundation, Inc.
+51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General
+Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free
+for all its users.
+
+This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and
+to any other libraries whose authors decide to use it. You can use it for your libraries, too.
+
+When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure
+that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive
+source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that
+you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender
+the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if
+you modify it.
+
+For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights
+that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them with the library, after making changes to the library and
+recompiling it. And you must show them these terms so they know their rights.
+
+Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you
+legal permission to copy, distribute and/or modify the library.
+
+Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this
+free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have
+is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing
+free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software.
+To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
+
+Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for
+utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license
+is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in
+the ordinary license.
+
+The reason we have a separate public license for some libraries is that they blur the distinction we usually make between
+modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in
+some sense simply using the library, and is analogous to running a utility program or application program. However, in a
+textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary
+General Public License treats it as such.
+
+Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote
+software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote
+sharing better.
+
+However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free
+status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs
+to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are
+incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as
+regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries.
+
+The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference
+between a "work based on the library" and a "work that uses the library". The former contains code derived from the library,
+while the latter only works together with the library.
+
+Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one.
+
+GNU LIBRARY GENERAL PUBLIC LICENSE
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+    0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other
+    authorized party saying it may be distributed under the terms of this Library General Public License (also called "this
+    License"). Each licensee
+    is addressed as "you".
+
+    A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application
+    programs (which use some of those functions and data) to form executables.
+
+    The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work
+    based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work
+    containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into
+    another language. (Hereinafter, translation is included without limitation in the term "modification".)
+
+    "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete
+    source code means all the source code for all modules it contains, plus any associated interface definition files, plus
+    the scripts used to control compilation and installation of the library.
+
+    Activities other than copying, distribution and modification are not covered by this License; they are outside its scope.
+    The act of running a program using the Library is not restricted, and output from such a program is covered only if its
+    contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether
+    that is true depends on what the Library does and what the program that uses the Library does.
+
+    1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium,
+    provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of
+    warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy
+    of this License along with the Library.
+
+    You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in
+    exchange for a fee.
+
+    2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and
+    copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of
+    these conditions:
+        a) The modified work must itself be a software library.
+        b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any
+        change.
+        c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this
+        License.
+        d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application
+        program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a
+        good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and
+        performs whatever part of its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent
+    of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function
+    must be optional: if the application does not supply it, the square root function must still compute square roots.)
+
+    These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the
+    Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms,
+    do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as
+    part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License,
+    whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who
+    wrote it.
+
+    Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you;
+    rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the
+    Library.
+
+    In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the
+    Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
+
+    3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of
+    the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary
+    GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU
+    General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change
+    in these notices.
+
+    Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License
+    applies to all subsequent copies and derivative works made from that copy.
+
+    This option is useful when you wish to copy part of the code of the Library into a program that is not a library.
+
+    4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable
+    form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding
+    machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily
+    used for software interchange.
+
+    If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access
+    to copy the source code from the same place satisfies the requirement to distribute the source code, even though third
+    parties are not compelled to copy the source along with the object code.
+
+    5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being
+    compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work
+    of the Library, and therefore falls outside the scope of this License.
+
+    However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library
+    (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore
+    covered by this License. Section 6 states terms for distribution of such executables.
+
+    When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the
+    work may be a derivative work of the Library even though the source code is not. Whether this is true is especially
+    significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to
+    be true is not precisely defined by law.
+
+    If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small
+    inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it
+    is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under
+    Section 6.)
+
+    Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of
+    Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with
+    the Library itself.
+
+    6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to
+    produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the
+    terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications.
+
+    You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use
+    are covered by this License. You must supply a copy of this License. If the work during execution displays copyright
+    notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to
+    the copy of this License. Also, you must do one of these things:
+        a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever
+        changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an
+        executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code
+        and/or source code, so that the user can modify the Library and then relink to produce a modified executable
+        containing the modified Library. (It is understood that the user who changes the contents of definitions files in
+        the Library will not necessarily be able to recompile the application to use the modified definitions.)
+        b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials
+        specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution.
+        c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access
+        to copy the above specified materials from the same place.
+        d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy.
+
+    For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed
+    for reproducing the executable from it. However, as a special exception, the source code distributed need not include
+    anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and
+    so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
+
+    It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not
+    normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together
+    in an executable that you distribute.
+
+    7. You may place library facilities that are a work based on the Library side-by-side in a single library together with
+    other library facilities not covered by this License, and distribute such a combined library, provided that the separate
+    distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided
+    that you do these two things:
+        a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library
+        facilities. This must be distributed under the terms of the Sections above.
+        b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and
+        explaining where to find the accompanying uncombined form of the same work.
+
+    8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this
+    License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will
+    automatically terminate your rights under this License. However, parties who have received copies, or rights, from you
+    under this License will not have their licenses terminated so long as such parties remain in full compliance.
+
+    9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission
+    to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this
+    License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your
+    acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library
+    or works based on it.
+
+    10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a
+    license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and
+    conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are
+    not responsible for enforcing compliance by third parties to this License.
+
+    11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to
+    patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the
+    conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as
+    to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence
+    you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution
+    of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy
+    both it and this License would be to refrain entirely from distribution of the Library.
+
+    If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the
+    section is intended to apply, and the section as a whole is intended to apply in other circumstances.
+
+    It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest
+    validity of any such claims; this section has the sole purpose of protecting the integrity of the free software
+    distribution system which is implemented by public license practices. Many people have made generous contributions to
+    the wide range of software distributed through that system in reliance on consistent application of that system; it
+    is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee
+    cannot impose that choice.
+
+    This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
+
+    12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted
+    interfaces, the original copyright holder who places the Library under this License may add an explicit geographical
+    distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus
+    excluded. In such case, this License incorporates the limitation as if written in the body of this License.
+    13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time
+    to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new
+    problems or concerns.
+
+    Each version is given a distinguishing version number. If the Library specifies a version number of this License which
+    applies to it and "any later version", you have the option of following the terms and conditions either of that version
+    or of any later version published by the Free Software Foundation. If the Library does not specify a license version
+    number, you may choose any version ever published by the Free Software Foundation.
+
+    14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible
+    with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation,
+    write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals
+    of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software
+    generally.
+
+    NO WARRANTY
+    15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY
+    APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY
+    "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY
+    IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+    16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY
+    WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL,
+    SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT
+    LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF
+    THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY
+    OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Libraries
+
+If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free
+software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or,
+alternatively, under the terms of the ordinary General Public License).
+
+To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source
+file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a
+pointer to where the full notice is found.
+
+one line to give the library's name and an idea of what it does.
+Copyright (C) year name of author
+
+This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free
+Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for
+the library, if necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in
+the library `Frob' (a library for tweaking knobs) written
+by James Random Hacker.
+
+signature of Ty Coon, 1 April 1990
+Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/LICENSES/LicenseRef-Tango-Icons-Public-Domain.txt b/LICENSES/LicenseRef-Tango-Icons-Public-Domain.txt
new file mode 100644
index 00000000000..220881da67d
--- /dev/null
+++ b/LICENSES/LicenseRef-Tango-Icons-Public-Domain.txt
@@ -0,0 +1 @@
+The icons in this repository are herefore released into the Public Domain.
diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt
new file mode 100644
index 00000000000..bc65af3cbd3
--- /dev/null
+++ b/LICENSES/MIT.txt
@@ -0,0 +1,11 @@
+
+
+MIT License
+
+Copyright (c) <year> <copyright holders>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/REUSE.toml b/REUSE.toml
new file mode 100644
index 00000000000..faaa2093da1
--- /dev/null
+++ b/REUSE.toml
@@ -0,0 +1,123 @@
+version = 1
+
+[[annotations]]
+path = ["src/core/doc/QtWebEngineDoc",
+        "src/core/doc/about_credits.tmpl",
+        "src/core/doc/about_credits_entry.tmpl",
+        "src/pdf/doc/about_credits.tmpl",
+        "src/pdf/doc/about_credits_entry.tmpl",
+        "src/pdf/doc/snippets/qtpdf-build.cmake",
+        "src/process/QtWebEngineProcess.entitlements",
+        "src/process/QtWebEngineProcess.exe.manifest",
+        "src/webenginequick/ui/information.png",
+        "src/webenginequick/ui/question.png",
+        "src/webenginewidgets/plugins/qwebengineview/images/qwebengineview.png",
+        "src/pdf/plugins/imageformats/pdf/pdf.json"]
+precedence = "closest"
+comment = "module and plugin"
+SPDX-FileCopyrightText = "Copyright (C) 2024 The Qt Company Ltd."
+SPDX-License-Identifier = "LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only"
+
+[[annotations]]
+path = ["tests/auto/widgets/qwebenginepage/resources/*",
+        "tests/auto/widgets/qwebenginescript/resources/*",
+        "tests/auto/widgets/qwebengineview/resources/*",
+        "tests/auto/widgets/qwebengineprofile/resources/*",
+        "tests/auto/widgets/qwebenginehistory/resources/*",
+        "tests/auto/core/certificateerror/resources/*",
+        "tests/auto/core/origins/resources/subdir/*",
+        "tests/auto/widgets/favicon/resources/**",
+        "tests/auto/quick/qquickwebengineview/html/**",
+        "tests/auto/quick/qmltests/resources/*",
+        "tests/auto/core/origins/resources/*",
+        "tests/auto/core/qwebengineclientcertificatestore/resources/*",
+        "tests/auto/core/qwebenginecookiestore/resources/*",
+        "tests/auto/core/qwebengineurlrequestinterceptor/resources/*",
+        "tests/auto/core/webenginedriver/resources/*",
+        "tests/auto/core/qwebengineframe/resources/*",
+        "tests/auto/core/qwebengineurlrequestinterceptor/resources/icons/favicon.png",
+        "tests/auto/quick/qmltests/data/icons/*",
+        "tests/auto/bic/data/*",
+        "tests/auto/httpserver/data/**",
+        "tests/auto/pdfquick/multipageview/data/*",
+        "tests/auto/pdfquick/pdfpageimage/data/*",
+        "tests/auto/quick/qmltests/data/*",
+        "tests/auto/pdf/**",
+        "tests/auto/core/qwebengineglobalsettings/cert/*",
+        "tests/auto/core/qwebengineurlrequestjob/*",
+        "tests/auto/widgets/spellchecking/dict/*",
+        "tests/auto/widgets/spellchecking/resources/index.html",
+        "tests/auto/quick/dialogs/index.html",
+        "tests/auto/quick/inspectorserver/html/basic_page.html",
+        "tests/auto/quick/qmltests/mock-delegates/TestParams/qmldir",
+        "tests/auto/quick/qquickwebenginedefaultsurfaceformat/html/basic_page.html",
+        "tests/auto/quick/qtbug-70248/test.qml",
+        "tests/auto/widgets/defaultsurfaceformat/resources/index.html",
+        "tests/auto/widgets/offscreen/test.html",
+        "tests/auto/widgets/printing/resources/basic_printing_page.html",
+        "tests/auto/widgets/proxypac/proxy.pac",
+        "tests/manual/html/pointer-events.html",
+        "tests/manual/quick/geopermission/geolocation.html",
+        "tests/manual/quick/pdf/test.pdf",
+        "tests/manual/touchmocking/touchpoint.png",
+        "tests/manual/widgets/geolocation/geolocation.html",
+        "tests/manual/widgets/inputmethods/testdata.csv",
+        "tests/manual/widgets/printing/printing_example.html",
+        "tests/manual/widgets/webrtc/index.html",
+        "tests/manual/widgets/webrtc/mediaPicker.ui"]
+precedence = "closest"
+comment = "test"
+SPDX-FileCopyrightText = "Copyright (C) 2024 The Qt Company Ltd."
+SPDX-License-Identifier = "LicenseRef-Qt-Commercial OR GPL-3.0-only"
+
+[[annotations]]
+path = ["**.pro", "**.qrc", "**CMakeLists.txt", ".cmake.conf", "**.yaml",
+        "**.cfg", "**BLACKLIST", "**.plist", "**.plist.in", "**BUILD**.in", "coin.nodes",
+        "coin/qt-installer-package-config.json", ".tag"]
+precedence = "closest"
+comment = "build system"
+SPDX-FileCopyrightText = "Copyright (C) 2024 The Qt Company Ltd."
+SPDX-License-Identifier = "BSD-3-Clause"
+
+[[annotations]]
+path = ["**/.gitattributes", "**.gitignore", ".gitmodules", "**.gitreview"]
+precedence = "closest"
+comment = "version control system. Infrastructure"
+SPDX-FileCopyrightText = "Copyright (C) 2024 The Qt Company Ltd."
+SPDX-License-Identifier = "LicenseRef-Qt-Commercial OR BSD-3-Clause"
+
+[[annotations]]
+path = ["examples/**", "tests/manual/examples/**", "**/doc/snippets/**"]
+comment = "this must be after the build system table because example and snippets take precedence over build system"
+precedence = "closest"
+SPDX-FileCopyrightText = "Copyright (C) 2024 The Qt Company Ltd."
+SPDX-License-Identifier = "LicenseRef-Qt-Commercial OR BSD-3-Clause"
+
+[[annotations]]
+path = ["**/doc/images/**", "**.qdocconf", "config_help.txt"]
+comment = "documentation"
+precedence = "closest"
+SPDX-FileCopyrightText = "Copyright (C) 2024 The Qt Company Ltd."
+SPDX-License-Identifier = "LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only"
+
+[[annotations]]
+path = ["**.toml", "licenseRule.json"]
+comment = "infrastructure"
+precedence = "override"
+SPDX-FileCopyrightText = "Copyright (C) 2024 The Qt Company Ltd."
+SPDX-License-Identifier = "LicenseRef-Qt-Commercial OR BSD-3-Clause"
+
+[[annotations]]
+path = ["**/qt_attribution.json"]
+comment = "documentation"
+precedence = "override"
+SPDX-FileCopyrightText = "Copyright (C) 2024 The Qt Company Ltd."
+SPDX-License-Identifier = "LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only"
+
+[[annotations]]
+path = ["**LICENSE*", "CHROMIUM_VERSION"]
+precedence = "override"
+comment = "License file."
+SPDX-FileCopyrightText = "None"
+SPDX-License-Identifier = "CC0-1.0"
+
diff --git a/cmake/FindGn.cmake b/cmake/FindGn.cmake
index fd03b7346d0..f30edcd3649 100644
--- a/cmake/FindGn.cmake
+++ b/cmake/FindGn.cmake
@@ -6,11 +6,11 @@ if(NOT DEFINED WEBENGINE_ROOT_BUILD_DIR)
 endif()
 find_program(Gn_EXECUTABLE NAMES gn PATHS "${WEBENGINE_ROOT_BUILD_DIR}/install/bin" NO_DEFAULT_PATH)
 if(NOT QT_HOST_PATH STREQUAL "")
-   find_program(Gn_EXECUTABLE NAMES gn PATHS ${QT_HOST_PATH}/${INSTALL_LIBEXECDIR} NO_DEFAULT_PATH)
+   find_program(Gn_EXECUTABLE NAMES gn PATHS ${QT_HOST_PATH}/${QT6_HOST_INFO_LIBEXECDIR} NO_DEFAULT_PATH)
    # note: mingw installs with INSTALL_LIBEXECDIR = bin,
    # however android on windows has INSTALL_LIBEXECDIR = libexec,
    # so cover this case also
-   find_program(Gn_EXECUTABLE NAMES gn PATHS ${QT_HOST_PATH}/${INSTALL_BINDIR} NO_DEFAULT_PATH)
+   find_program(Gn_EXECUTABLE NAMES gn PATHS ${QT_HOST_PATH}/${QT6_HOST_INFO_BINDIR} NO_DEFAULT_PATH)
 endif()
 find_program(Gn_EXECUTABLE NAMES gn)
 
diff --git a/cmake/Functions.cmake b/cmake/Functions.cmake
index 6cc8a401ea3..b0a9fd26168 100644
--- a/cmake/Functions.cmake
+++ b/cmake/Functions.cmake
@@ -618,16 +618,17 @@ endfunction()
 # Function maps TEST_architecture_arch or CMAKE_SYSTEM_PROCESSOR into gn architecture
 function(get_gn_arch result arch)
     set(armList arm armv7-a)
+    set(arm64List arm64 ARM64 aarch64)
     set(mips64List mips64 mipsel64)
     set(x86List i386 i686)
-    set(x64List x86_64 AMD64 x86_64h aarch64)
+    set(x64List x86_64 AMD64 x86_64h)
     if(arch IN_LIST x86List)
         set(${result} "x86" PARENT_SCOPE)
     elseif(arch IN_LIST x64List)
         set(${result} "x64" PARENT_SCOPE)
     elseif(arch IN_LIST armList)
         set(${result} "arm" PARENT_SCOPE)
-    elseif(arch STREQUAL "arm64")
+    elseif(arch IN_LIST arm64List)
         set(${result} "arm64" PARENT_SCOPE)
     elseif(arch STREQUAL "mipsel")
         set(${result} "mipsel" PARENT_SCOPE)
@@ -829,21 +830,34 @@ macro(append_build_type_setup)
         enable_rust=false # We do not yet support rust
     )
     if(${config} STREQUAL "Debug")
-        list(APPEND gnArgArg is_debug=true symbol_level=2)
+        list(APPEND gnArgArg
+            is_debug=true
+            symbol_level=2
+            enable_mojom_message_id_scrambling=false
+        )
         if(WIN32)
             list(APPEND gnArgArg enable_iterator_debugging=true)
         endif()
     elseif(${config} STREQUAL "Release")
-        list(APPEND gnArgArg is_debug=false symbol_level=0)
+        list(APPEND gnArgArg
+            is_debug=false
+            symbol_level=0
+            enable_mojom_message_id_scrambling=true
+        )
     elseif(${config} STREQUAL "RelWithDebInfo")
-        list(APPEND gnArgArg is_debug=false)
+        list(APPEND gnArgArg is_debug=false enable_mojom_message_id_scrambling=true)
         if(WIN32 AND NOT CLANG)
             list(APPEND gnArgArg symbol_level=2)
         else()
             list(APPEND gnArgArg symbol_level=1)
         endif()
     elseif(${config} STREQUAL "MinSizeRel")
-        list(APPEND gnArgArg is_debug=false symbol_level=0 optimize_for_size=true)
+        list(APPEND gnArgArg
+            is_debug=false
+            symbol_level=0
+            optimize_for_size=true
+            enable_mojom_message_id_scrambling=true
+        )
     endif()
     if(FEATURE_developer_build OR (${config} STREQUAL "Debug") OR QT_FEATURE_webengine_sanitizer)
         list(APPEND gnArgArg
@@ -872,7 +886,7 @@ macro(append_build_type_setup)
     if(QT_FEATURE_webengine_jumbo_build)
         list(APPEND gnArgArg jumbo_file_merge_limit=${QT_FEATURE_webengine_jumbo_file_merge_limit})
         if(QT_FEATURE_webengine_jumbo_file_merge_limit LESS_EQUAL 8)
-            list(APPEND gnArgArg jumbo_build_excluded=[\"browser\"])
+            list(APPEND gnArgArg "jumbo_build_excluded=[\"browser\"]")
         endif()
     endif()
 
@@ -886,6 +900,25 @@ macro(append_build_type_setup)
     )
 endmacro()
 
+function(get_clang_version_from_runtime_path result)
+if(CLANG AND CMAKE_CXX_COMPILER)
+    if( NOT DEFINED CLANG_RUNTIME_PATH)
+       execute_process(
+           COMMAND ${CMAKE_CXX_COMPILER} -print-runtime-dir
+           OUTPUT_VARIABLE clang_output
+           ERROR_QUIET
+           OUTPUT_STRIP_TRAILING_WHITESPACE
+        )
+        cmake_path(CONVERT "${clang_output}" TO_CMAKE_PATH_LIST clang_output NORMALIZE)
+        set(CLANG_RUNTIME_PATH "${clang_output}" CACHE INTERNAL "internal")
+        mark_as_advanced(CLANG_RUNTIME_PATH)
+     endif()
+     string(REGEX MATCH "\\/([0-9.]+)\\/" clang_run_time_path_version "${CLANG_RUNTIME_PATH}")
+     string(REPLACE "/" "" clang_run_time_path_version ${clang_run_time_path_version})
+     set(${result} ${clang_run_time_path_version} PARENT_SCOPE)
+endif()
+endfunction()
+
 macro(append_compiler_linker_sdk_setup)
     if(CMAKE_CXX_COMPILER_LAUNCHER)
         list(APPEND gnArgArg cc_wrapper="${CMAKE_CXX_COMPILER_LAUNCHER}")
@@ -906,12 +939,16 @@ macro(append_compiler_linker_sdk_setup)
             get_filename_component(clangBasePath ${CMAKE_CXX_COMPILER} DIRECTORY)
             get_filename_component(clangBasePath ${clangBasePath} DIRECTORY)
         endif()
-
-        string(REGEX MATCH "[0-9]+" clangVersion ${CMAKE_CXX_COMPILER_VERSION})
+            get_clang_version_from_runtime_path(clang_version)
+        if (NOT DEFINED clang_version)
+            message(FATAL_ERROR "Clang version for runtime is missing."
+                    "Please open bug report.Found clang runtime path: ${CLANG_RUNTIME_PATH}"
+            )
+        endif()
         list(APPEND gnArgArg
             clang_base_path="${clangBasePath}"
+            clang_version="${clang_version}"
             clang_use_chrome_plugins=false
-            clang_version=${clangVersion}
             fatal_linker_warnings=false
         )
 
@@ -922,6 +959,12 @@ macro(append_compiler_linker_sdk_setup)
                 mac_sdk_min="${macSdkVersion}"
                 use_libcxx=true
             )
+            _qt_internal_get_apple_sdk_version(apple_sdk_version)
+            if (apple_sdk_version LESS 13.2)
+                list(APPEND gnArgArg
+                    use_sck=false
+                )
+            endif()
         endif()
         if(IOS)
             list(APPEND gnArgArg
@@ -1115,7 +1158,7 @@ endmacro()
 
 function(add_ninja_command)
     cmake_parse_arguments(PARSE_ARGV 0 arg
-        "" "TARGET;BUILDDIR;MODULE" "OUTPUT;BYPRODUCTS"
+        "" "TARGET;BUILDDIR;MODULE" "OUTPUT;BYPRODUCTS;DEPENDS"
     )
     _qt_internal_validate_all_args_are_parsed(arg)
 
@@ -1135,7 +1178,7 @@ function(add_ninja_command)
         USES_TERMINAL
         VERBATIM
         COMMAND_EXPAND_LISTS
-        DEPENDS run_${arg_MODULE}_NinjaReady
+        DEPENDS run_${arg_MODULE}_NinjaReady ${arg_DEPENDS}
     )
 endfunction()
 
@@ -1165,7 +1208,7 @@ endfunction()
 
 function(add_gn_build_artifacts_to_target)
     cmake_parse_arguments(PARSE_ARGV 0 arg
-        "" "CMAKE_TARGET;NINJA_TARGET;BUILDDIR;MODULE;COMPLETE_STATIC;NINJA_STAMP;NINJA_DATA_STAMP" ""
+        "" "CMAKE_TARGET;NINJA_TARGET;BUILDDIR;MODULE;COMPLETE_STATIC;NINJA_STAMP;NINJA_DATA_STAMP;DEPENDS" ""
     )
     _qt_internal_validate_all_args_are_parsed(arg)
 
@@ -1178,12 +1221,16 @@ function(add_gn_build_artifacts_to_target)
         foreach(arch ${archs})
             set(target ${arg_NINJA_TARGET}_${config}_${arch})
             set(stamps ${arg_NINJA_STAMP} ${arg_NINJA_DATA_STAMP})
+            set(data_stamp_option "")
+            if(arg_NINJA_DATA_STAMP)
+                set(data_stamp_option NINJA_DATA_STAMP ${arg_NINJA_DATA_STAMP})
+            endif()
             add_ninja_target(
                 TARGET ${target}
                 NINJA_TARGET ${arg_NINJA_TARGET}
                 CMAKE_TARGET ${arg_CMAKE_TARGET}
                 NINJA_STAMP ${arg_NINJA_STAMP}
-                NINJA_DATA_STAMP ${arg_NINJA_DATA_STAMP}
+                ${data_stamp_option}
                 CONFIG ${config}
                 ARCH ${arch}
                 BUILDDIR ${arg_BUILDDIR}
@@ -1193,6 +1240,7 @@ function(add_gn_build_artifacts_to_target)
                 OUTPUT ${stamps}
                 BUILDDIR ${arg_BUILDDIR}/${config}/${arch}
                 MODULE ${arg_MODULE}
+                DEPENDS ${arg_DEPENDS}
             )
             add_dependencies(run_${arg_MODULE}_NinjaDone ${target})
             set_target_properties(${arg_CMAKE_TARGET} PROPERTIES
@@ -1266,8 +1314,8 @@ function(add_gn_command)
              -DSOURCE_DIR=${CMAKE_CURRENT_LIST_DIR}
              -DMODULE=${arg_MODULE}
              -DQT_HOST_PATH=${QT_HOST_PATH}
-             -DINSTALL_LIBEXECDIR=${INSTALL_LIBEXECDIR}
-             -DINSTALL_BINDIR=${INSTALL_BINDIR}
+             -DQT6_HOST_INFO_LIBEXECDIR=${QT6_HOST_INFO_LIBEXECDIR}
+             -DQT6_HOST_INFO_BINDIR=${QT6_HOST_INFO_BINDIR}
              -DPython3_EXECUTABLE=${Python3_EXECUTABLE}
              -DGN_THREADS=$ENV{QTWEBENGINE_GN_THREADS}
              -DQT_ALLOW_SYMLINK_IN_PATHS=${QT_ALLOW_SYMLINK_IN_PATHS}
@@ -1317,7 +1365,6 @@ function(addCopyCommand target src dst)
         COMMAND ${CMAKE_COMMAND} -E make_directory ${dst}
         COMMAND ${CMAKE_COMMAND} -E copy ${src} ${dst}
         TARGET ${target}
-        DEPENDS ${src}
         USES_TERMINAL
     )
 endfunction()
@@ -1405,3 +1452,40 @@ function(add_code_attributions_target)
      )
      add_custom_target(${arg_TARGET} DEPENDS ${arg_OUTPUT})
 endfunction()
+
+macro(qt_webengine_build_and_install_gn)
+    set(suppress_warning "${BUILD_ONLY_GN} ${QT_INTERNAL_CALLED_FROM_CONFIGURE}")
+    qt_internal_project_setup()
+    qt_webengine_externalproject_add(gn
+        SOURCE_DIR  ${CMAKE_CURRENT_LIST_DIR}/src/gn
+        BINARY_DIR  ${CMAKE_CURRENT_BINARY_DIR}/src/gn
+        INSTALL_DIR ${PROJECT_BINARY_DIR}/install
+    )
+    qt_internal_set_cmake_build_type()
+    get_install_config(install_config)
+    qt_install(
+        PROGRAMS "${PROJECT_BINARY_DIR}/install/bin/gn${CMAKE_EXECUTABLE_SUFFIX}"
+        CONFIGURATIONS ${install_config}
+        RUNTIME DESTINATION "${INSTALL_LIBEXECDIR}"
+    )
+    unset(suppress_warning)
+    unset(install_config)
+endmacro()
+
+macro(qt_webengine_externalproject_add)
+    list(JOIN CMAKE_OSX_ARCHITECTURES "," OSX_ARCH_STR)
+    externalproject_add(${ARGN}
+        PREFIX      gn
+        USES_TERMINAL_BUILD TRUE
+        LIST_SEPARATOR ","
+        CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release
+                   -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
+                   -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
+                   -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
+                   -DCMAKE_PREFIX_PATH:PATH=<INSTALL_DIR>
+                   -DCMAKE_OSX_ARCHITECTURES=${OSX_ARCH_STR}
+                   -DWEBENGINE_ROOT_BUILD_DIR=${PROJECT_BINARY_DIR}
+                   -DQT_ALLOW_SYMLINK_IN_PATHS=${QT_ALLOW_SYMLINK_IN_PATHS}
+    )
+    unset(OSX_ARCH_STR)
+endmacro()
diff --git a/cmake/Gn.cmake b/cmake/Gn.cmake
index 15b349e0898..2617f92dc1a 100644
--- a/cmake/Gn.cmake
+++ b/cmake/Gn.cmake
@@ -24,7 +24,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
 
 find_package(Gn ${QT_REPO_MODULE_VERSION} EXACT)
 if(NOT Python3_EXECUTABLE)
-    find_package(Python3 3.6 REQUIRED)
+    find_package(Python3 3.8 REQUIRED)
 endif()
 set(gnCmd ${Gn_EXECUTABLE})
 set(buildDir ${BUILD_DIR})
diff --git a/cmake/License.cmake b/cmake/License.cmake
index 2427ad67976..4884c8229f6 100644
--- a/cmake/License.cmake
+++ b/cmake/License.cmake
@@ -16,7 +16,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
 
 find_package(Gn ${QT_REPO_MODULE_VERSION} EXACT)
 if(NOT Python3_EXECUTABLE)
-    find_package(Python3 3.6 REQUIRED)
+    find_package(Python3 3.8 REQUIRED)
 endif()
 
 set(extraThirdPartyDirs "")
diff --git a/coin/module_config.yaml b/coin/module_config.yaml
index f1d69a2778f..968c504b0b6 100644
--- a/coin/module_config.yaml
+++ b/coin/module_config.yaml
@@ -1,4 +1,5 @@
 version: 2
+tags: [git]
 accept_configuration:
   condition: property
   property: features
@@ -12,6 +13,16 @@ machine_type:
 
 instructions:
   Build:
+    - type: AppendToEnvironmentVariable
+      variableName: COMMON_NON_QTBASE_CMAKE_ARGS
+      variableValue: " -DFEATURE_webengine_system_libxml=OFF"
+      enable_if:
+        condition: runtime
+        env_var: COIN_PLATFORM_ID
+        contains_value: "rhel-8.10"
+    - type: EnvironmentVariable
+      variableName: VERIFY_SOURCE_SBOM
+      variableValue: "ON"
     - type: EnvironmentVariable
       variableName: CMAKE_BUILD_TIMEOUT
       variableValue: "36000"
diff --git a/coin/qt-installer-package-config.json b/coin/qt-installer-package-config.json
index aec45688b39..433ddfd824a 100644
--- a/coin/qt-installer-package-config.json
+++ b/coin/qt-installer-package-config.json
@@ -3,6 +3,7 @@
     "module-split": {
         "qtpdf": [
             "**/bin/*Pdf*",
+            "**/sbom/qtpdf*",
             "**/include/*QtPdf*/**/*",
             "**/lib/cmake/Qt*Gui/*Pdf*",
             "**/lib/cmake/Qt*Pdf*/*",
diff --git a/configure.cmake b/configure.cmake
index 2d826e6ddc8..c0276751f10 100644
--- a/configure.cmake
+++ b/configure.cmake
@@ -13,7 +13,7 @@ else()
     find_package(Gn ${QT_REPO_MODULE_VERSION} EXACT)
     find_program(Python3_EXECUTABLE NAMES python3 python HINTS $ENV{PYTHON3_PATH})
     if(NOT Python3_EXECUTABLE)
-        find_package(Python3 3.6)
+        find_package(Python3 3.8)
     endif()
     find_package(GPerf)
     find_package(BISON)
@@ -52,11 +52,16 @@ if(PkgConfig_FOUND)
     pkg_check_modules(LCMS2 lcms2)
     pkg_check_modules(FREETYPE freetype2 IMPORTED_TARGET)
     pkg_check_modules(LIBXML2 libxml-2.0 libxslt IMPORTED_TARGET)
-    pkg_check_modules(FFMPEG libavcodec libavformat libavutil IMPORTED_TARGET)
+    pkg_check_modules(FFMPEG libavcodec>=60.31.102
+                             libavformat>=60.16.100
+                             libavutil>=58.29.100
+                             IMPORTED_TARGET)
     pkg_check_modules(OPUS opus>=1.3.1)
     pkg_check_modules(VPX vpx>=1.10.0 IMPORTED_TARGET)
     pkg_check_modules(LIBPCI libpci)
     pkg_check_modules(LIBOPENJP2 libopenjp2)
+    pkg_check_modules(XKBCOMMON xkbcommon)
+    pkg_check_modules(XKBFILE xkbfile)
 endif()
 
 if(Python3_EXECUTABLE)
@@ -124,21 +129,6 @@ int main() {
 }"
 )
 
-qt_config_compile_test(libxml2
-    LABEL "compatible libxml2 and libxslt"
-    LIBRARIES
-        PkgConfig::LIBXML2
-    CODE
-"
-#include \"libxml/xmlversion.h\"
-#if !defined(LIBXML_ICU_ENABLED)
-#error libxml icu not enabled
-#endif
-int main() {
-    return 0;
-}"
-)
-
 qt_config_compile_test(jpeg
     LABEL "compatible libjpeg"
     LIBRARIES
@@ -310,12 +300,13 @@ qt_feature("webengine-developer-build" PRIVATE
 )
 qt_feature("webengine-system-re2" PRIVATE
     LABEL "re2"
+    AUTODETECT FALSE
     CONDITION UNIX AND TEST_re2
 )
 qt_feature("webengine-system-icu" PRIVATE
     LABEL "icu"
     AUTODETECT FALSE
-    CONDITION ICU_FOUND
+    CONDITION UNIX AND NOT APPLE AND ICU_FOUND
 )
 qt_feature("webengine-system-libwebp" PRIVATE
     LABEL "libwebp, libwebpmux and libwebpdemux"
@@ -367,7 +358,7 @@ qt_feature("webengine-system-libevent" PRIVATE
 )
 qt_feature("webengine-system-libxml" PRIVATE
     LABEL "libxml2 and libxslt"
-    CONDITION UNIX AND TEST_libxml2
+    CONDITION UNIX AND LIBXML2_FOUND
 )
 qt_feature("webengine-system-lcms2" PRIVATE
     LABEL "lcms2"
@@ -427,7 +418,7 @@ qt_feature("webengine-system-libpci" PRIVATE
 )
 
 qt_feature("webengine-ozone-x11" PRIVATE
-    LABEL "Support GLX on qpa-xcb"
+    LABEL "Support X11 on qpa-xcb"
     CONDITION LINUX
         AND TARGET Qt::Gui
         AND QT_FEATURE_xcb
@@ -440,6 +431,8 @@ qt_feature("webengine-ozone-x11" PRIVATE
         AND XRANDR_FOUND
         AND XTST_FOUND
         AND XSHMFENCE_FOUND
+        AND XKBCOMMON_FOUND
+        AND XKBFILE_FOUND
 )
 
 #### Support Checks
@@ -503,7 +496,7 @@ add_check_for_support(
 add_check_for_support(
    MODULES QtWebEngine QtPdf
    CONDITION Python3_EXECUTABLE
-   MESSAGE "Python version 3.6 or later is required."
+   MESSAGE "Python version 3.8 or later is required."
 )
 add_check_for_support(
    MODULES QtWebEngine QtPdf
@@ -583,6 +576,17 @@ add_check_for_support(
    MESSAGE
        "${CMAKE_CXX_COMPILER_ID} compiler is not supported."
 )
+
+if (LINUX OR MINGW)
+    add_check_for_support(
+        MODULES QtWebEngine QtPdf
+        CONDITION NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR
+                  NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0
+        MESSAGE
+            "GCC build requires version 10.0 or later. Version ${CMAKE_CXX_COMPILER_VERSION} is not supported."
+    )
+endif()
+
 if(WIN32)
     if(MSVC)
         if(MSVC_TOOLSET_VERSION EQUAL 142) # VS 2019 (16.0)
@@ -687,7 +691,7 @@ qt_configure_add_report_entry(
 )
 
 if(LINUX AND QT_FEATURE_xcb AND TARGET Qt::Gui)
-    set(ozone_x11_support X11 LIBDRM XCOMPOSITE XCURSOR XRANDR XI XPROTO XSHMFENCE XTST)
+    set(ozone_x11_support X11 LIBDRM XCOMPOSITE XCURSOR XRANDR XI XPROTO XSHMFENCE XTST XKBCOMMON XKBFILE)
     set(ozone_x11_error OFF)
     foreach(xs ${ozone_x11_support})
         if(NOT ${xs}_FOUND)
diff --git a/dependencies.yaml b/dependencies.yaml
index f6db2abacf1..31d95285f1f 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -1,13 +1,13 @@
 dependencies:
   ../qtdeclarative:
-    ref: 4ad3d0c6096e6caefec74681eed86c2fa92149fd
+    ref: f9e719687fe5160894203a5030bbb2e33ab0cf6f
     required: true
   ../qtpositioning:
-    ref: 413ede61f32da23c446654a98f285ce06ef035c8
+    ref: 8277b8175e31ae6c81a79d8677a42c5e84db6261
     required: false
   ../qttools:
-    ref: 49438275bdf6b08ba46e2ea2d89753f1f9c63d76
+    ref: 77db9c950477d9aa89d583b025f01c9e13e3ae44
     required: false
   ../qtwebchannel:
-    ref: 9f1ca1c6bdde6adedeb215e487ebdb05a17ad2d0
+    ref: 36e6623be61e4d12376dcc1ed4c9ae059fa64bc3
     required: false
diff --git a/dist/REUSE.toml b/dist/REUSE.toml
new file mode 100644
index 00000000000..f14104bf6f4
--- /dev/null
+++ b/dist/REUSE.toml
@@ -0,0 +1,8 @@
+version = 1
+
+[[annotations]]
+path = ["*"]
+precedence = "override"
+comment = "Licensed as documentation."
+SPDX-FileCopyrightText = "Copyright (C) 2024 The Qt Company Ltd."
+SPDX-License-Identifier = "LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only"
diff --git a/examples/pdf/CMakeLists.txt b/examples/pdf/CMakeLists.txt
index bdc1d0f0524..7255752b814 100644
--- a/examples/pdf/CMakeLists.txt
+++ b/examples/pdf/CMakeLists.txt
@@ -3,6 +3,3 @@
 
 qt_internal_add_example(singlepage)
 qt_internal_add_example(multipage)
-if(NOT TARGET Qt::Svg)
-    message(WARNING "QtSvg is required as runtime dependency for qtpdfquick examples.")
-endif()
diff --git a/examples/pdf/multipage/CMakeLists.txt b/examples/pdf/multipage/CMakeLists.txt
index 8f8111c826d..fb4e449fd7a 100644
--- a/examples/pdf/multipage/CMakeLists.txt
+++ b/examples/pdf/multipage/CMakeLists.txt
@@ -12,7 +12,10 @@ endif()
 
 set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/pdf/multipage")
 
-find_package(Qt6 REQUIRED COMPONENTS Gui Qml)
+find_package(Qt6 REQUIRED COMPONENTS Gui Qml OPTIONAL_COMPONENTS Svg)
+if(NOT TARGET Qt::Svg)
+    message(WARNING "QtSvg is required as runtime dependency for qtpdfquick examples.")
+endif()
 
 qt_add_executable(multipage
     main.cpp
diff --git a/examples/pdf/multipage/viewer.qrc b/examples/pdf/multipage/viewer.qrc
index e786ae4ce6c..b8ec1c8c68e 100644
--- a/examples/pdf/multipage/viewer.qrc
+++ b/examples/pdf/multipage/viewer.qrc
@@ -1,5 +1,5 @@
 <RCC>
-    <qresource prefix="/singlepage">
+    <qresource prefix="/multipage">
         <file>viewer.qml</file>
         <file>resources/document-open.svg</file>
         <file>resources/edit-clear.svg</file>
diff --git a/examples/pdf/singlepage/CMakeLists.txt b/examples/pdf/singlepage/CMakeLists.txt
index fac95f54a98..647a30f8d4d 100644
--- a/examples/pdf/singlepage/CMakeLists.txt
+++ b/examples/pdf/singlepage/CMakeLists.txt
@@ -12,7 +12,10 @@ endif()
 
 set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/pdf/singlepage")
 
-find_package(Qt6 REQUIRED COMPONENTS Gui Qml)
+find_package(Qt6 REQUIRED COMPONENTS Gui Qml OPTIONAL_COMPONENTS Svg)
+if(NOT TARGET Qt::Svg)
+    message(WARNING "QtSvg is required as runtime dependency for qtpdfquick examples.")
+endif()
 
 qt_add_executable(pdfviewerquick
     main.cpp
diff --git a/examples/pdfwidgets/pdfviewer/doc/src/pdfviewer.qdoc b/examples/pdfwidgets/pdfviewer/doc/src/pdfviewer.qdoc
index b56958a9f28..6fb0fe65f19 100644
--- a/examples/pdfwidgets/pdfviewer/doc/src/pdfviewer.qdoc
+++ b/examples/pdfwidgets/pdfviewer/doc/src/pdfviewer.qdoc
@@ -15,11 +15,12 @@
     \e {PDF Viewer} demonstrates how to use the QPdfView class to render
     PDF documents and the QPdfPageNavigator class to navigate them.
 
-    Qt Creator and the integrated Qt Designer were used to create the example
-    UI and to connect it to the code. This affects the code, which might be
-    somewhat different to what you would typically write by hand.
-    For more information about using Qt Designer, see \l{Qt Designer Manual}
-    and \l{Qt Creator: Creating a Qt Widget Based Application}.
+    Qt Creator and the integrated Qt Widgets Designer were used to create the
+    example UI and to connect it to the code. This affects the code, which
+    might be somewhat different to what you would typically write by hand.
+    For more information about using Qt Widgets Designer, see
+    \l{Qt Widgets Designer Manual} and
+    \l{Qt Creator: Creating a Qt Widget Based Application}.
 
     \include examples-run.qdocinc
 
diff --git a/examples/pdfwidgets/pdfviewer/mainwindow.cpp b/examples/pdfwidgets/pdfviewer/mainwindow.cpp
index ce19359fc58..25c2cbaab99 100644
--- a/examples/pdfwidgets/pdfviewer/mainwindow.cpp
+++ b/examples/pdfwidgets/pdfviewer/mainwindow.cpp
@@ -19,6 +19,8 @@
 #include <QStandardPaths>
 #include <QtMath>
 
+using namespace Qt::StringLiterals;
+
 const qreal zoomMultiplier = qSqrt(2.0);
 
 Q_LOGGING_CATEGORY(lcExample, "qt.examples.pdfviewer")
@@ -115,8 +117,9 @@ void MainWindow::pageSelected(int page)
     const auto documentTitle = m_document->metaData(QPdfDocument::MetaDataField::Title).toString();
     setWindowTitle(!documentTitle.isEmpty() ? documentTitle : QStringLiteral("PDF Viewer"));
     setWindowTitle(tr("%1: page %2 (%3 of %4)")
-                   .arg(documentTitle.isEmpty() ? u"PDF Viewer"_qs : documentTitle,
-                        m_pageSelector->currentPageLabel(), QString::number(page + 1), QString::number(m_document->pageCount())));
+                           .arg(documentTitle.isEmpty() ? u"PDF Viewer"_s : documentTitle,
+                                m_pageSelector->currentPageLabel(), QString::number(page + 1),
+                                QString::number(m_document->pageCount())));
 }
 
 void MainWindow::searchResultSelected(const QModelIndex &current, const QModelIndex &previous)
diff --git a/examples/webenginequick/quicknanobrowser/ApplicationRoot.qml b/examples/webenginequick/quicknanobrowser/ApplicationRoot.qml
index 55c41440976..bd93cd9aa06 100644
--- a/examples/webenginequick/quicknanobrowser/ApplicationRoot.qml
+++ b/examples/webenginequick/quicknanobrowser/ApplicationRoot.qml
@@ -8,8 +8,14 @@ QtObject {
     id: root
 
     property QtObject defaultProfile: WebEngineProfile {
-        storageName: "Profile"
         offTheRecord: false
+        storageName: "Profile"
+        Component.onCompleted: {
+            let fullVersionList = defaultProfile.clientHints.fullVersionList;
+            fullVersionList["QuickNanoBrowser"] = "1.0";
+            defaultProfile.clientHints.fullVersionList = fullVersionList;
+        }
+
     }
 
     property QtObject otrProfile: WebEngineProfile {
diff --git a/examples/webenginequick/quicknanobrowser/BrowserWindow.qml b/examples/webenginequick/quicknanobrowser/BrowserWindow.qml
index e2cb71fb3ca..1b2816583c4 100644
--- a/examples/webenginequick/quicknanobrowser/BrowserWindow.qml
+++ b/examples/webenginequick/quicknanobrowser/BrowserWindow.qml
@@ -5,6 +5,7 @@ import QtCore
 import QtQml
 import QtQuick
 import QtQuick.Controls.Fusion
+import QtQuick.Dialogs
 import QtQuick.Layouts
 import QtQuick.Window
 import QtWebEngine
@@ -44,7 +45,7 @@ ApplicationWindow {
         property alias webRTCPublicInterfacesOnly : webRTCPublicInterfacesOnly.checked
         property alias devToolsEnabled: devToolsEnabled.checked
         property alias pdfViewerEnabled: pdfViewerEnabled.checked
-        property int imageAnimationPolicy: WebEngineSettings.AllowImageAnimation
+        property int imageAnimationPolicy: WebEngineSettings.ImageAnimationPolicy.Allow
     }
 
     Action {
@@ -377,9 +378,9 @@ ApplicationWindow {
                             text: "Disable All Image Animation"
                             checkable: true
                             autoExclusive: true
-                            checked: WebEngine.settings.imageAnimationPolicy === WebEngineSettings.DisallowImageAnimation
+                            checked: WebEngine.settings.imageAnimationPolicy === WebEngineSettings.ImageAnimationPolicy.Disallow
                             onTriggered: {
-                                appSettings.imageAnimationPolicy = WebEngineSettings.DisallowImageAnimation
+                                appSettings.imageAnimationPolicy = WebEngineSettings.ImageAnimationPolicy.Disallow
                             }
                         }
 
@@ -388,9 +389,9 @@ ApplicationWindow {
                             text: "Allow All Animated Images"
                             checkable: true
                             autoExclusive: true
-                            checked: WebEngine.settings.imageAnimationPolicy === WebEngineSettings.AllowImageAnimation
+                            checked: WebEngine.settings.imageAnimationPolicy === WebEngineSettings.ImageAnimationPolicy.Allow
                             onTriggered : {
-                                appSettings.imageAnimationPolicy = WebEngineSettings.AllowImageAnimation
+                                appSettings.imageAnimationPolicy = WebEngineSettings.ImageAnimationPolicy.Allow
                             }
                         }
 
@@ -399,9 +400,9 @@ ApplicationWindow {
                             text: "Animate Image Once"
                             checkable: true
                             autoExclusive: true
-                            checked: WebEngine.settings.imageAnimationPolicy === WebEngineSettings.AnimateImageOnce
+                            checked: WebEngine.settings.imageAnimationPolicy === WebEngineSettings.ImageAnimationPolicy.AnimateOnce
                             onTriggered : {
-                                appSettings.imageAnimationPolicy = WebEngineSettings.AnimateImageOnce
+                                appSettings.imageAnimationPolicy = WebEngineSettings.ImageAnimationPolicy.AnimateOnce
                             }
                         }
                     }
@@ -561,6 +562,7 @@ ApplicationWindow {
                 settings.webRTCPublicInterfacesOnly: appSettings.webRTCPublicInterfacesOnly
                 settings.pdfViewerEnabled: appSettings.pdfViewerEnabled
                 settings.imageAnimationPolicy: appSettings.imageAnimationPolicy
+                settings.screenCaptureEnabled: true
 
                 onCertificateError: function(error) {
                     if (!error.isMainFrame) {
@@ -610,6 +612,11 @@ ApplicationWindow {
                     request.accept();
                 }
 
+                onDesktopMediaRequested: function(request) {
+                    // select the primary screen
+                    request.selectScreen(request.screensModel.index(0, 0));
+                }
+
                 onRenderProcessTerminated: function(terminationStatus, exitCode) {
                     var status = "";
                     switch (terminationStatus) {
@@ -648,10 +655,9 @@ ApplicationWindow {
                         findBar.reset();
                 }
 
-                onFeaturePermissionRequested: function(securityOrigin, feature) {
-                    featurePermissionDialog.securityOrigin = securityOrigin;
-                    featurePermissionDialog.feature = feature;
-                    featurePermissionDialog.visible = true;
+                onPermissionRequested: function(permission) {
+                    permissionDialog.permission = permission;
+                    permissionDialog.visible = true;
                 }
                 onWebAuthUxRequested: function(request) {
                     webAuthDialog.init(request);
@@ -736,7 +742,7 @@ ApplicationWindow {
         }
     }
     Dialog {
-        id: featurePermissionDialog
+        id: permissionDialog
         anchors.centerIn: parent
         width: Math.min(browserWindow.width, browserWindow.height) / 3 * 2
         contentWidth: mainTextForPermissionDialog.width
@@ -744,59 +750,59 @@ ApplicationWindow {
         standardButtons: Dialog.No | Dialog.Yes
         title: "Permission Request"
 
-        property var feature;
-        property url securityOrigin;
+        property var permission;
 
         contentItem: Item {
             Label {
                 id: mainTextForPermissionDialog
-                text: featurePermissionDialog.questionForFeature()
             }
         }
 
-        onAccepted: currentWebView && currentWebView.grantFeaturePermission(securityOrigin, feature, true)
-        onRejected: currentWebView && currentWebView.grantFeaturePermission(securityOrigin, feature, false)
+        onAccepted: permission.grant()
+        onRejected: permission.deny()
         onVisibleChanged: {
-            if (visible)
+            if (visible) {
+                mainTextForPermissionDialog.text = questionForPermissionType();
                 width = contentWidth + 20;
+            }
         }
 
-        function questionForFeature() {
-            var question = "Allow " + securityOrigin + " to "
+        function questionForPermissionType() {
+            var question = "Allow " + permission.origin + " to "
 
-            switch (feature) {
-            case WebEngineView.Geolocation:
+            switch (permission.permissionType) {
+            case WebEnginePermission.PermissionType.Geolocation:
                 question += "access your location information?";
                 break;
-            case WebEngineView.MediaAudioCapture:
+            case WebEnginePermission.PermissionType.MediaAudioCapture:
                 question += "access your microphone?";
                 break;
-            case WebEngineView.MediaVideoCapture:
+            case WebEnginePermission.PermissionType.MediaVideoCapture:
                 question += "access your webcam?";
                 break;
-            case WebEngineView.MediaVideoCapture:
+            case WebEnginePermission.PermissionType.MediaAudioVideoCapture:
                 question += "access your microphone and webcam?";
                 break;
-            case WebEngineView.MouseLock:
+            case WebEnginePermission.PermissionType.MouseLock:
                 question += "lock your mouse cursor?";
                 break;
-            case WebEngineView.DesktopVideoCapture:
+            case WebEnginePermission.PermissionType.DesktopVideoCapture:
                 question += "capture video of your desktop?";
                 break;
-            case WebEngineView.DesktopAudioVideoCapture:
+            case WebEnginePermission.PermissionType.DesktopAudioVideoCapture:
                 question += "capture audio and video of your desktop?";
                 break;
-            case WebEngineView.Notifications:
+            case WebEnginePermission.PermissionType.Notifications:
                 question += "show notification on your desktop?";
                 break;
-            case WebEngineView.ClipboardReadWrite:
+            case WebEnginePermission.PermissionType.ClipboardReadWrite:
                 question += "read from and write to your clipboard?";
                 break;
-            case WebEngineView.LocalFontsAccess:
+            case WebEnginePermission.PermissionType.LocalFontsAccess:
                 question += "access the fonts stored on your machine?";
                 break;
             default:
-                question += "access unknown or unsupported feature [" + feature + "] ?";
+                question += "access unknown or unsupported permission type [" + permission.permissionType + "] ?";
                 break;
             }
 
@@ -819,10 +825,29 @@ ApplicationWindow {
         visible: false
     }
 
+    MessageDialog {
+        id: downloadAcceptDialog
+        property var downloadRequest: downloadView.pendingDownloadRequest
+        title: "Download requested"
+        text: downloadRequest ? downloadRequest.suggestedFileName : ""
+        buttons: Dialog.No | Dialog.Yes
+        onAccepted: {
+            downloadView.visible = true;
+            downloadView.append(downloadRequest);
+            downloadRequest.accept();
+        }
+        onRejected: {
+            downloadRequest.cancel();
+        }
+        onButtonClicked: {
+            visible = false;
+        }
+        visible: false
+    }
+
     function onDownloadRequested(download) {
-        downloadView.visible = true;
-        downloadView.append(download);
-        download.accept();
+        downloadView.pendingDownloadRequest = download;
+        downloadAcceptDialog.visible = true;
     }
 
     FindBar {
diff --git a/examples/webenginequick/quicknanobrowser/DownloadView.qml b/examples/webenginequick/quicknanobrowser/DownloadView.qml
index 421b4f55cc9..b116ab86734 100644
--- a/examples/webenginequick/quicknanobrowser/DownloadView.qml
+++ b/examples/webenginequick/quicknanobrowser/DownloadView.qml
@@ -9,6 +9,7 @@ import QtQuick.Layouts
 Rectangle {
     id: downloadView
     color: "lightgray"
+    property var pendingDownloadRequest: null
 
     ListModel {
         id: downloadModel
diff --git a/examples/webenginequick/quicknanobrowser/doc/src/quicknanobrowser.qdoc b/examples/webenginequick/quicknanobrowser/doc/src/quicknanobrowser.qdoc
index 1e00402ff3f..cff4d3354e2 100644
--- a/examples/webenginequick/quicknanobrowser/doc/src/quicknanobrowser.qdoc
+++ b/examples/webenginequick/quicknanobrowser/doc/src/quicknanobrowser.qdoc
@@ -96,25 +96,22 @@
     \skipto Dialog {
     \printuntil /^\ {4}\}/
 
-    \section1 Handling Feature Permission Requests
+    \section1 Handling Permission Requests
 
-    We use the \c onFeaturePermissionRequested() signal handler to handle requests for
-    accessing a certain feature or device. The \c securityOrigin parameter identifies the
-    requester web site, and the \c feature parameter is the requested feature. We use these
-    to construct the message of the dialog:
+    We use the \c onPermissionRequested() signal handler to handle requests for
+    accessing a certain feature or device. The \c permission parameter is an object of the
+    WebEnginePermission type, which can be used to handle the incoming request. We temporarily store
+    this object, since we need to use it to construct the message of the dialog:
 
     \quotefromfile webenginequick/quicknanobrowser/BrowserWindow.qml
-    \skipto onFeaturePermissionRequested
+    \skipto onPermissionRequested
     \printuntil }
 
-    We show a dialog where the user is asked to grant or deny access. The custom
+    We display a dialog where the user is asked to grant or deny access. The custom
     \c questionForFeature() JavaScript function generates a human-readable question about
     the request.
-    If users select \uicontrol Yes, we call the \l{WebEngineView::}{grantFeaturePermission()}
-    method with a third \c true parameter to grant the \c securityOrigin web site the permission
-    to access the \c feature.
-    If users select \uicontrol No, we call the same method but with the \c false parameter to
-    deny access:
+    If user selects \uicontrol Yes, we call the \l{webEnginePermission::grant}{grant()}
+    method, and if they select \uicontrol No we call \l{webEnginePermission::deny}{deny()}.
 
     \skipto id: sslDialog
     \skipto Dialog {
diff --git a/examples/webenginequick/quicknanobrowser/icons/3rdparty/REUSE.toml b/examples/webenginequick/quicknanobrowser/icons/3rdparty/REUSE.toml
new file mode 100644
index 00000000000..62a59c992ac
--- /dev/null
+++ b/examples/webenginequick/quicknanobrowser/icons/3rdparty/REUSE.toml
@@ -0,0 +1,15 @@
+version = 1
+
+[[annotations]]
+path = "*.png"
+precedence = "closest"
+SPDX-FileCopyrightText = ["Ulisse Perusin <uli.peru@gmail.com>",
+                          "Steven Garrity <sgarrity@silverorange.com>",
+                          "Lapo Calamandrei <calamandrei@gmail.com>",
+                          "Ryan Collier <rcollier@novell.com>",
+                          "Rodney Dawes <dobey@novell.com>",
+                          "Andreas Nilsson <nisses.mail@home.se>",
+                          "Tuomas Kuosmanen <tigert@tigert.com>",
+                          "Garrett LeSage <garrett@novell.com>",
+                          "Jakub Steiner <jimmac@novell.com>"]
+SPDX-License-Identifier = "LicenseRef-Tango-Icons-Public-Domain"
diff --git a/examples/webenginequick/quicknanobrowser/icons/3rdparty/qt_attribution.json b/examples/webenginequick/quicknanobrowser/icons/3rdparty/qt_attribution.json
index d8d85d6f153..48dbde66037 100644
--- a/examples/webenginequick/quicknanobrowser/icons/3rdparty/qt_attribution.json
+++ b/examples/webenginequick/quicknanobrowser/icons/3rdparty/qt_attribution.json
@@ -9,9 +9,8 @@
     "Homepage": "http://tango.freedesktop.org/Tango_Icon_Library",
     "Version": "0.8.90",
     "DownloadLocation": "http://tango.freedesktop.org/releases/tango-icon-theme-0.8.90.tar.gz",
-    "LicenseId": "urn:dje:license:public-domain",
+    "LicenseId": "LicenseRef-Tango-Icons-Public-Domain",
     "License": "Public Domain",
-    "LicenseFile": "COPYING",
     "Copyright": ["Ulisse Perusin <uli.peru@gmail.com>",
     "Steven Garrity <sgarrity@silverorange.com>",
     "Lapo Calamandrei <calamandrei@gmail.com>",
diff --git a/examples/webenginequick/quicknanobrowser/main.cpp b/examples/webenginequick/quicknanobrowser/main.cpp
index 1e693cbcd26..7a64eb8f3a9 100644
--- a/examples/webenginequick/quicknanobrowser/main.cpp
+++ b/examples/webenginequick/quicknanobrowser/main.cpp
@@ -14,12 +14,17 @@
 #include <QtCore/QCommandLineOption>
 #include <QtCore/QLoggingCategory>
 
-static QUrl startupUrl(const QCommandLineParser &parser)
+static QUrl startupUrl()
 {
-    if (!parser.positionalArguments().isEmpty()) {
-        const QUrl url = Utils::fromUserInput(parser.positionalArguments().constFirst());
-        if (url.isValid())
-            return url;
+    QUrl ret;
+    QStringList args(qApp->arguments());
+    args.takeFirst();
+    for (const QString &arg : qAsConst(args)) {
+        if (arg.startsWith(QLatin1Char('-')))
+             continue;
+        ret = Utils::fromUserInput(arg);
+        if (ret.isValid())
+            return ret;
     }
     return QUrl(QStringLiteral("chrome://qt"));
 }
@@ -34,20 +39,13 @@ int main(int argc, char **argv)
     QGuiApplication app(argc, argv);
     QLoggingCategory::setFilterRules(QStringLiteral("qt.webenginecontext.debug=true"));
 
-    QCommandLineParser parser;
-    parser.addHelpOption();
-    parser.addVersionOption();
-    parser.addPositionalArgument("url", "The URL to open.");
-    parser.process(app);
-
     QQmlApplicationEngine appEngine;
     appEngine.load(QUrl("qrc:/ApplicationRoot.qml"));
     if (appEngine.rootObjects().isEmpty())
         qFatal("Failed to load sources");
 
-    const QUrl url = startupUrl(parser);
     QMetaObject::invokeMethod(appEngine.rootObjects().constFirst(),
-                              "load", Q_ARG(QVariant, url));
+                              "load", Q_ARG(QVariant, startupUrl()));
 
     return app.exec();
 }
diff --git a/examples/webenginewidgets/CMakeLists.txt b/examples/webenginewidgets/CMakeLists.txt
index 3ce666d7293..ebe34d668fa 100644
--- a/examples/webenginewidgets/CMakeLists.txt
+++ b/examples/webenginewidgets/CMakeLists.txt
@@ -3,6 +3,7 @@
 
 qt_internal_add_example(contentmanipulation)
 qt_internal_add_example(cookiebrowser)
+qt_internal_add_example(permissionbrowser)
 qt_internal_add_example(notifications)
 qt_internal_add_example(simplebrowser)
 qt_internal_add_example(push-notifications)
diff --git a/examples/webenginewidgets/clientcertificate/CMakeLists.txt b/examples/webenginewidgets/clientcertificate/CMakeLists.txt
index 97c9393ffce..ef1f9a6d415 100644
--- a/examples/webenginewidgets/clientcertificate/CMakeLists.txt
+++ b/examples/webenginewidgets/clientcertificate/CMakeLists.txt
@@ -14,20 +14,20 @@ set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/webenginewidgets/clientcertificat
 
 find_package(Qt6 REQUIRED COMPONENTS Core Gui WebEngineWidgets)
 
-qt_add_executable(server
+qt_add_executable(example_cert_server
     server.cpp
 )
 
-qt_add_executable(client
+qt_add_executable(example_cert_client
     client.cpp
 )
 
-set_target_properties(client PROPERTIES
+set_target_properties(example_cert_client PROPERTIES
     WIN32_EXECUTABLE TRUE
     MACOSX_BUNDLE TRUE
 )
 
-qt_add_resources(client "client"
+qt_add_resources(example_cert_client "client"
     PREFIX
         "/"
     FILES
@@ -35,7 +35,7 @@ qt_add_resources(client "client"
        "resources/client.key"
 )
 
-qt_add_resources(server "server"
+qt_add_resources(example_cert_server "server"
     PREFIX
         "/"
     FILES
@@ -44,16 +44,16 @@ qt_add_resources(server "server"
        "resources/ca.pem"
 )
 
-target_link_libraries(client PUBLIC
+target_link_libraries(example_cert_client PUBLIC
     Qt::WebEngineWidgets
 )
 
-target_link_libraries(server PUBLIC
+target_link_libraries(example_cert_server PUBLIC
     Qt::Core
     Qt::Network
 )
 
-install(TARGETS server client
+install(TARGETS example_cert_server example_cert_client
     RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
     BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
     LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
diff --git a/examples/webenginewidgets/clientcertificate/client.cpp b/examples/webenginewidgets/clientcertificate/client.cpp
index 1227fa28e8a..c10cff24769 100644
--- a/examples/webenginewidgets/clientcertificate/client.cpp
+++ b/examples/webenginewidgets/clientcertificate/client.cpp
@@ -21,11 +21,17 @@ int main(int argc, char *argv[])
     QApplication app(argc, argv);
 
     QFile certFile(":/resources/client.pem");
-    certFile.open(QIODevice::ReadOnly);
+    if (!certFile.open(QIODevice::ReadOnly)) {
+        qFatal("Failed to read cert file %s: %s", qPrintable(certFile.fileName()),
+               qPrintable(certFile.errorString()));
+    }
     const QSslCertificate cert(certFile.readAll(), QSsl::Pem);
 
     QFile keyFile(":/resources/client.key");
-    keyFile.open(QIODevice::ReadOnly);
+    if (!keyFile.open(QIODevice::ReadOnly)) {
+        qFatal("Failed to read key file %s: %s", qPrintable(keyFile.fileName()),
+               qPrintable(keyFile.errorString()));
+    }
     const QSslKey sslKey(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, "");
 
     QWebEngineProfile::defaultProfile()->clientCertificateStore()->add(cert, sslKey);
@@ -36,7 +42,7 @@ int main(int argc, char *argv[])
 
     QObject::connect(
             &page, &QWebEnginePage::selectClientCertificate, &page,
-            [&cert](QWebEngineClientCertificateSelection selection) {
+            [](QWebEngineClientCertificateSelection selection) {
                 QDialog dialog;
                 QVBoxLayout *layout = new QVBoxLayout;
                 QLabel *label = new QLabel(QLatin1String("Select certificate"));
diff --git a/examples/webenginewidgets/clientcertificate/server.cpp b/examples/webenginewidgets/clientcertificate/server.cpp
index ee83dab8a59..0d1bf771030 100644
--- a/examples/webenginewidgets/clientcertificate/server.cpp
+++ b/examples/webenginewidgets/clientcertificate/server.cpp
@@ -38,7 +38,10 @@ int main(int argc, char *argv[])
     configuration.setPeerVerifyMode(QSslSocket::VerifyPeer);
 
     QFile keyFile(":/resources/server.key");
-    keyFile.open(QIODevice::ReadOnly);
+    if (!keyFile.open(QIODevice::ReadOnly)) {
+        qFatal("Failed to read key file %s: %s", qPrintable(keyFile.fileName()),
+               qPrintable(keyFile.errorString()));
+    }
 
     QSslKey key(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
     configuration.setPrivateKey(key);
diff --git a/examples/webenginewidgets/contentmanipulation/REUSE.toml b/examples/webenginewidgets/contentmanipulation/REUSE.toml
new file mode 100644
index 00000000000..f648b36f255
--- /dev/null
+++ b/examples/webenginewidgets/contentmanipulation/REUSE.toml
@@ -0,0 +1,8 @@
+version = 1
+
+[[annotations]]
+path = "jquery.min.js"
+comment = "license is not reuse readable."
+precedence = "closest"
+SPDX-FileCopyrightText = "Copyright (c) 2009 John Resig"
+SPDX-License-Identifier = "MIT"
diff --git a/examples/webenginewidgets/contentmanipulation/mainwindow.cpp b/examples/webenginewidgets/contentmanipulation/mainwindow.cpp
index 3990be2b8a7..4e661f4d825 100644
--- a/examples/webenginewidgets/contentmanipulation/mainwindow.cpp
+++ b/examples/webenginewidgets/contentmanipulation/mainwindow.cpp
@@ -12,7 +12,10 @@ MainWindow::MainWindow(const QUrl& url)
 
     QFile file;
     file.setFileName(":/jquery.min.js");
-    file.open(QIODevice::ReadOnly);
+    if (!file.open(QIODevice::ReadOnly)) {
+        qFatal("Failed to read jQuery file %s: %s", qPrintable(file.fileName()),
+               qPrintable(file.errorString()));
+    }
     jQuery = file.readAll();
     jQuery.append("\nvar qt = { 'jQuery': jQuery.noConflict(true) };");
     file.close();
diff --git a/examples/webenginewidgets/cookiebrowser/3rdparty/REUSE.toml b/examples/webenginewidgets/cookiebrowser/3rdparty/REUSE.toml
new file mode 100644
index 00000000000..340d98c0301
--- /dev/null
+++ b/examples/webenginewidgets/cookiebrowser/3rdparty/REUSE.toml
@@ -0,0 +1,15 @@
+version = 1
+
+[[annotations]]
+path = "view-refresh.png"
+precedence = "closest"
+SPDX-FileCopyrightText = ["Ulisse Perusin <uli.peru@gmail.com>",
+                          "Steven Garrity <sgarrity@silverorange.com>",
+                          "Lapo Calamandrei <calamandrei@gmail.com>",
+                          "Ryan Collier <rcollier@novell.com>",
+                          "Rodney Dawes <dobey@novell.com>",
+                          "Andreas Nilsson <nisses.mail@home.se>",
+                          "Tuomas Kuosmanen <tigert@tigert.com>",
+                          "Garrett LeSage <garrett@novell.com>",
+                          "Jakub Steiner <jimmac@novell.com>"]
+SPDX-License-Identifier = "LicenseRef-Tango-Icons-Public-Domain"
diff --git a/examples/webenginewidgets/cookiebrowser/3rdparty/qt_attribution.json b/examples/webenginewidgets/cookiebrowser/3rdparty/qt_attribution.json
index 35d9cf0d7ad..64ba15e00ef 100644
--- a/examples/webenginewidgets/cookiebrowser/3rdparty/qt_attribution.json
+++ b/examples/webenginewidgets/cookiebrowser/3rdparty/qt_attribution.json
@@ -9,9 +9,8 @@
     "Homepage": "http://tango.freedesktop.org/Tango_Icon_Library",
     "Version": "0.8.90",
     "DownloadLocation": "http://tango.freedesktop.org/releases/tango-icon-theme-0.8.90.tar.gz",
-    "LicenseId": "urn:dje:license:public-domain",
+    "LicenseId": "LicenseRef-Tango-Icons-Public-Domain",
     "License": "Public Domain",
-    "LicenseFile": "COPYING",
     "Copyright": ["Ulisse Perusin <uli.peru@gmail.com>",
     "Steven Garrity <sgarrity@silverorange.com>",
     "Lapo Calamandrei <calamandrei@gmail.com>",
diff --git a/examples/webenginewidgets/maps/doc/src/maps.qdoc b/examples/webenginewidgets/maps/doc/src/maps.qdoc
index 0175f8b6511..f9bd19bfcd8 100644
--- a/examples/webenginewidgets/maps/doc/src/maps.qdoc
+++ b/examples/webenginewidgets/maps/doc/src/maps.qdoc
@@ -48,10 +48,10 @@
     \printuntil setCentralWidget
 
     We then proceed to connect a lambda function to the \l
-    QWebEnginePage::featurePermissionRequested signal:
+    QWebEnginePage::permissionRequested signal:
 
     \skipto m_view->page()
-    \printuntil QWebEnginePage::Feature
+    \printuntil QWebEnginePermission permission
 
     This signal is emitted whenever a web page requests to make use of a certain
     feature or device, including not only location services but also audio
@@ -62,15 +62,14 @@
 
     Now comes the part where we actually ask the user for permission:
 
-    \printuntil securityOrigin
     \printuntil });
 
     Note that the question includes the host component of the web site's URI (\c
-    securityOrigin) to inform the user as to exactly which web site will be
+    permission.origin()) to inform the user as to exactly which web site will be
     receiving their location data.
 
-    We use the \l QWebEnginePage::setFeaturePermission method to communicate the
-    user's answer back to the web page.
+    We use the \l QWebEnginePermission::grant() and \l QWebEnginePermission::deny()
+    methods to communicate the user's answer back to the web page.
 
     Finally we ask the \l QWebEnginePage to load the web page that might want to
     use location services:
diff --git a/examples/webenginewidgets/maps/mainwindow.cpp b/examples/webenginewidgets/maps/mainwindow.cpp
index 0d2b499117c..31cc0bbf0cb 100644
--- a/examples/webenginewidgets/maps/mainwindow.cpp
+++ b/examples/webenginewidgets/maps/mainwindow.cpp
@@ -13,25 +13,21 @@ MainWindow::MainWindow(QWidget *parent)
 
     QWebEnginePage *page = m_view->page();
 
-    connect(page, &QWebEnginePage::featurePermissionRequested,
-            [this, page](const QUrl &securityOrigin, QWebEnginePage::Feature feature) {
-        if (feature != QWebEnginePage::Geolocation)
+    connect(page, &QWebEnginePage::permissionRequested, [this](QWebEnginePermission permission) {
+        if (permission.permissionType() != QWebEnginePermission::PermissionType::Geolocation)
             return;
 
         QMessageBox msgBox(this);
-        msgBox.setText(tr("%1 wants to know your location").arg(securityOrigin.host()));
+        msgBox.setText(tr("%1 wants to know your location").arg(permission.origin().host()));
         msgBox.setInformativeText(tr("Do you want to send your current location to this website?"));
         msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
         msgBox.setDefaultButton(QMessageBox::Yes);
 
-        if (msgBox.exec() == QMessageBox::Yes) {
-            page->setFeaturePermission(
-                securityOrigin, feature, QWebEnginePage::PermissionGrantedByUser);
-        } else {
-            page->setFeaturePermission(
-                securityOrigin, feature, QWebEnginePage::PermissionDeniedByUser);
-        }
+        if (msgBox.exec() == QMessageBox::Yes)
+            permission.grant();
+        else
+            permission.deny();
     });
 
-    page->load(QUrl(QStringLiteral("https://maps.google.com")));
+    page->load(QUrl(QStringLiteral("https://bing.com/maps")));
 }
diff --git a/examples/webenginewidgets/notifications/doc/src/notifications.qdoc b/examples/webenginewidgets/notifications/doc/src/notifications.qdoc
index f4fe1818fe1..bd0def910a2 100644
--- a/examples/webenginewidgets/notifications/doc/src/notifications.qdoc
+++ b/examples/webenginewidgets/notifications/doc/src/notifications.qdoc
@@ -45,11 +45,11 @@
 
     \section2 Requesting Feature Permissions
 
-    We then use the \l QWebEnginePage::featurePermissionRequested() call to
+    We then use the \l QWebEnginePage::permissionRequested() call to
     request the user's permission to show notifications on their device.
 
     \quotefromfile webenginewidgets/notifications/main.cpp
-    \skipto featurePermissionRequested
+    \skipto permissionRequested
     \printuntil });
 
     \section2 Handling New Notifications
diff --git a/examples/webenginewidgets/notifications/main.cpp b/examples/webenginewidgets/notifications/main.cpp
index c754aff3f84..e01dd03bc5f 100644
--- a/examples/webenginewidgets/notifications/main.cpp
+++ b/examples/webenginewidgets/notifications/main.cpp
@@ -33,11 +33,11 @@ int main(int argc, char *argv[])
     // set custom page to open all page's links for https scheme in system browser
     view.setPage(new WebEnginePage(&view));
 
-    QObject::connect(view.page(), &QWebEnginePage::featurePermissionRequested,
-                     [&] (const QUrl &origin, QWebEnginePage::Feature feature) {
-                         if (feature != QWebEnginePage::Notifications)
+    QObject::connect(view.page(), &QWebEnginePage::permissionRequested,
+                     [&] (QWebEnginePermission permission) {
+                         if (permission.permissionType() != QWebEnginePermission::PermissionType::Notifications)
                              return;
-                         view.page()->setFeaturePermission(origin, feature, QWebEnginePage::PermissionGrantedByUser);
+                         permission.grant();
                      });
 
     auto profile = view.page()->profile();
@@ -50,4 +50,3 @@ int main(int argc, char *argv[])
     view.setUrl(QStringLiteral("qrc:/index.html"));
     return app.exec();
 }
-
diff --git a/examples/webenginewidgets/permissionbrowser/CMakeLists.txt b/examples/webenginewidgets/permissionbrowser/CMakeLists.txt
new file mode 100644
index 00000000000..c7d241d8f67
--- /dev/null
+++ b/examples/webenginewidgets/permissionbrowser/CMakeLists.txt
@@ -0,0 +1,84 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(permissionbrowser LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTOUIC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+    set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/webenginewidgets/permissionbrowser")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui WebEngineWidgets)
+
+qt_add_executable(permissionbrowser
+    permissiondialog.ui
+    permissionwidget.ui
+    main.cpp
+    mainwindow.cpp mainwindow.h mainwindow.ui
+)
+
+if(WIN32)
+    set_property(
+        TARGET permissionbrowser
+        APPEND PROPERTY
+            SOURCES permissionbrowser.exe.manifest)
+endif()
+
+set_target_properties(permissionbrowser PROPERTIES
+    WIN32_EXECUTABLE TRUE
+    MACOSX_BUNDLE TRUE
+    MACOSX_BUNDLE_GUI_IDENTIFIER "io.qt.examples.webenginewidgets.permissionbrowser"
+)
+
+target_link_libraries(permissionbrowser PUBLIC
+    Qt::Core
+    Qt::Gui
+    Qt::WebEngineWidgets
+)
+
+# Resources:
+set(permissionbrowser_resource_files
+    "resources/3rdparty/view-refresh.png"
+    "resources/3rdparty/go-next.png"
+    "resources/3rdparty/go-previous.png"
+    "resources/AppLogoColor.png"
+    "resources/landing.html"
+)
+
+qt_add_resources(permissionbrowser "permissionbrowser"
+    PREFIX
+        "/"
+    BASE
+        "resources"
+    FILES
+        ${permissionbrowser_resource_files}
+)
+
+if (APPLE)
+    set_target_properties(permissionbrowser PROPERTIES
+        MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.cmake.macos.plist"
+    )
+
+    if (NOT CMAKE_GENERATOR STREQUAL "Xcode")
+        # Need to sign application for location permissions to work
+        if(QT_FEATURE_debug_and_release)
+            set(exe_path "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/")
+        else()
+            unset(exe_path)
+        endif()
+        add_custom_command(TARGET permissionbrowser
+            POST_BUILD COMMAND codesign --force -s - ${exe_path}permissionbrowser.app
+        )
+    endif()
+endif()
+
+install(TARGETS permissionbrowser
+    RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+    BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+    LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/webenginewidgets/permissionbrowser/Info.cmake.macos.plist b/examples/webenginewidgets/permissionbrowser/Info.cmake.macos.plist
new file mode 100644
index 00000000000..48ddaadc650
--- /dev/null
+++ b/examples/webenginewidgets/permissionbrowser/Info.cmake.macos.plist
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+    <key>CFBundleInfoDictionaryVersion</key>
+    <string>6.0</string>
+    <key>CFBundlePackageType</key>
+    <string>APPL</string>
+    <key>CFBundleName</key>
+    <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
+    <key>CFBundleIdentifier</key>
+    <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
+    <key>CFBundleExecutable</key>
+    <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
+    <key>CFBundleVersion</key>
+    <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
+    <key>CFBundleShortVersionString</key>
+    <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
+    <key>LSMinimumSystemVersion</key>
+    <string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>
+    <key>NSHumanReadableCopyright</key>
+    <string>${MACOSX_BUNDLE_COPYRIGHT}</string>
+    <key>CFBundleIconFile</key>
+    <string>${MACOSX_BUNDLE_ICON_FILE}</string>
+    <key>CFBundleDevelopmentRegion</key>
+    <string>English</string>
+    <key>NSSupportsAutomaticGraphicsSwitching</key>
+    <true/>
+    <key>NSLocationUsageDescription</key>
+    <string>Permission Browser would like to give web sites access to your location for demo purposes.</string>
+    <key>NSMicrophoneUsageDescription</key>
+    <string>Permission Browser would like to give web sites access to your computer's microphone for demo purposes.</string>
+    <key>NSCameraUsageDescription</key>
+    <string>Permission Browser would like to give web sites access to your computer's camera for demo purposes.</string>
+</dict>
+</plist>
diff --git a/examples/webenginewidgets/permissionbrowser/doc/images/permissionbrowser-example.png b/examples/webenginewidgets/permissionbrowser/doc/images/permissionbrowser-example.png
new file mode 100644
index 00000000000..56ea60025de
Binary files /dev/null and b/examples/webenginewidgets/permissionbrowser/doc/images/permissionbrowser-example.png differ
diff --git a/examples/webenginewidgets/permissionbrowser/doc/src/permissionbrowser.qdoc b/examples/webenginewidgets/permissionbrowser/doc/src/permissionbrowser.qdoc
new file mode 100644
index 00000000000..8a394e5c8e4
--- /dev/null
+++ b/examples/webenginewidgets/permissionbrowser/doc/src/permissionbrowser.qdoc
@@ -0,0 +1,221 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+    \example webenginewidgets/permissionbrowser
+    \examplecategory {Web Technologies}
+    \title WebEngine Widgets Permission Browser Example
+    \ingroup webengine-widgetexamples
+    \brief Demonstrates how to handle website permission requests, and manage
+    existing permissions.
+
+    \image permissionbrowser-example.png
+
+    Permission Browser demonstrates how to use the \l{QWebEnginePermission} class
+    to manage website permissions. The example includes code for handling incoming
+    permission requests, as well as modifying already existing permissions. Also
+    demonstrated are the effects of the different permission persistence policies
+    defined within the \l{QWebEngineProfile} class.
+
+    \include examples-run.qdocinc
+
+    \section1 Class Definitions
+
+    \section2 MainWindow
+
+    The \c MainWindow class inherits \l QMainWindow. Inside, we declare a convenience
+    pointer to the \l QVBoxLayout which will lay out the widgets used to manipulate
+    individual permissions, as well as another convenience pointer to the widget
+    displaying the currently pending permission request. We also declare a
+    \l QWebEngineView, which will be used to display the actual webpage contents.
+
+    \quotefromfile webenginewidgets/permissionbrowser/mainwindow.h
+    \skipto class MainWindow :
+    \printuntil /^\}/
+
+    The rest of the layout for the application is defined inside mainwindow.ui,
+    and was created using Qt Creator's Design mode. \c MainWindow is a child class
+    of Ui_MainWindow, which is a C++ class generated at compile time from the
+    definitions found inside mainwindow.ui.
+
+    \section2 PermissionWidget and PermissionDialog
+
+    The \c PermissionWidget class defines a widget corresponding to a single
+    \l QWebEnginePermission instance. For convenience, the \l QWebEnginePermission
+    object is stored within. The widget itself has controls for granting, denying,
+    or deleting the permission; all of this is defined inside \c PermissionWidget.ui.
+
+    \quotefromfile webenginewidgets/permissionbrowser/mainwindow.h
+    \skipto class PermissionWidget :
+    \printuntil /^\}/
+
+    When clicking the "New" button in the main window's UI, a pop-up window will
+    appear, allowing the user to pre-grant permission to a known origin. That
+    pop-up is defined by the \c PermissionDialog class:
+
+    \quotefromfile webenginewidgets/permissionbrowser/mainwindow.h
+    \skipto class PermissionDialog :
+    \printuntil /^\}/
+
+    \section1 Handling incoming permission requests
+
+    Whenever a website uses an API that might compromise the user's privacy, the
+    browser is expected to show them a prompt asking to either grant or deny permission.
+    The PermissionBrowser example has a dedicated section at the bottom right, which
+    gets populated with a \c PermissionWidget whenever that happens.
+
+    The \c PermissionWidget displays the permission's origin, the requested
+    \l QWebEnginePermission::PermissionType, as well as the current status of that permission.
+    It also has buttons for granting and denying the permission. Since the permission status
+    is (by default) remembered, the delete button allows the user to remove the permission
+    from the underlying storage.
+
+    To achieve all this, we first connect QWebEnginePage's \c permissionRequested signal
+    to \c MainWindow's \c handlePermissionRequested slot:
+
+    \quotefromfile webenginewidgets/permissionbrowser/mainwindow.cpp
+    \skipto connect(m_webview->page(),
+    \printline connect(m_webview->page(),
+
+    The signal handler is relatively simple: it attempts to create a \c PermissionWidget
+    instance for the provided QWebEnginePermission object, and if it succeeds it
+    plugs that widget into the QFrame designated for pending permissions. We also
+    subscribe to \c PermissionWidget's \c permissionModified signal so that we can later move
+    the \c PermissionWidget from the bottom right to the list of existing widgets above.
+
+    \quotefromfile webenginewidgets/permissionbrowser/mainwindow.cpp
+    \skipto void MainWindow::handlePermissionRequested
+    \printuntil /^\}/
+
+    We only create a new \c PermissionWidget if we don't already have an existing one:
+
+    \quotefromfile webenginewidgets/permissionbrowser/mainwindow.cpp
+    \skipto PermissionWidget *MainWindow::create PermissionWidget
+    \printuntil /^\}/
+
+    \target PermissionWidgetConstructor
+    \section1 Modifying a permission and displaying it to the user
+
+    The QWebEnginePermission interface provides the \l {QWebEnginePermission::grant}{grant}()
+    and \l {QWebEnginePermission::deny}{deny}() functions, which are all that's needed to
+    change the status of a permission. If the application needs to forget about a permission,
+    we use the \l {QWebEnginePermission::reset}{reset}() function.
+
+    Inside the \c PermissionWidget constructor, we hook those function up to the buttons'
+    \c clicked signal, so that we can execute the relevant functionality on the
+    QWebEnginePermission object.
+
+    Whenever a button is pressed, we emit the \c permissionModified signal, which
+    \c MainWindow uses to know when it needs to move the widget from the bottom-right
+    to the list of existing permissions. We also make sure to call \c updateState(), which
+    handles visual updates to the widget. When the delete button is pressed, we also make
+    sure mark the widget for deletion, since we only want to display existing permissions to
+    the user.
+
+    \quotefromfile webenginewidgets/permissionbrowser/mainwindow.cpp
+    \skipto PermissionWidget::PermissionWidget
+    \printuntil /^\}/
+
+    The \c updateState() function displays the data supplied by QWebEnginePermission
+    to the user. It also makes sure that, when a permission is in the
+    \l QWebEnginePermission::Invalid state, the buttons for granting or denying it
+    are disabled.
+
+    \quotefromfile webenginewidgets/permissionbrowser/mainwindow.cpp
+    \skipto void PermissionWidget::updateState()
+    \printuntil /^\}/
+
+    When a pending permission is granted or denied, we want to move the associated
+    widget to the list above, which contains all currently existing permissions.
+    We do this in the \c MainWindow::handlePermissionModified slot.
+
+    \quotefromfile webenginewidgets/permissionbrowser/mainwindow.cpp
+    \skipto void MainWindow::handlePermissionModified
+    \printuntil /^\}/
+
+    Notably, we only do this in cases where we know the permission is remembered;
+    some \c PermissionTypes are non-persistent, which means they require
+    a permission prompt be shown to the user every time they're used.
+    We also exclude permissions with a \l QWebEnginePermission::Ask state, which
+    indicates that the permission was \l {QWebEnginePermission::reset}{reset}(),
+    and we don't add anything to the list of existing permissions when
+    \l QWebEngineProfile::persistentPermissionsPolicy is set to
+    \c AskEveryTime.
+
+    \note Check the \l QWebEnginePermission::PermissionType documentation to see which
+    \c PermissionTypes are persistent.
+
+    \section1 Displaying and modifying existing permissions
+
+    By default, permissions are stored to disk and retrieved again on application
+    startup. To get a list of all existing website permissions, we call
+    \l QWebEngineProfile::listAllPermissions():
+
+    \quotefromfile webenginewidgets/permissionbrowser/mainwindow.cpp
+    \skipto void MainWindow::loadStoredPermissions()
+    \printuntil /^\}/
+
+    For every permission in the list, we simply construct a new \c PermissionWidget, and
+    add it to the list on the right-hand side of the screen. Existing permissions
+    are modified \l {PermissionWidgetConstructor}{using the exact same API as pending ones}.
+
+    \section1 Pre-granting permissions
+
+    Certain permissions may be granted in advance, provided the origin and permission type
+    are known. Clicking on the "New" button in the top right will create a pop-up
+    dialog that allows you to do just that. The dialog is implemented by the
+    \c PermissionDialog class:
+
+    \quotefromfile webenginewidgets/permissionbrowser/mainwindow.cpp
+    \skipto PermissionDialog::PermissionDialog
+    \printuntil /^\}/
+
+    We populate the \l QComboBox using the QMetaEnum type associated with
+    \l QWebEnginePermission::PermissionType. We make sure to filter out non-persistent
+    permission types, since pre-granting these is not supported.
+
+    We display the dialog and add show the resulting \c PermissionWidget
+    in the UI inside the \c MainWindow::handleNewClicked slot. The new
+    permission is handled the same way we would if a website requested it:
+    by calling \c handlePermissionRequested().
+
+    \quotefromfile webenginewidgets/permissionbrowser/mainwindow.cpp
+    \skipto void MainWindow::handleNewClicked()
+    \printuntil /^\}/
+
+    \section1 Changing the permission persistence policy
+
+    By default, permissions are stored to disk for every named QWebEngineProfile,
+    and in memory for every unnamed/off-the-record one. Normally, this setting
+    won't be changed at runtime, but this example explores the effects
+    of each option.
+
+    \list
+        \li \l QWebEngineProfile::StoreOnDisk is the default, and it ensures
+        that any permissions that have been granted in the current application run will be
+        loaded back up at next startup. A permission only needs to be granted once, and
+        subsequent uses of the API that triggered the request will automatically be granted,
+        until the application calls QWebEnginePermission::reset().
+        \li \l QWebEngineProfile::StoreInMemory Has the same behavior as above,
+        except that permissions will be destroyed at application exit, and not committed
+        to disk.
+        \li \l QWebEngineProfile::AskEveryTime makes sure permissions are never
+        remembered, and all act as if they're non-persistent. Thus, every time a web API needs
+        a permission, a new prompt will be shown to the user. This option is intended for
+        backwards compatibility and applications which implement their own permission storage.
+    \endlist
+
+    To ensure the user will be shown previously existing permissions, we need to call
+    \l QWebEngineProfile::listAllPermissions():
+
+    \quotefromfile webenginewidgets/permissionbrowser/mainwindow.cpp
+    \skipto void MainWindow::loadStoredPermissions()
+    \printuntil /^\}/
+
+    This is done one time at startup, as well as whenever the user changes the policy
+    from the \l QComboBox from the top right.
+
+    \quotefromfile webenginewidgets/permissionbrowser/mainwindow.cpp
+    \skipto void MainWindow::handlePolicyComboBoxIndexChanged
+    \printuntil /^\}/
+*/
diff --git a/examples/webenginewidgets/permissionbrowser/main.cpp b/examples/webenginewidgets/permissionbrowser/main.cpp
new file mode 100644
index 00000000000..5efc8c6f96a
--- /dev/null
+++ b/examples/webenginewidgets/permissionbrowser/main.cpp
@@ -0,0 +1,18 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwindow.h"
+#include <QApplication>
+#include <QLoggingCategory>
+#include <QUrl>
+
+int main(int argc, char *argv[])
+{
+    QCoreApplication::setOrganizationName("QtExamples");
+    QApplication app(argc, argv);
+    app.setWindowIcon(QIcon(QString(QLatin1StringView(":AppLogoColor.png"))));
+    MainWindow window(QUrl("qrc:/landing.html"));
+    window.resize(1024, 768);
+    window.show();
+    return app.exec();
+}
diff --git a/examples/webenginewidgets/permissionbrowser/mainwindow.cpp b/examples/webenginewidgets/permissionbrowser/mainwindow.cpp
new file mode 100644
index 00000000000..1cc9b74efd6
--- /dev/null
+++ b/examples/webenginewidgets/permissionbrowser/mainwindow.cpp
@@ -0,0 +1,259 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwindow.h"
+#include <QWebEngineProfile>
+#include <QWebEnginePermission>
+#include <QWebEngineSettings>
+#include <QMetaEnum>
+#include <QUrl>
+
+PermissionDialog::PermissionDialog(const QWebEngineProfile *profile, QWidget *parent)
+    : QDialog(parent), m_profile(profile)
+{
+    setupUi(this);
+
+    auto metaEnum = QMetaEnum::fromType<QWebEnginePermission::PermissionType>();
+    for (int i = 0; i < metaEnum.keyCount(); ++i) {
+        auto permissionType = QWebEnginePermission::PermissionType(metaEnum.value(i));
+        if (QWebEnginePermission::isPersistent(permissionType))
+            m_permissionTypeComboBox->addItem(metaEnum.key(i), QVariant::fromValue(permissionType));
+    }
+}
+
+QWebEnginePermission PermissionDialog::permission() const
+{
+    return m_profile->queryPermission(
+            QUrl(m_originLineEdit->text()),
+            m_permissionTypeComboBox->currentData().value<QWebEnginePermission::PermissionType>());
+}
+
+PermissionWidget::PermissionWidget(const QWebEnginePermission &permission, QWidget *parent)
+    : QWidget(parent)
+    , m_permission(permission)
+{
+    setupUi(this);
+    connect(m_deleteButton, &QPushButton::clicked, [this]() {
+        m_permission.reset();
+        emit permissionModified(this);
+        deleteLater();
+    });
+
+    connect(m_grantButton, &QPushButton::clicked, [this]() {
+        m_permission.grant();
+        updateState();
+        emit permissionModified(this);
+    });
+
+    connect(m_denyButton, &QPushButton::clicked, [this]() {
+        m_permission.deny();
+        updateState();
+        emit permissionModified(this);
+    });
+
+    updateState();
+}
+
+void PermissionWidget::updateState()
+{
+    switch (m_permission.state()) {
+    case QWebEnginePermission::State::Invalid:
+        m_stateLabel->setText("<font color='gray'>Invalid</font>");
+        m_grantButton->setEnabled(false);
+        m_denyButton->setEnabled(false);
+        break;
+    case QWebEnginePermission::State::Ask:
+        m_stateLabel->setText("<font color='yellow'>Waiting for response</font>");
+        break;
+    case QWebEnginePermission::State::Granted:
+        m_stateLabel->setText("<font color='green'>Granted</font>");
+        break;
+    case QWebEnginePermission::State::Denied:
+        m_stateLabel->setText("<font color='red'>Denied</font>");
+        break;
+    }
+
+    m_typeLabel->setText(QMetaEnum::fromType<QWebEnginePermission::PermissionType>().valueToKey((quint8)m_permission.permissionType()));
+    m_originLabel->setText(m_permission.origin().toDisplayString());
+}
+
+MainWindow::MainWindow(const QUrl &url)
+    : QMainWindow()
+    , m_layout(new QVBoxLayout)
+    , m_profile(new QWebEngineProfile("permissionbrowser"))
+    , m_webview(new QWebEngineView(m_profile, this))
+    , m_pendingWidget(nullptr)
+{
+    setupUi(this);
+
+    auto metaEnum = QMetaEnum::fromType<QWebEngineProfile::PersistentPermissionsPolicy>();
+    for (int i = 0; i < metaEnum.keyCount(); ++i) {
+        auto policy = QWebEngineProfile::PersistentPermissionsPolicy(metaEnum.value(i));
+        m_policyComboBox->addItem(metaEnum.key(i), QVariant::fromValue(policy));
+    }
+
+    m_urlLineEdit->setText(url.toString());
+
+    m_layout->addItem(new QSpacerItem(0,0, QSizePolicy::Minimum, QSizePolicy::Expanding));
+    m_layout->setContentsMargins(0, 0, 0, 0);
+    m_layout->setSpacing(0);
+
+    QWidget *w = new QWidget();
+    w->setLayout(m_layout);
+
+    m_storedScrollArea->setWidget(w);
+    m_storedScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+
+    (new QVBoxLayout(m_pendingFrame))->setContentsMargins(0, 0, 0, 0);
+
+    loadStoredPermissions();
+
+    connect(m_deleteAllButton, &QPushButton::clicked, this, &MainWindow::handleDeleteAllClicked);
+    connect(m_newButton, &QPushButton::clicked, this, &MainWindow::handleNewClicked);
+    connect(m_refreshButton, &QPushButton::clicked, this, &MainWindow::handleRefreshClicked);
+    connect(m_backButton, &QPushButton::clicked, this, &MainWindow::handleBackClicked);
+    connect(m_forwardButton, &QPushButton::clicked, this, &MainWindow::handleForwardClicked);
+    connect(m_policyComboBox, &QComboBox::currentIndexChanged, this, &MainWindow::handlePolicyComboBoxIndexChanged);
+    connect(m_webview, &QWebEngineView::urlChanged, this, &MainWindow::handleUrlChanged);
+    connect(m_webview->page(), &QWebEnginePage::permissionRequested, this, &MainWindow::handlePermissionRequested);
+
+    m_profile->settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true);
+    m_profile->settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, true);
+
+    m_frame->layout()->addWidget(m_webview);
+    static_cast<QVBoxLayout *>(m_frame->layout())->setStretchFactor(m_webview, 1);
+    m_webview->load(url);
+}
+
+MainWindow::~MainWindow()
+{
+    delete m_webview;
+    delete m_profile;
+}
+
+void MainWindow::handlePermissionRequested(QWebEnginePermission permission)
+{
+    PermissionWidget *widget = createPermissionWidget(permission);
+    if (widget) {
+        m_pendingFrame->layout()->addWidget(widget);
+        connect(widget, &PermissionWidget::permissionModified, this, &MainWindow::handlePermissionModified);
+
+        if (m_pendingWidget)
+            m_pendingWidget->deleteLater();
+
+        m_pendingWidget = widget;
+    }
+}
+
+void MainWindow::handlePermissionModified(PermissionWidget *widget)
+{
+    if (!m_pendingWidget || m_pendingWidget != widget)
+        return;
+
+    m_pendingFrame->layout()->removeWidget(widget);
+    m_pendingWidget = nullptr;
+
+    if (!QWebEnginePermission::isPersistent(widget->m_permission.permissionType())
+        || widget->m_permission.state() == QWebEnginePermission::State::Ask
+        || m_profile->persistentPermissionsPolicy() == QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime) {
+
+        widget->deleteLater();
+        return;
+    }
+
+    m_layout->insertWidget(0, widget);
+}
+
+void MainWindow::handleUrlChanged(const QUrl &url)
+{
+    m_urlLineEdit->setText(url.toString());
+}
+
+void MainWindow::handleDeleteAllClicked()
+{
+    for (int i = m_layout->count() - 1; i >= 0; i--) {
+        PermissionWidget *widget = qobject_cast<PermissionWidget *>(m_layout->itemAt(i)->widget());
+        if (!widget)
+            continue;
+
+        widget->m_permission.reset();
+        widget->deleteLater();
+    }
+}
+
+void MainWindow::handleNewClicked()
+{
+    PermissionDialog dialog(m_profile);
+    if (dialog.exec() == QDialog::Accepted) {
+        handlePermissionRequested(dialog.permission());
+    }
+}
+
+void MainWindow::handleRefreshClicked()
+{
+    m_webview->load(QUrl::fromUserInput(m_urlLineEdit->text()));
+}
+
+void MainWindow::handleBackClicked()
+{
+    m_webview->triggerPageAction(QWebEnginePage::Back);
+}
+
+void MainWindow::handleForwardClicked()
+{
+    m_webview->triggerPageAction(QWebEnginePage::Forward);
+}
+
+void MainWindow::handlePolicyComboBoxIndexChanged(int)
+{
+    auto policy =
+            m_policyComboBox->currentData().value<QWebEngineProfile::PersistentPermissionsPolicy>();
+    if (policy == m_profile->persistentPermissionsPolicy())
+        return;
+
+    for (int i = m_layout->count() - 1; i >= 0; i--) {
+        PermissionWidget *widget = qobject_cast<PermissionWidget *>(m_layout->itemAt(i)->widget());
+        if (!widget)
+            continue;
+
+        widget->deleteLater();
+    }
+
+    m_profile->setPersistentPermissionsPolicy(policy);
+    loadStoredPermissions();
+}
+
+bool MainWindow::containsPermission(const QWebEnginePermission &permission)
+{
+    for (const auto *w: std::as_const(m_storedScrollArea->widget()->children())) {
+        const PermissionWidget *widget = qobject_cast<const PermissionWidget *>(w);
+        if (!widget)
+            continue;
+        const QWebEnginePermission &widgetPermission = widget->m_permission;
+        if (widgetPermission == permission)
+            return true;
+    }
+
+    if (m_pendingWidget && m_pendingWidget->m_permission == permission)
+        return true;
+
+    return false;
+}
+
+PermissionWidget *MainWindow::createPermissionWidget(const QWebEnginePermission &permission)
+{
+    if (containsPermission(permission))
+        return nullptr;
+
+    return new PermissionWidget(permission, this);
+}
+
+void MainWindow::loadStoredPermissions()
+{
+    QList<QWebEnginePermission> permissionsList = m_profile->listAllPermissions();
+    for (QWebEnginePermission &permission : permissionsList) {
+        PermissionWidget *widget = createPermissionWidget(permission);
+        if (widget)
+            m_layout->insertWidget(0, widget);
+    }
+}
diff --git a/examples/webenginewidgets/permissionbrowser/mainwindow.h b/examples/webenginewidgets/permissionbrowser/mainwindow.h
new file mode 100644
index 00000000000..ce239be8250
--- /dev/null
+++ b/examples/webenginewidgets/permissionbrowser/mainwindow.h
@@ -0,0 +1,72 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "ui_mainwindow.h"
+#include "ui_permissiondialog.h"
+#include "ui_permissionwidget.h"
+#include <QDialog>
+#include <QMainWindow>
+#include <QWebEngineView>
+#include <QWebEnginePermission>
+
+QT_BEGIN_NAMESPACE
+class QWebEngineView;
+class QWebEngineProfile;
+class QLineEdit;
+QT_END_NAMESPACE
+
+class PermissionDialog : public QDialog, public Ui_PermissionDialog
+{
+    Q_OBJECT
+public:
+    PermissionDialog(const QWebEngineProfile *profile, QWidget *parent = nullptr);
+    QWebEnginePermission permission() const;
+
+private:
+    const QWebEngineProfile *m_profile;
+};
+
+class PermissionWidget : public QWidget, public Ui_PermissionWidget
+{
+    Q_OBJECT
+public:
+    PermissionWidget(const QWebEnginePermission &permission, QWidget *parent = nullptr);
+
+    QWebEnginePermission m_permission;
+
+signals:
+    void permissionModified(PermissionWidget *widget);
+
+private:
+    void updateState();
+};
+
+class MainWindow : public QMainWindow, public Ui_MainWindow
+{
+    Q_OBJECT
+public:
+    explicit MainWindow(const QUrl &url);
+    ~MainWindow();
+
+private slots:
+    void handlePermissionRequested(QWebEnginePermission permission);
+    void handleUrlChanged(const QUrl &url);
+
+    void handlePermissionModified(PermissionWidget *widget);
+    void handleDeleteAllClicked();
+    void handleNewClicked();
+    void handleRefreshClicked();
+    void handleBackClicked();
+    void handleForwardClicked();
+    void handlePolicyComboBoxIndexChanged(int);
+
+private:
+    bool containsPermission(const QWebEnginePermission &permission);
+    PermissionWidget *createPermissionWidget(const QWebEnginePermission &permission);
+    void loadStoredPermissions();
+
+    QVBoxLayout *m_layout;
+    QWebEngineProfile *m_profile;
+    QWebEngineView *m_webview;
+    PermissionWidget *m_pendingWidget;
+};
diff --git a/examples/webenginewidgets/permissionbrowser/mainwindow.ui b/examples/webenginewidgets/permissionbrowser/mainwindow.ui
new file mode 100644
index 00000000000..2cef35130ab
--- /dev/null
+++ b/examples/webenginewidgets/permissionbrowser/mainwindow.ui
@@ -0,0 +1,341 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>1600</width>
+    <height>900</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Permission Browser</string>
+  </property>
+  <widget class="QWidget" name="centralWidget">
+   <layout class="QHBoxLayout" name="horizontalLayout_2">
+    <item>
+     <widget class="QFrame" name="m_frame">
+      <property name="frameShape">
+       <enum>QFrame::StyledPanel</enum>
+      </property>
+      <property name="frameShadow">
+       <enum>QFrame::Raised</enum>
+      </property>
+      <layout class="QVBoxLayout" name="verticalLayout" stretch="0">
+       <item>
+        <widget class="QWidget" name="widget" native="true">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <layout class="QHBoxLayout" name="horizontalLayout">
+          <property name="leftMargin">
+           <number>0</number>
+          </property>
+          <property name="topMargin">
+           <number>0</number>
+          </property>
+          <property name="rightMargin">
+           <number>0</number>
+          </property>
+          <property name="bottomMargin">
+           <number>0</number>
+          </property>
+          <item>
+           <widget class="QPushButton" name="m_backButton">
+            <property name="text">
+             <string/>
+            </property>
+            <property name="icon">
+             <iconset resource="permissionbrowser.qrc">
+              <normaloff>:3rdparty/go-previous.png</normaloff>:3rdparty/go-previous.png</iconset>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="m_forwardButton">
+            <property name="text">
+             <string/>
+            </property>
+            <property name="icon">
+             <iconset resource="permissionbrowser.qrc">
+              <normaloff>:3rdparty/go-next.png</normaloff>:3rdparty/go-next.png</iconset>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="m_refreshButton">
+            <property name="text">
+             <string/>
+            </property>
+            <property name="icon">
+             <iconset resource="permissionbrowser.qrc">
+              <normaloff>:3rdparty/view-refresh.png</normaloff>:3rdparty/view-refresh.png</iconset>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QLineEdit" name="m_urlLineEdit"/>
+          </item>
+         </layout>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </item>
+    <item>
+     <widget class="QFrame" name="frame_2">
+      <property name="maximumSize">
+       <size>
+        <width>336</width>
+        <height>16777215</height>
+       </size>
+      </property>
+      <property name="frameShape">
+       <enum>QFrame::StyledPanel</enum>
+      </property>
+      <property name="frameShadow">
+       <enum>QFrame::Raised</enum>
+      </property>
+      <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0,0,0,1,0,0,0">
+       <item>
+        <widget class="QWidget" name="widget_2" native="true">
+         <layout class="QHBoxLayout" name="horizontalLayout_3">
+          <property name="leftMargin">
+           <number>0</number>
+          </property>
+          <property name="topMargin">
+           <number>0</number>
+          </property>
+          <property name="rightMargin">
+           <number>0</number>
+          </property>
+          <property name="bottomMargin">
+           <number>0</number>
+          </property>
+          <item>
+           <widget class="QLabel" name="label">
+            <property name="text">
+             <string>Permissions:</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <spacer name="horizontalSpacer">
+            <property name="orientation">
+             <enum>Qt::Horizontal</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>87</width>
+              <height>20</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+          <item>
+           <widget class="QPushButton" name="m_newButton">
+            <property name="text">
+             <string>New</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="m_deleteAllButton">
+            <property name="text">
+             <string>Delete All</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <widget class="QWidget" name="widget_3" native="true">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <layout class="QHBoxLayout" name="horizontalLayout_6">
+          <property name="leftMargin">
+           <number>0</number>
+          </property>
+          <property name="topMargin">
+           <number>0</number>
+          </property>
+          <property name="rightMargin">
+           <number>0</number>
+          </property>
+          <property name="bottomMargin">
+           <number>0</number>
+          </property>
+          <item>
+           <widget class="QLabel" name="label_2">
+            <property name="text">
+             <string>Persistence policy:</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <spacer name="horizontalSpacer_2">
+            <property name="orientation">
+             <enum>Qt::Horizontal</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>40</width>
+              <height>20</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+          <item>
+           <widget class="QComboBox" name="m_policyComboBox">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="minimumSize">
+             <size>
+              <width>0</width>
+              <height>25</height>
+             </size>
+            </property>
+            <property name="currentIndex">
+             <number>0</number>
+            </property>
+            <property name="sizeAdjustPolicy">
+             <enum>QComboBox::AdjustToContents</enum>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <widget class="Line" name="line">
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>10</height>
+          </size>
+         </property>
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="label_3">
+         <property name="text">
+          <string>Stored permissions</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QScrollArea" name="m_storedScrollArea">
+         <property name="minimumSize">
+          <size>
+           <width>320</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>320</width>
+           <height>16777215</height>
+          </size>
+         </property>
+         <property name="frameShape">
+          <enum>QFrame::StyledPanel</enum>
+         </property>
+         <property name="lineWidth">
+          <number>2</number>
+         </property>
+         <property name="verticalScrollBarPolicy">
+          <enum>Qt::ScrollBarAlwaysOff</enum>
+         </property>
+         <property name="horizontalScrollBarPolicy">
+          <enum>Qt::ScrollBarAlwaysOff</enum>
+         </property>
+         <property name="widgetResizable">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="Line" name="line_2">
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>10</height>
+          </size>
+         </property>
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="label_4">
+         <property name="text">
+          <string>Pending permission</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QFrame" name="m_pendingFrame">
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>104</height>
+          </size>
+         </property>
+         <property name="frameShape">
+          <enum>QFrame::StyledPanel</enum>
+         </property>
+         <property name="frameShadow">
+          <enum>QFrame::Sunken</enum>
+         </property>
+         <property name="lineWidth">
+          <number>2</number>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources>
+  <include location="permissionbrowser.qrc"/>
+ </resources>
+ <connections>
+  <connection>
+   <sender>m_urlLineEdit</sender>
+   <signal>returnPressed()</signal>
+   <receiver>m_refreshButton</receiver>
+   <slot>click()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>509</x>
+     <y>28</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>1024</x>
+     <y>27</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/examples/webenginewidgets/permissionbrowser/permissionbrowser.exe.manifest b/examples/webenginewidgets/permissionbrowser/permissionbrowser.exe.manifest
new file mode 100644
index 00000000000..acc40177692
--- /dev/null
+++ b/examples/webenginewidgets/permissionbrowser/permissionbrowser.exe.manifest
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <application>
+    <!--The ID below indicates application support for Windows Vista -->
+    <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
+    <!--The ID below indicates application support for Windows 7 -->
+    <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+    <!--The ID below indicates application support for Windows 8 -->
+    <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+    <!--The ID below indicates application support for Windows 8.1 -->
+    <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+    <!--The ID below indicates application support for Windows 10/11 -->
+    <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+    </application>
+</compatibility>
+</assembly>
diff --git a/examples/webenginewidgets/permissionbrowser/permissionbrowser.qrc b/examples/webenginewidgets/permissionbrowser/permissionbrowser.qrc
new file mode 100644
index 00000000000..80a7d20f254
--- /dev/null
+++ b/examples/webenginewidgets/permissionbrowser/permissionbrowser.qrc
@@ -0,0 +1,7 @@
+<RCC>
+    <qresource prefix="/">
+        <file alias="view-refresh.png">resources/3rdparty/view-refresh.png</file>
+        <file alias="go-next.png">resources/3rdparty/go-next.png</file>
+        <file alias="go-previous.png">resources/3rdparty/go-previous.png</file>
+    </qresource>
+</RCC>
diff --git a/examples/webenginewidgets/permissionbrowser/permissiondialog.ui b/examples/webenginewidgets/permissionbrowser/permissiondialog.ui
new file mode 100644
index 00000000000..430df2d58b4
--- /dev/null
+++ b/examples/webenginewidgets/permissionbrowser/permissiondialog.ui
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PermissionDialog</class>
+ <widget class="QDialog" name="PermissionDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>110</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Permission</string>
+  </property>
+  <layout class="QFormLayout" name="formLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>Origin</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="QLineEdit" name="m_originLineEdit">
+     <property name="readOnly">
+      <bool>false</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="label_2">
+     <property name="text">
+      <string>Permission type</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="1">
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="m_addButton">
+       <property name="enabled">
+        <bool>true</bool>
+       </property>
+       <property name="text">
+        <string>Add</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="m_cancelButton">
+       <property name="text">
+        <string>Cancel</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item row="1" column="1">
+    <widget class="QComboBox" name="m_permissionTypeComboBox"/>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>m_originLineEdit</tabstop>
+  <tabstop>m_addButton</tabstop>
+  <tabstop>m_cancelButton</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>m_cancelButton</sender>
+   <signal>clicked()</signal>
+   <receiver>PermissionDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>349</x>
+     <y>89</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>334</x>
+     <y>67</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>m_addButton</sender>
+   <signal>clicked()</signal>
+   <receiver>PermissionDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>264</x>
+     <y>88</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>218</x>
+     <y>68</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/examples/webenginewidgets/permissionbrowser/permissionwidget.ui b/examples/webenginewidgets/permissionbrowser/permissionwidget.ui
new file mode 100644
index 00000000000..0a09da5d3f0
--- /dev/null
+++ b/examples/webenginewidgets/permissionbrowser/permissionwidget.ui
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PermissionWidget</class>
+ <widget class="QWidget" name="PermissionWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>300</width>
+    <height>104</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>300</width>
+    <height>0</height>
+   </size>
+  </property>
+  <property name="maximumSize">
+   <size>
+    <width>310</width>
+    <height>16777215</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
+   <item>
+    <layout class="QFormLayout" name="formLayout">
+     <property name="sizeConstraint">
+      <enum>QLayout::SetDefaultConstraint</enum>
+     </property>
+     <property name="formAlignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+     </property>
+     <property name="verticalSpacing">
+      <number>10</number>
+     </property>
+     <item row="0" column="0">
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>Origin:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QLabel" name="m_originLabel">
+       <property name="text">
+        <string>Empty</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Type:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QLabel" name="m_typeLabel">
+       <property name="text">
+        <string>Empty</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="0">
+      <widget class="QLabel" name="label_3">
+       <property name="text">
+        <string>State:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QLabel" name="m_stateLabel">
+       <property name="text">
+        <string>Empty</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout">
+     <item>
+      <widget class="QPushButton" name="m_grantButton">
+       <property name="text">
+        <string>Grant</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="m_denyButton">
+       <property name="text">
+        <string>Deny</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="m_deleteButton">
+       <property name="text">
+        <string>Delete</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/examples/webenginewidgets/permissionbrowser/resources/3rdparty/COPYING b/examples/webenginewidgets/permissionbrowser/resources/3rdparty/COPYING
new file mode 100644
index 00000000000..220881da67d
--- /dev/null
+++ b/examples/webenginewidgets/permissionbrowser/resources/3rdparty/COPYING
@@ -0,0 +1 @@
+The icons in this repository are herefore released into the Public Domain.
diff --git a/examples/webenginewidgets/permissionbrowser/resources/3rdparty/REUSE.toml b/examples/webenginewidgets/permissionbrowser/resources/3rdparty/REUSE.toml
new file mode 100644
index 00000000000..62a59c992ac
--- /dev/null
+++ b/examples/webenginewidgets/permissionbrowser/resources/3rdparty/REUSE.toml
@@ -0,0 +1,15 @@
+version = 1
+
+[[annotations]]
+path = "*.png"
+precedence = "closest"
+SPDX-FileCopyrightText = ["Ulisse Perusin <uli.peru@gmail.com>",
+                          "Steven Garrity <sgarrity@silverorange.com>",
+                          "Lapo Calamandrei <calamandrei@gmail.com>",
+                          "Ryan Collier <rcollier@novell.com>",
+                          "Rodney Dawes <dobey@novell.com>",
+                          "Andreas Nilsson <nisses.mail@home.se>",
+                          "Tuomas Kuosmanen <tigert@tigert.com>",
+                          "Garrett LeSage <garrett@novell.com>",
+                          "Jakub Steiner <jimmac@novell.com>"]
+SPDX-License-Identifier = "LicenseRef-Tango-Icons-Public-Domain"
diff --git a/examples/webenginewidgets/permissionbrowser/resources/3rdparty/go-next.png b/examples/webenginewidgets/permissionbrowser/resources/3rdparty/go-next.png
new file mode 100644
index 00000000000..a68e2db7753
Binary files /dev/null and b/examples/webenginewidgets/permissionbrowser/resources/3rdparty/go-next.png differ
diff --git a/examples/webenginewidgets/permissionbrowser/resources/3rdparty/go-previous.png b/examples/webenginewidgets/permissionbrowser/resources/3rdparty/go-previous.png
new file mode 100644
index 00000000000..c37bc0414c2
Binary files /dev/null and b/examples/webenginewidgets/permissionbrowser/resources/3rdparty/go-previous.png differ
diff --git a/examples/webenginewidgets/permissionbrowser/resources/3rdparty/qt_attribution.json b/examples/webenginewidgets/permissionbrowser/resources/3rdparty/qt_attribution.json
new file mode 100644
index 00000000000..63fe372c21c
--- /dev/null
+++ b/examples/webenginewidgets/permissionbrowser/resources/3rdparty/qt_attribution.json
@@ -0,0 +1,23 @@
+{
+    "Id": "permissionbrowser-tango",
+    "Name": "Tango Icon Library",
+    "QDocModule": "qtwebengine",
+    "QtUsage": "Used in WebEngine Permission Browser example.",
+
+    "QtParts": [ "examples" ],
+    "Description": "Selected icons from the Tango Icon Library",
+    "Homepage": "http://tango.freedesktop.org/Tango_Icon_Library",
+    "Version": "0.8.90",
+    "DownloadLocation": "http://tango.freedesktop.org/releases/tango-icon-theme-0.8.90.tar.gz",
+    "LicenseId": "LicenseRef-Tango-Icons-Public-Domain",
+    "License": "Public Domain",
+    "Copyright": ["Ulisse Perusin <uli.peru@gmail.com>",
+    "Steven Garrity <sgarrity@silverorange.com>",
+    "Lapo Calamandrei <calamandrei@gmail.com>",
+    "Ryan Collier <rcollier@novell.com>",
+    "Rodney Dawes <dobey@novell.com>",
+    "Andreas Nilsson <nisses.mail@home.se>",
+    "Tuomas Kuosmanen <tigert@tigert.com>",
+    "Garrett LeSage <garrett@novell.com>",
+    "Jakub Steiner <jimmac@novell.com>"]
+}
diff --git a/examples/webenginewidgets/permissionbrowser/resources/3rdparty/view-refresh.png b/examples/webenginewidgets/permissionbrowser/resources/3rdparty/view-refresh.png
new file mode 100644
index 00000000000..cab4d02c756
Binary files /dev/null and b/examples/webenginewidgets/permissionbrowser/resources/3rdparty/view-refresh.png differ
diff --git a/examples/webenginewidgets/permissionbrowser/resources/AppLogoColor.png b/examples/webenginewidgets/permissionbrowser/resources/AppLogoColor.png
new file mode 100644
index 00000000000..2a49717828b
Binary files /dev/null and b/examples/webenginewidgets/permissionbrowser/resources/AppLogoColor.png differ
diff --git a/examples/webenginewidgets/permissionbrowser/resources/landing.html b/examples/webenginewidgets/permissionbrowser/resources/landing.html
new file mode 100644
index 00000000000..5d921671fcb
--- /dev/null
+++ b/examples/webenginewidgets/permissionbrowser/resources/landing.html
@@ -0,0 +1,88 @@
+<!doctype html>
+<html lang="en">
+    <head>
+        <meta charset="utf-8">
+        <title>Permissions example page</title>
+        <script>
+            function copyText() {
+                navigator.clipboard.writeText(document.getElementById("copyTextInput").value);
+            }
+
+            function pasteText() {
+                navigator.clipboard.readText()
+                    .then((text) => (
+                        document.getElementById("pasteTextInput").value = text
+                    ))
+                    .catch((err) => (
+                        document.getElementById("pasteTextInput").value = "<permission denied>"
+                    ));
+            }
+
+            function showNotification() {
+                new Notification("Example notification", {
+                    body: "Notification contents",
+                    tag: "notifTag",
+                    lang: "",
+                    dir: "auto",
+                    icon: "./3rdparty/go-next.png"
+                });
+            }
+
+            function tryShowNotification() {
+                if (Notification.permission !== "granted") {
+                    Notification.requestPermission()
+                        .then((permission) => {
+                            if (permission === "granted") { showNotification() }
+
+                        })
+                        .catch((err) => (
+                            document.getElementById("pasteTextInput").value = err
+                        ));
+                } else {
+                    showNotification();
+                }
+            }
+
+            function listFonts() {
+                var fontSelect = document.getElementById("fontSelect");
+                var i, length = fontSelect.options.length - 1;
+                for (i = length; i >= 0; i--) {
+                    fontSelect.remove(i);
+                }
+
+                window.queryLocalFonts()
+                    .then((fontsList) => {
+                        for (const font of fontsList) {
+                            fontSelect.add(new Option(text = font.fullName));
+                        }
+                    })
+                    .catch()
+            }
+        </script>
+    </head>
+    <body>
+        <h1>Permission Browser Example</h1>
+        <div>
+            <h2>Clipboard</h2>
+            <div>
+                <button onclick="copyText()">Copy text</button>
+                <input id="copyTextInput" value="Example text"></input>
+            </div>
+            <div>
+                <button onclick="pasteText()">Paste text</button>
+                <input id="pasteTextInput" placeholder="Text will be pasted here"></input>
+            </div>
+        </div>
+        <div>
+            <h2>Notifications</h2>
+            <div>
+                <button onclick="tryShowNotification()">Show notification</button>
+            </div>
+        </div>
+        <div>
+            <h2>Local fonts</h2>
+            <button onclick="listFonts()">List fonts</button>
+            <select id="fontSelect"></select>
+        </div>
+    </body>
+</html>
diff --git a/examples/webenginewidgets/push-notifications/main.cpp b/examples/webenginewidgets/push-notifications/main.cpp
index 18a862182b9..16465317dfc 100644
--- a/examples/webenginewidgets/push-notifications/main.cpp
+++ b/examples/webenginewidgets/push-notifications/main.cpp
@@ -20,13 +20,12 @@ int main(int argc, char *argv[])
     QWebEngineView view(profile.data());
     auto popup = new NotificationPopup(&view);
 
-    QObject::connect(view.page(), &QWebEnginePage::featurePermissionRequested,
-                     [&](const QUrl &origin, QWebEnginePage::Feature feature) {
-                         if (feature != QWebEnginePage::Notifications)
+    QObject::connect(view.page(), &QWebEnginePage::permissionRequested,
+                     [&](QWebEnginePermission permission) {
+                         if (permission.permissionType() != QWebEnginePermission::PermissionType::Notifications)
                              return;
 
-                         view.page()->setFeaturePermission(origin, feature,
-                                                           QWebEnginePage::PermissionGrantedByUser);
+                         permission.grant();
                      });
 
     profile->setPushServiceEnabled(true);
diff --git a/examples/webenginewidgets/recipebrowser/assets/3rdparty/REUSE.toml b/examples/webenginewidgets/recipebrowser/assets/3rdparty/REUSE.toml
new file mode 100644
index 00000000000..cfddbb75dd1
--- /dev/null
+++ b/examples/webenginewidgets/recipebrowser/assets/3rdparty/REUSE.toml
@@ -0,0 +1,13 @@
+version = 1
+
+[[annotations]]
+path = "marked.js"
+precedence = "closest"
+SPDX-FileCopyrightText = "Copyright (c) 2011-2018, Christopher Jeffrey"
+SPDX-License-Identifier = "MIT"
+
+[[annotations]]
+path = "markdown.css"
+precedence = "closest"
+SPDX-FileCopyrightText = "Copyright 2011 Kevin Burke Copyright Twitter Inc."
+SPDX-License-Identifier = "Apache-2.0"
diff --git a/examples/webenginewidgets/simplebrowser/browser.cpp b/examples/webenginewidgets/simplebrowser/browser.cpp
index fd68246d0ce..30c7fc98eab 100644
--- a/examples/webenginewidgets/simplebrowser/browser.cpp
+++ b/examples/webenginewidgets/simplebrowser/browser.cpp
@@ -33,6 +33,7 @@ BrowserWindow *Browser::createHiddenWindow(bool offTheRecord)
     }
     auto profile = !offTheRecord ? m_profile.get() : QWebEngineProfile::defaultProfile();
     auto mainWindow = new BrowserWindow(this, profile, false);
+    profile->setPersistentPermissionsPolicy(QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime);
     m_windows.append(mainWindow);
     QObject::connect(mainWindow, &QObject::destroyed, [this, mainWindow]() {
         m_windows.removeOne(mainWindow);
diff --git a/examples/webenginewidgets/simplebrowser/data/3rdparty/REUSE.toml b/examples/webenginewidgets/simplebrowser/data/3rdparty/REUSE.toml
new file mode 100644
index 00000000000..62a59c992ac
--- /dev/null
+++ b/examples/webenginewidgets/simplebrowser/data/3rdparty/REUSE.toml
@@ -0,0 +1,15 @@
+version = 1
+
+[[annotations]]
+path = "*.png"
+precedence = "closest"
+SPDX-FileCopyrightText = ["Ulisse Perusin <uli.peru@gmail.com>",
+                          "Steven Garrity <sgarrity@silverorange.com>",
+                          "Lapo Calamandrei <calamandrei@gmail.com>",
+                          "Ryan Collier <rcollier@novell.com>",
+                          "Rodney Dawes <dobey@novell.com>",
+                          "Andreas Nilsson <nisses.mail@home.se>",
+                          "Tuomas Kuosmanen <tigert@tigert.com>",
+                          "Garrett LeSage <garrett@novell.com>",
+                          "Jakub Steiner <jimmac@novell.com>"]
+SPDX-License-Identifier = "LicenseRef-Tango-Icons-Public-Domain"
diff --git a/examples/webenginewidgets/simplebrowser/data/3rdparty/qt_attribution.json b/examples/webenginewidgets/simplebrowser/data/3rdparty/qt_attribution.json
index fbc96416ee1..301e933eb8e 100644
--- a/examples/webenginewidgets/simplebrowser/data/3rdparty/qt_attribution.json
+++ b/examples/webenginewidgets/simplebrowser/data/3rdparty/qt_attribution.json
@@ -9,9 +9,8 @@
     "Homepage": "http://tango.freedesktop.org/Tango_Icon_Library",
     "Version": "0.8.90",
     "DownloadLocation": "http://tango.freedesktop.org/releases/tango-icon-theme-0.8.90.tar.gz",
-    "LicenseId": "urn:dje:license:public-domain",
+    "LicenseId": "LicenseRef-Tango-Icons-Public-Domain",
     "License": "Public Domain",
-    "LicenseFile": "COPYING",
     "Copyright": ["Ulisse Perusin <uli.peru@gmail.com>",
     "Steven Garrity <sgarrity@silverorange.com>",
     "Lapo Calamandrei <calamandrei@gmail.com>",
diff --git a/examples/webenginewidgets/simplebrowser/doc/src/simplebrowser.qdoc b/examples/webenginewidgets/simplebrowser/doc/src/simplebrowser.qdoc
index a312da3ad1e..8523ed6896f 100644
--- a/examples/webenginewidgets/simplebrowser/doc/src/simplebrowser.qdoc
+++ b/examples/webenginewidgets/simplebrowser/doc/src/simplebrowser.qdoc
@@ -180,7 +180,7 @@
 
     We add a menu item to the context menu, so that users can right-click to have an inspector
     opened in a new window. We override QWebEngineView::contextMenuEvent and use
-    QWebEnginePage::createStandardContextMenu to create a default QMenu with a
+    QWebEngineView::createStandardContextMenu to create a default QMenu with a
     default list of QWebEnginePage::WebAction actions.
 
     The default name for QWebEnginePage::InspectElement action is
diff --git a/examples/webenginewidgets/simplebrowser/downloadwidget.cpp b/examples/webenginewidgets/simplebrowser/downloadwidget.cpp
index 2fb65e1a8fc..ed12bc6607e 100644
--- a/examples/webenginewidgets/simplebrowser/downloadwidget.cpp
+++ b/examples/webenginewidgets/simplebrowser/downloadwidget.cpp
@@ -51,7 +51,11 @@ void DownloadWidget::updateWidget()
 {
     qreal totalBytes = m_download->totalBytes();
     qreal receivedBytes = m_download->receivedBytes();
-    qreal bytesPerSecond = receivedBytes / m_timeAdded.elapsed() * 1000;
+    qreal bytesPerSecond = 0;  // Initialized to 0 for a reasonable default value
+
+    // Check for division by zero
+    if (m_timeAdded.elapsed() != 0)
+        bytesPerSecond = receivedBytes / m_timeAdded.elapsed() * 1000;
 
     auto state = m_download->state();
     switch (state) {
@@ -59,7 +63,7 @@ void DownloadWidget::updateWidget()
         Q_UNREACHABLE();
         break;
     case QWebEngineDownloadRequest::DownloadInProgress:
-        if (totalBytes >= 0) {
+        if (totalBytes > 0) {
             m_progressBar->setValue(qRound(100 * receivedBytes / totalBytes));
             m_progressBar->setDisabled(false);
             m_progressBar->setFormat(
diff --git a/examples/webenginewidgets/simplebrowser/webview.cpp b/examples/webenginewidgets/simplebrowser/webview.cpp
index 08e044f709e..e7538f948ca 100644
--- a/examples/webenginewidgets/simplebrowser/webview.cpp
+++ b/examples/webenginewidgets/simplebrowser/webview.cpp
@@ -71,27 +71,31 @@ WebView::~WebView()
     m_imageAnimationGroup = nullptr;
 }
 
-inline QString questionForFeature(QWebEnginePage::Feature feature)
+inline QString questionForPermissionType(QWebEnginePermission::PermissionType permissionType)
 {
-    switch (feature) {
-    case QWebEnginePage::Geolocation:
+    switch (permissionType) {
+    case QWebEnginePermission::PermissionType::Geolocation:
         return QObject::tr("Allow %1 to access your location information?");
-    case QWebEnginePage::MediaAudioCapture:
+    case QWebEnginePermission::PermissionType::MediaAudioCapture:
         return QObject::tr("Allow %1 to access your microphone?");
-    case QWebEnginePage::MediaVideoCapture:
+    case QWebEnginePermission::PermissionType::MediaVideoCapture:
         return QObject::tr("Allow %1 to access your webcam?");
-    case QWebEnginePage::MediaAudioVideoCapture:
+    case QWebEnginePermission::PermissionType::MediaAudioVideoCapture:
         return QObject::tr("Allow %1 to access your microphone and webcam?");
-    case QWebEnginePage::MouseLock:
+    case QWebEnginePermission::PermissionType::MouseLock:
         return QObject::tr("Allow %1 to lock your mouse cursor?");
-    case QWebEnginePage::DesktopVideoCapture:
+    case QWebEnginePermission::PermissionType::DesktopVideoCapture:
         return QObject::tr("Allow %1 to capture video of your desktop?");
-    case QWebEnginePage::DesktopAudioVideoCapture:
+    case QWebEnginePermission::PermissionType::DesktopAudioVideoCapture:
         return QObject::tr("Allow %1 to capture audio and video of your desktop?");
-    case QWebEnginePage::Notifications:
+    case QWebEnginePermission::PermissionType::Notifications:
         return QObject::tr("Allow %1 to show notification on your desktop?");
-    case QWebEnginePage::ClipboardReadWrite:
+    case QWebEnginePermission::PermissionType::ClipboardReadWrite:
         return QObject::tr("Allow %1 to read from and write to the clipboard?");
+    case QWebEnginePermission::PermissionType::LocalFontsAccess:
+        return QObject::tr("Allow %1 to access fonts stored on this machine?");
+    case QWebEnginePermission::PermissionType::Unsupported:
+        break;
     }
     return QString();
 }
@@ -103,8 +107,8 @@ void WebView::setPage(WebPage *page)
                    &WebView::handleCertificateError);
         disconnect(oldPage, &QWebEnginePage::authenticationRequired, this,
                    &WebView::handleAuthenticationRequired);
-        disconnect(oldPage, &QWebEnginePage::featurePermissionRequested, this,
-                   &WebView::handleFeaturePermissionRequested);
+        disconnect(oldPage, &QWebEnginePage::permissionRequested, this,
+                   &WebView::handlePermissionRequested);
         disconnect(oldPage, &QWebEnginePage::proxyAuthenticationRequired, this,
                    &WebView::handleProxyAuthenticationRequired);
         disconnect(oldPage, &QWebEnginePage::registerProtocolHandlerRequested, this,
@@ -124,8 +128,8 @@ void WebView::setPage(WebPage *page)
     connect(page, &WebPage::createCertificateErrorDialog, this, &WebView::handleCertificateError);
     connect(page, &QWebEnginePage::authenticationRequired, this,
             &WebView::handleAuthenticationRequired);
-    connect(page, &QWebEnginePage::featurePermissionRequested, this,
-            &WebView::handleFeaturePermissionRequested);
+    connect(page, &QWebEnginePage::permissionRequested, this,
+            &WebView::handlePermissionRequested);
     connect(page, &QWebEnginePage::proxyAuthenticationRequired, this,
             &WebView::handleProxyAuthenticationRequired);
     connect(page, &QWebEnginePage::registerProtocolHandlerRequested, this,
@@ -227,29 +231,29 @@ void WebView::contextMenuEvent(QContextMenuEvent *event)
     disableImageAnimation->setCheckable(true);
     m_imageAnimationGroup->addAction(disableImageAnimation);
     connect(disableImageAnimation, &QAction::triggered, [this]() {
-        handleImageAnimationPolicyChange(QWebEngineSettings::DisallowImageAnimation);
+        handleImageAnimationPolicyChange(QWebEngineSettings::ImageAnimationPolicy::Disallow);
     });
     QAction *allowImageAnimationOnce =
             editImageAnimation->addAction(tr("Allow animated images, but only once"));
     allowImageAnimationOnce->setCheckable(true);
     m_imageAnimationGroup->addAction(allowImageAnimationOnce);
     connect(allowImageAnimationOnce, &QAction::triggered,
-            [this]() { handleImageAnimationPolicyChange(QWebEngineSettings::AnimateImageOnce); });
+            [this]() { handleImageAnimationPolicyChange(QWebEngineSettings::ImageAnimationPolicy::AnimateOnce); });
     QAction *allowImageAnimation = editImageAnimation->addAction(tr("Allow all animated images"));
     allowImageAnimation->setCheckable(true);
     m_imageAnimationGroup->addAction(allowImageAnimation);
     connect(allowImageAnimation, &QAction::triggered, [this]() {
-        handleImageAnimationPolicyChange(QWebEngineSettings::AllowImageAnimation);
+        handleImageAnimationPolicyChange(QWebEngineSettings::ImageAnimationPolicy::Allow);
     });
 
     switch (page()->settings()->imageAnimationPolicy()) {
-    case QWebEngineSettings::AllowImageAnimation:
+    case QWebEngineSettings::ImageAnimationPolicy::Allow:
         allowImageAnimation->setChecked(true);
         break;
-    case QWebEngineSettings::AnimateImageOnce:
+    case QWebEngineSettings::ImageAnimationPolicy::AnimateOnce:
         allowImageAnimationOnce->setChecked(true);
         break;
-    case QWebEngineSettings::DisallowImageAnimation:
+    case QWebEngineSettings::ImageAnimationPolicy::Disallow:
         disableImageAnimation->setChecked(true);
         break;
     default:
@@ -309,17 +313,14 @@ void WebView::handleAuthenticationRequired(const QUrl &requestUrl, QAuthenticato
     }
 }
 
-void WebView::handleFeaturePermissionRequested(const QUrl &securityOrigin,
-                                               QWebEnginePage::Feature feature)
+void WebView::handlePermissionRequested(QWebEnginePermission permission)
 {
     QString title = tr("Permission Request");
-    QString question = questionForFeature(feature).arg(securityOrigin.host());
+    QString question = questionForPermissionType(permission.permissionType()).arg(permission.origin().host());
     if (!question.isEmpty() && QMessageBox::question(window(), title, question) == QMessageBox::Yes)
-        page()->setFeaturePermission(securityOrigin, feature,
-                                     QWebEnginePage::PermissionGrantedByUser);
+        permission.grant();
     else
-        page()->setFeaturePermission(securityOrigin, feature,
-                                     QWebEnginePage::PermissionDeniedByUser);
+        permission.deny();
 }
 
 void WebView::handleProxyAuthenticationRequired(const QUrl &, QAuthenticator *auth,
diff --git a/examples/webenginewidgets/simplebrowser/webview.h b/examples/webenginewidgets/simplebrowser/webview.h
index c7e7f394c1b..d652fbdc9ac 100644
--- a/examples/webenginewidgets/simplebrowser/webview.h
+++ b/examples/webenginewidgets/simplebrowser/webview.h
@@ -14,6 +14,7 @@
 #include <QWebEngineRegisterProtocolHandlerRequest>
 #include <QWebEngineWebAuthUxRequest>
 #include <QWebEngineSettings>
+#include <QWebEnginePermission>
 #include <QActionGroup>
 
 class WebPage;
@@ -43,8 +44,7 @@ class WebView : public QWebEngineView
 private slots:
     void handleCertificateError(QWebEngineCertificateError error);
     void handleAuthenticationRequired(const QUrl &requestUrl, QAuthenticator *auth);
-    void handleFeaturePermissionRequested(const QUrl &securityOrigin,
-                                          QWebEnginePage::Feature feature);
+    void handlePermissionRequested(QWebEnginePermission permission);
     void handleProxyAuthenticationRequired(const QUrl &requestUrl, QAuthenticator *auth,
                                            const QString &proxyHost);
     void handleRegisterProtocolHandlerRequested(QWebEngineRegisterProtocolHandlerRequest request);
diff --git a/licenseRule.json b/licenseRule.json
new file mode 100644
index 00000000000..4ae4a4e8453
--- /dev/null
+++ b/licenseRule.json
@@ -0,0 +1,161 @@
+[
+    {
+        "comment": ["file_pattern_ending: strings matched against the end of a file name.",
+                     "location keys: regular expression matched against the beginning of",
+                     "the file path (relative to the git submodule root).",
+                     "spdx: list of SPDX-License-Expression's allowed in the matching files.",
+                     "-------------------------------------------------------",
+                     "Files with the following endings are Build System licensed,",
+                     "unless they are examples",
+                     "Files with other endings can also be build system files"
+                     ],
+        "file_pattern_ending": ["CMakeLists.txt", ".cmake", ".pro", ".pri", ".prf",
+                     "configure", "configure.bat", "cmake.in", "plist.in", "CMakeLists.txt.in",
+                     ".clang-format", ".plist", ".qrc", "BLACKLIST",
+                     ".tag", ".cmake.conf", ".yaml", "coin/qt-installer-package-config.json",
+                     "BUILD.root.gn.in", "BUILD.toolchain.gn.in", "BUILD.msvc.toolchain.gn.in",
+                     "BUILD.clang-cl.toolchain.gn.in", ".cfg", "coin.nodes"],
+        "location": {
+            "": {
+                "comment": "Default",
+                "file type": "build system",
+                "spdx": ["BSD-3-Clause"]
+            },
+            "(.*)(examples/|snippets/)": {
+                "comment": "Example takes precedence",
+                "file type": "examples and snippets",
+                "spdx": ["LicenseRef-Qt-Commercial OR BSD-3-Clause"]
+            }
+        }
+    },
+    {
+        "comments": ["Files with the following endings are infrastructure licensed"],
+        "file_pattern_ending": [".gitattributes", ".gitignore", ".gitmodules", ".gitreview",
+                                "clang-format", "licenseRule.json", "REUSE.toml"],
+        "location":{
+            "": {
+                "comment": "Default",
+                "file type": "infrastructure",
+                "spdx": ["LicenseRef-Qt-Commercial OR BSD-3-Clause"]
+            }
+        }
+    },
+    {
+        "comments": ["Files with the following endings are Tool licensed,",
+                      "unless they are examples.",
+                      "Files with other endings can also be tool files."],
+        "file_pattern_ending": [".sh", ".py", ".pl", ".bat", ".ps1"],
+        "location":{
+            "": {
+                "comment": "Default",
+                "file type": "tools and utils",
+                "spdx": ["LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0"]
+            },
+            "(.*)(examples/|snippets/)": {
+                "comment": "Example takes precedence",
+                "file type": "examples and snippets",
+                "spdx": ["LicenseRef-Qt-Commercial OR BSD-3-Clause"]
+            }
+        }
+    },
+    {
+        "comment": "Files with the following endings are Documentation licensed.",
+        "file_pattern_ending": [".qdoc", ".qdoc.in", ".qdocinc" , ".qdocconf", "README", "qt_attribution.json",
+                                 "config_help.txt"],
+        "location":{
+            "": {
+                "comment": "",
+                "file type": "documentation",
+                "spdx": ["LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only"]
+            }
+        }
+    },
+    {
+        "comment": ["All other files",
+                     "The licensing is defined only by the file location in the Qt module repository.",
+                     "NO <file_pattern_ending> key for this case!",
+                     "This needs to be the last entry of the file."],
+        "location": {
+            "": {
+                "comment": "Default",
+                "file type": "module and plugin",
+                "spdx": ["LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only"]
+            },
+            "dist/": {
+                "comment": "Default",
+                "file type": "documentation",
+                "spdx": ["LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only"]
+            },
+            "src/": {
+                "comment": "Default",
+                "file type": "module and plugin",
+                "spdx": ["LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only"]
+            },
+            "tests/": {
+                "comment": "Default",
+                "file type": "test",
+                "spdx": ["LicenseRef-Qt-Commercial OR GPL-3.0-only"]
+            },
+            "(.*)(examples/|snippets/)": {
+                "comment": "Default",
+                "file type": "examples and snippets",
+                "spdx": ["LicenseRef-Qt-Commercial OR BSD-3-Clause"]
+            },
+            "(.*|examples).*doc/images/": {
+                "comment": "Default",
+                "file type": "documentation",
+                "spdx": ["LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only"]
+            },
+            "src/host/config\\.tests/": {
+                "comment": "Default",
+                "file type": "build system",
+                "spdx": ["BSD-3-Clause"]
+            },
+            "src/core/clipboard_util_win.cpp": {
+                "comment": "Partially from Chromium",
+                "file type": "module and plugin",
+                "spdx": ["BSD-3-Clause"]
+            },
+            "src/core/common/qt_messages.*": {
+                "comment": "Partially from Chromium",
+                "file type": "module and plugin",
+                "spdx": ["BSD-3-Clause"]
+            },
+            "src/core/tools/qwebengine_convert_dict/main.cpp": {
+                "comment": "Partially from Chromium",
+                "file type": "module and plugin",
+                "spdx": ["BSD-3-Clause"]
+            },
+            "tests/auto/widgets/qwebengine(view|script|page|history)/tst_qwebengine.*.cpp": {
+                "comment": "exception",
+                "file type": "test",
+                "spdx": ["LGPL-2.0-or-later"]
+            },
+            "tests/auto/widgets/accessibility/tst_accessibility.cpp": {
+                "comment": "exception",
+                "file type": "test",
+                "spdx": ["LGPL-2.0-or-later"]
+            },
+            "tests/auto/core/qwebengine(settings|frame)/tst_qwebengine.*.cpp": {
+                "comment": "exception",
+                "file type": "test",
+                "spdx": ["LGPL-2.0-or-later"]
+            },
+            "examples/webenginewidgets/contentmanipulation/jquery.min.js": {
+                "comment": "",
+                "file type": "3rd party",
+                "spdx": ["MIT"]
+            },
+            "src/core/ozone/(ozone_extra.gni|BUILD.gn)": {
+                "comment": "Chrominum",
+                "file type": "3rd party",
+                "spdx": ["BSD-3-Clause"]
+            },
+            "CHROMIUM_VERSION": {
+                "comment": "Chrominum",
+                "file type": "3rd party",
+                "spdx": ["CC0-1.0"]
+            }
+        }
+    }
+]
diff --git a/src/3rdparty b/src/3rdparty
index f3033f5aa9b..1cacccf93f1 160000
--- a/src/3rdparty
+++ b/src/3rdparty
@@ -1 +1 @@
-Subproject commit f3033f5aa9be1e98096c55972166d0be6cb64920
+Subproject commit 1cacccf93f18d0f7267989001c509a5196b25bf7
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0084697f21f..789f15bc630 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -63,6 +63,10 @@ cmake_minimum_required(VERSION ${QT_SUPPORTED_MIN_CMAKE_VERSION_FOR_BUILDING_WEB
 #  MODULES
 ##
 
+if(QT_FEATURE_qtwebengine_build)
+    # Use implicit PROJECT_NAME.
+    qt_internal_sbom_begin_qt_repo_project()
+endif()
 if(QT_FEATURE_qtwebengine_core_build)
    add_subdirectory(core)
    add_subdirectory(process)
@@ -73,8 +77,12 @@ endif()
 if(QT_FEATURE_qtwebengine_quick_build)
    add_subdirectory(webenginequick)
 endif()
+if(QT_FEATURE_qtwebengine_build)
+    qt_internal_sbom_end_qt_repo_project()
+endif()
 
 if(QT_FEATURE_qtpdf_build)
+   qt_internal_sbom_begin_qt_repo_project(SBOM_PROJECT_NAME QtPdf)
    add_subdirectory(pdf)
    # keep log order, pdf build after webengine
    if(QT_FEATURE_qtwebengine_core_build)
@@ -98,6 +106,13 @@ endif()
 #  NINJA PROJECT
 ##
 
+# No SBOM information will be generated for any targets created past this point in the file
+# or any add_subdirectory calls. For the external project GN, we'll have to come up with some
+# additional API.
+if(QT_FEATURE_qtpdf_build)
+    qt_internal_sbom_end_qt_repo_project()
+endif()
+
 set(installDir ${PROJECT_BINARY_DIR}/install)
 
 if(NOT Ninja_FOUND)
@@ -129,20 +144,11 @@ if(CMAKE_CROSSCOMPILING AND NOT Gn_FOUND)
 endif()
 
 if(NOT Gn_FOUND)
-    externalproject_add(gn
+    qt_webengine_externalproject_add(gn
         SOURCE_DIR  ${CMAKE_CURRENT_LIST_DIR}/gn
         BINARY_DIR  ${CMAKE_CURRENT_BINARY_DIR}/gn
         INSTALL_DIR ${installDir}
-        PREFIX      gn
-        USES_TERMINAL_BUILD TRUE
         EXCLUDE_FROM_ALL TRUE
-        CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release
-                   -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-                   -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-                   -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
-                   -DCMAKE_PREFIX_PATH:PATH=<INSTALL_DIR>
-                   -DWEBENGINE_ROOT_BUILD_DIR=${PROJECT_BINARY_DIR}
-                   -DQT_ALLOW_SYMLINK_IN_PATHS=${QT_ALLOW_SYMLINK_IN_PATHS}
     )
     if(QT_FEATURE_qtwebengine_core_build)
         add_dependencies(run_core_GnReady gn)
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 8ba77607b79..c64b2cf78b8 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -98,7 +98,6 @@ foreach(arch ${archs})
                 color_chooser_qt.cpp color_chooser_qt.h
                 common/qt_messages.cpp common/qt_messages.h
                 compositor/compositor.cpp compositor/compositor.h
-                compositor/content_gpu_client_qt.cpp compositor/content_gpu_client_qt.h
                 compositor/display_overrides.cpp
                 compositor/display_software_output_surface.cpp compositor/display_software_output_surface.h
                 compositor/native_skia_output_device.cpp compositor/native_skia_output_device.h
@@ -148,14 +147,6 @@ foreach(arch ${archs})
                 net/url_request_custom_job_proxy.cpp net/url_request_custom_job_proxy.h
                 net/version_ui_qt.cpp net/version_ui_qt.h
                 net/webui_controller_factory_qt.cpp net/webui_controller_factory_qt.h
-                ozone/gl_context_qt.cpp ozone/gl_context_qt.h
-                ozone/gl_ozone_egl_qt.cpp ozone/gl_ozone_egl_qt.h
-                ozone/gl_share_context_qt.cpp ozone/gl_share_context_qt.h
-                ozone/gl_surface_egl_qt.cpp ozone/gl_surface_egl_qt.h
-                ozone/gl_surface_qt.cpp ozone/gl_surface_qt.h
-                ozone/gl_surface_wgl_qt.cpp ozone/gl_surface_wgl_qt.h
-                ozone/platform_window_qt.cpp ozone/platform_window_qt.h
-                ozone/surface_factory_qt.cpp ozone/surface_factory_qt.h
                 permission_manager_qt.cpp permission_manager_qt.h
                 pdf_util_qt.cpp pdf_util_qt.h
                 platform_notification_service_qt.cpp platform_notification_service_qt.h
@@ -204,11 +195,38 @@ foreach(arch ${archs})
                 authenticator_request_dialog_controller.cpp authenticator_request_dialog_controller.h authenticator_request_dialog_controller_p.h
         )
 
+        extend_gn_target(${buildGn} CONDITION config STREQUAL "Debug"
+            DEFINES
+                QT_USE_DEBUG_RESOURCES
+        )
+
         extend_gn_target(${buildGn} CONDITION QT_FEATURE_accessibility
             SOURCES
                 accessibility_activation_observer.cpp accessibility_activation_observer.h
         )
 
+        extend_gn_target(${buildGn} CONDITION LINUX OR WIN32
+            SOURCES
+                compositor/content_gpu_client_qt.cpp compositor/content_gpu_client_qt.h
+                ozone/gl_context_qt.cpp ozone/gl_context_qt.h
+                ozone/gl_share_context_qt.cpp ozone/gl_share_context_qt.h
+                ozone/gl_surface_qt.cpp ozone/gl_surface_qt.h
+        )
+
+        extend_gn_target(${buildGn} CONDITION WIN32
+            SOURCES
+                ozone/gl_surface_wgl_qt.cpp ozone/gl_surface_wgl_qt.h
+        )
+
+        extend_gn_target(${buildGn} CONDITION LINUX
+            SOURCES
+                ozone/gl_ozone_angle_qt.cpp ozone/gl_ozone_angle_qt.h
+                ozone/gl_ozone_egl_qt.cpp ozone/gl_ozone_egl_qt.h
+                ozone/gl_surface_egl_qt.cpp ozone/gl_surface_egl_qt.h
+                ozone/platform_window_qt.cpp ozone/platform_window_qt.h
+                ozone/surface_factory_qt.cpp ozone/surface_factory_qt.h
+        )
+
         extend_gn_target(${buildGn} CONDITION QT_FEATURE_webengine_ozone_x11
             SOURCES
                 ozone/gl_ozone_glx_qt.cpp ozone/gl_ozone_glx_qt.h
@@ -343,13 +361,16 @@ foreach(arch ${archs})
             optimize_webui=false
             strip_absolute_paths_from_debug_symbols=false
             pdf_use_skia=true
+            use_cups=false
             use_dawn=false
             skia_use_dawn=false
             devtools_fast_bundle=false
             devtools_skip_typecheck=false
             use_static_angle=true
             use_perfetto_client_library=false
-            trial_comparison_cert_verifier_supported=false
+            content_enable_legacy_ipc=true
+            enable_extensions_legacy_ipc=true
+            enable_bound_session_credentials=false
         )
         extend_gn_list(gnArgArg
             ARGS use_v8_context_snapshot v8_use_external_startup_data
@@ -359,10 +380,6 @@ foreach(arch ${archs})
             ARGS enable_printing enable_basic_printing enable_print_preview enable_pdf
             CONDITION QT_FEATURE_webengine_printing_and_pdf
         )
-        extend_gn_list(gnArgArg
-            ARGS use_cups
-            CONDITION QT_FEATURE_webengine_printing_and_pdf AND NOT WIN32
-        )
         extend_gn_list(gnArgArg
             ARGS enable_plugins
             CONDITION QT_FEATURE_webengine_printing_and_pdf OR
@@ -405,7 +422,7 @@ foreach(arch ${archs})
             CONDITION QT_FEATURE_webengine_kerberos
         )
         extend_gn_list(gnArgArg
-            ARGS proprietary_codecs
+            ARGS proprietary_codecs rtc_use_h264
             CONDITION QT_FEATURE_webengine_proprietary_codecs
         )
         if(QT_FEATURE_webengine_proprietary_codecs)
@@ -426,9 +443,9 @@ foreach(arch ${archs})
 
         if(LINUX)
             list(APPEND gnArgArg
+                angle_enable_gl=true
                 use_gtk=false # GTK toolkit bindings
                 use_qt=false # Qt5 toolkit bindings
-                use_cups=false
                 use_gio=false
                 use_bundled_fontconfig=false
                 use_glib=false
@@ -509,10 +526,6 @@ foreach(arch ${archs})
                 ARGS rtc_use_x11
                 CONDITION QT_FEATURE_webengine_ozone_x11 AND QT_FEATURE_webengine_webrtc
             )
-            extend_gn_list(gnArgArg
-                ARGS use_vaapi_x11
-                CONDITION QT_FEATURE_webengine_ozone_x11 AND QT_FEATURE_webengine_vaapi
-            )
 
             if(QT_FEATURE_webengine_kerberos)
                 list(APPEND gnArgArg
@@ -577,21 +590,24 @@ target_include_directories(WebEngineCore PRIVATE
     ${buildDir}/$<CONFIG>/${arch}/gen
     ${buildDir}/$<CONFIG>/${arch}/gen/third_party/perfetto
     ${buildDir}/$<CONFIG>/${arch}/gen/third_party/perfetto/build_config
+    ${buildDir}/$<CONFIG>/${arch}/gen/base/allocator/partition_allocator/src
 )
 
 set(stamps QtWebEngineCore.stamp)
+set(dataStampOption "")
 if(QT_FEATURE_webengine_v8_context_snapshot)
-    set(dataStamp obj/tools/v8_context_snapshot/v8_context_snapshot.stamp)
+    set(dataStampOption NINJA_DATA_STAMP obj/tools/v8_context_snapshot/v8_context_snapshot.stamp)
 endif()
 
 add_gn_build_artifacts_to_target(
     CMAKE_TARGET WebEngineCore
     NINJA_TARGET QtWebEngineCore
     MODULE core
+    DEPENDS WebEngineCore_sync_headers
     BUILDDIR ${buildDir}
     COMPLETE_STATIC FALSE
     NINJA_STAMP QtWebEngineCore.stamp
-    NINJA_DATA_STAMP ${dataStamp}
+    ${dataStampOption}
 )
 add_dependencies(WebEngineCore run_core_NinjaDone)
 if(COIN_BUG_699)
@@ -632,7 +648,9 @@ if(QT_FEATURE_webengine_spellchecker AND NOT CMAKE_CROSSCOMPILING)
     target_include_directories(${dict_target_name} PRIVATE
         ../3rdparty/chromium
         ../3rdparty/chromium/third_party/boringssl/src/include
+        ../3rdparty/chromium/base/allocator/partition_allocator/src
         ${buildDir}/$<CONFIG>/${arch}/gen
+        ${buildDir}/$<CONFIG>/${arch}/gen/base/allocator/partition_allocator/src
     )
     add_gn_build_artifacts_to_target(
         CMAKE_TARGET ${dict_target_name}
diff --git a/src/core/accessibility_activation_observer.cpp b/src/core/accessibility_activation_observer.cpp
index 4f25a35ffed..4e90b9b3b38 100644
--- a/src/core/accessibility_activation_observer.cpp
+++ b/src/core/accessibility_activation_observer.cpp
@@ -5,6 +5,8 @@
 
 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
 
+using namespace Qt::StringLiterals;
+
 namespace QtWebEngineCore {
 
 namespace {
@@ -14,9 +16,8 @@ bool isAccessibilityEnabled() {
     // QTWEBENGINE_ENABLE_LINUX_ACCESSIBILITY environment variable to 0. For details,
     // see QTBUG-59922.
 #ifdef Q_OS_LINUX
-    static bool accessibility_enabled
-            = qEnvironmentVariable("QTWEBENGINE_ENABLE_LINUX_ACCESSIBILITY", QLatin1String("1"))
-                == QLatin1String("1");
+    static bool accessibility_enabled =
+            qEnvironmentVariable("QTWEBENGINE_ENABLE_LINUX_ACCESSIBILITY", u"1"_s) == "1"_L1;
 #else
     const bool accessibility_enabled = true;
 #endif
diff --git a/src/core/accessibility_tree_formatter_qt.cpp b/src/core/accessibility_tree_formatter_qt.cpp
index 3a3b30cb4a6..bea929c6dd1 100644
--- a/src/core/accessibility_tree_formatter_qt.cpp
+++ b/src/core/accessibility_tree_formatter_qt.cpp
@@ -144,12 +144,10 @@ void AccessibilityTreeFormatterQt::AddProperties(const BrowserAccessibility &nod
 
 std::string AccessibilityTreeFormatterQt::ProcessTreeForOutput(const base::Value::Dict &node) const
 {
-    std::string error_value;
     if (auto error_value = node.FindString("error"))
         return *error_value;
 
     std::string line;
-    std::string role_value;
     if (auto role_value = node.FindString("role"))
         WriteAttribute(true, base::StringPrintf("%s", role_value->c_str()), &line);
 
@@ -166,9 +164,7 @@ std::string AccessibilityTreeFormatterQt::ProcessTreeForOutput(const base::Value
     if (auto description_value = node.FindString("description"))
         WriteAttribute(false, base::StringPrintf("description='%s'", description_value->c_str()), &line);
 
-    int id_value;
-    if (auto maybe_id = node.FindInt("id"))
-        id_value = *maybe_id;
+    int id_value = node.FindInt("id").value_or(0);
     WriteAttribute(false, base::StringPrintf("id=%d", id_value), &line);
 
     return line + "\n";
diff --git a/src/core/api/CMakeLists.txt b/src/core/api/CMakeLists.txt
index c06f5e4ce24..25028c701ec 100644
--- a/src/core/api/CMakeLists.txt
+++ b/src/core/api/CMakeLists.txt
@@ -29,6 +29,7 @@ qt_internal_add_module(WebEngineCore
         qwebenginenewwindowrequest.cpp qwebenginenewwindowrequest.h qwebenginenewwindowrequest_p.h
         qwebenginenotification.cpp qwebenginenotification.h
         qwebenginepage.cpp qwebenginepage.h qwebenginepage_p.h
+        qwebenginepermission.cpp qwebenginepermission.h qwebenginepermission_p.h
         qwebengineprofile.cpp qwebengineprofile.h qwebengineprofile_p.h
         qwebenginequotarequest.cpp qwebenginequotarequest.h
         qwebengineregisterprotocolhandlerrequest.cpp qwebengineregisterprotocolhandlerrequest.h
@@ -44,12 +45,14 @@ qt_internal_add_module(WebEngineCore
         qwebenginewebauthuxrequest.cpp qwebenginewebauthuxrequest.h qwebenginewebauthuxrequest_p.h
     DEFINES
         BUILDING_CHROMIUM
+        TOOLKIT_QT
     INCLUDE_DIRECTORIES
         ../
         ../../3rdparty/chromium
         ../../3rdparty/chromium/third_party/abseil-cpp
         ../../3rdparty/chromium/third_party/perfetto/include
         ../../3rdparty/chromium/third_party/boringssl/src/include
+        ../../3rdparty/chromium/base/allocator/partition_allocator/src
     LIBRARIES
         Qt::CorePrivate
         Qt::GuiPrivate
@@ -62,14 +65,22 @@ qt_internal_add_module(WebEngineCore
     EXTRA_CMAKE_FILES
         "${CMAKE_CURRENT_LIST_DIR}/${INSTALL_CMAKE_NAMESPACE}WebEngineCoreDeploySupport.cmake"
     NO_GENERATE_CPP_EXPORTS
+    SBOM_INCOMPLETE_3RD_PARTY_DEPENDENCIES
 )
 
+if(MSVC)
+    qt_internal_skip_intel_cet_hardening(WebEngineCore)
+endif()
+
 set_target_properties(WebEngineCore PROPERTIES QTWEBENGINEPROCESS_NAME ${qtWebEngineProcessName})
 set_target_properties(WebEngineCore PROPERTIES CXX_STANDARD 20)
-
 # Chromium included headers are not clean
 qt_skip_warnings_are_errors(WebEngineCore)
 
+if(MSVC AND NOT CLANG)
+    target_compile_options(WebEngineCore PRIVATE "/Zc:preprocessor")
+endif()
+
 if(CLANG OR GCC)
     target_compile_options(WebEngineCore PRIVATE
         "-Wno-unused-parameter"
@@ -125,10 +136,10 @@ set(localeList am ar bg bn ca cs da de el en-GB en-US es-419 es et fa fi fil fr
     gu he hi hr hu id it ja kn ko lt lv ml mr ms nb nl pl pt-BR pt-PT ro ru sk
     sl sr sv sw ta te th tr uk vi zh-CN zh-TW)
 
-set(resourceList qtwebengine_resources.pak
-    qtwebengine_resources_100p.pak
-    qtwebengine_resources_200p.pak
-    qtwebengine_devtools_resources.pak)
+set(resourceList qtwebengine_resources
+    qtwebengine_resources_100p
+    qtwebengine_resources_200p
+    qtwebengine_devtools_resources)
 
 set(stamps ${buildDir}/${config}/${arch}/QtWebEngineCore.stamp)
 
@@ -165,9 +176,16 @@ foreach(loc ${localeList})
     list(APPEND localeFiles ${locSourcePath})
 endforeach()
 
-foreach(res ${resourceList})
-    get_filename_component(resSourcePath ${buildDir}/${config}/${arch}/${res} ${path_mode})
-    list(APPEND resourceFiles ${resSourcePath})
+foreach(config ${configs})
+    if("${config}" STREQUAL "Debug")
+        set(ext_debug ".debug")
+    else()
+        unset(ext_debug)
+    endif()
+    foreach(res ${resourceList})
+        get_filename_component(resSourcePath ${buildDir}/${config}/${arch}/${res}${ext_debug}.pak ${path_mode})
+        list(APPEND resourceFiles ${resSourcePath})
+    endforeach()
 endforeach()
 
 
diff --git a/src/core/api/Qt6WebEngineCoreDeploySupport.cmake b/src/core/api/Qt6WebEngineCoreDeploySupport.cmake
index e67eb212b3d..2ff3d9e70de 100644
--- a/src/core/api/Qt6WebEngineCoreDeploySupport.cmake
+++ b/src/core/api/Qt6WebEngineCoreDeploySupport.cmake
@@ -74,37 +74,77 @@ function(_qt_internal_deploy_webenginecore_binary)
         endif()
     endforeach()
 
-    set(install_destination "${QT_DEPLOY_PREFIX}/")
+    # CMAKE_INSTALL_PREFIX does not contain $ENV{DESTDIR}, whereas QT_DEPLOY_PREFIX does.
+    # The install_ variant should be used in file(INSTALL) to avoid double DESTDIR in paths.
+    # Other code should reference the destdir_ variant instead.
+    set(install_destination "${CMAKE_INSTALL_PREFIX}/")
+    set(destdir_destination "${QT_DEPLOY_PREFIX}/")
+
     if(__QT_DEPLOY_SYSTEM_NAME STREQUAL "Windows")
         string(APPEND install_destination "${QT_DEPLOY_BIN_DIR}")
+        string(APPEND destdir_destination "${QT_DEPLOY_BIN_DIR}")
     else()
         string(APPEND install_destination "${QT_DEPLOY_LIBEXEC_DIR}")
+        string(APPEND destdir_destination "${QT_DEPLOY_LIBEXEC_DIR}")
     endif()
     file(INSTALL "${process_path}" DESTINATION "${install_destination}")
 
     get_filename_component(process_file_name "${process_path}" NAME)
     if(CMAKE_VERSION GREATER_EQUAL "3.19")
-        file(CHMOD "${install_destination}/${process_file_name}"
+        file(CHMOD "${destdir_destination}/${process_file_name}"
             PERMISSIONS OWNER_EXECUTE OWNER_READ OWNER_WRITE
                         GROUP_EXECUTE GROUP_READ
                         WORLD_EXECUTE WORLD_READ
         )
     else()
         execute_process(
-            COMMAND chmod 0755 "${install_destination}/${process_file_name}"
+            COMMAND chmod 0755 "${destdir_destination}/${process_file_name}"
+        )
+    endif()
+
+    if(NOT __QT_DEPLOY_SYSTEM_NAME STREQUAL "Windows")
+        qt6_deploy_qt_conf("${install_destination}/qt.conf"
+            PREFIX "${QT_DEPLOY_PREFIX}"
+            BIN_DIR "${QT_DEPLOY_BIN_DIR}"
+            LIB_DIR "${QT_DEPLOY_LIB_DIR}"
+            PLUGINS_DIR "${QT_DEPLOY_PLUGINS_DIR}"
+            QML_DIR "${QT_DEPLOY_QML_DIR}"
+        )
+    endif()
+    # Checking for __QT_DEPLOY_MUST_ADJUST_PLUGINS_RPATH is a bit strange because this is not a
+    # plugin, but it gives a single common way to detect when the rpath adjustment must be done,
+    # because the lib dir is different than the original Qt configured one.
+    if(__QT_DEPLOY_MUST_ADJUST_PLUGINS_RPATH)
+        _qt_internal_get_rpath_origin(rpath_origin)
+        file(RELATIVE_PATH rel_lib_dir "${destdir_destination}"
+            "${QT_DEPLOY_PREFIX}/${QT_DEPLOY_LIB_DIR}")
+        _qt_internal_set_rpath(
+            FILE "${destdir_destination}/${process_file_name}"
+            NEW_RPATH "${rpath_origin}/${rel_lib_dir}"
         )
     endif()
 endfunction()
 
 function(_qt_internal_deploy_webenginecore_data)
+    # used for deployment on Linux, using the CMake deployment API.
     _qt_internal_webenginecore_status_message("Deploying the WebEngineCore data files")
-    set(data_files
-        icudtl.dat
-        qtwebengine_devtools_resources.pak
-        qtwebengine_resources.pak
-        qtwebengine_resources_100p.pak
-        qtwebengine_resources_200p.pak
-    )
+    if(__QT_DEPLOY_ACTIVE_CONFIG STREQUAL "Debug")
+        set(data_files
+            icudtl.dat
+            qtwebengine_devtools_resources.debug.pak
+            qtwebengine_resources.debug.pak
+            qtwebengine_resources_100p.debug.pak
+            qtwebengine_resources_200p.debug.pak
+        )
+    else()
+        set(data_files
+            icudtl.dat
+            qtwebengine_devtools_resources.pak
+            qtwebengine_resources.pak
+            qtwebengine_resources_100p.pak
+            qtwebengine_resources_200p.pak
+        )
+    endif()
     get_filename_component(resources_dir "resources" ABSOLUTE
         BASE_DIR "${__QT_DEPLOY_QT_INSTALL_PREFIX}/${__QT_DEPLOY_QT_INSTALL_DATA}"
     )
@@ -117,8 +157,9 @@ function(_qt_internal_deploy_webenginecore_data)
         list(APPEND data_files "${snapshot_file}")
     endif()
 
+    # See comment above why we use CMAKE_INSTALL_PREFIX instead of QT_DEPLOY_PREFIX.
     get_filename_component(install_destination "${QT_DEPLOY_WEBENGINECORE_RESOURCES_DIR}" ABSOLUTE
-        BASE_DIR "${QT_DEPLOY_PREFIX}/${QT_DEPLOY_DATA_DIR}"
+        BASE_DIR "${CMAKE_INSTALL_PREFIX}/${QT_DEPLOY_DATA_DIR}"
     )
     foreach(data_file IN LISTS data_files)
         file(INSTALL "${resources_dir}/${data_file}" DESTINATION "${install_destination}")
@@ -163,8 +204,9 @@ function(_qt_internal_deploy_webenginecore_translations)
     get_filename_component(locales_dir "qtwebengine_locales" ABSOLUTE
         BASE_DIR "${__QT_DEPLOY_QT_INSTALL_PREFIX}/${__QT_DEPLOY_QT_INSTALL_TRANSLATIONS}"
     )
+    # See comment above why we use CMAKE_INSTALL_PREFIX instead of QT_DEPLOY_PREFIX.
     get_filename_component(install_destination "qtwebengine_locales" ABSOLUTE
-        BASE_DIR "${QT_DEPLOY_PREFIX}/${QT_DEPLOY_TRANSLATIONS_DIR}"
+        BASE_DIR "${CMAKE_INSTALL_PREFIX}/${QT_DEPLOY_TRANSLATIONS_DIR}"
     )
     file(GLOB locale_files "${locales_dir}/*.pak")
     foreach(locale_file IN LISTS locale_files)
diff --git a/src/core/api/configure.cmake b/src/core/api/configure.cmake
index f8488c05718..9557e2eb1e6 100644
--- a/src/core/api/configure.cmake
+++ b/src/core/api/configure.cmake
@@ -18,8 +18,6 @@ if(NOT QT_CONFIGURE_RUNNING)
             pkg_check_modules(GIO gio-2.0)
         endif()
     endif()
-    find_package(Cups)
-
     find_package(Qt6 ${PROJECT_VERSION} CONFIG QUIET
         OPTIONAL_COMPONENTS Positioning WebChannel PrintSupport)
 endif()
@@ -87,8 +85,7 @@ qt_feature("webengine-printing-and-pdf" PRIVATE
     LABEL "Printing and PDF"
     PURPOSE "Provides printing and output to PDF."
     AUTODETECT NOT QT_FEATURE_webengine_embedded_build
-    CONDITION TARGET Qt::PrintSupport AND QT_FEATURE_printer AND
-    (CUPS_FOUND OR WIN32)
+    CONDITION TARGET Qt::PrintSupport AND QT_FEATURE_printer
 )
 qt_feature("webengine-pepper-plugins" PRIVATE
     LABEL "Pepper Plugins"
@@ -163,6 +160,7 @@ qt_feature("webengine-vulkan" PRIVATE
     LABEL "Vulkan support"
     PURPOSE "Enables support for Vulkan rendering"
     CONDITION QT_FEATURE_vulkan
+    DISABLE MACOS
 )
 qt_feature("webengine-vaapi" PRIVATE
     SECTION "WebEngine"
diff --git a/src/core/api/qtwebenginecoreglobal.cpp b/src/core/api/qtwebenginecoreglobal.cpp
index d5112ccb342..1d02dac9174 100644
--- a/src/core/api/qtwebenginecoreglobal.cpp
+++ b/src/core/api/qtwebenginecoreglobal.cpp
@@ -36,6 +36,42 @@ static void deleteShareContext()
     shareContext = 0;
 }
 
+static void ensureShareContext()
+{
+    // No need to override the shared context if QApplication already set one (e.g with Qt::AA_ShareOpenGLContexts).
+    if (qt_gl_global_share_context())
+        return;
+
+    QCoreApplication *app = QCoreApplication::instance();
+    if (!app) {
+        qFatal("QtWebEngineQuick::initialize() but no core application instance.");
+        return;
+    }
+
+    // Bail out silently if the user did not construct a QGuiApplication.
+    if (!qobject_cast<QGuiApplication *>(app))
+        return;
+
+    if (app->thread() != QThread::currentThread()) {
+        qFatal("QtWebEngineQuick::initialize() must be called from the Qt gui thread.");
+        return;
+    }
+
+    if (shareContext)
+        return;
+
+    shareContext = new QOpenGLContext;
+    QSurfaceFormat format = QSurfaceFormat::defaultFormat();
+
+    shareContext->setFormat(format);
+    shareContext->create();
+    qAddPostRoutine(deleteShareContext);
+    qt_gl_set_global_share_context(shareContext);
+
+    // Classes like QOpenGLWidget check for the attribute
+    app->setAttribute(Qt::AA_ShareOpenGLContexts);
+}
+
 #endif
 // ### Qt 6: unify this logic and Qt::AA_ShareOpenGLContexts.
 // QtWebEngineQuick::initialize was introduced first and meant to be called
@@ -56,39 +92,10 @@ Q_WEBENGINECORE_EXPORT void initialize()
 #endif
     )
         QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
-    // No need to override the shared context if QApplication already set one (e.g with Qt::AA_ShareOpenGLContexts).
-    if (!qt_gl_global_share_context()) {
-
-        QCoreApplication *app = QCoreApplication::instance();
-        if (!app) {
-            qFatal("QtWebEngineQuick::initialize() but no core application instance.");
-            return;
-        }
-
-        // Bail out silently if the user did not construct a QGuiApplication.
-        if (!qobject_cast<QGuiApplication *>(app))
-            return;
-
-        if (app->thread() != QThread::currentThread()) {
-            qFatal("QtWebEngineQuick::initialize() must be called from the Qt gui thread.");
-            return;
-        }
-
-        if (shareContext)
-            return;
-
-        shareContext = new QOpenGLContext;
-        QSurfaceFormat format = QSurfaceFormat::defaultFormat();
-
-        shareContext->setFormat(format);
-        shareContext->create();
-        qAddPostRoutine(deleteShareContext);
-        qt_gl_set_global_share_context(shareContext);
-
-        // Classes like QOpenGLWidget check for the attribute
-        app->setAttribute(Qt::AA_ShareOpenGLContexts);
-    }
 
+    // ensure we have shared OpenGL context
+    if (QQuickWindow::graphicsApi() != QSGRendererInterface::Direct3D11)
+        ensureShareContext();
 #endif // QT_CONFIG(opengl) && !defined(Q_OS_MACOS)
 }
 
diff --git a/src/core/api/qtwebenginecoreglobal_p.h b/src/core/api/qtwebenginecoreglobal_p.h
index a63568c8acd..0b8f40e562c 100644
--- a/src/core/api/qtwebenginecoreglobal_p.h
+++ b/src/core/api/qtwebenginecoreglobal_p.h
@@ -18,6 +18,7 @@
 #include <QtWebEngineCore/qtwebenginecoreglobal.h>
 #include <QtCore/private/qglobal_p.h>
 #include <QtWebEngineCore/private/qtwebenginecore-config_p.h>
+#include <QLoggingCategory>
 
 #ifdef QT_WEBENGINE_LOGGING
 #define QT_NOT_YET_IMPLEMENTED fprintf(stderr, "function %s not implemented! - %s:%d\n", __func__, __FILE__, __LINE__);
diff --git a/src/core/api/qwebenginecertificateerror.cpp b/src/core/api/qwebenginecertificateerror.cpp
index 85c5d512752..60ec3c95b19 100644
--- a/src/core/api/qwebenginecertificateerror.cpp
+++ b/src/core/api/qwebenginecertificateerror.cpp
@@ -42,6 +42,7 @@ QWebEngineCertificateError::~QWebEngineCertificateError() = default;
     QSslError::SslError values are not used directly, because the Qt error
     categories cannot be mapped to the Chromium error categories.
 
+    \value Ok There was no actual certificate error.
     \value SslPinnedKeyNotInCertificateChain The certificate did not match the built-in public keys
             pinned for the host name.
     \value CertificateCommonNameInvalid The certificate's common name did not match the host name.
@@ -74,7 +75,9 @@ QWebEngineCertificateError::~QWebEngineCertificateError() = default;
 */
 bool QWebEngineCertificateError::isOverridable() const
 {
-    return d->overridable();
+    if (Q_LIKELY(d))
+        return d->overridable();
+    return false;
 }
 
 /*!
@@ -84,7 +87,9 @@ bool QWebEngineCertificateError::isOverridable() const
 */
 QUrl QWebEngineCertificateError::url() const
 {
-    return d->url();
+    if (Q_LIKELY(d))
+        return d->url();
+    return QUrl();
 }
 
 /*!
@@ -97,7 +102,9 @@ QUrl QWebEngineCertificateError::url() const
 */
 bool QWebEngineCertificateError::isMainFrame() const
 {
-    return d->isMainFrame();
+    if (Q_LIKELY(d))
+        return d->isMainFrame();
+    return false;
 }
 
 /*!
@@ -107,7 +114,9 @@ bool QWebEngineCertificateError::isMainFrame() const
 */
 QWebEngineCertificateError::Type QWebEngineCertificateError::type() const
 {
-    return d->error();
+    if (Q_LIKELY(d))
+        return d->error();
+    return Ok;
 }
 
 /*!
@@ -117,7 +126,9 @@ QWebEngineCertificateError::Type QWebEngineCertificateError::type() const
 */
 QString QWebEngineCertificateError::description() const
 {
-    return d->errorString();
+    if (Q_LIKELY(d))
+        return d->errorString();
+    return QString();
 }
 
 /*!
@@ -135,7 +146,8 @@ QString QWebEngineCertificateError::description() const
 */
 void QWebEngineCertificateError::defer()
 {
-    d->defer();
+    if (Q_LIKELY(d))
+        d->defer();
 }
 
 /*!
@@ -145,7 +157,8 @@ void QWebEngineCertificateError::defer()
 */
 void QWebEngineCertificateError::acceptCertificate()
 {
-    d->ignoreCertificateError();
+    if (Q_LIKELY(d))
+        d->ignoreCertificateError();
 }
 
 /*!
@@ -155,7 +168,8 @@ void QWebEngineCertificateError::acceptCertificate()
 */
 void QWebEngineCertificateError::rejectCertificate()
 {
-    d->rejectCertificate();
+    if (Q_LIKELY(d))
+        d->rejectCertificate();
 }
 
 /*!
@@ -167,7 +181,9 @@ void QWebEngineCertificateError::rejectCertificate()
 */
 QList<QSslCertificate> QWebEngineCertificateError::certificateChain() const
 {
-    return d->certificateChain();
+    if (Q_LIKELY(d))
+        return d->certificateChain();
+    return QList<QSslCertificate>();
 }
 
 QT_END_NAMESPACE
diff --git a/src/core/api/qwebenginecertificateerror.h b/src/core/api/qwebenginecertificateerror.h
index 3eef3dcca58..73a3ac09099 100644
--- a/src/core/api/qwebenginecertificateerror.h
+++ b/src/core/api/qwebenginecertificateerror.h
@@ -27,12 +27,15 @@ class Q_WEBENGINECORE_EXPORT QWebEngineCertificateError
     Q_PROPERTY(bool isMainFrame READ isMainFrame CONSTANT FINAL REVISION(6, 8))
 
 public:
+    QWebEngineCertificateError() = default;
     QWebEngineCertificateError(const QWebEngineCertificateError &other);
     QWebEngineCertificateError &operator=(const QWebEngineCertificateError &other);
     ~QWebEngineCertificateError();
 
     // Keep this identical to NET_ERROR in net_error_list.h, or add mapping layer.
     enum Type {
+        Ok = 0, // No actual error. See net_errors.h for that one
+
         SslPinnedKeyNotInCertificateChain = -150,
         CertificateCommonNameInvalid = -200,
         CertificateDateInvalid = -201,
diff --git a/src/core/api/qwebengineclienthints.cpp b/src/core/api/qwebengineclienthints.cpp
index 907d4ae76d8..ced25b4c91c 100644
--- a/src/core/api/qwebengineclienthints.cpp
+++ b/src/core/api/qwebengineclienthints.cpp
@@ -6,6 +6,7 @@
 #include "profile_adapter.h"
 
 #include <QJsonObject>
+#include <QVariantMap>
 
 QT_BEGIN_NAMESPACE
 
@@ -35,11 +36,15 @@ QT_BEGIN_NAMESPACE
     \sa QWebEngineProfile::clientHints(), QQuickWebEngineProfile::clientHints()
 */
 
+/*! \internal
+ */
 QWebEngineClientHints::QWebEngineClientHints(QtWebEngineCore::ProfileAdapter *profileAdapter)
     : m_profileAdapter(profileAdapter)
 {
 }
 
+/*! \internal
+ */
 QWebEngineClientHints::~QWebEngineClientHints()
 {
 }
@@ -50,6 +55,8 @@ QWebEngineClientHints::~QWebEngineClientHints()
 */
 QString QWebEngineClientHints::arch() const
 {
+    if (!m_profileAdapter)
+        return QString();
     return m_profileAdapter->clientHint(QtWebEngineCore::ProfileAdapter::UAArchitecture).toString();
 }
 
@@ -61,6 +68,8 @@ QString QWebEngineClientHints::arch() const
 */
 QString QWebEngineClientHints::platform() const
 {
+    if (!m_profileAdapter)
+        return QString();
     return m_profileAdapter->clientHint(QtWebEngineCore::ProfileAdapter::UAPlatform).toString();
 }
 
@@ -70,6 +79,8 @@ QString QWebEngineClientHints::platform() const
 */
 QString QWebEngineClientHints::model() const
 {
+    if (!m_profileAdapter)
+        return QString();
     return m_profileAdapter->clientHint(QtWebEngineCore::ProfileAdapter::UAModel).toString();
 }
 
@@ -81,6 +92,8 @@ QString QWebEngineClientHints::model() const
 */
 bool QWebEngineClientHints::isMobile() const
 {
+    if (!m_profileAdapter)
+        return false;
     return m_profileAdapter->clientHint(QtWebEngineCore::ProfileAdapter::UAMobile).toBool();
 }
 
@@ -90,6 +103,8 @@ bool QWebEngineClientHints::isMobile() const
 */
 QString QWebEngineClientHints::fullVersion() const
 {
+    if (!m_profileAdapter)
+        return QString();
     return m_profileAdapter->clientHint(QtWebEngineCore::ProfileAdapter::UAFullVersion).toString();
 }
 
@@ -99,6 +114,8 @@ QString QWebEngineClientHints::fullVersion() const
 */
 QString QWebEngineClientHints::platformVersion() const
 {
+    if (!m_profileAdapter)
+        return QString();
     return m_profileAdapter->clientHint(QtWebEngineCore::ProfileAdapter::UAPlatformVersion).toString();
 }
 
@@ -108,6 +125,8 @@ QString QWebEngineClientHints::platformVersion() const
 */
 QString QWebEngineClientHints::bitness() const
 {
+    if (!m_profileAdapter)
+        return QString();
     return m_profileAdapter->clientHint(QtWebEngineCore::ProfileAdapter::UABitness).toString();
 }
 
@@ -115,15 +134,17 @@ QString QWebEngineClientHints::bitness() const
     \property QWebEngineClientHints::fullVersionList
     The value of the \c{Sec-CH-UA-Full-Version-List} HTTP header and \c{fullVersionList} member of NavigatorUAData in JavaScript.
 
-    It holds brand name and version number pairs in a QHash. The provided values will be automatically extended by the currently used version
+    It holds brand name and version number pairs in a QVariantMap. The provided values will be automatically extended by the currently used version
     of Chromium and a semi-random brand.
 */
-QHash<QString,QString> QWebEngineClientHints::fullVersionList() const
+QVariantMap QWebEngineClientHints::fullVersionList() const
 {
-    QHash<QString, QString> ret;
+    QVariantMap ret;
+    if (!m_profileAdapter)
+        return ret;
     QJsonObject fullVersionList = m_profileAdapter->clientHint(QtWebEngineCore::ProfileAdapter::UAFullVersionList).toJsonObject();
     for (const QString &key : fullVersionList.keys())
-        ret.insert(key, fullVersionList.value(key).toString());
+        ret.insert(key, fullVersionList.value(key).toVariant());
     return ret;
 }
 
@@ -133,54 +154,74 @@ QHash<QString,QString> QWebEngineClientHints::fullVersionList() const
 */
 bool QWebEngineClientHints::isWow64() const
 {
+    if (!m_profileAdapter)
+        return false;
     return m_profileAdapter->clientHint(QtWebEngineCore::ProfileAdapter::UAWOW64).toBool();
 }
 
 void QWebEngineClientHints::setArch(const QString &arch)
 {
+    if (!m_profileAdapter)
+        return;
     m_profileAdapter->setClientHint(QtWebEngineCore::ProfileAdapter::UAArchitecture, QVariant(arch));
 }
 
 void QWebEngineClientHints::setPlatform(const QString &platform)
 {
+    if (!m_profileAdapter)
+        return;
     m_profileAdapter->setClientHint(QtWebEngineCore::ProfileAdapter::UAPlatform, QVariant(platform));
 }
 
 void QWebEngineClientHints::setModel(const QString &model)
 {
+    if (!m_profileAdapter)
+        return;
     m_profileAdapter->setClientHint(QtWebEngineCore::ProfileAdapter::UAModel, QVariant(model));
 }
 
-void QWebEngineClientHints::setIsMobile(const bool mobile)
+void QWebEngineClientHints::setIsMobile(bool mobile)
 {
+    if (!m_profileAdapter)
+        return;
     m_profileAdapter->setClientHint(QtWebEngineCore::ProfileAdapter::UAMobile, QVariant(mobile));
 }
 
 void QWebEngineClientHints::setFullVersion(const QString &fullVerson)
 {
+    if (!m_profileAdapter)
+        return;
     m_profileAdapter->setClientHint(QtWebEngineCore::ProfileAdapter::UAFullVersion, QVariant(fullVerson));
 }
 
 void QWebEngineClientHints::setPlatformVersion(const QString &platformVersion)
 {
+    if (!m_profileAdapter)
+        return;
     m_profileAdapter->setClientHint(QtWebEngineCore::ProfileAdapter::UAPlatformVersion, QVariant(platformVersion));
 }
 
 void QWebEngineClientHints::setBitness(const QString &bitness)
 {
+    if (!m_profileAdapter)
+        return;
     m_profileAdapter->setClientHint(QtWebEngineCore::ProfileAdapter::UABitness, QVariant(bitness));
 }
 
-void QWebEngineClientHints::setFullVersionList(const QHash<QString,QString> &fullVersionList)
+void QWebEngineClientHints::setFullVersionList(const QVariantMap &fullVersionList)
 {
+    if (!m_profileAdapter)
+        return;
     QJsonObject jsonObject;
     for (auto i = fullVersionList.cbegin(), end = fullVersionList.cend(); i != end; ++i)
-        jsonObject.insert(i.key(), QJsonValue(i.value()));
+        jsonObject.insert(i.key(), QJsonValue(i.value().toString()));
     m_profileAdapter->setClientHint(QtWebEngineCore::ProfileAdapter::UAFullVersionList, QVariant(jsonObject));
 }
 
-void QWebEngineClientHints::setIsWow64(const bool wow64)
+void QWebEngineClientHints::setIsWow64(bool wow64)
 {
+    if (!m_profileAdapter)
+        return;
     m_profileAdapter->setClientHint(QtWebEngineCore::ProfileAdapter::UAWOW64, QVariant(wow64));
 }
 
@@ -192,11 +233,15 @@ void QWebEngineClientHints::setIsWow64(const bool wow64)
 */
 bool QWebEngineClientHints::isAllClientHintsEnabled()
 {
+    if (!m_profileAdapter)
+        return true;
     return m_profileAdapter->clientHintsEnabled();
 }
 
 void QWebEngineClientHints::setAllClientHintsEnabled(bool enabled)
 {
+    if (!m_profileAdapter)
+        return;
     m_profileAdapter->setClientHintsEnabled(enabled);
 }
 
@@ -205,6 +250,8 @@ void QWebEngineClientHints::setAllClientHintsEnabled(bool enabled)
 */
 void QWebEngineClientHints::resetAll()
 {
+    if (!m_profileAdapter)
+        return;
     m_profileAdapter->resetClientHints();
 }
 
diff --git a/src/core/api/qwebengineclienthints.h b/src/core/api/qwebengineclienthints.h
index 8956b5cb629..efaebb99e94 100644
--- a/src/core/api/qwebengineclienthints.h
+++ b/src/core/api/qwebengineclienthints.h
@@ -7,7 +7,9 @@
 #include <QtWebEngineCore/qtwebenginecoreglobal.h>
 
 #include <QtCore/qobject.h>
-#include <QtCore/qhash.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qvariantmap.h>
+#include <QtQml/qqmlregistration.h>
 
 namespace QtWebEngineCore {
 class ProfileAdapter;
@@ -15,22 +17,26 @@ class ProfileAdapter;
 
 QT_BEGIN_NAMESPACE
 
-class Q_WEBENGINECORE_EXPORT QWebEngineClientHints
+class Q_WEBENGINECORE_EXPORT QWebEngineClientHints : public QObject
 {
-    Q_GADGET
-    Q_PROPERTY(QString arch READ arch WRITE setArch)
-    Q_PROPERTY(QString platform READ platform WRITE setPlatform)
-    Q_PROPERTY(QString model READ model WRITE setModel)
-    Q_PROPERTY(bool mobile READ isMobile WRITE setIsMobile)
-    Q_PROPERTY(QString fullVersion READ fullVersion WRITE setFullVersion)
-    Q_PROPERTY(QString platformVersion READ platformVersion WRITE setPlatformVersion)
-    Q_PROPERTY(QString bitness READ bitness WRITE setBitness)
-    Q_PROPERTY(QHash<QString,QString> fullVersionList READ fullVersionList WRITE setFullVersionList)
-    Q_PROPERTY(bool wow64 READ isWow64 WRITE setIsWow64)
-
-    Q_PROPERTY(bool isAllClientHintsEnabled READ isAllClientHintsEnabled WRITE setAllClientHintsEnabled)
+    Q_OBJECT
+    Q_PROPERTY(QString arch READ arch WRITE setArch FINAL)
+    Q_PROPERTY(QString platform READ platform WRITE setPlatform FINAL)
+    Q_PROPERTY(QString model READ model WRITE setModel FINAL)
+    Q_PROPERTY(bool mobile READ isMobile WRITE setIsMobile FINAL)
+    Q_PROPERTY(QString fullVersion READ fullVersion WRITE setFullVersion FINAL)
+    Q_PROPERTY(QString platformVersion READ platformVersion WRITE setPlatformVersion FINAL)
+    Q_PROPERTY(QString bitness READ bitness WRITE setBitness FINAL)
+    Q_PROPERTY(QVariantMap fullVersionList READ fullVersionList WRITE setFullVersionList FINAL)
+    Q_PROPERTY(bool wow64 READ isWow64 WRITE setIsWow64 FINAL)
+
+    Q_PROPERTY(bool isAllClientHintsEnabled READ isAllClientHintsEnabled WRITE setAllClientHintsEnabled FINAL)
 
 public:
+    QML_NAMED_ELEMENT(WebEngineClientHints)
+    QML_UNCREATABLE("")
+    QML_ADDED_IN_VERSION(6, 8)
+
     ~QWebEngineClientHints();
 
     QString arch() const;
@@ -40,31 +46,32 @@ class Q_WEBENGINECORE_EXPORT QWebEngineClientHints
     QString fullVersion() const;
     QString platformVersion() const;
     QString bitness() const;
-    QHash<QString,QString> fullVersionList() const;
+    QVariantMap fullVersionList() const;
     bool isWow64() const;
 
     void setArch(const QString &);
     void setPlatform(const QString &);
     void setModel(const QString &);
-    void setIsMobile(const bool);
+    void setIsMobile(bool);
     void setFullVersion(const QString &);
     void setPlatformVersion(const QString &);
     void setBitness(const QString &);
-    void setFullVersionList(const QHash<QString,QString> &);
-    void setIsWow64(const bool);
+    void setFullVersionList(const QVariantMap &);
+    void setIsWow64(bool);
 
     bool isAllClientHintsEnabled();
     void setAllClientHintsEnabled(bool enabled);
 
-    void resetAll();
+    Q_INVOKABLE void resetAll();
 
 private:
     explicit QWebEngineClientHints(QtWebEngineCore::ProfileAdapter *profileAdapter);
+
     Q_DISABLE_COPY(QWebEngineClientHints)
     friend class QWebEngineProfilePrivate;
     friend class QQuickWebEngineProfilePrivate;
 
-    QtWebEngineCore::ProfileAdapter *m_profileAdapter;
+    QPointer<QtWebEngineCore::ProfileAdapter> m_profileAdapter;
 };
 
 QT_END_NAMESPACE
diff --git a/src/core/api/qwebenginedesktopmediarequest.cpp b/src/core/api/qwebenginedesktopmediarequest.cpp
index dae69a68c57..1fdbfa65527 100644
--- a/src/core/api/qwebenginedesktopmediarequest.cpp
+++ b/src/core/api/qwebenginedesktopmediarequest.cpp
@@ -28,11 +28,12 @@ QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QWebEngineDesktopMediaRequestPrivate)
 
     The data model's \e Qt::DisplayRole specifies the name of the source which is the title of a
     window or the number of the display.
-    The model is dynamically updates if the available list of sources has changed e.g a window is
-    opened/closed.
+    The model is dynamically updated if the available list of sources has changed;
+    e.g when a window is opened/closed.
+
+    The signal handler needs to then either call \l selectScreen() or \l selectWindow() to accept
+    the request and start screensharing.
 
-    The signal handler needs to then either call QWebEngineDesktopMediaRequest:selectScreen() or
-    QWebEngineDesktopMediaRequest::selectWindow() to accept the request and start screensharing.
     \sa QWebEnginePage::desktopMediaRequested().
 */
 
@@ -108,7 +109,8 @@ QWebEngineDesktopMediaRequestPrivate::~QWebEngineDesktopMediaRequestPrivate()
 {
     // Keep old behavior, if there were no user action select the primary screen.
     if (!didSelectOrCancel)
-        controller->selectScreen(0);
+        controller->screens()->getSourceCount() > 0 ? controller->selectScreen(0)
+                                                    : controller->cancel();
 }
 
 void QWebEngineDesktopMediaRequestPrivate::selectWindow(const QModelIndex &index)
@@ -174,7 +176,7 @@ QAbstractListModel *QWebEngineDesktopMediaRequest::windowsModel() const
 }
 
 /*!
-    Selects the window on the \a index to be captured.
+    Selects the window at the \a index to be captured.
 
     \sa QWebEngineDesktopMediaRequest::selectScreen()
 */
@@ -184,7 +186,7 @@ void QWebEngineDesktopMediaRequest::selectWindow(const QModelIndex &index) const
 }
 
 /*!
-    Selects the screen on the \a index to be captured.
+    Selects the screen at the \a index to be captured.
 
     \sa QWebEngineDesktopMediaRequest::selectWindow()
 */
diff --git a/src/core/api/qwebenginedownloadrequest.cpp b/src/core/api/qwebenginedownloadrequest.cpp
index cbf46b4481b..f67454134bd 100644
--- a/src/core/api/qwebenginedownloadrequest.cpp
+++ b/src/core/api/qwebenginedownloadrequest.cpp
@@ -9,6 +9,7 @@
 #include "profile_adapter.h"
 #include "web_contents_adapter_client.h"
 
+#include <QDir>
 #include <QFileInfo>
 
 QT_BEGIN_NAMESPACE
@@ -81,11 +82,9 @@ static inline QWebEngineDownloadRequest::DownloadInterruptReason toDownloadInter
     requests, which it does by emitting the
     \l{QWebEngineProfile::downloadRequested}{downloadRequested} signal together
     with a newly created QWebEngineDownloadRequest. The application can then
-    examine this item and decide whether to accept it or not. A signal handler
-    must explicitly call accept() on the item for \QWE to actually start
-    downloading and writing data to disk. If no signal handler calls accept(),
-    then the download request will be automatically rejected and nothing will be
-    written to disk.
+    examine this item and decide whether to accept it or not. When a decision is
+    made, the application must explicitly call accept() or cancel() on the item
+    for \QWE to actually start downloading or rejecting the request.
 
     \note Some properties, such as setting the path and file name where the file
     will be saved (see \l downloadDirectory() and \l downloadFileName()), can
@@ -93,18 +92,10 @@ static inline QWebEngineDownloadRequest::DownloadInterruptReason toDownloadInter
 
     \section2 Object Life Cycle
 
-    All items are guaranteed to be valid during the emission of the
-    \l{QWebEngineProfile::downloadRequested}{downloadRequested} signal. If
-    accept() is \e not called by any signal handler, then the item will be
-    deleted \e immediately after signal emission. This means that the
-    application \b{must not} keep references to rejected download items. It also
-    means the application should not use a queued connection to this signal.
-
-    If accept() \e is called by a signal handler, then the QWebEngineProfile
-    will take ownership of the item. However, it is safe for the application to
-    delete the item at any time, except during the handling of the
-    \l{QWebEngineProfile::downloadRequested}{downloadRequested} signal. The
-    QWebEngineProfile being a long-lived object, it is in fact recommended that
+    In each and every case, the QWebEngineProfile takes the ownership of the item.
+    However, it is safe for the application to delete the item at any time, except
+    during the handling of the \l{QWebEngineProfile::downloadRequested}{downloadRequested}
+    signal. The QWebEngineProfile being a long-lived object, it is in fact recommended that
     the application delete any items it is no longer interested in.
 
     \note Deleting an item will also automatically cancel a download since 5.12.2,
@@ -119,6 +110,12 @@ static inline QWebEngineDownloadRequest::DownloadInterruptReason toDownloadInter
     into a special file format (\l savePageFormat). To check if a download is
     for a file or a web page, use \l isSavePageDownload.
 
+    Web page save requests are accepted automatically and started from
+    \l DownloadInProgress state by convenience reasons. The first directly connected
+    \l{QWebEngineProfile::downloadRequested}{downloadRequested} signal handler
+    can prevent this by calling cancel(), otherwise the save operation will start
+    writing data to the disk.
+
     \sa QWebEngineProfile, QWebEngineProfile::downloadRequested,
     QWebEnginePage::download, QWebEnginePage::save
 */
@@ -137,7 +134,9 @@ void QWebEngineDownloadRequestPrivate::update(const ProfileAdapterClient::Downlo
 {
     Q_Q(QWebEngineDownloadRequest);
 
-    Q_ASSERT(downloadState != QWebEngineDownloadRequest::DownloadRequested);
+    // Don't change download state until users accept/cancel the request
+    if (!answered && downloadState == QWebEngineDownloadRequest::DownloadRequested)
+        return;
 
     if (toDownloadInterruptReason(info.downloadInterruptReason) != interruptReason) {
         interruptReason = toDownloadInterruptReason(info.downloadInterruptReason);
@@ -147,17 +146,13 @@ void QWebEngineDownloadRequestPrivate::update(const ProfileAdapterClient::Downlo
         downloadState = toDownloadState(info.state);
         Q_EMIT q->stateChanged(downloadState);
     }
-
-    if (info.receivedBytes != receivedBytes || info.totalBytes != totalBytes) {
-
-      if (info.receivedBytes != receivedBytes) {
-          receivedBytes = info.receivedBytes;
-          Q_EMIT q->receivedBytesChanged();
-      }
-      if (info.totalBytes != totalBytes) {
-          totalBytes = info.totalBytes;
-          Q_EMIT q->totalBytesChanged();
-      }
+    if (info.receivedBytes != receivedBytes) {
+        receivedBytes = info.receivedBytes;
+        Q_EMIT q->receivedBytesChanged();
+    }
+    if (info.totalBytes != totalBytes) {
+        totalBytes = info.totalBytes;
+        Q_EMIT q->totalBytesChanged();
     }
 
     if (info.done)
@@ -178,6 +173,20 @@ void QWebEngineDownloadRequestPrivate::setFinished()
     Q_EMIT q_ptr->isFinishedChanged();
 }
 
+void QWebEngineDownloadRequestPrivate::answer()
+{
+    if (answered)
+        return;
+
+    if (profileAdapter) {
+        QString path = QDir(downloadDirectory).filePath(downloadFileName);
+        bool accepted = downloadState != QWebEngineDownloadRequest::DownloadCancelled
+                && downloadState != QWebEngineDownloadRequest::DownloadRequested;
+        profileAdapter->acceptDownload(downloadId, accepted, useDownloadTargetCallback, path, savePageFormat);
+        answered = true;
+    }
+}
+
 /*!
     Accepts the current download request, which will start the download.
 
@@ -197,6 +206,7 @@ void QWebEngineDownloadRequest::accept()
 
     d->downloadState = QWebEngineDownloadRequest::DownloadInProgress;
     Q_EMIT stateChanged(d->downloadState);
+    d->answer();
 }
 
 /*!
@@ -239,6 +249,7 @@ void QWebEngineDownloadRequest::cancel()
         Q_EMIT stateChanged(d->downloadState);
         d->setFinished();
     }
+    d->answer();
 }
 
 /*!
@@ -465,7 +476,7 @@ void QWebEngineDownloadRequest::setDownloadDirectory(const QString &directory)
                                                                                  d->suggestedFileName,
                                                                                  d->startTime)).fileName();
         if (d->downloadFileName != newFileName) {
-            d->downloadFileName = newFileName;
+            d->downloadFileName = std::move(newFileName);
             Q_EMIT downloadFileNameChanged();
         }
     }
diff --git a/src/core/api/qwebenginedownloadrequest_p.h b/src/core/api/qwebenginedownloadrequest_p.h
index eef6c9bb52f..e829f94b1b3 100644
--- a/src/core/api/qwebenginedownloadrequest_p.h
+++ b/src/core/api/qwebenginedownloadrequest_p.h
@@ -36,6 +36,7 @@ class Q_WEBENGINECORE_EXPORT QWebEngineDownloadRequestPrivate
 
     void update(const QtWebEngineCore::ProfileAdapterClient::DownloadItemInfo &info);
     void setFinished();
+    void answer();
 
     bool downloadFinished = false;
     quint32 downloadId = -1;
@@ -56,10 +57,14 @@ class Q_WEBENGINECORE_EXPORT QWebEngineDownloadRequestPrivate
     bool isCustomFileName = false;
     qint64 totalBytes = -1;
     qint64 receivedBytes = 0;
+    // The user initiated the download by saving the page
     bool isSavePageDownload = false;
+    // Which type of callback should be called when the request is answered
+    bool useDownloadTargetCallback = true;
     QWebEngineDownloadRequest *q_ptr;
     QPointer<QtWebEngineCore::ProfileAdapter> profileAdapter;
     QtWebEngineCore::WebContentsAdapterClient *adapterClient = nullptr;
+    bool answered = false;
     Q_DECLARE_PUBLIC(QWebEngineDownloadRequest)
 };
 
diff --git a/src/core/api/qwebenginefilesystemaccessrequest.cpp b/src/core/api/qwebenginefilesystemaccessrequest.cpp
index 3f901b671cf..0834468f659 100644
--- a/src/core/api/qwebenginefilesystemaccessrequest.cpp
+++ b/src/core/api/qwebenginefilesystemaccessrequest.cpp
@@ -72,7 +72,8 @@ QWebEngineFileSystemAccessRequest::QWebEngineFileSystemAccessRequest(
 */
 void QWebEngineFileSystemAccessRequest::reject()
 {
-    d_ptr->reject();
+    if (Q_LIKELY(d_ptr))
+        d_ptr->reject();
 }
 
 /*!
@@ -80,7 +81,8 @@ void QWebEngineFileSystemAccessRequest::reject()
 */
 void QWebEngineFileSystemAccessRequest::accept()
 {
-    d_ptr->accept();
+    if (Q_LIKELY(d_ptr))
+        d_ptr->accept();
 }
 
 /*!
@@ -90,7 +92,9 @@ void QWebEngineFileSystemAccessRequest::accept()
 
 QUrl QWebEngineFileSystemAccessRequest::origin() const
 {
-    return d_ptr->origin();
+    if (Q_LIKELY(d_ptr))
+        return d_ptr->origin();
+    return QUrl();
 }
 
 /*!
@@ -100,7 +104,9 @@ QUrl QWebEngineFileSystemAccessRequest::origin() const
 
 QUrl QWebEngineFileSystemAccessRequest::filePath() const
 {
-    return d_ptr->filePath();
+    if (Q_LIKELY(d_ptr))
+        return d_ptr->filePath();
+    return QUrl();
 }
 
 /*!
@@ -109,7 +115,9 @@ QUrl QWebEngineFileSystemAccessRequest::filePath() const
  */
 HandleType QWebEngineFileSystemAccessRequest::handleType() const
 {
-    return d_ptr->handleType();
+    if (Q_LIKELY(d_ptr))
+        return d_ptr->handleType();
+    return File;
 }
 
 /*!
@@ -118,7 +126,9 @@ HandleType QWebEngineFileSystemAccessRequest::handleType() const
  */
 AccessFlags QWebEngineFileSystemAccessRequest::accessFlags() const
 {
-    return d_ptr->accessFlags();
+    if (Q_LIKELY(d_ptr))
+        return d_ptr->accessFlags();
+    return {};
 }
 
 QT_END_NAMESPACE
diff --git a/src/core/api/qwebenginefilesystemaccessrequest.h b/src/core/api/qwebenginefilesystemaccessrequest.h
index 37b1e186111..7707c607c8c 100644
--- a/src/core/api/qwebenginefilesystemaccessrequest.h
+++ b/src/core/api/qwebenginefilesystemaccessrequest.h
@@ -24,6 +24,7 @@ class Q_WEBENGINECORE_EXPORT QWebEngineFileSystemAccessRequest
     Q_PROPERTY(AccessFlags accessFlags READ accessFlags CONSTANT FINAL)
 
 public:
+    QWebEngineFileSystemAccessRequest() = default;
     QWebEngineFileSystemAccessRequest(const QWebEngineFileSystemAccessRequest &other);
     QWebEngineFileSystemAccessRequest &operator=(const QWebEngineFileSystemAccessRequest &other);
     QWebEngineFileSystemAccessRequest(QWebEngineFileSystemAccessRequest &&other) noexcept = default;
diff --git a/src/core/api/qwebengineframe.cpp b/src/core/api/qwebengineframe.cpp
index 1eedc4b9254..6eb628ffabf 100644
--- a/src/core/api/qwebengineframe.cpp
+++ b/src/core/api/qwebengineframe.cpp
@@ -5,12 +5,19 @@
 
 #include "qwebenginescript.h"
 #include <QtQml/qqmlengine.h>
+#include <QtGui/qpagelayout.h>
+#include <QtGui/qpageranges.h>
 
 #include "web_contents_adapter_client.h"
 #include "web_contents_adapter.h"
 
 QT_BEGIN_NAMESPACE
 
+#define LOCK_ADAPTER(adapter_variable, return_value)                                               \
+    auto adapter = m_adapter.lock();                                                               \
+    if (!adapter)                                                                                  \
+    return return_value
+
 /*!
     \class QWebEngineFrame
     \brief The QWebEngineFrame class gives information about and control over a page frame.
@@ -32,8 +39,9 @@ QT_BEGIN_NAMESPACE
 
 /*! \internal
  */
-QWebEngineFrame::QWebEngineFrame(QtWebEngineCore::WebContentsAdapterClient *adapter, quint64 id)
-    : m_adapterClient(adapter), m_id(id)
+QWebEngineFrame::QWebEngineFrame(QWeakPointer<QtWebEngineCore::WebContentsAdapter> adapter,
+                                 quint64 id)
+    : m_adapter(std::move(adapter)), m_id(id)
 {
 }
 
@@ -44,7 +52,8 @@ QWebEngineFrame::QWebEngineFrame(QtWebEngineCore::WebContentsAdapterClient *adap
 */
 bool QWebEngineFrame::isValid() const
 {
-    return m_adapterClient->webContentsAdapter()->hasFrame(m_id);
+    LOCK_ADAPTER(adapter, false);
+    return adapter->hasFrame(m_id);
 }
 
 /*!
@@ -56,7 +65,8 @@ bool QWebEngineFrame::isValid() const
 */
 QString QWebEngineFrame::name() const
 {
-    return m_adapterClient->webContentsAdapter()->frameName(m_id);
+    LOCK_ADAPTER(adapter, QString());
+    return adapter->frameName(m_id);
 }
 
 /*!
@@ -68,7 +78,8 @@ QString QWebEngineFrame::name() const
 */
 QString QWebEngineFrame::htmlName() const
 {
-    return m_adapterClient->webContentsAdapter()->frameHtmlName(m_id);
+    LOCK_ADAPTER(adapter, QString());
+    return adapter->frameHtmlName(m_id);
 }
 
 /*!
@@ -78,9 +89,10 @@ QString QWebEngineFrame::htmlName() const
  */
 QList<QWebEngineFrame> QWebEngineFrame::children() const
 {
+    LOCK_ADAPTER(adapter, {});
     QList<QWebEngineFrame> result;
-    for (auto childId : m_adapterClient->webContentsAdapter()->frameChildren(m_id))
-        result.push_back(QWebEngineFrame{ m_adapterClient, childId });
+    for (auto childId : adapter->frameChildren(m_id))
+        result.push_back(QWebEngineFrame{ m_adapter, childId });
     return result;
 }
 
@@ -91,7 +103,8 @@ QList<QWebEngineFrame> QWebEngineFrame::children() const
  */
 QUrl QWebEngineFrame::url() const
 {
-    return m_adapterClient->webContentsAdapter()->frameUrl(m_id);
+    LOCK_ADAPTER(adapter, QUrl());
+    return adapter->frameUrl(m_id);
 }
 
 /*!
@@ -101,7 +114,17 @@ QUrl QWebEngineFrame::url() const
  */
 QSizeF QWebEngineFrame::size() const
 {
-    return m_adapterClient->webContentsAdapter()->frameSize(m_id);
+    LOCK_ADAPTER(adapter, QSizeF());
+    return adapter->frameSize(m_id);
+}
+
+/*!
+    Returns \c{true} if this object represents the page's main frame; \c{false} otherwise.
+*/
+bool QWebEngineFrame::isMainFrame() const
+{
+    LOCK_ADAPTER(adapter, false);
+    return adapter->mainFrameId() == m_id;
 }
 
 /*! \fn void QWebEngineFrame::runJavaScript(const QString &script, const std::function<void(const QVariant &)> &callback)
@@ -143,7 +166,8 @@ void QWebEngineFrame::runJavaScript(const QString &script,
 void QWebEngineFrame::runJavaScript(const QString &script, quint32 worldId,
                                     const std::function<void(const QVariant &)> &callback)
 {
-    m_adapterClient->runJavaScript(script, worldId, m_id, callback);
+    LOCK_ADAPTER(adapter, );
+    adapter->runJavaScript(script, worldId, m_id, callback);
 }
 
 void QWebEngineFrame::runJavaScript(const QString &script, quint32 worldId)
@@ -159,9 +183,10 @@ void QWebEngineFrame::runJavaScript(const QString &script, const QJSValue &callb
 void QWebEngineFrame::runJavaScript(const QString &script, quint32 worldId,
                                     const QJSValue &callback)
 {
+    LOCK_ADAPTER(adapter, );
     std::function<void(const QVariant &)> wrappedCallback;
     if (!callback.isUndefined()) {
-        const QObject *holdingObject = m_adapterClient->holdingQObject();
+        const QObject *holdingObject = adapter->adapterClient()->holdingQObject();
         wrappedCallback = [holdingObject, callback](const QVariant &result) {
             if (auto engine = qmlEngine(holdingObject)) {
                 QJSValueList args;
@@ -175,6 +200,70 @@ void QWebEngineFrame::runJavaScript(const QString &script, quint32 worldId,
     runJavaScript(script, worldId, wrappedCallback);
 }
 
+/*!
+    Renders the current content of the frame into a PDF document and saves it in the location
+    specified in \a filePath. Printing uses a page size of A4, portrait layout, and includes the
+    full range of pages.
+
+    This method issues an asynchronous request for printing the web page into a PDF and returns
+    immediately. To be informed about the result of the request, connect to the \l
+    QWebEnginePage::pdfPrintingFinished() signal.
+
+    \note The \l QWebEnginePage::Stop web action can be used to interrupt this asynchronous
+    operation.
+
+    If a file already exists at the provided file path, it will be overwritten.
+
+    \sa QWebEnginePage::pdfPrintingFinished()
+ */
+void QWebEngineFrame::printToPdf(const QString &filePath)
+{
+    LOCK_ADAPTER(adapter, );
+    QPageLayout layout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF());
+    adapter->adapterClient()->printToPdf(filePath, layout, QPageRanges(), m_id);
+}
+
+/*!
+    Renders the current content of the frame into a PDF document and returns a byte array containing
+    the PDF data as parameter to \a callback. Printing uses a page size of A4, portrait layout, and
+    includes the full range of pages.
+
+    The \a callback must take a const reference to a QByteArray as parameter. If printing was
+    successful, this byte array will contain the PDF data, otherwise, the byte array will be empty.
+
+    \note The \l QWebEnginePage::Stop web action can be used to interrupt this operation.
+*/
+void QWebEngineFrame::printToPdf(const std::function<void(const QByteArray &)> &callback)
+{
+    LOCK_ADAPTER(adapter, );
+    std::function wrappedCallback = [callback](QSharedPointer<QByteArray> result) {
+        if (callback)
+            callback(result ? *result : QByteArray());
+    };
+    QPageLayout layout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF());
+    adapter->adapterClient()->printToPdf(std::move(wrappedCallback), layout, QPageRanges(), m_id);
+}
+
+void QWebEngineFrame::printToPdf(const QJSValue &callback)
+{
+    LOCK_ADAPTER(adapter, );
+    std::function<void(QSharedPointer<QByteArray>)> wrappedCallback;
+    if (!callback.isUndefined()) {
+        const QObject *holdingObject = adapter->adapterClient()->holdingQObject();
+        wrappedCallback = [holdingObject, callback](QSharedPointer<QByteArray> result) {
+            if (auto engine = qmlEngine(holdingObject)) {
+                QJSValueList args;
+                args.append(engine->toScriptValue(result ? *result : QByteArray()));
+                callback.call(args);
+            } else {
+                qWarning("No QML engine found to execute runJavaScript() callback");
+            }
+        };
+    }
+    QPageLayout layout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF());
+    adapter->adapterClient()->printToPdf(std::move(wrappedCallback), layout, QPageRanges(), m_id);
+}
+
 /*! \fn bool QWebEngineFrame::operator==(const QWebEngineFrame &left, const QWebEngineFrame &right) noexcept
 
     Returns \c{true} if \a left and \a right represent the same frame in the same web page,
diff --git a/src/core/api/qwebengineframe.h b/src/core/api/qwebengineframe.h
index 3042fbae074..02f442f4f47 100644
--- a/src/core/api/qwebengineframe.h
+++ b/src/core/api/qwebengineframe.h
@@ -12,47 +12,57 @@
 #include <QtCore/QSizeF>
 #include <QtCore/QString>
 #include <QtCore/QUrl>
+#include <QtCore/QWeakPointer>
 
 namespace QtWebEngineCore {
-class WebContentsAdapterClient;
+class WebContentsAdapter;
 }
 
 QT_BEGIN_NAMESPACE
 
-class Q_WEBENGINECORE_EXPORT QWebEngineFrame
+class QWebEngineFrame
 {
-    Q_GADGET
+    Q_GADGET_EXPORT(Q_WEBENGINECORE_EXPORT)
 
     Q_PROPERTY(bool isValid READ isValid FINAL)
     Q_PROPERTY(QString name READ name FINAL)
     Q_PROPERTY(QString htmlName READ htmlName FINAL)
     Q_PROPERTY(QUrl url READ url FINAL)
     Q_PROPERTY(QSizeF size READ size FINAL)
+    Q_PROPERTY(bool isMainFrame READ isMainFrame FINAL)
 
 public:
     QML_VALUE_TYPE(webEngineFrame)
     QML_ADDED_IN_VERSION(6, 8)
 
-    bool isValid() const;
-    QString name() const;
-    QString htmlName() const;
-    QList<QWebEngineFrame> children() const;
-    QUrl url() const;
-    QSizeF size() const;
-
-    void runJavaScript(const QString &script,
-                       const std::function<void(const QVariant &)> &callback);
-    void runJavaScript(const QString &script, quint32 worldId,
-                       const std::function<void(const QVariant &)> &callback);
-    Q_INVOKABLE void runJavaScript(const QString &script, quint32 worldId = 0);
-    Q_INVOKABLE void runJavaScript(const QString &script, const QJSValue &callback);
-    Q_INVOKABLE void runJavaScript(const QString &script, quint32 worldId,
-                                   const QJSValue &callback);
+    Q_WEBENGINECORE_EXPORT bool isValid() const;
+    Q_WEBENGINECORE_EXPORT QString name() const;
+    Q_WEBENGINECORE_EXPORT QString htmlName() const;
+    Q_WEBENGINECORE_EXPORT QList<QWebEngineFrame> children() const;
+    Q_WEBENGINECORE_EXPORT QUrl url() const;
+    Q_WEBENGINECORE_EXPORT QSizeF size() const;
+    Q_WEBENGINECORE_EXPORT bool isMainFrame() const;
+
+    Q_WEBENGINECORE_EXPORT void
+    runJavaScript(const QString &script, const std::function<void(const QVariant &)> &callback);
+    Q_WEBENGINECORE_EXPORT void
+    runJavaScript(const QString &script, quint32 worldId,
+                  const std::function<void(const QVariant &)> &callback);
+    Q_WEBENGINECORE_EXPORT Q_INVOKABLE void runJavaScript(const QString &script,
+                                                          quint32 worldId = 0);
+    Q_WEBENGINECORE_EXPORT Q_INVOKABLE void runJavaScript(const QString &script,
+                                                          const QJSValue &callback);
+    Q_WEBENGINECORE_EXPORT Q_INVOKABLE void runJavaScript(const QString &script, quint32 worldId,
+                                                          const QJSValue &callback);
+
+    Q_WEBENGINECORE_EXPORT Q_INVOKABLE void printToPdf(const QString &filePath);
+    Q_WEBENGINECORE_EXPORT void printToPdf(const std::function<void(const QByteArray &)> &callback);
+    Q_WEBENGINECORE_EXPORT Q_INVOKABLE void printToPdf(const QJSValue &callback);
 
     friend inline bool comparesEqual(const QWebEngineFrame &lhs,
                                      const QWebEngineFrame &rhs) noexcept
     {
-        return lhs.m_adapterClient == rhs.m_adapterClient && lhs.m_id == rhs.m_id;
+        return lhs.m_adapter == rhs.m_adapter && lhs.m_id == rhs.m_id;
     }
 
     Q_DECLARE_EQUALITY_COMPARABLE(QWebEngineFrame);
@@ -63,9 +73,10 @@ class Q_WEBENGINECORE_EXPORT QWebEngineFrame
     friend class QQuickWebEngineView;
     friend class QQuickWebEngineViewPrivate;
 
-    QWebEngineFrame(QtWebEngineCore::WebContentsAdapterClient *page, quint64 id);
+    Q_WEBENGINECORE_EXPORT
+    QWebEngineFrame(QWeakPointer<QtWebEngineCore::WebContentsAdapter> adapter, quint64 id);
 
-    QtWebEngineCore::WebContentsAdapterClient *m_adapterClient;
+    QWeakPointer<QtWebEngineCore::WebContentsAdapter> m_adapter;
     quint64 m_id;
 };
 
diff --git a/src/core/api/qwebenginefullscreenrequest.h b/src/core/api/qwebenginefullscreenrequest.h
index 8e25286b64f..b07d158fd35 100644
--- a/src/core/api/qwebenginefullscreenrequest.h
+++ b/src/core/api/qwebenginefullscreenrequest.h
@@ -23,6 +23,7 @@ class Q_WEBENGINECORE_EXPORT QWebEngineFullScreenRequest
     Q_PROPERTY(QUrl origin READ origin CONSTANT)
 
 public:
+    QWebEngineFullScreenRequest() : QWebEngineFullScreenRequest(QUrl(), false, [](bool){}) {}
     QWebEngineFullScreenRequest(const QWebEngineFullScreenRequest &other);
     QWebEngineFullScreenRequest &operator=(const QWebEngineFullScreenRequest &other);
     QWebEngineFullScreenRequest(QWebEngineFullScreenRequest &&other);
diff --git a/src/core/api/qwebenginehttprequest.cpp b/src/core/api/qwebenginehttprequest.cpp
index 050213d1e2d..0039533d687 100644
--- a/src/core/api/qwebenginehttprequest.cpp
+++ b/src/core/api/qwebenginehttprequest.cpp
@@ -148,8 +148,8 @@ QWebEngineHttpRequest QWebEngineHttpRequest::postRequest(const QUrl &url,
 
     QByteArray buffer;
     for (QMap<QString, QString>::const_iterator it = postData.begin(); it != postData.end(); it++) {
-        QByteArray key = QUrl::toPercentEncoding(it.key());
-        QByteArray value = QUrl::toPercentEncoding(it.value());
+        const QByteArray key = QUrl::toPercentEncoding(it.key());
+        const QByteArray value = QUrl::toPercentEncoding(it.value());
 
         if (buffer.size() > 0)
             buffer += '&';
diff --git a/src/core/api/qwebengineloadinginfo.h b/src/core/api/qwebengineloadinginfo.h
index e50718d7cae..5c206d9a709 100644
--- a/src/core/api/qwebengineloadinginfo.h
+++ b/src/core/api/qwebengineloadinginfo.h
@@ -12,9 +12,6 @@
 #include <QtCore/qurl.h>
 
 namespace QtWebEngineCore {
-namespace {
-class CustomURLLoader;
-}
 class WebContentsAdapter;
 class WebContentsDelegateQt;
 }
@@ -53,6 +50,7 @@ class Q_WEBENGINECORE_EXPORT QWebEngineLoadingInfo
     };
     Q_ENUM(ErrorDomain)
 
+    QWebEngineLoadingInfo() : QWebEngineLoadingInfo(QUrl(), LoadStartedStatus) {}
     QWebEngineLoadingInfo(const QWebEngineLoadingInfo &other);
     QWebEngineLoadingInfo &operator=(const QWebEngineLoadingInfo &other);
     QWebEngineLoadingInfo(QWebEngineLoadingInfo &&other);
@@ -78,7 +76,6 @@ class Q_WEBENGINECORE_EXPORT QWebEngineLoadingInfo
     friend class QQuickWebEngineViewPrivate;
     friend class QtWebEngineCore::WebContentsAdapter;
     friend class QtWebEngineCore::WebContentsDelegateQt;
-    friend class QtWebEngineCore::CustomURLLoader;
 };
 
 QT_END_NAMESPACE
diff --git a/src/core/api/qwebenginenavigationrequest.cpp b/src/core/api/qwebenginenavigationrequest.cpp
index dc7447b882e..a83a9289de5 100644
--- a/src/core/api/qwebenginenavigationrequest.cpp
+++ b/src/core/api/qwebenginenavigationrequest.cpp
@@ -41,7 +41,7 @@ class QWebEngineNavigationRequestPrivate {
 
 /*!
     \qmltype WebEngineNavigationRequest
-    \instantiates QWebEngineNavigationRequest
+    \nativetype QWebEngineNavigationRequest
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.0
 
diff --git a/src/core/api/qwebenginenewwindowrequest.cpp b/src/core/api/qwebenginenewwindowrequest.cpp
index 895033fd126..500f3ac2bf2 100644
--- a/src/core/api/qwebenginenewwindowrequest.cpp
+++ b/src/core/api/qwebenginenewwindowrequest.cpp
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
 
 /*!
     \qmltype WebEngineNewWindowRequest
-    \instantiates QWebEngineNewWindowRequest
+    \nativetype QWebEngineNewWindowRequest
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.12
 
@@ -98,7 +98,7 @@ QWebEngineNewWindowRequest::DestinationType QWebEngineNewWindowRequest::destinat
     \brief The URL that is requested for the new page.
 */
 /*!
-    \qmlproperty QUrl WebEngineNewWindowRequest::requestedUrl
+    \qmlproperty url WebEngineNewWindowRequest::requestedUrl
     \brief The URL that is requested for the new page.
     \since QtWebEngine 1.5
  */
@@ -112,7 +112,7 @@ QUrl QWebEngineNewWindowRequest::requestedUrl() const
     \brief The size that is requested for the new page.
 */
 /*!
-    \qmlproperty QRect WebEngineNewWindowRequest::requestedGeometry
+    \qmlproperty rect WebEngineNewWindowRequest::requestedGeometry
     \brief The size that is requested for the new page.
     \since QtWebEngine 2.0
  */
diff --git a/src/core/api/qwebenginenotification.cpp b/src/core/api/qwebenginenotification.cpp
index 136047042fb..0c72848c6aa 100644
--- a/src/core/api/qwebenginenotification.cpp
+++ b/src/core/api/qwebenginenotification.cpp
@@ -13,7 +13,7 @@ using QtWebEngineCore::UserNotificationController;
 
 /*!
     \qmltype WebEngineNotification
-    \instantiates QWebEngineNotification
+    \nativetype QWebEngineNotification
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.9
     \brief Encapsulates the data of an HTML5 web notification.
diff --git a/src/core/api/qwebenginepage.cpp b/src/core/api/qwebenginepage.cpp
index 7c6a53d0f08..ee9af797e86 100644
--- a/src/core/api/qwebenginepage.cpp
+++ b/src/core/api/qwebenginepage.cpp
@@ -24,6 +24,7 @@
 #include "qwebenginescriptcollection_p.h"
 #include "qwebenginesettings.h"
 #include "qwebenginewebauthuxrequest.h"
+#include "qwebenginepermission_p.h"
 
 #include "authentication_dialog_controller.h"
 #include "autofill_popup_controller.h"
@@ -56,6 +57,7 @@
 
 QT_BEGIN_NAMESPACE
 
+using namespace Qt::StringLiterals;
 using namespace QtWebEngineCore;
 
 static QWebEnginePage::WebWindowType toWindowType(WebContentsAdapterClient::WindowOpenDisposition disposition)
@@ -275,15 +277,17 @@ void QWebEnginePagePrivate::loadFinished(QWebEngineLoadingInfo info)
 }
 
 void QWebEnginePagePrivate::printToPdf(const QString &filePath, const QPageLayout &layout,
-                                       const QPageRanges &ranges)
+                                       const QPageRanges &ranges, quint64 frameId)
 {
-    adapter->printToPDF(layout, ranges, filePath);
+    adapter->printToPDF(layout, ranges, filePath, frameId);
 }
 
 void QWebEnginePagePrivate::printToPdf(std::function<void(QSharedPointer<QByteArray>)> &&callback,
-                                       const QPageLayout &layout, const QPageRanges &ranges)
+                                       const QPageLayout &layout, const QPageRanges &ranges,
+                                       quint64 frameId)
 {
-    adapter->printToPDFCallbackResult(std::move(callback), layout, ranges);
+    adapter->printToPDFCallbackResult(std::move(callback), layout, ranges, /*colorMode*/ true,
+                                      /*useCustomMargins*/ true, frameId);
 }
 
 void QWebEnginePagePrivate::didPrintPageToPdf(const QString &filePath, bool success)
@@ -568,7 +572,8 @@ void QWebEnginePagePrivate::authenticationRequired(QSharedPointer<Authentication
         return;
     }
 
-    controller->accept(networkAuth.user(), networkAuth.password());
+    controller->credentials(networkAuth.user(), networkAuth.password());
+    controller->accept();
 }
 
 void QWebEnginePagePrivate::releaseProfile()
@@ -587,50 +592,111 @@ void QWebEnginePagePrivate::showColorDialog(QSharedPointer<ColorChooserControlle
 void QWebEnginePagePrivate::runMediaAccessPermissionRequest(const QUrl &securityOrigin, WebContentsAdapterClient::MediaRequestFlags requestFlags)
 {
     Q_Q(QWebEnginePage);
-    QWebEnginePage::Feature feature;
-    if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture) &&
-        requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture))
-        feature = QWebEnginePage::MediaAudioVideoCapture;
+    QWebEnginePermission::PermissionType permissionType;
+
+    if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture)
+            && requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture))
+        permissionType = QWebEnginePermission::PermissionType::MediaAudioVideoCapture;
+    else if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture))
+        permissionType = QWebEnginePermission::PermissionType::MediaAudioCapture;
+    else if (requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture))
+        permissionType = QWebEnginePermission::PermissionType::MediaVideoCapture;
+    else if (requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopAudioCapture)
+            && requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture))
+        permissionType = QWebEnginePermission::PermissionType::DesktopAudioVideoCapture;
+    else // if (requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture))
+        permissionType = QWebEnginePermission::PermissionType::DesktopVideoCapture;
+
+    Q_EMIT q->permissionRequested(createFeaturePermissionObject(securityOrigin, permissionType));
+
+#if QT_DEPRECATED_SINCE(6, 8)
+    QT_WARNING_PUSH
+    QT_WARNING_DISABLE_DEPRECATED
+    QWebEnginePage::Feature deprecatedFeature;
+
+    if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture)
+            && requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture))
+        deprecatedFeature = QWebEnginePage::MediaAudioVideoCapture;
     else if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture))
-        feature = QWebEnginePage::MediaAudioCapture;
+        deprecatedFeature = QWebEnginePage::MediaAudioCapture;
     else if (requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture))
-        feature = QWebEnginePage::MediaVideoCapture;
-    else if (requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopAudioCapture) &&
-             requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture))
-        feature = QWebEnginePage::DesktopAudioVideoCapture;
+        deprecatedFeature = QWebEnginePage::MediaVideoCapture;
+    else if (requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopAudioCapture)
+            && requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture))
+        deprecatedFeature = QWebEnginePage::DesktopAudioVideoCapture;
     else // if (requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture))
-        feature = QWebEnginePage::DesktopVideoCapture;
-    Q_EMIT q->featurePermissionRequested(securityOrigin, feature);
+        deprecatedFeature = QWebEnginePage::DesktopVideoCapture;
+
+    Q_EMIT q->featurePermissionRequested(securityOrigin, deprecatedFeature);
+    QT_WARNING_POP
+#endif // QT_DEPRECATED_SINCE(6, 8)
 }
 
-static QWebEnginePage::Feature toFeature(QtWebEngineCore::ProfileAdapter::PermissionType type)
+#if QT_DEPRECATED_SINCE(6, 8)
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+static QWebEnginePage::Feature toDeprecatedFeature(QWebEnginePermission::PermissionType permissionType)
 {
-    switch (type) {
-    case QtWebEngineCore::ProfileAdapter::NotificationPermission:
+    switch (permissionType) {
+    case QWebEnginePermission::PermissionType::Notifications:
         return QWebEnginePage::Notifications;
-    case QtWebEngineCore::ProfileAdapter::GeolocationPermission:
+    case QWebEnginePermission::PermissionType::Geolocation:
         return QWebEnginePage::Geolocation;
-    case QtWebEngineCore::ProfileAdapter::ClipboardReadWrite:
+    case QWebEnginePermission::PermissionType::ClipboardReadWrite:
         return QWebEnginePage::ClipboardReadWrite;
-    case QtWebEngineCore::ProfileAdapter::LocalFontsPermission:
+    case QWebEnginePermission::PermissionType::LocalFontsAccess:
         return QWebEnginePage::LocalFontsAccess;
-    default:
+    case QWebEnginePermission::PermissionType::MediaAudioCapture:
+        return QWebEnginePage::MediaAudioCapture;
+    case QWebEnginePermission::PermissionType::MediaVideoCapture:
+        return QWebEnginePage::MediaVideoCapture;
+    case QWebEnginePermission::PermissionType::MediaAudioVideoCapture:
+        return QWebEnginePage::MediaAudioVideoCapture;
+    case QWebEnginePermission::PermissionType::DesktopVideoCapture:
+        return QWebEnginePage::DesktopVideoCapture;
+    case QWebEnginePermission::PermissionType::DesktopAudioVideoCapture:
+        return QWebEnginePage::DesktopAudioVideoCapture;
+    case QWebEnginePermission::PermissionType::MouseLock:
+        return QWebEnginePage::MouseLock;
+    case QWebEnginePermission::PermissionType::Unsupported:
         break;
     }
+
     Q_UNREACHABLE();
     return QWebEnginePage::Feature(-1);
 }
+QT_WARNING_POP
+#endif // QT_DEPRECATED_SINCE(6, 8)
 
-void QWebEnginePagePrivate::runFeaturePermissionRequest(QtWebEngineCore::ProfileAdapter::PermissionType permission, const QUrl &securityOrigin)
+void QWebEnginePagePrivate::runFeaturePermissionRequest(QWebEnginePermission::PermissionType permissionType, const QUrl &securityOrigin)
 {
     Q_Q(QWebEnginePage);
-    Q_EMIT q->featurePermissionRequested(securityOrigin, toFeature(permission));
+
+    if (QWebEnginePermission::isPersistent(permissionType)) {
+        Q_EMIT q->permissionRequested(createFeaturePermissionObject(securityOrigin, permissionType));
+#if QT_DEPRECATED_SINCE(6, 8)
+        QT_WARNING_PUSH
+        QT_WARNING_DISABLE_DEPRECATED
+        Q_EMIT q->featurePermissionRequested(securityOrigin, toDeprecatedFeature(permissionType));
+        QT_WARNING_POP
+#endif // QT_DEPRECATED_SINCE(6, 8)
+        return;
+    }
+
+    Q_UNREACHABLE();
 }
 
 void QWebEnginePagePrivate::runMouseLockPermissionRequest(const QUrl &securityOrigin)
 {
     Q_Q(QWebEnginePage);
+    Q_EMIT q->permissionRequested(createFeaturePermissionObject(securityOrigin, QWebEnginePermission::PermissionType::MouseLock));
+
+#if QT_DEPRECATED_SINCE(6, 8)
+    QT_WARNING_PUSH
+    QT_WARNING_DISABLE_DEPRECATED
     Q_EMIT q->featurePermissionRequested(securityOrigin, QWebEnginePage::MouseLock);
+    QT_WARNING_POP
+#endif // QT_DEPRECATED_SINCE(6, 8)
 }
 
 void QWebEnginePagePrivate::runRegisterProtocolHandlerRequest(QWebEngineRegisterProtocolHandlerRequest request)
@@ -819,6 +885,12 @@ void QWebEnginePagePrivate::showWebAuthDialog(QWebEngineWebAuthUxRequest *reques
     Q_EMIT q->webAuthUxRequested(request);
 }
 
+QWebEnginePermission QWebEnginePagePrivate::createFeaturePermissionObject(const QUrl &securityOrigin, QWebEnginePermission::PermissionType feature)
+{
+    auto *returnPrivate = new QWebEnginePermissionPrivate(securityOrigin, feature, adapter, profileAdapter());
+    return QWebEnginePermission(returnPrivate);
+}
+
 QWebEnginePage::QWebEnginePage(QObject* parent)
     : QObject(parent)
     , d_ptr(new QWebEnginePagePrivate())
@@ -1265,16 +1337,16 @@ void QWebEnginePage::triggerAction(WebAction action, bool)
         break;
     case CopyLinkToClipboard:
         if (d->view && d->view->lastContextMenuRequest() && !d->view->lastContextMenuRequest()->linkUrl().isEmpty()) {
-            QString urlString = d->view->lastContextMenuRequest()->linkUrl().toString(
-                    QUrl::FullyEncoded);
-            QString linkText = d->view->lastContextMenuRequest()->linkText().toHtmlEscaped();
+            const QString urlString =
+                    d->view->lastContextMenuRequest()->linkUrl().toString(QUrl::FullyEncoded);
+            const QString linkText = d->view->lastContextMenuRequest()->linkText().toHtmlEscaped();
             QString title = d->view->lastContextMenuRequest()->titleText();
             if (!title.isEmpty())
-                title = QStringLiteral(" title=\"%1\"").arg(title.toHtmlEscaped());
+                title = " title=\""_L1 + title.toHtmlEscaped() + u'"';
             QMimeData *data = new QMimeData();
             data->setText(urlString);
-            QString html = QStringLiteral("<a href=\"") + urlString + QStringLiteral("\"") + title + QStringLiteral(">")
-                         + linkText + QStringLiteral("</a>");
+            const QString html =
+                    "<a href=\""_L1 + urlString + u'"' + title + u'>' + linkText + "</a>"_L1;
             data->setHtml(html);
             data->setUrls(QList<QUrl>() << d->view->lastContextMenuRequest()->linkUrl());
             QGuiApplication::clipboard()->setMimeData(data);
@@ -1301,17 +1373,17 @@ void QWebEnginePage::triggerAction(WebAction action, bool)
         if (d->view && d->view->lastContextMenuRequest() && d->view->lastContextMenuRequest()->mediaUrl().isValid()
             && d->view->lastContextMenuRequest()->mediaType()
                     == QWebEngineContextMenuRequest::MediaTypeImage) {
-            QString urlString =
+            const QString urlString =
                     d->view->lastContextMenuRequest()->mediaUrl().toString(QUrl::FullyEncoded);
             QString alt = d->view->lastContextMenuRequest()->altText();
             if (!alt.isEmpty())
-                alt = QStringLiteral(" alt=\"%1\"").arg(alt.toHtmlEscaped());
+                alt = " alt=\""_L1 + alt.toHtmlEscaped() + u'"';
             QString title = d->view->lastContextMenuRequest()->titleText();
             if (!title.isEmpty())
-                title = QStringLiteral(" title=\"%1\"").arg(title.toHtmlEscaped());
+                title = " title=\""_L1 + title.toHtmlEscaped() + u'"';
             QMimeData *data = new QMimeData();
             data->setText(urlString);
-            QString html = QStringLiteral("<img src=\"") + urlString + QStringLiteral("\"") + title + alt + QStringLiteral("></img>");
+            const QString html = "<img src=\""_L1 + urlString + u'"' + title + alt + "></img>"_L1;
             data->setHtml(html);
             data->setUrls(QList<QUrl>() << d->view->lastContextMenuRequest()->mediaUrl());
             QGuiApplication::clipboard()->setMimeData(data);
@@ -1331,20 +1403,19 @@ void QWebEnginePage::triggerAction(WebAction action, bool)
                         == QWebEngineContextMenuRequest::MediaTypeAudio
                 || d->view->lastContextMenuRequest()->mediaType()
                         == QWebEngineContextMenuRequest::MediaTypeVideo)) {
-            QString urlString =
+            const QString urlString =
                     d->view->lastContextMenuRequest()->mediaUrl().toString(QUrl::FullyEncoded);
             QString title = d->view->lastContextMenuRequest()->titleText();
             if (!title.isEmpty())
-                title = QStringLiteral(" title=\"%1\"").arg(title.toHtmlEscaped());
+                title = " title=\""_L1 + title.toHtmlEscaped() + u'"';
             QMimeData *data = new QMimeData();
             data->setText(urlString);
-            if (d->view->lastContextMenuRequest()->mediaType()
-                == QWebEngineContextMenuRequest::MediaTypeAudio)
-                data->setHtml(QStringLiteral("<audio src=\"") + urlString + QStringLiteral("\"") + title +
-                              QStringLiteral("></audio>"));
-            else
-                data->setHtml(QStringLiteral("<video src=\"") + urlString + QStringLiteral("\"") + title +
-                              QStringLiteral("></video>"));
+            const bool isAudio = d->view->lastContextMenuRequest()->mediaType()
+                    == QWebEngineContextMenuRequest::MediaTypeAudio;
+            const auto avTagName = isAudio ? "audio"_L1 : "video"_L1;
+            const QString html = u'<' + avTagName + "src=\""_L1 + urlString + u'"' + title
+                    + "></"_L1 + avTagName + u'>';
+            data->setHtml(html);
             data->setUrls(QList<QUrl>() << d->view->lastContextMenuRequest()->mediaUrl());
             QGuiApplication::clipboard()->setMimeData(data);
         }
@@ -1420,40 +1491,47 @@ void QWebEnginePage::triggerAction(WebAction action, bool)
         QTimer::singleShot(0, this, [d](){ d->adapter->viewSource(); });
         break;
     case ToggleBold:
-        runJavaScript(QStringLiteral("document.execCommand('bold');"), QWebEngineScript::ApplicationWorld);
+        runJavaScript(u"document.execCommand('bold');"_s, QWebEngineScript::ApplicationWorld);
         break;
     case ToggleItalic:
-        runJavaScript(QStringLiteral("document.execCommand('italic');"), QWebEngineScript::ApplicationWorld);
+        runJavaScript(u"document.execCommand('italic');"_s, QWebEngineScript::ApplicationWorld);
         break;
     case ToggleUnderline:
-        runJavaScript(QStringLiteral("document.execCommand('underline');"), QWebEngineScript::ApplicationWorld);
+        runJavaScript(u"document.execCommand('underline');"_s, QWebEngineScript::ApplicationWorld);
         break;
     case ToggleStrikethrough:
-        runJavaScript(QStringLiteral("document.execCommand('strikethrough');"), QWebEngineScript::ApplicationWorld);
+        runJavaScript(u"document.execCommand('strikethrough');"_s,
+                      QWebEngineScript::ApplicationWorld);
         break;
     case AlignLeft:
-        runJavaScript(QStringLiteral("document.execCommand('justifyLeft');"), QWebEngineScript::ApplicationWorld);
+        runJavaScript(u"document.execCommand('justifyLeft');"_s,
+                      QWebEngineScript::ApplicationWorld);
         break;
     case AlignCenter:
-        runJavaScript(QStringLiteral("document.execCommand('justifyCenter');"), QWebEngineScript::ApplicationWorld);
+        runJavaScript(u"document.execCommand('justifyCenter');"_s,
+                      QWebEngineScript::ApplicationWorld);
         break;
     case AlignRight:
-        runJavaScript(QStringLiteral("document.execCommand('justifyRight');"), QWebEngineScript::ApplicationWorld);
+        runJavaScript(u"document.execCommand('justifyRight');"_s,
+                      QWebEngineScript::ApplicationWorld);
         break;
     case AlignJustified:
-        runJavaScript(QStringLiteral("document.execCommand('justifyFull');"), QWebEngineScript::ApplicationWorld);
+        runJavaScript(u"document.execCommand('justifyFull');"_s,
+                      QWebEngineScript::ApplicationWorld);
         break;
     case Indent:
-        runJavaScript(QStringLiteral("document.execCommand('indent');"), QWebEngineScript::ApplicationWorld);
+        runJavaScript(u"document.execCommand('indent');"_s, QWebEngineScript::ApplicationWorld);
         break;
     case Outdent:
-        runJavaScript(QStringLiteral("document.execCommand('outdent');"), QWebEngineScript::ApplicationWorld);
+        runJavaScript(u"document.execCommand('outdent');"_s, QWebEngineScript::ApplicationWorld);
         break;
     case InsertOrderedList:
-        runJavaScript(QStringLiteral("document.execCommand('insertOrderedList');"), QWebEngineScript::ApplicationWorld);
+        runJavaScript(u"document.execCommand('insertOrderedList');"_s,
+                      QWebEngineScript::ApplicationWorld);
         break;
     case InsertUnorderedList:
-        runJavaScript(QStringLiteral("document.execCommand('insertUnorderedList');"), QWebEngineScript::ApplicationWorld);
+        runJavaScript(u"document.execCommand('insertUnorderedList');"_s,
+                      QWebEngineScript::ApplicationWorld);
         break;
     case ChangeTextDirectionLTR:
         d->adapter->changeTextDirection(true /*left to right*/);
@@ -1504,6 +1582,17 @@ bool QWebEnginePage::event(QEvent *e)
     return QObject::event(e);
 }
 
+/*!
+    \fn QWebEnginePage::desktopMediaRequested(const QWebEngineDesktopMediaRequest &request)
+    \since 6.7
+
+    This signal is emitted when a web application requests access to the contents of a display.
+
+    The \a request argument holds references to data models for windows and screens available
+    for capturing. To accept the request, the signal handler can call either
+    QWebEngineDesktopMediaRequest::selectScreen() or QWebEngineDesktopMediaRequest::selectWindow().
+*/
+
 void QWebEnginePagePrivate::desktopMediaRequested(
         QtWebEngineCore::DesktopMediaController *controller)
 {
@@ -1692,13 +1781,13 @@ void QWebEnginePagePrivate::printRequested()
     This signal is emitted when the JavaScript \c{window.print()} method is called on \a frame.
     If the frame is the main frame, \c{printRequested} is emitted instead.
 
-    \sa printRequested(), printToPdf()
+    \sa printRequested(), printToPdf(), QWebEngineFrame::printToPdf()
 */
 
 void QWebEnginePagePrivate::printRequestedByFrame(quint64 frameId)
 {
     Q_Q(QWebEnginePage);
-    QWebEngineFrame frame(this, frameId);
+    QWebEngineFrame frame(adapter, frameId);
     QTimer::singleShot(0, q, [q, frame]() { Q_EMIT q->printRequestedByFrame(frame); });
     if (view)
         view->printRequestedByFrame(frame);
@@ -1771,104 +1860,68 @@ void QWebEnginePage::setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *i
     d->adapter->setRequestInterceptor(interceptor);
 }
 
+#if QT_DEPRECATED_SINCE(6, 8)
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
 void QWebEnginePage::setFeaturePermission(const QUrl &securityOrigin, QWebEnginePage::Feature feature, QWebEnginePage::PermissionPolicy policy)
 {
     Q_D(QWebEnginePage);
-    if (policy == PermissionUnknown) {
-        switch (feature) {
-        case MediaAudioVideoCapture:
-        case MediaAudioCapture:
-        case MediaVideoCapture:
-        case DesktopAudioVideoCapture:
-        case DesktopVideoCapture:
-        case MouseLock:
-            break;
-        case Geolocation:
-            d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::GeolocationPermission, ProfileAdapter::AskPermission);
-            break;
-        case Notifications:
-            d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::NotificationPermission, ProfileAdapter::AskPermission);
-            break;
-        case ClipboardReadWrite:
-            d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::ClipboardReadWrite,
-                                               ProfileAdapter::AskPermission);
-            break;
-        case LocalFontsAccess:
-            d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::LocalFontsPermission, ProfileAdapter::AskPermission);
-            break;
-        }
-        return;
+    QWebEnginePermission::PermissionType f = QWebEnginePermission::PermissionType::Unsupported;
+    QWebEnginePermission::State s = QWebEnginePermission::State::Invalid;
+
+    switch (feature) {
+    case QWebEnginePage::Notifications:
+        f = QWebEnginePermission::PermissionType::Notifications;
+        break;
+    case QWebEnginePage::Geolocation:
+        f = QWebEnginePermission::PermissionType::Geolocation;
+        break;
+    case QWebEnginePage::MediaAudioCapture:
+        f = QWebEnginePermission::PermissionType::MediaAudioCapture;
+        break;
+    case QWebEnginePage::MediaVideoCapture:
+        f = QWebEnginePermission::PermissionType::MediaVideoCapture;
+        break;
+    case QWebEnginePage::MediaAudioVideoCapture:
+        f = QWebEnginePermission::PermissionType::MediaAudioVideoCapture;
+        break;
+    case QWebEnginePage::MouseLock:
+        f = QWebEnginePermission::PermissionType::MouseLock;
+        break;
+    case QWebEnginePage::DesktopVideoCapture:
+        f = QWebEnginePermission::PermissionType::DesktopVideoCapture;
+        break;
+    case QWebEnginePage::DesktopAudioVideoCapture:
+        f = QWebEnginePermission::PermissionType::DesktopAudioVideoCapture;
+        break;
+    case QWebEnginePage::ClipboardReadWrite:
+        f = QWebEnginePermission::PermissionType::ClipboardReadWrite;
+        break;
+    case QWebEnginePage::LocalFontsAccess:
+        f = QWebEnginePermission::PermissionType::LocalFontsAccess;
+        break;
+    default:
+        Q_UNREACHABLE();
     }
 
-    const WebContentsAdapterClient::MediaRequestFlags audioVideoCaptureFlags(
-        WebContentsAdapterClient::MediaVideoCapture |
-        WebContentsAdapterClient::MediaAudioCapture);
-    const WebContentsAdapterClient::MediaRequestFlags desktopAudioVideoCaptureFlags(
-        WebContentsAdapterClient::MediaDesktopVideoCapture |
-        WebContentsAdapterClient::MediaDesktopAudioCapture);
-
-    if (policy == PermissionGrantedByUser) {
-        switch (feature) {
-        case MediaAudioVideoCapture:
-            d->adapter->grantMediaAccessPermission(securityOrigin, audioVideoCaptureFlags);
-            break;
-        case MediaAudioCapture:
-            d->adapter->grantMediaAccessPermission(securityOrigin, WebContentsAdapterClient::MediaAudioCapture);
-            break;
-        case MediaVideoCapture:
-            d->adapter->grantMediaAccessPermission(securityOrigin, WebContentsAdapterClient::MediaVideoCapture);
-            break;
-        case DesktopAudioVideoCapture:
-            d->adapter->grantMediaAccessPermission(securityOrigin, desktopAudioVideoCaptureFlags);
-            break;
-        case DesktopVideoCapture:
-            d->adapter->grantMediaAccessPermission(securityOrigin, WebContentsAdapterClient::MediaDesktopVideoCapture);
-            break;
-        case MouseLock:
-            d->adapter->grantMouseLockPermission(securityOrigin, true);
-            break;
-        case Geolocation:
-            d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::GeolocationPermission, ProfileAdapter::AllowedPermission);
-            break;
-        case Notifications:
-            d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::NotificationPermission, ProfileAdapter::AllowedPermission);
-            break;
-        case ClipboardReadWrite:
-            d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::ClipboardReadWrite,
-                                               ProfileAdapter::AllowedPermission);
-            break;
-        case LocalFontsAccess:
-            d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::LocalFontsPermission, ProfileAdapter::AllowedPermission);
-            break;
-        }
-    } else { // if (policy == PermissionDeniedByUser)
-        switch (feature) {
-        case MediaAudioVideoCapture:
-        case MediaAudioCapture:
-        case MediaVideoCapture:
-        case DesktopAudioVideoCapture:
-        case DesktopVideoCapture:
-            d->adapter->grantMediaAccessPermission(securityOrigin, WebContentsAdapterClient::MediaNone);
-            break;
-        case Geolocation:
-            d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::GeolocationPermission, ProfileAdapter::DeniedPermission);
-            break;
-        case MouseLock:
-            d->adapter->grantMouseLockPermission(securityOrigin, false);
-            break;
-        case Notifications:
-            d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::NotificationPermission, ProfileAdapter::DeniedPermission);
-            break;
-        case ClipboardReadWrite:
-            d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::ClipboardReadWrite,
-                                               ProfileAdapter::DeniedPermission);
-            break;
-        case LocalFontsAccess:
-            d->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::LocalFontsPermission, ProfileAdapter::DeniedPermission);
-            break;
-        }
+    switch (policy) {
+    case QWebEnginePage::PermissionUnknown:
+        s = QWebEnginePermission::State::Ask;
+        break;
+    case QWebEnginePage::PermissionDeniedByUser:
+        s = QWebEnginePermission::State::Denied;
+        break;
+    case QWebEnginePage::PermissionGrantedByUser:
+        s = QWebEnginePermission::State::Granted;
+        break;
+    default:
+        Q_UNREACHABLE();
     }
+
+    d->adapter->setPermission(securityOrigin, f, s);
 }
+QT_WARNING_POP
+#endif // QT_DEPRECATED_SINCE(6, 8)
 
 static inline QWebEnginePage::FileSelectionMode toPublic(FilePickerController::FileChooserMode mode)
 {
@@ -1969,7 +2022,7 @@ void QWebEnginePage::toPlainText(const std::function<void(const QString &)> &res
 
 void QWebEnginePage::setHtml(const QString &html, const QUrl &baseUrl)
 {
-    setContent(html.toUtf8(), QStringLiteral("text/html;charset=UTF-8"), baseUrl);
+    setContent(html.toUtf8(), u"text/html;charset=UTF-8"_s, baseUrl);
 }
 
 void QWebEnginePage::setContent(const QByteArray &data, const QString &mimeType, const QUrl &baseUrl)
@@ -2238,7 +2291,7 @@ bool QWebEnginePage::javaScriptPrompt(const QUrl &securityOrigin, const QString
 void QWebEnginePage::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString &message, int lineNumber, const QString &sourceID)
 {
     static QLoggingCategory loggingCategory("js", QtWarningMsg);
-    static QByteArray file = sourceID.toUtf8();
+    const QByteArray file = sourceID.toUtf8();
     QMessageLogger logger(file.constData(), lineNumber, nullptr, loggingCategory.categoryName());
 
     switch (level) {
@@ -2330,7 +2383,7 @@ void QWebEnginePage::printToPdf(const QString &filePath, const QPageLayout &layo
 #if QT_CONFIG(webengine_printing_and_pdf)
     Q_D(QWebEnginePage);
     d->ensureInitialized();
-    d->printToPdf(filePath, layout, ranges);
+    d->printToPdf(filePath, layout, ranges, WebContentsAdapter::kUseMainFrameId);
 #else
     Q_UNUSED(filePath);
     Q_UNUSED(layout);
@@ -2362,7 +2415,7 @@ void QWebEnginePage::printToPdf(const std::function<void(const QByteArray&)> &re
         if (resultCallback && result)
             resultCallback(*result);
     };
-    d->printToPdf(std::move(wrappedCallback), layout, ranges);
+    d->printToPdf(std::move(wrappedCallback), layout, ranges, WebContentsAdapter::kUseMainFrameId);
 #else
     Q_UNUSED(layout);
     Q_UNUSED(ranges);
@@ -2521,7 +2574,7 @@ void QWebEnginePage::setVisible(bool visible)
 QWebEngineFrame QWebEnginePage::mainFrame()
 {
     Q_D(QWebEnginePage);
-    return QWebEngineFrame(d, d->adapter->mainFrameId());
+    return QWebEngineFrame(d->adapter, d->adapter->mainFrameId());
 }
 
 /*!
@@ -2530,11 +2583,11 @@ QWebEngineFrame QWebEnginePage::mainFrame()
     Returns the frame with the given \a name. If there are multiple frames with the same
     name, which one is returned is arbitrary. If no frame was found, returns \c std::nullopt.
 */
-std::optional<QWebEngineFrame> QWebEnginePage::findFrameByName(const QString &name)
+std::optional<QWebEngineFrame> QWebEnginePage::findFrameByName(QAnyStringView name)
 {
     Q_D(QWebEnginePage);
-    if (auto maybeId = d->adapter->findFrameIdByName(name)) {
-        return QWebEngineFrame(d, *maybeId);
+    if (auto maybeId = d->adapter->findFrameIdByName(name.toString())) {
+        return QWebEngineFrame(d->adapter, *maybeId);
     }
     return {};
 }
diff --git a/src/core/api/qwebenginepage.h b/src/core/api/qwebenginepage.h
index 59eed2b9b4a..ada0af9dab9 100644
--- a/src/core/api/qwebenginepage.h
+++ b/src/core/api/qwebenginepage.h
@@ -9,7 +9,9 @@
 #include <QtWebEngineCore/qwebenginedownloadrequest.h>
 #include <QtWebEngineCore/qwebenginequotarequest.h>
 #include <QtWebEngineCore/qwebengineframe.h>
+#include <QtWebEngineCore/qwebenginepermission.h>
 
+#include <QtCore/qanystringview.h>
 #include <QtCore/qobject.h>
 #include <QtCore/qurl.h>
 #include <QtGui/qpagelayout.h>
@@ -147,12 +149,17 @@ class Q_WEBENGINECORE_EXPORT QWebEnginePage : public QObject
     };
     Q_ENUM(WebWindowType)
 
+#if QT_DEPRECATED_SINCE(6, 8)
     enum PermissionPolicy {
-        PermissionUnknown,
-        PermissionGrantedByUser,
-        PermissionDeniedByUser
+        PermissionUnknown Q_DECL_ENUMERATOR_DEPRECATED_X(
+            "Use QWebEnginePermission::State::Ask instead"),
+        PermissionGrantedByUser Q_DECL_ENUMERATOR_DEPRECATED_X(
+            "Use QWebEnginePermission::State::Granted instead"),
+        PermissionDeniedByUser Q_DECL_ENUMERATOR_DEPRECATED_X(
+            "Use QWebEnginePermission::State::Denied instead")
     };
     Q_ENUM(PermissionPolicy)
+#endif
 
     // must match WebContentsAdapterClient::NavigationType
     enum NavigationType {
@@ -166,19 +173,31 @@ class Q_WEBENGINECORE_EXPORT QWebEnginePage : public QObject
     };
     Q_ENUM(NavigationType)
 
+#if QT_DEPRECATED_SINCE(6, 8)
     enum Feature {
-        Notifications = 0,
-        Geolocation = 1,
-        MediaAudioCapture = 2,
-        MediaVideoCapture,
-        MediaAudioVideoCapture,
-        MouseLock,
-        DesktopVideoCapture,
-        DesktopAudioVideoCapture,
-        ClipboardReadWrite,
-        LocalFontsAccess,
+        Notifications Q_DECL_ENUMERATOR_DEPRECATED_X(
+            "Use QWebEnginePermission::PermissionType::Notifications instead") = 0,
+        Geolocation Q_DECL_ENUMERATOR_DEPRECATED_X(
+            "Use QWebEnginePermission::PermissionType::Geolocation instead") = 1,
+        MediaAudioCapture Q_DECL_ENUMERATOR_DEPRECATED_X(
+            "Use QWebEnginePermission::PermissionType::MediaAudioCapture instead") = 2,
+        MediaVideoCapture Q_DECL_ENUMERATOR_DEPRECATED_X(
+            "Use QWebEnginePermission::PermissionType::MediaVideoCapture instead"),
+        MediaAudioVideoCapture Q_DECL_ENUMERATOR_DEPRECATED_X(
+            "Use QWebEnginePermission::PermissionType::MediaAudioVideoCapture instead"),
+        MouseLock Q_DECL_ENUMERATOR_DEPRECATED_X(
+            "Use QWebEnginePermission::PermissionType::MouseLock instead"),
+        DesktopVideoCapture Q_DECL_ENUMERATOR_DEPRECATED_X(
+            "Use QWebEnginePermission::PermissionType::DesktopVideoCapture instead"),
+        DesktopAudioVideoCapture Q_DECL_ENUMERATOR_DEPRECATED_X(
+            "Use QWebEnginePermission::PermissionType::DesktopAudioVideoCapture instead"),
+        ClipboardReadWrite Q_DECL_ENUMERATOR_DEPRECATED_X(
+            "Use QWebEnginePermission::PermissionType::ClipboardReadWrite instead"),
+        LocalFontsAccess Q_DECL_ENUMERATOR_DEPRECATED_X(
+            "Use QWebEnginePermission::PermissionType::LocalFontsAccess instead"),
     };
     Q_ENUM(Feature)
+#endif
 
     // Ex-QWebFrame enum
 
@@ -236,7 +255,11 @@ class Q_WEBENGINECORE_EXPORT QWebEnginePage : public QObject
 
     void findText(const QString &subString, FindFlags options = {}, const std::function<void(const QWebEngineFindTextResult &)> &resultCallback = std::function<void(const QWebEngineFindTextResult &)>());
 
+#if QT_DEPRECATED_SINCE(6, 8)
+    QT_DEPRECATED_VERSION_X_6_8(
+        "Setting permissions through QWebEnginePage has been deprecated. Please use QWebEnginePermission instead.")
     void setFeaturePermission(const QUrl &securityOrigin, Feature feature, PermissionPolicy policy);
+#endif
 
     bool isLoading() const;
 
@@ -304,7 +327,7 @@ class Q_WEBENGINECORE_EXPORT QWebEnginePage : public QObject
     void setVisible(bool visible);
 
     QWebEngineFrame mainFrame();
-    std::optional<QWebEngineFrame> findFrameByName(const QString &name);
+    std::optional<QWebEngineFrame> findFrameByName(QAnyStringView name);
 
     void acceptAsNewWindow(QWebEngineNewWindowRequest &request);
 
@@ -319,9 +342,18 @@ class Q_WEBENGINECORE_EXPORT QWebEnginePage : public QObject
     void geometryChangeRequested(const QRect &geom);
     void windowCloseRequested();
 
+#if QT_DEPRECATED_SINCE(6, 8)
+    QT_MOC_COMPAT QT_DEPRECATED_VERSION_X_6_8(
+        "The signal has been deprecated; please use permissionRequested instead.")
     void featurePermissionRequested(const QUrl &securityOrigin, QWebEnginePage::Feature feature);
+    QT_MOC_COMPAT QT_DEPRECATED_VERSION_X_6_8(
+        "The signal has been deprecated, and no longer functions.")
     void featurePermissionRequestCanceled(const QUrl &securityOrigin, QWebEnginePage::Feature feature);
+#endif // QT_DEPRECATED_SINCE(6, 8)
+
     void fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest);
+    void permissionRequested(QWebEnginePermission permissionRequest);
+
 #if QT_DEPRECATED_SINCE(6, 5)
     QT_DEPRECATED_VERSION_X_6_5("Requesting host quota is no longer supported.")
     void quotaRequested(QWebEngineQuotaRequest quotaRequest);
diff --git a/src/core/api/qwebenginepage_p.h b/src/core/api/qwebenginepage_p.h
index 3aeda6711a1..5e81a0cf91c 100644
--- a/src/core/api/qwebenginepage_p.h
+++ b/src/core/api/qwebenginepage_p.h
@@ -136,10 +136,10 @@ class Q_WEBENGINECORE_EXPORT QWebEnginePagePrivate : public QtWebEngineCore::Web
                        const std::function<void(const QVariant &)> &callback) override;
     void didFetchDocumentMarkup(quint64 requestId, const QString &result) override;
     void didFetchDocumentInnerText(quint64 requestId, const QString &result) override;
-    void printToPdf(const QString &filePath, const QPageLayout &layout,
-                    const QPageRanges &ranges) override;
+    void printToPdf(const QString &filePath, const QPageLayout &layout, const QPageRanges &ranges,
+                    quint64 frameId) override;
     void printToPdf(std::function<void(QSharedPointer<QByteArray>)> &&callback,
-                    const QPageLayout &layout, const QPageRanges &ranges) override;
+                    const QPageLayout &layout, const QPageRanges &ranges, quint64 frameId) override;
     void didPrintPageToPdf(const QString &filePath, bool success) override;
     bool passOnFocus(bool reverse) override;
     void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString &message,
@@ -148,7 +148,7 @@ class Q_WEBENGINECORE_EXPORT QWebEnginePagePrivate : public QtWebEngineCore::Web
             QSharedPointer<QtWebEngineCore::AuthenticationDialogController>) override;
     void releaseProfile() override;
     void runMediaAccessPermissionRequest(const QUrl &securityOrigin, MediaRequestFlags requestFlags) override;
-    void runFeaturePermissionRequest(QtWebEngineCore::ProfileAdapter::PermissionType permission, const QUrl &securityOrigin) override;
+    void runFeaturePermissionRequest(QWebEnginePermission::PermissionType permissionType, const QUrl &securityOrigin) override;
     void runMouseLockPermissionRequest(const QUrl &securityOrigin) override;
     void runRegisterProtocolHandlerRequest(QWebEngineRegisterProtocolHandlerRequest) override;
     void runFileSystemAccessRequest(QWebEngineFileSystemAccessRequest) override;
@@ -180,6 +180,7 @@ class Q_WEBENGINECORE_EXPORT QWebEnginePagePrivate : public QtWebEngineCore::Web
                            const QRect &bounds, bool autoselectFirstSuggestion) override;
     void hideAutofillPopup() override;
     void showWebAuthDialog(QWebEngineWebAuthUxRequest *controller) override;
+    QWebEnginePermission createFeaturePermissionObject(const QUrl &securityOrigin, QWebEnginePermission::PermissionType permissionType) override;
 
     QtWebEngineCore::ProfileAdapter *profileAdapter() override;
     QtWebEngineCore::WebContentsAdapter *webContentsAdapter() override;
diff --git a/src/core/api/qwebenginepermission.cpp b/src/core/api/qwebenginepermission.cpp
new file mode 100644
index 00000000000..5fa1ed4842e
--- /dev/null
+++ b/src/core/api/qwebenginepermission.cpp
@@ -0,0 +1,315 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwebenginepermission.h"
+#include "qwebenginepermission_p.h"
+#include "web_contents_adapter.h"
+#include "profile_adapter.h"
+
+QT_BEGIN_NAMESPACE
+
+QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QWebEnginePermissionPrivate)
+
+/*! \internal */
+QWebEnginePermissionPrivate::QWebEnginePermissionPrivate()
+    : QSharedData()
+    , permissionType(QWebEnginePermission::PermissionType::Unsupported)
+{
+}
+
+/*! \internal */
+QWebEnginePermissionPrivate::QWebEnginePermissionPrivate(const QUrl &origin_, QWebEnginePermission::PermissionType permissionType_,
+        QSharedPointer<QtWebEngineCore::WebContentsAdapter> webContentsAdapter_, QtWebEngineCore::ProfileAdapter *profileAdapter_)
+    : QSharedData()
+    , origin(origin_)
+    , permissionType(permissionType_)
+    , webContentsAdapter(webContentsAdapter_)
+    , profileAdapter(profileAdapter_)
+{
+}
+
+/*!
+    \class QWebEnginePermission
+    \inmodule QtWebEngineCore
+    \since 6.8
+    \brief A QWebEnginePermission is an object used to access and modify the state of a single permission that's been
+    granted or denied to a specific origin URL.
+
+    The typical usage pattern is as follows:
+    \list 1
+        \li A website requests a specific permission, triggering the QWebEnginePage::permissionRequested() signal;
+        \li The signal handler triggers a prompt asking the user whether they want to grant the permission;
+        \li When the user has made their decision, the application calls \l grant() or \l deny();
+    \endlist
+
+    Alternatively, an application interested in modifying already granted permissions may use QWebEngineProfile::listAllPermissions()
+    to get a list of existing permissions associated with a profile, or QWebEngineProfile::queryPermission() to get
+    a QWebEnginePermission object for a specific permission.
+
+    The \l origin() property can be used to query which origin the QWebEnginePermission is associated with, while the
+    \l permissionType() property describes the type of the requested permission. A website origin is the combination of
+    its scheme, hostname, and port. Permissions are granted on a per-origin basis; thus, if the web page
+    \c{https://www.example.com:12345/some/page.html} requests a permission, it will be granted to the origin
+    \c{https://www.example.com:12345/}.
+
+    \l QWebEnginePermission::PermissionType describes all the permission types Qt WebEngine supports. Only some permission types
+    are remembered between browsing sessions; they are \e persistent. Non-persistent permissions query the user every time a
+    website requests them. You can check whether a permission type is persistent at runtime
+    using the static method QWebEnginePermission::isPersistent().
+
+    Persistent permissions are stored inside the active QWebEngineProfile, and their lifetime depends on the value of
+    QWebEngineProfile::persistentPermissionsPolicy(). By default, named profiles store their permissions on disk, whereas
+    off-the-record ones store them in memory (and destroy them when the profile is destroyed). A stored permission will not
+    query the user the next time a website requests it; instead it will be automatically granted or denied, depending on
+    the resolution the user picked initially. To erase a stored permission, call \l reset() on it.
+
+    A non-persistent permission, on the other hand, is only usable until the related QWebEnginePage performs a navigation to
+    a different URL, or is destroyed.
+
+    You can check whether a QWebEnginePermission is in a valid state using its \l isValid() property. For invalid objects, calls to \l grant(),
+    \l deny(), or \l reset() will do nothing, while calls to \l state() will always return QWebEnginePermission::Invalid.
+
+    \sa QWebEnginePage::permissionRequested(), QWebEngineProfile::queryPermission(), QWebEngineProfile::listAllPermissions()
+*/
+
+/*! \fn QWebEnginePermission::QWebEnginePermission()
+    \internal
+*/
+
+/*! \internal */
+QWebEnginePermission::QWebEnginePermission()
+    : d_ptr(new QWebEnginePermissionPrivate())
+{
+}
+
+/*! \internal */
+QWebEnginePermission::QWebEnginePermission(QWebEnginePermissionPrivate *pvt)
+    : d_ptr(pvt)
+{
+}
+
+QWebEnginePermission::QWebEnginePermission(const QWebEnginePermission &other)
+    : d_ptr(other.d_ptr)
+{
+}
+
+QWebEnginePermission::~QWebEnginePermission() = default;
+
+QWebEnginePermission &QWebEnginePermission::operator=(const QWebEnginePermission &other)
+{
+    d_ptr = other.d_ptr;
+    return *this;
+}
+
+bool QWebEnginePermission::equals(const QWebEnginePermission &other) const
+{
+    if (this == &other)
+        return true;
+
+    if (!d_ptr || !other.d_ptr)
+        return false;
+
+    if (d_ptr->permissionType != other.d_ptr->permissionType || d_ptr->origin != other.d_ptr->origin)
+        return false;
+
+    if (!isPersistent(d_ptr->permissionType)) {
+        if (d_ptr->webContentsAdapter != other.d_ptr->webContentsAdapter)
+            return false;
+    } else {
+        QtWebEngineCore::ProfileAdapter *thisProfile = d_ptr->webContentsAdapter
+            ? d_ptr->webContentsAdapter.toStrongRef()->profileAdapter()
+            : d_ptr->profileAdapter.get();
+        QtWebEngineCore::ProfileAdapter *otherProfile = d_ptr->webContentsAdapter
+            ? other.d_ptr->webContentsAdapter.toStrongRef()->profileAdapter()
+            : other.d_ptr->profileAdapter.get();
+
+        if (thisProfile != otherProfile)
+            return false;
+    }
+
+    return true;
+}
+
+/*!
+    \property QWebEnginePermission::origin
+    \brief The URL of the permission's associated origin.
+
+    A website origin is the combination of its scheme, hostname, and port. Permissions are granted on a
+    per-origin basis; thus, if the web page \c{https://www.example.com:12345/some/page.html}
+    requests a permission, it will be granted to the origin \c{https://www.example.com:12345/}.
+*/
+QUrl QWebEnginePermission::origin() const
+{
+    return d_ptr ? d_ptr->origin : QUrl();
+}
+
+/*!
+    \enum QWebEnginePermission::PermissionType
+
+    This enum type holds the type of the requested permission type:
+
+    \value MediaAudioCapture Access to a microphone, or another audio source. This permission is \e not persistent.
+    \value MediaVideoCapture Access to a webcam, or another video source. This permission is \e not persistent.
+    \value MediaAudioVideoCapture Combination of \l MediaAudioCapture and \l MediaVideoCapture. This permission is \e not persistent.
+    \value DesktopVideoCapture Access to the contents of the user's screen. This permission is \e not persistent.
+    \value DesktopAudioVideoCapture Access to the contents of the user's screen, and application audio. This permission is \e not persistent.
+    \value MouseLock Locks the pointer inside an element on the web page. This permission is \e not persistent.
+    \value Notifications Allows the website to send notifications to the user. This permission is persistent.
+    \value Geolocation Access to the user's physical location. This permission is persistent.
+    \value ClipboardReadWrite Access to the user's clipboard. This permission is persistent.
+    \value LocalFontsAccess Access to the fonts installed on the user's machine. Only available on desktops. This permission is persistent.
+    \value Unsupported An unsupported permission type.
+
+    \note Non-persistent permission types are ones that will never be remembered by the underlying storage, and will trigger
+    a permission request every time a website tries to use them.
+*/
+
+/*!
+    \property QWebEnginePermission::permissionType
+    \brief The permission type associated with this permission.
+*/
+QWebEnginePermission::PermissionType QWebEnginePermission::permissionType() const
+{
+    return d_ptr ? d_ptr->permissionType : PermissionType::Unsupported;
+}
+
+/*!
+    \enum QWebEnginePermission::State
+
+    This enum type holds the current state of the requested permission:
+
+    \value Invalid Object is in an invalid state, and any attempts to modify the described permission will fail.
+    \value Ask Either the permission has not been requested before, or the permissionType() is not persistent.
+    \value Granted Permission has already been granted.
+    \value Denied Permission has already been denied.
+*/
+
+/*!
+    \property QWebEnginePermission::state
+    \brief The current state of the permission.
+
+    If a permission for the specified \l permissionType() and \l origin() has already been granted or denied,
+    the return value is QWebEnginePermission::Granted, or QWebEnginePermission::Denied, respectively.
+    When this is the first time the permission is requested,
+    the return value is QWebEnginePermission::Ask. If the object is in an invalid state, the returned
+    value is QWebEnginePermission::Invalid.
+
+    \sa isValid(), isPersistent()
+*/
+QWebEnginePermission::State QWebEnginePermission::state() const
+{
+    if (!isValid())
+        return State::Invalid;
+    if (d_ptr->webContentsAdapter)
+        return d_ptr->webContentsAdapter.toStrongRef()->getPermissionState(origin(), permissionType());
+    if (d_ptr->profileAdapter)
+        return d_ptr->profileAdapter->getPermissionState(origin(), permissionType());
+    Q_UNREACHABLE_RETURN(State::Ask);
+}
+
+/*!
+    \property QWebEnginePermission::isValid
+    \brief Indicates whether attempts to change the permission's state will be successful.
+
+    An invalid QWebEnginePermission is either:
+    \list
+        \li One whose \l permissionType() is unsupported;
+        \li One whose \l origin() is invalid;
+        \li One whose associated profile has been destroyed
+    \endlist
+
+    \sa isPersistent()
+*/
+bool QWebEnginePermission::isValid() const
+{
+    if (!d_ptr)
+        return false;
+    if (permissionType() == PermissionType::Unsupported)
+        return false;
+    if (!d_ptr->profileAdapter && !d_ptr->webContentsAdapter)
+        return false;
+    if (!d_ptr->origin.isValid())
+        return false;
+    return true;
+}
+
+/*!
+    Allows the associated origin to access the requested permissionType. Does nothing when \l isValid() evaluates to false.
+
+    \sa deny(), reset(), isValid()
+*/
+void QWebEnginePermission::grant() const
+{
+    if (!isValid())
+        return;
+    if (d_ptr->webContentsAdapter)
+        d_ptr->webContentsAdapter.toStrongRef()->setPermission(origin(), permissionType(), State::Granted);
+    else if (d_ptr->profileAdapter)
+        d_ptr->profileAdapter->setPermission(origin(), permissionType(), State::Granted);
+}
+
+/*!
+    Stops the associated origin from accessing the requested permissionType. Does nothing when \l isValid() evaluates to false.
+
+    \sa grant(), reset(), isValid()
+*/
+void QWebEnginePermission::deny() const
+{
+    if (!isValid())
+        return;
+    if (d_ptr->webContentsAdapter)
+        d_ptr->webContentsAdapter.toStrongRef()->setPermission(origin(), permissionType(), State::Denied);
+    else if (d_ptr->profileAdapter)
+        d_ptr->profileAdapter->setPermission(origin(), permissionType(), State::Denied);
+}
+
+/*!
+    Removes the permission from the profile's underlying storage. By default, permissions are stored on disk (except for
+    off-the-record profiles, where permissions are stored in memory and are destroyed with the profile).
+    This means that an already granted/denied permission will not be requested twice, but will get automatically
+    granted/denied every subsequent time a website requests it. Calling reset() allows the query to be displayed
+    again the next time the website requests it.
+
+    Does nothing when \l isValid() evaluates to false.
+
+    \sa grant(), deny(), isValid(), QWebEngineProfile::persistentPermissionsPolicy()
+*/
+void QWebEnginePermission::reset() const
+{
+    if (!isValid())
+        return;
+    if (d_ptr->webContentsAdapter)
+        d_ptr->webContentsAdapter.toStrongRef()->setPermission(origin(), permissionType(), State::Ask);
+    else if (d_ptr->profileAdapter)
+        d_ptr->profileAdapter->setPermission(origin(), permissionType(), State::Ask);
+}
+
+/*!
+    Returns whether a \a permissionType is \e persistent, meaning that a permission's state will be remembered
+    and the user will not be queried the next time the website requests the same permission.
+*/
+bool QWebEnginePermission::isPersistent(QWebEnginePermission::PermissionType permissionType)
+{
+    switch (permissionType) {
+    case QWebEnginePermission::PermissionType::Notifications:
+    case QWebEnginePermission::PermissionType::Geolocation:
+    case QWebEnginePermission::PermissionType::ClipboardReadWrite:
+    case QWebEnginePermission::PermissionType::LocalFontsAccess:
+        return true;
+    case QWebEnginePermission::PermissionType::MediaAudioCapture:
+    case QWebEnginePermission::PermissionType::MediaVideoCapture:
+    case QWebEnginePermission::PermissionType::MediaAudioVideoCapture:
+    case QWebEnginePermission::PermissionType::DesktopVideoCapture:
+    case QWebEnginePermission::PermissionType::DesktopAudioVideoCapture:
+    case QWebEnginePermission::PermissionType::MouseLock:
+        return false;
+    case QWebEnginePermission::PermissionType::Unsupported:
+        return false;
+    }
+
+    Q_UNREACHABLE_RETURN(false);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qwebenginepermission.cpp"
diff --git a/src/core/api/qwebenginepermission.h b/src/core/api/qwebenginepermission.h
new file mode 100644
index 00000000000..476c8bdd40b
--- /dev/null
+++ b/src/core/api/qwebenginepermission.h
@@ -0,0 +1,106 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWEBENGINEPERMISSION_H
+#define QWEBENGINEPERMISSION_H
+
+#include <QtWebEngineCore/qtwebenginecoreglobal.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qshareddata.h>
+
+namespace QtWebEngineCore {
+class PermissionManagerQt;
+} // namespace
+
+QT_BEGIN_NAMESPACE
+
+class QWebEnginePagePrivate;
+class QWebEngineProfile;
+class QQuickWebEngineViewPrivate;
+class QQuickWebEngineProfile;
+
+struct QWebEnginePermissionPrivate;
+QT_DECLARE_QESDP_SPECIALIZATION_DTOR(QWebEnginePermissionPrivate)
+
+class QWebEnginePermission
+{
+    Q_GADGET_EXPORT(Q_WEBENGINECORE_EXPORT)
+    Q_PROPERTY(QUrl origin READ origin CONSTANT FINAL)
+    Q_PROPERTY(PermissionType permissionType READ permissionType CONSTANT FINAL)
+    Q_PROPERTY(State state READ state CONSTANT FINAL)
+    Q_PROPERTY(bool isValid READ isValid CONSTANT FINAL)
+    Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
+
+public:
+    Q_WEBENGINECORE_EXPORT QWebEnginePermission();
+
+    Q_WEBENGINECORE_EXPORT QWebEnginePermission(const QWebEnginePermission &other);
+    QWebEnginePermission(QWebEnginePermission &&other) noexcept = default;
+    Q_WEBENGINECORE_EXPORT ~QWebEnginePermission();
+
+    Q_WEBENGINECORE_EXPORT QWebEnginePermission &operator=(const QWebEnginePermission &other);
+    QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QWebEnginePermission)
+    void swap(QWebEnginePermission &other) noexcept { d_ptr.swap(other.d_ptr); }
+
+    enum class PermissionType : quint8 {
+        Unsupported,
+        MediaAudioCapture,
+        MediaVideoCapture,
+        MediaAudioVideoCapture,
+        DesktopVideoCapture,
+        DesktopAudioVideoCapture,
+        MouseLock,
+        Notifications,
+        Geolocation,
+        ClipboardReadWrite,
+        LocalFontsAccess,
+    };
+    Q_ENUM(PermissionType)
+
+    enum class State : quint8 {
+        Invalid,
+        Ask,
+        Granted,
+        Denied,
+    };
+    Q_ENUM(State)
+
+    Q_WEBENGINECORE_EXPORT QUrl origin() const;
+    Q_WEBENGINECORE_EXPORT PermissionType permissionType() const;
+    Q_WEBENGINECORE_EXPORT State state() const;
+    Q_WEBENGINECORE_EXPORT bool isValid() const;
+
+    Q_WEBENGINECORE_EXPORT Q_INVOKABLE void grant() const;
+    Q_WEBENGINECORE_EXPORT Q_INVOKABLE void deny() const;
+    Q_WEBENGINECORE_EXPORT Q_INVOKABLE void reset() const;
+
+    Q_WEBENGINECORE_EXPORT Q_INVOKABLE static bool isPersistent(QWebEnginePermission::PermissionType permissionType);
+
+private:
+    inline friend bool operator==(const QWebEnginePermission &lhs, const QWebEnginePermission &rhs)
+        { return lhs.equals(rhs); };
+    inline friend bool operator!=(const QWebEnginePermission &lhs, const QWebEnginePermission &rhs)
+        { return !lhs.equals(rhs); };
+
+    Q_WEBENGINECORE_EXPORT bool equals(const QWebEnginePermission &other) const;
+
+protected:
+    friend class QWebEnginePagePrivate;
+    friend class QWebEngineProfile;
+    friend class QQuickWebEngineViewPrivate;
+    friend class QQuickWebEngineProfile;
+    friend class QtWebEngineCore::PermissionManagerQt;
+
+    Q_WEBENGINECORE_EXPORT QWebEnginePermission(QWebEnginePermissionPrivate *pvt);
+
+    QExplicitlySharedDataPointer<QWebEnginePermissionPrivate> d_ptr;
+};
+
+
+
+Q_DECLARE_SHARED(QWebEnginePermission)
+
+QT_END_NAMESPACE
+
+#endif // QWEBENGINEPERMISSION_H
diff --git a/src/core/api/qwebenginepermission_p.h b/src/core/api/qwebenginepermission_p.h
new file mode 100644
index 00000000000..fe4dc62f81a
--- /dev/null
+++ b/src/core/api/qwebenginepermission_p.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWEBENGINEPERMISSION_P_H
+#define QWEBENGINEPERMISSION_P_H
+
+//
+//  W A R N I N G
+//  -------------
+//
+// This file is not part of the Qt API.  It exists purely as an
+// implementation detail.  This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qwebenginepermission.h"
+
+#include <QtCore/qpointer.h>
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qshareddata.h>
+
+namespace QtWebEngineCore {
+class WebContentsAdapter;
+class ProfileAdapter;
+}
+
+QT_BEGIN_NAMESPACE
+
+struct QWebEnginePermissionPrivate : public QSharedData
+{
+    Q_WEBENGINECORE_EXPORT QWebEnginePermissionPrivate();
+    Q_WEBENGINECORE_EXPORT QWebEnginePermissionPrivate(const QUrl &, QWebEnginePermission::PermissionType,
+        QSharedPointer<QtWebEngineCore::WebContentsAdapter>, QtWebEngineCore::ProfileAdapter *);
+
+    QUrl origin;
+    QWebEnginePermission::PermissionType permissionType;
+
+    QWeakPointer<QtWebEngineCore::WebContentsAdapter> webContentsAdapter;
+    QPointer<QtWebEngineCore::ProfileAdapter> profileAdapter;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWEBENGINEPERMISSION_P_H
diff --git a/src/core/api/qwebengineprofile.cpp b/src/core/api/qwebengineprofile.cpp
index a9477929ba9..a7955549ac1 100644
--- a/src/core/api/qwebengineprofile.cpp
+++ b/src/core/api/qwebengineprofile.cpp
@@ -11,12 +11,13 @@
 #include "qwebenginesettings.h"
 #include "qwebenginescriptcollection.h"
 #include "qwebenginescriptcollection_p.h"
+#include "qwebenginepermission_p.h"
 #include "qtwebenginecoreglobal.h"
 #include "profile_adapter.h"
 #include "visited_links_manager_qt.h"
 #include "web_engine_settings.h"
 
-#include <QDir>
+#include <QFileInfo>
 #include <QtWebEngineCore/qwebengineurlscheme.h>
 
 QT_BEGIN_NAMESPACE
@@ -112,18 +113,18 @@ using QtWebEngineCore::ProfileAdapter;
 
     This enum describes the policy for permission persistence:
 
-    \value  NoPersistentPermissions
+    \value  AskEveryTime
             The application will ask for permissions every time they're needed, regardless of
             whether they've been granted before or not. This is intended for backwards compatibility
             with existing applications, and otherwise not recommended.
-    \value  PersistentPermissionsInMemory
+    \value  StoreInMemory
             A request will be made only the first time a permission is needed. Any subsequent
             requests will be automatically granted or denied, depending on the initial user choice.
             This carries over to all pages that use the same QWebEngineProfile instance, until the
             application is shut down. This is the setting applied if \c off-the-record is set
             or no persistent data path is available.
-    \value  PersistentPermissionsOnDisk
-            Works the same way as \c PersistentPermissionsInMemory, but the permissions are saved to
+    \value  StoreOnDisk
+            Works the same way as \c StoreInMemory, but the permissions are saved to
             and restored from disk. This is the default setting.
 */
 
@@ -215,10 +216,16 @@ void QWebEngineProfilePrivate::cleanDownloads()
     m_ongoingDownloads.clear();
 }
 
-void QWebEngineProfilePrivate::downloadRequested(DownloadItemInfo &info)
+void QWebEngineProfilePrivate::downloadRequested(const DownloadItemInfo &info)
 {
     Q_Q(QWebEngineProfile);
 
+    if (!q->receivers(SIGNAL(downloadRequested(QWebEngineDownloadRequest*)))) {
+        m_profileAdapter->acceptDownload(info.id, info.accepted, info.useDownloadTargetCallback, info.path,
+                                         info.savePageFormat);
+        return;
+    }
+
     Q_ASSERT(!m_ongoingDownloads.contains(info.id));
     QWebEngineDownloadRequestPrivate *itemPrivate =
             new QWebEngineDownloadRequestPrivate(m_profileAdapter);
@@ -234,6 +241,7 @@ void QWebEngineProfilePrivate::downloadRequested(DownloadItemInfo &info)
     itemPrivate->mimeType = info.mimeType;
     itemPrivate->savePageFormat = static_cast<QWebEngineDownloadRequest::SavePageFormat>(info.savePageFormat);
     itemPrivate->isSavePageDownload = info.isSavePageDownload;
+    itemPrivate->useDownloadTargetCallback = info.useDownloadTargetCallback;
     if (info.page && info.page->clientType() == QtWebEngineCore::WebContentsAdapterClient::WidgetsClient)
         itemPrivate->adapterClient = info.page;
     else
@@ -246,18 +254,9 @@ void QWebEngineProfilePrivate::downloadRequested(DownloadItemInfo &info)
 
     Q_EMIT q->downloadRequested(download);
 
-    QWebEngineDownloadRequest::DownloadState state = download->state();
-
-    info.path = QDir(download->downloadDirectory()).filePath(download->downloadFileName());
-    info.savePageFormat = static_cast<QtWebEngineCore::ProfileAdapterClient::SavePageFormat>(
-                download->savePageFormat());
-    info.accepted = state != QWebEngineDownloadRequest::DownloadCancelled;
-
-    if (state == QWebEngineDownloadRequest::DownloadRequested) {
-        // Delete unaccepted downloads.
-        info.accepted = false;
-        delete download;
-    }
+    // Callbacks of automatically accepted save operations have to be called here
+    if (info.isSavePageDownload && info.accepted)
+        itemPrivate->answer();
 }
 
 void QWebEngineProfilePrivate::downloadUpdated(const DownloadItemInfo &info)
@@ -596,9 +595,10 @@ void QWebEngineProfile::setPersistentCookiesPolicy(QWebEngineProfile::Persistent
     Returns the current policy for persistent permissions.
 
     Off-the-record profiles are not allowed to save data to the disk, so they can only return
-    PersistentPermissionsInMemory or NoPersistentPermissions.
+    \c StoreInMemory or \c AskEveryTime.
 
-    \sa setPersistentPermissionsPolicy()
+    \since 6.8
+    \sa QWebEngineProfile::PersistentPermissionsPolicy, setPersistentPermissionsPolicy()
 */
 QWebEngineProfile::PersistentPermissionsPolicy QWebEngineProfile::persistentPermissionsPolicy() const
 {
@@ -609,7 +609,8 @@ QWebEngineProfile::PersistentPermissionsPolicy QWebEngineProfile::persistentPerm
 /*!
     Sets the policy for persistent permissions to \a newPersistentPermissionsPolicy.
 
-    \sa persistentPermissionsPolicy()
+    \since 6.8
+    \sa QWebEngineProfile::PersistentPermissionsPolicy, persistentPermissionsPolicy()
 */
 void QWebEngineProfile::setPersistentPermissionsPolicy(QWebEngineProfile::PersistentPermissionsPolicy newPersistentPermissionsPolicy)
 {
@@ -976,6 +977,98 @@ void QWebEngineProfile::requestIconForIconURL(const QUrl &url, int desiredSizeIn
                                                iconAvailableCallback);
 }
 
+/*!
+ * Returns a QWebEnginePermission object corresponding to a single permission for the provided \a securityOrigin and
+ * \a permissionType. The object may be used to query for the current state of the permission, or to change it. It is not required
+ * for a permission to already exist; the returned object may also be used to pre-grant a permission if a website is
+ * known to use it.
+ *
+ * You may use this to pre-grant a permission of a non-persistent type. Doing so will keep the permission in
+ * the granted (or denied) state until the next time a website with the associated origin requests it. At that point,
+ * the permission's lifetime will be tied to that specific web page's lifetime, and navigating away will invalidate
+ * the permission.
+ *
+ * \since 6.8
+ * \sa listAllPermissions(), listPermissionsForOrigin(), listPermissionsForPermissionType(), QWebEnginePermission::PermissionType
+ */
+QWebEnginePermission QWebEngineProfile::queryPermission(const QUrl &securityOrigin, QWebEnginePermission::PermissionType permissionType) const
+{
+    Q_D(const QWebEngineProfile);
+
+    if (permissionType == QWebEnginePermission::PermissionType::Unsupported) {
+        qWarning("Attempting to get unsupported permission. Returned object will be in an invalid state.");
+        return QWebEnginePermission(new QWebEnginePermissionPrivate());
+    }
+
+    auto *pvt = new QWebEnginePermissionPrivate(securityOrigin, permissionType, nullptr, d->profileAdapter());
+    return QWebEnginePermission(pvt);
+}
+
+/*!
+ * Returns a QList of QWebEnginePermission objects, each one representing a single permission currently
+ * present in the permissions store. The returned list contains all previously granted/denied permissions for this profile,
+ * provided they are of a \e persistent type.
+ *
+ * \note When persistentPermissionPolicy() is set to \c AskEveryTime, this will return an empty list.
+ * \since 6.8
+ * \sa queryPermission(), QWebEnginePermission::PermissionType, QWebEnginePermission::isPersistent()
+ */
+QList<QWebEnginePermission> QWebEngineProfile::listAllPermissions() const
+{
+    Q_D(const QWebEngineProfile);
+    if (persistentPermissionsPolicy() == PersistentPermissionsPolicy::AskEveryTime)
+        return QList<QWebEnginePermission>();
+    return d->profileAdapter()->listPermissions();
+}
+
+/*!
+ * Returns a QList of QWebEnginePermission objects, each one representing a single permission currently
+ * present in the permissions store. The returned list contains all previously granted/denied permissions associated with a
+ * specific \a securityOrigin for this profile, provided they are of a \e persistent type.
+ *
+ * \note Since permissions are granted on a per-origin basis, the provided \a securityOrigin will be stripped to its
+ * origin form, and the returned list will contain all permissions for the origin. Thus, passing https://www.example.com/some/page.html
+ * is the same as passing just https://www.example.com/.
+ * \note When persistentPermissionPolicy() is set to \c AskEveryTime, this will return an empty list.
+ * \since 6.8
+ * \sa queryPermission(), QWebEnginePermission::PermissionType, QWebEnginePermission::isPersistent()
+ */
+QList<QWebEnginePermission> QWebEngineProfile::listPermissionsForOrigin(const QUrl &securityOrigin) const
+{
+    Q_D(const QWebEngineProfile);
+    if (persistentPermissionsPolicy() == PersistentPermissionsPolicy::AskEveryTime)
+        return QList<QWebEnginePermission>();
+    return d->profileAdapter()->listPermissions(securityOrigin);
+}
+
+/*!
+ * Returns a QList of QWebEnginePermission objects, each one representing a single permission currently
+ * present in the permissions store. The returned list contains all previously granted/denied permissions of the provided
+ * \a permissionType. If the \permissionType is non-persistent, the list will be empty.
+ *
+ * \note When persistentPermissionPolicy() is set to \c AskEveryTime, this will return an empty list.
+ * \since 6.8
+ * \sa queryPermission(), QWebEnginePermission::PermissionType, QWebEnginePermission::isPersistent()
+ */
+QList<QWebEnginePermission> QWebEngineProfile::listPermissionsForPermissionType(QWebEnginePermission::PermissionType permissionType) const
+{
+    Q_D(const QWebEngineProfile);
+    if (persistentPermissionsPolicy() == PersistentPermissionsPolicy::AskEveryTime)
+        return QList<QWebEnginePermission>();
+
+    if (permissionType == QWebEnginePermission::PermissionType::Unsupported) {
+        qWarning("Attempting to get permission list for an unsupported type. Returned list will be empty.");
+        return QList<QWebEnginePermission>();
+    }
+
+    if (!QWebEnginePermission::isPersistent(permissionType)) {
+        qWarning() << "Attempting to get permission list for permission type" << permissionType << ". Returned list will be empty.";
+        return QList<QWebEnginePermission>();
+    }
+
+    return d->profileAdapter()->listPermissions(QUrl(), permissionType);
+}
+
 /*!
     Return the Client Hints settings associated with this browsing context.
 
diff --git a/src/core/api/qwebengineprofile.h b/src/core/api/qwebengineprofile.h
index 259de4bff4f..c83ffbe3343 100644
--- a/src/core/api/qwebengineprofile.h
+++ b/src/core/api/qwebengineprofile.h
@@ -5,6 +5,7 @@
 #define QWEBENGINEPROFILE_H
 
 #include <QtWebEngineCore/qtwebenginecoreglobal.h>
+#include <QtWebEngineCore/qwebenginepermission.h>
 
 #include <QtCore/qobject.h>
 #include <QtCore/qscopedpointer.h>
@@ -49,10 +50,10 @@ class Q_WEBENGINECORE_EXPORT QWebEngineProfile : public QObject
     };
     Q_ENUM(PersistentCookiesPolicy)
 
-    enum PersistentPermissionsPolicy : quint8 {
-        NoPersistentPermissions,
-        PersistentPermissionsInMemory,
-        PersistentPermissionsOnDisk,
+    enum class PersistentPermissionsPolicy : quint8 {
+        AskEveryTime = 0,
+        StoreInMemory,
+        StoreOnDisk,
     };
     Q_ENUM(PersistentPermissionsPolicy)
 
@@ -120,6 +121,11 @@ class Q_WEBENGINECORE_EXPORT QWebEngineProfile : public QObject
     void requestIconForPageURL(const QUrl &url, int desiredSizeInPixel, std::function<void(const QIcon &, const QUrl &, const QUrl &)> iconAvailableCallback) const;
     void requestIconForIconURL(const QUrl &url, int desiredSizeInPixel, std::function<void(const QIcon &, const QUrl &)> iconAvailableCallback) const;
 
+    QWebEnginePermission queryPermission(const QUrl &securityOrigin, QWebEnginePermission::PermissionType permissionType) const;
+    QList<QWebEnginePermission> listAllPermissions() const;
+    QList<QWebEnginePermission> listPermissionsForOrigin(const QUrl &securityOrigin) const;
+    QList<QWebEnginePermission> listPermissionsForPermissionType(QWebEnginePermission::PermissionType permissionType) const;
+
     static QWebEngineProfile *defaultProfile();
 
 Q_SIGNALS:
diff --git a/src/core/api/qwebengineprofile_p.h b/src/core/api/qwebengineprofile_p.h
index 0ccc2703756..d4b3513cd5d 100644
--- a/src/core/api/qwebengineprofile_p.h
+++ b/src/core/api/qwebengineprofile_p.h
@@ -51,7 +51,7 @@ class Q_WEBENGINECORE_EXPORT QWebEngineProfilePrivate : public QtWebEngineCore::
 
     void cleanDownloads();
 
-    void downloadRequested(DownloadItemInfo &info) override;
+    void downloadRequested(const DownloadItemInfo &info) override;
     void downloadUpdated(const DownloadItemInfo &info) override;
 
     void showNotification(QSharedPointer<QtWebEngineCore::UserNotificationController> &) override;
diff --git a/src/core/api/qwebenginescript.cpp b/src/core/api/qwebenginescript.cpp
index 29b92a39652..ab91576bd89 100644
--- a/src/core/api/qwebenginescript.cpp
+++ b/src/core/api/qwebenginescript.cpp
@@ -7,6 +7,7 @@
 #include <QtCore/QDebug>
 #include <QtCore/QFile>
 
+using namespace Qt::StringLiterals;
 using QtWebEngineCore::UserScript;
 
 QT_BEGIN_NAMESPACE
@@ -131,12 +132,26 @@ void QWebEngineScript::setName(const QString &scriptName)
     d->setName(scriptName);
 }
 
-
+/*!
+ * Returns the remote source location of the user script (if any).
+ */
 QUrl QWebEngineScript::sourceUrl() const
 {
     return d->sourceUrl();
 }
 
+/*!
+ * Sets the remote source location of the user script to \a url.
+ *
+ * Unlike \l setSourceCode(), this function allows referring to user scripts that
+ * are not already loaded in memory, for instance, when stored on disk.
+ *
+ * Setting this value will change the \l sourceCode of the script.
+ *
+ * \note At present, only file-based sources are supported.
+ *
+ * \sa setSourceCode()
+ */
 void QWebEngineScript::setSourceUrl(const QUrl &url)
 {
     if (url == sourceUrl())
@@ -147,17 +162,17 @@ void QWebEngineScript::setSourceUrl(const QUrl &url)
     QFile file;
     if (url.isLocalFile()) {
         file.setFileName(url.toLocalFile());
-    } else if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0) {
+    } else if (url.scheme().compare("qrc"_L1, Qt::CaseInsensitive) == 0) {
         if (url.authority().isEmpty())
             file.setFileName(QLatin1Char(':') + url.path());
     }
 
     if (!file.open(QIODevice::ReadOnly)) {
-        qWarning() << "Can't open user script " << url;
+        qWarning("Can't open user script %ls", qUtf16Printable(url.toString()));
         return;
     }
 
-    QString source = QString::fromUtf8(file.readAll());
+    const QString source = QString::fromUtf8(file.readAll());
     setSourceCode(source);
 }
 
diff --git a/src/core/api/qwebenginesettings.cpp b/src/core/api/qwebenginesettings.cpp
index f19d8efe57f..a7ae80cd126 100644
--- a/src/core/api/qwebenginesettings.cpp
+++ b/src/core/api/qwebenginesettings.cpp
@@ -115,7 +115,7 @@ QWebEngineSettings::ImageAnimationPolicy QWebEngineSettings::imageAnimationPolic
 
 void QWebEngineSettings::resetImageAnimationPolicy()
 {
-    d_ptr->setImageAnimationPolicy(QWebEngineSettings::InheritedImageAnimationPolicy);
+    d_ptr->setImageAnimationPolicy(QWebEngineSettings::ImageAnimationPolicy::Inherited);
 }
 
 QT_END_NAMESPACE
diff --git a/src/core/api/qwebenginesettings.h b/src/core/api/qwebenginesettings.h
index 7f89f1ea631..f6b5dfe4d11 100644
--- a/src/core/api/qwebenginesettings.h
+++ b/src/core/api/qwebenginesettings.h
@@ -78,11 +78,11 @@ class Q_WEBENGINECORE_EXPORT QWebEngineSettings
         AllowAllUnknownUrlSchemes
     };
 
-    enum ImageAnimationPolicy {
-        InheritedImageAnimationPolicy = 0,
-        AllowImageAnimation,
-        AnimateImageOnce,
-        DisallowImageAnimation
+    enum class ImageAnimationPolicy : uint8_t {
+        Inherited = 0,
+        Allow,
+        AnimateOnce,
+        Disallow,
     };
 
 public:
diff --git a/src/core/api/qwebengineurlrequestinfo.cpp b/src/core/api/qwebengineurlrequestinfo.cpp
index 152cf4dd09e..5c0fa178863 100644
--- a/src/core/api/qwebengineurlrequestinfo.cpp
+++ b/src/core/api/qwebengineurlrequestinfo.cpp
@@ -161,6 +161,7 @@ QWebEngineUrlRequestInfo::QWebEngineUrlRequestInfo(QWebEngineUrlRequestInfoPriva
     \value ResourceTypeNavigationPreloadSubFrame  A sub-frame service worker navigation preload request. (Added in Qt 5.14)
     \value ResourceTypeWebSocket  A WebSocket request. (Added in Qt 6.4)
     \value ResourceTypeUnknown  Unknown request type.
+    \value ResourceTypeJson A JSON module. (Added in Qt 6.8)
 
     \note For forward compatibility all values not matched should be treated as unknown,
     not just \c ResourceTypeUnknown.
@@ -274,7 +275,6 @@ void QWebEngineUrlRequestInfo::resetChanged()
 
 /*!
     Redirects this request to \a url.
-    It is only possible to redirect requests that do not have payload data, such as GET requests.
 */
 
 void QWebEngineUrlRequestInfo::redirect(const QUrl &url)
diff --git a/src/core/api/qwebengineurlrequestinfo.h b/src/core/api/qwebengineurlrequestinfo.h
index 33efcbedafe..b8a02d07a82 100644
--- a/src/core/api/qwebengineurlrequestinfo.h
+++ b/src/core/api/qwebengineurlrequestinfo.h
@@ -47,8 +47,9 @@ class Q_WEBENGINECORE_EXPORT QWebEngineUrlRequestInfo
         ResourceTypePluginResource, // A resource requested by a plugin
         ResourceTypeNavigationPreloadMainFrame = 19, // A main-frame service worker navigation preload request
         ResourceTypeNavigationPreloadSubFrame,  // A sub-frame service worker navigation preload request
+        ResourceTypeJson, // a JSON module (import ... with { type: "json" })
 #ifndef Q_QDOC
-        ResourceTypeLast = ResourceTypeNavigationPreloadSubFrame,
+        ResourceTypeLast = ResourceTypeJson,
 #endif
         ResourceTypeWebSocket = 254,
         ResourceTypeUnknown = 255
diff --git a/src/core/api/qwebengineurlrequestjob.cpp b/src/core/api/qwebengineurlrequestjob.cpp
index 5ad22f3441d..b969b2369e1 100644
--- a/src/core/api/qwebengineurlrequestjob.cpp
+++ b/src/core/api/qwebengineurlrequestjob.cpp
@@ -32,7 +32,7 @@ QT_BEGIN_NAMESPACE
     This enum type holds the type of the error that occurred:
 
     \value  NoError
-            The request was successful. (Deprecated since Qt 6.7)
+            The request was successful. (Deprecated since Qt 6.8)
     \value  UrlNotFound
             The requested URL was not found.
     \value  UrlInvalid
diff --git a/src/core/api/qwebengineurlrequestjob.h b/src/core/api/qwebengineurlrequestjob.h
index 858c03e6eb3..8448efdefd3 100644
--- a/src/core/api/qwebengineurlrequestjob.h
+++ b/src/core/api/qwebengineurlrequestjob.h
@@ -26,11 +26,11 @@ class Q_WEBENGINECORE_EXPORT QWebEngineUrlRequestJob : public QObject
     ~QWebEngineUrlRequestJob();
 
     enum Error {
-#if QT_DEPRECATED_SINCE(6, 7)
+#if QT_DEPRECATED_SINCE(6, 8)
         NoError Q_DECL_ENUMERATOR_DEPRECATED_X(
             "This attribute has no effect.") = 0,
 #endif
-        UrlNotFound,
+        UrlNotFound = 1,
         UrlInvalid,
         RequestAborted,
         RequestDenied,
diff --git a/src/core/api/qwebengineurlscheme.cpp b/src/core/api/qwebengineurlscheme.cpp
index 3093c2b8edd..d2ca56f62d8 100644
--- a/src/core/api/qwebengineurlscheme.cpp
+++ b/src/core/api/qwebengineurlscheme.cpp
@@ -335,29 +335,33 @@ void QWebEngineUrlScheme::setFlags(Flags newValue)
 void QWebEngineUrlScheme::registerScheme(const QWebEngineUrlScheme &scheme)
 {
     if (scheme.d->name.empty()) {
-        qWarning() << "QWebEngineUrlScheme::registerScheme: Scheme name cannot be empty";
+        qWarning("QWebEngineUrlScheme::registerScheme: Scheme name cannot be empty");
         return;
     }
 
     bool needsPort = scheme.d->has_port_component();
     bool hasPort = scheme.d->default_port != url::PORT_UNSPECIFIED;
     if (needsPort && !hasPort) {
-        qWarning() << "QWebEngineUrlScheme::registerScheme: Scheme" << scheme.name() << "needs a default port";
+        qWarning("QWebEngineUrlScheme::registerScheme: Scheme %s needs a default port",
+                 scheme.d->name.data());
         return;
     }
 
     if (url::CustomScheme::FindScheme(scheme.d->name)) {
-        qWarning() << "QWebEngineUrlScheme::registerScheme: Scheme" << scheme.name() << "already registered";
+        qWarning("QWebEngineUrlScheme::registerScheme: Scheme %s already registered",
+                 scheme.d->name.data());
         return;
     }
 
     if (url::IsStandard(scheme.d->name.data(), url::Component(0, static_cast<int>(scheme.d->name.size())))) {
-        qWarning() << "QWebEngineUrlScheme::registerScheme: Scheme" << scheme.name() << "is a standard scheme";
+        qWarning("QWebEngineUrlScheme::registerScheme: Scheme %s is a standard scheme",
+                 scheme.d->name.data());
         return;
     }
 
     if (g_schemesLocked) {
-        qWarning() << "QWebEngineUrlScheme::registerScheme: Too late to register scheme" << scheme.name();
+        qWarning("QWebEngineUrlScheme::registerScheme: Too late to register scheme %s",
+                 scheme.d->name.data());
         return;
     }
 
@@ -372,7 +376,7 @@ void QWebEngineUrlScheme::registerScheme(const QWebEngineUrlScheme &scheme)
 */
 QWebEngineUrlScheme QWebEngineUrlScheme::schemeByName(const QByteArray &name)
 {
-    base::StringPiece namePiece{ name.data(), static_cast<size_t>(name.size()) };
+    std::string_view namePiece{ name.data(), static_cast<size_t>(name.size()) };
     if (const url::CustomScheme *cs = url::CustomScheme::FindScheme(namePiece))
         return QWebEngineUrlScheme(new QWebEngineUrlSchemePrivate(*cs));
     return QWebEngineUrlScheme();
diff --git a/src/core/api/qwebengineurlschemehandler.cpp b/src/core/api/qwebengineurlschemehandler.cpp
index e01ecef4942..cc77bcac88f 100644
--- a/src/core/api/qwebengineurlschemehandler.cpp
+++ b/src/core/api/qwebengineurlschemehandler.cpp
@@ -50,7 +50,7 @@ QT_BEGIN_NAMESPACE
             const QUrl url = job->requestUrl();
 
             if (isValidUrl(url)) {
-                if (method == QByteArrayLiteral("GET")) {
+                if (method == QByteArrayLiteral("GET"))
                     job->reply(QByteArrayLiteral("text/html"), makeReply(url));
                 else // Unsupported method
                     job->fail(QWebEngineUrlRequestJob::RequestDenied);
diff --git a/src/core/api/qwebenginewebauthuxrequest.cpp b/src/core/api/qwebenginewebauthuxrequest.cpp
index 1f09e55bba2..3c289fd5c3a 100644
--- a/src/core/api/qwebenginewebauthuxrequest.cpp
+++ b/src/core/api/qwebenginewebauthuxrequest.cpp
@@ -7,7 +7,7 @@
 
 /*!
     \qmltype WebEngineWebAuthUxRequest
-    \instantiates QWebEngineWebAuthUxRequest
+    \nativetype QWebEngineWebAuthUxRequest
     \inqmlmodule QtWebEngine
     \since QtWebEngine 6.7
     \brief Encapsulates the data of a WebAuth UX request.
@@ -94,8 +94,8 @@
 */
 
 /*!
-    \qmltype WebEngineWebAuthPinRequest
-    \instantiates QWebEngineWebAuthPinRequest
+    \qmlvaluetype webEngineWebAuthPinRequest
+    \ingroup qmlvaluetypes
     \inqmlmodule QtWebEngine
     \since QtWebEngine 6.8
     \brief Encapsulates the data of a PIN WebAuth UX request.
@@ -105,16 +105,17 @@
     \li The reason for the PIN prompt.
     \li The error details for the PIN prompt.
     \li The number of attempts remaining before a hard lock. Should be ignored unless
-        \l{WebEngineWebAuthPinRequest::reason} is
-        \l{WebEngineWebAuthUxRequest.PinEntryReason.Challenge}.
+        \l{webEngineWebAuthPinRequest::reason} is
+        \c{WebEngineWebAuthUxRequest.PinEntryReason.Challenge}.
     \li The minimum PIN length that the authenticator will accept for the PIN.
     \endlist
-    Use this structure to update the WebAuth UX dialog when the WebAuth UX state is \l
-    WebEngineWebAuthUxRequest.WebAuthUxState.CollectPin.
+    Use this structure to update the WebAuth UX dialog when the WebAuth UX
+    \l {WebEngineWebAuthUxRequest::}{state} is
+    \c {WebEngineWebAuthUxRequest.WebAuthUxState.CollectPin}.
 */
 
 /*!
-    \qmlproperty enumeration WebEngineWebAuthPinRequest::reason
+    \qmlproperty enumeration QtWebEngine::webEngineWebAuthPinRequest::reason
     \brief The reason for the PIN prompt.
 
     \value WebEngineWebAuthUxRequest.PinEntryReason.Set A new PIN is being set.
@@ -123,7 +124,7 @@
 */
 
 /*!
-    \qmlproperty enumeration WebEngineWebAuthPinRequest::error
+    \qmlproperty enumeration QtWebEngine::webEngineWebAuthPinRequest::error
     \brief The error details for the PIN prompt.
 
     \value WebEngineWebAuthUxRequest.PinEntryError.NoError No error has occurred.
@@ -135,14 +136,14 @@
 */
 
 /*!
-    \qmlproperty int WebEngineWebAuthPinRequest::remainingAttempts
+    \qmlproperty int QtWebEngine::webEngineWebAuthPinRequest::remainingAttempts
     \brief The number of attempts remaining before a hard lock. Should be ignored unless
-           \l{WebEngineWebAuthPinRequest::reason} is
-           \l{WebEngineWebAuthUxRequest.PinEntryReason.Challenge}.
+           \l{webEngineWebAuthPinRequest::reason} is
+           \c{WebEngineWebAuthUxRequest.PinEntryReason.Challenge}.
 */
 
 /*!
-    \qmlproperty int WebEngineWebAuthPinRequest::minPinLength
+    \qmlproperty int QtWebEngine::webEngineWebAuthPinRequest::minPinLength
     \brief The minimum PIN length that the authenticator will accept for the PIN.
 */
 
diff --git a/src/core/authentication_dialog_controller.cpp b/src/core/authentication_dialog_controller.cpp
index 4efd5dad3a7..87536b9e45c 100644
--- a/src/core/authentication_dialog_controller.cpp
+++ b/src/core/authentication_dialog_controller.cpp
@@ -14,7 +14,7 @@ AuthenticationDialogControllerPrivate::AuthenticationDialogControllerPrivate(bas
 {
 }
 
-void AuthenticationDialogControllerPrivate::dialogFinished(bool accepted, const QString &user, const QString &password)
+void AuthenticationDialogControllerPrivate::dialogFinished(bool accepted)
 {
     content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
                    base::BindOnce(&LoginDelegateQt::sendAuthToRequester,
@@ -51,9 +51,15 @@ bool AuthenticationDialogController::isProxy() const
     return d->isProxy;
 }
 
-void AuthenticationDialogController::accept(const QString &user, const QString &password)
+void AuthenticationDialogController::credentials(const QString &user, const QString &password)
 {
-    d->dialogFinished(true, user, password);
+    d->user = user;
+    d->password = password;
+}
+
+void AuthenticationDialogController::accept()
+{
+    d->dialogFinished(true);
 }
 
 void AuthenticationDialogController::reject()
diff --git a/src/core/authentication_dialog_controller.h b/src/core/authentication_dialog_controller.h
index 944ec4d159a..beb61f2c154 100644
--- a/src/core/authentication_dialog_controller.h
+++ b/src/core/authentication_dialog_controller.h
@@ -33,7 +33,8 @@ class Q_WEBENGINECORE_EXPORT AuthenticationDialogController : public QObject {
     bool isProxy() const;
 
 public Q_SLOTS:
-    void accept(const QString &user, const QString &password);
+    void credentials(const QString &user, const QString &password);
+    void accept();
     void reject();
 
 private:
diff --git a/src/core/authentication_dialog_controller_p.h b/src/core/authentication_dialog_controller_p.h
index 6c35a60dc66..3cea053fa02 100644
--- a/src/core/authentication_dialog_controller_p.h
+++ b/src/core/authentication_dialog_controller_p.h
@@ -14,13 +14,15 @@ class AuthenticationDialogControllerPrivate {
 
 public:
     AuthenticationDialogControllerPrivate(base::WeakPtr<LoginDelegateQt> loginDelegate);
-    void dialogFinished(bool accepted, const QString &user = QString(), const QString &password = QString());
+    void dialogFinished(bool accepted);
 
     base::WeakPtr<LoginDelegateQt> loginDelegate;
     QUrl url;
     QString host;
     QString realm;
     bool isProxy;
+    QString user;
+    QString password;
 };
 
 } // namespace QtWebEngineCore
diff --git a/src/core/authenticator_request_client_delegate_qt.cpp b/src/core/authenticator_request_client_delegate_qt.cpp
index 5f9fd9ca37d..8837dd85e96 100644
--- a/src/core/authenticator_request_client_delegate_qt.cpp
+++ b/src/core/authenticator_request_client_delegate_qt.cpp
@@ -150,7 +150,7 @@ void AuthenticatorRequestClientDelegateQt::SelectAccount(
         if (has_user_identifying_info) {
             QString userName = toQt(*response.user_entity->name);
             m_userMap[userName] = nIndex;
-            userList.append(userName);
+            userList.append(std::move(userName));
         }
     }
     m_dialogController->selectAccount(userList);
diff --git a/src/core/autofill_client_qt.cpp b/src/core/autofill_client_qt.cpp
index 3bdc62e199c..15162e9e926 100644
--- a/src/core/autofill_client_qt.cpp
+++ b/src/core/autofill_client_qt.cpp
@@ -70,12 +70,10 @@ void AutofillClientQt::ShowAutofillPopup(const autofill::AutofillClient::PopupOp
                                        autoSelectFirstSuggestion);
 }
 
-void AutofillClientQt::UpdateAutofillPopupDataListValues(const std::vector<std::u16string> &values,
-                                                         const std::vector<std::u16string> &labels)
+void AutofillClientQt::UpdateAutofillPopupDataListValues(
+        base::span<const autofill::SelectOption> datalist)
 {
-    Q_UNUSED(labels);
-
-    if (values.empty())
+    if (datalist.empty())
         HideAutofillPopup(autofill::PopupHidingReason::kNoSuggestions);
 }
 
@@ -99,12 +97,6 @@ std::vector<autofill::Suggestion> AutofillClientQt::GetPopupSuggestions() const
     return {};
 }
 
-void AutofillClientQt::UpdatePopup(const std::vector<autofill::Suggestion> &, autofill::PopupType, autofill::AutofillSuggestionTriggerSource)
-{
-    // Called by password_manager component only.
-    NOTIMPLEMENTED();
-}
-
 void AutofillClientQt::HideAutofillPopup(autofill::PopupHidingReason)
 {
     adapterClient()->hideAutofillPopup();
@@ -130,13 +122,6 @@ scoped_refptr<network::SharedURLLoaderFactory> AutofillClientQt::GetURLLoaderFac
     return nullptr;
 }
 
-void AutofillClientQt::PropagateAutofillPredictionsDeprecated(autofill::AutofillDriver *,
-                                                              const std::vector<autofill::FormStructure *> &)
-{
-    // For testing purposes only.
-    NOTIMPLEMENTED();
-}
-
 WebContentsAdapterClient *AutofillClientQt::adapterClient()
 {
     return WebContentsViewQt::from(
diff --git a/src/core/autofill_client_qt.h b/src/core/autofill_client_qt.h
index cd38de462e6..59b728cc7e6 100644
--- a/src/core/autofill_client_qt.h
+++ b/src/core/autofill_client_qt.h
@@ -19,9 +19,9 @@
 #include <vector>
 
 #include "base/memory/weak_ptr.h"
+#include "base/containers/span.h"
 #include "components/autofill/content/browser/content_autofill_client.h"
 #include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_contents_user_data.h"
 
 #include <QScopedPointer>
 
@@ -46,20 +46,18 @@ class AutofillClientQt : public autofill::ContentAutofillClient,
 
     void ShowAutofillPopup(const autofill::AutofillClient::PopupOpenArgs &open_args,
                            base::WeakPtr<autofill::AutofillPopupDelegate> delegate) override;
-    void UpdateAutofillPopupDataListValues(const std::vector<std::u16string> &values,
-                                           const std::vector<std::u16string> &labels) override;
+    void UpdateAutofillPopupDataListValues(
+            base::span<const autofill::SelectOption> datalist) override;
     void PinPopupView() override;
     PopupOpenArgs GetReopenPopupArgs(
             autofill::AutofillSuggestionTriggerSource trigger_source) const override;
     std::vector<autofill::Suggestion> GetPopupSuggestions() const override;
-    void UpdatePopup(const std::vector<autofill::Suggestion>& suggestions,
-                     autofill::PopupType popup_type,
-                     autofill::AutofillSuggestionTriggerSource trigger_source) override;
+    void UpdatePopup(const std::vector<autofill::Suggestion> &suggestions,
+                     autofill::FillingProduct main_filling_product,
+                     autofill::AutofillSuggestionTriggerSource trigger_source) override{};
     void HideAutofillPopup(autofill::PopupHidingReason reason) override;
     bool IsAutocompleteEnabled() const override;
     bool IsPasswordManagerEnabled() override;
-    void PropagateAutofillPredictionsDeprecated(autofill::AutofillDriver *,
-                                                const std::vector<autofill::FormStructure *> &) override;
     bool IsOffTheRecord() override;
     scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
 
diff --git a/src/core/autofill_popup_controller.cpp b/src/core/autofill_popup_controller.cpp
index a6baf5fc29f..a4ce0b7f885 100644
--- a/src/core/autofill_popup_controller.cpp
+++ b/src/core/autofill_popup_controller.cpp
@@ -9,6 +9,8 @@
 
 namespace QtWebEngineCore {
 
+using SuggestionPosition = autofill::AutofillPopupDelegate::SuggestionPosition;
+
 AutofillPopupController::AutofillPopupController(AutofillPopupControllerPrivate *dd)
 {
     Q_ASSERT(dd);
@@ -26,7 +28,7 @@ void AutofillPopupController::setCurrentIndex(const QModelIndex &index)
 
     if (m_currentIndex.isValid()) {
         const autofill::Suggestion &suggestion = d->suggestions[m_currentIndex.row()];
-        d->delegate->DidSelectSuggestion(suggestion, autofill::AutofillSuggestionTriggerSource::kFormControlElementClicked);
+        d->delegate->DidSelectSuggestion(suggestion);
     }
 
     Q_EMIT currentIndexChanged(index);
@@ -79,7 +81,7 @@ void AutofillPopupController::acceptSuggestion()
 
     const int index = m_currentIndex.row();
     const autofill::Suggestion &suggestion = d->suggestions[index];
-    d->delegate->DidAcceptSuggestion(suggestion, index, autofill::AutofillSuggestionTriggerSource::kFormControlElementClicked);
+    d->delegate->DidAcceptSuggestion(suggestion, SuggestionPosition{ .row = index });
 }
 
 void AutofillPopupController::notifyPopupShown()
diff --git a/src/core/browser_accessibility_qt.cpp b/src/core/browser_accessibility_qt.cpp
index de3347df36d..f2959350d0a 100644
--- a/src/core/browser_accessibility_qt.cpp
+++ b/src/core/browser_accessibility_qt.cpp
@@ -159,8 +159,8 @@ BrowserAccessibilityInterface::BrowserAccessibilityInterface(BrowserAccessibilit
 {
     if (parent() && parent()->object()) {
         m_object = new QObject(parent()->object());
-        QString name = toQt(q->GetAuthorUniqueId());
-        if (!name.isEmpty())
+        const std::u16string name = q->GetAuthorUniqueId();
+        if (!name.empty())
             m_object->setObjectName(name);
     }
 
@@ -408,6 +408,7 @@ QAccessible::Role BrowserAccessibilityInterface::role() const
     case ax::mojom::Role::kDirectory:
         return QAccessible::List;
     case ax::mojom::Role::kDisclosureTriangle:
+    case ax::mojom::Role::kDisclosureTriangleGrouped:
         return QAccessible::Button;
     case ax::mojom::Role::kGenericContainer:
         return QAccessible::Section;
@@ -617,7 +618,7 @@ QAccessible::Role BrowserAccessibilityInterface::role() const
         return QAccessible::ComboBox;
     case ax::mojom::Role::kPortal:
         return QAccessible::Button;
-    case ax::mojom::Role::kPre:
+    case ax::mojom::Role::kPreDeprecated:
         return QAccessible::Section;
     case ax::mojom::Role::kProgressIndicator:
         return QAccessible::ProgressBar;
diff --git a/src/core/browser_message_filter_qt.cpp b/src/core/browser_message_filter_qt.cpp
index e8fcb994ab1..8708fc8306d 100644
--- a/src/core/browser_message_filter_qt.cpp
+++ b/src/core/browser_message_filter_qt.cpp
@@ -19,6 +19,7 @@ BrowserMessageFilterQt::BrowserMessageFilterQt(int /*render_process_id*/, Profil
 {
 }
 
+#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
 bool BrowserMessageFilterQt::OnMessageReceived(const IPC::Message& message)
 {
     IPC_BEGIN_MESSAGE_MAP(BrowserMessageFilterQt, message)
@@ -31,6 +32,7 @@ bool BrowserMessageFilterQt::OnMessageReceived(const IPC::Message& message)
     IPC_END_MESSAGE_MAP()
     return true;
 }
+#endif
 
 void BrowserMessageFilterQt::OnAllowStorageAccess(int /*render_frame_id*/,
                                                   const GURL &origin_url,
diff --git a/src/core/browser_message_filter_qt.h b/src/core/browser_message_filter_qt.h
index 0f5721c824f..c19d2f352e9 100644
--- a/src/core/browser_message_filter_qt.h
+++ b/src/core/browser_message_filter_qt.h
@@ -5,6 +5,7 @@
 #define BROWSER_MESSAGE_FILTER_QT_H
 
 #include "base/functional/callback.h"
+#include "content/common/buildflags.h"
 #include "content/public/browser/browser_message_filter.h"
 
 class GURL;
@@ -20,8 +21,9 @@ class BrowserMessageFilterQt : public content::BrowserMessageFilter
     BrowserMessageFilterQt(int render_process_id, Profile *profile);
 
 private:
+#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
     bool OnMessageReceived(const IPC::Message& message) override;
-
+#endif
 
     void OnAllowStorageAccess(int render_frame_id,
                               const GURL &origin_url,
diff --git a/src/core/chromium_overrides.cpp b/src/core/chromium_overrides.cpp
index 28917e6c5c2..dd273d3b79b 100644
--- a/src/core/chromium_overrides.cpp
+++ b/src/core/chromium_overrides.cpp
@@ -17,6 +17,7 @@
 #include "gpu/vulkan/buildflags.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
 #include "ui/base/dragdrop/os_exchange_data_provider_factory.h"
+#include "ui/base/ozone_buildflags.h"
 
 #include <QGuiApplication>
 #include <QFontDatabase>
@@ -37,10 +38,12 @@
 #endif // defined(USE_OZONE)
 #endif // defined(ENABLE_VULKAN)
 
+#if BUILDFLAG(IS_OZONE_X11)
 void *GetQtXDisplay()
 {
     return GLContextHelper::getXDisplay();
 }
+#endif
 
 namespace content {
 class RenderViewHostDelegateView;
diff --git a/src/core/client_cert_select_controller.cpp b/src/core/client_cert_select_controller.cpp
index d6af984c187..e898da618ae 100644
--- a/src/core/client_cert_select_controller.cpp
+++ b/src/core/client_cert_select_controller.cpp
@@ -75,7 +75,7 @@ void ClientCertSelectController::select(const QSslCertificate &certificate)
         LOG(WARNING) << "ClientCertSelectController::select() certificate already selected";
         return;
     }
-    QByteArray derCertificate = certificate.toDer();
+    const QByteArray derCertificate = certificate.toDer();
     scoped_refptr<net::X509Certificate> selectedCert =
             net::X509Certificate::CreateFromBytes(base::make_span((const unsigned char *)derCertificate.constData(),
                                                                   (long unsigned)derCertificate.length()));
diff --git a/src/core/clipboard_qt.cpp b/src/core/clipboard_qt.cpp
index 3f49c6e3c26..25ea8897136 100644
--- a/src/core/clipboard_qt.cpp
+++ b/src/core/clipboard_qt.cpp
@@ -9,10 +9,12 @@
 #include "clipboard_change_observer.h"
 #include "type_conversion.h"
 
+#include "base/containers/map_util.h"
 #include "base/logging.h"
 #include "base/strings/utf_offset_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/types/variant_util.h"
+#include "base/types/optional_util.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/clipboard/custom_data_helper.h"
 #include "ui/base/clipboard/clipboard.h"
@@ -30,6 +32,8 @@
 
 #include <memory>
 
+using namespace Qt::StringLiterals;
+
 namespace QtWebEngineCore {
 
 static void registerMetaTypes()
@@ -128,15 +132,18 @@ void ClipboardQt::WriteText(base::StringPiece text)
     getUncommittedData()->setText(toQString(text));
 }
 
-void ClipboardQt::WriteHTML(base::StringPiece markup, absl::optional<base::StringPiece> source_url)
+void ClipboardQt::WriteHTML(base::StringPiece markup, absl::optional<base::StringPiece> source_url,
+                            ui::ClipboardContentType /*content_type*/)
 {
-    QString markup_string = toQString(markup);
+
+    QString markup_string;
 #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("<meta charset='utf-8'>"));
+    markup_string += "<meta charset='utf-8'>"_L1;
 #endif
+    markup_string += toQString(markup);
 
 #if !defined(Q_OS_WIN)
     getUncommittedData()->setHtml(markup_string);
@@ -178,8 +185,8 @@ void ClipboardQt::WriteBookmark(base::StringPiece title_in, base::StringPiece ur
 {
     // FIXME: Untested, seems to be used only for drag-n-drop.
     // Write as a mozilla url (UTF16: URL, newline, title).
-    QString url = toQString(url_in);
-    QString title = toQString(title_in);
+    const QString url = toQString(url_in);
+    const QString title = toQString(title_in);
 
     QByteArray data;
     data.append(reinterpret_cast<const char *>(url.utf16()), url.size() * 2);
@@ -234,7 +241,8 @@ void ClipboardQt::ReadAvailableTypes(ui::ClipboardBuffer type,
 
     if (mimeData->hasFormat(QString::fromLatin1(ui::kMimeTypeWebCustomData))) {
         const QByteArray customData = mimeData->data(QString::fromLatin1(ui::kMimeTypeWebCustomData));
-        ui::ReadCustomDataTypes(customData.constData(), customData.size(), types);
+        const base::span custom_data(customData.constData(), (unsigned long)customData.size());
+        ui::ReadCustomDataTypes(base::as_bytes(custom_data), types);
     }
 }
 
@@ -342,7 +350,9 @@ void ClipboardQt::ReadCustomData(ui::ClipboardBuffer clipboard_type, const std::
     if (!mimeData)
         return;
     const QByteArray customData = mimeData->data(QString::fromLatin1(ui::kMimeTypeWebCustomData));
-    ui::ReadCustomDataForType(customData.constData(), customData.size(), type, result);
+    const base::span custom_data(customData.constData(), (unsigned long)customData.size());
+    if (auto maybe_result = ui::ReadCustomDataForType(base::as_bytes(custom_data), type))
+        *result = *std::move(maybe_result);
 }
 
 void ClipboardQt::ReadBookmark(const ui::DataTransferEndpoint *data_dst, std::u16string *title, std::string *url) const
@@ -387,10 +397,9 @@ const ui::ClipboardSequenceNumberToken &ClipboardQt::GetSequenceNumber(ui::Clipb
             : clipboardChangeObserver()->getSelectionSequenceNumber();
 }
 
-const ui::DataTransferEndpoint *ClipboardQt::GetSource(ui::ClipboardBuffer buffer) const
+absl::optional<ui::DataTransferEndpoint> ClipboardQt::GetSource(ui::ClipboardBuffer buffer) const
 {
-    auto it = m_dataSrc.find(buffer);
-    return it == m_dataSrc.end() ? nullptr : it->second.get();
+    return base::OptionalFromPtr(base::FindPtrOrNull(m_dataSrc, buffer));
 }
 
 void ClipboardQt::ReadFilenames(ui::ClipboardBuffer buffer,
@@ -420,11 +429,6 @@ void ClipboardQt::WriteFilenames(std::vector<ui::FileInfo> filenames)
     getUncommittedData()->setUrls(urls);
 }
 
-void ClipboardQt::WriteUnsanitizedHTML(base::StringPiece markup, absl::optional<base::StringPiece> source_url)
-{
-    WriteHTML(std::move(markup), std::move(source_url));
-}
-
 #if defined(USE_OZONE)
 bool ClipboardQt::IsSelectionBufferAvailable() const
 {
diff --git a/src/core/clipboard_qt.h b/src/core/clipboard_qt.h
index 22f24cfe5ac..62dc2fbe6a3 100644
--- a/src/core/clipboard_qt.h
+++ b/src/core/clipboard_qt.h
@@ -36,7 +36,8 @@ class ClipboardQt : public ui::Clipboard
 
     std::vector<std::u16string> GetStandardFormats(ui::ClipboardBuffer buffer, const ui::DataTransferEndpoint *data_dst) const override;
 
-    const ui::DataTransferEndpoint *GetSource(ui::ClipboardBuffer buffer) const override;
+    absl::optional<ui::DataTransferEndpoint> GetSource(ui::ClipboardBuffer buffer) const override;
+
     void ReadFilenames(ui::ClipboardBuffer buffer,
                        const ui::DataTransferEndpoint *data_dst,
                        std::vector<ui::FileInfo> *result) const override;
@@ -48,7 +49,8 @@ class ClipboardQt : public ui::Clipboard
                                                  std::unique_ptr<ui::DataTransferEndpoint> data_src) override;
 
     void WriteText(base::StringPiece text) override;
-    void WriteHTML(base::StringPiece markup, absl::optional<base::StringPiece> source_url) override;
+    void WriteHTML(base::StringPiece markup, absl::optional<base::StringPiece> source_url,
+                   ui::ClipboardContentType content_type) override;
     void WriteRTF(base::StringPiece rtf) override;
     void WriteBookmark(base::StringPiece title, base::StringPiece url) override;
     void WriteWebSmartPaste() override;
@@ -56,7 +58,6 @@ class ClipboardQt : public ui::Clipboard
     void WriteData(const ui::ClipboardFormatType &format, base::span<const uint8_t> data) override;
     void WriteSvg(base::StringPiece markup) override;
     void WriteFilenames(std::vector<ui::FileInfo> filenames) override;
-    void WriteUnsanitizedHTML(base::StringPiece markup, absl::optional<base::StringPiece> source_url) override;
 
     base::flat_map<ui::ClipboardBuffer, std::unique_ptr<ui::DataTransferEndpoint>> m_dataSrc;
 };
diff --git a/src/core/clipboard_util_win.cpp b/src/core/clipboard_util_win.cpp
index 601e4418026..2dbd173d3c8 100644
--- a/src/core/clipboard_util_win.cpp
+++ b/src/core/clipboard_util_win.cpp
@@ -1,6 +1,6 @@
 // Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: BSD-3-Clause
 
 #include <string>
 
diff --git a/src/core/common/extensions/extensions_api_provider_qt.cpp b/src/core/common/extensions/extensions_api_provider_qt.cpp
index 1c8e89747a5..e479e20df63 100644
--- a/src/core/common/extensions/extensions_api_provider_qt.cpp
+++ b/src/core/common/extensions/extensions_api_provider_qt.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2018 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #include "extensions_api_provider_qt.h"
 
@@ -57,7 +58,7 @@ bool ExtensionsAPIProviderQt::IsAPISchemaGenerated(const std::string &name)
             api::QtWebEngineGeneratedSchemas::IsGenerated(name);
 }
 
-base::StringPiece ExtensionsAPIProviderQt::GetAPISchema(const std::string &name)
+std::string_view ExtensionsAPIProviderQt::GetAPISchema(const std::string &name)
 {
     if (!api::GeneratedSchemas::Get(name).empty())
         return api::GeneratedSchemas::Get(name);
diff --git a/src/core/common/extensions/extensions_api_provider_qt.h b/src/core/common/extensions/extensions_api_provider_qt.h
index 34e8bdd9f6b..7270039e5e8 100644
--- a/src/core/common/extensions/extensions_api_provider_qt.h
+++ b/src/core/common/extensions/extensions_api_provider_qt.h
@@ -1,5 +1,7 @@
 // Copyright (C) 2018 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
+
 #ifndef EXTENSIONS_API_PROVIDER_QT_H
 #define EXTENSIONS_API_PROVIDER_QT_H
 
@@ -18,7 +20,7 @@ class ExtensionsAPIProviderQt : public ExtensionsAPIProvider
     void AddPermissionFeatures(FeatureProvider* provider) override;
 
     bool IsAPISchemaGenerated(const std::string& name) override;
-    base::StringPiece GetAPISchema(const std::string& name) override;
+    std::string_view GetAPISchema(const std::string &name) override;
 
     // Adds feature definitions to the given |provider| of the specified type.
     void AddManifestFeatures(FeatureProvider* provider) override { }
diff --git a/src/core/common/extensions/extensions_client_qt.cpp b/src/core/common/extensions/extensions_client_qt.cpp
index 9240e6528d0..acbd0a5626f 100644
--- a/src/core/common/extensions/extensions_client_qt.cpp
+++ b/src/core/common/extensions/extensions_client_qt.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2018 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 // Portions copyright 2013 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
diff --git a/src/core/common/extensions/extensions_client_qt.h b/src/core/common/extensions/extensions_client_qt.h
index b2d128bf99c..d1677e09750 100644
--- a/src/core/common/extensions/extensions_client_qt.h
+++ b/src/core/common/extensions/extensions_client_qt.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2018 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 // Portions copyright 2013 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
diff --git a/src/core/common/qt_messages.cpp b/src/core/common/qt_messages.cpp
index bc3a1560d3b..aaaddf83010 100644
--- a/src/core/common/qt_messages.cpp
+++ b/src/core/common/qt_messages.cpp
@@ -1,5 +1,5 @@
 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: BSD-3-Clause
 
 // Get basic type definitions.
 #define IPC_MESSAGE_IMPL
diff --git a/src/core/common/qt_messages.h b/src/core/common/qt_messages.h
index 17adcbb0da1..eaf3d03f23b 100644
--- a/src/core/common/qt_messages.h
+++ b/src/core/common/qt_messages.h
@@ -1,5 +1,5 @@
 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: BSD-3-Clause
 
 // Multiply-included file, no traditional include guard.
 
diff --git a/src/core/compositor/compositor.cpp b/src/core/compositor/compositor.cpp
index 4c0bd4c0d80..9a99ddfd952 100644
--- a/src/core/compositor/compositor.cpp
+++ b/src/core/compositor/compositor.cpp
@@ -6,12 +6,15 @@
 #include "base/memory/ref_counted.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
 
+#include <QGuiApplication>
 #include <QHash>
 #include <QMutex>
 #include <QQuickWindow>
 
 namespace QtWebEngineCore {
 
+Q_LOGGING_CATEGORY(lcWebEngineCompositor, "qt.webengine.compositor");
+
 // Compositor::Id
 
 Compositor::Id::Id(viz::FrameSinkId fid) : client_id(fid.client_id()), sink_id(fid.sink_id()) { }
@@ -82,12 +85,13 @@ void Compositor::Observer::bind(Id id)
 
 void Compositor::Observer::unbind()
 {
-    DCHECK(m_binding);
     g_bindings.lock();
-    m_binding->observer = nullptr;
-    if (m_binding->compositor == nullptr)
-        delete m_binding;
-    m_binding = nullptr;
+    if (m_binding) {
+        m_binding->observer = nullptr;
+        if (m_binding->compositor == nullptr)
+            delete m_binding;
+        m_binding = nullptr;
+    }
     g_bindings.unlock();
 }
 
@@ -100,6 +104,11 @@ Compositor::Handle<Compositor> Compositor::Observer::compositor()
     return nullptr;
 }
 
+Compositor::Observer::~Observer()
+{
+    DCHECK(!m_binding); // check that unbind() was called by derived final class
+}
+
 // Compositor
 
 void Compositor::bind(Id id)
@@ -114,12 +123,13 @@ void Compositor::bind(Id id)
 
 void Compositor::unbind()
 {
-    DCHECK(m_binding);
     g_bindings.lock();
-    m_binding->compositor = nullptr;
-    if (m_binding->observer == nullptr)
-        delete m_binding;
-    m_binding = nullptr;
+    if (m_binding) {
+        m_binding->compositor = nullptr;
+        if (m_binding->observer == nullptr)
+            delete m_binding;
+        m_binding = nullptr;
+    }
     g_bindings.unlock();
 }
 
@@ -154,6 +164,30 @@ bool Compositor::textureIsFlipped()
 
 void Compositor::releaseResources() { }
 
+Compositor::Compositor(Type type) : m_type(type)
+{
+    if (Q_UNLIKELY(lcWebEngineCompositor().isDebugEnabled())) {
+        switch (m_type) {
+        case Type::Software:
+            qCDebug(lcWebEngineCompositor, "Compositor Type: Software");
+            break;
+        case Type::OpenGL:
+            qCDebug(lcWebEngineCompositor, "Compositor Type: OpenGL");
+            break;
+        case Type::Native:
+            qCDebug(lcWebEngineCompositor, "Compositor Type: Native");
+            break;
+        }
+    }
+    qCDebug(lcWebEngineCompositor, "QPA Platform Plugin: %ls",
+            qUtf16Printable(QGuiApplication::platformName()));
+}
+
+Compositor::~Compositor()
+{
+    DCHECK(!m_binding); // check that unbind() was called by derived final class
+}
+
 // static
 void Compositor::unlockBindings()
 {
diff --git a/src/core/compositor/compositor.h b/src/core/compositor/compositor.h
index 501559060d4..316c19684e1 100644
--- a/src/core/compositor/compositor.h
+++ b/src/core/compositor/compositor.h
@@ -18,6 +18,8 @@ class FrameSinkId;
 
 namespace QtWebEngineCore {
 
+Q_DECLARE_LOGGING_CATEGORY(lcWebEngineCompositor);
+
 // Produces composited frames for display.
 //
 // Used by quick/widgets libraries for accessing the frames and
@@ -90,7 +92,7 @@ class Q_WEBENGINECORE_EXPORT Compositor
 
     protected:
         Observer() = default;
-        ~Observer() { if (m_binding) unbind(); }
+        ~Observer();
 
     private:
         Binding *m_binding = nullptr;
@@ -132,12 +134,15 @@ class Q_WEBENGINECORE_EXPORT Compositor
     // Is the texture produced upside down?
     virtual bool textureIsFlipped();
 
+    // Are there resources to be released?
+    virtual bool hasResources() { return false; }
+
     // Release resources created in texture()
     virtual void releaseResources();
 
 protected:
-    Compositor(Type type) : m_type(type) { }
-    virtual ~Compositor() { if (m_binding) unbind(); }
+    Compositor(Type type);
+    virtual ~Compositor();
 
 private:
     template<typename T>
diff --git a/src/core/compositor/display_overrides.cpp b/src/core/compositor/display_overrides.cpp
index ebaa96dbb56..feb3dafa148 100644
--- a/src/core/compositor/display_overrides.cpp
+++ b/src/core/compositor/display_overrides.cpp
@@ -43,9 +43,11 @@ viz::SkiaOutputSurfaceImplOnGpu::CreateOutputDevice()
     if (graphicsApi == QSGRendererInterface::OpenGL) {
         if (gl::GetGLImplementation() != gl::kGLImplementationEGLANGLE) {
 #if !defined(Q_OS_MACOS)
-            return std::make_unique<QtWebEngineCore::DisplaySkiaOutputDevice>(
-                    context_state_, renderer_settings_.requires_alpha_channel,
-                    shared_gpu_deps_->memory_tracker(), GetDidSwapBuffersCompleteCallback());
+            if (context_state_->gr_context_type() == gpu::GrContextType::kGL) {
+                return std::make_unique<QtWebEngineCore::DisplaySkiaOutputDevice>(
+                        context_state_, renderer_settings_.requires_alpha_channel,
+                        shared_gpu_deps_->memory_tracker(), GetDidSwapBuffersCompleteCallback());
+            }
 #else
             qFatal("macOS only supports ANGLE.");
 #endif // !defined(Q_OS_MACOS)
diff --git a/src/core/compositor/display_skia_output_device.cpp b/src/core/compositor/display_skia_output_device.cpp
index 0ca466fe82a..07986278e80 100644
--- a/src/core/compositor/display_skia_output_device.cpp
+++ b/src/core/compositor/display_skia_output_device.cpp
@@ -26,12 +26,16 @@ class DisplaySkiaOutputDevice::Buffer
     }
     void initialize()
     {
+        qCDebug(lcWebEngineCompositor, "Initializing buffer %p with GrBackendTexture:", this);
+        qCDebug(lcWebEngineCompositor, "  Pixels size: %dx%d", m_shape.imageInfo.width(),
+                m_shape.imageInfo.height());
+
         const auto &colorType = m_shape.imageInfo.colorType();
         DCHECK(colorType != kUnknown_SkColorType);
 
         m_texture = m_parent->m_contextState->gr_context()->createBackendTexture(
                 m_shape.imageInfo.width(), m_shape.imageInfo.height(), colorType,
-                GrMipMapped::kNo, GrRenderable::kYes);
+                skgpu::Mipmapped::kNo, GrRenderable::kYes);
         DCHECK(m_texture.isValid());
 
         DCHECK(m_texture.backend() == GrBackendApi::kOpenGL);
@@ -95,6 +99,8 @@ DisplaySkiaOutputDevice::DisplaySkiaOutputDevice(
     , m_contextState(contextState)
     , m_requiresAlpha(requiresAlpha)
 {
+    qCDebug(lcWebEngineCompositor, "Display Skia Output Device: OpenGL");
+
     capabilities_.uses_default_gl_framebuffer = false;
     capabilities_.supports_surfaceless = true;
     capabilities_.preserve_buffer_content = true;
@@ -113,6 +119,7 @@ DisplaySkiaOutputDevice::DisplaySkiaOutputDevice(
 
 DisplaySkiaOutputDevice::~DisplaySkiaOutputDevice()
 {
+    unbind();
 }
 
 void DisplaySkiaOutputDevice::SetFrameSinkId(const viz::FrameSinkId &id)
@@ -193,6 +200,8 @@ void DisplaySkiaOutputDevice::waitForTexture()
 
 QSGTexture *DisplaySkiaOutputDevice::texture(QQuickWindow *win, uint32_t textureOptions)
 {
+    qCDebug(lcWebEngineCompositor, "OPENGL: Importing shared GL Texture.");
+
     if (!m_frontBuffer)
         return nullptr;
 
diff --git a/src/core/compositor/display_software_output_surface.cpp b/src/core/compositor/display_software_output_surface.cpp
index 2c208ca5736..62f23edb50a 100644
--- a/src/core/compositor/display_software_output_surface.cpp
+++ b/src/core/compositor/display_software_output_surface.cpp
@@ -21,6 +21,7 @@ class DisplaySoftwareOutputSurface::Device final : public viz::SoftwareOutputDev
 {
 public:
     Device(bool requiresAlpha);
+    ~Device() override;
 
     // Overridden from viz::SoftwareOutputDevice.
     void Resize(const gfx::Size &sizeInPixels, float devicePixelRatio) override;
@@ -50,6 +51,11 @@ DisplaySoftwareOutputSurface::Device::Device(bool requiresAlpha)
 {
 }
 
+DisplaySoftwareOutputSurface::Device::~Device()
+{
+    unbind();
+}
+
 void DisplaySoftwareOutputSurface::Device::Resize(const gfx::Size &sizeInPixels, float devicePixelRatio)
 {
     if (viewport_pixel_size_ == sizeInPixels && m_devicePixelRatio == devicePixelRatio)
diff --git a/src/core/compositor/native_skia_output_device.cpp b/src/core/compositor/native_skia_output_device.cpp
index 708692df7d5..944f5863e08 100644
--- a/src/core/compositor/native_skia_output_device.cpp
+++ b/src/core/compositor/native_skia_output_device.cpp
@@ -60,11 +60,14 @@ NativeSkiaOutputDevice::NativeSkiaOutputDevice(
     m_isNativeBufferSupported = ui::OzonePlatform::GetInstance()
                                         ->GetPlatformRuntimeProperties()
                                         .supports_native_pixmaps;
+    qCDebug(lcWebEngineCompositor, "Native Buffer Supported: %s",
+            m_isNativeBufferSupported ? "yes" : "no");
 #endif
 }
 
 NativeSkiaOutputDevice::~NativeSkiaOutputDevice()
 {
+    unbind();
 }
 
 void NativeSkiaOutputDevice::SetFrameSinkId(const viz::FrameSinkId &id)
@@ -122,6 +125,9 @@ SkSurface *NativeSkiaOutputDevice::BeginPaint(std::vector<GrBackendSemaphore> *e
         }
     }
     auto surface = m_backBuffer->beginWriteSkia();
+    if (!surface)
+        return nullptr;
+
     *end_semaphores = m_backBuffer->takeEndWriteSkiaSemaphores();
     return surface;
 }
@@ -164,6 +170,11 @@ void NativeSkiaOutputDevice::releaseTexture()
     }
 }
 
+bool NativeSkiaOutputDevice::hasResources()
+{
+    return m_frontBuffer && m_frontBuffer->textureCleanupCallback;
+}
+
 void NativeSkiaOutputDevice::releaseResources()
 {
     if (m_frontBuffer)
@@ -209,10 +220,15 @@ NativeSkiaOutputDevice::Buffer::Buffer(NativeSkiaOutputDevice *parent)
 
 NativeSkiaOutputDevice::Buffer::~Buffer()
 {
+    // FIXME: Can't be called in case of threaded rendering with unexposed window.
+    //DCHECK(!textureCleanupCallback);
+    if (textureCleanupCallback)
+        qWarning("NativeSkiaOutputDevice: Leaking graphics resources.");
+
     if (m_scopedSkiaWriteAccess)
         endWriteSkia(false);
 
-    if (!m_mailbox.IsZero())
+    if (!m_mailbox.IsZero() && m_parent->m_factory)
         m_parent->m_factory->DestroySharedImage(m_mailbox);
 }
 
@@ -222,14 +238,29 @@ NativeSkiaOutputDevice::Buffer::~Buffer()
 // found in the LICENSE file.
 bool NativeSkiaOutputDevice::Buffer::initialize()
 {
-    static const uint32_t kDefaultSharedImageUsage = gpu::SHARED_IMAGE_USAGE_SCANOUT
-            | gpu::SHARED_IMAGE_USAGE_DISPLAY_READ | gpu::SHARED_IMAGE_USAGE_DISPLAY_WRITE
+    qCDebug(lcWebEngineCompositor, "Initializing buffer %p with SharedImage:", this);
+
+    uint32_t kDefaultSharedImageUsage = gpu::SHARED_IMAGE_USAGE_DISPLAY_READ
+            | gpu::SHARED_IMAGE_USAGE_DISPLAY_WRITE
             | gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT;
+
+    if (m_parent->m_isNativeBufferSupported)
+        kDefaultSharedImageUsage |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
+
+    qCDebug(lcWebEngineCompositor, "  Usage: %s",
+            gpu::CreateLabelForSharedImageUsage(kDefaultSharedImageUsage).c_str());
+    qCDebug(lcWebEngineCompositor, "  Pixels size: %dx%d", m_shape.imageInfo.width(),
+            m_shape.imageInfo.height());
+
     auto mailbox = gpu::Mailbox::GenerateForSharedImage();
 
-    SkColorType skColorType = m_shape.imageInfo.colorType();
+    const SkColorType skColorType = m_shape.imageInfo.colorType();
+    const viz::SharedImageFormat sharedImageFormat =
+            viz::SkColorTypeToSinglePlaneSharedImageFormat(skColorType);
+    qCDebug(lcWebEngineCompositor, "  Format: %s", sharedImageFormat.ToString().c_str());
+
     if (!m_parent->m_factory->CreateSharedImage(
-                mailbox, viz::SkColorTypeToSinglePlaneSharedImageFormat(skColorType),
+                mailbox, sharedImageFormat,
                 { m_shape.imageInfo.width(), m_shape.imageInfo.height() }, m_shape.colorSpace,
                 kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, m_parent->m_deps->GetSurfaceHandle(),
                 kDefaultSharedImageUsage, "QWE_SharedImageBuffer")) {
@@ -259,6 +290,7 @@ bool NativeSkiaOutputDevice::Buffer::initialize()
 SkSurface *NativeSkiaOutputDevice::Buffer::beginWriteSkia()
 {
     DCHECK(!m_scopedSkiaWriteAccess);
+    DCHECK(!m_scopedOverlayReadAccess);
     DCHECK(!m_presentCount);
     DCHECK(m_endSemaphores.empty());
 
@@ -270,7 +302,10 @@ SkSurface *NativeSkiaOutputDevice::Buffer::beginWriteSkia()
     m_scopedSkiaWriteAccess = m_skiaRepresentation->BeginScopedWriteAccess(
             m_shape.sampleCount, surface_props, &beginSemaphores, &m_endSemaphores,
             gpu::SharedImageRepresentation::AllowUnclearedAccess::kYes);
-    DCHECK(m_scopedSkiaWriteAccess);
+    if (!m_scopedSkiaWriteAccess) {
+        qWarning("SKIA: Failed to begin write access.");
+        return nullptr;
+    }
     if (!beginSemaphores.empty()) {
         m_scopedSkiaWriteAccess->surface()->wait(beginSemaphores.size(), beginSemaphores.data(),
                                                  /*deleteSemaphoresAfterWait=*/false);
diff --git a/src/core/compositor/native_skia_output_device.h b/src/core/compositor/native_skia_output_device.h
index 2c35cef77b4..fe76e26b05f 100644
--- a/src/core/compositor/native_skia_output_device.h
+++ b/src/core/compositor/native_skia_output_device.h
@@ -77,6 +77,7 @@ class NativeSkiaOutputDevice : public viz::SkiaOutputDevice, public Compositor
     void swapFrame() override;
     void waitForTexture() override;
     void releaseTexture() override;
+    bool hasResources() override;
     void releaseResources() override;
     bool textureIsFlipped() override;
     QSize size() override;
@@ -164,18 +165,17 @@ class NativeSkiaOutputDevice : public viz::SkiaOutputDevice, public Compositor
 
     void SwapBuffersFinished();
 
+    bool m_requiresAlpha;
+    gpu::SharedImageFactory *const m_factory;
+    gpu::SharedImageRepresentationFactory *const m_representationFactory;
+    viz::SkiaOutputSurfaceDependency *const m_deps;
     mutable QMutex m_mutex;
     Shape m_shape;
     std::unique_ptr<Buffer> m_middleBuffer;
     std::unique_ptr<Buffer> m_backBuffer;
     viz::OutputSurfaceFrame m_frame;
     bool m_readyToUpdate = false;
-    bool m_requiresAlpha;
     scoped_refptr<base::SingleThreadTaskRunner> m_gpuTaskRunner;
-
-    const raw_ptr<gpu::SharedImageFactory> m_factory;
-    const raw_ptr<gpu::SharedImageRepresentationFactory> m_representationFactory;
-    const raw_ptr<viz::SkiaOutputSurfaceDependency> m_deps;
 };
 
 } // namespace QtWebEngineCore
diff --git a/src/core/compositor/native_skia_output_device_direct3d11.cpp b/src/core/compositor/native_skia_output_device_direct3d11.cpp
index 2f1ed5f61ee..ff469382f84 100644
--- a/src/core/compositor/native_skia_output_device_direct3d11.cpp
+++ b/src/core/compositor/native_skia_output_device_direct3d11.cpp
@@ -21,6 +21,8 @@ NativeSkiaOutputDeviceDirect3D11::NativeSkiaOutputDeviceDirect3D11(
                              shared_image_factory, shared_image_representation_factory,
                              didSwapBufferCompleteCallback)
 {
+    qCDebug(lcWebEngineCompositor, "Native Skia Output Device: Direct3D11");
+
     SkColorType skColorType = kRGBA_8888_SkColorType;
     capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBA_8888)] = skColorType;
     capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBX_8888)] = skColorType;
@@ -37,43 +39,56 @@ QSGTexture *NativeSkiaOutputDeviceDirect3D11::texture(QQuickWindow *win, uint32_
 
     absl::optional<gl::DCLayerOverlayImage> overlayImage = m_frontBuffer->overlayImage();
     if (!overlayImage) {
-        qWarning("No overlay image.");
+        qWarning("D3D: No overlay image.");
         return nullptr;
     }
 
-    QSGRendererInterface *ri = win->rendererInterface();
+    qCDebug(lcWebEngineCompositor, "D3D: Importing DXGI Resource into D3D11 Texture.");
 
-    HRESULT status = S_OK;
-    HANDLE sharedHandle = nullptr;
-    IDXGIResource1 *resource = nullptr;
-    if (!overlayImage->nv12_texture()) {
-        qWarning("No D3D texture.");
+    Q_ASSERT(overlayImage->type() == gl::DCLayerOverlayType::kNV12Texture);
+    Microsoft::WRL::ComPtr<ID3D11Texture2D> chromeTexture = overlayImage->nv12_texture();
+    if (!chromeTexture) {
+        qWarning("D3D: No D3D texture.");
         return nullptr;
     }
-    status = overlayImage->nv12_texture()->QueryInterface(__uuidof(IDXGIResource1),
-                                                          (void **)&resource);
-    Q_ASSERT(status == S_OK);
-    status = resource->CreateSharedHandle(NULL, DXGI_SHARED_RESOURCE_READ, NULL, &sharedHandle);
-    Q_ASSERT(status == S_OK);
-    Q_ASSERT(sharedHandle);
-    resource->Release();
+
+    HRESULT hr;
+
+    Microsoft::WRL::ComPtr<IDXGIResource1> dxgiResource;
+    hr = chromeTexture->QueryInterface(IID_PPV_ARGS(&dxgiResource));
+    Q_ASSERT(SUCCEEDED(hr));
+
+    HANDLE sharedHandle = INVALID_HANDLE_VALUE;
+    hr = dxgiResource->CreateSharedHandle(nullptr, DXGI_SHARED_RESOURCE_READ, nullptr,
+                                          &sharedHandle);
+    Q_ASSERT(SUCCEEDED(hr));
+    Q_ASSERT(sharedHandle != INVALID_HANDLE_VALUE);
 
     // Pass texture between two D3D devices:
-    ID3D11Device1 *device = static_cast<ID3D11Device1 *>(
+    QSGRendererInterface *ri = win->rendererInterface();
+    ID3D11Device *device = static_cast<ID3D11Device *>(
             ri->getResource(win, QSGRendererInterface::DeviceResource));
-
-    ID3D11Texture2D *qtTexture;
-    status = device->OpenSharedResource1(sharedHandle, __uuidof(ID3D11Texture2D),
-                                         (void **)&qtTexture);
-    if (status != S_OK) {
-        qWarning("Failed to share D3D11 texture (%s). This will result in failed rendering. Report "
-                 "the bug, and try restarting with QTWEBENGINE_CHROMIUM_FLAGS=--disble-gpu",
-                 qPrintable(QSystemError::windowsComString(status)));
+    if (!device) {
+        qWarning("D3D: No D3D device.");
         ::CloseHandle(sharedHandle);
         return nullptr;
     }
 
+    Microsoft::WRL::ComPtr<ID3D11Device1> device1;
+    hr = device->QueryInterface(IID_PPV_ARGS(&device1));
+    Q_ASSERT(SUCCEEDED(hr));
+
+    ID3D11Texture2D *qtTexture = nullptr;
+    hr = device1->OpenSharedResource1(sharedHandle, IID_PPV_ARGS(&qtTexture));
+    if (FAILED(hr)) {
+        qWarning("D3D: Failed to share D3D11 texture (%ls). This will result in failed rendering. "
+                 "Report the bug, and try restarting with QTWEBENGINE_CHROMIUM_FLAGS=--disble-gpu",
+                 qUtf16Printable(QSystemError::windowsComString(hr)));
+        ::CloseHandle(sharedHandle);
+        return nullptr;
+    }
     Q_ASSERT(qtTexture);
+
     QQuickWindow::CreateTextureOptions texOpts(textureOptions);
     QSGTexture *texture =
             QNativeInterface::QSGD3D11Texture::fromNative(qtTexture, win, size(), texOpts);
diff --git a/src/core/compositor/native_skia_output_device_metal.cpp b/src/core/compositor/native_skia_output_device_metal.cpp
index a9d6e4fd53b..5ca0a58b689 100644
--- a/src/core/compositor/native_skia_output_device_metal.cpp
+++ b/src/core/compositor/native_skia_output_device_metal.cpp
@@ -18,6 +18,8 @@ NativeSkiaOutputDeviceMetal::NativeSkiaOutputDeviceMetal(
                              shared_image_factory, shared_image_representation_factory,
                              didSwapBufferCompleteCallback)
 {
+    qCDebug(lcWebEngineCompositor, "Native Skia Output Device: Metal");
+
     SkColorType skColorType = kRGBA_8888_SkColorType;
     capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBA_8888)] = skColorType;
     capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBX_8888)] = skColorType;
@@ -42,6 +44,8 @@ QSGTexture *NativeSkiaOutputDeviceMetal::texture(QQuickWindow *win, uint32_t tex
         return nullptr;
     }
 
+    qCDebug(lcWebEngineCompositor, "METAL: Importing IOSurface into Metal Texture.");
+
     // This is a workaround to not to release metal texture too early.
     // In RHI, QMetalTexture wraps MTLTexture. QMetalTexture seems to be only destructed after the
     // next MTLTexture is imported. The "old" MTLTexture can be still pontentially used by RHI
diff --git a/src/core/compositor/native_skia_output_device_opengl.cpp b/src/core/compositor/native_skia_output_device_opengl.cpp
index 058573b9e55..be9038b84b9 100644
--- a/src/core/compositor/native_skia_output_device_opengl.cpp
+++ b/src/core/compositor/native_skia_output_device_opengl.cpp
@@ -1,6 +1,10 @@
 // Copyright (C) 2024 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE.Chromium file.
+
 #include "native_skia_output_device_opengl.h"
 
 #include <QtGui/qopenglcontext.h>
@@ -8,8 +12,185 @@
 #include <QtQuick/qquickwindow.h>
 #include <QtQuick/qsgtexture.h>
 
+#include "ui/base/ozone_buildflags.h"
+#include "ui/gl/gl_implementation.h"
+
+#if defined(USE_OZONE)
+#include "ui/gl/gl_bindings.h"
+#undef glBindTexture
+#undef glCreateMemoryObjectsEXT
+#undef glDeleteMemoryObjectsEXT
+#undef glDeleteTextures
+#undef glGenTextures
+#undef glGetError
+#undef glImportMemoryFdEXT
+#undef glIsMemoryObjectEXT
+#undef glMemoryObjectParameterivEXT
+#undef glTexParameteri
+#undef glTexStorageMem2DEXT
+
+#include "base/posix/eintr_wrapper.h"
+#include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
+#include "ui/gfx/linux/drm_util_linux.h"
+#include "ui/gfx/linux/native_pixmap_dmabuf.h"
+
+#if BUILDFLAG(IS_OZONE_X11)
+#include "ui/gfx/x/connection.h"
+#include "ui/gfx/x/dri3.h"
+#include "ui/gfx/x/future.h"
+#include "ui/gfx/x/glx.h"
+#include "ui/gfx/x/xproto.h"
+#endif // BUILDFLAG(IS_OZONE_X11)
+
+#if BUILDFLAG(ENABLE_VULKAN)
+#if BUILDFLAG(IS_OZONE_X11)
+// We need to define USE_VULKAN_XCB for proper vulkan function pointers.
+// Avoiding it may lead to call wrong vulkan functions.
+// This is originally defined in chromium/gpu/vulkan/BUILD.gn.
+#define USE_VULKAN_XCB
+#endif // BUILDFLAG(IS_OZONE_X11)
+#include "components/viz/common/gpu/vulkan_context_provider.h"
+#include "gpu/vulkan/vulkan_device_queue.h"
+#include "gpu/vulkan/vulkan_function_pointers.h"
+#include "third_party/skia/include/gpu/vk/GrVkTypes.h"
+#include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h"
+#endif // BUILDFLAG(ENABLE_VULKAN)
+
+// Keep it at the end.
+#include "ozone/gl_context_qt.h"
+#endif // defined(USE_OZONE)
+
 namespace QtWebEngineCore {
 
+#if BUILDFLAG(IS_OZONE_X11)
+namespace {
+
+// Based on //ui/ozone/platform/x11/native_pixmap_egl_x11_binding.cc
+x11::Pixmap XPixmapFromNativePixmap(const gfx::NativePixmap &nativePixmap)
+{
+    // Hard coded values for gfx::BufferFormat::BGRA_8888:
+    const uint8_t depth = 32;
+    const uint8_t bpp = 32;
+
+    const int dmaBufFd = HANDLE_EINTR(dup(nativePixmap.GetDmaBufFd(0)));
+    if (dmaBufFd < 0) {
+        qWarning("Could not import the dma-buf as an XPixmap because the FD couldn't be dup()ed.");
+        return x11::Pixmap::None;
+    }
+    x11::RefCountedFD refCountedFD(dmaBufFd);
+
+    uint32_t size = base::checked_cast<uint32_t>(nativePixmap.GetDmaBufPlaneSize(0));
+    uint16_t width = base::checked_cast<uint16_t>(nativePixmap.GetBufferSize().width());
+    uint16_t height = base::checked_cast<uint16_t>(nativePixmap.GetBufferSize().height());
+    uint16_t stride = base::checked_cast<uint16_t>(nativePixmap.GetDmaBufPitch(0));
+
+    auto *connection = x11::Connection::Get();
+    const x11::Pixmap pixmapId = connection->GenerateId<x11::Pixmap>();
+    if (pixmapId == x11::Pixmap::None) {
+        qWarning("Could not import the dma-buf as an XPixmap because an ID couldn't be generated.");
+        return x11::Pixmap::None;
+    }
+
+    auto response = connection->dri3()
+                            .PixmapFromBuffer(pixmapId, connection->default_root(), size, width,
+                                              height, stride, depth, bpp, refCountedFD)
+                            .Sync();
+
+    if (response.error) {
+        qWarning("Could not import the dma-buf as an XPixmap because PixmapFromBuffer() "
+                 "failed; error: %s",
+                 response.error->ToString().c_str());
+        return x11::Pixmap::None;
+    }
+
+    return pixmapId;
+}
+
+void FreeXPixmap(x11::Pixmap pixmap)
+{
+    auto *connection = x11::Connection::Get();
+    connection->FreePixmap({ pixmap });
+}
+
+GLXFBConfig GetFBConfig(Display *display)
+{
+    // clang-format off
+    static const int configAttribs[] = {
+        GLX_RED_SIZE, 8,
+        GLX_GREEN_SIZE, 8,
+        GLX_BLUE_SIZE, 8,
+        GLX_ALPHA_SIZE, 8,
+        GLX_BUFFER_SIZE, 32,
+        GLX_BIND_TO_TEXTURE_RGBA_EXT, 1,
+        GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
+        GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
+        GLX_DOUBLEBUFFER, 0,
+        GLX_Y_INVERTED_EXT, static_cast<int>(GLX_DONT_CARE),
+        0
+    };
+    // clang-format on
+
+    int numConfigs = 0;
+    GLXFBConfig *configs = glXChooseFBConfig(display, /* screen */ 0, configAttribs, &numConfigs);
+    if (!configs || numConfigs < 1)
+        qFatal("GLX: Failed to find frame buffer configuration for pixmap.");
+
+    return configs[0];
+}
+
+} // namespace
+#endif // BUILDFLAG(IS_OZONE_X11)
+
+class ScopedGLContextForCleanup
+{
+public:
+    ScopedGLContextForCleanup(QOpenGLContext *createContext, QSurface *createSurface)
+        : m_createContext(createContext), m_currentContext(QOpenGLContext::currentContext())
+    {
+        if (m_createContext == m_currentContext)
+            return;
+
+        if (!m_createContext->isValid()) {
+            skipCleanup = true;
+            return;
+        }
+
+        if (m_currentContext)
+            m_currentSurface = m_currentContext->surface();
+
+        if (!createContext->makeCurrent(createSurface)) {
+            skipCleanup = true;
+            qWarning("Failed to make OpenGL context current for clean-up, OpenGL resources will "
+                     "not be destroyed.");
+        }
+    }
+
+    ~ScopedGLContextForCleanup()
+    {
+        if (!m_currentContext || m_createContext == m_currentContext || skipCleanup)
+            return;
+
+        if (!m_currentContext->makeCurrent(m_currentSurface))
+            qFatal("Failed to restore OpenGL context after clean-up.");
+    }
+
+    void deleteTexture(GLuint glTexture)
+    {
+        if (skipCleanup)
+            return;
+
+        auto *glFun = m_createContext->functions();
+        Q_ASSERT(glFun->glGetError() == GL_NO_ERROR);
+        glFun->glDeleteTextures(1, &glTexture);
+    }
+
+private:
+    QOpenGLContext *m_createContext;
+    QOpenGLContext *m_currentContext;
+    QSurface *m_currentSurface = nullptr;
+    bool skipCleanup = false;
+};
+
 NativeSkiaOutputDeviceOpenGL::NativeSkiaOutputDeviceOpenGL(
         scoped_refptr<gpu::SharedContextState> contextState, bool requiresAlpha,
         gpu::MemoryTracker *memoryTracker, viz::SkiaOutputSurfaceDependency *dependency,
@@ -20,7 +201,16 @@ NativeSkiaOutputDeviceOpenGL::NativeSkiaOutputDeviceOpenGL(
                              shared_image_factory, shared_image_representation_factory,
                              didSwapBufferCompleteCallback)
 {
+    qCDebug(lcWebEngineCompositor, "Native Skia Output Device: OpenGL");
+
     SkColorType skColorType = kRGBA_8888_SkColorType;
+#if BUILDFLAG(IS_OZONE_X11)
+    if (GLContextHelper::getGlxPlatformInterface()
+        && m_contextState->gr_context_type() == gpu::GrContextType::kGL) {
+        skColorType = kBGRA_8888_SkColorType;
+    }
+#endif // BUILDFLAG(IS_OZONE_X11)
+
     capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBA_8888)] = skColorType;
     capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBX_8888)] = skColorType;
     capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::BGRA_8888)] = skColorType;
@@ -40,10 +230,49 @@ QSGTexture *NativeSkiaOutputDeviceOpenGL::texture(QQuickWindow *win, uint32_t te
 
 #if defined(USE_OZONE)
     scoped_refptr<gfx::NativePixmap> nativePixmap = m_frontBuffer->nativePixmap();
+#if BUILDFLAG(ENABLE_VULKAN)
+    GrVkImageInfo vkImageInfo;
+    if (!nativePixmap) {
+        if (m_isNativeBufferSupported) {
+            qWarning("No native pixmap.");
+            return nullptr;
+        }
+
+        sk_sp<SkImage> skImage = m_frontBuffer->skImage();
+        if (!skImage) {
+            qWarning("No SkImage.");
+            return nullptr;
+        }
+
+        if (!skImage->isTextureBacked()) {
+            qWarning("SkImage is not backed by GPU texture.");
+            return nullptr;
+        }
+
+        GrBackendTexture backendTexture;
+        bool success = SkImages::GetBackendTextureFromImage(skImage, &backendTexture, false);
+        if (!success || !backendTexture.isValid()) {
+            qWarning("Failed to retrieve backend texture from SkImage.");
+            return nullptr;
+        }
+
+        if (backendTexture.backend() != GrBackendApi::kVulkan) {
+            qWarning("Backend texture is not a Vulkan texture.");
+            return nullptr;
+        }
+
+        GrBackendTextures::GetVkImageInfo(backendTexture, &vkImageInfo);
+        if (vkImageInfo.fAlloc.fMemory == VK_NULL_HANDLE) {
+            qWarning("Unable to access Vulkan memory.");
+            return nullptr;
+        }
+    }
+#else
     if (!nativePixmap) {
         qWarning("No native pixmap.");
         return nullptr;
     }
+#endif // BUILDFLAG(ENABLE_VULKAN)
 #elif defined(Q_OS_WIN)
     auto overlayImage = m_frontBuffer->overlayImage();
     if (!overlayImage) {
@@ -62,23 +291,193 @@ QSGTexture *NativeSkiaOutputDeviceOpenGL::texture(QQuickWindow *win, uint32_t te
     QSGTexture *texture = nullptr;
 
 #if defined(USE_OZONE)
-    // TODO(QTBUG-112281): Add ANGLE support to Linux.
-    QT_NOT_YET_IMPLEMENTED
+    QOpenGLContext *glContext = QOpenGLContext::currentContext();
+    Q_ASSERT(glContext);
+    auto glFun = glContext->functions();
+    GLuint glTexture = 0;
+
+    if (nativePixmap) {
+        Q_ASSERT(m_contextState->gr_context_type() == gpu::GrContextType::kGL);
+
+#if BUILDFLAG(IS_OZONE_X11)
+        if (GLContextHelper::getGlxPlatformInterface()) {
+            qCDebug(lcWebEngineCompositor, "GLX: Importing NativePixmap into GL Texture.");
+
+            x11::Pixmap pixmapId =
+                    XPixmapFromNativePixmap(*(gfx::NativePixmapDmaBuf *)nativePixmap.get());
+            if (pixmapId == x11::Pixmap::None)
+                qFatal("GLX: Failed to import XPixmap.");
+
+            // clang-format off
+            static const int pixmapAttribs[] = {
+                 GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
+                 GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
+                 0
+            };
+            // clang-format on
+
+            Display *display = static_cast<Display *>(GLContextHelper::getXDisplay());
+            GLXPixmap glxPixmap = glXCreatePixmap(display, GetFBConfig(display),
+                                                  static_cast<::Pixmap>(pixmapId), pixmapAttribs);
+
+            glFun->glGenTextures(1, &glTexture);
+            glFun->glBindTexture(GL_TEXTURE_2D, glTexture);
+            glXBindTexImageEXT(display, glxPixmap, GLX_FRONT_LEFT_EXT, nullptr);
+            glFun->glBindTexture(GL_TEXTURE_2D, 0);
+
+            QSurface *createSurface = glContext->surface();
+            m_frontBuffer->textureCleanupCallback = [glContext, createSurface, glFun, glTexture,
+                                                     display, glxPixmap, pixmapId]() {
+                ScopedGLContextForCleanup cleanupContext(glContext, createSurface);
+                cleanupContext.deleteTexture(glTexture);
+                glXDestroyGLXPixmap(display, glxPixmap);
+                FreeXPixmap(pixmapId);
+            };
+        }
+#endif // BUILDFLAG(IS_OZONE_X11)
+
+        if (GLContextHelper::getEglPlatformInterface()) {
+            qCDebug(lcWebEngineCompositor, "EGL: Importing NativePixmap into GL Texture.");
+
+            EGLHelper *eglHelper = EGLHelper::instance();
+            auto *eglFun = eglHelper->functions();
+
+            const int dmaBufFd = HANDLE_EINTR(dup(nativePixmap->GetDmaBufFd(0)));
+            if (dmaBufFd < 0) {
+                qFatal("Could not import the dma-buf as an EGLImage because the FD couldn't be "
+                       "dup()ed.");
+            }
+            base::ScopedFD scopedFd(dmaBufFd);
+
+            int drmFormat = ui::GetFourCCFormatFromBufferFormat(nativePixmap->GetBufferFormat());
+            uint64_t modifier = nativePixmap->GetBufferFormatModifier();
+
+            // clang-format off
+            EGLAttrib const attributeList[] = {
+                EGL_WIDTH, size().width(),
+                EGL_HEIGHT, size().height(),
+                EGL_LINUX_DRM_FOURCC_EXT, drmFormat,
+                EGL_DMA_BUF_PLANE0_FD_EXT, scopedFd.get(),
+                EGL_DMA_BUF_PLANE0_OFFSET_EXT, static_cast<EGLAttrib>(nativePixmap->GetDmaBufOffset(0)),
+                EGL_DMA_BUF_PLANE0_PITCH_EXT, nativePixmap->GetDmaBufPitch(0),
+                EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, static_cast<EGLAttrib>(modifier & 0xffffffff),
+                EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, static_cast<EGLAttrib>(modifier >> 32),
+                EGL_NONE
+            };
+            // clang-format on
+            EGLImage eglImage = eglFun->eglCreateImage(GLContextHelper::getEGLDisplay(),
+                                                       EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT,
+                                                       (EGLClientBuffer)NULL, attributeList);
+            Q_ASSERT(eglImage != EGL_NO_IMAGE_KHR);
+
+            static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC imageTargetTexture =
+                    (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)glContext->getProcAddress(
+                            "glEGLImageTargetTexture2DOES");
+
+            glFun->glGenTextures(1, &glTexture);
+            glFun->glBindTexture(GL_TEXTURE_2D, glTexture);
+            imageTargetTexture(GL_TEXTURE_2D, eglImage);
+            glFun->glBindTexture(GL_TEXTURE_2D, 0);
+
+            QSurface *createSurface = glContext->surface();
+            m_frontBuffer->textureCleanupCallback = [glContext, createSurface, glFun, eglFun,
+                                                     glTexture, eglImage]() {
+                ScopedGLContextForCleanup cleanupContext(glContext, createSurface);
+                cleanupContext.deleteTexture(glTexture);
+                eglFun->eglDestroyImage(GLContextHelper::getEGLDisplay(), eglImage);
+            };
+        }
+    } else {
+#if BUILDFLAG(ENABLE_VULKAN)
+        qCDebug(lcWebEngineCompositor, "VULKAN: Importing VkImage into GL Texture.");
+        Q_ASSERT(m_contextState->gr_context_type() == gpu::GrContextType::kVulkan);
+
+        gpu::VulkanFunctionPointers *vfp = gpu::GetVulkanFunctionPointers();
+        gpu::VulkanDeviceQueue *vulkanDeviceQueue =
+                m_contextState->vk_context_provider()->GetDeviceQueue();
+        VkDevice vulkanDevice = vulkanDeviceQueue->GetVulkanDevice();
+
+        VkDeviceMemory importedImageMemory = vkImageInfo.fAlloc.fMemory;
+        VkDeviceSize importedImageSize = vkImageInfo.fAlloc.fSize;
+
+        VkMemoryGetFdInfoKHR exportInfo = {
+            .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
+            .pNext = nullptr,
+            .memory = importedImageMemory,
+            .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR,
+        };
+
+        int fd = -1;
+        if (vfp->vkGetMemoryFdKHR(vulkanDevice, &exportInfo, &fd) != VK_SUCCESS)
+            qFatal("VULKAN: Unable to extract file descriptor out of external VkImage.");
+
+        static PFNGLCREATEMEMORYOBJECTSEXTPROC glCreateMemoryObjectsEXT =
+                (PFNGLCREATEMEMORYOBJECTSEXTPROC)glContext->getProcAddress(
+                        "glCreateMemoryObjectsEXT");
+        static PFNGLIMPORTMEMORYFDEXTPROC glImportMemoryFdEXT =
+                (PFNGLIMPORTMEMORYFDEXTPROC)glContext->getProcAddress("glImportMemoryFdEXT");
+        static PFNGLISMEMORYOBJECTEXTPROC glIsMemoryObjectEXT =
+                (PFNGLISMEMORYOBJECTEXTPROC)glContext->getProcAddress("glIsMemoryObjectEXT");
+        static PFNGLMEMORYOBJECTPARAMETERIVEXTPROC glMemoryObjectParameterivEXT =
+                (PFNGLMEMORYOBJECTPARAMETERIVEXTPROC)glContext->getProcAddress(
+                        "glMemoryObjectParameterivEXT");
+        static PFNGLTEXSTORAGEMEM2DEXTPROC glTexStorageMem2DEXT =
+                (PFNGLTEXSTORAGEMEM2DEXTPROC)glContext->getProcAddress("glTexStorageMem2DEXT");
+
+        // Import memory object
+        GLuint glMemoryObject;
+        glCreateMemoryObjectsEXT(1, &glMemoryObject);
+        GLint dedicated = GL_TRUE;
+        glMemoryObjectParameterivEXT(glMemoryObject, GL_DEDICATED_MEMORY_OBJECT_EXT, &dedicated);
+        glImportMemoryFdEXT(glMemoryObject, importedImageSize, GL_HANDLE_TYPE_OPAQUE_FD_EXT, fd);
+        if (!glIsMemoryObjectEXT(glMemoryObject))
+            qFatal("VULKAN: Failed to import memory object.");
+
+        // Bind memory object to texture
+        glFun->glGenTextures(1, &glTexture);
+        glFun->glBindTexture(GL_TEXTURE_2D, glTexture);
+        glFun->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_TILING_EXT,
+                               vkImageInfo.fImageTiling == VK_IMAGE_TILING_OPTIMAL
+                                       ? GL_OPTIMAL_TILING_EXT
+                                       : GL_LINEAR_TILING_EXT);
+        glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, size().width(), size().height(),
+                             glMemoryObject, 0);
+        glFun->glBindTexture(GL_TEXTURE_2D, 0);
+
+        QSurface *createSurface = glContext->surface();
+        m_frontBuffer->textureCleanupCallback = [glContext, createSurface, glTexture,
+                                                 glMemoryObject]() {
+            ScopedGLContextForCleanup cleanupContext(glContext, createSurface);
+            static PFNGLDELETEMEMORYOBJECTSEXTPROC glDeleteMemoryObjectsEXT =
+                    (PFNGLDELETEMEMORYOBJECTSEXTPROC)glContext->getProcAddress(
+                            "glDeleteMemoryObjectsEXT");
+
+            glDeleteMemoryObjectsEXT(1, &glMemoryObject);
+            cleanupContext.deleteTexture(glTexture);
+        };
+#else
+        Q_UNREACHABLE();
+#endif // BUILDFLAG(ENABLE_VULKAN)
+    }
+
+    texture = QNativeInterface::QSGOpenGLTexture::fromNative(glTexture, win, size(), texOpts);
+    Q_ASSERT(glFun->glGetError() == GL_NO_ERROR);
 #elif defined(Q_OS_WIN)
+    qCDebug(lcWebEngineCompositor, "WGL: Importing DXGI Resource into GL Texture.");
     // TODO: Add WGL support over ANGLE.
     QT_NOT_YET_IMPLEMENTED
 #elif defined(Q_OS_MACOS)
+    qCDebug(lcWebEngineCompositor, "CGL: Importing IOSurface into GL Texture.");
     uint32_t glTexture = makeCGLTexture(win, ioSurface.get(), size());
     texture = QNativeInterface::QSGOpenGLTexture::fromNative(glTexture, win, size(), texOpts);
 
-    m_frontBuffer->textureCleanupCallback = [glTexture]() {
-        auto *glContext = QOpenGLContext::currentContext();
-        if (!glContext)
-            return;
-        auto glFun = glContext->functions();
-        glFun->glDeleteTextures(1, &glTexture);
+    QOpenGLContext *glContext = QOpenGLContext::currentContext();
+    QSurface *createSurface = glContext->surface();
+    m_frontBuffer->textureCleanupCallback = [glContext, createSurface, glTexture]() {
+        ScopedGLContextForCleanup cleanupContext(glContext, createSurface);
+        cleanupContext.deleteTexture(glTexture);
     };
-#endif
+#endif // defined(USE_OZONE)
 
     return texture;
 }
diff --git a/src/core/compositor/native_skia_output_device_vulkan.cpp b/src/core/compositor/native_skia_output_device_vulkan.cpp
index f51eba98521..8cb01cd22c1 100644
--- a/src/core/compositor/native_skia_output_device_vulkan.cpp
+++ b/src/core/compositor/native_skia_output_device_vulkan.cpp
@@ -4,6 +4,7 @@
 #include "native_skia_output_device_vulkan.h"
 
 #include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
+#include "ui/base/ozone_buildflags.h"
 
 #include <QtGui/qvulkaninstance.h>
 #include <QtGui/qvulkanfunctions.h>
@@ -11,17 +12,17 @@
 #include <QtQuick/qsgtexture.h>
 
 #if defined(USE_OZONE)
-#include "ui/ozone/buildflags.h"
-#if BUILDFLAG(OZONE_PLATFORM_X11)
+#if BUILDFLAG(IS_OZONE_X11)
 // We need to define USE_VULKAN_XCB for proper vulkan function pointers.
 // Avoiding it may lead to call wrong vulkan functions.
 // This is originally defined in chromium/gpu/vulkan/BUILD.gn.
 #define USE_VULKAN_XCB
-#endif // BUILDFLAG(OZONE_PLATFORM_X11)
+#endif // BUILDFLAG(IS_OZONE_X11)
 #include "gpu/vulkan/vulkan_function_pointers.h"
-
 #include "components/viz/common/gpu/vulkan_context_provider.h"
 #include "gpu/vulkan/vulkan_device_queue.h"
+#include "third_party/skia/include/gpu/vk/GrVkTypes.h"
+#include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h"
 #endif // defined(USE_OZONE)
 
 namespace QtWebEngineCore {
@@ -36,6 +37,8 @@ NativeSkiaOutputDeviceVulkan::NativeSkiaOutputDeviceVulkan(
                              shared_image_factory, shared_image_representation_factory,
                              didSwapBufferCompleteCallback)
 {
+    qCDebug(lcWebEngineCompositor, "Native Skia Output Device: Vulkan");
+
     SkColorType skColorType = kRGBA_8888_SkColorType;
     capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBA_8888)] = skColorType;
     capabilities_.sk_color_types[static_cast<int>(gfx::BufferFormat::RGBX_8888)] = skColorType;
@@ -84,7 +87,8 @@ QSGTexture *NativeSkiaOutputDeviceVulkan::texture(QQuickWindow *win, uint32_t te
             return nullptr;
         }
 
-        backendTexture.getVkImageInfo(&vkImageInfo);
+        GrBackendTextures::GetVkImageInfo(backendTexture, &vkImageInfo);
+
         if (vkImageInfo.fAlloc.fMemory == VK_NULL_HANDLE) {
             qWarning("VULKAN: Unable to access Vulkan memory.");
             return nullptr;
@@ -108,54 +112,61 @@ QSGTexture *NativeSkiaOutputDeviceVulkan::texture(QQuickWindow *win, uint32_t te
     QVulkanFunctions *f = win->vulkanInstance()->functions();
     QVulkanDeviceFunctions *df = win->vulkanInstance()->deviceFunctions(qtVulkanDevice);
 
-    VkImageLayout imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-    VkPhysicalDeviceProperties deviceProperties;
-    f->vkGetPhysicalDeviceProperties(qtPhysicalDevice, &deviceProperties);
-    if (deviceProperties.vendorID == 0x10DE) {
-        // FIXME: This is a workaround for Nvidia driver.
-        // The imported image is empty if the initialLayout is not
-        // VK_IMAGE_LAYOUT_PREINITIALIZED.
-        imageLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
-    }
-
     VkExternalMemoryImageCreateInfoKHR externalMemoryImageCreateInfo = {
-        VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR
+        .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR,
+        .pNext = nullptr,
+        .handleTypes = 0,
     };
 #if defined(USE_OZONE)
-    VkSubresourceLayout planeLayout = {};
+    base::ScopedFD scopedFd;
+
+    VkSubresourceLayout planeLayout = {
+        .offset = 0,
+        .size = 0,
+        .rowPitch = 0,
+        .arrayPitch = 0,
+        .depthPitch = 0,
+    };
+
     VkImageDrmFormatModifierExplicitCreateInfoEXT modifierInfo = {
-        VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT
+        .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT,
+        .pNext = nullptr,
+        .drmFormatModifier = 0,
+        .drmFormatModifierPlaneCount = 1,
+        .pPlaneLayouts = &planeLayout,
     };
-    base::ScopedFD scopedFd;
 
+    bool usingDrmModifier = false;
     if (nativePixmap) {
+        qCDebug(lcWebEngineCompositor, "VULKAN: Importing NativePixmap into VkImage.");
         gfx::NativePixmapHandle nativePixmapHandle = nativePixmap->ExportHandle();
-        if (nativePixmapHandle.planes.size() != 1)
-            qFatal("VULKAN: Multiple planes are not supported.");
+        qCDebug(lcWebEngineCompositor, "  DRM Format Modifier: 0x%lx", nativePixmapHandle.modifier);
 
-        planeLayout.offset = nativePixmapHandle.planes[0].offset;
-        planeLayout.size = 0;
-        planeLayout.rowPitch = nativePixmapHandle.planes[0].stride;
-        planeLayout.arrayPitch = 0;
-        planeLayout.depthPitch = 0;
+        if (nativePixmapHandle.modifier != gfx::NativePixmapHandle::kNoModifier) {
+            usingDrmModifier = true;
+            if (nativePixmapHandle.planes.size() != 1)
+                qFatal("VULKAN: Multiple planes are not supported.");
 
-        modifierInfo.drmFormatModifier = nativePixmapHandle.modifier;
-        modifierInfo.drmFormatModifierPlaneCount = 1;
-        modifierInfo.pPlaneLayouts = &planeLayout;
+            planeLayout.offset = nativePixmapHandle.planes[0].offset;
+            planeLayout.rowPitch = nativePixmapHandle.planes[0].stride;
+            modifierInfo.drmFormatModifier = nativePixmapHandle.modifier;
 
-        externalMemoryImageCreateInfo.pNext = &modifierInfo;
+            externalMemoryImageCreateInfo.pNext = &modifierInfo;
+        }
         externalMemoryImageCreateInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
 
         scopedFd = std::move(nativePixmapHandle.planes[0].fd);
     } else {
-        externalMemoryImageCreateInfo.pNext = nullptr;
+        qCDebug(lcWebEngineCompositor, "VULKAN: Importing VkImage into VkImage.");
         externalMemoryImageCreateInfo.handleTypes =
                 VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
 
-        VkMemoryGetFdInfoKHR exportInfo = { VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR };
-        exportInfo.pNext = nullptr;
-        exportInfo.memory = vkImageInfo.fAlloc.fMemory;
-        exportInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
+        VkMemoryGetFdInfoKHR exportInfo = {
+            .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
+            .pNext = nullptr,
+            .memory = vkImageInfo.fAlloc.fMemory,
+            .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR,
+        };
 
         gpu::VulkanFunctionPointers *vfp = gpu::GetVulkanFunctionPointers();
         gpu::VulkanDeviceQueue *vulkanDeviceQueue =
@@ -172,53 +183,57 @@ QSGTexture *NativeSkiaOutputDeviceVulkan::texture(QQuickWindow *win, uint32_t te
     if (!scopedFd.is_valid())
         qFatal("VULKAN: Unable to extract file descriptor.");
 #elif defined(Q_OS_WIN)
-    externalMemoryImageCreateInfo.pNext = nullptr;
+    qCDebug(lcWebEngineCompositor, "VULKAN: Importing DXGI Resource into VkImage.");
     externalMemoryImageCreateInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT;
 
-    HRESULT status = S_OK;
-    HANDLE sharedHandle = nullptr;
-    IDXGIResource1 *resource = nullptr;
-    if (!overlayImage->nv12_texture()) {
+    Q_ASSERT(overlayImage->type() == gl::DCLayerOverlayType::kNV12Texture);
+    Microsoft::WRL::ComPtr<ID3D11Texture2D> chromeTexture = overlayImage->nv12_texture();
+    if (!chromeTexture) {
         qWarning("VULKAN: No D3D texture.");
         return nullptr;
     }
-    status = overlayImage->nv12_texture()->QueryInterface(__uuidof(IDXGIResource1),
-                                                          (void **)&resource);
-    Q_ASSERT(status == S_OK);
-    status = resource->CreateSharedHandle(NULL, DXGI_SHARED_RESOURCE_READ, NULL, &sharedHandle);
-    Q_ASSERT(status == S_OK);
-    resource->Release();
-
-    if (!sharedHandle)
-        qFatal("VULKAN: Unable to extract shared handle.");
+
+    HRESULT hr;
+
+    Microsoft::WRL::ComPtr<IDXGIResource1> dxgiResource;
+    hr = chromeTexture->QueryInterface(IID_PPV_ARGS(&dxgiResource));
+    Q_ASSERT(SUCCEEDED(hr));
+
+    HANDLE sharedHandle = INVALID_HANDLE_VALUE;
+    hr = dxgiResource->CreateSharedHandle(nullptr, DXGI_SHARED_RESOURCE_READ, nullptr,
+                                          &sharedHandle);
+    Q_ASSERT(SUCCEEDED(hr));
+    Q_ASSERT(sharedHandle != INVALID_HANDLE_VALUE);
 #endif
 
-    constexpr VkImageUsageFlags kUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
-            | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT
-            | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
-
-    VkImageCreateInfo importedImageCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
-    importedImageCreateInfo.pNext = &externalMemoryImageCreateInfo;
-    importedImageCreateInfo.flags = 0;
-    importedImageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
-    importedImageCreateInfo.format = gpu::ToVkFormat(m_frontBuffer->sharedImageFormat());
-    importedImageCreateInfo.extent.width = static_cast<uint32_t>(size().width());
-    importedImageCreateInfo.extent.height = static_cast<uint32_t>(size().height());
-    importedImageCreateInfo.extent.depth = 1;
-    importedImageCreateInfo.mipLevels = 1;
-    importedImageCreateInfo.arrayLayers = 1;
-    importedImageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
-    importedImageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
-    importedImageCreateInfo.usage = kUsage;
-    importedImageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
-    importedImageCreateInfo.queueFamilyIndexCount = 0;
-    importedImageCreateInfo.pQueueFamilyIndices = nullptr;
-    importedImageCreateInfo.initialLayout = imageLayout;
+    VkImageCreateInfo importedImageCreateInfo = {
+        .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+        .pNext = &externalMemoryImageCreateInfo,
+        .flags = 0,
+        .imageType = VK_IMAGE_TYPE_2D,
+        .format = gpu::ToVkFormatSinglePlanar(m_frontBuffer->sharedImageFormat()),
+        .extent = {
+            .width = static_cast<uint32_t>(size().width()),
+            .height = static_cast<uint32_t>(size().height()),
+            .depth = 1,
+        },
+        .mipLevels = 1,
+        .arrayLayers = 1,
+        .samples = VK_SAMPLE_COUNT_1_BIT,
+        .tiling = VK_IMAGE_TILING_OPTIMAL,
+        // The image is fed into a combined image sampler
+        .usage = VK_IMAGE_USAGE_SAMPLED_BIT,
+        .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
+        .queueFamilyIndexCount = 0,
+        .pQueueFamilyIndices = nullptr,
+        // VkExternalMemoryImageCreateInfo only allows UNDEFINED
+        .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+    };
 
 #if defined(USE_OZONE)
-    if (nativePixmap)
+    if (usingDrmModifier)
         importedImageCreateInfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
-    else
+    else if (vkImageInfo.fAlloc.fMemory != VK_NULL_HANDLE)
         importedImageCreateInfo.tiling = vkImageInfo.fImageTiling;
 #endif
 
@@ -227,35 +242,27 @@ QSGTexture *NativeSkiaOutputDeviceVulkan::texture(QQuickWindow *win, uint32_t te
     result = df->vkCreateImage(qtVulkanDevice, &importedImageCreateInfo, nullptr /* pAllocator */,
                                &importedImage);
     if (result != VK_SUCCESS)
-        qFatal() << "VULKAN: vkCreateImage failed result:" << result;
+        qFatal("VULKAN: vkCreateImage failed result: %d", static_cast<int>(result));
 
 #if defined(USE_OZONE)
     VkImportMemoryFdInfoKHR importMemoryHandleInfo = {
-        VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR
+        .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR,
+        .pNext = nullptr,
+        .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR,
+        .fd = scopedFd.release(),
     };
-    importMemoryHandleInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
-    importMemoryHandleInfo.fd = scopedFd.release();
 
     if (nativePixmap)
         importMemoryHandleInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
 #elif defined(Q_OS_WIN)
     VkImportMemoryWin32HandleInfoKHR importMemoryHandleInfo = {
-        VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR
+        .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR,
+        .pNext = nullptr,
+        .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT,
+        .handle = sharedHandle,
     };
-    importMemoryHandleInfo.pNext = nullptr;
-    importMemoryHandleInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT;
-    importMemoryHandleInfo.handle = sharedHandle;
 #endif
 
-    VkMemoryDedicatedAllocateInfoKHR dedicatedMemoryInfo = {
-        VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR
-    };
-    dedicatedMemoryInfo.pNext = &importMemoryHandleInfo;
-    dedicatedMemoryInfo.image = importedImage;
-
-    VkMemoryAllocateInfo memoryAllocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
-    memoryAllocateInfo.pNext = &dedicatedMemoryInfo;
-
     VkMemoryRequirements requirements;
     df->vkGetImageMemoryRequirements(qtVulkanDevice, importedImage, &requirements);
     if (!requirements.memoryTypeBits)
@@ -278,20 +285,31 @@ QSGTexture *NativeSkiaOutputDeviceVulkan::texture(QQuickWindow *win, uint32_t te
     if (memoryTypeIndex > kMaxIndex)
         qFatal("VULKAN: Cannot find valid memory type index.");
 
-    memoryAllocateInfo.allocationSize = requirements.size;
-    memoryAllocateInfo.memoryTypeIndex = memoryTypeIndex;
+    VkMemoryDedicatedAllocateInfoKHR dedicatedMemoryInfo = {
+        .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR,
+        .pNext = &importMemoryHandleInfo,
+        .image = importedImage,
+        .buffer = VK_NULL_HANDLE,
+    };
+
+    VkMemoryAllocateInfo memoryAllocateInfo = {
+        .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+        .pNext = &dedicatedMemoryInfo,
+        .allocationSize = requirements.size,
+        .memoryTypeIndex = memoryTypeIndex,
+    };
 
     VkDeviceMemory importedImageMemory = VK_NULL_HANDLE;
     result = df->vkAllocateMemory(qtVulkanDevice, &memoryAllocateInfo, nullptr /* pAllocator */,
                                   &importedImageMemory);
     if (result != VK_SUCCESS)
-        qFatal() << "VULKAN: vkAllocateMemory failed result:" << result;
+        qFatal("VULKAN: vkAllocateMemory failed result: %d", static_cast<int>(result));
 
     df->vkBindImageMemory(qtVulkanDevice, importedImage, importedImageMemory, 0);
 
     QQuickWindow::CreateTextureOptions texOpts(textureOptions);
-    QSGTexture *texture = QNativeInterface::QSGVulkanTexture::fromNative(importedImage, imageLayout,
-                                                                         win, size(), texOpts);
+    QSGTexture *texture = QNativeInterface::QSGVulkanTexture::fromNative(
+            importedImage, importedImageCreateInfo.initialLayout, win, size(), texOpts);
 
     m_frontBuffer->textureCleanupCallback = [=]() {
         df->vkDestroyImage(qtVulkanDevice, importedImage, nullptr);
diff --git a/src/core/configure/BUILD.root.gn.in b/src/core/configure/BUILD.root.gn.in
index 986db502629..a50c6dd6299 100644
--- a/src/core/configure/BUILD.root.gn.in
+++ b/src/core/configure/BUILD.root.gn.in
@@ -92,6 +92,8 @@ config("cpp20_config") {
   # Chromium is built with C++20
   if (is_win) {
     cflags_cc = [ "/std:c++20" ]
+  } else if(is_gcc) {
+    cflags_cc = [ "-std=gnu++20" ]
   } else {
     cflags_cc = [ "-std=c++20" ]
   }
@@ -168,6 +170,7 @@ shared_library("QtWebEngineCore") {
   }
 
   if (is_win) {
+    configs -= [ "//build/config/compiler:no_rtti" ]
     configs += [ "//build/config/compiler:rtti" ]
     data_deps += [ ":QtWebEngineCoreSandbox" ]
   }
@@ -207,6 +210,8 @@ source_set("qtwebengine_spellcheck_sources") {
   sources = [
     "//chrome/browser/spellchecker/spell_check_host_chrome_impl.cc",
     "//chrome/browser/spellchecker/spell_check_host_chrome_impl.h",
+    "//chrome/browser/spellchecker/spell_check_initialization_host_impl.cc",
+    "//chrome/browser/spellchecker/spell_check_initialization_host_impl.h",
     "//chrome/browser/spellchecker/spellcheck_custom_dictionary.cc",
     "//chrome/browser/spellchecker/spellcheck_custom_dictionary.h",
     "//chrome/browser/spellchecker/spellcheck_factory.cc",
@@ -233,6 +238,7 @@ source_set("qtwebengine_spellcheck_sources") {
 source_set("devtools_sources") {
   configs += [ ":cpp20_config" ]
   deps = [
+    "//chrome/app:generated_resources",
     "//components/zoom",
     "//third_party/blink/public/mojom:mojom_platform",
     ]
@@ -278,6 +284,7 @@ source_set("qtwebengine_sources") {
     "//components/performance_manager",
     "//components/permissions:permissions_common",
     "//components/plugins/renderer/",
+    "//components/compose:buildflags",
     "//content/browser/resources/quota:resources",
     "//extensions/buildflags:buildflags",
     "//pdf:buildflags",
@@ -347,12 +354,12 @@ source_set("qtwebengine_sources") {
     "//chrome/browser/tab_contents/form_interaction_tab_helper.h",
     "//chrome/browser/tab_contents/web_contents_collection.cc",
     "//chrome/browser/tab_contents/web_contents_collection.h",
-    "//chrome/browser/ui/webui/device_log_ui.cc",
-    "//chrome/browser/ui/webui/device_log_ui.h",
-    "//chrome/browser/ui/webui/devtools_ui.cc",
-    "//chrome/browser/ui/webui/devtools_ui.h",
-    "//chrome/browser/ui/webui/devtools_ui_data_source.cc",
-    "//chrome/browser/ui/webui/devtools_ui_data_source.h",
+    "//chrome/browser/ui/webui/device_log/device_log_ui.cc",
+    "//chrome/browser/ui/webui/device_log/device_log_ui.h",
+    "//chrome/browser/ui/webui/devtools/devtools_ui.cc",
+    "//chrome/browser/ui/webui/devtools/devtools_ui.h",
+    "//chrome/browser/ui/webui/devtools/devtools_ui_data_source.cc",
+    "//chrome/browser/ui/webui/devtools/devtools_ui_data_source.h",
     "//chrome/browser/ui/webui/net_internals/net_internals_ui.cc",
     "//chrome/browser/ui/webui/net_internals/net_internals_ui.h",
     "//chrome/browser/ui/webui/user_actions/user_actions_ui.cc",
@@ -364,7 +371,6 @@ source_set("qtwebengine_sources") {
     "//chrome/common/chrome_switches.cc",
     "//chrome/common/chrome_switches.h",
     "//chrome/common/pref_names.h",
-    "//chrome/common/url_constants.cc",
     "//chrome/common/url_constants.h",
     "//chrome/common/webui_url_constants.cc",
     "//chrome/common/webui_url_constants.h",
@@ -385,11 +391,14 @@ source_set("qtwebengine_sources") {
       deps += [
         "//ui/base/x:gl",
         "//ui/gfx/linux:gpu_memory_buffer_support_x11",
+        "//ui/gfx/x",
       ]
 
       sources += [
         "//ui/ozone/platform/x11/gl_egl_utility_x11.cc",
         "//ui/ozone/platform/x11/gl_egl_utility_x11.h",
+        "//ui/ozone/platform/x11/native_pixmap_egl_x11_binding.cc",
+        "//ui/ozone/platform/x11/native_pixmap_egl_x11_binding.h",
       ]
     }
   }
@@ -558,6 +567,7 @@ group("qtwebengine_resources") {
       "//chrome/browser:resources",
       "//chrome/browser/resources:component_extension_resources",
       "//chrome/common:resources",
+      "//chrome/browser/resources/device_log:resources",
       "//components/resources:components_resources",
       ":qtwebengine_repack_resources",
       ":qtwebengine_repack_resources_100",
@@ -573,9 +583,11 @@ repack("qtwebengine_repack_resources") {
     "$root_gen_dir/chrome/accessibility_resources.pak",
     "$root_gen_dir/chrome/common_resources.pak",
     "$root_gen_dir/chrome/dev_ui_browser_resources.pak",
+    "$root_gen_dir/chrome/device_log_resources.pak",
     "$root_gen_dir/chrome/net_internals_resources.pak",
     "$root_gen_dir/components/components_resources.pak",
     "$root_gen_dir/components/dev_ui_components_resources.pak",
+    "$root_gen_dir/components/ukm_resources.pak",
     "$root_gen_dir/content/attribution_internals_resources.pak",
     "$root_gen_dir/content/browser/resources/media/media_internals_resources.pak",
     "$root_gen_dir/content/browser/tracing/tracing_resources.pak",
@@ -592,15 +604,22 @@ repack("qtwebengine_repack_resources") {
     "$root_gen_dir/third_party/blink/public/resources/blink_resources.pak",
     "$root_gen_dir/ui/resources/webui_resources.pak",
   ]
-  output = "$root_out_dir/qtwebengine_resources.pak"
+  if (!is_debug) {
+    output = "$root_out_dir/qtwebengine_resources.pak"
+  } else {
+    output = "$root_out_dir/qtwebengine_resources.debug.pak"
+  }
+
   deps = [
      "//qtwebengine/browser:qt_webengine_resources",
      "//chrome/browser:dev_ui_browser_resources_grit",
      "//chrome/browser/resources/accessibility:resources",
      "//chrome/browser/resources/net_internals:resources",
      "//chrome/common:resources_grit",
+     "//chrome/browser/resources/device_log:resources_grit",
      "//components/resources:components_resources_grit",
      "//components/resources:dev_ui_components_resources_grit",
+     "//components/ukm/debug:resources",
      "//content/browser/resources/attribution_reporting:resources",
      "//content/browser/resources/gpu:resources",
      "//content/browser/resources/histograms:resources_grit",
@@ -670,7 +689,11 @@ repack("qtwebengine_repack_resources_100") {
     "$root_gen_dir/third_party/blink/public/resources/blink_scaled_resources_100_percent.pak",
     "$root_gen_dir/ui/resources/ui_resources_100_percent.pak",
   ]
-  output = "$root_out_dir/qtwebengine_resources_100p.pak"
+  if (!is_debug) {
+    output = "$root_out_dir/qtwebengine_resources_100p.pak"
+  } else {
+    output = "$root_out_dir/qtwebengine_resources_100p.debug.pak"
+  }
   deps = [
     "//chrome/renderer:resources_grit",
     "//components/resources:components_scaled_resources_grit",
@@ -694,7 +717,11 @@ repack("qtwebengine_repack_resources_200") {
     "$root_gen_dir/third_party/blink/public/resources/blink_scaled_resources_200_percent.pak",
     "$root_gen_dir/ui/resources/ui_resources_200_percent.pak",
   ]
-  output = "$root_out_dir/qtwebengine_resources_200p.pak"
+  if (!is_debug) {
+    output = "$root_out_dir/qtwebengine_resources_200p.pak"
+  } else {
+    output = "$root_out_dir/qtwebengine_resources_200p.debug.pak"
+  }
   deps = [
     "//chrome/renderer:resources_grit",
     "//components/resources:components_scaled_resources_grit",
@@ -716,7 +743,11 @@ repack("qtwebengine_repack_resources_devtools") {
     "$root_gen_dir/content/browser/devtools/devtools_resources.pak",
     "$root_gen_dir/third_party/blink/public/resources/inspector_overlay_resources.pak",
   ]
-  output = "$root_out_dir/qtwebengine_devtools_resources.pak"
+  if (!is_debug) {
+    output = "$root_out_dir/qtwebengine_devtools_resources.pak"
+  } else {
+    output = "$root_out_dir/qtwebengine_devtools_resources.debug.pak"
+  }
   deps = [
     "//content/browser/devtools:devtools_resources_grit",
     "//third_party/blink/public:devtools_inspector_resources_grit",
diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp
index f57cf545572..1c12a72eaa5 100644
--- a/src/core/content_browser_client_qt.cpp
+++ b/src/core/content_browser_client_qt.cpp
@@ -16,7 +16,6 @@
 #include "components/performance_manager/embedder/performance_manager_registry.h"
 #include "components/performance_manager/public/performance_manager.h"
 #include "content/browser/web_contents/web_contents_impl.h"
-#include "content/public/browser/browser_main_runner.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/child_process_security_policy.h"
 #include "content/public/browser/client_certificate_delegate.h"
@@ -25,17 +24,16 @@
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
-#include "content/public/browser/render_view_host.h"
 #include "content/public/browser/shared_cors_origin_access_list.h"
 #include "content/public/browser/url_loader_request_interceptor.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_contents_user_data.h"
 #include "content/public/browser/web_contents_view_delegate.h"
 #include "content/public/browser/web_ui_url_loader_factory.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/main_function_params.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/common/user_agent.h"
+#include "base/version_info/version_info.h"
+#include "ipc/ipc_channel_proxy.h"
 #include "extensions/buildflags/buildflags.h"
 #include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
 #include "pdf/buildflags.h"
@@ -57,7 +55,6 @@
 
 #include "profile_adapter.h"
 #include "browser_main_parts_qt.h"
-#include "browser_message_filter_qt.h"
 #include "certificate_error_controller.h"
 #include "client_cert_select_controller.h"
 #include "custom_handlers/protocol_handler_registry_factory.h"
@@ -70,7 +67,6 @@
 #include "net/proxying_restricted_cookie_manager_qt.h"
 #include "net/proxying_url_loader_factory_qt.h"
 #include "net/system_network_context_manager.h"
-#include "platform_notification_service_qt.h"
 #include "profile_qt.h"
 #include "profile_io_data_qt.h"
 #include "renderer_host/user_resource_controller_host.h"
@@ -80,7 +76,6 @@
 #include "web_contents_adapter.h"
 #include "web_contents_delegate_qt.h"
 #include "web_contents_view_qt.h"
-#include "web_engine_context.h"
 #include "web_engine_library_info.h"
 #include "web_engine_settings.h"
 #include "authenticator_request_client_delegate_qt.h"
@@ -88,6 +83,10 @@
 #include "api/qwebenginecookiestore_p.h"
 #include "api/qwebengineurlrequestinfo_p.h"
 
+#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
+#include "browser_message_filter_qt.h"
+#endif
+
 #if QT_CONFIG(webengine_geolocation)
 #include "base/memory/ptr_util.h"
 #include "location_provider_qt.h"
@@ -95,6 +94,7 @@
 
 #if QT_CONFIG(webengine_spellchecker)
 #include "chrome/browser/spellchecker/spell_check_host_chrome_impl.h"
+#include "chrome/browser/spellchecker/spell_check_initialization_host_impl.h"
 #include "chrome/browser/spellchecker/spellcheck_factory.h"
 #include "chrome/browser/spellchecker/spellcheck_service.h"
 #include "components/spellcheck/browser/pref_names.h"
@@ -147,12 +147,11 @@
 #include "components/pdf/browser/pdf_navigation_throttle.h"
 #include "components/pdf/browser/pdf_url_loader_request_interceptor.h"
 #include "components/pdf/browser/pdf_document_helper.h"
-
-#include "printing/pdf_document_helper_client_qt.h"
 #endif
 
 #if BUILDFLAG(ENABLE_PDF) && BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/pdf_iframe_navigation_throttle_qt.h"
+#include "printing/pdf_document_helper_client_qt.h"
 #endif
 
 #include <QGuiApplication>
@@ -186,11 +185,11 @@ bool IsHandledProtocol(base::StringPiece scheme)
         if (scheme == protocol)
             return true;
     }
-    if (const auto cs = url::CustomScheme::FindScheme(scheme))
+    if (url::CustomScheme::FindScheme(scheme))
         return true;
     return false;
 }
-}
+} // namespace url
 
 namespace QtWebEngineCore {
 
@@ -228,7 +227,7 @@ void ContentBrowserClientQt::RenderProcessWillLaunch(content::RenderProcessHost
 #endif
 
 #if QT_CONFIG(webengine_webrtc) && QT_CONFIG(webengine_extensions)
-    WebRtcLoggingController::AttachToRenderProcessHost(host, WebEngineContext::current()->webRtcLogUploader());
+    WebRtcLoggingController::AttachToRenderProcessHost(host);
 #endif
 
     // Allow requesting custom schemes.
@@ -240,12 +239,13 @@ void ContentBrowserClientQt::RenderProcessWillLaunch(content::RenderProcessHost
     // FIXME: Add a settings variable to enable/disable the file scheme.
     policy->GrantRequestScheme(id, url::kFileScheme);
     profileAdapter->userResourceController()->renderProcessStartedWithHost(host);
+#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
     host->AddFilter(new BrowserMessageFilterQt(id, profile));
 #if BUILDFLAG(ENABLE_EXTENSIONS)
     host->AddFilter(new extensions::ExtensionMessageFilter(id, profile));
     host->AddFilter(new extensions::MessagingAPIMessageFilter(id, profile));
 #endif //ENABLE_EXTENSIONS
-
+#endif // CONTENT_ENABLE_LEGACY_IPC
     bool is_incognito_process = profile->IsOffTheRecord();
     mojo::AssociatedRemote<qtwebengine::mojom::RendererConfiguration> renderer_configuration;
     host->GetChannel()->GetRemoteAssociatedInterface(&renderer_configuration);
@@ -390,8 +390,9 @@ void ContentBrowserClientQt::BindHostReceiverForRenderer(content::RenderProcessH
                                                          mojo::GenericPendingReceiver receiver)
 {
 #if QT_CONFIG(webengine_spellchecker)
-    if (auto host_receiver = receiver.As<spellcheck::mojom::SpellCheckHost>()) {
-        SpellCheckHostChromeImpl::Create(render_process_host->GetID(), std::move(host_receiver));
+    if (auto host_receiver = receiver.As<spellcheck::mojom::SpellCheckInitializationHost>()) {
+        SpellCheckInitializationHostImpl::Create(render_process_host->GetID(),
+                                                 std::move(host_receiver));
         return;
     }
 #endif
@@ -434,6 +435,15 @@ void ContentBrowserClientQt::RegisterBrowserInterfaceBindersForFrame(
         mojo::BinderMapWithContext<content::RenderFrameHost *> *map)
 {
     map->Add<network_hints::mojom::NetworkHintsHandler>(base::BindRepeating(&BindNetworkHintsHandler));
+#if QT_CONFIG(webengine_spellchecker)
+    map->Add<spellcheck::mojom::SpellCheckHost>(base::BindRepeating(
+            [](content::RenderFrameHost *frame_host,
+               mojo::PendingReceiver<spellcheck::mojom::SpellCheckHost> receiver) {
+                SpellCheckHostChromeImpl::Create(frame_host->GetProcess()->GetID(),
+                                                 std::move(receiver));
+            }));
+#endif
+
 #if BUILDFLAG(ENABLE_EXTENSIONS)
     map->Add<extensions::mime_handler::MimeHandlerService>(base::BindRepeating(&BindMimeHandlerService));
     map->Add<extensions::mime_handler::BeforeUnloadControl>(base::BindRepeating(&BindBeforeUnloadControl));
@@ -460,13 +470,9 @@ void ContentBrowserClientQt::ExposeInterfacesToRenderer(service_manager::BinderR
 {
     if (auto *manager = performance_manager::PerformanceManagerRegistry::GetInstance())
         manager->CreateProcessNodeAndExposeInterfacesToRendererProcess(registry, render_process_host);
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-    associated_registry->AddInterface<extensions::mojom::EventRouter>(
-                base::BindRepeating(&extensions::EventRouter::BindForRenderer, render_process_host->GetID()));
-    associated_registry->AddInterface<guest_view::mojom::GuestViewHost>(
-                base::BindRepeating(&extensions::ExtensionsGuestView::CreateForComponents, render_process_host->GetID()));
-    associated_registry->AddInterface<extensions::mojom::GuestView>(
-                base::BindRepeating(&extensions::ExtensionsGuestView::CreateForExtensions, render_process_host->GetID()));
+#if BUILDFLAG(ENABLE_EXTENSIONS) && BUILDFLAG(ENABLE_EXTENSIONS_LEGACY_IPC)
+    associated_registry->AddInterface<extensions::mojom::EventRouter>(base::BindRepeating(
+            &extensions::EventRouter::BindForRenderer, render_process_host->GetID()));
 #else
     Q_UNUSED(associated_registry);
 #endif
@@ -502,13 +508,14 @@ void ContentBrowserClientQt::RegisterAssociatedInterfaceBindersForRenderFrameHos
                         extensions::ExtensionWebContentsObserverQt::BindLocalFrameHost(std::move(receiver), render_frame_host);
                     }, &rfh));
 #endif
-    associated_registry.AddInterface<autofill::mojom::AutofillDriver>(
-                base::BindRepeating(
-                    [](content::RenderFrameHost *render_frame_host,
-                       mojo::PendingAssociatedReceiver<autofill::mojom::AutofillDriver> receiver) {
-                        autofill::ContentAutofillDriverFactory::BindAutofillDriver(std::move(receiver), render_frame_host);
-                    }, &rfh));
-#if BUILDFLAG(ENABLE_PDF)
+    associated_registry.AddInterface<autofill::mojom::AutofillDriver>(base::BindRepeating(
+            [](content::RenderFrameHost *render_frame_host,
+               mojo::PendingAssociatedReceiver<autofill::mojom::AutofillDriver> receiver) {
+                autofill::ContentAutofillDriverFactory::BindAutofillDriver(render_frame_host,
+                                                                           std::move(receiver));
+            },
+            &rfh));
+#if BUILDFLAG(ENABLE_PDF) && BUILDFLAG(ENABLE_EXTENSIONS)
     associated_registry.AddInterface<pdf::mojom::PdfService>(
                 base::BindRepeating(
                     [](content::RenderFrameHost *render_frame_host,
@@ -517,6 +524,12 @@ void ContentBrowserClientQt::RegisterAssociatedInterfaceBindersForRenderFrameHos
                     }, &rfh));
 #endif  // BUILDFLAG(ENABLE_PDF)
     ContentBrowserClient::RegisterAssociatedInterfaceBindersForRenderFrameHost(rfh, associated_registry);
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+    associated_registry.AddInterface<guest_view::mojom::GuestViewHost>(base::BindRepeating(
+            &extensions::ExtensionsGuestView::CreateForComponents, rfh.GetGlobalId()));
+    associated_registry.AddInterface<extensions::mojom::GuestView>(base::BindRepeating(
+            &extensions::ExtensionsGuestView::CreateForExtensions, rfh.GetGlobalId()));
+#endif
 }
 
 bool ContentBrowserClientQt::CanCreateWindow(
@@ -764,7 +777,8 @@ std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
 ContentBrowserClientQt::CreateURLLoaderThrottles(
         const network::ResourceRequest &request, content::BrowserContext *browser_context,
         const base::RepeatingCallback<content::WebContents *()> & /*wc_getter*/,
-        content::NavigationUIData * /*navigation_ui_data*/, int frame_tree_node_id)
+        content::NavigationUIData * /*navigation_ui_data*/, int frame_tree_node_id,
+        absl::optional<int64_t> navigation_id)
 {
     std::vector<std::unique_ptr<blink::URLLoaderThrottle>> result;
     result.push_back(std::make_unique<ProtocolHandlerThrottle>(
@@ -883,13 +897,10 @@ bool ContentBrowserClientQt::HasErrorPage(int httpStatusCode, content::WebConten
 }
 
 std::unique_ptr<content::LoginDelegate> ContentBrowserClientQt::CreateLoginDelegate(
-        const net::AuthChallengeInfo &authInfo,
-        content::WebContents *web_contents,
-        const content::GlobalRequestID & /*request_id*/,
-        bool /*is_main_frame*/,
-        const GURL &url,
-        scoped_refptr<net::HttpResponseHeaders> /*response_headers*/,
-        bool first_auth_attempt,
+        const net::AuthChallengeInfo &authInfo, content::WebContents *web_contents,
+        content::BrowserContext *browser_context, const content::GlobalRequestID & /*request_id*/,
+        bool /*is_main_frame*/, const GURL &url,
+        scoped_refptr<net::HttpResponseHeaders> /*response_headers*/, bool first_auth_attempt,
         LoginAuthRequiredCallback auth_required_callback)
 {
     auto loginDelegate = std::make_unique<LoginDelegateQt>(authInfo, web_contents, url, first_auth_attempt, std::move(auth_required_callback));
@@ -967,8 +978,8 @@ std::string ContentBrowserClientQt::getUserAgent()
 {
     // Mention the Chromium version we're based on to get passed stupid UA-string-based feature detection (several WebRTC demos need this)
     return content::BuildUserAgentFromProduct("QtWebEngine/" + std::string(qWebEngineVersion())
-                                              + " Chrome/"
-                                              + std::string(qWebEngineChromiumVersion()));
+                                              + " Chrome/" + version_info::GetMajorVersionNumber()
+                                              + ".0.0.0");
 }
 
 blink::UserAgentMetadata ContentBrowserClientQt::GetUserAgentMetadata()
@@ -980,7 +991,7 @@ blink::UserAgentMetadata ContentBrowserClientQt::GetUserAgentMetadata()
 
 std::string ContentBrowserClientQt::GetProduct()
 {
-    QString productName(qApp->applicationName() % '/' % qApp->applicationVersion());
+    QString productName(qApp->applicationName() % u'/' % qApp->applicationVersion());
     return productName.toStdString();
 }
 
@@ -1029,9 +1040,8 @@ std::vector<base::FilePath> ContentBrowserClientQt::GetNetworkContextsParentDire
         toFilePath(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)) };
 }
 
-void ContentBrowserClientQt::RegisterNonNetworkNavigationURLLoaderFactories(int frame_tree_node_id,
-                                                                            ukm::SourceIdObj ukm_source_id,
-                                                                            NonNetworkURLLoaderFactoryMap *factories)
+void ContentBrowserClientQt::RegisterNonNetworkNavigationURLLoaderFactories(
+        int frame_tree_node_id, NonNetworkURLLoaderFactoryMap *factories)
 {
     content::WebContents *web_contents = content::WebContents::FromFrameTreeNodeId(frame_tree_node_id);
     Profile *profile = Profile::FromBrowserContext(web_contents->GetBrowserContext());
@@ -1041,10 +1051,9 @@ void ContentBrowserClientQt::RegisterNonNetworkNavigationURLLoaderFactories(int
         factories->emplace(scheme.toStdString(), CreateCustomURLLoaderFactory(profileAdapter, web_contents));
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
-    factories->emplace(
-        extensions::kExtensionScheme,
-        extensions::CreateExtensionNavigationURLLoaderFactory(profile, ukm_source_id,
-                                                              !!extensions::WebViewGuest::FromWebContents(web_contents)));
+    factories->emplace(extensions::kExtensionScheme,
+                       extensions::CreateExtensionNavigationURLLoaderFactory(
+                               profile, !!extensions::WebViewGuest::FromWebContents(web_contents)));
 #endif
 }
 
@@ -1309,7 +1318,7 @@ void ContentBrowserClientQt::CreateWebSocket(
     std::move(factory).Run(to_url, std::move(headers), std::move(handshake_client), mojo::NullRemote(), mojo::NullRemote());
 }
 
-void ContentBrowserClientQt::SiteInstanceGotProcess(content::SiteInstance *site_instance)
+void ContentBrowserClientQt::SiteInstanceGotProcessAndSite(content::SiteInstance *site_instance)
 {
 #if BUILDFLAG(ENABLE_EXTENSIONS)
     content::BrowserContext *context = site_instance->GetBrowserContext();
diff --git a/src/core/content_browser_client_qt.h b/src/core/content_browser_client_qt.h
index 7d8e9802898..333b08ea22d 100644
--- a/src/core/content_browser_client_qt.h
+++ b/src/core/content_browser_client_qt.h
@@ -10,17 +10,10 @@
 namespace content {
 class BrowserContext;
 class BrowserMainParts;
-
-#if QT_CONFIG(webengine_pepper_plugins)
-class BrowserPpapiHost;
-#endif
-
 class DevToolsManagerDelegate;
 class RenderFrameHost;
 class RenderProcessHost;
-class ResourceContext;
 class WebContents;
-struct MainFunctionParams;
 struct Referrer;
 } // namespace content
 
@@ -150,15 +143,13 @@ class ContentBrowserClientQt : public content::ContentBrowserClient
     void GetAdditionalMappedFilesForChildProcess(const base::CommandLine& command_line, int child_process_id, content::PosixFileDescriptorInfo* mappings) override;
 #endif
 
-    std::unique_ptr<content::LoginDelegate> CreateLoginDelegate(
-            const net::AuthChallengeInfo &auth_info,
-            content::WebContents *web_contents,
-            const content::GlobalRequestID& request_id,
-            bool is_request_for_main_frame,
-            const GURL &url,
-            scoped_refptr<net::HttpResponseHeaders> response_headers,
-            bool first_auth_attempt,
-            LoginAuthRequiredCallback auth_required_callback) override;
+    std::unique_ptr<content::LoginDelegate>
+    CreateLoginDelegate(const net::AuthChallengeInfo &auth_info, content::WebContents *web_contents,
+                        content::BrowserContext *browser_context,
+                        const content::GlobalRequestID &request_id, bool is_request_for_main_frame,
+                        const GURL &url, scoped_refptr<net::HttpResponseHeaders> response_headers,
+                        bool first_auth_attempt,
+                        LoginAuthRequiredCallback auth_required_callback) override;
 
     bool HandleExternalProtocol(
             const GURL &url,
@@ -174,10 +165,12 @@ class ContentBrowserClientQt : public content::ContentBrowserClient
             content::RenderFrameHost *initiator_document,
             mojo::PendingRemote<network::mojom::URLLoaderFactory> *out_factory) override;
 
-    std::vector<std::unique_ptr<blink::URLLoaderThrottle>> CreateURLLoaderThrottles(
-            const network::ResourceRequest &request, content::BrowserContext *browser_context,
-            const base::RepeatingCallback<content::WebContents *()> &wc_getter,
-            content::NavigationUIData *navigation_ui_data, int frame_tree_node_id) override;
+    std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
+    CreateURLLoaderThrottles(const network::ResourceRequest &request,
+                             content::BrowserContext *browser_context,
+                             const base::RepeatingCallback<content::WebContents *()> &wc_getter,
+                             content::NavigationUIData *navigation_ui_data, int frame_tree_node_id,
+                             absl::optional<int64_t> navigation_id) override;
 
     std::vector<std::unique_ptr<content::NavigationThrottle>> CreateThrottlesForNavigation(
             content::NavigationHandle *navigation_handle) override;
@@ -214,9 +207,8 @@ class ContentBrowserClientQt : public content::ContentBrowserClient
                                        cert_verifier::mojom::CertVerifierCreationParams *cert_verifier_creation_params) override;
 
     std::vector<base::FilePath> GetNetworkContextsParentDirectory() override;
-    void RegisterNonNetworkNavigationURLLoaderFactories(int frame_tree_node_id,
-                                                        ukm::SourceIdObj ukm_source_id,
-                                                        NonNetworkURLLoaderFactoryMap *factories) override;
+    void RegisterNonNetworkNavigationURLLoaderFactories(
+            int frame_tree_node_id, NonNetworkURLLoaderFactoryMap *factories) override;
     void RegisterNonNetworkSubresourceURLLoaderFactories(int render_process_id, int render_frame_id,
                                                          const absl::optional<url::Origin>& request_initiator_origin,
                                                          NonNetworkURLLoaderFactoryMap *factories) override;
@@ -224,7 +216,7 @@ class ContentBrowserClientQt : public content::ContentBrowserClient
                                                                 NonNetworkURLLoaderFactoryMap* factories) override;
     void RegisterNonNetworkServiceWorkerUpdateURLLoaderFactories(content::BrowserContext* browser_context,
                                                                  NonNetworkURLLoaderFactoryMap* factories) override;
-    void SiteInstanceGotProcess(content::SiteInstance *site_instance) override;
+    void SiteInstanceGotProcessAndSite(content::SiteInstance *site_instance) override;
     base::flat_set<std::string> GetPluginMimeTypesWithExternalHandlers(content::BrowserContext *browser_context) override;
 
     std::unique_ptr<content::WebContentsViewDelegate> GetWebContentsViewDelegate(content::WebContents *web_contents) override;
diff --git a/src/core/content_client_qt.cpp b/src/core/content_client_qt.cpp
index b6a0909b01c..0901f39b6b4 100644
--- a/src/core/content_client_qt.cpp
+++ b/src/core/content_client_qt.cpp
@@ -29,6 +29,7 @@
 #include <QFile>
 #include <QLibraryInfo>
 #include <QString>
+#include <QSysInfo>
 
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
@@ -57,13 +58,15 @@ const char kWidevineCdmFileName[] =
 const char kPdfPluginPath[] = "internal-pdf-viewer";
 #endif // QT_CONFIG(webengine_printing_and_pdf)
 
+using namespace Qt::StringLiterals;
 using Robustness = content::CdmInfo::Robustness;
 
 static QString webenginePluginsPath()
 {
     // Look for plugins in /plugins/webengine or application dir.
     static bool initialized = false;
-    static QString potentialPluginsPath = QLibraryInfo::path(QLibraryInfo::PluginsPath) % QLatin1String("/webengine");
+    static QString potentialPluginsPath =
+            QLibraryInfo::path(QLibraryInfo::PluginsPath) % "/webengine"_L1;
     if (!initialized) {
         initialized = true;
         if (!QFileInfo::exists(potentialPluginsPath))
@@ -107,7 +110,8 @@ static QString ppapiPluginsPath()
 {
     // Look for plugins in /plugins/ppapi or application dir.
     static bool initialized = false;
-    static QString potentialPluginsPath = QLibraryInfo::path(QLibraryInfo::PluginsPath) % QLatin1String("/ppapi");
+    static QString potentialPluginsPath =
+            QLibraryInfo::path(QLibraryInfo::PluginsPath) % "/ppapi"_L1;
     if (!initialized) {
         initialized = true;
         if (!QFileInfo::exists(potentialPluginsPath))
@@ -151,7 +155,7 @@ namespace QtWebEngineCore {
 static const QDir widevineCdmDirHint(const QDir &widevineDir)
 {
     const QString hintFilePath = widevineDir.absolutePath() % QDir::separator()
-            % QLatin1String("latest-component-updated-widevine-cdm");
+            % "latest-component-updated-widevine-cdm"_L1;
     if (!QFileInfo::exists(hintFilePath)) {
         // CDM hint file does not exist.
         return widevineDir;
@@ -189,32 +193,36 @@ static bool IsWidevineAvailable(base::FilePath *cdm_path,
     if (!widevine_argument.empty())
         pluginPaths << QtWebEngineCore::toQt(widevine_argument);
     else {
-        pluginPaths << webenginePluginsPath() + QStringLiteral("/") + QString::fromLatin1(kWidevineCdmFileName);
+        pluginPaths << webenginePluginsPath() + u'/' + QLatin1StringView(kWidevineCdmFileName);
 #if QT_CONFIG(webengine_pepper_plugins)
-        pluginPaths << ppapiPluginsPath() + QStringLiteral("/") + QString::fromLatin1(kWidevineCdmFileName);
+        pluginPaths << ppapiPluginsPath() + u'/' + QLatin1StringView(kWidevineCdmFileName);
 #endif
 #if defined(Q_OS_OSX)
-    QDir potentialWidevineDir("/Applications/Google Chrome.app/Contents/Frameworks");
+    QDir potentialWidevineDir(u"/Applications/Google Chrome.app/Contents/Frameworks"_s);
+    const auto archDir = QSysInfo::currentCpuArchitecture() == "x86_64"_L1
+                       ? "mac_x64/"_L1
+                       : "mac_arm64/"_L1;
     if (potentialWidevineDir.exists()) {
         QFileInfoList widevineVersionDirs = potentialWidevineDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot,
                                                                                QDir::Name | QDir::Reversed);
-        const QString library = QLatin1String("/Versions/Current/Libraries/"
-                                              "WidevineCdm/_platform_specific/mac_x64/libwidevinecdm.dylib");
+        const auto libraryBase = "/Versions/Current/Libraries/WidevineCdm/_platform_specific/"_L1;
+        const auto libraryFilename = "libwidevinecdm.dylib"_L1;
         for (const QFileInfo &info : widevineVersionDirs)
-            pluginPaths << info.absoluteFilePath() + library;
+            pluginPaths << info.absoluteFilePath() + libraryBase + archDir + libraryFilename;
     }
 
-    QDir oldPotentialWidevineDir(QDir::homePath() + "/Library/Application Support/Google/Chrome/WidevineCDM");
+    QDir oldPotentialWidevineDir(QDir::homePath() + "/Library/Application Support/Google/Chrome/WidevineCDM"_L1);
     if (oldPotentialWidevineDir.exists()) {
         QFileInfoList widevineVersionDirs = oldPotentialWidevineDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed);
         for (int i = 0; i < widevineVersionDirs.size(); ++i) {
-            QString versionDirPath(widevineVersionDirs.at(i).absoluteFilePath());
-            QString potentialWidevinePluginPath = versionDirPath + "/_platform_specific/mac_x64/" + QString::fromLatin1(kWidevineCdmFileName);
-            pluginPaths << potentialWidevinePluginPath;
+            const QString versionDirPath = widevineVersionDirs.at(i).absoluteFilePath();
+            QString potentialWidevinePluginPath = versionDirPath + "/_platform_specific/"_L1 + archDir
+                    + QLatin1StringView(kWidevineCdmFileName);
+            pluginPaths.append(std::move(potentialWidevinePluginPath));
         }
     }
 #elif defined(Q_OS_WIN)
-    const QString googleChromeDir = QLatin1String("/Google/Chrome/Application");
+    const auto googleChromeDir = "/Google/Chrome/Application"_L1;
     const QStringList programFileDirs{getProgramFilesDir() + googleChromeDir,
                                       getProgramFilesDir(true) + googleChromeDir};
     for (const QString &dir : programFileDirs) {
@@ -222,54 +230,54 @@ static bool IsWidevineAvailable(base::FilePath *cdm_path,
         if (d.exists()) {
             QFileInfoList widevineVersionDirs = d.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed);
             for (int i = 0; i < widevineVersionDirs.size(); ++i) {
-                QString versionDirPath(widevineVersionDirs.at(i).absoluteFilePath());
+                const QString versionDirPath = widevineVersionDirs.at(i).absoluteFilePath();
 #ifdef WIN64
                 QString potentialWidevinePluginPath = versionDirPath +
-                                                        "/WidevineCdm/_platform_specific/win_x64/" +
-                                                        QString::fromLatin1(kWidevineCdmFileName);
+                                                        "/WidevineCdm/_platform_specific/win_x64/"_L1 +
+                                                        QLatin1StringView(kWidevineCdmFileName);
 #else
                 QString potentialWidevinePluginPath = versionDirPath +
-                                                        "/WidevineCdm/_platform_specific/win_x86/" +
-                                                        QString::fromLatin1(kWidevineCdmFileName);
+                                                        "/WidevineCdm/_platform_specific/win_x86/"_L1 +
+                                                        QLatin1StringView(kWidevineCdmFileName);
 #endif
-                pluginPaths << potentialWidevinePluginPath;
+                pluginPaths.append(std::move(potentialWidevinePluginPath));
             }
         }
     }
-    QDir potentialWidevineDir(getLocalAppDataDir() + "/Google/Chrome/User Data/WidevineCDM");
+    QDir potentialWidevineDir(getLocalAppDataDir() + "/Google/Chrome/User Data/WidevineCDM"_L1);
     if (potentialWidevineDir.exists()) {
         QFileInfoList widevineVersionDirs = potentialWidevineDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed);
         for (int i = 0; i < widevineVersionDirs.size(); ++i) {
-            QString versionDirPath(widevineVersionDirs.at(i).absoluteFilePath());
+            const QString versionDirPath = widevineVersionDirs.at(i).absoluteFilePath();
 #ifdef WIN64
-            QString potentialWidevinePluginPath = versionDirPath + "/_platform_specific/win_x64/" + QString::fromLatin1(kWidevineCdmFileName);
+            QString potentialWidevinePluginPath = versionDirPath + "/_platform_specific/win_x64/"_L1 + QLatin1StringView(kWidevineCdmFileName);
 #else
-            QString potentialWidevinePluginPath = versionDirPath + "/_platform_specific/win_x86/" + QString::fromLatin1(kWidevineCdmFileName);
+            QString potentialWidevinePluginPath = versionDirPath + "/_platform_specific/win_x86/"_L1 + QLatin1StringView(kWidevineCdmFileName);
 #endif
-            pluginPaths << potentialWidevinePluginPath;
+            pluginPaths.append(std::move(potentialWidevinePluginPath));
         }
     }
 #elif defined(Q_OS_LINUX)
         QList<QDir> potentialWidevineVersionDirs;
 
         // Google Chrome widevine modules
-        QDir chromeWidevineDir(QDir::homePath() + "/.config/google-chrome/WidevineCdm");
+        QDir chromeWidevineDir(QDir::homePath() + "/.config/google-chrome/WidevineCdm"_L1);
         if (chromeWidevineDir.exists())
             potentialWidevineVersionDirs << widevineCdmDirHint(chromeWidevineDir);
 
         // Firefox widevine modules
-        QDir firefoxPotentialProfilesDir(QDir::homePath() + "/.mozilla/firefox");
+        QDir firefoxPotentialProfilesDir(QDir::homePath() + "/.mozilla/firefox"_L1);
         if (firefoxPotentialProfilesDir.exists()) {
             QFileInfoList firefoxProfileDirs = firefoxPotentialProfilesDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed);
             for (const QFileInfo &info : firefoxProfileDirs) {
-                QDir widevinePluginsDir(info.absoluteFilePath() + "/gmp-widevinecdm");
+                QDir widevinePluginsDir(info.absoluteFilePath() + "/gmp-widevinecdm"_L1);
                 if (widevinePluginsDir.exists())
                     potentialWidevineVersionDirs << widevinePluginsDir;
             }
         }
 
         // Chromium widevine modules (might not work with proprietary codecs)
-        QDir chromiumWidevineDir(QDir::homePath() + "/.config/chromium/WidevineCdm");
+        QDir chromiumWidevineDir(QDir::homePath() + "/.config/chromium/WidevineCdm"_L1);
         if (chromiumWidevineDir.exists())
             potentialWidevineVersionDirs << widevineCdmDirHint(chromiumWidevineDir);
 
@@ -279,28 +287,29 @@ static bool IsWidevineAvailable(base::FilePath *cdm_path,
             widevineVersionDirs.prepend(QFileInfo(dir.absolutePath()));
             // ### alternatively look up in the manifest.json and take the path from there.
 #if Q_PROCESSOR_WORDSIZE == 8
-            const QString library = QLatin1String("/_platform_specific/linux_x64/libwidevinecdm.so");
+            const auto library = "/_platform_specific/linux_x64/libwidevinecdm.so"_L1;
 #else
-            const QString library = QLatin1String("/_platform_specific/linux_x86/libwidevinecdm.so");
+            const auto library = "/_platform_specific/linux_x86/libwidevinecdm.so"_L1;
 #endif
             for (const QFileInfo &info : widevineVersionDirs) {
-                pluginPaths << info.absoluteFilePath() + "/libwidevinecdm.so";
+                pluginPaths << info.absoluteFilePath() + "/libwidevinecdm.so"_L1;
                 pluginPaths << info.absoluteFilePath() + library;
             }
         }
 
         // Fixed paths:
-        pluginPaths << QStringLiteral("/usr/lib/chromium/libwidevinecdm.so") // Arch
-                    << QStringLiteral("/usr/lib/chromium-browser/libwidevinecdm.so") // Ubuntu/neon
-                    << QStringLiteral("/usr/lib64/chromium/libwidevinecdm.so") // OpenSUSE style
+        pluginPaths
+                << u"/usr/lib/chromium/libwidevinecdm.so"_s // Arch
+                << u"/usr/lib/chromium-browser/libwidevinecdm.so"_s // Ubuntu/neon
+                << u"/usr/lib64/chromium/libwidevinecdm.so"_s // OpenSUSE style
 #if Q_PROCESSOR_WORDSIZE == 8
-                    << QStringLiteral("/usr/lib64/chromium-browser/WidevineCdm/_platform_specific/linux_x64/libwidevinecdm.so") // Gentoo
-                    << QStringLiteral("/opt/google/chrome/WidevineCdm/_platform_specific/linux_x64/libwidevinecdm.so") // Old Google Chrome
+                << u"/usr/lib64/chromium-browser/WidevineCdm/_platform_specific/linux_x64/libwidevinecdm.so"_s // Gentoo
+                << u"/opt/google/chrome/WidevineCdm/_platform_specific/linux_x64/libwidevinecdm.so"_s // Old Google Chrome
 #else
-                    << QStringLiteral("/usr/lib/chromium-browser/WidevineCdm/_platform_specific/linux_x86/libwidevinecdm.so") // Gentoo
-                    << QStringLiteral("/opt/google/chrome/WidevineCdm/_platform_specific/linux_x86/libwidevinecdm.so") // Old Google Chrome
+                << u"/usr/lib/chromium-browser/WidevineCdm/_platform_specific/linux_x86/libwidevinecdm.so"_s // Gentoo
+                << u"/opt/google/chrome/WidevineCdm/_platform_specific/linux_x86/libwidevinecdm.so"_s // Old Google Chrome
 #endif
-                    << QStringLiteral("/opt/google/chrome/libwidevinecdm.so"); // Older Google Chrome
+                << u"/opt/google/chrome/libwidevinecdm.so"_s; // Older Google Chrome
 #endif
     }
 
@@ -342,9 +351,12 @@ void ContentClientQt::AddContentDecryptionModules(std::vector<content::CdmInfo>
 {
     Q_UNUSED(cdm_host_file_paths);
     if (cdms) {
+#if defined(WIDEVINE_CDM_AVAILABLE_NOT_COMPONENT) || BUILDFLAG(ENABLE_LIBRARY_CDMS)
+        media::CdmCapability capability;
+#endif
+
 #if defined(WIDEVINE_CDM_AVAILABLE_NOT_COMPONENT)
         base::FilePath cdm_path;
-        media::CdmCapability capability;
         if (IsWidevineAvailable(&cdm_path, &capability)) {
             const base::Version version;
             cdms->push_back(content::CdmInfo(kWidevineKeySystem, Robustness::kSoftwareSecure, std::move(capability),
@@ -359,10 +371,10 @@ void ContentClientQt::AddContentDecryptionModules(std::vector<content::CdmInfo>
         base::FilePath clear_key_cdm_path = command_line->GetSwitchValuePath(switches::kClearKeyCdmPathForTesting);
         if (!clear_key_cdm_path.empty() && base::PathExists(clear_key_cdm_path)) {
             // Supported codecs are hard-coded in ExternalClearKeyProperties.
-            media::CdmCapability capability(
-                {}, {}, {media::EncryptionScheme::kCenc, media::EncryptionScheme::kCbcs},
-                {media::CdmSessionType::kTemporary,
-                 media::CdmSessionType::kPersistentLicense});
+            capability = media::CdmCapability(
+                    {}, {}, { media::EncryptionScheme::kCenc, media::EncryptionScheme::kCbcs },
+                    { media::CdmSessionType::kTemporary,
+                      media::CdmSessionType::kPersistentLicense });
 
             // Register media::kExternalClearKeyDifferentCdmTypeTestKeySystem first separately.
             // Otherwise, it'll be treated as a sub-key-system of normal
diff --git a/src/core/content_main_delegate_qt.cpp b/src/core/content_main_delegate_qt.cpp
index f949d93a5ad..148be4f1c9d 100644
--- a/src/core/content_main_delegate_qt.cpp
+++ b/src/core/content_main_delegate_qt.cpp
@@ -201,11 +201,13 @@ content::ContentBrowserClient *ContentMainDelegateQt::CreateContentBrowserClient
     return m_browserClient.get();
 }
 
+#if defined(USE_OZONE) || BUILDFLAG(IS_WIN)
 content::ContentGpuClient *ContentMainDelegateQt::CreateContentGpuClient()
 {
     m_gpuClient.reset(new ContentGpuClientQt);
     return m_gpuClient.get();
 }
+#endif
 
 content::ContentRendererClient *ContentMainDelegateQt::CreateContentRendererClient()
 {
diff --git a/src/core/content_main_delegate_qt.h b/src/core/content_main_delegate_qt.h
index a177bd6df84..534d9e4dd63 100644
--- a/src/core/content_main_delegate_qt.h
+++ b/src/core/content_main_delegate_qt.h
@@ -6,11 +6,14 @@
 
 #include "content/public/app/content_main_delegate.h"
 
-#include "compositor/content_gpu_client_qt.h"
 #include "content_browser_client_qt.h"
 #include "content_client_qt.h"
 #include "content_utility_client_qt.h"
 
+#if defined(USE_OZONE) || BUILDFLAG(IS_WIN)
+#include "compositor/content_gpu_client_qt.h"
+#endif
+
 namespace QtWebEngineCore {
 
 class ContentMainDelegateQt : public content::ContentMainDelegate
@@ -23,7 +26,9 @@ class ContentMainDelegateQt : public content::ContentMainDelegate
 
     content::ContentClient *CreateContentClient() override;
     content::ContentBrowserClient* CreateContentBrowserClient() override;
+#if defined(USE_OZONE) || BUILDFLAG(IS_WIN)
     content::ContentGpuClient* CreateContentGpuClient() override;
+#endif
     content::ContentRendererClient* CreateContentRendererClient() override;
     content::ContentUtilityClient* CreateContentUtilityClient() override;
     absl::optional<int> BasicStartupComplete() override;
@@ -31,7 +36,9 @@ class ContentMainDelegateQt : public content::ContentMainDelegate
 private:
     ContentClientQt m_contentClient;
     std::unique_ptr<ContentBrowserClientQt> m_browserClient;
+#if defined(USE_OZONE) || BUILDFLAG(IS_WIN)
     std::unique_ptr<ContentGpuClientQt> m_gpuClient;
+#endif
     std::unique_ptr<ContentUtilityClientQt> m_utilityClient;
 };
 
diff --git a/src/core/desktop_media_controller.cpp b/src/core/desktop_media_controller.cpp
index 50ac0a40c45..2321d31047c 100644
--- a/src/core/desktop_media_controller.cpp
+++ b/src/core/desktop_media_controller.cpp
@@ -15,6 +15,8 @@
 #include "content/public/browser/desktop_capture.h"
 #endif // QT_CONFIG(webengine_webrtc)
 
+#include <QtCore/QTimer>
+
 namespace QtWebEngineCore {
 namespace {
 DesktopMediaList::Type toMediaListType(DesktopMediaType type)
@@ -39,15 +41,21 @@ std::unique_ptr<DesktopMediaList> createMediaList(DesktopMediaType type)
     case DesktopMediaList::Type::kScreen: {
         std::unique_ptr<webrtc::DesktopCapturer> screenCapturer =
                 webrtc::DesktopCapturer::CreateScreenCapturer(options);
-        std::unique_ptr<DesktopCapturerWrapper> capturer =
-                std::make_unique<DesktopCapturerWrapper>(std::move(screenCapturer));
+        if (!screenCapturer) {
+            qWarning("Screen capturing is not available. Media list will be empty.");
+            return nullptr;
+        }
+        auto capturer = std::make_unique<DesktopCapturerWrapper>(std::move(screenCapturer));
         return std::make_unique<NativeDesktopMediaList>(listType, std::move(capturer));
     }
     case DesktopMediaList::Type::kWindow: {
         std::unique_ptr<webrtc::DesktopCapturer> windowCapturer =
                 webrtc::DesktopCapturer::CreateWindowCapturer(options);
-        std::unique_ptr<DesktopCapturerWrapper> capturer =
-                std::make_unique<DesktopCapturerWrapper>(std::move(windowCapturer));
+        if (!windowCapturer) {
+            qWarning("Window capturing is not available. Media list will be empty.");
+            return nullptr;
+        }
+        auto capturer = std::make_unique<DesktopCapturerWrapper>(std::move(windowCapturer));
         return std::make_unique<NativeDesktopMediaList>(
                 listType, std::move(capturer),
                 !content::desktop_capture::ShouldEnumerateCurrentProcessWindows());
@@ -69,6 +77,7 @@ class DesktopMediaListQtPrivate : public DesktopMediaListObserver
 
     void init();
     void startUpdating();
+    int getSourceCount() const;
     const DesktopMediaList::Source& getSource(int index) const;
 
     void OnSourceAdded(int index) override;
@@ -80,21 +89,19 @@ class DesktopMediaListQtPrivate : public DesktopMediaListObserver
     void OnDelegatedSourceListSelection() override { }
     void OnDelegatedSourceListDismissed() override { }
 
-    bool isInitialized;
     std::unique_ptr<DesktopMediaList> mediaList;
     DesktopMediaListQt *q_ptr;
     Q_DECLARE_PUBLIC(DesktopMediaListQt)
 };
 
 DesktopMediaListQtPrivate::DesktopMediaListQtPrivate(DesktopMediaType type, DesktopMediaListQt *qq)
-    : isInitialized(false)
-    , mediaList(createMediaList(type))
-    , q_ptr(qq)
+    : mediaList(createMediaList(type)), q_ptr(qq)
 {
 }
 
 const DesktopMediaList::Source& DesktopMediaListQtPrivate::getSource(int index) const
 {
+    Q_ASSERT(mediaList);
     return mediaList->GetSource(index);
 }
 
@@ -105,21 +112,30 @@ void DesktopMediaListQtPrivate::init()
     // This makes direct 'selectScreen/Window' calls possible from the frontend.
     // Note: StartUpdating should be called after Update is completed as it can overwrite the
     // internal cb.
-    base::OnceCallback<void()> onComplete = base::BindOnce(
-            [](DesktopMediaListQtPrivate *observer) {
-                observer->isInitialized = true;
-                Q_EMIT observer->q_ptr->initialized();
-                observer->startUpdating();
-            },
-            this);
-    mediaList->Update(std::move(onComplete));
+    if (mediaList) {
+        base::OnceCallback<void()> onComplete = base::BindOnce(
+                [](DesktopMediaListQtPrivate *observer) {
+                    Q_EMIT observer->q_ptr->initialized();
+                    observer->startUpdating();
+                },
+                this);
+        mediaList->Update(std::move(onComplete));
+    } else {
+        QTimer::singleShot(0, q_ptr, [this]() { Q_EMIT q_ptr->initialized(); });
+    }
 }
 
 void DesktopMediaListQtPrivate::startUpdating()
 {
+    Q_ASSERT(mediaList);
     mediaList->StartUpdating(this);
 }
 
+int DesktopMediaListQtPrivate::getSourceCount() const
+{
+    return mediaList ? mediaList->GetSourceCount() : 0;
+}
+
 void DesktopMediaListQtPrivate::OnSourceAdded(int index)
 {
     Q_Q(DesktopMediaListQt);
@@ -159,12 +175,7 @@ QString DesktopMediaListQt::getSourceName(int index) const
 
 int DesktopMediaListQt::getSourceCount() const
 {
-    return d->mediaList->GetSourceCount();
-}
-
-bool DesktopMediaListQt::isInitialized() const
-{
-    return d->isInitialized;
+    return d->getSourceCount();
 }
 
 DesktopMediaControllerPrivate::DesktopMediaControllerPrivate(
@@ -172,6 +183,7 @@ DesktopMediaControllerPrivate::DesktopMediaControllerPrivate(
     : doneCallback(std::move(doneCallback))
     , screens(new DesktopMediaListQt(DesktopMediaType::Screen))
     , windows(new DesktopMediaListQt(DesktopMediaType::Window))
+    , numInitialized(0)
 {
 }
 
@@ -198,16 +210,14 @@ DesktopMediaController::DesktopMediaController(DesktopMediaControllerPrivate *dd
     // Make sure both lists are populated before sending the request.
     DesktopMediaListQt *screens = DesktopMediaController::screens();
     DesktopMediaListQt *windows = DesktopMediaController::windows();
-    QObject::connect(screens, &DesktopMediaListQt::initialized, [windows, this]() {
-        if (windows->isInitialized())
-            Q_EMIT mediaListsInitialized();
-    });
-
-    QObject::connect(windows, &DesktopMediaListQt::initialized, [screens, this]() {
-        if (screens->isInitialized())
+    auto initCb = [this] {
+        ++d->numInitialized;
+        if (d->numInitialized == 2)
             Q_EMIT mediaListsInitialized();
-    });
+    };
 
+    QObject::connect(screens, &DesktopMediaListQt::initialized, initCb);
+    QObject::connect(windows, &DesktopMediaListQt::initialized, initCb);
     screens->d->init();
     windows->d->init();
 }
diff --git a/src/core/desktop_media_controller.h b/src/core/desktop_media_controller.h
index 0cb7412250a..fee3f34c9fd 100644
--- a/src/core/desktop_media_controller.h
+++ b/src/core/desktop_media_controller.h
@@ -35,7 +35,6 @@ class Q_WEBENGINECORE_EXPORT DesktopMediaListQt : public QObject
 private:
     friend class DesktopMediaController;
     friend class DesktopMediaControllerPrivate;
-    bool isInitialized() const;
     explicit DesktopMediaListQt(DesktopMediaType type);
     std::unique_ptr<DesktopMediaListQtPrivate> d;
 };
diff --git a/src/core/desktop_media_controller_p.h b/src/core/desktop_media_controller_p.h
index 4bb3a6312f7..95d4fd585c5 100644
--- a/src/core/desktop_media_controller_p.h
+++ b/src/core/desktop_media_controller_p.h
@@ -21,6 +21,7 @@ class Q_WEBENGINECORE_EXPORT DesktopMediaControllerPrivate
     base::OnceCallback<void(content::DesktopMediaID)> doneCallback;
     QScopedPointer<DesktopMediaListQt> screens;
     QScopedPointer<DesktopMediaListQt> windows;
+    int numInitialized;
 };
 
 } // namespace QtWebEngineCore
diff --git a/src/core/desktop_screen_qt.cpp b/src/core/desktop_screen_qt.cpp
index fb68f7b092f..2b45e91d11d 100644
--- a/src/core/desktop_screen_qt.cpp
+++ b/src/core/desktop_screen_qt.cpp
@@ -2,7 +2,7 @@
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 
 #include "desktop_screen_qt.h"
-
+#include "ui/base/ozone_buildflags.h"
 #include "ui/display/display.h"
 
 #include "type_conversion.h"
@@ -10,14 +10,11 @@
 #include <QGuiApplication>
 #include <QScreen>
 
-#if defined(USE_OZONE)
-#include "ui/ozone/buildflags.h"
-#if BUILDFLAG(OZONE_PLATFORM_X11)
+#if BUILDFLAG(IS_LINUX) && BUILDFLAG(IS_OZONE_X11)
 #define USE_XSCREENSAVER
 #include "ui/base/x/x11_screensaver.h"
 #include "ui/base/x/x11_util.h"
 #endif
-#endif
 
 #include <cmath>
 
diff --git a/src/core/devtools_manager_delegate_qt.cpp b/src/core/devtools_manager_delegate_qt.cpp
index 6654ead0ec6..b584ecb0de7 100644
--- a/src/core/devtools_manager_delegate_qt.cpp
+++ b/src/core/devtools_manager_delegate_qt.cpp
@@ -20,6 +20,7 @@
 #include "net/socket/tcp_server_socket.h"
 #include "ui/base/resource/resource_bundle.h"
 
+using namespace Qt::StringLiterals;
 using content::DevToolsAgentHost;
 
 namespace {
@@ -52,7 +53,7 @@ class TCPServerSocketFactory : public content::DevToolsSocketFactory {
 namespace QtWebEngineCore {
 
 DevToolsServerQt::DevToolsServerQt()
-    : m_bindAddress(QLatin1String("127.0.0.1"))
+    : m_bindAddress(u"127.0.0.1"_s)
     , m_port(0)
     , m_valid(false)
     , m_isStarted(false)
@@ -72,7 +73,7 @@ void DevToolsServerQt::parseAddressAndPort()
     if (commandLine.HasSwitch(switches::kRemoteDebuggingPort)) {
         portStr = QString::fromStdString(commandLine.GetSwitchValueASCII(switches::kRemoteDebuggingPort));
     } else if (!inspectorEnv.isEmpty()) {
-        int portColonPos = inspectorEnv.lastIndexOf(':');
+        int portColonPos = inspectorEnv.lastIndexOf(u':');
         if (portColonPos != -1) {
             portStr = inspectorEnv.mid(portColonPos + 1);
             m_bindAddress = inspectorEnv.mid(0, portColonPos);
@@ -83,8 +84,12 @@ void DevToolsServerQt::parseAddressAndPort()
 
     m_port = portStr.toInt(&m_valid);
     m_valid = m_valid && (m_port > 0 && m_port < 65535);
-    if (!m_valid)
-        qWarning("Invalid port given for the inspector server \"%s\". Examples of valid input: \"12345\" or \"192.168.2.14:12345\" (with the address of one of this host's network interface).", qPrintable(portStr));
+    if (!m_valid) {
+        qWarning("Invalid port given for the inspector server \"%ls\". "
+                 "Examples of valid input: \"12345\" or \"192.168.2.14:12345\" "
+                 "(with the address of one of this host's network interface).",
+                 qUtf16Printable(portStr));
+    }
 }
 
 std::unique_ptr<content::DevToolsSocketFactory> DevToolsServerQt::CreateSocketFactory()
@@ -123,8 +128,9 @@ void DevToolsServerQt::stop()
 void DevToolsManagerDelegateQt::Initialized(const net::IPEndPoint *ip_address)
 {
     if (ip_address && ip_address->address().size()) {
-        QString addressAndPort = QString::fromStdString(ip_address->ToString());
-        qWarning("Remote debugging server started successfully. Try pointing a Chromium-based browser to http://%s", qPrintable(addressAndPort));
+        qWarning("Remote debugging server started successfully. "
+                 "Try pointing a Chromium-based browser to http://%s",
+                 ip_address->ToString().c_str());
     }
     else
         qWarning("Couldn't start the inspector server on bind address. In case of invalid input, try something like: \"12345\" or \"192.168.2.14:12345\" (with the address of one of this host's interface).");
diff --git a/src/core/doc/qtwebengine.qdocconf b/src/core/doc/qtwebengine.qdocconf
index 6b78f13f8ce..d67c6c69fea 100644
--- a/src/core/doc/qtwebengine.qdocconf
+++ b/src/core/doc/qtwebengine.qdocconf
@@ -13,7 +13,7 @@ qhp.QtWebEngine.virtualFolder       = qtwebengine
 qhp.QtWebEngine.indexTitle          = Qt WebEngine
 qhp.QtWebEngine.indexRoot           =
 
-qhp.QtWebEngine.subprojects         = classes qmltypes examples
+qhp.QtWebEngine.subprojects         = classes qmltypes examples widgetexamples
 
 qhp.QtWebEngine.subprojects.classes.title = C++ Classes and Namespaces
 qhp.QtWebEngine.subprojects.classes.indexTitle = Qt WebEngine C++ Classes and Namespaces
@@ -22,14 +22,19 @@ qhp.QtWebEngine.subprojects.classes.sortPages = true
 
 qhp.QtWebEngine.subprojects.qmltypes.title = QML Types
 qhp.QtWebEngine.subprojects.qmltypes.indexTitle = Qt WebEngine QML Types
-qhp.QtWebEngine.subprojects.qmltypes.selectors = qmltype
+qhp.QtWebEngine.subprojects.qmltypes.selectors = qmltype qmlvaluetype
 qhp.QtWebEngine.subprojects.qmltypes.sortPages = true
 
-qhp.QtWebEngine.subprojects.examples.title = Examples
-qhp.QtWebEngine.subprojects.examples.indexTitle = Qt WebEngine Examples
-qhp.QtWebEngine.subprojects.examples.selectors = doc:example
+qhp.QtWebEngine.subprojects.examples.title = Examples (Quick)
+qhp.QtWebEngine.subprojects.examples.indexTitle = Qt WebEngine Quick Examples
+qhp.QtWebEngine.subprojects.examples.selectors = group:webengine-examples
 qhp.QtWebEngine.subprojects.examples.sortPages = true
 
+qhp.QtWebEngine.subprojects.widgetexamples.title = Examples (Widgets)
+qhp.QtWebEngine.subprojects.widgetexamples.indexTitle = Qt WebEngine Widgets Examples
+qhp.QtWebEngine.subprojects.widgetexamples.selectors = group:webengine-widgetexamples
+qhp.QtWebEngine.subprojects.widgetexamples.sortPages = true
+
 manifestmeta.highlighted.names += "QtWebEngine/WebEngine Widgets Simple Browser Example" \
                 "QtWebEngine/WebEngine Quick Nano Browser" \
                 "QtWebEngine/Recipe Browser"
@@ -65,6 +70,7 @@ headerdirs  += ../../core/api \
 
 sourcedirs  += ../../core/api \
                ../../core/doc \
+               ../../core/tools/webenginedriver \
                ../../webenginequick/api \
                ../../webenginequick/doc \
                ../../webenginewidgets/api \
@@ -92,5 +98,9 @@ navigation.qmltypespage = "Qt WebEngine QML Types"
 # \QWE macro expands to 'Qt WebEngine' without auto-linking anywhere.
 macro.QWE    = "Qt \\WebEngine"
 
+# QTBUG-134416: Ignore overwrite warnings, attribution
+# documentation generates many of these
+spurious += "Output file already exists, overwriting .*"
+
 # Enforce zero documentation warnings
-warninglimit = 0
+warninglimit = 2
diff --git a/src/core/doc/src/qtwebengine-debugging.qdoc b/src/core/doc/src/qtwebengine-debugging.qdoc
index 3dd4d9276c3..1d996ad7dc8 100644
--- a/src/core/doc/src/qtwebengine-debugging.qdoc
+++ b/src/core/doc/src/qtwebengine-debugging.qdoc
@@ -90,6 +90,14 @@
             sandboxing of the network service though.
     \endlist
 
+    Any WebEngine command line options should be specified after the
+    \c {--webEngineArgs} option, which is used to separate the user's application
+    specific options from the WebEngine's ones.
+
+    \badcode
+    --webEngineArgs [WebEngine specific options]
+    \endcode
+
     Alternatively, the environment variable QTWEBENGINE_CHROMIUM_FLAGS can be
     set. For example, the following value could be set to disable logging while
     debugging an application called \e mybrowser:
diff --git a/src/core/doc/src/qtwebengine-deploying.qdoc b/src/core/doc/src/qtwebengine-deploying.qdoc
index 3d8a976c8f0..e79d7e1a3fb 100644
--- a/src/core/doc/src/qtwebengine-deploying.qdoc
+++ b/src/core/doc/src/qtwebengine-deploying.qdoc
@@ -29,6 +29,8 @@
         \li On Windows, \QWE only supports Windows Vista or newer as
             target platform. Due to use of newer API in Chromium, Windows XP is
             not supported. WinRT is not supported, either.
+        \li On Windows, the Visual C++ Redistributable version 14.28 or higher
+            is needed to run \QWE applications.
     \endlist
 
     \section1 Deploying Applications Manually
diff --git a/src/core/doc/src/qtwebengine-features.qdoc b/src/core/doc/src/qtwebengine-features.qdoc
index 9465d75a201..6599a229f96 100644
--- a/src/core/doc/src/qtwebengine-features.qdoc
+++ b/src/core/doc/src/qtwebengine-features.qdoc
@@ -358,9 +358,9 @@
 
     \QWE supports JavaScript Geolocation API with \l {Qt Positioning} as a
     backend. HTML5 geolocation is disabled by default. To explicitly allow it, the application
-    needs to listen to QWebEnginePage::featurePermissionRequested. Use QWebEnginePage::Geolocation
-    with a QWebEnginePage::setFeaturePermission() call or \l{WebEngineView::Feature}
-    with a \l{WebEngineView::grantFeaturePermission} {WebEngineView.grantFeaturePermission}() call
+    needs to listen to QWebEnginePage::permissionRequested. When a permission request
+    with a type of QWebEnginePermission::PermissionType::Geolocation is received,
+    you can call QWebEnginePermission::grant() on the received object
     to grant the required permission.
 
     If \QWE was built with Qt Positioning support then this feature can be
@@ -457,7 +457,7 @@
     WebEngineView::javaScriptDialogRequested(),
     WebEngineView::colorDialogRequested(),
     WebEngineView::fileDialogRequested(), and
-    WebEngineView::formValidationMessageRequested() signals. For an example,
+    WebEngineView::formValidationMessageRequested() signals.
 
     \section1 PDF File Viewing
 
@@ -643,6 +643,11 @@
     When the \QWE spellchecker initializes, it will try to load the
     \c bdict dictionaries and to check them for consistency.
 
+    For CMake, you can use the \l qt_add_webengine_dictionary command to convert
+    Hunspell \c .dic files into the \c .bdic binary format. The command creates
+    a \c qtwebengine_dictionaries target, which your project can use a
+    dependency.
+
     If \c QTWEBENGINE_DICTIONARIES_PATH is set, the spellchecker uses the
     dictionaries in the specified directory without looking anywere else.
     Otherwise, it uses the \e qtwebengine_dictionaries directory relative to the
diff --git a/src/core/doc/src/qtwebengine-index.qdoc b/src/core/doc/src/qtwebengine-index.qdoc
index b20a96dc1fe..b4243d9f824 100644
--- a/src/core/doc/src/qtwebengine-index.qdoc
+++ b/src/core/doc/src/qtwebengine-index.qdoc
@@ -9,6 +9,10 @@
 
     \QWE provides functionality for rendering regions of dynamic web content.
 
+    \note By default, Qt WebEngine depends on the \l {Qt Positioning} and
+    \l {Qt WebChannel} optional modules, but can be built without them. Be sure to
+    install these add-ons when using the online installer.
+
     The functionality in \QWE is divided into the following modules:
 
     \annotatedlist qtwebengine-modules
@@ -25,7 +29,6 @@
         \li \l{Porting from Qt WebKit to Qt WebEngine}
     \endlist
 
-
     \section1 Module Evolution
 
     \l{Changes to Qt WebEngine} lists important changes in the module API
diff --git a/src/core/doc/src/qtwebengine-platform-notes.qdoc b/src/core/doc/src/qtwebengine-platform-notes.qdoc
index 33bac101a08..dc71931130e 100644
--- a/src/core/doc/src/qtwebengine-platform-notes.qdoc
+++ b/src/core/doc/src/qtwebengine-platform-notes.qdoc
@@ -57,7 +57,7 @@
 
     \section2 Linux
 
-    On Linux, Clang or GCC version 9 or later is required.
+    On Linux, Clang or GCC version 10 or later is required.
 
     \QWE requires \c pkg-config to detect most of its dependencies. The
     following \c pkg-config files are required:
diff --git a/src/core/doc/src/qwebenginepage_lgpl.qdoc b/src/core/doc/src/qwebenginepage_lgpl.qdoc
index 1640ca8be37..e464a2472b3 100644
--- a/src/core/doc/src/qwebenginepage_lgpl.qdoc
+++ b/src/core/doc/src/qwebenginepage_lgpl.qdoc
@@ -245,6 +245,7 @@
 
 /*!
     \enum QWebEnginePage::PermissionPolicy
+    \deprecated [6.8] Replaced by QWebEnginePermission::State.
 
     This enum describes the permission policies that the user may set for data or device access:
 
@@ -273,6 +274,7 @@
 
 /*!
     \enum QWebEnginePage::Feature
+    \deprecated [6.8] Replaced by QWebEnginePermission::Feature.
 
     This enum describes the platform feature access categories that the user may be asked to grant
     or deny access to:
@@ -459,7 +461,7 @@
     The action is owned by the QWebEnginePage but you can customize the look by
     changing its properties.
 
-    \l{QWebEnginePage::action(WebAction action)} does not have a default styled icon.
+    The returned action does not have a default styled icon.
     Use \l{QWebEngineView::pageAction()} to have an action with a default styled icon.
 
     QWebEnginePage also takes care of implementing the action, so that upon
@@ -779,6 +781,8 @@
 
 /*!
     \fn void QWebEnginePage::setFeaturePermission(const QUrl &securityOrigin, Feature feature, PermissionPolicy policy)
+    \deprecated [6.8] Use QWebEnginePermission's \l {QWebEnginePermission::grant} {grant}(),
+    \l {QWebEnginePermission::deny} {deny}(), and \l {QWebEnginePermission::reset} {reset}() functions instead.
 
     Sets the permission for the web site identified by \a securityOrigin to use \a feature to \a policy.
 
@@ -792,6 +796,7 @@
 
 /*!
     \fn void QWebEnginePage::featurePermissionRequested(const QUrl &securityOrigin, Feature feature)
+    \deprecated [6.8] Use permissionRequested() instead.
 
     This signal is emitted when the web site identified by \a securityOrigin requests to make use of
     the resource or device identified by \a feature.
@@ -801,6 +806,7 @@
 
 /*!
     \fn void QWebEnginePage::featurePermissionRequestCanceled(const QUrl &securityOrigin, Feature feature)
+    \deprecated [6.8] This signal is no longer emitted.
 
     This signal is emitted when the web site identified by \a securityOrigin cancels a previously issued
     request to make use of \a feature.
@@ -809,6 +815,15 @@
 
 */
 
+/*!
+    \fn void QWebEnginePage::permissionRequested(QWebEnginePermission permission)
+    \since 6.8
+
+    This signal is emitted when a web site requests to make use of a feature (e.g. geolocation access,
+    permission to send notifications). The \a permission object can queried for the requesting URL
+    and the \c{QWebEnginePermission::Feature} it's asking for, as well as to grant or deny permission.
+*/
+
 /*!
     \fn void QWebEnginePage::titleChanged(const QString &title)
 
diff --git a/src/core/doc/src/qwebenginesettings_lgpl.qdoc b/src/core/doc/src/qwebenginesettings_lgpl.qdoc
index cd7ff8e8c0d..89fc0c8ddac 100644
--- a/src/core/doc/src/qwebenginesettings_lgpl.qdoc
+++ b/src/core/doc/src/qwebenginesettings_lgpl.qdoc
@@ -70,10 +70,13 @@
             Allows JavaScript programs to open popup windows without user
             interaction. Enabled by default.
     \value  JavascriptCanAccessClipboard
-            Allows JavaScript programs to read from and write to the clipboard.
-            Writing to the clipboard is always allowed if it is specifically requested by the user.
-            See JavascriptCanPaste to also allow pasting the content of the clipboard content from
-            JavaScript. Since unrestricted clipboard access is a potential security concern, it is
+            Allows JavaScript programs to write (copy) sanitized content to the clipboard. A
+            sanitized write is done with the \c{write} and \c{writeText} JavaScript Clipboard API
+            calls and must be accompanied by user action.
+            Unsanitized writes, and reading from the clipboard, are
+            enabled by \l{JavascriptCanPaste}.
+            Prior to Chromium version 81, this setting enabled all clipboard writes.
+            Since unrestricted clipboard access is a potential security concern, it is
             recommended that applications leave this disabled and instead respond to
             \l{QWebEnginePage::ClipboardReadWrite}{ClipboardReadWrite} feature permission requests.
             Disabled by default.
@@ -105,7 +108,6 @@
             Enables displaying the built-in error pages of Chromium. Enabled by default.
     \value  PluginsEnabled
             Enables support for Pepper plugins, such as the Flash player. Disabled by default.
-            See also \l{Pepper Plugin API}. (Added in Qt 5.6)
     \value  FullScreenSupportEnabled
             Enables fullscreen support in an application. Disabled by default. (Added in Qt 5.6)
     \value  ScreenCaptureEnabled
@@ -153,11 +155,15 @@
             similar to Chrome on desktops. To overwrite the default behavior,
             disable this setting. (Added in Qt 5.11)
     \value  JavascriptCanPaste
-            Enables JavaScript \c{execCommand("paste")}. This also requires enabling
-            JavascriptCanAccessClipboard. Since unrestricted clipboard access is a potential
-            security concern, it is recommended that applications leave this disabled
-            and instead respond to \l{QWebEnginePage::ClipboardReadWrite}{ClipboardReadWrite}
-            feature permission requests.
+            Allows JavaScript programs to read (paste) from the clipboard and to write unsanitized
+            content. A sanitized write is done with the \c{write} and \c{writeText} JavaScript
+            Clipboard API calls and must be accompanied by user action; unsanitized writes are any
+            writes which do not meet these criteria.
+            For this setting to have any effect, \l{JavascriptCanAccessClipboard} must also be
+            enabled.
+            Since unrestricted clipboard access is a potential security concern, it is
+            recommended that applications leave this disabled and instead respond to
+            \l{QWebEnginePage::ClipboardReadWrite}{ClipboardReadWrite} feature permission requests.
             Disabled by default. (Added in Qt 5.11)
     \value  WebRTCPublicInterfacesOnly
             Limits WebRTC to public IP addresses only. When disabled WebRTC may also use
@@ -208,13 +214,13 @@
     This enum describes how an image animation should be handled when the image frames
     are rendered for animation.
 
-    \value AllowImageAnimation
+    \value Allow
            Allows image animation when the image frames are rendered.
-    \value AnimateImageOnce
+    \value AnimateOnce
            Animate the image once when the image frames are rendered.
-    \value DisallowImageAnimation
+    \value Disallow
            Disallows image animation when the image frames are rendered.
-    \omitvalue InheritedImageAnimationPolicy
+    \omitvalue Inherited
 
     \sa imageAnimationPolicy setImageAnimationPolicy resetImageAnimationPolicy
 */
@@ -312,7 +318,7 @@
     \fn QWebEngineSettings::ImageAnimationPolicy QWebEngineSettings::imageAnimationPolicy() const
     \since Qt 6.8
     Returns the currently selected policy for handling image animation when the image frames are rendered.
-    Default is \l{QWebEngineSettings::AllowImageAnimation}.
+    Default is \l{QWebEngineSettings::ImageAnimationPolicy::Allow}.
     \sa setImageAnimationPolicy resetImageAnimationPolicy
 */
 
@@ -320,13 +326,13 @@
     \fn void QWebEngineSettings::setImageAnimationPolicy(QWebEngineSettings::ImageAnimationPolicy policy)
     \since Qt 6.8
     Sets the policy for handling image animation when the image frames are rendered to \a policy.
-    Default is \l{QWebEngineSettings::AllowImageAnimation}.
+    Default is \l{QWebEngineSettings::ImageAnimationPolicy::Allow}.
     \sa imageAnimationPolicy resetImageAnimationPolicy
 */
 
 /*!
     \fn void QWebEngineSettings::resetImageAnimationPolicy()
-    \since Qt 6.7
+    \since Qt 6.8
     Removes the policy for handling image animation.
     \sa imageAnimationPolicy setImageAnimationPolicy
 */
diff --git a/src/core/download_manager_delegate_qt.cpp b/src/core/download_manager_delegate_qt.cpp
index c0fd0d3eeed..0d4983fb6b8 100644
--- a/src/core/download_manager_delegate_qt.cpp
+++ b/src/core/download_manager_delegate_qt.cpp
@@ -14,6 +14,7 @@
 #include <QFileInfo>
 #include <QMimeDatabase>
 #include <QStandardPaths>
+#include <QTimer>
 
 #include "profile_adapter_client.h"
 #include "profile_adapter.h"
@@ -22,8 +23,24 @@
 #include "type_conversion.h"
 #include "web_contents_delegate_qt.h"
 
+using namespace Qt::StringLiterals;
+
 namespace QtWebEngineCore {
 
+void provideDownloadTarget(download::DownloadItem *item, download::DownloadTargetCallback *callback,
+                           const base::FilePath &target)
+{
+    download::DownloadTargetInfo target_info;
+    target_info.target_disposition = download::DownloadItem::TARGET_DISPOSITION_OVERWRITE;
+    target_info.danger_type = download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT;
+    target_info.insecure_download_status = download::DownloadItem::VALIDATED;
+    target_info.mime_type = item->GetMimeType();
+    target_info.display_name = item->GetFileNameToReportUser();
+    target_info.target_path = target;
+    target_info.intermediate_path = target.AddExtensionASCII("download");
+    std::move(*callback).Run(std::move(target_info));
+}
+
 DownloadManagerDelegateQt::DownloadManagerDelegateQt(ProfileAdapter *profileAdapter)
     : m_profileAdapter(profileAdapter)
     , m_currentId(0)
@@ -47,20 +64,19 @@ download::DownloadItem *DownloadManagerDelegateQt::findDownloadById(quint32 down
     return dlm->GetDownload(downloadId);
 }
 
-void DownloadManagerDelegateQt::cancelDownload(content::DownloadTargetCallback callback)
+void DownloadManagerDelegateQt::cancelDownload(download::DownloadTargetCallback callback)
 {
-    std::move(callback).Run(base::FilePath(),
-                            download::DownloadItem::TARGET_DISPOSITION_PROMPT,
-                            download::DownloadDangerType::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
-                            download::DownloadItem::UNKNOWN,
-                            base::FilePath(),
-                            base::FilePath(),
-                            std::string(),
-                            download::DownloadInterruptReason::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED);
+    download::DownloadTargetInfo target_info;
+    target_info.target_disposition = download::DownloadItem::TARGET_DISPOSITION_PROMPT;
+    target_info.danger_type = download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT;
+    target_info.interrupt_reason = download::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED;
+    std::move(callback).Run(std::move(target_info));
 }
 
 bool DownloadManagerDelegateQt::cancelDownload(quint32 downloadId)
 {
+    m_pendingDownloads.erase(downloadId);
+    m_pendingSaves.erase(downloadId);
     if (download::DownloadItem *download = findDownloadById(downloadId)) {
         download->Cancel(/* user_cancel */ true);
         return true;
@@ -84,24 +100,36 @@ void DownloadManagerDelegateQt::removeDownload(quint32 downloadId)
 {
     if (download::DownloadItem *download = findDownloadById(downloadId))
         download->Remove();
+    m_pendingDownloads.erase(downloadId);
+    m_pendingSaves.erase(downloadId);
 }
 
 bool DownloadManagerDelegateQt::DetermineDownloadTarget(download::DownloadItem *item,
-                                                        content::DownloadTargetCallback *callback)
+                                                        download::DownloadTargetCallback *callback)
 {
+    // The item came back for another round of target determination; this happens for example when
+    // network error occurs. We already gave it a target path, let it use that, then it can report
+    // the reason of its failure in OnDownloadUpdated().
+    if (m_currentId >= item->GetId() && !item->GetTargetFilePath().empty()) {
+        provideDownloadTarget(item, callback, item->GetTargetFilePath());
+        return true;
+    }
+
     m_currentId = item->GetId();
 
     // Keep the forced file path if set, also as the temporary file, so the check for existence
     // will already return that the file exists. Forced file paths seem to be only used for
     // store downloads and other special downloads, so they might never end up here anyway.
     if (!item->GetForcedFilePath().empty()) {
-        std::move(*callback).Run(item->GetForcedFilePath(), download::DownloadItem::TARGET_DISPOSITION_PROMPT,
-                                 download::DownloadDangerType::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-                                 download::DownloadItem::VALIDATED,
-                                 item->GetForcedFilePath(),
-                                 item->GetFileNameToReportUser(),
-                                 item->GetMimeType(),
-                                 download::DownloadInterruptReason::DOWNLOAD_INTERRUPT_REASON_NONE);
+        download::DownloadTargetInfo target_info;
+        target_info.target_disposition = download::DownloadItem::TARGET_DISPOSITION_PROMPT;
+        target_info.danger_type = download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT;
+        target_info.insecure_download_status = download::DownloadItem::VALIDATED;
+        target_info.mime_type = item->GetMimeType();
+        target_info.display_name = item->GetFileNameToReportUser();
+        target_info.target_path = item->GetForcedFilePath();
+        target_info.intermediate_path = item->GetForcedFilePath();
+        std::move(*callback).Run(std::move(target_info));
         return true;
     }
 
@@ -150,10 +178,10 @@ bool DownloadManagerDelegateQt::DetermineDownloadTarget(download::DownloadItem *
     }
 
     if (suggestedFilename.isEmpty()) {
-        suggestedFilename = QStringLiteral("qwe_download");
+        suggestedFilename += "qwe_download"_L1;
         QMimeType mimeType = QMimeDatabase().mimeTypeForName(mimeTypeString);
         if (mimeType.isValid() && !mimeType.preferredSuffix().isEmpty())
-            suggestedFilename += QStringLiteral(".") + mimeType.preferredSuffix();
+            suggestedFilename += u'.' + mimeType.preferredSuffix();
     }
 
     QDir defaultDownloadDirectory(m_profileAdapter->downloadPath());
@@ -164,67 +192,69 @@ bool DownloadManagerDelegateQt::DetermineDownloadTarget(download::DownloadItem *
     item->AddObserver(this);
     QList<ProfileAdapterClient*> clients = m_profileAdapter->clients();
     if (!clients.isEmpty()) {
-        Q_ASSERT(m_currentId == item->GetId());
-        ProfileAdapterClient::DownloadItemInfo info = {
-            item->GetId(),
-            toQt(item->GetURL()),
-            item->GetState(),
-            item->GetTotalBytes(),
-            item->GetReceivedBytes(),
-            mimeTypeString,
-            suggestedFilePath,
-            ProfileAdapterClient::UnknownSavePageFormat,
-            acceptedByDefault,
-            false /* paused */,
-            false /* done */,
-            isSavePageDownload,
-            item->GetLastReason(),
-            adapterClient,
-            suggestedFilename,
-            item->GetStartTime().ToTimeT()
-        };
-
-        for (ProfileAdapterClient *client : std::as_const(clients)) {
-            client->downloadRequested(info);
-            if (info.accepted)
-                break;
-        }
-
-        QFileInfo suggestedFile(info.path);
-
-        if (info.accepted && !suggestedFile.absoluteDir().mkpath(suggestedFile.absolutePath())) {
-#if defined(Q_OS_WIN)
-            // TODO: Remove this when https://bugreports.qt.io/browse/QTBUG-85997 is fixed.
-            QDir suggestedDir = QDir(suggestedFile.absolutePath());
-            if (!suggestedDir.isRoot() || !suggestedDir.exists()) {
-#endif
-            qWarning("Creating download path failed, download cancelled: %s", suggestedFile.absolutePath().toUtf8().data());
-            info.accepted = false;
-#if defined(Q_OS_WIN)
-            }
-#endif
-        }
-
-        if (!info.accepted) {
-            cancelDownload(std::move(*callback));
-            return true;
-        }
-
-        base::FilePath filePathForCallback(toFilePathString(suggestedFile.absoluteFilePath()));
-        std::move(*callback).Run(filePathForCallback,
-                                 download::DownloadItem::TARGET_DISPOSITION_OVERWRITE,
-                                 download::DownloadDangerType::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
-                                 download::DownloadItem::VALIDATED,
-                                 filePathForCallback.AddExtension(toFilePathString("download")),
-                                 base::FilePath(),
-                                 item->GetMimeType(),
-                                 download::DownloadInterruptReason::DOWNLOAD_INTERRUPT_REASON_NONE);
+        ProfileAdapterClient::DownloadItemInfo info = {};
+        info.id = item->GetId();
+        info.url = toQt(item->GetURL());
+        info.state = item->GetState();
+        info.totalBytes = item->GetTotalBytes();
+        info.receivedBytes = item->GetReceivedBytes();
+        info.mimeType = std::move(mimeTypeString);
+        info.path = std::move(suggestedFilePath);
+        info.savePageFormat = ProfileAdapterClient::UnknownSavePageFormat;
+        info.accepted = acceptedByDefault;
+        info.paused = false;
+        info.done = false;
+        info.isSavePageDownload = isSavePageDownload;
+        info.useDownloadTargetCallback = true;
+        info.downloadInterruptReason = item->GetLastReason();
+        info.page = adapterClient;
+        info.suggestedFileName = std::move(suggestedFilename);
+        info.startTime = item->GetStartTime().ToTimeT();
+
+        m_pendingDownloads.emplace(m_currentId, std::move(*callback));
+        QTimer::singleShot(0, m_profileAdapter,
+                           [client = clients[0], info]() { client->downloadRequested(info); });
     } else
         cancelDownload(std::move(*callback));
 
     return true;
 }
 
+void DownloadManagerDelegateQt::downloadTargetDetermined(quint32 downloadId, bool accepted,
+                                                         const QString &path)
+{
+    if (!m_pendingDownloads.contains(downloadId))
+        return;
+    auto callback = std::move(m_pendingDownloads.find(downloadId)->second);
+    m_pendingDownloads.erase(downloadId);
+
+    download::DownloadItem *item = findDownloadById(downloadId);
+    if (!accepted || !item) {
+        cancelDownload(std::move(callback));
+        return;
+    }
+
+    QFileInfo suggestedFile(path);
+    if (!suggestedFile.absoluteDir().mkpath(suggestedFile.absolutePath())) {
+        qWarning() << "Creating download path failed, download cancelled:" << suggestedFile.absolutePath();
+        cancelDownload(std::move(callback));
+        return;
+    }
+    base::FilePath targetPath(toFilePathString(suggestedFile.absoluteFilePath()));
+
+    download::DownloadTargetInfo target_info;
+    target_info.target_disposition = download::DownloadItem::TARGET_DISPOSITION_OVERWRITE;
+    target_info.danger_type = download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT;
+    target_info.insecure_download_status = download::DownloadItem::VALIDATED;
+    target_info.mime_type = item->GetMimeType();
+    target_info.intermediate_path =
+            targetPath.AddExtension(toFilePathString("download"));
+    target_info.display_name = base::FilePath();
+    target_info.target_path = targetPath;
+    target_info.interrupt_reason = download::DOWNLOAD_INTERRUPT_REASON_NONE;
+    std::move(callback).Run(std::move(target_info));
+}
+
 void DownloadManagerDelegateQt::GetSaveDir(content::BrowserContext* browser_context,
                                            base::FilePath* website_save_dir,
                                            base::FilePath* download_save_dir)
@@ -260,8 +290,8 @@ void DownloadManagerDelegateQt::ChooseSavePath(content::WebContents *web_content
     }
 
     if (suggestedFilePath.isEmpty()) {
-        suggestedFilePath = QFileInfo(toQt(suggested_path.AsUTF8Unsafe())).completeBaseName()
-                + QStringLiteral(".mhtml");
+        suggestedFilePath +=
+                QFileInfo(toQt(suggested_path.AsUTF8Unsafe())).completeBaseName() + ".mhtml"_L1;
     } else {
         acceptedByDefault = true;
     }
@@ -277,36 +307,48 @@ void DownloadManagerDelegateQt::ChooseSavePath(content::WebContents *web_content
     if (web_contents)
         adapterClient = static_cast<WebContentsDelegateQt *>(web_contents->GetDelegate())->adapterClient();
 
+    ProfileAdapterClient::DownloadItemInfo info = {};
     // Chromium doesn't increase download ID when saving page.
-    ProfileAdapterClient::DownloadItemInfo info = {
-        ++m_currentId,
-        toQt(web_contents->GetURL()),
-        download::DownloadItem::IN_PROGRESS,
-        -1, /* totalBytes */
-        0, /* receivedBytes */
-        QStringLiteral("application/x-mimearchive"),
-        suggestedFilePath,
-        suggestedSaveFormat,
-        acceptedByDefault,
-        false, /* paused */
-        false, /* done */
-        true, /* isSavePageDownload */
-        ProfileAdapterClient::NoReason,
-        adapterClient,
-        QFileInfo(suggestedFilePath).fileName(),
-        QDateTime::currentMSecsSinceEpoch()
-    };
-
-    for (ProfileAdapterClient *client : std::as_const(clients)) {
-        client->downloadRequested(info);
-        if (info.accepted)
-            break;
+    info.id = ++m_currentId;
+    info.url = toQt(web_contents->GetURL());
+    info.state = download::DownloadItem::IN_PROGRESS;
+    info.totalBytes = -1;
+    info.receivedBytes = 0;
+    info.mimeType = u"application/x-mimearchive"_s;
+    info.path = suggestedFilePath;
+    info.savePageFormat = suggestedSaveFormat;
+    info.accepted = acceptedByDefault;
+    info.paused = false;
+    info.done = false;
+    info.isSavePageDownload = true;
+    info.useDownloadTargetCallback = false;
+    info.downloadInterruptReason = ProfileAdapterClient::NoReason;
+    info.page = adapterClient;
+    info.suggestedFileName = QFileInfo(suggestedFilePath).fileName();
+    info.startTime = QDateTime::currentMSecsSinceEpoch();
+
+    m_pendingSaves.emplace(m_currentId, std::move(callback));
+    QTimer::singleShot(0, m_profileAdapter,
+                       [client = clients[0], info]() { client->downloadRequested(info); });
+}
+
+void DownloadManagerDelegateQt::savePathDetermined(quint32 downloadId, bool accepted,
+                                                   const QString &path, int format)
+{
+    if (!accepted) {
+        m_pendingSaves.erase(downloadId);
+        return;
     }
 
-    if (!info.accepted)
+    if (!m_pendingSaves.contains(downloadId))
         return;
+    auto callback = std::move(m_pendingSaves.find(downloadId)->second);
+    m_pendingSaves.erase(downloadId);
 
-    std::move(callback).Run(toFilePath(info.path), static_cast<content::SavePageType>(info.savePageFormat),
+    content::SavePackagePathPickedParams params;
+    params.file_path = toFilePath(path);
+    params.save_type = static_cast<content::SavePageType>(format);
+    std::move(callback).Run(std::move(params),
                             base::BindOnce(&DownloadManagerDelegateQt::savePackageDownloadCreated,
                                            m_weakPtrFactory.GetWeakPtr()));
 }
@@ -326,28 +368,28 @@ void DownloadManagerDelegateQt::OnDownloadUpdated(download::DownloadItem *downlo
         if (webContents)
             adapterClient = static_cast<WebContentsDelegateQt *>(webContents->GetDelegate())->adapterClient();
 
-        ProfileAdapterClient::DownloadItemInfo info = {
-            download->GetId(),
-            toQt(download->GetURL()),
-            download->GetState(),
-            download->GetTotalBytes(),
-            download->GetReceivedBytes(),
-            toQt(download->GetMimeType()),
-            QString(),
-            ProfileAdapterClient::UnknownSavePageFormat,
-            true /* accepted */,
-            download->IsPaused(),
-            download->IsDone(),
-            0 /* downloadType (unused) */,
-            download->GetLastReason(),
-            adapterClient,
-            toQt(download->GetSuggestedFilename()),
-            download->GetStartTime().ToTimeT()
-        };
-
-        for (ProfileAdapterClient *client : std::as_const(clients)) {
-            client->downloadUpdated(info);
-        }
+        ProfileAdapterClient::DownloadItemInfo info = {};
+        // Chromium doesn't increase download ID when saving page.
+        info.id = download->GetId();
+        info.url = toQt(download->GetURL());
+        info.state = download->GetState();
+        info.totalBytes = download->GetTotalBytes();
+        info.receivedBytes = download->GetReceivedBytes();
+        info.mimeType = toQt(download->GetMimeType());
+        info.path = QString();
+        info.savePageFormat = ProfileAdapterClient::UnknownSavePageFormat;
+        info.accepted = true;
+        info.paused = download->IsPaused();
+        info.done = download->IsDone();
+        info.isSavePageDownload = false; // unused
+        info.useDownloadTargetCallback = false; // unused
+        info.downloadInterruptReason = download->GetLastReason();
+        info.page = adapterClient;
+        info.suggestedFileName = toQt(download->GetSuggestedFilename());
+        info.startTime = download->GetStartTime().ToTimeT();
+
+        QTimer::singleShot(0, m_profileAdapter,
+                           [client = clients[0], info]() { client->downloadUpdated(info); });
     }
 }
 
diff --git a/src/core/download_manager_delegate_qt.h b/src/core/download_manager_delegate_qt.h
index cc6d49764f6..3b7ccc1c4a1 100644
--- a/src/core/download_manager_delegate_qt.h
+++ b/src/core/download_manager_delegate_qt.h
@@ -7,7 +7,11 @@
 #include "content/public/browser/download_manager_delegate.h"
 #include <base/memory/weak_ptr.h>
 
+#include <QString>
 #include <QtGlobal>
+#include <map>
+
+#include "profile_adapter_client.h"
 
 namespace base {
 class FilePath;
@@ -35,7 +39,7 @@ class DownloadManagerDelegateQt
     void GetNextId(content::DownloadIdCallback callback) override;
 
     bool DetermineDownloadTarget(download::DownloadItem *item,
-                                 content::DownloadTargetCallback *callback) override;
+                                 download::DownloadTargetCallback *callback) override;
 
     void GetSaveDir(content::BrowserContext* browser_context,
                     base::FilePath* website_save_dir,
@@ -51,17 +55,22 @@ class DownloadManagerDelegateQt
     void resumeDownload(quint32 downloadId);
     void removeDownload(quint32 downloadId);
 
+    void downloadTargetDetermined(quint32 downloadId, bool accepted, const QString &path);
+    void savePathDetermined(quint32 downloadId, bool accepted, const QString &path, int format);
+
     // Inherited from content::DownloadItem::Observer
     void OnDownloadUpdated(download::DownloadItem *download) override;
     void OnDownloadDestroyed(download::DownloadItem *download) override;
 
 private:
-    void cancelDownload(content::DownloadTargetCallback callback);
+    void cancelDownload(download::DownloadTargetCallback callback);
     download::DownloadItem *findDownloadById(quint32 downloadId);
     void savePackageDownloadCreated(download::DownloadItem *download);
     ProfileAdapter *m_profileAdapter;
 
     uint32_t m_currentId;
+    std::map<quint32, download::DownloadTargetCallback> m_pendingDownloads;
+    std::map<quint32, content::SavePackagePathPickedCallback> m_pendingSaves;
     base::WeakPtrFactory<DownloadManagerDelegateQt> m_weakPtrFactory;
 };
 
diff --git a/src/core/extensions/extension_host_delegate_qt.cpp b/src/core/extensions/extension_host_delegate_qt.cpp
index aa408a544d9..16133a598f8 100644
--- a/src/core/extensions/extension_host_delegate_qt.cpp
+++ b/src/core/extensions/extension_host_delegate_qt.cpp
@@ -86,10 +86,9 @@ void ExtensionHostDelegateQt::ProcessMediaAccessRequest(content::WebContents *we
     });
 }
 
-bool ExtensionHostDelegateQt::CheckMediaAccessPermission(content::RenderFrameHost *render_frame_host,
-                                                         const GURL &security_origin,
-                                                         blink::mojom::MediaStreamType type,
-                                                         const Extension *extension)
+bool ExtensionHostDelegateQt::CheckMediaAccessPermission(
+        content::RenderFrameHost *render_frame_host, const url::Origin &security_origin,
+        blink::mojom::MediaStreamType type, const Extension *extension)
 {
     Q_UNUSED(render_frame_host);
     Q_UNUSED(security_origin);
diff --git a/src/core/extensions/extension_host_delegate_qt.h b/src/core/extensions/extension_host_delegate_qt.h
index 1c2688933ea..38b5d4a2fd1 100644
--- a/src/core/extensions/extension_host_delegate_qt.h
+++ b/src/core/extensions/extension_host_delegate_qt.h
@@ -27,7 +27,7 @@ class ExtensionHostDelegateQt : public ExtensionHostDelegate
                                    content::MediaResponseCallback callback,
                                    const Extension *extension) override;
     bool CheckMediaAccessPermission(content::RenderFrameHost *render_frame_host,
-                                    const GURL &security_origin,
+                                    const url::Origin &security_origin,
                                     blink::mojom::MediaStreamType type,
                                     const Extension *extension) override;
     content::PictureInPictureResult EnterPictureInPicture(content::WebContents *web_contents) override;
diff --git a/src/core/extensions/extensions_browser_client_qt.cpp b/src/core/extensions/extensions_browser_client_qt.cpp
index 19fc6c808b5..36151cee562 100644
--- a/src/core/extensions/extensions_browser_client_qt.cpp
+++ b/src/core/extensions/extensions_browser_client_qt.cpp
@@ -542,5 +542,10 @@ media_device_salt::MediaDeviceSaltService *ExtensionsBrowserClientQt::GetMediaDe
     // Not needed for QWE
     return nullptr;
 }
-
+mojo::PendingRemote<network::mojom::URLLoaderFactory>
+ExtensionsBrowserClientQt::GetControlledFrameEmbedderURLLoader(
+        int frame_tree_node_id, content::BrowserContext *browser_context)
+{
+    return mojo::PendingRemote<network::mojom::URLLoaderFactory>();
+}
 } // namespace extensions
diff --git a/src/core/extensions/extensions_browser_client_qt.h b/src/core/extensions/extensions_browser_client_qt.h
index bcc8f142b26..34a84631106 100644
--- a/src/core/extensions/extensions_browser_client_qt.h
+++ b/src/core/extensions/extensions_browser_client_qt.h
@@ -108,6 +108,10 @@ class ExtensionsBrowserClientQt : public ExtensionsBrowserClient
 
     media_device_salt::MediaDeviceSaltService *GetMediaDeviceSaltService(content::BrowserContext *context) override;
 
+    mojo::PendingRemote<network::mojom::URLLoaderFactory>
+    GetControlledFrameEmbedderURLLoader(int frame_tree_node_id,
+                                        content::BrowserContext *browser_context) override;
+
 private:
     // Support for extension APIs.
     std::unique_ptr<ExtensionsAPIClient> api_client_;
diff --git a/src/core/extensions/file_system_delegate_qt.cpp b/src/core/extensions/file_system_delegate_qt.cpp
index 7c1c5bbd843..6c5b3391913 100644
--- a/src/core/extensions/file_system_delegate_qt.cpp
+++ b/src/core/extensions/file_system_delegate_qt.cpp
@@ -41,36 +41,21 @@ FileEntryPickerQt::FileEntryPickerQt(
 
 FileEntryPickerQt::~FileEntryPickerQt() = default;
 
-void FileEntryPickerQt::FileSelected(const base::FilePath &path,
-        int index,
-        void *params)
+void FileEntryPickerQt::FileSelected(const ui::SelectedFileInfo &file, int index, void *params)
 {
-    MultiFilesSelected({path}, params);
+    MultiFilesSelected({ file }, params);
 }
 
-void FileEntryPickerQt::FileSelectedWithExtraInfo(const ui::SelectedFileInfo& file,
-        int index,
-        void *params)
-{
-    FileSelected(file.file_path, index, params);
-}
-
-void FileEntryPickerQt::MultiFilesSelected(const std::vector<base::FilePath>& files,
-                      void* params)
+void FileEntryPickerQt::MultiFilesSelected(const std::vector<ui::SelectedFileInfo> &files,
+                                           void *params)
 {
     Q_UNUSED(params);
-    std::move(m_filesSelectedCallback).Run(files);
-    delete this;
-}
-
-void FileEntryPickerQt::MultiFilesSelectedWithExtraInfo(
-        const std::vector<ui::SelectedFileInfo> &files,
-        void *params)
-{
     std::vector<base::FilePath> paths;
-    for (const auto& file : files)
+    for (const auto &file : files) {
         paths.push_back(file.file_path);
-    MultiFilesSelected(paths, params);
+    }
+    std::move(m_filesSelectedCallback).Run(paths);
+    delete this;
 }
 
 void FileEntryPickerQt::FileSelectionCanceled(void *params)
diff --git a/src/core/extensions/file_system_delegate_qt.h b/src/core/extensions/file_system_delegate_qt.h
index 1e9d87c38fa..01b56fa569d 100644
--- a/src/core/extensions/file_system_delegate_qt.h
+++ b/src/core/extensions/file_system_delegate_qt.h
@@ -19,6 +19,10 @@ namespace content {
 class BrowserContext;
 }  // namespace content
 
+namespace ui {
+struct SelectedFileInfo;
+}
+
 namespace extensions {
 
 class FileEntryPickerQt : public ui::SelectFileDialog::Listener {
@@ -38,17 +42,8 @@ class FileEntryPickerQt : public ui::SelectFileDialog::Listener {
     ~FileEntryPickerQt() override;
 
     // ui::SelectFileDialog::Listener implementation.
-    void FileSelected(const base::FilePath &path,
-        int index,
-        void *params) override;
-    void FileSelectedWithExtraInfo(const ui::SelectedFileInfo &file,
-        int index,
-        void *params) override;
-    void MultiFilesSelected(const std::vector<base::FilePath> &files,
-                          void *params) override;
-    void MultiFilesSelectedWithExtraInfo(
-        const std::vector<ui::SelectedFileInfo> &files,
-        void *params) override;
+    void FileSelected(const ui::SelectedFileInfo &file, int index, void *params) override;
+    void MultiFilesSelected(const std::vector<ui::SelectedFileInfo> &files, void *params) override;
     void FileSelectionCanceled(void *params) override;
 
     FileSystemDelegate::FilesSelectedCallback m_filesSelectedCallback;
diff --git a/src/core/favicon_service_factory_qt.cpp b/src/core/favicon_service_factory_qt.cpp
index dd2a1979acb..1ed33d559e2 100644
--- a/src/core/favicon_service_factory_qt.cpp
+++ b/src/core/favicon_service_factory_qt.cpp
@@ -46,7 +46,7 @@ std::unique_ptr<history::HistoryBackendClient> HistoryClientQt::CreateBackendCli
     return nullptr;
 }
 
-void HistoryClientQt::UpdateBookmarkLastUsedTime(const base::Uuid &, base::Time /*time*/)
+void HistoryClientQt::UpdateBookmarkLastUsedTime(int64_t /*bookmark_node_id*/, base::Time /*time*/)
 {
 }
 
diff --git a/src/core/favicon_service_factory_qt.h b/src/core/favicon_service_factory_qt.h
index 55d5f3b3306..0c3bfd7b56a 100644
--- a/src/core/favicon_service_factory_qt.h
+++ b/src/core/favicon_service_factory_qt.h
@@ -51,7 +51,7 @@ class HistoryClientQt : public history::HistoryClient
     history::CanAddURLCallback GetThreadSafeCanAddURLCallback() const override;
     void NotifyProfileError(sql::InitStatus init_status, const std::string &diagnostics) override;
     std::unique_ptr<history::HistoryBackendClient> CreateBackendClient() override;
-    void UpdateBookmarkLastUsedTime(const base::Uuid &, base::Time) override;
+    void UpdateBookmarkLastUsedTime(int64_t bookmark_node_id, base::Time time) override;
 };
 
 class HistoryServiceFactoryQt : public BrowserContextKeyedServiceFactory
diff --git a/src/core/file_picker_controller.cpp b/src/core/file_picker_controller.cpp
index 9b452135865..3aeb0f5da5f 100644
--- a/src/core/file_picker_controller.cpp
+++ b/src/core/file_picker_controller.cpp
@@ -9,6 +9,7 @@
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/file_select_listener.h"
 #include "ui/shell_dialogs/select_file_dialog.h"
+#include "ui/shell_dialogs/selected_file_info.h"
 
 #include <QtCore/qcoreapplication.h>
 #include <QDir>
@@ -17,6 +18,8 @@
 #include <QStringList>
 #include <QVariant>
 
+using namespace Qt::StringLiterals;
+
 namespace QtWebEngineCore {
 
 class FilePickerControllerPrivate {
@@ -74,7 +77,7 @@ void FilePickerController::accepted(const QStringList &files)
             continue;
         }
 
-        if (urlString.startsWith("file:")) {
+        if (urlString.startsWith("file:"_L1)) {
             base::FilePath filePath = toFilePath(urlString).NormalizePathSeparators();
             std::vector<base::FilePath::StringType> pathComponents;
             // Splits the file URL into scheme, host name, path and file name.
@@ -82,18 +85,20 @@ void FilePickerController::accepted(const QStringList &files)
 
             QString absolutePath;
 #if !defined(Q_OS_WIN)
-            absolutePath = "/";
+            absolutePath += u'/';
 #endif
 
             QString scheme = toQt(pathComponents[0]);
             if (scheme.size() > 5) {
 #if defined(Q_OS_WIN)
                 // There is no slash at the end of the file scheme and it is valid on Windows: file:C:/
-                if (scheme.size() == 7 && scheme.at(5).isLetter() && scheme.at(6) == ':') {
-                    absolutePath += scheme.at(5) + ":/";
+                if (scheme.size() == 7 && scheme.at(5).isLetter() && scheme.at(6) == u':') {
+                    absolutePath += scheme.at(5) + ":/"_L1;
                 } else {
 #endif
-                    qWarning("Ignoring invalid item in FilePickerController::accepted(QStringList): %s", qPrintable(urlString));
+                    qWarning("Ignoring invalid item in "
+                             "FilePickerController::accepted(QStringList): %ls",
+                             qUtf16Printable(urlString));
                     continue;
 #if defined(Q_OS_WIN)
                 }
@@ -105,28 +110,34 @@ void FilePickerController::accepted(const QStringList &files)
                 && base::FilePath::IsSeparator(urlString.at(6).toLatin1())
                 && !base::FilePath::IsSeparator(urlString.at(7).toLatin1())) {
 #if defined(Q_OS_WIN)
-                if (urlString.at(8) != ':' && pathComponents.size() > 2) {
-                    absolutePath += "//";
+                if (urlString.at(8) != u':' && pathComponents.size() > 2) {
+                    absolutePath += "//"_L1;
 #else
                 if (pathComponents.size() > 2) {
-                    absolutePath += "/";
+                    absolutePath += u'/';
 #endif
                 } else {
-                    qWarning("Ignoring invalid item in FilePickerController::accepted(QStringList): %s", qPrintable(urlString));
+                    qWarning("Ignoring invalid item in "
+                             "FilePickerController::accepted(QStringList): %ls",
+                             qUtf16Printable(urlString));
                     continue;
                 }
             }
 
-            // Build absolute path from file URI componenets.
-            for (size_t j = 1; j < pathComponents.size(); j++)
-                absolutePath += toQt(pathComponents[j]) + (j != pathComponents.size()-1 ? "/" : "");
+            // Build absolute path from file URI components.
+            for (size_t j = 1; j < pathComponents.size(); j++) {
+                absolutePath += toQt(pathComponents[j]);
+                if (j != pathComponents.size() - 1)
+                    absolutePath += u'/';
+            }
 
             if (toFilePath(absolutePath).IsAbsolute()) {
                 stringList.append(absolutePath);
                 continue;
             }
         }
-        qWarning("Ignoring invalid item in FilePickerController::accepted(QStringList): %s", qPrintable(urlString));
+        qWarning("Ignoring invalid item in FilePickerController::accepted(QStringList): %ls",
+                 qUtf16Printable(urlString));
     }
 
     FilePickerController::filesSelectedInChooser(stringList);
@@ -160,7 +171,7 @@ static QStringList listRecursively(const QDir &dir)
     const QFileInfoList infoList(dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden));
     for (const QFileInfo &fileInfo : infoList) {
         if (fileInfo.isDir()) {
-            ret.append(fileInfo.absolutePath() + QStringLiteral("/.")); // Match chromium's behavior. See chrome/browser/file_select_helper.cc
+            ret.append(fileInfo.absolutePath() + "/."_L1); // Match chromium's behavior. See chrome/browser/file_select_helper.cc
             ret.append(listRecursively(QDir(fileInfo.absoluteFilePath())));
         } else
             ret.append(fileInfo.absoluteFilePath());
@@ -213,8 +224,12 @@ void FilePickerController::filesSelectedInChooser(const QStringList &filesList)
 
         if (files.empty())
             d_ptr->fileSystemAccessDialogListener->FileSelectionCanceled(nullptr);
+        else if (files.size() == 1)
+            d_ptr->fileSystemAccessDialogListener->FileSelected(
+                    ui::SelectedFileInfo(files[0]), 0, nullptr);
         else
-            d_ptr->fileSystemAccessDialogListener->MultiFilesSelected(files, nullptr);
+            d_ptr->fileSystemAccessDialogListener->MultiFilesSelected(
+                    ui::FilePathListToSelectedFileInfoList(files), nullptr);
     }
 }
 
@@ -243,34 +258,34 @@ QStringList FilePickerController::nameFilters(const QStringList &acceptedMimeTyp
         return nameFilters;
 
     for (QString type : acceptedMimeTypes) {
-        if (type.startsWith(".")) {
+        if (type.startsWith(u'.')) {
             // A single suffix
             // Filename.type doesn't have to exist and mimeTypeForFile() supports
             // custom suffixes as valid (but unknown) MIME types.
-            const QMimeType &mimeType = mimeDatabase.mimeTypeForFile("filename" + type);
+            const QMimeType &mimeType = mimeDatabase.mimeTypeForFile("filename"_L1 + type);
             if (mimeType.isValid()) {
-                QString glob = "*" + type;
-                acceptedGlobs.append(glob);
-                nameFilters.append(mimeType.comment() + " (" + glob + ")");
+                QString glob = u'*' + type;
+                nameFilters.append(mimeType.comment() + " ("_L1 + glob + u')');
+                acceptedGlobs.append(std::move(glob));
             }
-        } else if (type.contains("/") && !type.endsWith("*")) {
+        } else if (type.contains(u'/') && !type.endsWith(u'*')) {
             // All suffixes for a given MIME type
             const QMimeType &mimeType = mimeDatabase.mimeTypeForName(type);
             if (mimeType.isValid() && !mimeType.globPatterns().isEmpty()) {
-                QString globs = mimeType.globPatterns().join(" ");
+                QString globs = mimeType.globPatterns().join(u' ');
+                nameFilters.append(mimeType.comment() + " ("_L1 + globs + u')');
                 acceptedGlobs.append(mimeType.globPatterns());
-                nameFilters.append(mimeType.comment() + " (" + globs + ")");
             }
-        } else if (type.endsWith("/*")) {
+        } else if (type.endsWith("/*"_L1)) {
             // All MIME types for audio/*, image/* or video/*
             // as separate filters as Chrome does
             static const QList<QMimeType> &allMimeTypes = mimeDatabase.allMimeTypes();
-            type = type.remove("/*");
+            type.chop(2);
             for (const QMimeType &m : allMimeTypes) {
                 if (m.name().startsWith(type) && !m.globPatterns().isEmpty()) {
-                    QString globs = m.globPatterns().join(" ");
+                    QString globs = m.globPatterns().join(u' ');
+                    nameFilters.append(m.comment() + " ("_L1 + globs + u')');
                     acceptedGlobs.append(m.globPatterns());
-                    nameFilters.append(m.comment() + " (" + globs + ")");
                 }
             }
         } else {
@@ -280,7 +295,7 @@ QStringList FilePickerController::nameFilters(const QStringList &acceptedMimeTyp
 
     const QString filter =
         QCoreApplication::translate("FilePickerController",
-                                    "Accepted types (%1)").arg(acceptedGlobs.join(' '));
+                                    "Accepted types (%1)").arg(acceptedGlobs.join(u' '));
     nameFilters.prepend(filter);
 
     return nameFilters;
diff --git a/src/core/file_system_access/file_system_access_permission_context_factory_qt.cpp b/src/core/file_system_access/file_system_access_permission_context_factory_qt.cpp
index 51c8a899191..ea7232fb708 100644
--- a/src/core/file_system_access/file_system_access_permission_context_factory_qt.cpp
+++ b/src/core/file_system_access/file_system_access_permission_context_factory_qt.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2022 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #include "file_system_access_permission_context_factory_qt.h"
 
diff --git a/src/core/file_system_access/file_system_access_permission_context_factory_qt.h b/src/core/file_system_access/file_system_access_permission_context_factory_qt.h
index 1d98243d6e3..8c3cd55a974 100644
--- a/src/core/file_system_access/file_system_access_permission_context_factory_qt.h
+++ b/src/core/file_system_access/file_system_access_permission_context_factory_qt.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2022 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #ifndef FILE_SYSTEM_ACCESS_PERMISSION_CONTEXT_FACTORY_QT_H
 #define FILE_SYSTEM_ACCESS_PERMISSION_CONTEXT_FACTORY_QT_H
diff --git a/src/core/file_system_access/file_system_access_permission_context_qt.cpp b/src/core/file_system_access/file_system_access_permission_context_qt.cpp
index c290aab82a7..8fbbd630309 100644
--- a/src/core/file_system_access/file_system_access_permission_context_qt.cpp
+++ b/src/core/file_system_access/file_system_access_permission_context_qt.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2022 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 // This file is based on chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc:
 // Copyright 2019 The Chromium Authors. All rights reserved.
diff --git a/src/core/file_system_access/file_system_access_permission_context_qt.h b/src/core/file_system_access/file_system_access_permission_context_qt.h
index 06fbfae3f0e..2fd4790ab37 100644
--- a/src/core/file_system_access/file_system_access_permission_context_qt.h
+++ b/src/core/file_system_access/file_system_access_permission_context_qt.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2022 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 // This file is based on chrome/browser/file_system_access/chrome_file_system_access_permission_context.h:
 // Copyright 2019 The Chromium Authors. All rights reserved.
@@ -53,6 +54,8 @@ class FileSystemAccessPermissionContextQt : public content::FileSystemAccessPerm
     base::FilePath GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory directory, const url::Origin &origin) override;
     std::u16string GetPickerTitle(const blink::mojom::FilePickerOptionsPtr &) override;
     void NotifyEntryMoved(const url::Origin &, const base::FilePath &, const base::FilePath &) override;
+    void OnFileCreatedFromShowSaveFilePicker(const GURL &file_picker_binding_context,
+                                             const storage::FileSystemURL &url) override{};
 
     void NavigatedAwayFromOrigin(const url::Origin &origin);
     content::BrowserContext *profile() const { return m_profile; }
diff --git a/src/core/file_system_access/file_system_access_permission_grant_qt.cpp b/src/core/file_system_access/file_system_access_permission_grant_qt.cpp
index 67fa1c8cfbb..1685f6364b4 100644
--- a/src/core/file_system_access/file_system_access_permission_grant_qt.cpp
+++ b/src/core/file_system_access/file_system_access_permission_grant_qt.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2022 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #include "file_system_access_permission_grant_qt.h"
 
diff --git a/src/core/file_system_access/file_system_access_permission_grant_qt.h b/src/core/file_system_access/file_system_access_permission_grant_qt.h
index 829d2b88912..f4b53d2494b 100644
--- a/src/core/file_system_access/file_system_access_permission_grant_qt.h
+++ b/src/core/file_system_access/file_system_access_permission_grant_qt.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2022 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #ifndef FILE_SYSTEM_ACCESS_PERMISSION_GRANT_QT_H
 #define FILE_SYSTEM_ACCESS_PERMISSION_GRANT_QT_H
diff --git a/src/core/file_system_access/file_system_access_permission_request_controller.h b/src/core/file_system_access/file_system_access_permission_request_controller.h
index e659f81a770..fcc16c206ae 100644
--- a/src/core/file_system_access/file_system_access_permission_request_controller.h
+++ b/src/core/file_system_access/file_system_access_permission_request_controller.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2022 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #ifndef FILE_SYSTEM_ACCESS_PERMISSION_REQUEST_CONTROLLER_H
 #define FILE_SYSTEM_ACCESS_PERMISSION_REQUEST_CONTROLLER_H
diff --git a/src/core/file_system_access/file_system_access_permission_request_controller_impl.cpp b/src/core/file_system_access/file_system_access_permission_request_controller_impl.cpp
index f77c974d0b6..ea7275d720f 100644
--- a/src/core/file_system_access/file_system_access_permission_request_controller_impl.cpp
+++ b/src/core/file_system_access/file_system_access_permission_request_controller_impl.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2022 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #include "file_system_access_permission_request_controller_impl.h"
 
diff --git a/src/core/file_system_access/file_system_access_permission_request_controller_impl.h b/src/core/file_system_access/file_system_access_permission_request_controller_impl.h
index 6cca1e5494d..ca4ee8ac9e2 100644
--- a/src/core/file_system_access/file_system_access_permission_request_controller_impl.h
+++ b/src/core/file_system_access/file_system_access_permission_request_controller_impl.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2022 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #ifndef FILE_SYSTEM_ACCESS_PERMISSION_REQUEST_CONTROLLER_IMPL_H
 #define FILE_SYSTEM_ACCESS_PERMISSION_REQUEST_CONTROLLER_IMPL_H
diff --git a/src/core/file_system_access/file_system_access_permission_request_manager_qt.cpp b/src/core/file_system_access/file_system_access_permission_request_manager_qt.cpp
index c384dc7b3ea..792da94a2fa 100644
--- a/src/core/file_system_access/file_system_access_permission_request_manager_qt.cpp
+++ b/src/core/file_system_access/file_system_access_permission_request_manager_qt.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2022 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #include "file_system_access_permission_request_manager_qt.h"
 
diff --git a/src/core/file_system_access/file_system_access_permission_request_manager_qt.h b/src/core/file_system_access/file_system_access_permission_request_manager_qt.h
index 84085491153..d9a5a110b9a 100644
--- a/src/core/file_system_access/file_system_access_permission_request_manager_qt.h
+++ b/src/core/file_system_access/file_system_access_permission_request_manager_qt.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2022 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #ifndef FILE_SYSTEM_ACCESS_PERMISSION_REQUEST_MANAGER_QT_H
 #define FILE_SYSTEM_ACCESS_PERMISSION_REQUEST_MANAGER_QT_H
diff --git a/src/core/find_text_helper.cpp b/src/core/find_text_helper.cpp
index 5dc12fab7db..cc52e65c14a 100644
--- a/src/core/find_text_helper.cpp
+++ b/src/core/find_text_helper.cpp
@@ -19,6 +19,7 @@ FindTextHelper::FindTextHelper(content::WebContents *webContents, WebContentsAda
     , m_viewClient(viewClient)
     , m_currentFindRequestId(m_findRequestIdCounter++)
     , m_lastCompletedFindRequestId(m_currentFindRequestId)
+    , m_previousCaseSensitively(false)
 {
 }
 
@@ -64,7 +65,7 @@ void FindTextHelper::startFinding(const QString &findText, bool caseSensitively,
 {
     Q_ASSERT(!findText.isEmpty());
 
-    const bool findNext = !m_previousFindText.isEmpty() && findText == m_previousFindText;
+    const bool findNext = !m_previousFindText.isEmpty() && findText == m_previousFindText && caseSensitively == m_previousCaseSensitively;
     if (isFindTextInProgress()) {
         // There are cases where the render process will overwrite a previous request
         // with the new search and we'll have a dangling callback, leaving the application
@@ -83,6 +84,7 @@ void FindTextHelper::startFinding(const QString &findText, bool caseSensitively,
     options->match_case = caseSensitively;
     options->new_session = !findNext;
     m_previousFindText = findText;
+    m_previousCaseSensitively = caseSensitively;
 
     m_currentFindRequestId = m_findRequestIdCounter++;
     m_webContents->Find(m_currentFindRequestId, toString16(findText), std::move(options), /*skip_delay=*/true);
diff --git a/src/core/find_text_helper.h b/src/core/find_text_helper.h
index 6d2e48b632a..fa3ea08d33d 100644
--- a/src/core/find_text_helper.h
+++ b/src/core/find_text_helper.h
@@ -61,6 +61,7 @@ class Q_WEBENGINECORE_EXPORT FindTextHelper {
     int m_lastCompletedFindRequestId;
 
     QString m_previousFindText;
+    bool m_previousCaseSensitively;
 
     QMap<int, QJSValue> m_quickCallbacks;
     QMap<int, std::function<void(const QWebEngineFindTextResult &)>> m_widgetCallbacks;
diff --git a/src/core/location_provider_qt.cpp b/src/core/location_provider_qt.cpp
index dc0d80aa79d..479228dc498 100644
--- a/src/core/location_provider_qt.cpp
+++ b/src/core/location_provider_qt.cpp
@@ -214,8 +214,12 @@ void QtPositioningHelper::error(QGeoPositionInfoSource::Error positioningError)
         break;
     }
     auto newResult = device::mojom::GeopositionResult::NewError(std::move(newError));
-    if (m_locationProvider)
+    if (m_locationProvider) {
         postToLocationProvider(base::BindOnce(&LocationProviderQt::updatePosition, m_locationProviderFactory.GetWeakPtr(), std::move(newResult)));
+        if (positioningError == QGeoPositionInfoSource::AccessError) {
+            postToLocationProvider(base::BindOnce(&LocationProviderQt::StopProvider, m_locationProviderFactory.GetWeakPtr()));
+        }
+    }
 }
 
 inline void QtPositioningHelper::postToLocationProvider(base::OnceClosure task)
diff --git a/src/core/media_capture_devices_dispatcher.cpp b/src/core/media_capture_devices_dispatcher.cpp
index 6dc45c4429b..73cf2be6cac 100644
--- a/src/core/media_capture_devices_dispatcher.cpp
+++ b/src/core/media_capture_devices_dispatcher.cpp
@@ -13,11 +13,13 @@
 #include "web_engine_settings.h"
 
 #include "base/strings/strcat.h"
+#include "blink/public/common/page/page_zoom.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/desktop_media_id.h"
 #include "content/public/browser/desktop_streams_registry.h"
+#include "content/public/browser/host_zoom_map.h"
 #include "content/public/browser/media_capture_devices.h"
 #include "content/public/browser/render_process_host.h"
 #include "media/audio/audio_device_description.h"
@@ -107,6 +109,27 @@ media::mojom::CaptureHandlePtr CreateCaptureHandle(content::WebContents *capture
     return result;
 }
 
+absl::optional<int> GetZoomLevel(content::WebContents *capturer,
+                                 const content::DesktopMediaID &captured_id)
+{
+    content::RenderFrameHost *const captured_rfh =
+            content::RenderFrameHost::FromID(captured_id.web_contents_id.render_process_id,
+                                             captured_id.web_contents_id.main_render_frame_id);
+    if (!captured_rfh || !captured_rfh->IsActive()) {
+        return absl::nullopt;
+    }
+
+    content::WebContents *const captured_wc =
+            content::WebContents::FromRenderFrameHost(captured_rfh);
+    if (!captured_wc) {
+        return absl::nullopt;
+    }
+
+    double zoom_level =
+            blink::PageZoomLevelToZoomFactor(content::HostZoomMap::GetZoomLevel(captured_wc));
+    return std::round(100 * zoom_level);
+}
+
 // Based on chrome/browser/media/webrtc/desktop_capture_devices_util.cc:
 media::mojom::DisplayMediaInformationPtr DesktopMediaIDToDisplayMediaInformation(content::WebContents *capturer,
                                                                                  const url::Origin &capturer_origin,
@@ -122,6 +145,7 @@ media::mojom::DisplayMediaInformationPtr DesktopMediaIDToDisplayMediaInformation
 #endif  // defined(USE_AURA)
 
     media::mojom::CaptureHandlePtr capture_handle;
+    int zoom_level = 100;
     switch (media_id.type) {
     case content::DesktopMediaID::TYPE_SCREEN:
         display_surface = media::mojom::DisplayCaptureSurfaceType::MONITOR;
@@ -137,12 +161,16 @@ media::mojom::DisplayMediaInformationPtr DesktopMediaIDToDisplayMediaInformation
         display_surface = media::mojom::DisplayCaptureSurfaceType::BROWSER;
         cursor = media::mojom::CursorCaptureType::MOTION;
         capture_handle = CreateCaptureHandle(capturer, capturer_origin, media_id);
+        if (base::FeatureList::IsEnabled(features::kCapturedSurfaceControlKillswitch)) {
+            zoom_level = GetZoomLevel(capturer, media_id).value_or(zoom_level);
+        }
         break;
     case content::DesktopMediaID::TYPE_NONE:
         break;
     }
 
-    return media::mojom::DisplayMediaInformation::New(display_surface, logical_surface, cursor, std::move(capture_handle));
+    return media::mojom::DisplayMediaInformation::New(display_surface, logical_surface, cursor,
+                                                      std::move(capture_handle), zoom_level);
 }
 
 
@@ -333,8 +361,12 @@ void MediaCaptureDevicesDispatcher::handleMediaAccessPermissionResponse(content:
     bool securityOriginsMatch = (requestSecurityOrigin.host() == securityOrigin.host()
                                  && requestSecurityOrigin.scheme() == securityOrigin.scheme()
                                  && requestSecurityOrigin.port() == securityOrigin.port());
-    if (!securityOriginsMatch)
-        qWarning("Security origin mismatch for media access permission: %s requested and %s provided\n", qPrintable(requestSecurityOrigin.toString()), qPrintable(securityOrigin.toString()));
+    if (!securityOriginsMatch) {
+        qWarning("Security origin mismatch for media access permission: %ls requested "
+                 "and %ls provided\n",
+                 qUtf16Printable(requestSecurityOrigin.toString()),
+                 qUtf16Printable(securityOrigin.toString()));
+    }
 
     WebContentsAdapterClient::MediaRequestFlags requestFlags = mediaRequestFlagsForRequest(request);
     WebContentsAdapterClient::MediaRequestFlags finalFlags = requestFlags & authorizationFlags;
diff --git a/src/core/native_web_keyboard_event_qt_mac.mm b/src/core/native_web_keyboard_event_qt_mac.mm
index 0f5b12db43d..1499631eb23 100644
--- a/src/core/native_web_keyboard_event_qt_mac.mm
+++ b/src/core/native_web_keyboard_event_qt_mac.mm
@@ -103,7 +103,7 @@
         key = QAppleKeyMapper::fromCocoaKey(character);
     }
 
-    QString text = QString::fromNSString(characters);
+    const QString text = QString::fromNSString(characters);
     bool autorep = nsevent.ARepeat;
 
     return new QKeyEvent(type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers,
diff --git a/src/core/net/client_cert_qt.cpp b/src/core/net/client_cert_qt.cpp
index 044e5618e6a..9fdee972a30 100644
--- a/src/core/net/client_cert_qt.cpp
+++ b/src/core/net/client_cert_qt.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2018 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #include "client_cert_qt.h"
 
diff --git a/src/core/net/client_cert_qt.h b/src/core/net/client_cert_qt.h
index 96579fae65d..52599ce56e0 100644
--- a/src/core/net/client_cert_qt.h
+++ b/src/core/net/client_cert_qt.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2018 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #ifndef CLIENT_CERT_QT_P_H
 #define CLIENT_CERT_QT_P_H
diff --git a/src/core/net/client_cert_store_data.cpp b/src/core/net/client_cert_store_data.cpp
index 0de6885df85..6dbdb9776b6 100644
--- a/src/core/net/client_cert_store_data.cpp
+++ b/src/core/net/client_cert_store_data.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2019 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:critical reason:network-protocol
 
 #include "net/client_cert_store_data.h"
 
@@ -92,8 +93,8 @@ namespace QtWebEngineCore {
 
 void ClientCertificateStoreData::add(const QSslCertificate &certificate, const QSslKey &privateKey)
 {
-    QByteArray sslKeyInBytes = privateKey.toPem();
-    QByteArray certInBytes = certificate.toDer();
+    const QByteArray sslKeyInBytes = privateKey.toPem();
+    const QByteArray certInBytes = certificate.toDer();
 
     Entry *data = new Entry;
     data->keyPtr = wrapOpenSSLPrivateKey(sslKeyInBytes);
diff --git a/src/core/net/client_cert_store_data.h b/src/core/net/client_cert_store_data.h
index c2e28ac1883..e53d19eaf3f 100644
--- a/src/core/net/client_cert_store_data.h
+++ b/src/core/net/client_cert_store_data.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2019 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:critical reason:network-protocol
 
 #ifndef CLIENT_CERT_STORE_DATA_H
 #define CLIENT_CERT_STORE_DATA_H
diff --git a/src/core/net/cookie_monster_delegate_qt.cpp b/src/core/net/cookie_monster_delegate_qt.cpp
index d107c520c41..9f317894a09 100644
--- a/src/core/net/cookie_monster_delegate_qt.cpp
+++ b/src/core/net/cookie_monster_delegate_qt.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2016 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #include "cookie_monster_delegate_qt.h"
 
@@ -50,7 +51,7 @@ class CookieAccessFilter : public network::mojom::CookieRemoteAccessFilter
 
 static GURL sourceUrlForCookie(const QNetworkCookie &cookie)
 {
-    QString urlFragment = QStringLiteral("%1%2").arg(cookie.domain()).arg(cookie.path());
+    const QString urlFragment = cookie.domain() % cookie.path();
     return net::cookie_util::CookieOriginToURL(urlFragment.toStdString(), /* is_https */ cookie.isSecure());
 }
 
diff --git a/src/core/net/cookie_monster_delegate_qt.h b/src/core/net/cookie_monster_delegate_qt.h
index f6872323dcb..57f56c8f5d2 100644
--- a/src/core/net/cookie_monster_delegate_qt.h
+++ b/src/core/net/cookie_monster_delegate_qt.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2016 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 //
 //  W A R N I N G
diff --git a/src/core/net/custom_url_loader_factory.cpp b/src/core/net/custom_url_loader_factory.cpp
index 954253b54de..6004b938e2d 100644
--- a/src/core/net/custom_url_loader_factory.cpp
+++ b/src/core/net/custom_url_loader_factory.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2019 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:critical reason:network-protocol
 
 #include "custom_url_loader_factory.h"
 
@@ -28,6 +29,7 @@
 #include "qwebengineloadinginfo.h"
 #include "type_conversion.h"
 #include "web_contents_adapter_client.h"
+#include "web_contents_delegate_qt.h"
 #include "web_contents_view_qt.h"
 
 #include <QtCore/qbytearray.h>
@@ -358,10 +360,9 @@ class CustomURLLoader : public network::mojom::URLLoader
     void notifySuccess() override
     {
         if (m_webContents) {
-            WebContentsAdapterClient *client = WebContentsViewQt::from(static_cast<content::WebContentsImpl *>(m_webContents)->GetView())->client();
-            QWebEngineLoadingInfo info(toQt(m_request.url), QWebEngineLoadingInfo::LoadSucceededStatus);
-            client->loadFinished(std::move(info));
-            client->updateNavigationActions();
+            WebContentsDelegateQt *delegate =
+                    static_cast<WebContentsDelegateQt *>(m_webContents->GetDelegate());
+            delegate->emitLoadSucceeded(toQt(m_request.url));
         }
     }
     void notifyReadyRead() override
@@ -534,4 +535,3 @@ mojo::PendingRemote<network::mojom::URLLoaderFactory> CreateCustomURLLoaderFacto
 }
 
 } // namespace QtWebEngineCore
-
diff --git a/src/core/net/custom_url_loader_factory.h b/src/core/net/custom_url_loader_factory.h
index e4a767c8563..e46f2f7994a 100644
--- a/src/core/net/custom_url_loader_factory.h
+++ b/src/core/net/custom_url_loader_factory.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2019 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 //
 //  W A R N I N G
diff --git a/src/core/net/plugin_response_interceptor_url_loader_throttle.cpp b/src/core/net/plugin_response_interceptor_url_loader_throttle.cpp
index 159fa28ca97..d6c890282c3 100644
--- a/src/core/net/plugin_response_interceptor_url_loader_throttle.cpp
+++ b/src/core/net/plugin_response_interceptor_url_loader_throttle.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2019 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:critical reason:network-protocol
 
 // based on chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc
 // Copyright 2018 The Chromium Authors. All rights reserved.
@@ -30,6 +31,9 @@
 #include <tuple>
 
 namespace {
+
+constexpr uint32_t kFullPageMimeHandlerDataPipeSize = 512U;
+
 void ClearAllButFrameAncestors(network::mojom::URLResponseHead *response_head)
 {
     response_head->headers->RemoveHeader("Content-Security-Policy");
@@ -121,11 +125,7 @@ void PluginResponseInterceptorURLLoaderThrottle::WillProcessResponse(const GURL
     if (extension_id == extension_misc::kPdfExtensionId && response_head->headers)
         ClearAllButFrameAncestors(response_head);
 
-    MimeTypesHandler::ReportUsedHandler(extension_id);
-
-    std::string view_id = base::Uuid::GenerateRandomV4().AsLowercaseString();
-    // The string passed down to the original client with the response body.
-    std::string payload = view_id;
+    const std::string stream_id = base::Uuid::GenerateRandomV4().AsLowercaseString();
 
     mojo::PendingRemote<network::mojom::URLLoader> dummy_new_loader;
     std::ignore = dummy_new_loader.InitWithNewPipeAndPassReceiver();
@@ -133,20 +133,20 @@ void PluginResponseInterceptorURLLoaderThrottle::WillProcessResponse(const GURL
     mojo::PendingReceiver<network::mojom::URLLoaderClient> new_client_receiver =
         new_client.BindNewPipeAndPassReceiver();
 
-
-    uint32_t data_pipe_size = 64U;
+    const std::string internal_id = base::UnguessableToken::Create().ToString();
     // Provide the MimeHandlerView code a chance to override the payload. This is
     // the case where the resource is handled by frame-based MimeHandlerView.
-    *defer = extensions::MimeHandlerViewAttachHelper::OverrideBodyForInterceptedResponse(
-        m_frame_tree_node_id, response_url, response_head->mime_type, view_id,
-        &payload, &data_pipe_size,
-        base::BindOnce(
-            &PluginResponseInterceptorURLLoaderThrottle::ResumeLoad,
-            weak_factory_.GetWeakPtr()));
+    const std::string payload =
+            extensions::MimeHandlerViewAttachHelper::OverrideBodyForInterceptedResponse(
+                    m_frame_tree_node_id, response_url, response_head->mime_type, stream_id,
+                    internal_id,
+                    base::BindOnce(&PluginResponseInterceptorURLLoaderThrottle::ResumeLoad,
+                                   weak_factory_.GetWeakPtr()));
+    *defer = true;
 
     mojo::ScopedDataPipeProducerHandle producer_handle;
     mojo::ScopedDataPipeConsumerHandle consumer_handle;
-    CHECK_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(data_pipe_size, producer_handle, consumer_handle));
+    CHECK_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(kFullPageMimeHandlerDataPipeSize, producer_handle, consumer_handle));
 
     uint32_t len = static_cast<uint32_t>(payload.size());
     CHECK_EQ(MOJO_RESULT_OK,
@@ -186,11 +186,10 @@ void PluginResponseInterceptorURLLoaderThrottle::WillProcessResponse(const GURL
     bool embedded = m_request_destination !=
             network::mojom::RequestDestination::kDocument;
     content::GetUIThreadTaskRunner({})->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-            &extensions::StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent,
-            extension_id, view_id, embedded, m_frame_tree_node_id,
-            std::move(transferrable_loader), response_url));
+            FROM_HERE,
+            base::BindOnce(&extensions::StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent,
+                           extension_id, stream_id, embedded, m_frame_tree_node_id,
+                           std::move(transferrable_loader), response_url, internal_id));
 }
 
 void PluginResponseInterceptorURLLoaderThrottle::ResumeLoad()
diff --git a/src/core/net/plugin_response_interceptor_url_loader_throttle.h b/src/core/net/plugin_response_interceptor_url_loader_throttle.h
index fb3918c45e3..e66d50b550a 100644
--- a/src/core/net/plugin_response_interceptor_url_loader_throttle.h
+++ b/src/core/net/plugin_response_interceptor_url_loader_throttle.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2019 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #ifndef PLUGIN_RESPONSE_INTERCEPTOR_URL_LOADER_THROTTLE_H_
 #define PLUGIN_RESPONSE_INTERCEPTOR_URL_LOADER_THROTTLE_H_
diff --git a/src/core/net/proxy_config_monitor.cpp b/src/core/net/proxy_config_monitor.cpp
index 8315b7bf26f..d564f55bd28 100644
--- a/src/core/net/proxy_config_monitor.cpp
+++ b/src/core/net/proxy_config_monitor.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2019 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 // originally based on chrome/browser/net/proxy_config_monitor.cc
 // Copyright 2017 The Chromium Authors. All rights reserved.
diff --git a/src/core/net/proxy_config_monitor.h b/src/core/net/proxy_config_monitor.h
index 585e4b7ede5..514e892be89 100644
--- a/src/core/net/proxy_config_monitor.h
+++ b/src/core/net/proxy_config_monitor.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2019 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 // originally based on chrome/browser/net/proxy_config_monitor.h
 // Copyright 2017 The Chromium Authors. All rights reserved.
diff --git a/src/core/net/proxy_config_service_qt.cpp b/src/core/net/proxy_config_service_qt.cpp
index fcce08550a9..9d434677634 100644
--- a/src/core/net/proxy_config_service_qt.cpp
+++ b/src/core/net/proxy_config_service_qt.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2016 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 
 //================ Based on ChromeProxyConfigService =======================
@@ -27,7 +28,6 @@ net::ProxyServer ProxyConfigServiceQt::fromQNetworkProxy(const QNetworkProxy &qt
         return net::ProxyServer::FromSchemeHostAndPort(net::ProxyServer::SCHEME_HTTP, host, port);
     case QNetworkProxy::NoProxy:
     case QNetworkProxy::DefaultProxy:
-        return net::ProxyServer(net::ProxyServer::SCHEME_DIRECT, net::HostPortPair());
     default:
         return net::ProxyServer(net::ProxyServer::SCHEME_INVALID, net::HostPortPair());
     }
diff --git a/src/core/net/proxy_config_service_qt.h b/src/core/net/proxy_config_service_qt.h
index 49c9877a5ed..b3ec0ca2d53 100644
--- a/src/core/net/proxy_config_service_qt.h
+++ b/src/core/net/proxy_config_service_qt.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2016 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #ifndef PROXY_CONFIG_SERVICE_QT_H
 #define PROXY_CONFIG_SERVICE_QT_H
diff --git a/src/core/net/proxying_restricted_cookie_manager_qt.cpp b/src/core/net/proxying_restricted_cookie_manager_qt.cpp
index d4d5cc4ab3c..bf2723c1085 100644
--- a/src/core/net/proxying_restricted_cookie_manager_qt.cpp
+++ b/src/core/net/proxying_restricted_cookie_manager_qt.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2019 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 // originally based on android_webview/browser/network_service/aw_proxying_restricted_cookie_manager.cc:
 // Copyright 2019 The Chromium Authors. All rights reserved.
@@ -67,13 +68,14 @@ void ProxyingRestrictedCookieManagerQt::GetAllForUrl(const GURL &url,
                                                      const net::SiteForCookies &site_for_cookies,
                                                      const url::Origin &top_frame_origin, bool has_storage_access,
                                                      network::mojom::CookieManagerGetOptionsPtr options,
+                                                     bool is_ad_tagged,
                                                      GetAllForUrlCallback callback)
 {
     DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
     if (allowCookies(url, site_for_cookies)) {
         underlying_restricted_cookie_manager_->GetAllForUrl(url, site_for_cookies, top_frame_origin, has_storage_access,
-                                                            std::move(options), std::move(callback));
+                                                            std::move(options), is_ad_tagged, std::move(callback));
     } else {
         std::move(callback).Run(std::vector<net::CookieWithAccessResult>());
     }
@@ -121,7 +123,7 @@ void ProxyingRestrictedCookieManagerQt::SetCookieFromString(const GURL &url,
         underlying_restricted_cookie_manager_->SetCookieFromString(url, site_for_cookies, top_frame_origin, has_storage_access,
                                                                    cookie, std::move(callback));
     } else {
-        std::move(callback).Run(false, false); // FIXME: is true, true in aw_proxying_restricted_cookie_manager.cc though..
+        std::move(callback).Run();
     }
 }
 
@@ -129,6 +131,7 @@ void ProxyingRestrictedCookieManagerQt::GetCookiesString(const GURL &url,
                                                          const net::SiteForCookies &site_for_cookies,
                                                          const url::Origin &top_frame_origin,
                                                          bool has_storage_access, bool get_version_shared_memory,
+                                                         bool is_ad_tagged,
                                                          GetCookiesStringCallback callback)
 {
     DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
@@ -136,6 +139,7 @@ void ProxyingRestrictedCookieManagerQt::GetCookiesString(const GURL &url,
     if (allowCookies(url, site_for_cookies)) {
         underlying_restricted_cookie_manager_->GetCookiesString(url, site_for_cookies, top_frame_origin,
                                                                 has_storage_access, get_version_shared_memory,
+                                                                is_ad_tagged,
                                                                 std::move(callback));
     } else {
         std::move(callback).Run(network::mojom::kInvalidCookieVersion, base::ReadOnlySharedMemoryRegion(), "");
diff --git a/src/core/net/proxying_restricted_cookie_manager_qt.h b/src/core/net/proxying_restricted_cookie_manager_qt.h
index faf0545c31b..f78b878e3d4 100644
--- a/src/core/net/proxying_restricted_cookie_manager_qt.h
+++ b/src/core/net/proxying_restricted_cookie_manager_qt.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2019 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #ifndef PROXYING_RESTRICTED_COOKIE_MANAGER_QT_H
 #define PROXYING_RESTRICTED_COOKIE_MANAGER_QT_H
@@ -29,6 +30,7 @@ class ProxyingRestrictedCookieManagerQt : public network::mojom::RestrictedCooki
                       const url::Origin &top_frame_origin,
                       bool has_storage_access,
                       network::mojom::CookieManagerGetOptionsPtr options,
+                      bool is_ad_tagged,
                       GetAllForUrlCallback callback) override;
 
     void SetCanonicalCookie(const net::CanonicalCookie& cookie,
@@ -54,6 +56,7 @@ class ProxyingRestrictedCookieManagerQt : public network::mojom::RestrictedCooki
                           const net::SiteForCookies &site_for_cookies,
                           const url::Origin &top_frame_origin,
                           bool has_storage_access, bool get_version_shared_memory,
+                          bool is_ad_tagged,
                           GetCookiesStringCallback callback) override;
     void CookiesEnabledFor(const GURL &url,
                            const net::SiteForCookies &site_for_cookies,
diff --git a/src/core/net/proxying_url_loader_factory_qt.cpp b/src/core/net/proxying_url_loader_factory_qt.cpp
index 3a83ed7ea74..ab9f88785fa 100644
--- a/src/core/net/proxying_url_loader_factory_qt.cpp
+++ b/src/core/net/proxying_url_loader_factory_qt.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2019 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:critical reason:network-protocol
 
 #include "proxying_url_loader_factory_qt.h"
 
@@ -65,6 +66,7 @@ ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeCspReport, blink::mojom
 ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypePluginResource, blink::mojom::ResourceType::kPluginResource)
 ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeNavigationPreloadMainFrame, blink::mojom::ResourceType::kNavigationPreloadMainFrame)
 ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeNavigationPreloadSubFrame, blink::mojom::ResourceType::kNavigationPreloadSubFrame)
+ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeJson, blink::mojom::ResourceType::kJson)
 ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeLast, blink::mojom::ResourceType::kMaxValue)
 
 extern WebContentsAdapterClient::NavigationType pageTransitionToNavigationType(ui::PageTransition transition);
@@ -373,8 +375,9 @@ void InterceptedRequest::ContinueAfterIntercept()
         }
 
         if (info.changed) {
+            // Used the same error code as AwProxyingURLLoaderFactory's blocked request.
             if (info.shouldBlockRequest)
-                return SendErrorAndCompleteImmediately(net::ERR_BLOCKED_BY_CLIENT);
+                return SendErrorAndCompleteImmediately(net::ERR_ACCESS_DENIED);
 
             if (info.shouldRedirectRequest) {
                 net::RedirectInfo::FirstPartyURLPolicy first_party_url_policy =
diff --git a/src/core/net/proxying_url_loader_factory_qt.h b/src/core/net/proxying_url_loader_factory_qt.h
index 904a40c2dde..9dddb02141c 100644
--- a/src/core/net/proxying_url_loader_factory_qt.h
+++ b/src/core/net/proxying_url_loader_factory_qt.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2019 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:critical reason:network-protocol
 
 #ifndef PROXYING_URL_LOADER_FACTORY_QT_H_
 #define PROXYING_URL_LOADER_FACTORY_QT_H_
diff --git a/src/core/net/qrc_url_scheme_handler.cpp b/src/core/net/qrc_url_scheme_handler.cpp
index a8b4e4388a2..894d18162c0 100644
--- a/src/core/net/qrc_url_scheme_handler.cpp
+++ b/src/core/net/qrc_url_scheme_handler.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2018 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #include "qrc_url_scheme_handler.h"
 
@@ -12,19 +13,21 @@
 
 #include <memory>
 
+using namespace Qt::StringLiterals;
+
 namespace QtWebEngineCore {
 
 void QrcUrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob *job)
 {
-    QByteArray requestMethod = job->requestMethod();
+    const QByteArray requestMethod = job->requestMethod();
     if (requestMethod != "GET") {
         job->fail(QWebEngineUrlRequestJob::RequestDenied);
         return;
     }
 
-    QUrl requestUrl = job->requestUrl();
-    QString requestPath = requestUrl.path();
-    auto file = std::make_unique<QFile>(':' + requestPath, job);
+    const QUrl requestUrl = job->requestUrl();
+    const QString requestPath = requestUrl.path();
+    auto file = std::make_unique<QFile>(u':' + requestPath, job);
     if (!file->exists() || file->size() == 0) {
         qWarning("QResource '%s' not found or is empty", qUtf8Printable(requestPath));
         job->fail(QWebEngineUrlRequestJob::UrlNotFound);
@@ -33,7 +36,7 @@ void QrcUrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob *job)
     QFileInfo fileInfo(*file);
     QMimeDatabase mimeDatabase;
     QMimeType mimeType = mimeDatabase.mimeTypeForFile(fileInfo);
-    if (mimeType.name() == QStringLiteral("application/x-extension-html"))
+    if (mimeType.name() == "application/x-extension-html"_L1)
         job->reply("text/html", file.release());
     else
         job->reply(mimeType.name().toUtf8(), file.release());
diff --git a/src/core/net/qrc_url_scheme_handler.h b/src/core/net/qrc_url_scheme_handler.h
index 96155b05bcf..de776990c2d 100644
--- a/src/core/net/qrc_url_scheme_handler.h
+++ b/src/core/net/qrc_url_scheme_handler.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2018 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #ifndef QRC_URL_SCHEME_HANDLER_H
 #define QRC_URL_SCHEME_HANDLER_H
diff --git a/src/core/net/resource_request_body_qt.cpp b/src/core/net/resource_request_body_qt.cpp
index d0d54784dd9..2dceb201ce0 100644
--- a/src/core/net/resource_request_body_qt.cpp
+++ b/src/core/net/resource_request_body_qt.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2023 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:critical reason:data-parser
 
 #include "resource_request_body_qt.h"
 #include "type_conversion.h"
@@ -9,6 +10,8 @@
 #include "services/network/public/mojom/url_request.mojom-shared.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
+using namespace Qt::StringLiterals;
+
 namespace QtWebEngineCore {
 
 ResourceRequestBody::ResourceRequestBody(network::ResourceRequestBody *requestBody, QObject *parent)
@@ -58,8 +61,8 @@ qint64 ResourceRequestBody::readData(char *data, qint64 maxSize)
                 break;
             }
             case network::mojom::DataElementDataView::Tag::kChunkedDataPipe: {
-                setErrorString(QStringLiteral("Chunked data pipe is used in request body upload, which "
-                                              "is currently not supported"));
+                setErrorString(u"Chunked data pipe is used in request body upload, which "
+                               "is currently not supported"_s);
                 // Nothing should come before or after DataElementChunkedDataPipe
                 return -1;
             }
@@ -110,7 +113,14 @@ void ResourceRequestBody::readDataElementFile(const base::FilePath &filePath, co
     const std::size_t fileSize = std::min(file.size(), length) - realOffset;
     const std::size_t bytesToRead = std::min(fileSize, static_cast<std::size_t>(maxSize));
 
-    file.open(QFile::ReadOnly);
+    if (!file.open(QFile::ReadOnly)) {
+        m_dataElementsIdx++;
+        m_dataElementFileIdx = 0;
+        setErrorString(u"Error while reading from file, skipping remaining content of "_s
+                       % file.fileName() % u": "_s % file.errorString());
+        return;
+    }
+
     file.seek(realOffset);
 
     std::memcpy(*data, file.read(bytesToRead).data(), bytesToRead);
@@ -154,8 +164,8 @@ void ResourceRequestBody::readDataElementPipe(
             *data += bytesToRead;
             bytesRead += bytesToRead;
         } else if (result != MOJO_RESULT_SHOULD_WAIT && result != MOJO_RESULT_FAILED_PRECONDITION) {
-            setErrorString(QString::fromLatin1("Error while reading from data pipe, skipping"
-                                               "remaining content of data pipe. Mojo error code: ")
+            setErrorString("Error while reading from data pipe, skipping "
+                           "remaining content of data pipe. Mojo error code: "_L1
                            + QString::number(result));
         }
     } while ((result == MOJO_RESULT_SHOULD_WAIT || result == MOJO_RESULT_OK)
diff --git a/src/core/net/resource_request_body_qt.h b/src/core/net/resource_request_body_qt.h
index 717885d7d5b..710c0bb3826 100644
--- a/src/core/net/resource_request_body_qt.h
+++ b/src/core/net/resource_request_body_qt.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2023 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:critical reason:data-parser
 
 #ifndef RESOURCEREQUESTBODY_QT_H
 #define RESOURCEREQUESTBODY_QT_H
diff --git a/src/core/net/ssl_host_state_delegate_qt.cpp b/src/core/net/ssl_host_state_delegate_qt.cpp
index 41967f14efc..69f094809c7 100644
--- a/src/core/net/ssl_host_state_delegate_qt.cpp
+++ b/src/core/net/ssl_host_state_delegate_qt.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2016 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #include "ssl_host_state_delegate_qt.h"
 
@@ -127,9 +128,9 @@ void SSLHostStateDelegateQt::SetHttpsEnforcementForHost(const std::string &host,
     // Intentional no-op see aw_ssl_host_state_delegate
 }
 
-bool SSLHostStateDelegateQt::IsHttpsEnforcedForHost(const std::string &host, content::StoragePartition *storage_partition)
+bool SSLHostStateDelegateQt::IsHttpsEnforcedForUrl(const GURL &url,
+                                                   content::StoragePartition *storage_partition)
 {
-    // Intentional no-op
     return false;
 }
 
diff --git a/src/core/net/ssl_host_state_delegate_qt.h b/src/core/net/ssl_host_state_delegate_qt.h
index 0b3d7974c2c..1910e4e6500 100644
--- a/src/core/net/ssl_host_state_delegate_qt.h
+++ b/src/core/net/ssl_host_state_delegate_qt.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2016 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #ifndef SSL_HOST_STATE_DELEGATE_QT_H
 #define SSL_HOST_STATE_DELEGATE_QT_H
@@ -39,11 +40,13 @@ class SSLHostStateDelegateQt : public content::SSLHostStateDelegate
     bool DidHostRunInsecureContent(const std::string &host, int child_id, InsecureContentType content_type) override;
     void AllowHttpForHost(const std::string &host, content::StoragePartition *web_contents) override;
     bool IsHttpAllowedForHost(const std::string &host, content::StoragePartition *web_contents) override;
-    void SetHttpsEnforcementForHost(const std::string &host, bool enforce, content::StoragePartition *storage_partition) override;
-    bool IsHttpsEnforcedForHost(const std::string &host, content::StoragePartition *web_contents) override;
+    void SetHttpsEnforcementForHost(const std::string &host, bool enforce,
+                                    content::StoragePartition *storage_partition) override;
     void RevokeUserAllowExceptions(const std::string &host) override;
     bool HasAllowException(const std::string &host, content::StoragePartition *web_contents) override;
     bool HasAllowExceptionForAnyHost(content::StoragePartition *storage_partition) override;
+    bool IsHttpsEnforcedForUrl(const GURL &url,
+                               content::StoragePartition *storage_partition) override;
 
 private:
     std::map<std::string, CertPolicy> m_certPolicyforHost;
diff --git a/src/core/net/system_network_context_manager.cpp b/src/core/net/system_network_context_manager.cpp
index 439d1066c29..3a3b22c6ce7 100644
--- a/src/core/net/system_network_context_manager.cpp
+++ b/src/core/net/system_network_context_manager.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2021 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 // based on chrome/browser/net/system_network_context_manager.cc:
 // Copyright 2017 The Chromium Authors. All rights reserved.
@@ -236,10 +237,7 @@ void SystemNetworkContextManager::OnNetworkServiceCreated(network::mojom::Networ
 
         log_list_mojo.push_back(std::move(log_info));
     }
-    network_service->UpdateCtLogList(
-            std::move(log_list_mojo),
-            certificate_transparency::GetLogListTimestamp(),
-            base::DoNothing());
+    network_service->UpdateCtLogList(std::move(log_list_mojo), base::DoNothing());
 
     // The system NetworkContext is created first
     network_service_network_context_.reset();
diff --git a/src/core/net/system_network_context_manager.h b/src/core/net/system_network_context_manager.h
index d56bdab7893..7101871c11d 100644
--- a/src/core/net/system_network_context_manager.h
+++ b/src/core/net/system_network_context_manager.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2019 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 // based on chrome/browser/net/system_network_context_manager.h:
 // Copyright 2017 The Chromium Authors. All rights reserved.
diff --git a/src/core/net/url_request_custom_job_delegate.cpp b/src/core/net/url_request_custom_job_delegate.cpp
index fb6b605a49b..ebf414c64df 100644
--- a/src/core/net/url_request_custom_job_delegate.cpp
+++ b/src/core/net/url_request_custom_job_delegate.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2016 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #include "url_request_custom_job_delegate.h"
 #include "url_request_custom_job_proxy.h"
diff --git a/src/core/net/url_request_custom_job_delegate.h b/src/core/net/url_request_custom_job_delegate.h
index 63db4646433..110758719cb 100644
--- a/src/core/net/url_request_custom_job_delegate.h
+++ b/src/core/net/url_request_custom_job_delegate.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2016 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 //
 //  W A R N I N G
diff --git a/src/core/net/url_request_custom_job_proxy.cpp b/src/core/net/url_request_custom_job_proxy.cpp
index 54faddc6227..8cad09633ac 100644
--- a/src/core/net/url_request_custom_job_proxy.cpp
+++ b/src/core/net/url_request_custom_job_proxy.cpp
@@ -1,11 +1,13 @@
 // Copyright (C) 2017 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #include "url_request_custom_job_proxy.h"
 #include "url_request_custom_job_delegate.h"
 
 #include "content/public/browser/browser_thread.h"
 #include "net/base/net_errors.h"
+#include "net/http/http_util.h"
 #include "services/network/public/cpp/resource_request_body.h"
 
 #include "api/qwebengineurlrequestjob.h"
@@ -47,18 +49,8 @@ void URLRequestCustomJobProxy::reply(std::string contentType, QIODevice *device,
     if (!m_client)
         return;
     DCHECK (!m_ioTaskRunner || m_ioTaskRunner->RunsTasksInCurrentSequence());
-    QByteArray qcontentType = QByteArray::fromStdString(contentType).toLower();
-    const int sidx = qcontentType.indexOf(';');
-    if (sidx > 0) {
-        const int cidx = qcontentType.indexOf("charset=", sidx);
-        if (cidx > 0) {
-            m_client->m_charset = qcontentType.mid(cidx + 8).trimmed().toStdString();
-            qcontentType = qcontentType.first(sidx);
-        } else {
-            qWarning() << "QWebEngineUrlRequestJob::reply(): Unrecognized content-type format with ';'" << qcontentType;
-        }
-    }
-    m_client->m_mimeType = qcontentType.trimmed().toStdString();
+    bool hadCharset = false;
+    net::HttpUtil::ParseContentType(contentType, &m_client->m_mimeType, &m_client->m_charset, &hadCharset, nullptr);
     m_client->m_device = device;
     m_client->m_additionalResponseHeaders = std::move(additionalResponseHeaders);
     if (m_client->m_device && !m_client->m_device->isReadable())
diff --git a/src/core/net/url_request_custom_job_proxy.h b/src/core/net/url_request_custom_job_proxy.h
index c0399241179..ea2ed9d68b6 100644
--- a/src/core/net/url_request_custom_job_proxy.h
+++ b/src/core/net/url_request_custom_job_proxy.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2017 Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #ifndef URL_REQUEST_CUSTOM_JOB_PROXY_H_
 #define URL_REQUEST_CUSTOM_JOB_PROXY_H_
diff --git a/src/core/net/version_ui_qt.cpp b/src/core/net/version_ui_qt.cpp
index 61a89596a2d..02dc5a87dbf 100644
--- a/src/core/net/version_ui_qt.cpp
+++ b/src/core/net/version_ui_qt.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2023 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #include "version_ui_qt.h"
 #include "api/qtwebenginecoreglobal.h"
@@ -9,12 +10,14 @@
 #include "chrome/browser/profiles/profile.h"
 #include "qtwebengine/grit/qt_webengine_resources.h"
 #include "services/network/public/cpp/content_security_policy/content_security_policy.h"
+#include "v8/include/v8-version-string.h"
 
 namespace {
 const char kQtWebEngineVersion[] = "qtwebengine_version";
 const char kQtWebEngineChromiumVersion[] = "qtwebengine_chromium_version";
 const char kQtWebEngineChromiumSecurityPatchVersion[] =
         "qtwebengine_chromium_security_patch_version";
+const char kQtWebEngineChromiumV8Version[] = "qtwebengine_chromium_v8_version";
 const char kCommandLine[] = "command_line";
 const char kQtVersionCSS[] = "qt_version.css";
 const char kQtLogo[] = "images/qt.png";
@@ -23,7 +26,6 @@ const char kQtWebEngineLogo[] = "images/qtwebengine.png";
 
 VersionUIQt::VersionUIQt(content::WebUI *web_ui) : content::WebUIController(web_ui)
 {
-
     Profile *profile = Profile::FromWebUI(web_ui);
     content::WebUIDataSource *html_source =
             content::WebUIDataSource::CreateAndAdd(profile, chrome::kChromeUIVersionQtHost);
@@ -39,6 +41,7 @@ VersionUIQt::VersionUIQt(content::WebUI *web_ui) : content::WebUIController(web_
     html_source->AddString(kQtWebEngineChromiumVersion, qWebEngineChromiumVersion());
     html_source->AddString(kQtWebEngineChromiumSecurityPatchVersion,
                            qWebEngineChromiumSecurityPatchVersion());
+    html_source->AddString(kQtWebEngineChromiumV8Version, V8_VERSION_STRING);
 #if BUILDFLAG(IS_WIN)
     html_source->AddString(
             kCommandLine,
diff --git a/src/core/net/version_ui_qt.h b/src/core/net/version_ui_qt.h
index 1fe8ef9e0a6..b974cd0b00b 100644
--- a/src/core/net/version_ui_qt.h
+++ b/src/core/net/version_ui_qt.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2023 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 //
 //  W A R N I N G
diff --git a/src/core/net/webui_controller_factory_qt.cpp b/src/core/net/webui_controller_factory_qt.cpp
index ed35a3e36fe..d151c269051 100644
--- a/src/core/net/webui_controller_factory_qt.cpp
+++ b/src/core/net/webui_controller_factory_qt.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2017 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 // Based on chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc:
 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
@@ -14,8 +15,8 @@
 #include "build/build_config.h"
 #include "chrome/browser/accessibility/accessibility_ui.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/device_log_ui.h"
-#include "chrome/browser/ui/webui/devtools_ui.h"
+#include "chrome/browser/ui/webui/device_log/device_log_ui.h"
+#include "chrome/browser/ui/webui/devtools/devtools_ui.h"
 #include "chrome/browser/ui/webui/net_internals/net_internals_ui.h"
 #include "chrome/browser/ui/webui/user_actions/user_actions_ui.h"
 #include "chrome/common/url_constants.h"
diff --git a/src/core/net/webui_controller_factory_qt.h b/src/core/net/webui_controller_factory_qt.h
index 22219dd5a4c..dd78d1963c8 100644
--- a/src/core/net/webui_controller_factory_qt.h
+++ b/src/core/net/webui_controller_factory_qt.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2017 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #ifndef WEB_UI_CONTROLLER_FACTORY_QT_H_
 #define WEB_UI_CONTROLLER_FACTORY_QT_H_
diff --git a/src/core/ozone/REUSE.toml b/src/core/ozone/REUSE.toml
new file mode 100644
index 00000000000..e57f56e4b90
--- /dev/null
+++ b/src/core/ozone/REUSE.toml
@@ -0,0 +1,7 @@
+version = 1
+
+[[annotations]]
+path = ["BUILD.gn", "ozone_extra.gni"]
+precedence = "closest"
+SPDX-FileCopyrightText = "Copyright 2016 The Chromium Authors. All rights reserved."
+SPDX-License-Identifier = "BSD-3-Clause"
diff --git a/src/core/ozone/gl_context_qt.cpp b/src/core/ozone/gl_context_qt.cpp
index 2e358c0fbb0..ab6717b5e19 100644
--- a/src/core/ozone/gl_context_qt.cpp
+++ b/src/core/ozone/gl_context_qt.cpp
@@ -2,6 +2,7 @@
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 
 #include "gl_context_qt.h"
+#include "web_engine_context.h"
 
 #include <QGuiApplication>
 #include <QOpenGLContext>
@@ -75,23 +76,6 @@ void GLContextHelper::destroy()
     contextHelper = nullptr;
 }
 
-bool GLContextHelper::initializeContextOnBrowserThread(gl::GLContext* context, gl::GLSurface* surface, gl::GLContextAttribs attribs)
-{
-    return context->Initialize(surface, attribs);
-}
-
-bool GLContextHelper::initializeContext(gl::GLContext* context, gl::GLSurface* surface, gl::GLContextAttribs attribs)
-{
-    bool ret = false;
-    Qt::ConnectionType connType = (QThread::currentThread() == qApp->thread()) ? Qt::DirectConnection : Qt::BlockingQueuedConnection;
-    QMetaObject::invokeMethod(contextHelper, "initializeContextOnBrowserThread", connType,
-            Q_RETURN_ARG(bool, ret),
-            Q_ARG(gl::GLContext*, context),
-            Q_ARG(gl::GLSurface*, surface),
-            Q_ARG(gl::GLContextAttribs, attribs));
-    return ret;
-}
-
 void* GLContextHelper::getEGLConfig()
 {
     QByteArray resource = QByteArrayLiteral("eglconfig");
@@ -175,15 +159,20 @@ bool GLContextHelper::isCreateContextRobustnessSupported()
     return contextHelper->m_robustness;
 }
 
-#if QT_CONFIG(opengl) && defined(USE_OZONE)
+#if QT_CONFIG(opengl) && QT_CONFIG(egl) && defined(USE_OZONE)
 class ScopedGLContext
 {
 public:
-    ScopedGLContext(QOffscreenSurface *surface)
-        : m_context(new QOpenGLContext())
-        , m_previousContext(gl::GLContext::GetCurrent())
-        , m_previousSurface(gl::GLSurface::GetCurrent())
+    ScopedGLContext(QOffscreenSurface *surface) : m_context(new QOpenGLContext())
     {
+        if (gl::GLContext::GetCurrent()) {
+            auto eglFun = EGLHelper::instance()->functions();
+            m_previousEGLContext = eglFun->eglGetCurrentContext();
+            m_previousEGLDrawSurface = eglFun->eglGetCurrentSurface(EGL_DRAW);
+            m_previousEGLReadSurface = eglFun->eglGetCurrentSurface(EGL_READ);
+            m_previousEGLDisplay = eglFun->eglGetCurrentDisplay();
+        }
+
         if (!m_context->create()) {
             qWarning("Failed to create OpenGL context.");
             return;
@@ -203,8 +192,18 @@ class ScopedGLContext
             glFun->glDeleteTextures(m_textures.size(), m_textures.data());
         }
 
-        if (m_previousContext)
-            m_previousContext->MakeCurrent(m_previousSurface);
+        if (m_previousEGLContext) {
+            // Make sure the scoped context is not current when restoring the previous
+            // EGL context otherwise the QOpenGLContext destructor resets the restored
+            // current context.
+            m_context->doneCurrent();
+
+            auto eglFun = EGLHelper::instance()->functions();
+            eglFun->eglMakeCurrent(m_previousEGLDisplay, m_previousEGLDrawSurface,
+                                   m_previousEGLReadSurface, m_previousEGLContext);
+            if (eglFun->eglGetError() != EGL_SUCCESS)
+                qWarning("Failed to restore EGL context.");
+        }
     }
 
     bool isValid() const { return m_context->isValid() && (m_context->surface() != nullptr); }
@@ -240,11 +239,15 @@ class ScopedGLContext
 
 private:
     QScopedPointer<QOpenGLContext> m_context;
-    gl::GLContext *m_previousContext;
-    gl::GLSurface *m_previousSurface;
+    EGLContext m_previousEGLContext = nullptr;
+    EGLSurface m_previousEGLDrawSurface = nullptr;
+    EGLSurface m_previousEGLReadSurface = nullptr;
+    EGLDisplay m_previousEGLDisplay = nullptr;
     std::vector<uint> m_textures;
 };
+#endif // QT_CONFIG(opengl) && QT_COFNIG(egl) && defined(USE_OZONE)
 
+#if QT_CONFIG(opengl) && defined(USE_OZONE)
 EGLHelper::EGLFunctions::EGLFunctions()
 {
     const static auto getProcAddress =
@@ -252,11 +255,18 @@ EGLHelper::EGLFunctions::EGLFunctions()
 
     eglCreateImage = reinterpret_cast<PFNEGLCREATEIMAGEPROC>(getProcAddress("eglCreateImage"));
     eglDestroyImage = reinterpret_cast<PFNEGLDESTROYIMAGEPROC>(getProcAddress("eglDestroyImage"));
-    eglGetError = reinterpret_cast<PFNEGLGETERRORPROC>(getProcAddress("eglGetError"));
     eglExportDMABUFImageMESA = reinterpret_cast<PFNEGLEXPORTDMABUFIMAGEMESAPROC>(
             getProcAddress("eglExportDMABUFImageMESA"));
     eglExportDMABUFImageQueryMESA = reinterpret_cast<PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC>(
             getProcAddress("eglExportDMABUFImageQueryMESA"));
+    eglGetCurrentContext =
+            reinterpret_cast<PFNEGLGETCURRENTCONTEXTPROC>(getProcAddress("eglGetCurrentContext"));
+    eglGetCurrentDisplay =
+            reinterpret_cast<PFNEGLGETCURRENTDISPLAYPROC>(getProcAddress("eglGetCurrentDisplay"));
+    eglGetCurrentSurface =
+            reinterpret_cast<PFNEGLGETCURRENTSURFACEPROC>(getProcAddress("eglGetCurrentSurface"));
+    eglGetError = reinterpret_cast<PFNEGLGETERRORPROC>(getProcAddress("eglGetError"));
+    eglMakeCurrent = reinterpret_cast<PFNEGLMAKECURRENTPROC>(getProcAddress("eglMakeCurrent"));
     eglQueryString = reinterpret_cast<PFNEGLQUERYSTRINGPROC>(getProcAddress("eglQueryString"));
 }
 
@@ -289,23 +299,14 @@ EGLHelper::EGLHelper()
     Q_ASSERT(QThread::currentThread() == qApp->thread());
     m_offscreenSurface->create();
 
-    const char *displayExtensions = m_functions->eglQueryString(eglDisplay, EGL_EXTENSIONS);
-    m_isDmaBufSupported = strstr(displayExtensions, "EGL_EXT_image_dma_buf_import")
-            && strstr(displayExtensions, "EGL_EXT_image_dma_buf_import_modifiers")
-            && strstr(displayExtensions, "EGL_MESA_image_dma_buf_export");
+    m_isDmaBufSupported = QtWebEngineCore::WebEngineContext::isGbmSupported();
 
+    // Check extensions.
     if (m_isDmaBufSupported) {
-        // FIXME: This disables GBM for nvidia. Remove this when nvidia fixes its GBM support.
-        //
-        // "Buffer allocation and submission to DRM KMS using gbm is not currently supported."
-        // See: https://download.nvidia.com/XFree86/Linux-x86_64/550.40.07/README/kms.html
-        //
-        // Chromium uses GBM to allocate scanout buffers. Scanout requires DRM KMS. If KMS is
-        // enabled, gbm_device and gbm_buffer are created without any issues but rendering to the
-        // buffer will malfunction. It is not known how to detect this problem before rendering
-        // so we just disable GBM for nvidia.
-        const char *displayVendor = m_functions->eglQueryString(eglDisplay, EGL_VENDOR);
-        m_isDmaBufSupported = !strstr(displayVendor, "NVIDIA");
+        const char *displayExtensions = m_functions->eglQueryString(eglDisplay, EGL_EXTENSIONS);
+        m_isDmaBufSupported = strstr(displayExtensions, "EGL_EXT_image_dma_buf_import")
+                && strstr(displayExtensions, "EGL_EXT_image_dma_buf_import_modifiers")
+                && strstr(displayExtensions, "EGL_MESA_image_dma_buf_export");
     }
 
     // Try to create dma-buf.
@@ -322,6 +323,7 @@ EGLHelper::EGLHelper()
 void EGLHelper::queryDmaBuf(const int width, const int height, int *fd, int *stride, int *offset,
                             uint64_t *modifiers)
 {
+#if QT_CONFIG(egl)
     if (!m_isDmaBufSupported)
         return;
 
@@ -340,23 +342,24 @@ void EGLHelper::queryDmaBuf(const int width, const int height, int *fd, int *str
     EGLImage eglImage = m_functions->eglCreateImage(eglDisplay, eglContext, EGL_GL_TEXTURE_2D,
                                                     (EGLClientBuffer)textureId, NULL);
     if (eglImage == EGL_NO_IMAGE) {
-        qWarning() << "EGL: Failed to create EGLImage:"
-                   << ui::GetEGLErrorString(m_functions->eglGetError());
+        qWarning("EGL: Failed to create EGLImage: %s",
+                 ui::GetEGLErrorString(m_functions->eglGetError()));
         return;
     }
 
     int numPlanes = 0;
     if (!m_functions->eglExportDMABUFImageQueryMESA(eglDisplay, eglImage, nullptr, &numPlanes,
                                                     modifiers))
-        qWarning() << "EGL: Failed to retrieve the pixel format of the buffer:"
-                   << ui::GetEGLErrorString(m_functions->eglGetError());
+        qWarning("EGL: Failed to retrieve the pixel format of the buffer: %s",
+                 ui::GetEGLErrorString(m_functions->eglGetError()));
     Q_ASSERT(numPlanes == 1);
 
     if (!m_functions->eglExportDMABUFImageMESA(eglDisplay, eglImage, fd, stride, offset))
-        qWarning() << "EGL: Failed to retrieve the dma_buf file descriptor:"
-                   << ui::GetEGLErrorString(m_functions->eglGetError());
+        qWarning("EGL: Failed to retrieve the dma_buf file descriptor: %s",
+                 ui::GetEGLErrorString(m_functions->eglGetError()));
 
     m_functions->eglDestroyImage(eglDisplay, eglImage);
+#endif // QT_CONFIG(egl)
 }
 
 bool EGLHelper::isDmaBufSupported()
@@ -384,7 +387,10 @@ scoped_refptr<GLContext> CreateGLContext(GLShareGroup *share_group,
         return context;
     }
     case kGLImplementationEGLANGLE:
-    case kGLImplementationEGLGLES2:
+        if (Q_UNLIKELY(!compatible_surface)) {
+            qWarning("EGL: no compatible surface.");
+            return nullptr;
+        }
         return InitializeGLContext(new GLContextEGL(share_group),
                                    compatible_surface, attribs);
     case kGLImplementationDisabled:
diff --git a/src/core/ozone/gl_context_qt.h b/src/core/ozone/gl_context_qt.h
index 41c6a5f0c9b..81c08a65073 100644
--- a/src/core/ozone/gl_context_qt.h
+++ b/src/core/ozone/gl_context_qt.h
@@ -28,7 +28,6 @@ class GLContextHelper : public QObject {
 public:
     static void initialize();
     static void destroy();
-    static bool initializeContext(gl::GLContext* context, gl::GLSurface* surface, gl::GLContextAttribs attribs);
 
     static void* getEGLConfig();
     static void* getGlXConfig();
@@ -42,8 +41,6 @@ class GLContextHelper : public QObject {
     static void *getEglPlatformInterface();
 
 private:
-    Q_INVOKABLE bool initializeContextOnBrowserThread(gl::GLContext* context, gl::GLSurface* surface, gl::GLContextAttribs attribs);
-
     static GLContextHelper* contextHelper;
     bool m_robustness = false;
 };
@@ -53,7 +50,11 @@ class GLContextHelper : public QObject {
 #undef eglDestroyImage
 #undef eglExportDMABUFImageMESA
 #undef eglExportDMABUFImageQueryMESA
+#undef eglGetCurrentContext
+#undef eglGetCurrentDisplay
+#undef eglGetCurrentSurface
 #undef eglGetError
+#undef eglMakeCurrent
 #undef eglQueryString
 
 class EGLHelper
@@ -67,7 +68,11 @@ class EGLHelper
         PFNEGLDESTROYIMAGEPROC eglDestroyImage;
         PFNEGLEXPORTDMABUFIMAGEMESAPROC eglExportDMABUFImageMESA;
         PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC eglExportDMABUFImageQueryMESA;
+        PFNEGLGETCURRENTCONTEXTPROC eglGetCurrentContext;
+        PFNEGLGETCURRENTDISPLAYPROC eglGetCurrentDisplay;
+        PFNEGLGETCURRENTSURFACEPROC eglGetCurrentSurface;
         PFNEGLGETERRORPROC eglGetError;
+        PFNEGLMAKECURRENTPROC eglMakeCurrent;
         PFNEGLQUERYSTRINGPROC eglQueryString;
     };
 
diff --git a/src/core/ozone/gl_ozone_angle_qt.cpp b/src/core/ozone/gl_ozone_angle_qt.cpp
new file mode 100644
index 00000000000..13258e4247f
--- /dev/null
+++ b/src/core/ozone/gl_ozone_angle_qt.cpp
@@ -0,0 +1,164 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+// Copyright 2016 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#if defined(USE_OZONE)
+#include "gl_context_qt.h"
+#include "gl_ozone_angle_qt.h"
+#include "gl_surface_egl_qt.h"
+
+#include "ui/base/ozone_buildflags.h"
+#include "ui/gl/gl_bindings.h"
+#include "ui/gl/gl_display.h"
+#include "ui/gl/gl_surface.h"
+#include "ui/gl/gl_utils.h"
+#include "ui/ozone/common/native_pixmap_egl_binding.h"
+
+#if BUILDFLAG(IS_OZONE_X11)
+#include "ui/gl/gl_glx_api_implementation.h"
+
+#include "ui/ozone/platform/x11/native_pixmap_egl_x11_binding.h"
+#endif
+
+extern "C" {
+typedef void (*__eglMustCastToProperFunctionPointerType)(void);
+extern __eglMustCastToProperFunctionPointerType EGL_GetProcAddress(const char *procname);
+}
+
+namespace ui {
+namespace {
+// Based on //ui/ozone/platform/x11/x11_surface_factory.cc
+enum class NativePixmapSupportType {
+    // Importing native pixmaps not supported.
+    kNone,
+
+    // Native pixmaps are imported directly into EGL using the
+    // EGL_EXT_image_dma_buf_import extension.
+    kDMABuf,
+
+    // Native pixmaps are first imported as X11 pixmaps using DRI3 and then into
+    // EGL.
+    kX11Pixmap,
+};
+
+NativePixmapSupportType GetNativePixmapSupportType()
+{
+    if (gl::GLSurfaceEGL::GetGLDisplayEGL()->ext->b_EGL_EXT_image_dma_buf_import)
+        return NativePixmapSupportType::kDMABuf;
+
+#if BUILDFLAG(IS_OZONE_X11)
+    if (NativePixmapEGLX11Binding::CanImportNativeGLXPixmap())
+        return NativePixmapSupportType::kX11Pixmap;
+#endif
+
+    return NativePixmapSupportType::kNone;
+}
+} // namespace
+
+bool GLOzoneANGLEQt::LoadGLES2Bindings(const gl::GLImplementationParts & /*implementation*/)
+{
+    gl::SetGLGetProcAddressProc(&EGL_GetProcAddress);
+    return true;
+}
+
+bool GLOzoneANGLEQt::InitializeStaticGLBindings(const gl::GLImplementationParts &implementation)
+{
+    bool res = GLOzoneEGL::InitializeStaticGLBindings(implementation);
+
+#if BUILDFLAG(IS_OZONE_X11)
+    if (GLContextHelper::getGlxPlatformInterface()) {
+        gl::SetGLGetProcAddressProc(reinterpret_cast<gl::GLGetProcAddressProc>(
+                GLContextHelper::getGlXGetProcAddress()));
+        gl::InitializeStaticGLBindingsGLX();
+        gl::SetGLGetProcAddressProc(&EGL_GetProcAddress);
+    }
+#endif
+
+    return res;
+}
+
+bool GLOzoneANGLEQt::InitializeExtensionSettingsOneOffPlatform(gl::GLDisplay *display)
+{
+    bool res = GLOzoneEGL::InitializeExtensionSettingsOneOffPlatform(
+            static_cast<gl::GLDisplayEGL *>(display));
+
+#if BUILDFLAG(IS_OZONE_X11)
+    if (GLContextHelper::getGlxPlatformInterface()) {
+        gl::SetGLGetProcAddressProc(reinterpret_cast<gl::GLGetProcAddressProc>(
+                GLContextHelper::getGlXGetProcAddress()));
+        std::string extensions =
+                glXQueryExtensionsString((struct _XDisplay *)GLContextHelper::getXDisplay(), 0);
+        gl::g_driver_glx.InitializeExtensionBindings(extensions.c_str());
+        gl::SetGLGetProcAddressProc(&EGL_GetProcAddress);
+    }
+#endif
+
+    return res;
+}
+
+scoped_refptr<gl::GLSurface> GLOzoneANGLEQt::CreateViewGLSurface(gl::GLDisplay *display,
+                                                                 gfx::AcceleratedWidget window)
+{
+    Q_UNUSED(display);
+    Q_UNUSED(window);
+    return nullptr;
+}
+
+// based on GLOzoneEGLX11::CreateOffscreenGLSurface() (x11_surface_factory.cc)
+scoped_refptr<gl::GLSurface> GLOzoneANGLEQt::CreateOffscreenGLSurface(gl::GLDisplay *display,
+                                                                      const gfx::Size &size)
+{
+    gl::GLDisplayEGL *eglDisplay = display->GetAs<gl::GLDisplayEGL>();
+
+    if (eglDisplay->IsEGLSurfacelessContextSupported() && size.width() == 0 && size.height() == 0)
+        return InitializeGLSurface(new gl::SurfacelessEGL(eglDisplay, size));
+
+    return InitializeGLSurface(new gl::PbufferGLSurfaceEGL(eglDisplay, size));
+}
+
+gl::EGLDisplayPlatform GLOzoneANGLEQt::GetNativeDisplay()
+{
+#if BUILDFLAG(IS_OZONE_X11)
+    void *xdisplay = GLContextHelper::getXDisplay();
+    if (xdisplay)
+        return gl::EGLDisplayPlatform(reinterpret_cast<EGLNativeDisplayType>(xdisplay));
+#endif
+
+    if (gl::g_driver_egl.client_ext.b_EGL_MESA_platform_surfaceless)
+        return gl::EGLDisplayPlatform(EGL_DEFAULT_DISPLAY, EGL_PLATFORM_SURFACELESS_MESA);
+
+    return gl::EGLDisplayPlatform(EGL_DEFAULT_DISPLAY);
+}
+
+bool GLOzoneANGLEQt::CanImportNativePixmap()
+{
+    return GetNativePixmapSupportType() != NativePixmapSupportType::kNone;
+}
+
+std::unique_ptr<NativePixmapGLBinding>
+GLOzoneANGLEQt::ImportNativePixmap(scoped_refptr<gfx::NativePixmap> pixmap,
+                                   gfx::BufferFormat plane_format, gfx::BufferPlane plane,
+                                   gfx::Size plane_size, const gfx::ColorSpace &color_space,
+                                   GLenum target, GLuint texture_id)
+{
+    switch (GetNativePixmapSupportType()) {
+    case NativePixmapSupportType::kDMABuf:
+        return NativePixmapEGLBinding::Create(pixmap, plane_format, plane, plane_size, color_space,
+                                              target, texture_id);
+#if BUILDFLAG(IS_OZONE_X11)
+    case NativePixmapSupportType::kX11Pixmap:
+        return NativePixmapEGLX11Binding::Create(pixmap, plane_format, plane_size, target,
+                                                 texture_id);
+#endif
+    default:
+        NOTREACHED();
+        return nullptr;
+    }
+}
+
+} // namespace ui
+
+#endif // defined(USE_OZONE)
diff --git a/src/core/ozone/gl_ozone_angle_qt.h b/src/core/ozone/gl_ozone_angle_qt.h
new file mode 100644
index 00000000000..bba276fc09b
--- /dev/null
+++ b/src/core/ozone/gl_ozone_angle_qt.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef GL_OZONE_ANGLE_QT_H
+#define GL_OZONE_ANGLE_QT_H
+
+#if defined(USE_OZONE)
+#include "ui/ozone/common/gl_ozone_egl.h"
+
+namespace ui {
+
+class GLOzoneANGLEQt : public GLOzoneEGL
+{
+public:
+    bool InitializeStaticGLBindings(const gl::GLImplementationParts &implementation) override;
+    bool InitializeExtensionSettingsOneOffPlatform(gl::GLDisplay *display) override;
+    scoped_refptr<gl::GLSurface> CreateViewGLSurface(gl::GLDisplay *display,
+                                                     gfx::AcceleratedWidget window) override;
+    scoped_refptr<gl::GLSurface> CreateOffscreenGLSurface(gl::GLDisplay *display,
+                                                          const gfx::Size &size) override;
+    bool CanImportNativePixmap() override;
+    std::unique_ptr<NativePixmapGLBinding>
+    ImportNativePixmap(scoped_refptr<gfx::NativePixmap> pixmap, gfx::BufferFormat plane_format,
+                       gfx::BufferPlane plane, gfx::Size plane_size,
+                       const gfx::ColorSpace &color_space, GLenum target,
+                       GLuint texture_id) override;
+
+protected:
+    // Returns native platform display handle. This is used to obtain the EGL
+    // display connection for the native display.
+    gl::EGLDisplayPlatform GetNativeDisplay() override;
+
+    // Sets up GL bindings for the native surface.
+    bool LoadGLES2Bindings(const gl::GLImplementationParts &implementation) override;
+};
+
+} // namespace ui
+
+#endif // defined(USE_OZONE)
+
+#endif // GL_OZONE_ANGLE_QT_H
diff --git a/src/core/ozone/gl_ozone_egl_qt.cpp b/src/core/ozone/gl_ozone_egl_qt.cpp
index 26d11df3157..8cc79384151 100644
--- a/src/core/ozone/gl_ozone_egl_qt.cpp
+++ b/src/core/ozone/gl_ozone_egl_qt.cpp
@@ -75,7 +75,8 @@ scoped_refptr<gl::GLSurface> GLOzoneEGLQt::CreateOffscreenGLSurface(gl::GLDispla
 gl::EGLDisplayPlatform GLOzoneEGLQt::GetNativeDisplay()
 {
     static void *display = GLContextHelper::getNativeDisplay();
-    static gl::EGLDisplayPlatform platform(display ? reinterpret_cast<intptr_t>(display) : EGL_DEFAULT_DISPLAY);
+    static gl::EGLDisplayPlatform platform(display ? reinterpret_cast<EGLNativeDisplayType>(display)
+                                                   : EGL_DEFAULT_DISPLAY);
     return platform;
 }
 
diff --git a/src/core/ozone/gl_share_context_qt.cpp b/src/core/ozone/gl_share_context_qt.cpp
index b1c5e201f10..56b50084aa5 100644
--- a/src/core/ozone/gl_share_context_qt.cpp
+++ b/src/core/ozone/gl_share_context_qt.cpp
@@ -20,9 +20,6 @@ QtShareGLContext::QtShareGLContext(QOpenGLContext *context)
     : gl::GLContext(nullptr), m_handle(nullptr)
 {
 #if QT_CONFIG(opengl)
-#if defined(Q_OS_MACOS)
-    qFatal("macOS only support using ANGLE.");
-#endif
 #if defined(Q_OS_WIN)
     auto *win_ctx = context->nativeInterface<QNativeInterface::QWGLContext>();
     if (win_ctx && !m_handle)
@@ -43,6 +40,11 @@ QtShareGLContext::QtShareGLContext(QOpenGLContext *context)
 #endif // QT_CONFIG(opengl)
 }
 
+QtShareGLContext::~QtShareGLContext()
+{
+    OnContextWillDestroy();
+}
+
 unsigned int QtShareGLContext::CheckStickyGraphicsResetStatusImpl()
 {
 #if QT_CONFIG(opengl)
diff --git a/src/core/ozone/gl_share_context_qt.h b/src/core/ozone/gl_share_context_qt.h
index 89be0042118..6b0546a72ea 100644
--- a/src/core/ozone/gl_share_context_qt.h
+++ b/src/core/ozone/gl_share_context_qt.h
@@ -18,10 +18,11 @@ class QtShareGLContext : public gl::GLContext
 
 public:
     QtShareGLContext(QOpenGLContext *qtContext);
+    ~QtShareGLContext() override;
     void *GetHandle() override { return m_handle; }
     unsigned int CheckStickyGraphicsResetStatusImpl() override;
     // We don't care about the rest, this context shouldn't be used except for its handle.
-    bool Initialize(gl::GLSurface *, const gl::GLContextAttribs &) override
+    bool InitializeImpl(gl::GLSurface *, const gl::GLContextAttribs &) override
     {
         Q_UNREACHABLE();
         return false;
diff --git a/src/core/ozone/gl_surface_egl_qt.cpp b/src/core/ozone/gl_surface_egl_qt.cpp
index a0c120ac61c..0ac02681dd3 100644
--- a/src/core/ozone/gl_surface_egl_qt.cpp
+++ b/src/core/ozone/gl_surface_egl_qt.cpp
@@ -14,7 +14,7 @@
 #include "ui/gl/gl_display_manager.h"
 #include "ui/gl/init/gl_factory.h"
 
-#if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_WIN)
+#if defined(USE_OZONE)
 
 using ui::GetLastEGLErrorString;
 
@@ -47,6 +47,23 @@ gl::GLDisplay *GLSurfaceEGLQt::InitializeOneOff(gl::GpuPreference preference)
         return nullptr;
     }
 
+    egl_display->ext->InitializeExtensionSettings(egl_display->GetDisplay());
+    if (egl_display->ext->b_EGL_EXT_create_context_robustness) {
+        egl_display->ext->b_EGL_EXT_create_context_robustness =
+                GLContextHelper::isCreateContextRobustnessSupported();
+    }
+
+#if QT_CONFIG(opengl)
+    if (egl_display->ext->b_EGL_EXT_image_dma_buf_import
+        || egl_display->ext->b_EGL_EXT_image_dma_buf_import_modifiers
+        || egl_display->ext->b_EGL_MESA_image_dma_buf_export) {
+        const bool dmaBufSupported = EGLHelper::instance()->isDmaBufSupported();
+        egl_display->ext->b_EGL_EXT_image_dma_buf_import = dmaBufSupported;
+        egl_display->ext->b_EGL_EXT_image_dma_buf_import_modifiers = dmaBufSupported;
+        egl_display->ext->b_EGL_MESA_image_dma_buf_export = dmaBufSupported;
+    }
+#endif
+
     g_config = GLContextHelper::getEGLConfig();
     if (!g_config) {
         LOG(ERROR) << "GLContextHelper::getEGLConfig() failed.";
@@ -195,4 +212,4 @@ void* GLSurfacelessQtEGL::GetShareHandle()
 }
 
 } // namespace gl
-#endif // !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_WIN)
+#endif // defined(USE_OZONE)
diff --git a/src/core/ozone/gl_surface_qt.cpp b/src/core/ozone/gl_surface_qt.cpp
index 0cbe75cbd3e..68aa35df3e7 100644
--- a/src/core/ozone/gl_surface_qt.cpp
+++ b/src/core/ozone/gl_surface_qt.cpp
@@ -7,28 +7,10 @@
 
 #include "qtwebenginecoreglobal_p.h"
 
-#if !defined(Q_OS_MACOS)
-
 #include "gl_surface_qt.h"
 
 #include "base/logging.h"
 
-#if BUILDFLAG(IS_WIN)
-#include "web_engine_context.h"
-#include "ozone/gl_surface_wgl_qt.h"
-
-#include "gpu/ipc/service/image_transport_surface.h"
-#include "ui/gl/init/gl_display_initializer.h"
-#include "ui/gl/direct_composition_support.h"
-#include "ui/gl/gl_angle_util_win.h"
-#include "ui/gl/gl_display.h"
-#include "ui/gl/gl_implementation.h"
-#include "ui/gl/gl_surface_egl.h"
-#include "ui/gl/gl_utils.h"
-#include "ui/gl/vsync_provider_win.h"
-#endif
-
-
 namespace gl {
 
 GLDisplay *GLSurfaceQt::g_display = nullptr;
@@ -89,80 +71,4 @@ void* GLSurfaceQt::GetConfig()
     return g_config;
 }
 
-#if BUILDFLAG(IS_WIN)
-namespace init {
-
-gl::GLDisplay *InitializeGLOneOffPlatform(gl::GpuPreference gpu_preference)
-{
-    VSyncProviderWin::InitializeOneOff();
-
-    if (GetGLImplementation() == kGLImplementationDesktopGL || GetGLImplementation() == kGLImplementationDesktopGLCoreProfile)
-        return GLSurfaceWGLQt::InitializeOneOff(gpu_preference);
-
-    GLDisplayEGL *display = GetDisplayEGL(gpu_preference);
-    switch (GetGLImplementation()) {
-    case kGLImplementationEGLANGLE:
-    case kGLImplementationEGLGLES2:
-        if (!InitializeDisplay(display, EGLDisplayPlatform(GetDC(nullptr)))) {
-            LOG(ERROR) << "GLDisplayEGL::Initialize failed.";
-            return nullptr;
-        }
-        if (auto d3d11_device = QueryD3D11DeviceObjectFromANGLE())
-            InitializeDirectComposition(std::move(d3d11_device));
-        break;
-    case kGLImplementationMockGL:
-    case kGLImplementationStubGL:
-        break;
-    default:
-        NOTREACHED();
-    }
-    return display;
-}
-
-bool usingSoftwareDynamicGL()
-{
-#if QT_CONFIG(opengl)
-    return QtWebEngineCore::usingSoftwareDynamicGL();
-#else
-    return false;
-#endif // QT_CONFIG(opengl)
-}
-
-scoped_refptr<GLSurface>
-CreateOffscreenGLSurfaceWithFormat(GLDisplay *display, const gfx::Size& size, GLSurfaceFormat format)
-{
-    scoped_refptr<GLSurface> surface;
-    switch (GetGLImplementation()) {
-    case kGLImplementationDesktopGLCoreProfile:
-    case kGLImplementationDesktopGL: {
-        surface = new GLSurfaceWGLQt(size);
-        if (surface->Initialize(format))
-            return surface;
-        break;
-    }
-    case kGLImplementationEGLANGLE:
-    case kGLImplementationEGLGLES2: {
-        GLDisplayEGL *display_egl = display->GetAs<gl::GLDisplayEGL>();
-        if (display_egl->IsEGLSurfacelessContextSupported() && size.width() == 0 && size.height() == 0)
-            return InitializeGLSurfaceWithFormat(new SurfacelessEGL(display_egl, size), format);
-        return InitializeGLSurfaceWithFormat(new PbufferGLSurfaceEGL(display_egl, size), format);
-    }
-    default:
-        break;
-    }
-    LOG(ERROR) << "Requested OpenGL implementation is not supported. Implementation: " << GetGLImplementation();
-    Q_UNREACHABLE();
-    return nullptr;
-}
-
-scoped_refptr<GLSurface>
-CreateViewGLSurface(GLDisplay *display, gfx::AcceleratedWidget window)
-{
-    return nullptr;
-}
-
-} // namespace init
-#endif  // BUILDFLAG(IS_WIN)
 } // namespace gl
-
-#endif // !defined(Q_OS_MACOS)
diff --git a/src/core/ozone/gl_surface_wgl_qt.cpp b/src/core/ozone/gl_surface_wgl_qt.cpp
index db4aed88461..afd68e5a263 100644
--- a/src/core/ozone/gl_surface_wgl_qt.cpp
+++ b/src/core/ozone/gl_surface_wgl_qt.cpp
@@ -2,10 +2,18 @@
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 
 #include "gl_surface_wgl_qt.h"
+#include "web_engine_context.h"
 
-#if BUILDFLAG(IS_WIN)
+#include "ui/gl/init/gl_display_initializer.h"
+#include "ui/gl/direct_composition_support.h"
+#include "ui/gl/gl_angle_util_win.h"
+#include "ui/gl/gl_display.h"
 #include "ui/gl/gl_display_manager.h"
+#include "ui/gl/gl_implementation.h"
+#include "ui/gl/gl_surface_egl.h"
 #include "ui/gl/gl_surface_wgl.h"
+#include "ui/gl/gl_utils.h"
+#include "ui/gl/vsync_provider_win.h"
 
 namespace gl {
 
@@ -55,6 +63,74 @@ void *GLSurfaceWGLQt::GetConfig()
     return m_surfaceBuffer->GetConfig();
 }
 
-} //namespace gl
+// Overrides for WGL:
+namespace init {
 
-#endif // BUILDFLAG(IS_WIN)
+gl::GLDisplay *InitializeGLOneOffPlatform(gl::GpuPreference gpu_preference)
+{
+    VSyncProviderWin::InitializeOneOff();
+
+    if (GetGLImplementation() == kGLImplementationDesktopGL || GetGLImplementation() == kGLImplementationDesktopGLCoreProfile)
+        return GLSurfaceWGLQt::InitializeOneOff(gpu_preference);
+
+    GLDisplayEGL *display = GetDisplayEGL(gpu_preference);
+    switch (GetGLImplementation()) {
+    case kGLImplementationEGLANGLE:
+        if (!InitializeDisplay(display, EGLDisplayPlatform(GetDC(nullptr)))) {
+            LOG(ERROR) << "GLDisplayEGL::Initialize failed.";
+            return nullptr;
+        }
+        if (auto d3d11_device = QueryD3D11DeviceObjectFromANGLE())
+            InitializeDirectComposition(std::move(d3d11_device));
+        break;
+    case kGLImplementationMockGL:
+    case kGLImplementationStubGL:
+        break;
+    default:
+        NOTREACHED();
+    }
+    return display;
+}
+
+bool usingSoftwareDynamicGL()
+{
+#if QT_CONFIG(opengl)
+    return QtWebEngineCore::usingSoftwareDynamicGL();
+#else
+    return false;
+#endif // QT_CONFIG(opengl)
+}
+
+scoped_refptr<GLSurface> CreateOffscreenGLSurface(GLDisplay *display, const gfx::Size &size)
+{
+    scoped_refptr<GLSurface> surface;
+    switch (GetGLImplementation()) {
+    case kGLImplementationDesktopGLCoreProfile:
+    case kGLImplementationDesktopGL: {
+        surface = new GLSurfaceWGLQt(size);
+        if (surface->Initialize(GLSurfaceFormat()))
+            return surface;
+        break;
+    }
+    case kGLImplementationEGLANGLE: {
+        GLDisplayEGL *display_egl = display->GetAs<gl::GLDisplayEGL>();
+        if (display_egl->IsEGLSurfacelessContextSupported() && size.width() == 0 && size.height() == 0)
+            return InitializeGLSurface(new SurfacelessEGL(display_egl, size));
+        return InitializeGLSurface(new PbufferGLSurfaceEGL(display_egl, size));
+    }
+    default:
+        break;
+    }
+    LOG(ERROR) << "Requested OpenGL implementation is not supported. Implementation: " << GetGLImplementation();
+    Q_UNREACHABLE();
+    return nullptr;
+}
+
+scoped_refptr<GLSurface>
+CreateViewGLSurface(GLDisplay *display, gfx::AcceleratedWidget window)
+{
+    return nullptr;
+}
+
+} // namespace init
+} // namespace gl
diff --git a/src/core/ozone/gl_surface_wgl_qt.h b/src/core/ozone/gl_surface_wgl_qt.h
index 6c590e46c6d..014b77d766e 100644
--- a/src/core/ozone/gl_surface_wgl_qt.h
+++ b/src/core/ozone/gl_surface_wgl_qt.h
@@ -6,8 +6,6 @@
 
 #include "gl_surface_qt.h"
 
-#if BUILDFLAG(IS_WIN)
-
 namespace gl {
 
 class PbufferGLSurfaceWGL;
@@ -31,7 +29,7 @@ class GLSurfaceWGLQt: public GLSurfaceQt {
     scoped_refptr<PbufferGLSurfaceWGL> m_surfaceBuffer;
 };
 
-}
-#endif // BUILDFLAG(IS_WIN)
+} // namespace gl
+
 #endif // GL_SURFACE_WGL_QT_H
 
diff --git a/src/core/ozone/ozone_platform_qt.cpp b/src/core/ozone/ozone_platform_qt.cpp
index 623cf43cfb8..6384ea2dbda 100644
--- a/src/core/ozone/ozone_platform_qt.cpp
+++ b/src/core/ozone/ozone_platform_qt.cpp
@@ -38,7 +38,7 @@
 #include <filesystem>
 #endif // BUILDFLAG(USE_XKBCOMMON)
 
-#if BUILDFLAG(OZONE_PLATFORM_X11)
+#if BUILDFLAG(IS_OZONE_X11)
 #include "ui/gfx/linux/gpu_memory_buffer_support_x11.h"
 #include "ui/ozone/platform/x11/gl_egl_utility_x11.h"
 
@@ -113,9 +113,6 @@ const ui::OzonePlatform::PlatformProperties &OzonePlatformQt::GetPlatformPropert
     if (!initialized) {
         DCHECK(m_supportsNativePixmaps);
         properties->fetch_buffer_formats_for_gmb_on_gpu = m_supportsNativePixmaps.value();
-#if BUILDFLAG(USE_VAAPI)
-        properties->supports_vaapi = m_supportsNativePixmaps.value();
-#endif
         initialized = true;
     }
 
@@ -238,7 +235,7 @@ void OzonePlatformQt::InitializeGPU(const ui::OzonePlatform::InitParams &params)
 {
     surface_factory_ozone_.reset(new QtWebEngineCore::SurfaceFactoryQt());
 
-#if BUILDFLAG(OZONE_PLATFORM_X11)
+#if BUILDFLAG(IS_OZONE_X11)
     if (params.enable_native_gpu_memory_buffers) {
         base::ThreadPool::PostTask(FROM_HERE,
                                    base::BindOnce([]()
@@ -263,7 +260,7 @@ bool OzonePlatformQt::IsNativePixmapConfigSupported(gfx::BufferFormat format, gf
 PlatformGLEGLUtility *OzonePlatformQt::GetPlatformGLEGLUtility()
 {
     if (!gl_egl_utility_) {
-#if BUILDFLAG(OZONE_PLATFORM_X11)
+#if BUILDFLAG(IS_OZONE_X11)
         if (GetQtXDisplay())
             gl_egl_utility_ = std::make_unique<GLEGLUtilityX11>();
         else
diff --git a/src/core/ozone/surface_factory_qt.cpp b/src/core/ozone/surface_factory_qt.cpp
index 2d311a02a26..e2aec542d67 100644
--- a/src/core/ozone/surface_factory_qt.cpp
+++ b/src/core/ozone/surface_factory_qt.cpp
@@ -6,19 +6,22 @@
 
 #include "qtwebenginecoreglobal_p.h"
 #include "ozone/gl_context_qt.h"
+#include "ozone/gl_ozone_angle_qt.h"
 #include "ozone/gl_ozone_egl_qt.h"
+#include "qtwebenginecoreglobal_p.h"
+#include "web_engine_context.h"
 
 #include "media/gpu/buildflags.h"
+#include "ui/base/ozone_buildflags.h"
 #include "ui/gfx/linux/drm_util_linux.h"
 #include "ui/gfx/linux/gbm_buffer.h"
 #include "ui/gfx/linux/native_pixmap_dmabuf.h"
 #include "ui/gl/egl_util.h"
-#include "ui/ozone/buildflags.h"
 
 #include <QDebug>
 #include <QtGui/qtgui-config.h>
 
-#if BUILDFLAG(OZONE_PLATFORM_X11)
+#if BUILDFLAG(IS_OZONE_X11)
 #include "ozone/gl_ozone_glx_qt.h"
 
 #include "ui/gfx/linux/gpu_memory_buffer_support_x11.h"
@@ -32,32 +35,44 @@ namespace QtWebEngineCore {
 
 SurfaceFactoryQt::SurfaceFactoryQt()
 {
-#if BUILDFLAG(OZONE_PLATFORM_X11)
+#if BUILDFLAG(IS_OZONE_X11)
     if (GLContextHelper::getGlxPlatformInterface()) {
-        m_impl = { gl::GLImplementationParts(gl::kGLImplementationDesktopGL),
-                   gl::GLImplementationParts(gl::kGLImplementationDisabled) };
-        m_ozone.reset(new ui::GLOzoneGLXQt());
+        m_impls.push_back({ gl::GLImplementationParts(gl::kGLImplementationDesktopGL),
+                            std::make_unique<ui::GLOzoneGLXQt>() });
     } else
 #endif
     if (GLContextHelper::getEglPlatformInterface()) {
-        m_impl = { gl::GLImplementationParts(gl::kGLImplementationEGLGLES2),
-                   gl::GLImplementationParts(gl::kGLImplementationDesktopGL),
-                   gl::GLImplementationParts(gl::kGLImplementationDisabled) };
-        m_ozone.reset(new ui::GLOzoneEGLQt());
-    } else {
-        m_impl = { gl::GLImplementationParts(gl::kGLImplementationDisabled) };
+        m_impls.push_back({ gl::GLImplementationParts(gl::kGLImplementationEGLGLES2),
+                            std::make_unique<ui::GLOzoneEGLQt>() });
+        m_impls.push_back({ gl::GLImplementationParts(gl::kGLImplementationDesktopGL),
+                            std::make_unique<ui::GLOzoneEGLQt>() });
     }
+
+    m_impls.push_back({ gl::GLImplementationParts(gl::kGLImplementationEGLANGLE),
+                        std::make_unique<ui::GLOzoneANGLEQt>() });
+    m_impls.push_back({ gl::GLImplementationParts(gl::kGLImplementationDisabled), nullptr });
 }
 
 std::vector<gl::GLImplementationParts> SurfaceFactoryQt::GetAllowedGLImplementations()
 {
-    return m_impl;
+    std::vector<gl::GLImplementationParts> allowed;
+    for (const auto &impl : m_impls)
+        allowed.push_back(impl.first);
+
+    return allowed;
 }
 
 ui::GLOzone *SurfaceFactoryQt::GetGLOzone(const gl::GLImplementationParts &implementation)
 {
-    return m_ozone.get();
+    for (const auto &impl : m_impls) {
+        if (impl.first.gl == implementation.gl)
+            return impl.second.get();
+    }
+
+    qFatal("GLOzone not found for %s", gl::GetGLImplementationGLName(implementation));
+    return nullptr;
 }
+
 #if BUILDFLAG(ENABLE_VULKAN)
 std::unique_ptr<gpu::VulkanImplementation>
 SurfaceFactoryQt::CreateVulkanImplementation(bool /*allow_protected_memory*/,
@@ -73,7 +88,7 @@ SurfaceFactoryQt::CreateVulkanImplementation(bool /*allow_protected_memory*/,
 
 bool SurfaceFactoryQt::CanCreateNativePixmapForFormat(gfx::BufferFormat format)
 {
-#if BUILDFLAG(OZONE_PLATFORM_X11)
+#if BUILDFLAG(IS_OZONE_X11)
     if (GLContextHelper::getGlxPlatformInterface())
         return ui::GpuMemoryBufferSupportX11::GetInstance()->CanCreateNativePixmapForFormat(format);
 #endif
@@ -101,7 +116,7 @@ scoped_refptr<gfx::NativePixmap> SurfaceFactoryQt::CreateNativePixmap(
 
     gfx::NativePixmapHandle handle;
 
-#if BUILDFLAG(OZONE_PLATFORM_X11)
+#if BUILDFLAG(IS_OZONE_X11)
     if (GLContextHelper::getGlxPlatformInterface()) {
         auto gbmBuffer =
                 ui::GpuMemoryBufferSupportX11::GetInstance()->CreateBuffer(format, size, usage);
@@ -165,7 +180,7 @@ SurfaceFactoryQt::CreateNativePixmapFromHandle(
 #if QT_CONFIG(opengl)
     gfx::NativePixmapHandle bufferHandle;
 
-#if BUILDFLAG(OZONE_PLATFORM_X11)
+#if BUILDFLAG(IS_OZONE_X11)
     if (GLContextHelper::getGlxPlatformInterface()) {
         auto gbmBuffer = ui::GpuMemoryBufferSupportX11::GetInstance()->CreateBufferFromHandle(
                 size, format, std::move(handle));
@@ -208,8 +223,7 @@ SurfaceFactoryQt::CreateNativePixmapFromHandle(
                 eglFun->eglCreateImage(eglDisplay, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT,
                                        (EGLClientBuffer)NULL, attrs.data());
         if (eglImage == EGL_NO_IMAGE_KHR) {
-            qFatal() << "Failed to import EGLImage:"
-                     << ui::GetEGLErrorString(eglFun->eglGetError());
+            qFatal("Failed to import EGLImage: %s", ui::GetEGLErrorString(eglFun->eglGetError()));
         }
 
         Q_ASSERT(numPlanes <= 3);
@@ -217,8 +231,7 @@ SurfaceFactoryQt::CreateNativePixmapFromHandle(
         int strides[3];
         int offsets[3];
         if (!eglFun->eglExportDMABUFImageMESA(eglDisplay, eglImage, fds, strides, offsets)) {
-            qFatal() << "Failed to export EGLImage:"
-                     << ui::GetEGLErrorString(eglFun->eglGetError());
+            qFatal("Failed to export EGLImage: %s", ui::GetEGLErrorString(eglFun->eglGetError()));
         }
 
         bufferHandle.modifier = handle.modifier;
@@ -250,10 +263,12 @@ SurfaceFactoryQt::CreateNativePixmapFromHandle(
 bool SurfaceFactoryQt::SupportsNativePixmaps()
 {
 #if QT_CONFIG(opengl)
-#if BUILDFLAG(OZONE_PLATFORM_X11)
-    if (GLContextHelper::getGlxPlatformInterface())
-        return ui::GpuMemoryBufferSupportX11::GetInstance()->has_gbm_device();
-#endif // BUILDFLAG(OZONE_PLATFORM_X11)
+#if BUILDFLAG(IS_OZONE_X11)
+    if (GLContextHelper::getGlxPlatformInterface()) {
+        return QtWebEngineCore::WebEngineContext::isGbmSupported()
+                && ui::GpuMemoryBufferSupportX11::GetInstance()->has_gbm_device();
+    }
+#endif // BUILDFLAG(IS_OZONE_X11)
 
     if (GLContextHelper::getEglPlatformInterface())
         return EGLHelper::instance()->isDmaBufSupported();
diff --git a/src/core/ozone/surface_factory_qt.h b/src/core/ozone/surface_factory_qt.h
index d69467a26ab..557bda09148 100644
--- a/src/core/ozone/surface_factory_qt.h
+++ b/src/core/ozone/surface_factory_qt.h
@@ -43,8 +43,7 @@ class SurfaceFactoryQt : public ui::SurfaceFactoryOzone
     static bool SupportsNativePixmaps();
 
 private:
-    std::vector<gl::GLImplementationParts> m_impl;
-    std::unique_ptr<ui::GLOzone> m_ozone;
+    std::vector<std::pair<gl::GLImplementationParts, std::unique_ptr<ui::GLOzone>>> m_impls;
 };
 
 } // namespace QtWebEngineCore
diff --git a/src/core/permission_manager_qt.cpp b/src/core/permission_manager_qt.cpp
index 100b7eb7cb2..058cefddcb7 100644
--- a/src/core/permission_manager_qt.cpp
+++ b/src/core/permission_manager_qt.cpp
@@ -21,34 +21,47 @@
 #include "components/proxy_config/pref_proxy_config_tracker_impl.h"
 #include "components/prefs/pref_service.h"
 
+#include <QtWebEngineCore/private/qwebenginepermission_p.h>
 #include "type_conversion.h"
 #include "web_contents_delegate_qt.h"
 #include "web_engine_settings.h"
 
+using namespace Qt::StringLiterals;
+
 namespace QtWebEngineCore {
 
-static ProfileAdapter::PermissionType toQt(blink::PermissionType type)
+// Extra permission types that don't exist on the Chromium side.
+enum class ExtraPermissionType {
+    POINTER_LOCK = 39, // TODO this exists in Chromium 126, remove after we merge
+};
+
+static QWebEnginePermission::PermissionType toQt(blink::PermissionType type)
 {
     switch (type) {
     case blink::PermissionType::GEOLOCATION:
-        return ProfileAdapter::GeolocationPermission;
+        return QWebEnginePermission::PermissionType::Geolocation;
     case blink::PermissionType::AUDIO_CAPTURE:
-        return ProfileAdapter::AudioCapturePermission;
+        return QWebEnginePermission::PermissionType::MediaAudioCapture;
     case blink::PermissionType::VIDEO_CAPTURE:
-        return ProfileAdapter::VideoCapturePermission;
+        return QWebEnginePermission::PermissionType::MediaVideoCapture;
+    case blink::PermissionType::DISPLAY_CAPTURE:
+        return QWebEnginePermission::PermissionType::DesktopAudioVideoCapture;
         // We treat these both as read/write since we do not currently have a
-        // ClipboardSanitizedWrite feature.
+        // ClipboardSanitizedWrite permission type.
     case blink::PermissionType::CLIPBOARD_READ_WRITE:
     case blink::PermissionType::CLIPBOARD_SANITIZED_WRITE:
-        return ProfileAdapter::ClipboardReadWrite;
+        return QWebEnginePermission::PermissionType::ClipboardReadWrite;
     case blink::PermissionType::NOTIFICATIONS:
-        return ProfileAdapter::NotificationPermission;
+        return QWebEnginePermission::PermissionType::Notifications;
     case blink::PermissionType::LOCAL_FONTS:
-        return ProfileAdapter::LocalFontsPermission;
+        return QWebEnginePermission::PermissionType::LocalFontsAccess;
+    case (blink::PermissionType)ExtraPermissionType::POINTER_LOCK:
+        return QWebEnginePermission::PermissionType::MouseLock;
     case blink::PermissionType::ACCESSIBILITY_EVENTS:
     case blink::PermissionType::CAMERA_PAN_TILT_ZOOM:
     case blink::PermissionType::WINDOW_MANAGEMENT:
-        return ProfileAdapter::UnsupportedPermission;
+    case blink::PermissionType::NUM:
+        return QWebEnginePermission::PermissionType::Unsupported;
     case blink::PermissionType::MIDI_SYSEX:
     case blink::PermissionType::PROTECTED_MEDIA_IDENTIFIER:
     case blink::PermissionType::MIDI:
@@ -65,71 +78,112 @@ static ProfileAdapter::PermissionType toQt(blink::PermissionType type)
     case blink::PermissionType::AR:
     case blink::PermissionType::VR:
     case blink::PermissionType::STORAGE_ACCESS_GRANT:
-    case blink::PermissionType::DISPLAY_CAPTURE:
     case blink::PermissionType::TOP_LEVEL_STORAGE_ACCESS:
-    case blink::PermissionType::NUM:
-        LOG(INFO) << "Unexpected unsupported WebEngine permission type: " << static_cast<int>(type);
+    case blink::PermissionType::CAPTURED_SURFACE_CONTROL:
+    case blink::PermissionType::SMART_CARD:
+    case blink::PermissionType::WEB_PRINTING:
+        LOG(INFO) << "Unexpected unsupported Blink permission type: " << static_cast<int>(type);
         break;
     }
-    return ProfileAdapter::UnsupportedPermission;
+    return QWebEnginePermission::PermissionType::Unsupported;
 }
 
-static blink::PermissionType toBlink(ProfileAdapter::PermissionType type)
+static blink::PermissionType toBlink(QWebEnginePermission::PermissionType permissionType)
 {
-    switch (type) {
-    case ProfileAdapter::GeolocationPermission:
+    switch (permissionType) {
+    case QWebEnginePermission::PermissionType::Notifications:
+        return blink::PermissionType::NOTIFICATIONS;
+    case QWebEnginePermission::PermissionType::Geolocation:
         return blink::PermissionType::GEOLOCATION;
-    case ProfileAdapter::AudioCapturePermission:
+    case QWebEnginePermission::PermissionType::MediaAudioCapture:
         return blink::PermissionType::AUDIO_CAPTURE;
-    case ProfileAdapter::VideoCapturePermission:
+    case QWebEnginePermission::PermissionType::MediaVideoCapture:
         return blink::PermissionType::VIDEO_CAPTURE;
-    case ProfileAdapter::ClipboardReadWrite:
+    case QWebEnginePermission::PermissionType::DesktopVideoCapture:
+    case QWebEnginePermission::PermissionType::DesktopAudioVideoCapture:
+        return blink::PermissionType::DISPLAY_CAPTURE;
+    case QWebEnginePermission::PermissionType::ClipboardReadWrite:
         return blink::PermissionType::CLIPBOARD_READ_WRITE;
-    case ProfileAdapter::NotificationPermission:
-        return blink::PermissionType::NOTIFICATIONS;
-    case ProfileAdapter::LocalFontsPermission:
+    case QWebEnginePermission::PermissionType::LocalFontsAccess:
         return blink::PermissionType::LOCAL_FONTS;
+    case QWebEnginePermission::PermissionType::MouseLock:
+        return (blink::PermissionType)ExtraPermissionType::POINTER_LOCK;
+    case QWebEnginePermission::PermissionType::MediaAudioVideoCapture:
+        LOG(INFO) << "Unexpected unsupported WebEngine permission type: " << static_cast<int>(permissionType);
+        Q_FALLTHROUGH();
+    case QWebEnginePermission::PermissionType::Unsupported:
+        return blink::PermissionType::NUM;
     }
 
-    LOG(INFO) << "Unexpected unsupported Blink permission type: " << static_cast<int>(type);
+    Q_UNREACHABLE();
     return blink::PermissionType::NUM;
 }
 
-static bool canRequestPermissionFor(ProfileAdapter::PermissionType type)
+static QWebEnginePermission::State toQt(blink::mojom::PermissionStatus state)
 {
-    switch (type) {
-    case ProfileAdapter::GeolocationPermission:
-    case ProfileAdapter::NotificationPermission:
-    case ProfileAdapter::ClipboardReadWrite:
-    case ProfileAdapter::LocalFontsPermission:
-        return true;
-    default:
-        break;
+    switch (state) {
+    case blink::mojom::PermissionStatus::ASK:
+        return QWebEnginePermission::State::Ask;
+    case blink::mojom::PermissionStatus::GRANTED:
+        return QWebEnginePermission::State::Granted;
+    case blink::mojom::PermissionStatus::DENIED:
+        return QWebEnginePermission::State::Denied;
     }
-    return false;
 }
 
-static blink::mojom::PermissionStatus toBlink(ProfileAdapter::PermissionState reply)
+static blink::mojom::PermissionStatus toBlink(QWebEnginePermission::State state)
 {
-    switch (reply) {
-    case ProfileAdapter::AskPermission:
+    switch (state) {
+    case QWebEnginePermission::State::Invalid:
+    case QWebEnginePermission::State::Ask:
         return blink::mojom::PermissionStatus::ASK;
-    case ProfileAdapter::AllowedPermission:
+    case QWebEnginePermission::State::Granted:
         return blink::mojom::PermissionStatus::GRANTED;
-    case ProfileAdapter::DeniedPermission:
+    case QWebEnginePermission::State::Denied:
         return blink::mojom::PermissionStatus::DENIED;
     }
 }
 
+std::string permissionTypeString(QWebEnginePermission::PermissionType permissionType)
+{
+    // This is separate from blink::permissionTypeString() for the sake of future-proofing;
+    // e.g. in case we add extra Features that do not correspond to a PermissionType, and
+    // we need to store them.
+    switch (permissionType) {
+    case QWebEnginePermission::PermissionType::MediaAudioCapture:
+        return "MediaAudioCapture";
+    case QWebEnginePermission::PermissionType::MediaVideoCapture:
+        return "MediaVideoCapture";
+    case QWebEnginePermission::PermissionType::DesktopAudioVideoCapture:
+        return "DesktopAudioVideoCapture";
+    case QWebEnginePermission::PermissionType::MouseLock:
+        return "MouseLock";
+    case QWebEnginePermission::PermissionType::Notifications:
+        return "Notifications";
+    case QWebEnginePermission::PermissionType::Geolocation:
+        return "Geolocation";
+    case QWebEnginePermission::PermissionType::ClipboardReadWrite:
+        return "ClipboardReadWrite";
+    case QWebEnginePermission::PermissionType::LocalFontsAccess:
+        return "LocalFontsAccess";
+    default:
+        Q_UNREACHABLE();
+        return nullptr;
+    }
+}
+
 static blink::mojom::PermissionStatus getStatusFromSettings(blink::PermissionType type, WebEngineSettings *settings)
 {
     switch (type) {
     case blink::PermissionType::CLIPBOARD_READ_WRITE:
-    case blink::PermissionType::CLIPBOARD_SANITIZED_WRITE:
         if (settings->testAttribute(QWebEngineSettings::JavascriptCanPaste)
             && settings->testAttribute(QWebEngineSettings::JavascriptCanAccessClipboard))
             return blink::mojom::PermissionStatus::GRANTED;
         return blink::mojom::PermissionStatus::ASK;
+    case blink::PermissionType::CLIPBOARD_SANITIZED_WRITE:
+        if (settings->testAttribute(QWebEngineSettings::JavascriptCanAccessClipboard))
+            return blink::mojom::PermissionStatus::GRANTED;
+        return blink::mojom::PermissionStatus::ASK;
     default:
         return blink::mojom::PermissionStatus::ASK;
     }
@@ -137,37 +191,44 @@ static blink::mojom::PermissionStatus getStatusFromSettings(blink::PermissionTyp
 
 PermissionManagerQt::PermissionManagerQt(ProfileAdapter *profileAdapter)
     : m_requestIdCount(0)
-    , m_persistence(true)
+    , m_transientWriteCount(0)
     , m_profileAdapter(profileAdapter)
+    , m_persistence(true)
 {
     PrefServiceFactory factory;
     factory.set_async(false);
     factory.set_command_line_prefs(base::MakeRefCounted<ChromeCommandLinePrefStore>(
             base::CommandLine::ForCurrentProcess()));
 
-    QString userPrefStorePath = profileAdapter->dataPath();
+    QString userPrefStorePath;
+    userPrefStorePath += profileAdapter->dataPath();
     auto prefRegistry = base::MakeRefCounted<PrefRegistrySimple>();
 
     auto policy = profileAdapter->persistentPermissionsPolicy();
-    if (!profileAdapter->isOffTheRecord() && policy == ProfileAdapter::PersistentPermissionsOnDisk &&
+    if (!profileAdapter->isOffTheRecord() && policy == ProfileAdapter::PersistentPermissionsPolicy::StoreOnDisk &&
             !userPrefStorePath.isEmpty() && profileAdapter->ensureDataPathExists()) {
         userPrefStorePath += QDir::separator();
-        userPrefStorePath += QStringLiteral("permissions.json");
+        userPrefStorePath += "permissions.json"_L1;
         factory.set_user_prefs(base::MakeRefCounted<JsonPrefStore>(toFilePath(userPrefStorePath)));
     } else {
         factory.set_user_prefs(new InMemoryPrefStore);
     }
 
+    m_permissionTypes.push_back(QWebEnginePermission::PermissionType::MediaAudioCapture);
+    m_permissionTypes.push_back(QWebEnginePermission::PermissionType::MediaVideoCapture);
+    m_permissionTypes.push_back(QWebEnginePermission::PermissionType::MouseLock);
+    m_permissionTypes.push_back(QWebEnginePermission::PermissionType::Notifications);
+    m_permissionTypes.push_back(QWebEnginePermission::PermissionType::Geolocation);
+    m_permissionTypes.push_back(QWebEnginePermission::PermissionType::ClipboardReadWrite);
+    m_permissionTypes.push_back(QWebEnginePermission::PermissionType::LocalFontsAccess);
+
     // Register all preference types as keys prior to doing anything else
-    prefRegistry->RegisterDictionaryPref(GetPermissionString(toBlink(ProfileAdapter::GeolocationPermission)));
-    prefRegistry->RegisterDictionaryPref(GetPermissionString(toBlink(ProfileAdapter::AudioCapturePermission)));
-    prefRegistry->RegisterDictionaryPref(GetPermissionString(toBlink(ProfileAdapter::VideoCapturePermission)));
-    prefRegistry->RegisterDictionaryPref(GetPermissionString(toBlink(ProfileAdapter::ClipboardReadWrite)));
-    prefRegistry->RegisterDictionaryPref(GetPermissionString(toBlink(ProfileAdapter::NotificationPermission)));
-    prefRegistry->RegisterDictionaryPref(GetPermissionString(toBlink(ProfileAdapter::LocalFontsPermission)));
+    for (auto &type : m_permissionTypes) {
+        prefRegistry->RegisterDictionaryPref(permissionTypeString(type));
+    }
     PrefProxyConfigTrackerImpl::RegisterPrefs(prefRegistry.get());
 
-    if (policy == ProfileAdapter::NoPersistentPermissions)
+    if (policy == ProfileAdapter::PersistentPermissionsPolicy::AskEveryTime)
         m_persistence = false;
 
     {
@@ -181,34 +242,51 @@ PermissionManagerQt::~PermissionManagerQt()
     commit();
 }
 
-void PermissionManagerQt::permissionRequestReply(const QUrl &url, ProfileAdapter::PermissionType type, ProfileAdapter::PermissionState reply)
+void PermissionManagerQt::setPermission(
+    const QUrl &url,
+    QWebEnginePermission::PermissionType permissionType,
+    QWebEnginePermission::State state,
+    content::RenderFrameHost *rfh)
 {
     // Normalize the QUrl to Chromium origin form.
     const GURL gorigin = toGurl(url).DeprecatedGetOriginAsURL();
     const QUrl origin = gorigin.is_empty() ? url : toQt(gorigin);
     if (origin.isEmpty())
         return;
-    if (reply == ProfileAdapter::AskPermission)
-        ResetPermission(toBlink(type), gorigin, gorigin);
-    else
-        setPermission(toBlink(type), gorigin, reply == ProfileAdapter::AllowedPermission);
-    blink::mojom::PermissionStatus status = toBlink(reply);
-    if (reply != ProfileAdapter::AskPermission) {
+
+    // Send eligible permissions with an associated rfh to the transient store. When pre-granting
+    // a non-persistent permission (or pre-granting any permission in AskEveryTime mode), it is allowed
+    // to pass through the persistent store. It will be moved to the transient store and associated
+    // with a rfh the next time its status is requested.
+    bool inTransientStore = rfh && (!QWebEnginePermission::isPersistent(permissionType) || !m_persistence);
+
+    blink::mojom::PermissionStatus blinkStatus = toBlink(state);
+    if (state == QWebEnginePermission::State::Ask) {
+        if (inTransientStore)
+            resetTransientPermission(toBlink(permissionType), gorigin, rfh->GetGlobalFrameToken());
+        else
+            ResetPermission(toBlink(permissionType), gorigin, gorigin);
+    } else {
+        if (inTransientStore)
+            setTransientPermission(toBlink(permissionType), gorigin, state == QWebEnginePermission::State::Granted, rfh->GetGlobalFrameToken());
+        else
+            setPersistentPermission(toBlink(permissionType), gorigin, state == QWebEnginePermission::State::Granted);
         auto it = m_requests.begin();
         while (it != m_requests.end()) {
-            if (it->origin == origin && it->type == type) {
-                std::move(it->callback).Run(status);
+            if (it->origin == origin && it->type == permissionType) {
+                std::move(it->callback).Run(blinkStatus);
                 it = m_requests.erase(it);
             } else
                 ++it;
         }
     }
-    for (const auto &it: m_subscribers) {
-        if (it.second.origin == origin && it.second.type == type)
-            it.second.callback.Run(status);
+
+    for (const auto &it : m_subscribers) {
+        if (it.second.origin == origin && it.second.type == permissionType)
+            it.second.callback.Run(blinkStatus);
     }
 
-    if (reply == ProfileAdapter::AskPermission)
+    if (state == QWebEnginePermission::State::Ask)
         return;
 
     auto it = m_multiRequests.begin();
@@ -218,13 +296,18 @@ void PermissionManagerQt::permissionRequestReply(const QUrl &url, ProfileAdapter
             std::vector<blink::mojom::PermissionStatus> result;
             result.reserve(it->types.size());
             for (blink::PermissionType permission : it->types) {
-                if (toQt(permission) == ProfileAdapter::UnsupportedPermission) {
+                if (toQt(permission) == QWebEnginePermission::PermissionType::Unsupported) {
                     result.push_back(blink::mojom::PermissionStatus::DENIED);
                     continue;
                 }
 
-                blink::mojom::PermissionStatus permissionStatus = GetPermissionStatus(permission, gorigin, GURL());
-                if (permissionStatus == toBlink(reply)) {
+                blink::mojom::PermissionStatus permissionStatus;
+                if (inTransientStore)
+                    permissionStatus = toBlink(getPermissionState(url, permissionType, rfh));
+                else
+                    permissionStatus = GetPermissionStatus(permission, gorigin, GURL());
+
+                if (permissionStatus == toBlink(state)) {
                     if (permissionStatus == blink::mojom::PermissionStatus::ASK) {
                         answerable = false;
                         break;
@@ -232,8 +315,8 @@ void PermissionManagerQt::permissionRequestReply(const QUrl &url, ProfileAdapter
 
                     result.push_back(permissionStatus);
                 } else {
-                    // Reached when the PersistentPermissionsPolicy is set to NoPersistentPermissions
-                    result.push_back(toBlink(reply));
+                    // Reached when the PersistentPermissionsPolicy is set to AskEveryTime
+                    result.push_back(toBlink(state));
                 }
             }
             if (answerable) {
@@ -246,9 +329,56 @@ void PermissionManagerQt::permissionRequestReply(const QUrl &url, ProfileAdapter
     }
 }
 
-bool PermissionManagerQt::checkPermission(const QUrl &origin, ProfileAdapter::PermissionType type)
+QWebEnginePermission::State PermissionManagerQt::getPermissionState(const QUrl &origin, QWebEnginePermission::PermissionType permissionType,
+    content::RenderFrameHost *rfh)
+{
+    if (rfh) {
+        // Ignore the origin parameter
+        return toQt(GetPermissionStatusForCurrentDocument(toBlink(permissionType), rfh));
+    }
+
+    return toQt(GetPermissionStatus(toBlink(permissionType), toGurl(origin), GURL()));
+}
+
+QList<QWebEnginePermission> PermissionManagerQt::listPermissions(const QUrl &origin, QWebEnginePermission::PermissionType permissionType)
 {
-    return GetPermissionStatus(toBlink(type), toGurl(origin), GURL()) == blink::mojom::PermissionStatus::GRANTED;
+    Q_ASSERT(origin.isEmpty() || permissionType == QWebEnginePermission::PermissionType::Unsupported);
+    QList<QWebEnginePermission> returnList;
+    GURL gorigin = toGurl(origin).DeprecatedGetOriginAsURL();
+    std::string originSpec = gorigin.spec();
+
+    if (!origin.isEmpty() && !gorigin.is_valid())
+        return returnList;
+
+    std::vector<QWebEnginePermission::PermissionType> types;
+    if (permissionType == QWebEnginePermission::PermissionType::Unsupported)
+        types = m_permissionTypes;
+    else
+        types.push_back(permissionType);
+
+    for (auto &type : types) {
+        // Transient types may end up in the permission store as an implementation detail,
+        // but we do not want to expose them to callers.
+        if (!QWebEnginePermission::isPersistent(type))
+            continue;
+
+        auto *pref = m_prefService->FindPreference(permissionTypeString(type));
+        if (!pref)
+            continue;
+
+        auto *prefDict = pref->GetValue()->GetIfDict();
+        Q_ASSERT(prefDict);
+
+        for (const auto &entry : *prefDict) {
+            if (!originSpec.empty() && entry.first != originSpec)
+                continue;
+
+            auto *pvt = new QWebEnginePermissionPrivate(toQt(GURL(std::string_view(entry.first))), type, nullptr, m_profileAdapter.get());
+            returnList.push_back(QWebEnginePermission(pvt));
+        }
+    }
+
+    return returnList;
 }
 
 void PermissionManagerQt::commit()
@@ -275,16 +405,43 @@ void PermissionManagerQt::RequestPermissions(content::RenderFrameHost *frameHost
     std::vector<content::PermissionStatus> result;
     result.reserve(requestDescription.permissions.size());
     for (blink::PermissionType permission : requestDescription.permissions) {
-        const ProfileAdapter::PermissionType permissionType = toQt(permission);
-        if (permissionType == ProfileAdapter::UnsupportedPermission) {
+        const QWebEnginePermission::PermissionType permissionType = toQt(permission);
+        if (permissionType == QWebEnginePermission::PermissionType::Unsupported) {
             result.push_back(blink::mojom::PermissionStatus::DENIED);
             continue;
         }
 
         blink::mojom::PermissionStatus permissionStatus = getStatusFromSettings(permission, contentsDelegate->webEngineSettings());
         if (permissionStatus == blink::mojom::PermissionStatus::ASK) {
-            permissionStatus = GetPermissionStatus(permission, requestDescription.requesting_origin, GURL());
-            if (m_persistence && permissionStatus != blink::mojom::PermissionStatus::ASK) {
+            const GURL &rorigin = requestDescription.requesting_origin;
+
+            if (!m_persistence) {
+                answerable = false;
+                break;
+            }
+
+            bool inTransientStore = !QWebEnginePermission::isPersistent(toQt(permission));
+            if (inTransientStore) {
+                permissionStatus = getTransientPermissionStatus(permission, rorigin, frameHost->GetGlobalFrameToken());
+
+                if (permissionStatus != blink::mojom::PermissionStatus::ASK) {
+                    result.push_back(permissionStatus);
+                    continue;
+                }
+
+                // Fall through to check if permission was pre-granted (and thus landed in the permanent store)
+            }
+
+            permissionStatus = GetPermissionStatus(permission, rorigin, rorigin);
+
+            if (inTransientStore && permissionStatus != blink::mojom::PermissionStatus::ASK) {
+                // Move the pre-granted permission to the transient store and associate it with the rfh
+                ResetPermission(permission, rorigin, rorigin);
+                setTransientPermission(permission, rorigin, permissionStatus == blink::mojom::PermissionStatus::GRANTED,
+                    frameHost->GetGlobalFrameToken());
+            }
+
+            if (permissionStatus != blink::mojom::PermissionStatus::ASK) {
                 // Automatically grant/deny without prompt if already asked once
                 result.push_back(permissionStatus);
             } else {
@@ -306,8 +463,8 @@ void PermissionManagerQt::RequestPermissions(content::RenderFrameHost *frameHost
     auto requestOrigin = toQt(requestDescription.requesting_origin);
     m_multiRequests.push_back({ request_id, requestDescription.permissions, requestOrigin, std::move(callback) });
     for (blink::PermissionType permission : requestDescription.permissions) {
-        const ProfileAdapter::PermissionType permissionType = toQt(permission);
-        if (canRequestPermissionFor(permissionType))
+        const QWebEnginePermission::PermissionType permissionType = toQt(permission);
+        if (QWebEnginePermission::isPersistent(permissionType))
             contentsDelegate->requestFeaturePermission(permissionType, requestOrigin);
     }
 }
@@ -324,12 +481,12 @@ blink::mojom::PermissionStatus PermissionManagerQt::GetPermissionStatus(
     const GURL& requesting_origin,
     const GURL& /*embedding_origin*/)
 {
-    const ProfileAdapter::PermissionType permissionType = toQt(permission);
-    if (permissionType == ProfileAdapter::UnsupportedPermission)
+    const QWebEnginePermission::PermissionType permissionType = toQt(permission);
+    if (permissionType == QWebEnginePermission::PermissionType::Unsupported)
         return blink::mojom::PermissionStatus::DENIED;
 
     permission = toBlink(toQt(permission)); // Filter out merged/unsupported permissions (e.g. clipboard)
-    auto *pref = m_prefService->FindPreference(GetPermissionString(permission));
+    auto *pref = m_prefService->FindPreference(permissionTypeString(toQt(permission)));
     if (!pref)
         return blink::mojom::PermissionStatus::ASK; // Permission type not in database
 
@@ -343,8 +500,7 @@ blink::mojom::PermissionStatus PermissionManagerQt::GetPermissionStatus(
     // Workaround: local fonts are entirely managed by Chromium, which only calls RequestPermission() _after_
     // it's checked whether the permission has been granted. By always returning ASK, we force the request to
     // come through every time.
-    if (permission == blink::PermissionType::LOCAL_FONTS
-        && m_profileAdapter->persistentPermissionsPolicy() == ProfileAdapter::NoPersistentPermissions)
+    if (permission == blink::PermissionType::LOCAL_FONTS && !m_persistence)
         return blink::mojom::PermissionStatus::ASK;
 
     if (requestedPermission.value())
@@ -356,6 +512,8 @@ blink::mojom::PermissionStatus PermissionManagerQt::GetPermissionStatusForCurren
         blink::PermissionType permission,
         content::RenderFrameHost *render_frame_host)
 {
+    Q_ASSERT(render_frame_host);
+
     if (permission == blink::PermissionType::CLIPBOARD_READ_WRITE ||
             permission == blink::PermissionType::CLIPBOARD_SANITIZED_WRITE) {
         WebContentsDelegateQt *delegate = static_cast<WebContentsDelegateQt *>(
@@ -366,10 +524,34 @@ blink::mojom::PermissionStatus PermissionManagerQt::GetPermissionStatusForCurren
             return status;
     }
 
-    return GetPermissionStatus(
-                permission,
-                render_frame_host->GetLastCommittedOrigin().GetURL(),
-                render_frame_host->GetLastCommittedOrigin().GetURL());
+    permission = toBlink(toQt(permission)); // Filter out merged/unsupported permissions (e.g. clipboard)
+    if (toQt(permission) == QWebEnginePermission::PermissionType::Unsupported)
+        return blink::mojom::PermissionStatus::DENIED;
+
+    GURL origin = render_frame_host->GetLastCommittedOrigin().GetURL();
+    auto status = blink::mojom::PermissionStatus::ASK;
+
+    bool inTransientStore = !QWebEnginePermission::isPersistent(toQt(permission)) || !m_persistence;
+    if (inTransientStore) {
+        status = getTransientPermissionStatus(permission, origin, render_frame_host->GetGlobalFrameToken());
+
+        if (status != blink::mojom::PermissionStatus::ASK) {
+            return status;
+        }
+
+        // Fall through to check if permission was pre-granted (and thus landed in the permanent store)
+    }
+
+    status = GetPermissionStatus(permission, origin, origin);
+
+    if (inTransientStore && status != blink::mojom::PermissionStatus::ASK) {
+        // Move the pre-granted permission to the transient store and associate it with the rfh
+        ResetPermission(permission, origin, origin);
+        setTransientPermission(permission, origin, status == blink::mojom::PermissionStatus::GRANTED,
+            render_frame_host->GetGlobalFrameToken());
+    }
+
+    return status;
 }
 
 blink::mojom::PermissionStatus PermissionManagerQt::GetPermissionStatusForWorker(
@@ -377,6 +559,7 @@ blink::mojom::PermissionStatus PermissionManagerQt::GetPermissionStatusForWorker
         content::RenderProcessHost *render_process_host,
         const GURL &url)
 {
+    Q_UNUSED(render_process_host);
     return GetPermissionStatus(permission, url, url);
 }
 
@@ -405,20 +588,19 @@ void PermissionManagerQt::ResetPermission(
     const GURL& requesting_origin,
     const GURL& /*embedding_origin*/)
 {
-    const ProfileAdapter::PermissionType permissionType = toQt(permission);
-    if (permissionType == ProfileAdapter::UnsupportedPermission)
+    const QWebEnginePermission::PermissionType permissionType = toQt(permission);
+    if (permissionType == QWebEnginePermission::PermissionType::Unsupported)
         return;
 
-    ScopedDictPrefUpdate updater(m_prefService.get(), GetPermissionString(permission));
+    ScopedDictPrefUpdate updater(m_prefService.get(), permissionTypeString(permissionType));
     updater.Get().Remove(requesting_origin.spec());
 }
 
-content::PermissionControllerDelegate::SubscriptionId PermissionManagerQt::SubscribePermissionStatusChange(
-    blink::PermissionType permission,
-    content::RenderProcessHost * /*render_process_host*/,
-    content::RenderFrameHost * /* render_frame_host */,
-    const GURL& requesting_origin,
-    base::RepeatingCallback<void(blink::mojom::PermissionStatus)> callback)
+content::PermissionControllerDelegate::SubscriptionId
+PermissionManagerQt::SubscribeToPermissionStatusChange(
+        blink::PermissionType permission, content::RenderProcessHost * /*render_process_host*/,
+        content::RenderFrameHost * /* render_frame_host */, const GURL &requesting_origin,
+        base::RepeatingCallback<void(blink::mojom::PermissionStatus)> callback)
 {
     auto subscriber_id = subscription_id_generator_.GenerateNextId();
     m_subscribers.insert( { subscriber_id,
@@ -426,26 +608,101 @@ content::PermissionControllerDelegate::SubscriptionId PermissionManagerQt::Subsc
     return subscriber_id;
 }
 
-void PermissionManagerQt::UnsubscribePermissionStatusChange(content::PermissionControllerDelegate::SubscriptionId subscription_id)
+void PermissionManagerQt::UnsubscribeFromPermissionStatusChange(
+        content::PermissionControllerDelegate::SubscriptionId subscription_id)
 {
     if (!m_subscribers.erase(subscription_id))
         LOG(WARNING) << "PermissionManagerQt::UnsubscribePermissionStatusChange called on unknown subscription id" << subscription_id;
 }
 
-void PermissionManagerQt::setPermission(
+blink::mojom::PermissionStatus PermissionManagerQt::getTransientPermissionStatus(blink::PermissionType permission,
+        const GURL& requesting_origin,
+        content::GlobalRenderFrameHostToken token)
+{
+    const QWebEnginePermission::PermissionType permissionType = toQt(permission);
+    if (permissionType == QWebEnginePermission::PermissionType::Unsupported)
+        return blink::mojom::PermissionStatus::DENIED;
+
+    if (!m_transientPermissions.contains(token))
+        return blink::mojom::PermissionStatus::ASK;
+
+    auto &permissionsForToken = m_transientPermissions[token];
+    for (auto p = permissionsForToken.begin(); p != permissionsForToken.end(); ++p) {
+        if (get<0>(*p) == requesting_origin && get<1>(*p) == permission) {
+            return get<2>(*p) ? blink::mojom::PermissionStatus::GRANTED : blink::mojom::PermissionStatus::DENIED;
+        }
+    }
+
+    return blink::mojom::PermissionStatus::ASK;
+}
+
+void PermissionManagerQt::setPersistentPermission(
     blink::PermissionType permission,
     const GURL& requesting_origin,
     bool granted)
 {
-    const ProfileAdapter::PermissionType permissionType = toQt(permission);
-    if (permissionType == ProfileAdapter::UnsupportedPermission)
+    const QWebEnginePermission::PermissionType permissionType = toQt(permission);
+    if (permissionType == QWebEnginePermission::PermissionType::Unsupported)
         return;
 
-    if (!m_prefService->FindPreference(GetPermissionString(permission)))
+    if (!m_prefService->FindPreference(permissionTypeString(permissionType)))
         return;
 
-    ScopedDictPrefUpdate updater(m_prefService.get(), GetPermissionString(permission));
+    ScopedDictPrefUpdate updater(m_prefService.get(), permissionTypeString(permissionType));
     updater.Get().Set(requesting_origin.spec(), granted);
+
+    m_prefService->SchedulePendingLossyWrites();
+}
+
+void PermissionManagerQt::setTransientPermission(blink::PermissionType permission,
+        const GURL& requesting_origin,
+        bool granted,
+        content::GlobalRenderFrameHostToken token)
+{
+    const QWebEnginePermission::PermissionType permissionType = toQt(permission);
+    if (permissionType == QWebEnginePermission::PermissionType::Unsupported)
+        return;
+
+    auto &permissionsForToken = m_transientPermissions[token];
+    for (auto &p : permissionsForToken) {
+        if (get<0>(p) == requesting_origin && get<1>(p) == permission) {
+            get<2>(p) = granted;
+            return;
+        }
+    }
+
+    permissionsForToken.push_back({requesting_origin, permission, granted});
+
+    // Render frame hosts get discarded often, so the map will eventualy fill up with junk unless
+    // periodically cleaned. The number 25 was chosen arbitrarily.
+    if (++m_transientWriteCount > 25) {
+        content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
+            base::BindOnce([](PermissionManagerQt *p){
+                for (auto i = p->m_transientPermissions.begin(); i != p->m_transientPermissions.end(); ++i) {
+                    if (content::RenderFrameHost::FromFrameToken(i->first) == nullptr) {
+                        i = p->m_transientPermissions.erase(i);
+                    }
+                }
+            }, this));
+        m_transientWriteCount = 0;
+    }
+}
+
+void PermissionManagerQt::resetTransientPermission(blink::PermissionType permission,
+        const GURL& requesting_origin,
+        content::GlobalRenderFrameHostToken token)
+{
+    const QWebEnginePermission::PermissionType permissionType = toQt(permission);
+    if (permissionType == QWebEnginePermission::PermissionType::Unsupported)
+        return;
+
+    auto &permissionsForToken = m_transientPermissions[token];
+    for (auto i = permissionsForToken.begin(); i != permissionsForToken.end(); ++i) {
+        if (get<0>(*i) == requesting_origin && get<1>(*i) == permission) {
+            permissionsForToken.erase(i);
+            return;
+        }
+    }
 }
 
 } // namespace QtWebEngineCore
diff --git a/src/core/permission_manager_qt.h b/src/core/permission_manager_qt.h
index 5dedaa612fb..88b777c7faa 100644
--- a/src/core/permission_manager_qt.h
+++ b/src/core/permission_manager_qt.h
@@ -5,11 +5,15 @@
 #define PERMISSION_MANAGER_QT_H
 
 #include "base/functional/callback.h"
+#include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/permission_controller_delegate.h"
+#include "content/public/browser/render_frame_host.h"
 
+#include <QtWebEngineCore/qwebenginepermission.h>
 #include "profile_adapter.h"
 
 #include <map>
+#include <tuple>
 
 class PrefService;
 
@@ -21,8 +25,15 @@ class PermissionManagerQt : public content::PermissionControllerDelegate
     PermissionManagerQt(ProfileAdapter *adapter);
     ~PermissionManagerQt();
 
-    void permissionRequestReply(const QUrl &origin, ProfileAdapter::PermissionType type, ProfileAdapter::PermissionState reply);
-    bool checkPermission(const QUrl &origin, ProfileAdapter::PermissionType type);
+    void setPermission(
+        const QUrl &origin,
+        QWebEnginePermission::PermissionType permissionType,
+        QWebEnginePermission::State state,
+        content::RenderFrameHost *rfh = nullptr);
+    QWebEnginePermission::State getPermissionState(const QUrl &origin, QWebEnginePermission::PermissionType permissionType,
+        content::RenderFrameHost *rfh = nullptr);
+    QList<QWebEnginePermission> listPermissions(const QUrl &origin, QWebEnginePermission::PermissionType permissionType);
+
     void commit();
 
     // content::PermissionManager implementation:
@@ -54,19 +65,18 @@ class PermissionManagerQt : public content::PermissionControllerDelegate
             const content::PermissionRequestDescription &request_description,
             base::OnceCallback<void(const std::vector<blink::mojom::PermissionStatus> &)> callback) override;
 
-    content::PermissionControllerDelegate::SubscriptionId SubscribePermissionStatusChange(
-        blink::PermissionType permission,
-        content::RenderProcessHost* render_process_host,
-        content::RenderFrameHost* render_frame_host,
-        const GURL& requesting_origin,
-        const base::RepeatingCallback<void(blink::mojom::PermissionStatus)> callback) override;
+    content::PermissionControllerDelegate::SubscriptionId SubscribeToPermissionStatusChange(
+            blink::PermissionType permission, content::RenderProcessHost *render_process_host,
+            content::RenderFrameHost *render_frame_host, const GURL &requesting_origin,
+            const base::RepeatingCallback<void(blink::mojom::PermissionStatus)> callback) override;
 
-    void UnsubscribePermissionStatusChange(content::PermissionControllerDelegate::SubscriptionId subscription_id) override;
+    void UnsubscribeFromPermissionStatusChange(
+            content::PermissionControllerDelegate::SubscriptionId subscription_id) override;
 
 private:
     struct Request {
         int id;
-        ProfileAdapter::PermissionType type;
+        QWebEnginePermission::PermissionType type;
         QUrl origin;
         base::OnceCallback<void(blink::mojom::PermissionStatus)> callback;
     };
@@ -77,20 +87,37 @@ class PermissionManagerQt : public content::PermissionControllerDelegate
         base::OnceCallback<void(const std::vector<blink::mojom::PermissionStatus>&)> callback;
     };
     struct Subscription {
-        ProfileAdapter::PermissionType type;
+        QWebEnginePermission::PermissionType type;
         QUrl origin;
         base::RepeatingCallback<void(blink::mojom::PermissionStatus)> callback;
     };
 
-    void setPermission(blink::PermissionType permission,
+    blink::mojom::PermissionStatus getTransientPermissionStatus(blink::PermissionType permission,
+        const GURL& requesting_origin,
+        content::GlobalRenderFrameHostToken token);
+
+    void setPersistentPermission(blink::PermissionType permission,
         const GURL& requesting_origin,
         bool granted);
 
+    void setTransientPermission(blink::PermissionType permission,
+        const GURL& requesting_origin,
+        bool granted,
+        content::GlobalRenderFrameHostToken token);
+
+    void resetTransientPermission(blink::PermissionType permission,
+        const GURL& requesting_origin,
+        content::GlobalRenderFrameHostToken token);
+
     std::vector<Request> m_requests;
     std::vector<MultiRequest> m_multiRequests;
+    std::vector<QWebEnginePermission::PermissionType> m_permissionTypes;
+    std::map<content::GlobalRenderFrameHostToken,
+        QList<std::tuple<GURL, blink::PermissionType, bool>>> m_transientPermissions;
     std::map<content::PermissionControllerDelegate::SubscriptionId, Subscription> m_subscribers;
     content::PermissionControllerDelegate::SubscriptionId::Generator subscription_id_generator_;
     int m_requestIdCount;
+    int m_transientWriteCount;
     std::unique_ptr<PrefService> m_prefService;
     QPointer<QtWebEngineCore::ProfileAdapter> m_profileAdapter;
     bool m_persistence;
diff --git a/src/core/platform_notification_service_qt.cpp b/src/core/platform_notification_service_qt.cpp
index 182a5ad849f..2c0a39a67c1 100644
--- a/src/core/platform_notification_service_qt.cpp
+++ b/src/core/platform_notification_service_qt.cpp
@@ -8,6 +8,8 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_event_dispatcher.h"
 #include "ui/message_center/public/cpp/notification_delegate.h"
+#include "url/gurl.h"
+#include "url/origin.h"
 
 #include "profile_adapter.h"
 #include "profile_adapter_client.h"
@@ -153,6 +155,24 @@ void PlatformNotificationServiceQt::GetDisplayedNotifications(DisplayedNotificat
     std::move(callback).Run(std::move(movableStdStringSet), true /* supports_synchronization */);
 }
 
+void PlatformNotificationServiceQt::GetDisplayedNotificationsForOrigin(const GURL &url, DisplayedNotificationsCallback callback)
+{
+    Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+    ProfileQt *profile = static_cast<ProfileQt *>(browser_context);
+
+    const url::Origin origin = url::Origin::Create(url);
+    std::set<std::string> movableStdStringSet;
+    auto it = profile->profileAdapter()->persistentNotifications().constBegin();
+    const auto end = profile->profileAdapter()->persistentNotifications().constEnd();
+    while (it != end) {
+        if (it.value()->isShown() && origin.IsSameOriginWith(toGurl(it.value()->origin())))
+            movableStdStringSet.insert(it.key().toStdString());
+        ++it;
+    }
+
+    std::move(callback).Run(std::move(movableStdStringSet), true /* supports_synchronization */);
+}
+
 int64_t PlatformNotificationServiceQt::ReadNextPersistentNotificationId()
 {
     Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
diff --git a/src/core/platform_notification_service_qt.h b/src/core/platform_notification_service_qt.h
index bf8fcca04fa..1435f79e984 100644
--- a/src/core/platform_notification_service_qt.h
+++ b/src/core/platform_notification_service_qt.h
@@ -56,6 +56,8 @@ class PlatformNotificationServiceQt : public content::PlatformNotificationServic
     // Records a given notification to UKM.
     void RecordNotificationUkmEvent(const content::NotificationDatabaseData&) override { }
 
+    void GetDisplayedNotificationsForOrigin(const GURL&, DisplayedNotificationsCallback callback) override;
+
     content::BrowserContext *browser_context;
 };
 
diff --git a/src/core/pref_service_adapter.cpp b/src/core/pref_service_adapter.cpp
index 544a84de1f1..210d900e25a 100644
--- a/src/core/pref_service_adapter.cpp
+++ b/src/core/pref_service_adapter.cpp
@@ -6,7 +6,9 @@
 #include "profile_adapter.h"
 #include "type_conversion.h"
 #include "web_engine_context.h"
+#include "web_engine_library_info.h"
 
+#include "base/base_paths.h"
 #include "base/threading/thread_restrictions.h"
 #include "chrome/browser/prefs/chrome_command_line_pref_store.h"
 #include "content/public/browser/browser_thread.h"
@@ -19,9 +21,12 @@
 #include "components/prefs/pref_service_factory.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/signin/internal/identity_manager/account_tracker_service.h"
-#include "components/signin/public/base/signin_pref_names.h"
+#include "components/signin/internal/identity_manager/gaia_cookie_manager_service.h"
+#include "components/signin/internal/identity_manager/primary_account_manager.h"
+#include "components/signin/internal/identity_manager/profile_oauth2_token_service.h"
 #include "components/user_prefs/user_prefs.h"
 #include "components/proxy_config/pref_proxy_config_tracker_impl.h"
+#include "chrome/browser/gcm/gcm_product_util.h"
 #include "chrome/common/pref_names.h"
 #include "extensions/buildflags/buildflags.h"
 #include "content/public/browser/browser_context.h"
@@ -50,6 +55,8 @@ namespace {
 static const char kPrefMediaDeviceIDSalt[] = "qtwebengine.media_device_salt_id";
 }
 
+using namespace Qt::StringLiterals;
+
 namespace QtWebEngineCore {
 
 void PrefServiceAdapter::setup(const ProfileAdapter &profileAdapter)
@@ -59,11 +66,12 @@ void PrefServiceAdapter::setup(const ProfileAdapter &profileAdapter)
     factory.set_command_line_prefs(base::MakeRefCounted<ChromeCommandLinePrefStore>(
             base::CommandLine::ForCurrentProcess()));
 
-    QString userPrefStorePath = profileAdapter.dataPath();
+    QString userPrefStorePath;
+    userPrefStorePath += profileAdapter.dataPath();
     if (!profileAdapter.isOffTheRecord() && !userPrefStorePath.isEmpty() &&
             const_cast<ProfileAdapter *>(&profileAdapter)->ensureDataPathExists()) {
         userPrefStorePath += QDir::separator();
-        userPrefStorePath += QStringLiteral("user_prefs.json");
+        userPrefStorePath += "user_prefs.json"_L1;
         factory.set_user_prefs(base::MakeRefCounted<JsonPrefStore>(toFilePath(userPrefStorePath)));
     } else {
         factory.set_user_prefs(new InMemoryPrefStore);
@@ -84,17 +92,20 @@ void PrefServiceAdapter::setup(const ProfileAdapter &profileAdapter)
 #endif // QT_CONFIG(webengine_spellchecker)
     registry->RegisterBooleanPref(prefs::kShowInternalAccessibilityTree, false);
     registry->RegisterBooleanPref(prefs::kAccessibilityImageLabelsEnabled, false);
+
+    // chrome/browser/notifications
     registry->RegisterIntegerPref(prefs::kNotificationNextPersistentId, 10000);
+    // chrome/browser/push_messaging
     registry->RegisterDictionaryPref(prefs::kPushMessagingAppIdentifierMap);
-    registry->RegisterListPref(prefs::kAccountInfo);
-    registry->RegisterStringPref(prefs::kGoogleServicesLastUsername,
-                               std::string());
-    registry->RegisterStringPref(prefs::kGoogleServicesAccountId, std::string());
-    registry->RegisterBooleanPref(prefs::kGoogleServicesConsentedToSync, false);
-    registry->RegisterBooleanPref(prefs::kAutologinEnabled, true);
-    registry->RegisterListPref(prefs::kReverseAutologinRejectedEmailList);
-    registry->RegisterBooleanPref(prefs::kSigninAllowed, true);
-    registry->RegisterBooleanPref(prefs::kSignedInWithCredentialProvider, false);
+    // chrome/browser/gcm
+    gcm::RegisterPrefs(registry.get());
+
+    // signin
+    PrimaryAccountManager::RegisterProfilePrefs(registry.get());
+    ProfileOAuth2TokenService::RegisterProfilePrefs(registry.get());
+    GaiaCookieManagerService::RegisterPrefs(registry.get());
+    AccountTrackerService::RegisterPrefs(registry.get());
+
 #if defined(Q_OS_WIN)
     OSCrypt::RegisterLocalPrefs(registry.get());
 #endif
@@ -134,10 +145,6 @@ void PrefServiceAdapter::setup(const ProfileAdapter &profileAdapter)
     registry->RegisterDictionaryPref(prefs::kDevToolsSyncedPreferencesSyncDisabled);
     registry->RegisterDictionaryPref(prefs::kDevToolsSyncedPreferencesSyncEnabled);
 
-    registry->RegisterStringPref(prefs::kGoogleServicesSigninScopedDeviceId, std::string());
-    registry->RegisterStringPref(prefs::kGaiaCookieLastListAccountsData, std::string());
-    registry->RegisterStringPref(prefs::kGCMProductCategoryForSubtypes, std::string());
-
     {
         base::ScopedAllowBlocking allowBlock;
         m_prefService = factory.Create(registry);
@@ -148,6 +155,8 @@ void PrefServiceAdapter::setup(const ProfileAdapter &profileAdapter)
     m_prefService->ClearPref(spellcheck::prefs::kSpellCheckEnable);
     m_prefService->ClearPref(spellcheck::prefs::kSpellCheckDictionaries);
 #endif // QT_CONFIG(webengine_spellchecker)
+
+    m_prefService->SchedulePendingLossyWrites();
 }
 
 void PrefServiceAdapter::commit()
@@ -183,6 +192,7 @@ void PrefServiceAdapter::setSpellCheckLanguages(const QStringList &languages)
     for (const auto &language : languages)
         dictionaries.push_back(language.toStdString());
     dictionaries_pref.SetValue(dictionaries);
+    m_prefService->SchedulePendingLossyWrites();
 }
 
 QStringList PrefServiceAdapter::spellCheckLanguages() const
@@ -198,7 +208,13 @@ QStringList PrefServiceAdapter::spellCheckLanguages() const
 
 void PrefServiceAdapter::setSpellCheckEnabled(bool enabled)
 {
-    m_prefService->SetBoolean(spellcheck::prefs::kSpellCheckEnable, enabled);
+    if (enabled == m_prefService->GetBoolean(spellcheck::prefs::kSpellCheckEnable))
+        return;
+
+    if (!WebEngineLibraryInfo::getPath(base::DIR_APP_DICTIONARIES, enabled).empty()) {
+        m_prefService->SetBoolean(spellcheck::prefs::kSpellCheckEnable, enabled);
+        m_prefService->SchedulePendingLossyWrites();
+    }
 }
 
 bool PrefServiceAdapter::isSpellCheckEnabled() const
diff --git a/src/core/printing/print_view_manager_base_qt.cpp b/src/core/printing/print_view_manager_base_qt.cpp
index b2b8e34fc3e..a8af1f6037b 100644
--- a/src/core/printing/print_view_manager_base_qt.cpp
+++ b/src/core/printing/print_view_manager_base_qt.cpp
@@ -17,7 +17,6 @@
 #include "base/task/thread_pool.h"
 #include "base/timer/timer.h"
 #include "base/values.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/printing/print_job.h"
 #include "chrome/browser/printing/print_job_manager.h"
 #include "chrome/browser/printing/printer_query.h"
@@ -34,6 +33,10 @@
 #include "printing/print_job_constants.h"
 #include "printing/printed_document.h"
 
+#include <string>
+
+using namespace std::string_literals;
+
 namespace QtWebEngineCore {
 
 namespace {
@@ -161,7 +164,7 @@ void PrintViewManagerBaseQt::NavigationStopped()
 
 std::u16string PrintViewManagerBaseQt::RenderSourceName()
 {
-     return toString16(QLatin1String(""));
+    return u""s;
 }
 
 void PrintViewManagerBaseQt::PrintDocument(scoped_refptr<base::RefCountedMemory> print_data,
diff --git a/src/core/printing/print_view_manager_qt.cpp b/src/core/printing/print_view_manager_qt.cpp
index db9dc8743d1..41f2cd999a6 100644
--- a/src/core/printing/print_view_manager_qt.cpp
+++ b/src/core/printing/print_view_manager_qt.cpp
@@ -11,6 +11,7 @@
 #include "pdf_util_qt.h"
 #include "type_conversion.h"
 #include "web_contents_adapter_client.h"
+#include "web_contents_adapter.h"
 #include "web_contents_view_qt.h"
 #include "web_engine_context.h"
 
@@ -173,8 +174,8 @@ PrintViewManagerQt::~PrintViewManagerQt()
 
 void PrintViewManagerQt::PrintToPDFFileWithCallback(const QPageLayout &pageLayout,
                                                     const QPageRanges &pageRanges,
-                                                    bool printInColor,
-                                                    const QString &filePath,
+                                                    bool printInColor, const QString &filePath,
+                                                    quint64 frameId,
                                                     PrintToPDFFileCallback callback)
 {
     if (callback.is_null())
@@ -188,7 +189,8 @@ void PrintViewManagerQt::PrintToPDFFileWithCallback(const QPageLayout &pageLayou
 
     m_pdfOutputPath = toFilePath(filePath);
     m_pdfSaveCallback = std::move(callback);
-    if (!PrintToPDFInternal(pageLayout, pageRanges, printInColor)) {
+    if (!PrintToPDFInternal(pageLayout, pageRanges, printInColor, /*useCustomMargins*/ true,
+                            frameId)) {
         content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
                        base::BindOnce(std::move(m_pdfSaveCallback), false));
         resetPdfState();
@@ -196,9 +198,8 @@ void PrintViewManagerQt::PrintToPDFFileWithCallback(const QPageLayout &pageLayou
 }
 
 void PrintViewManagerQt::PrintToPDFWithCallback(const QPageLayout &pageLayout,
-                                                const QPageRanges &pageRanges,
-                                                bool printInColor,
-                                                bool useCustomMargins,
+                                                const QPageRanges &pageRanges, bool printInColor,
+                                                bool useCustomMargins, quint64 frameId,
                                                 PrintToPDFCallback callback)
 {
     if (callback.is_null())
@@ -212,7 +213,7 @@ void PrintViewManagerQt::PrintToPDFWithCallback(const QPageLayout &pageLayout,
     }
 
     m_pdfPrintCallback = std::move(callback);
-    if (!PrintToPDFInternal(pageLayout, pageRanges, printInColor, useCustomMargins)) {
+    if (!PrintToPDFInternal(pageLayout, pageRanges, printInColor, useCustomMargins, frameId)) {
         content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
                        base::BindOnce(std::move(m_pdfPrintCallback), QSharedPointer<QByteArray>()));
 
@@ -221,9 +222,8 @@ void PrintViewManagerQt::PrintToPDFWithCallback(const QPageLayout &pageLayout,
 }
 
 bool PrintViewManagerQt::PrintToPDFInternal(const QPageLayout &pageLayout,
-                                            const QPageRanges &pageRanges,
-                                            const bool printInColor,
-                                            const bool useCustomMargins)
+                                            const QPageRanges &pageRanges, const bool printInColor,
+                                            const bool useCustomMargins, quint64 frameId)
 {
     if (!pageLayout.isValid())
         return false;
@@ -239,11 +239,22 @@ bool PrintViewManagerQt::PrintToPDFInternal(const QPageLayout &pageLayout,
     if (web_contents()->IsCrashed())
         return false;
 
-    content::RenderFrameHost *rfh = web_contents()->GetPrimaryMainFrame();
-    // Use the plugin frame for printing if web_contents() is a PDF viewer guest
-    content::RenderFrameHost *full_page_plugin = GetFullPagePlugin(web_contents());
-    if (content::RenderFrameHost *pdf_rfh = FindPdfChildFrame(full_page_plugin ? full_page_plugin : rfh))
-        rfh = pdf_rfh;
+    content::RenderFrameHost *rfh = nullptr;
+    if (frameId == WebContentsAdapter::kInvalidFrameId) {
+        return false;
+    } else if (frameId == WebContentsAdapter::kUseMainFrameId) {
+        rfh = web_contents()->GetPrimaryMainFrame();
+        // Use the plugin frame for printing if web_contents() is a PDF viewer guest
+        content::RenderFrameHost *full_page_plugin = GetFullPagePlugin(web_contents());
+        if (content::RenderFrameHost *pdf_rfh =
+                    FindPdfChildFrame(full_page_plugin ? full_page_plugin : rfh))
+            rfh = pdf_rfh;
+    } else {
+        auto *ftn = content::FrameTreeNode::GloballyFindByID(static_cast<int>(frameId));
+        if (!ftn)
+            return false;
+        rfh = ftn->current_frame_host();
+    }
     GetPrintRenderFrame(rfh)->InitiatePrintPreview(false);
 
     DCHECK(!m_printPreviewRfh);
@@ -353,6 +364,11 @@ void PrintViewManagerQt::RequestPrintPreview(printing::mojom::RequestPrintPrevie
         content::WebContentsView *view = static_cast<content::WebContentsImpl*>(web_contents()->GetOutermostWebContents())->GetView();
         if (WebContentsAdapterClient *client = WebContentsViewQt::from(view)->client())
             client->printRequested();
+
+        content::GlobalRenderFrameHostId rfhId = GetCurrentTargetFrame()->GetGlobalId();
+        auto *renderFrameHost = content::RenderFrameHost::FromID(rfhId);
+        if (renderFrameHost && renderFrameHost->IsRenderFrameLive())
+            GetPrintRenderFrame(renderFrameHost)->OnPrintPreviewDialogClosed();
         return;
     }
 
diff --git a/src/core/printing/print_view_manager_qt.h b/src/core/printing/print_view_manager_qt.h
index 956849ef9ee..879a89ef000 100644
--- a/src/core/printing/print_view_manager_qt.h
+++ b/src/core/printing/print_view_manager_qt.h
@@ -41,21 +41,18 @@ class PrintViewManagerQt
     typedef base::OnceCallback<void(bool success)> PrintToPDFFileCallback;
 
     // Method to print a page to a Pdf document with page size \a pageSize in location \a filePath.
-    void PrintToPDFFileWithCallback(const QPageLayout &pageLayout,
-                                    const QPageRanges &pageRanges,
-                                    bool printInColor,
-                                    const QString &filePath,
+    void PrintToPDFFileWithCallback(const QPageLayout &pageLayout, const QPageRanges &pageRanges,
+                                    bool printInColor, const QString &filePath, quint64 frameId,
                                     PrintToPDFFileCallback callback);
-    void PrintToPDFWithCallback(const QPageLayout &pageLayout,
-                                const QPageRanges &pageRanges,
-                                bool printInColor,
-                                bool useCustomMargins,
+    void PrintToPDFWithCallback(const QPageLayout &pageLayout, const QPageRanges &pageRanges,
+                                bool printInColor, bool useCustomMargins, quint64 frameId,
                                 PrintToPDFCallback callback);
 
 protected:
     explicit PrintViewManagerQt(content::WebContents*);
 
-    bool PrintToPDFInternal(const QPageLayout &, const QPageRanges &, bool printInColor, bool useCustomMargins = true);
+    bool PrintToPDFInternal(const QPageLayout &, const QPageRanges &, bool printInColor,
+                            bool useCustomMargins, quint64 frameId);
 
     // content::WebContentsObserver implementation.
     // Cancels the print job.
diff --git a/src/core/profile_adapter.cpp b/src/core/profile_adapter.cpp
index 6fec8aa2879..fa84f53e76f 100644
--- a/src/core/profile_adapter.cpp
+++ b/src/core/profile_adapter.cpp
@@ -6,7 +6,6 @@
 #include "base/files/file_util.h"
 #include "base/task/cancelable_task_tracker.h"
 #include "base/threading/thread_restrictions.h"
-#include "base/time/time_to_iso8601.h"
 #include "components/embedder_support/user_agent_utils.h"
 #include "components/favicon/core/favicon_service.h"
 #include "components/history/content/browser/history_database_helper.h"
@@ -45,13 +44,16 @@
 #include <QString>
 #include <QStandardPaths>
 
+using namespace Qt::StringLiterals;
+
 namespace {
 inline QString buildLocationFromStandardPath(const QString &standardPath, const QString &name) {
-    QString location = standardPath;
+    QString location;
+    location += standardPath;
     if (location.isEmpty())
-        location = QDir::homePath() % QLatin1String("/.") % QCoreApplication::applicationName();
+        location += QDir::homePath() % "/."_L1 % QCoreApplication::applicationName();
 
-    location.append(QLatin1String("/QtWebEngine/") % name);
+    location += "/QtWebEngine/"_L1 % name;
     return location;
 }
 }
@@ -64,7 +66,7 @@ ProfileAdapter::ProfileAdapter(const QString &storageName):
     , m_downloadPath(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation))
     , m_httpCacheType(DiskHttpCache)
     , m_persistentCookiesPolicy(AllowPersistentCookies)
-    , m_persistentPermissionsPolicy(PersistentPermissionsOnDisk)
+    , m_persistentPermissionsPolicy(PersistentPermissionsPolicy::StoreOnDisk)
     , m_visitedLinksPolicy(TrackVisitedLinksOnDisk)
     , m_clientHintsEnabled(true)
     , m_pushServiceEnabled(false)
@@ -229,6 +231,15 @@ void ProfileAdapter::removeDownload(quint32 downloadId)
     downloadManagerDelegate()->removeDownload(downloadId);
 }
 
+void ProfileAdapter::acceptDownload(quint32 downloadId, bool accepted, bool useDownloadTargetCallback,
+                                    const QString &path, int savePageFormat)
+{
+    if (useDownloadTargetCallback)
+        downloadManagerDelegate()->downloadTargetDetermined(downloadId, accepted, path);
+    else
+        downloadManagerDelegate()->savePathDetermined(downloadId, accepted, path, savePageFormat);
+}
+
 ProfileAdapter *ProfileAdapter::createDefaultProfileAdapter()
 {
     return WebEngineContext::current()->createDefaultProfileAdapter();
@@ -254,9 +265,9 @@ QString ProfileAdapter::dataPath() const
     // a location to do so.
     QString name = m_name;
     if (m_offTheRecord)
-        name = QStringLiteral("OffTheRecord");
+        name = u"OffTheRecord"_s;
     else if (m_name.isEmpty())
-        name = QStringLiteral("UnknownProfile");
+        name = u"UnknownProfile"_s;
     return buildLocationFromStandardPath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), name);
 }
 
@@ -266,6 +277,8 @@ void ProfileAdapter::setDataPath(const QString &path)
         return;
     m_dataPath = path;
     m_profile->setupPrefService();
+    m_profile->setupPermissionsManager();
+    m_profile->setupStoragePath();
     if (!m_profile->m_profileIOData->isClearHttpCacheInProgress())
         m_profile->m_profileIOData->resetNetworkContext();
     if (!m_offTheRecord && m_visitedLinksManager)
@@ -304,9 +317,9 @@ QString ProfileAdapter::httpCachePath() const
 {
     if (m_offTheRecord)
         return QString();
-    QString basePath = cachePath();
+    const QString basePath = cachePath();
     if (!basePath.isEmpty())
-        return basePath % QLatin1String("/Cache");
+        return basePath % "/Cache"_L1;
     return QString();
 }
 
@@ -334,9 +347,9 @@ void ProfileAdapter::setHttpUserAgent(const QString &userAgent)
         }
 
     m_profile->ForEachLoadedStoragePartition(
-                base::BindRepeating([](const std::string &user_agent, content::StoragePartition *storage_partition) {
-                    storage_partition->GetNetworkContext()->SetUserAgent(user_agent);
-                }, stdUserAgent));
+            [stdUserAgent](content::StoragePartition *storage_partition) {
+                storage_partition->GetNetworkContext()->SetUserAgent(stdUserAgent);
+            });
 }
 
 ProfileAdapter::HttpCacheType ProfileAdapter::httpCacheType() const
@@ -378,10 +391,10 @@ void ProfileAdapter::setPersistentCookiesPolicy(ProfileAdapter::PersistentCookie
 
 ProfileAdapter::PersistentPermissionsPolicy ProfileAdapter::persistentPermissionsPolicy() const
 {
-    if (m_persistentPermissionsPolicy == NoPersistentPermissions)
-        return NoPersistentPermissions;
+    if (m_persistentPermissionsPolicy == PersistentPermissionsPolicy::AskEveryTime)
+        return PersistentPermissionsPolicy::AskEveryTime;
     if (isOffTheRecord() || m_name.isEmpty())
-        return PersistentPermissionsInMemory;
+        return PersistentPermissionsPolicy::StoreInMemory;
     return m_persistentPermissionsPolicy;
 }
 
@@ -491,10 +504,9 @@ const QList<QByteArray> ProfileAdapter::customUrlSchemes() const
 
 void ProfileAdapter::updateCustomUrlSchemeHandlers()
 {
-    m_profile->ForEachLoadedStoragePartition(
-        base::BindRepeating([](content::StoragePartition *storage_partition) {
-            storage_partition->ResetURLLoaderFactories();
-        }));
+    m_profile->ForEachLoadedStoragePartition([](content::StoragePartition *storage_partition) {
+        storage_partition->ResetURLLoaderFactories();
+    });
 }
 
 void ProfileAdapter::removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *handler)
@@ -520,7 +532,7 @@ void ProfileAdapter::removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *handler)
 
 void ProfileAdapter::removeUrlScheme(const QByteArray &scheme)
 {
-    QByteArray canonicalScheme = scheme.toLower();
+    const QByteArray canonicalScheme = scheme.toLower();
     if (schemeType(canonicalScheme) == SchemeType::Protected) {
         qWarning("Cannot remove the URL scheme handler for an internal scheme: %s", scheme.constData());
         return;
@@ -532,7 +544,7 @@ void ProfileAdapter::removeUrlScheme(const QByteArray &scheme)
 void ProfileAdapter::installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler)
 {
     Q_ASSERT(handler);
-    QByteArray canonicalScheme = scheme.toLower();
+    const QByteArray canonicalScheme = scheme.toLower();
     SchemeType type = schemeType(canonicalScheme);
 
     if (type == SchemeType::Protected) {
@@ -569,24 +581,34 @@ UserResourceControllerHost *ProfileAdapter::userResourceController()
     return m_userResourceController.data();
 }
 
-void ProfileAdapter::permissionRequestReply(const QUrl &origin, PermissionType type, PermissionState reply)
+void ProfileAdapter::setPermission(const QUrl &origin, QWebEnginePermission::PermissionType permissionType,
+    QWebEnginePermission::State state, content::RenderFrameHost *rfh)
+{
+    static_cast<PermissionManagerQt*>(profile()->GetPermissionControllerDelegate())->setPermission(origin, permissionType, state, rfh);
+}
+
+QWebEnginePermission::State ProfileAdapter::getPermissionState(const QUrl &origin, QWebEnginePermission::PermissionType permissionType,
+    content::RenderFrameHost *rfh)
 {
-    static_cast<PermissionManagerQt*>(profile()->GetPermissionControllerDelegate())->permissionRequestReply(origin, type, reply);
+    return static_cast<PermissionManagerQt*>(profile()->GetPermissionControllerDelegate())->getPermissionState(origin, permissionType, rfh);
 }
 
-bool ProfileAdapter::checkPermission(const QUrl &origin, PermissionType type)
+QList<QWebEnginePermission> ProfileAdapter::listPermissions(const QUrl &origin, QWebEnginePermission::PermissionType permissionType)
 {
-    return static_cast<PermissionManagerQt*>(profile()->GetPermissionControllerDelegate())->checkPermission(origin, type);
+    if (persistentPermissionsPolicy() == ProfileAdapter::PersistentPermissionsPolicy::AskEveryTime)
+        return QList<QWebEnginePermission>();
+
+    return static_cast<PermissionManagerQt*>(profile()->GetPermissionControllerDelegate())->listPermissions(origin, permissionType);
 }
 
 QString ProfileAdapter::httpAcceptLanguageWithoutQualities() const
 {
-    const QStringList list = m_httpAcceptLanguage.split(QLatin1Char(','));
     QString out;
-    for (const QString &str : list) {
-        if (!out.isEmpty())
-            out.append(QLatin1Char(','));
-        out.append(str.split(QLatin1Char(';')).first());
+    auto sep = ""_L1;
+    for (auto lang : m_httpAcceptLanguage.tokenize(u',')) {
+        out += sep;
+        out += *lang.tokenize(u';').begin(); // tokenize() is never empty with KeepEmptyParts!
+        sep = ","_L1;
     }
     return out;
 }
@@ -614,9 +636,9 @@ void ProfileAdapter::setHttpAcceptLanguage(const QString &httpAcceptLanguage)
     }
 
     m_profile->ForEachLoadedStoragePartition(
-        base::BindRepeating([](std::string accept_language, content::StoragePartition *storage_partition) {
-            storage_partition->GetNetworkContext()->SetAcceptLanguage(accept_language);
-        }, http_accept_language));
+            [http_accept_language](content::StoragePartition *storage_partition) {
+                storage_partition->GetNetworkContext()->SetAcceptLanguage(http_accept_language);
+            });
 }
 
 QVariant ProfileAdapter::clientHint(ClientHint clientHint) const
diff --git a/src/core/profile_adapter.h b/src/core/profile_adapter.h
index 18d82ba9631..4301cd8a2e7 100644
--- a/src/core/profile_adapter.h
+++ b/src/core/profile_adapter.h
@@ -28,6 +28,7 @@
 #include <QtWebEngineCore/qwebenginecookiestore.h>
 #include <QtWebEngineCore/qwebengineurlrequestinterceptor.h>
 #include <QtWebEngineCore/qwebengineurlschemehandler.h>
+#include <QtWebEngineCore/qwebenginepermission.h>
 #include "net/qrc_url_scheme_handler.h"
 
 QT_FORWARD_DECLARE_CLASS(QObject)
@@ -36,6 +37,10 @@ namespace base {
 class CancelableTaskTracker;
 }
 
+namespace content {
+class RenderFrameHost;
+}
+
 namespace QtWebEngineCore {
 
 class UserNotificationController;
@@ -72,6 +77,9 @@ class Q_WEBENGINECORE_EXPORT ProfileAdapter : public QObject
     void pauseDownload(quint32 downloadId);
     void resumeDownload(quint32 downloadId);
     void removeDownload(quint32 downloadId);
+    void acceptDownload(quint32 downloadId, bool accepted,
+                        bool useDownloadTargetCallback, const QString &path,
+                        int savePageFormat);
 
     ProfileQt *profile();
     bool ensureDataPathExists();
@@ -127,26 +135,10 @@ class Q_WEBENGINECORE_EXPORT ProfileAdapter : public QObject
         TrackVisitedLinksOnDisk,
     };
 
-    enum PersistentPermissionsPolicy {
-        NoPersistentPermissions = 0,
-        PersistentPermissionsInMemory,
-        PersistentPermissionsOnDisk,
-    };
-
-    enum PermissionType {
-        UnsupportedPermission = 0,
-        GeolocationPermission = 1,
-        NotificationPermission = 2,
-        AudioCapturePermission = 3,
-        VideoCapturePermission = 4,
-        ClipboardReadWrite = 5,
-        LocalFontsPermission = 6,
-    };
-
-    enum PermissionState {
-        AskPermission = 0,
-        AllowedPermission = 1,
-        DeniedPermission = 2
+    enum class PersistentPermissionsPolicy : quint8 {
+        AskEveryTime = 0,
+        StoreInMemory,
+        StoreOnDisk,
     };
 
     enum ClientHint : uchar {
@@ -187,8 +179,12 @@ class Q_WEBENGINECORE_EXPORT ProfileAdapter : public QObject
     const QList<QByteArray> customUrlSchemes() const;
     UserResourceControllerHost *userResourceController();
 
-    void permissionRequestReply(const QUrl &origin, PermissionType type, PermissionState reply);
-    bool checkPermission(const QUrl &origin, PermissionType type);
+    void setPermission(const QUrl &origin, QWebEnginePermission::PermissionType permissionType,
+        QWebEnginePermission::State state, content::RenderFrameHost *rfh = nullptr);
+    QWebEnginePermission::State getPermissionState(const QUrl &origin, QWebEnginePermission::PermissionType permissionType,
+        content::RenderFrameHost *rfh = nullptr);
+    QList<QWebEnginePermission> listPermissions(const QUrl &origin = QUrl(),
+        QWebEnginePermission::PermissionType permissionType = QWebEnginePermission::PermissionType::Unsupported);
 
     QString httpAcceptLanguageWithoutQualities() const;
     QString httpAcceptLanguage() const;
@@ -200,6 +196,7 @@ class Q_WEBENGINECORE_EXPORT ProfileAdapter : public QObject
     void setClientHintsEnabled(bool enabled);
     void resetClientHints();
 
+
     void clearHttpCache();
 
 #if QT_CONFIG(ssl)
diff --git a/src/core/profile_adapter_client.h b/src/core/profile_adapter_client.h
index 06ac0de8b37..cb83e82799b 100644
--- a/src/core/profile_adapter_client.h
+++ b/src/core/profile_adapter_client.h
@@ -82,19 +82,19 @@ class Q_WEBENGINECORE_EXPORT ProfileAdapterClient
     };
 
     struct DownloadItemInfo {
-        const quint32 id;
-        const QUrl url;
-        const int state;
-        const qint64 totalBytes;
-        const qint64 receivedBytes;
-        const QString mimeType;
-
+        quint32 id;
+        QUrl url;
+        int state;
+        qint64 totalBytes;
+        qint64 receivedBytes;
+        QString mimeType;
         QString path;
         int savePageFormat;
         bool accepted;
         bool paused;
         bool done;
         bool isSavePageDownload;
+        bool useDownloadTargetCallback;
         int downloadInterruptReason;
         WebContentsAdapterClient *page;
         QString suggestedFileName;
@@ -103,7 +103,7 @@ class Q_WEBENGINECORE_EXPORT ProfileAdapterClient
 
     virtual ~ProfileAdapterClient() { }
 
-    virtual void downloadRequested(DownloadItemInfo &info) = 0;
+    virtual void downloadRequested(const DownloadItemInfo &info) = 0;
     virtual void downloadUpdated(const DownloadItemInfo &info) = 0;
     virtual void showNotification(QSharedPointer<UserNotificationController> &) { }
 
diff --git a/src/core/profile_io_data_qt.cpp b/src/core/profile_io_data_qt.cpp
index 859aff8d4fa..7bab41530dc 100644
--- a/src/core/profile_io_data_qt.cpp
+++ b/src/core/profile_io_data_qt.cpp
@@ -37,8 +37,6 @@ ProfileIODataQt::~ProfileIODataQt()
 {
     if (content::BrowserThread::IsThreadInitialized(content::BrowserThread::IO))
         DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
-    m_resourceContext.reset();
 }
 
 QPointer<ProfileAdapter> ProfileIODataQt::profileAdapter()
@@ -64,16 +62,11 @@ void ProfileIODataQt::shutdownOnUIThread()
 
     bool posted = content::BrowserThread::DeleteSoon(content::BrowserThread::IO, FROM_HERE, this);
     if (!posted) {
-        qWarning() << "Could not delete ProfileIODataQt on io thread !";
+        qWarning("Could not delete ProfileIODataQt on io thread !");
         delete this;
     }
 }
 
-content::ResourceContext *ProfileIODataQt::resourceContext()
-{
-    return m_resourceContext.get();
-}
-
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 extensions::ExtensionSystemQt* ProfileIODataQt::GetExtensionSystem()
 {
@@ -91,7 +84,6 @@ void ProfileIODataQt::initializeOnUIThread()
 {
     m_profileAdapter = m_profile->profileAdapter();
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-    m_resourceContext.reset(new content::ResourceContext());
     m_cookieDelegate = new CookieMonsterDelegateQt();
     m_cookieDelegate->setClient(m_profile->profileAdapter()->cookieStore());
     m_proxyConfigMonitor.reset(new ProxyConfigMonitor(m_profile->GetPrefs()));
@@ -152,15 +144,12 @@ void ProfileIODataQt::resetNetworkContext()
     Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     Q_ASSERT(m_clearHttpCacheState != Removing);
     setFullConfiguration();
-    m_profile->ForEachLoadedStoragePartition(
-            base::BindRepeating([](ProfileIODataQt *profileData,
-                                   content::StoragePartition *storage) {
-                storage->SetNetworkContextCreatedObserver(profileData);
-
-                auto storage_impl = static_cast<content::StoragePartitionImpl *>(storage);
-                storage_impl->ResetURLLoaderFactories();
-                storage_impl->ResetNetworkContext();
-            }, this));
+    m_profile->ForEachLoadedStoragePartition([this](content::StoragePartition *storage) {
+        storage->SetNetworkContextCreatedObserver(this);
+        auto storage_impl = static_cast<content::StoragePartitionImpl *>(storage);
+        storage_impl->ResetURLLoaderFactories();
+        storage_impl->ResetNetworkContext();
+    });
 }
 
 void ProfileIODataQt::OnNetworkContextCreated(content::StoragePartition *storage)
@@ -174,12 +163,10 @@ void ProfileIODataQt::OnNetworkContextCreated(content::StoragePartition *storage
 
     bool pendingReset = false;
     m_profile->ForEachLoadedStoragePartition(
-            base::BindRepeating([](bool *pendingReset,
-                                   ProfileIODataQt *profileData,
-                                   content::StoragePartition *storage) {
-                if (storage->GetNetworkContextCreatedObserver() == profileData)
-                    *pendingReset = true;
-            }, &pendingReset, this));
+            [&pendingReset, this](content::StoragePartition *storage) {
+                if (storage->GetNetworkContextCreatedObserver() == this)
+                    pendingReset = true;
+            });
 
     if (pendingReset)
         return;
diff --git a/src/core/profile_io_data_qt.h b/src/core/profile_io_data_qt.h
index 0d032e4dcfc..a871307d70f 100644
--- a/src/core/profile_io_data_qt.h
+++ b/src/core/profile_io_data_qt.h
@@ -20,10 +20,6 @@ namespace mojom {
 class CertVerifierCreationParams;
 }}
 
-namespace content {
-class ResourceContext;
-}
-
 namespace extensions {
 class ExtensionSystemQt;
 }
@@ -66,7 +62,6 @@ class ProfileIODataQt : public content::StoragePartition::NetworkContextCreatedO
     virtual ~ProfileIODataQt();
 
     QPointer<ProfileAdapter> profileAdapter();
-    content::ResourceContext *resourceContext();
 #if BUILDFLAG(ENABLE_EXTENSIONS)
     extensions::ExtensionSystemQt* GetExtensionSystem();
 #endif // BUILDFLAG(ENABLE_EXTENSIONS)
@@ -104,7 +99,6 @@ class ProfileIODataQt : public content::StoragePartition::NetworkContextCreatedO
     void removeBrowsingDataRemoverObserver();
 
     ProfileQt *m_profile;
-    std::unique_ptr<content::ResourceContext> m_resourceContext;
     scoped_refptr<CookieMonsterDelegateQt> m_cookieDelegate;
     QPointer<ProfileAdapter> m_profileAdapter; // never dereferenced in IO thread and it is passed by qpointer
     ProfileAdapter::PersistentCookiesPolicy m_persistentCookiesPolicy;
diff --git a/src/core/profile_qt.cpp b/src/core/profile_qt.cpp
index d8a6c191cb2..eaaa4f2536b 100644
--- a/src/core/profile_qt.cpp
+++ b/src/core/profile_qt.cpp
@@ -15,10 +15,11 @@
 #include "qtwebenginecoreglobal_p.h"
 #include "type_conversion.h"
 #include "web_engine_library_info.h"
-#include "web_engine_context.h"
 
 #include "base/base_paths.h"
+#include "base/path_service.h"
 #include "base/files/file_util.h"
+#include "base/task/thread_pool.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_prefs/user_prefs.h"
@@ -41,6 +42,11 @@
 
 namespace QtWebEngineCore {
 
+enum {
+    PATH_QT_START = 1000, // Same as PATH_START in chrome_paths.h; no chance of collision
+    PATH_QT_END = 1999
+};
+
 ProfileQt::ProfileQt(ProfileAdapter *profileAdapter)
     : m_profileIOData(new ProfileIODataQt(this))
     , m_profileAdapter(profileAdapter)
@@ -54,6 +60,7 @@ ProfileQt::ProfileQt(ProfileAdapter *profileAdapter)
         : profile_metrics::BrowserProfileType::kRegular);
 
     setupPrefService();
+    setupStoragePath();
 
     // Mark the context as live. This prevents the use-after-free DCHECK in
     // AssertBrowserContextWasntDestroyed from being triggered when a new
@@ -116,11 +123,6 @@ bool ProfileQt::IsOffTheRecord()
     return m_profileAdapter->isOffTheRecord();
 }
 
-content::ResourceContext *ProfileQt::GetResourceContext()
-{
-    return m_profileIOData->resourceContext();
-}
-
 content::DownloadManagerDelegate *ProfileQt::GetDownloadManagerDelegate()
 {
     return m_profileAdapter->downloadManagerDelegate();
@@ -260,6 +262,40 @@ void ProfileQt::setupPrefService()
 #endif
 }
 
+void ProfileQt::setupStoragePath()
+{
+#if defined(Q_OS_WIN)
+    if (IsOffTheRecord())
+        return;
+
+    // Mark the storage path as a "safe" path, allowing the path service on Windows to
+    // block file execution and prevent assertions when saving blobs to disk.
+    // We keep a static list of all profile paths
+
+    base::FilePath thisStoragePath = GetPath();
+
+    static std::vector<base::FilePath> storagePaths;
+    auto it = std::find(storagePaths.begin(), storagePaths.end(), thisStoragePath);
+    if (it == storagePaths.end()) {
+        if (storagePaths.size() >= (PATH_QT_END - PATH_QT_START)) {
+            qWarning("Number of profile paths exceeded %ull, storage may break",
+                     static_cast<qulonglong>(PATH_QT_END - PATH_QT_START));
+            return;
+        }
+
+        storagePaths.push_back(thisStoragePath);
+        it = storagePaths.end() - 1;
+    }
+
+    int pathID = PATH_QT_START + (it - storagePaths.begin());
+    base::ThreadPool::PostTaskAndReplyWithResult(FROM_HERE, { base::MayBlock() },
+        base::BindOnce(base::PathService::Override, PATH_QT_START + (it - storagePaths.begin()), thisStoragePath),
+        base::BindOnce([](int pathID_, bool succeeded) {
+            if (succeeded) base::SetExtraNoExecuteAllowedPath(pathID_);
+        }, pathID));
+#endif // defined(Q_OS_WIN)
+}
+
 void ProfileQt::setupPermissionsManager()
 {
     m_permissionManager.reset(new PermissionManagerQt(profileAdapter()));
diff --git a/src/core/profile_qt.h b/src/core/profile_qt.h
index 4865632559e..f5ca0375915 100644
--- a/src/core/profile_qt.h
+++ b/src/core/profile_qt.h
@@ -11,10 +11,6 @@
 
 class PrefService;
 
-namespace content {
-class ResourceContext;
-}
-
 namespace extensions {
 class ExtensionSystemQt;
 }
@@ -40,7 +36,6 @@ class ProfileQt : public Profile
     base::FilePath GetPath() override;
     bool IsOffTheRecord() override;
 
-    content::ResourceContext *GetResourceContext() override;
     content::DownloadManagerDelegate *GetDownloadManagerDelegate() override;
     content::BrowserPluginGuestManager *GetGuestManager() override;
     storage::SpecialStoragePolicy *GetSpecialStoragePolicy() override;
@@ -77,6 +72,7 @@ class ProfileQt : public Profile
     // Build/Re-build the preference service. Call when updating the storage
     // data path.
     void setupPrefService();
+    void setupStoragePath();
     void setupPermissionsManager();
 
     PrefServiceAdapter &prefServiceAdapter();
diff --git a/src/core/render_view_context_menu_qt.cpp b/src/core/render_view_context_menu_qt.cpp
index b7aabfd5b66..089b9202003 100644
--- a/src/core/render_view_context_menu_qt.cpp
+++ b/src/core/render_view_context_menu_qt.cpp
@@ -36,7 +36,7 @@ namespace QtWebEngineCore {
             QT_TRANSLATE_NOOP("RenderViewContextMenuQt", "Save page"),
             QT_TRANSLATE_NOOP("RenderViewContextMenuQt", "View page source")
         };
-        return QCoreApplication::translate("RenderViewContextMenuQt", qUtf8Printable(names[menuItem]));
+        return QCoreApplication::translate("RenderViewContextMenuQt", names[menuItem]);
     }
 
     RenderViewContextMenuQt::RenderViewContextMenuQt(QWebEngineContextMenuRequest *request)
diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp
index 81817442ae1..707afca92bd 100644
--- a/src/core/render_widget_host_view_qt.cpp
+++ b/src/core/render_widget_host_view_qt.cpp
@@ -21,7 +21,6 @@
 #include "content/browser/renderer_host/frame_tree.h"
 #include "content/browser/renderer_host/frame_tree_node.h"
 #include "content/browser/renderer_host/cursor_manager.h"
-#include "content/browser/renderer_host/input/synthetic_gesture_target.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/renderer_host/render_view_host_delegate.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
@@ -29,6 +28,7 @@
 #include "content/browser/renderer_host/ui_events_helper.h"
 #include "content/common/content_switches_internal.h"
 #include "content/common/cursors/webcursor.h"
+#include "content/common/input/synthetic_gesture_target.h"
 #include "content/public/browser/web_contents.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/cursor/cursor.h"
@@ -160,6 +160,8 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost *widget
                                        &m_delegatedFrameHostClient,
                                        true /* should_register_frame_sink_id */));
 
+    m_delegatedFrameHost->SetIsFrameSinkIdOwner(true);
+
     content::ImageTransportFactory *imageTransportFactory = content::ImageTransportFactory::GetInstance();
     ui::ContextFactory *contextFactory = imageTransportFactory->GetContextFactory();
     m_uiCompositor.reset(new FlingingCompositor(
@@ -386,7 +388,7 @@ void RenderWidgetHostViewQt::UpdateBackgroundColor()
     m_rootLayer->SetColor(color);
     m_uiCompositor->SetBackgroundColor(color);
 
-    if (color == SK_ColorTRANSPARENT)
+    if (color == SK_ColorTRANSPARENT && host()->owner_delegate())
         host()->owner_delegate()->SetBackgroundOpaque(false);
 }
 
@@ -874,6 +876,11 @@ bool RenderWidgetHostViewQt::updateScreenInfo()
         return false;
 
     display::ScreenInfos newScreenInfos = screenInfosFromQtForUpdate(window->screen());
+
+    // We always want to use the scale from our current window
+    // This screen information is stored on a per-view basis
+    auto &screen = newScreenInfos.mutable_current();
+    screen.device_scale_factor = window->devicePixelRatio();
     if (screen_infos_ == newScreenInfos)
         return false;
 
@@ -904,7 +911,8 @@ void RenderWidgetHostViewQt::WheelEventAck(const blink::WebMouseWheelEvent &even
 {
     if (event.phase == blink::WebMouseWheelEvent::kPhaseEnded)
         return;
-    Q_ASSERT(m_wheelAckPending);
+    if (!m_wheelAckPending)
+        return;
     m_wheelAckPending = false;
     while (!m_pendingWheelEvents.isEmpty() && !m_wheelAckPending) {
         blink::WebMouseWheelEvent webEvent = m_pendingWheelEvents.takeFirst();
@@ -916,9 +924,10 @@ void RenderWidgetHostViewQt::WheelEventAck(const blink::WebMouseWheelEvent &even
 }
 
 void RenderWidgetHostViewQt::GestureEventAck(const blink::WebGestureEvent &event,
-                                             blink::mojom::InputEventResultState ack_result,
-                                             blink::mojom::ScrollResultDataPtr scroll_result_data)
+                                             blink::mojom::InputEventResultState ack_result)
 {
+    ForwardTouchpadZoomEventIfNecessary(event, ack_result);
+
     // Forward unhandled scroll events back as wheel events
     if (event.GetType() != blink::WebInputEvent::Type::kGestureScrollUpdate)
         return;
diff --git a/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h
index 43cff430bc8..49c1125fc26 100644
--- a/src/core/render_widget_host_view_qt.h
+++ b/src/core/render_widget_host_view_qt.h
@@ -95,8 +95,7 @@ class RenderWidgetHostViewQt
     void WheelEventAck(const blink::WebMouseWheelEvent &event,
                        blink::mojom::InputEventResultState ack_result) override;
     void GestureEventAck(const blink::WebGestureEvent &event,
-                         blink::mojom::InputEventResultState ack_result,
-                         blink::mojom::ScrollResultDataPtr scroll_result_data) override;
+                         blink::mojom::InputEventResultState ack_result) override;
     content::MouseWheelPhaseHandler *GetMouseWheelPhaseHandler() override;
     viz::ScopedSurfaceIdAllocator DidUpdateVisualProperties(const cc::RenderFrameMetadata &metadata) override;
     void OnDidUpdateVisualPropertiesComplete(const cc::RenderFrameMetadata &metadata);
diff --git a/src/core/render_widget_host_view_qt_delegate_client.cpp b/src/core/render_widget_host_view_qt_delegate_client.cpp
index 3e8cad6693a..493648ceaa4 100644
--- a/src/core/render_widget_host_view_qt_delegate_client.cpp
+++ b/src/core/render_widget_host_view_qt_delegate_client.cpp
@@ -14,6 +14,7 @@
 #include "content/browser/renderer_host/render_widget_host_input_event_router.h"
 #include "ui/touch_selection/touch_selection_controller.h"
 
+#include <QDebug>
 #include <QEvent>
 #include <QInputMethodEvent>
 #include <QSet>
@@ -24,6 +25,8 @@
 #include <QtGui/private/qguiapplication_p.h>
 #include <QtGui/private/qinputcontrol_p.h>
 
+using namespace Qt::StringLiterals;
+
 namespace QtWebEngineCore {
 
 static inline int firstAvailableId(const QMap<int, int> &map)
@@ -76,18 +79,42 @@ class MotionEventQt : public ui::MotionEvent
         , flags(flagsFromModifiers(modifiers))
         , index(index)
     {
-        // index is only valid for ACTION_DOWN and ACTION_UP and should correspond to the point causing it
-        // see blink_event_util.cc:ToWebTouchPointState for details
-        Q_ASSERT_X((action != Action::POINTER_DOWN && action != Action::POINTER_UP && index == -1)
-                || (action == Action::POINTER_DOWN && index >= 0 && touchPoint(index).state() == QEventPoint::Pressed)
-                || (action == Action::POINTER_UP && index >= 0 && touchPoint(index).state() == QEventPoint::Released),
-                "MotionEventQt", qPrintable(QString("action: %1, index: %2, state: %3").arg(int(action)).arg(index).arg(touchPoint(index).state())));
+#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS)
+        // index is only valid for POINTER_DOWN and POINTER_UP and should correspond to the point
+        // causing it see blink_event_util.cc:ToWebTouchPointState for details
+        if (action == Action::POINTER_DOWN || action == Action::POINTER_UP) {
+            const auto actionString = (action == Action::POINTER_DOWN ? "Action::POINTER_DOWN"_L1
+                                                                      : "Action::POINTER_UP"_L1);
+            Q_ASSERT_X(index >= 0 && index < touchPoints.size(), "MotionEventQt",
+                       qPrintable("Invalid index for "_L1 + actionString + ": "_L1
+                                  + QString::number(index)));
+
+            const QEventPoint::State state = touchPoint(index).state();
+            QString stateString;
+            QDebug(&stateString) << state;
+            Q_ASSERT_X(
+                    (action == Action::POINTER_DOWN && state == QEventPoint::Pressed)
+                            || (action == Action::POINTER_UP && state == QEventPoint::Released),
+                    "MotionEventQt",
+                    qPrintable("Unexpected state for "_L1 + actionString + ": "_L1 + stateString));
+        } else {
+            Q_ASSERT_X(index == -1, "MotionEventQt",
+                       qPrintable("Unexpected index for action "_L1
+                                  + QString::number(static_cast<int>(action)) + ": "_L1
+                                  + QString::number(index)));
+        }
+#endif
     }
 
     uint32_t GetUniqueEventId() const override { return eventId; }
     Action GetAction() const override { return action; }
     int GetActionIndex() const override { return index; }
     size_t GetPointerCount() const override { return touchPoints.size(); }
+    int32_t GetSourceDeviceId(size_t pointer_index) const override
+    {
+        return static_cast<int32_t>(
+                touchPoints[pointer_index].second.device()->uniqueId().numericId());
+    }
     int GetPointerId(size_t pointer_index) const override
     {
         return touchPoints[pointer_index].first;
@@ -194,8 +221,10 @@ void RenderWidgetHostViewQtDelegateClient::visualPropertiesChanged()
 
     bool screenInfoChanged = m_rwhv->updateScreenInfo();
 
-    if (m_viewRectInDips != oldViewRect || m_windowRectInDips != oldWindowRect)
+    if (m_viewRectInDips != oldViewRect || m_windowRectInDips != oldWindowRect) {
         m_rwhv->host()->SendScreenRects();
+        m_rwhv->synchronizeVisualProperties(std::nullopt);
+    }
 
     if (m_viewRectInDips.size() != oldViewRect.size() || screenInfoChanged)
         m_rwhv->synchronizeVisualProperties(absl::nullopt);
@@ -731,8 +760,8 @@ void RenderWidgetHostViewQtDelegateClient::handleInputMethodEvent(QInputMethodEv
     if (!m_rwhv->host())
         return;
 
-    QString commitString = event->commitString();
-    QString preeditString = event->preeditString();
+    const QString commitString = event->commitString();
+    const QString preeditString = event->preeditString();
 
     int cursorPositionInPreeditString = -1;
     gfx::Range selectionRange = gfx::Range::InvalidRange();
diff --git a/src/core/render_widget_host_view_qt_delegate_item.cpp b/src/core/render_widget_host_view_qt_delegate_item.cpp
index 23e5bc93594..7d7ae9054f2 100644
--- a/src/core/render_widget_host_view_qt_delegate_item.cpp
+++ b/src/core/render_widget_host_view_qt_delegate_item.cpp
@@ -5,15 +5,20 @@
 
 #include "render_widget_host_view_qt_delegate_client.h"
 
+#include <QtCore/qrunnable.h>
+#include <QtCore/qthread.h>
 #include <QtGui/qevent.h>
 #include <QtGui/qguiapplication.h>
 #include <QtGui/qwindow.h>
 #include <QtQuick/qsgimagenode.h>
+#include <rhi/qrhi.h>
 
 #if QT_CONFIG(accessibility)
 #include <QtGui/qaccessible.h>
 #endif
 
+using namespace Qt::StringLiterals;
+
 namespace QtWebEngineCore {
 
 RenderWidgetHostViewQtDelegateItem::RenderWidgetHostViewQtDelegateItem(RenderWidgetHostViewQtDelegateClient *client, bool isPopup)
@@ -30,12 +35,15 @@ RenderWidgetHostViewQtDelegateItem::RenderWidgetHostViewQtDelegateItem(RenderWid
         setFocus(true);
         setActiveFocusOnTab(true);
     }
-    bind(client->compositorId());
+    bind(client->compositorId()); // Compositor::Observer
 }
 
 RenderWidgetHostViewQtDelegateItem::~RenderWidgetHostViewQtDelegateItem()
 {
-    releaseTextureResources();
+    if (QQuickItem::window())
+        releaseResources();
+
+    unbind(); // Compositor::Observer
     if (m_widgetDelegate) {
         m_widgetDelegate->Unbind();
         m_widgetDelegate->Destroy();
@@ -53,8 +61,8 @@ void RenderWidgetHostViewQtDelegateItem::initAsPopup(const QRect &screenRect)
 QRectF RenderWidgetHostViewQtDelegateItem::viewGeometry() const
 {
     // Transform the entire rect to find the correct top left corner.
-    const QPointF p1 = mapToGlobal(mapFromScene(QPointF(0, 0)));
-    const QPointF p2 = mapToGlobal(mapFromScene(QPointF(width(), height())));
+    const QPointF p1 = mapToGlobal(mapFromItem(this, QPointF(0, 0)));
+    const QPointF p2 = mapToGlobal(mapFromItem(this, QPointF(width(), height())));
     QRectF geometry = QRectF(p1, p2).normalized();
     // But keep the size untransformed to behave like other QQuickItems.
     geometry.setSize(size());
@@ -126,6 +134,8 @@ void RenderWidgetHostViewQtDelegateItem::readyToSwap()
 
 void RenderWidgetHostViewQtDelegateItem::updateCursor(const QCursor &cursor)
 {
+    if (m_widgetDelegate)
+        m_widgetDelegate->SetCursor(cursor);
     setCursor(cursor);
 }
 
@@ -317,9 +327,12 @@ void RenderWidgetHostViewQtDelegateItem::itemChange(ItemChange change, const Ite
 {
     QQuickItem::itemChange(change, value);
     if (change == QQuickItem::ItemSceneChange) {
-        for (const QMetaObject::Connection &c : std::as_const(m_windowConnections))
-            disconnect(c);
-        m_windowConnections.clear();
+        if (!m_windowConnections.isEmpty()) {
+            for (const QMetaObject::Connection &c : std::as_const(m_windowConnections))
+                disconnect(c);
+            m_windowConnections.clear();
+        }
+
         if (value.window) {
             m_windowConnections.append(connect(value.window, &QQuickWindow::beforeRendering,
                                                this, &RenderWidgetHostViewQtDelegateItem::onBeforeRendering, Qt::DirectConnection));
@@ -329,12 +342,14 @@ void RenderWidgetHostViewQtDelegateItem::itemChange(ItemChange change, const Ite
             m_windowConnections.append(connect(value.window, SIGNAL(xChanged(int)), SLOT(onWindowPosChanged())));
             m_windowConnections.append(
                     connect(value.window, SIGNAL(yChanged(int)), SLOT(onWindowPosChanged())));
-            m_windowConnections.append(
-                    connect(value.window, &QQuickWindow::sceneGraphAboutToStop, this,
-                            &RenderWidgetHostViewQtDelegateItem::releaseTextureResources,
-                            Qt::DirectConnection));
+            m_windowConnections.append(connect(
+                    value.window, &QQuickWindow::sceneGraphAboutToStop, this,
+                    &RenderWidgetHostViewQtDelegateItem::releaseResources, Qt::DirectConnection));
+            m_windowConnections.append(connect(
+                    value.window, &QQuickWindow::sceneGraphInvalidated, this,
+                    &RenderWidgetHostViewQtDelegateItem::releaseResources, Qt::DirectConnection));
             if (!m_isPopup)
-                m_windowConnections.append(connect(value.window, SIGNAL(closing(QQuickCloseEvent *)), SLOT(onHide())));
+                m_windowConnections.append(connect(value.window, SIGNAL(closing(QQuickCloseEvent*)), SLOT(onHide())));
         }
         m_client->visualPropertiesChanged();
     } else if (change == QQuickItem::ItemVisibleHasChanged) {
@@ -345,6 +360,63 @@ void RenderWidgetHostViewQtDelegateItem::itemChange(ItemChange change, const Ite
             if (!m_isPopup)
                 onHide();
         }
+    } else if (change == QQuickItem::ItemDevicePixelRatioHasChanged) {
+        m_client->visualPropertiesChanged();
+    }
+}
+
+class CleanupJob : public QRunnable
+{
+public:
+    CleanupJob(Compositor::Handle<Compositor> compositor) : m_compositor(std::move(compositor)) { }
+
+    ~CleanupJob()
+    {
+        if (m_compositor->hasResources()) {
+            qWarning("Failed to release graphics resources because the clean-up render job was "
+                     "deleted.");
+        }
+    }
+
+    void run() override { m_compositor->releaseResources(); }
+
+private:
+    Compositor::Handle<Compositor> m_compositor;
+};
+
+void RenderWidgetHostViewQtDelegateItem::releaseResources()
+{
+    auto comp = compositor();
+    if (!comp || comp->type() != Compositor::Type::Native || !comp->hasResources())
+        return;
+
+    comp->releaseTexture();
+
+    QQuickWindow *win = QQuickItem::window();
+    if (!win) {
+        qWarning("Failed to release graphics resources because QQuickWindow is not available.");
+        return;
+    }
+
+    QRhi *rhi = win->rhi();
+    if (!rhi) {
+        qWarning("Failed to release graphics resources because RHI is not available.");
+        return;
+    }
+
+    // Do not schedule clean-up if the resources were created on the current thread.
+    if (QThread::currentThread() == rhi->thread()) {
+        comp->releaseResources();
+        return;
+    }
+
+    if (win->isExposed())
+        win->scheduleRenderJob(new CleanupJob(std::move(comp)), QQuickWindow::NoStage);
+    else {
+        // TODO: Try to find a proper way to schedule job on the render thread if the window is
+        // not exposed.
+        // This is reproducible with ./tst_qquickwebengineviewgraphics simpleGraphics simpleGraphics
+        qWarning("Failed to release graphics resources because QQuickWindow is not exposed.");
     }
 }
 
@@ -354,6 +426,12 @@ QSGNode *RenderWidgetHostViewQtDelegateItem::updatePaintNode(QSGNode *oldNode, U
     if (!comp)
         return oldNode;
 
+    if (comp->type() == Compositor::Type::Native
+        && QGuiApplication::platformName() == "offscreen"_L1) {
+        comp->swapFrame();
+        return oldNode;
+    }
+
     QQuickWindow *win = QQuickItem::window();
 
     QSGImageNode *node = nullptr;
@@ -423,15 +501,6 @@ void RenderWidgetHostViewQtDelegateItem::onHide()
     m_client->forwardEvent(&event);
 }
 
-void RenderWidgetHostViewQtDelegateItem::releaseTextureResources()
-{
-    auto comp = compositor();
-    if (!comp || comp->type() != Compositor::Type::Native)
-        return;
-
-    comp->releaseResources();
-}
-
 void RenderWidgetHostViewQtDelegateItem::adapterClientChanged(WebContentsAdapterClient *client)
 {
     m_adapterClient = client;
diff --git a/src/core/render_widget_host_view_qt_delegate_item.h b/src/core/render_widget_host_view_qt_delegate_item.h
index 0da6b494834..f0351aa9fe4 100644
--- a/src/core/render_widget_host_view_qt_delegate_item.h
+++ b/src/core/render_widget_host_view_qt_delegate_item.h
@@ -35,6 +35,7 @@ class WidgetDelegate
     virtual void Destroy() = 0;
     virtual void Resize(int, int) { }
     virtual QWindow *Window() { return nullptr; }
+    virtual void SetCursor(const QCursor &) { }
     virtual void unhandledWheelEvent(QWheelEvent *) { }
 };
 
@@ -94,6 +95,7 @@ class Q_WEBENGINECORE_EXPORT RenderWidgetHostViewQtDelegateItem
     void inputMethodEvent(QInputMethodEvent *event) override;
     void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
     void itemChange(ItemChange change, const ItemChangeData &value) override;
+    void releaseResources() override;
     QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override;
 
     void adapterClientChanged(WebContentsAdapterClient *client) override;
@@ -103,7 +105,6 @@ private Q_SLOTS:
     void onBeforeRendering();
     void onAfterFrameEnd();
     void onWindowPosChanged();
-    void releaseTextureResources();
     void onHide();
 
 private:
diff --git a/src/core/renderer/content_renderer_client_qt.cpp b/src/core/renderer/content_renderer_client_qt.cpp
index cc127e55fc2..a7733af772c 100644
--- a/src/core/renderer/content_renderer_client_qt.cpp
+++ b/src/core/renderer/content_renderer_client_qt.cpp
@@ -11,7 +11,6 @@
 #include "web_engine_library_info.h"
 
 #include "base/task/sequenced_task_runner.h"
-#include "components/autofill/content/renderer/autofill_agent.h"
 #include "components/autofill/content/renderer/password_autofill_agent.h"
 #include "components/autofill/content/renderer/password_generation_agent.h"
 #include "components/cdm/renderer/external_clear_key_key_system_info.h"
@@ -37,6 +36,7 @@
 #include "third_party/blink/public/platform/web_url_error.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/webui/jstemplate_builder.h"
+#include "ui/base/webui/web_ui_util.h"
 
 #if QT_CONFIG(webengine_spellchecker)
 #include "components/spellcheck/renderer/spellcheck.h"
@@ -178,7 +178,7 @@ void ContentRendererClientQt::RenderFrameCreated(content::RenderFrame *render_fr
     new QtWebEngineCore::ContentSettingsObserverQt(render_frame);
 
 #if QT_CONFIG(webengine_spellchecker)
-    new SpellCheckProvider(render_frame, m_spellCheck.data(), this);
+    new SpellCheckProvider(render_frame, m_spellCheck.data());
 #endif
 #if QT_CONFIG(webengine_printing_and_pdf)
     new printing::PrintRenderFrameHelper(render_frame, base::WrapUnique(new PrintWebViewHelperDelegateQt()));
@@ -188,22 +188,24 @@ void ContentRendererClientQt::RenderFrameCreated(content::RenderFrame *render_fr
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
     associated_interfaces->AddInterface<extensions::mojom::MimeHandlerViewContainerManager>(
-                base::BindRepeating(
-                    &extensions::MimeHandlerViewContainerManager::BindReceiver,
-                    render_frame->GetRoutingID()));
+            base::BindRepeating(&extensions::MimeHandlerViewContainerManager::BindReceiver,
+                                base::Unretained(render_frame)));
 
     auto registry = std::make_unique<service_manager::BinderRegistry>();
     ExtensionsRendererClientQt::GetInstance()->RenderFrameCreated(render_frame, render_frame_observer->registry());
 #endif
 
-    autofill::PasswordAutofillAgent *password_autofill_agent =
-            new autofill::PasswordAutofillAgent(render_frame, associated_interfaces);
-    autofill::PasswordGenerationAgent *password_generation_agent =
-            new autofill::PasswordGenerationAgent(render_frame, password_autofill_agent,
-                                                  associated_interfaces);
-
-    new autofill::AutofillAgent(render_frame, password_autofill_agent, password_generation_agent,
-                                associated_interfaces);
+    auto password_autofill_agent =
+            std::make_unique<autofill::PasswordAutofillAgent>(render_frame, associated_interfaces);
+    auto password_generation_agent =
+            std::make_unique<autofill::PasswordGenerationAgent>(render_frame, password_autofill_agent.get(), associated_interfaces);
+
+    new autofill::AutofillAgent(
+            render_frame,
+            { autofill::AutofillAgent::UsesKeyboardAccessoryForSuggestions(false),
+              autofill::AutofillAgent::ExtractAllDatalists(false) },
+            std::move(password_autofill_agent), std::move(password_generation_agent),
+            associated_interfaces);
 }
 
 void ContentRendererClientQt::WebViewCreated(blink::WebView *web_view,
@@ -303,13 +305,13 @@ void ContentRendererClientQt::GetNavigationErrorStringsInternal(content::RenderF
         if (template_html.empty())
             NOTREACHED() << "unable to load template. ID: " << resourceId;
         else // "t" is the id of the templates root node.
-            *errorHtml = webui::GetTemplatesHtml(template_html, errorPageState.strings, "t");
+            *errorHtml = webui::GetLocalizedHtml(template_html, errorPageState.strings);
     }
 }
 
-uint64_t ContentRendererClientQt::VisitedLinkHash(const char *canonicalUrl, size_t length)
+uint64_t ContentRendererClientQt::VisitedLinkHash(std::string_view canonicalUrl)
 {
-    return m_visitedLinkReader->ComputeURLFingerprint(canonicalUrl, length);
+    return m_visitedLinkReader->ComputeURLFingerprint(canonicalUrl);
 }
 
 bool ContentRendererClientQt::IsLinkVisited(uint64_t linkHash)
@@ -355,8 +357,8 @@ void AppendParams(const std::vector<content::WebPluginMimeType::Param> &addition
         values[existing_size + i] = blink::WebString::FromUTF16(additional_params[i].value);
     }
 
-    existing_names->Swap(names);
-    existing_values->Swap(values);
+    existing_names->swap(names);
+    existing_values->swap(values);
 }
 #endif  // BUILDFLAG(ENABLE_PLUGINS)
 
@@ -419,11 +421,12 @@ bool ContentRendererClientQt::OverrideCreatePlugin(content::RenderFrame *render_
 
 #if BUILDFLAG(ENABLE_PLUGINS)
     content::WebPluginInfo info;
-    std::string mime_type;
+    std::string actual_mime_type;
     bool found = false;
 
-    static_cast<content::RenderFrameImpl *>(render_frame)->GetPepperHost()->GetPluginInfo(
-                params.url, params.mime_type.Utf8(), &found, &info, &mime_type);
+    static_cast<content::RenderFrameImpl *>(render_frame)
+            ->GetPepperHost()
+            ->GetPluginInfo(params.url, params.mime_type.Utf8(), &found, &info, &actual_mime_type);
     if (!found) {
         *plugin = LoadablePluginPlaceholderQt::CreateLoadableMissingPlugin(render_frame, params)->plugin();
         return true;
diff --git a/src/core/renderer/content_renderer_client_qt.h b/src/core/renderer/content_renderer_client_qt.h
index b2231f00acd..8151c9710fe 100644
--- a/src/core/renderer/content_renderer_client_qt.h
+++ b/src/core/renderer/content_renderer_client_qt.h
@@ -66,7 +66,7 @@ class ContentRendererClientQt
                                             int http_status,
                                             content::mojom::AlternativeErrorPageOverrideInfoPtr alternative_error_page_info,
                                             std::string *error_html)  override;
-    uint64_t VisitedLinkHash(const char *canonical_url, size_t length) override;
+    uint64_t VisitedLinkHash(std::string_view canonical_url) override;
     bool IsLinkVisited(uint64_t linkHash) override;
     std::unique_ptr<blink::WebPrescientNetworking> CreatePrescientNetworking(content::RenderFrame *render_frame) override;
     void GetSupportedKeySystems(media::GetSupportedKeySystemsCB cb) override;
diff --git a/src/core/renderer/content_settings_observer_qt.cpp b/src/core/renderer/content_settings_observer_qt.cpp
index 3e3c159f516..c78f53d8a84 100644
--- a/src/core/renderer/content_settings_observer_qt.cpp
+++ b/src/core/renderer/content_settings_observer_qt.cpp
@@ -39,6 +39,7 @@ ContentSettingsObserverQt::ContentSettingsObserverQt(content::RenderFrame *rende
 
 ContentSettingsObserverQt::~ContentSettingsObserverQt() {}
 
+#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
 bool ContentSettingsObserverQt::OnMessageReceived(const IPC::Message &message)
 {
     bool handled = true;
@@ -49,6 +50,7 @@ bool ContentSettingsObserverQt::OnMessageReceived(const IPC::Message &message)
 
     return handled;
 }
+#endif
 
 void ContentSettingsObserverQt::DidCommitProvisionalLoad(ui::PageTransition /*transition*/)
 {
@@ -83,11 +85,12 @@ void ContentSettingsObserverQt::AllowStorageAccess(StorageType storage_type,
 
     // Verify there are no duplicate insertions.
     DCHECK(inserted);
-
+#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
     Send(new QtWebEngineHostMsg_RequestStorageAccessAsync(routing_id(), m_currentRequestId,
                                                           url::Origin(frame->GetSecurityOrigin()).GetURL(),
                                                           url::Origin(frame->Top()->GetSecurityOrigin()).GetURL(),
                                                           int(storage_type)));
+#endif
 }
 
 bool ContentSettingsObserverQt::AllowStorageAccessSync(StorageType storage_type)
@@ -105,9 +108,11 @@ bool ContentSettingsObserverQt::AllowStorageAccessSync(StorageType storage_type)
     }
 
     bool result = false;
+#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
     Send(new QtWebEngineHostMsg_AllowStorageAccess(routing_id(), url::Origin(frame->GetSecurityOrigin()).GetURL(),
                                                    url::Origin(frame->Top()->GetSecurityOrigin()).GetURL(),
                                                    int(storage_type), &result));
+#endif
     if (sameOrigin)
         m_cachedStoragePermissions[key] = result;
     return result;
diff --git a/src/core/renderer/content_settings_observer_qt.h b/src/core/renderer/content_settings_observer_qt.h
index 415d0b6b739..f843c61ee38 100644
--- a/src/core/renderer/content_settings_observer_qt.h
+++ b/src/core/renderer/content_settings_observer_qt.h
@@ -9,6 +9,7 @@
 #define CONTENT_SETTINGS_OBSERVER_QT_H
 
 #include "base/containers/flat_map.h"
+#include "content/common/buildflags.h"
 #include "content/public/renderer/render_frame_observer.h"
 #include "content/public/renderer/render_frame_observer_tracker.h"
 #include "third_party/blink/public/platform/web_content_settings_client.h"
@@ -33,7 +34,9 @@ class ContentSettingsObserverQt
 
 private:
     // RenderFrameObserver implementation:
+#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
     bool OnMessageReceived(const IPC::Message &message) override;
+#endif
     void DidCommitProvisionalLoad(ui::PageTransition transition) override;
     void OnDestruct() override;
 
diff --git a/src/core/renderer/extensions/extensions_renderer_client_qt.cpp b/src/core/renderer/extensions/extensions_renderer_client_qt.cpp
index b36ed9e8b45..9db6bbe3627 100644
--- a/src/core/renderer/extensions/extensions_renderer_client_qt.cpp
+++ b/src/core/renderer/extensions/extensions_renderer_client_qt.cpp
@@ -31,6 +31,7 @@
 #include "extensions/renderer/extensions_render_frame_observer.h"
 #include "extensions/renderer/renderer_extension_registry.h"
 #include "extensions/renderer/script_context.h"
+#include "extensions/renderer/extensions_renderer_api_provider.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/public/web/web_plugin_params.h"
 
@@ -108,7 +109,9 @@ void ExtensionsRendererClientQt::RenderThreadStarted()
 {
     content::RenderThread *thread = content::RenderThread::Get();
     if (!extension_dispatcher_)
-        extension_dispatcher_.reset(new extensions::Dispatcher(std::make_unique<ExtensionsDispatcherDelegateQt>()));
+        extension_dispatcher_.reset(new extensions::Dispatcher(
+                std::make_unique<ExtensionsDispatcherDelegateQt>(),
+                std::vector<std::unique_ptr<extensions::ExtensionsRendererAPIProvider>>()));
     extension_dispatcher_->OnRenderThreadStarted(thread);
     permissions_policy_delegate_.reset(new RendererPermissionsPolicyDelegateQt(extension_dispatcher_.get()));
     resource_request_policy_.reset(new extensions::ResourceRequestPolicyQt(extension_dispatcher_.get()));
@@ -149,7 +152,7 @@ void ExtensionsRendererClientQt::WillSendRequest(blink::WebLocalFrame *frame,
     if (url.ProtocolIs(extensions::kExtensionScheme) &&
             !resource_request_policy_->CanRequestResource(url, frame,
                                                           transition_type,
-                                                          base::OptionalFromPtr(initiator_origin))) {
+                                                          initiator_origin)) {
         *new_url = GURL(chrome::kExtensionInvalidRequestURL);
     }
 }
diff --git a/src/core/renderer/extensions/resource_request_policy_qt.cpp b/src/core/renderer/extensions/resource_request_policy_qt.cpp
index a61e533107a..2e4eea771de 100644
--- a/src/core/renderer/extensions/resource_request_policy_qt.cpp
+++ b/src/core/renderer/extensions/resource_request_policy_qt.cpp
@@ -51,7 +51,7 @@ void ResourceRequestPolicyQt::OnExtensionUnloaded(const ExtensionId &extension_i
 bool ResourceRequestPolicyQt::CanRequestResource(const GURL &resource_url,
                                                  blink::WebLocalFrame *frame,
                                                  ui::PageTransition transition_type,
-                                                 const absl::optional<url::Origin>& initiator_origin)
+                                                 const url::Origin *initiator_origin)
 {
     CHECK(resource_url.SchemeIs(kExtensionScheme));
 
diff --git a/src/core/renderer/extensions/resource_request_policy_qt.h b/src/core/renderer/extensions/resource_request_policy_qt.h
index ec108519fa5..6807f509137 100644
--- a/src/core/renderer/extensions/resource_request_policy_qt.h
+++ b/src/core/renderer/extensions/resource_request_policy_qt.h
@@ -39,7 +39,7 @@ class ResourceRequestPolicyQt
     bool CanRequestResource(const GURL &resource_url,
                             blink::WebLocalFrame *frame,
                             ui::PageTransition transition_type,
-                            const absl::optional<url::Origin> &initiator_origin);
+                            const url::Origin *initiator_origin);
 
 private:
     Dispatcher *m_dispatcher;
diff --git a/src/core/renderer/plugins/loadable_plugin_placeholder_qt.cpp b/src/core/renderer/plugins/loadable_plugin_placeholder_qt.cpp
index 06fd4f71f96..4d25be12a27 100644
--- a/src/core/renderer/plugins/loadable_plugin_placeholder_qt.cpp
+++ b/src/core/renderer/plugins/loadable_plugin_placeholder_qt.cpp
@@ -22,11 +22,11 @@ namespace QtWebEngineCore {
 // static
 gin::WrapperInfo LoadablePluginPlaceholderQt::kWrapperInfo = {gin::kEmbedderNativeGin};
 
-LoadablePluginPlaceholderQt::LoadablePluginPlaceholderQt(content::RenderFrame* render_frame,
-                                                         const blink::WebPluginParams& params,
-                                                         const std::string& html_data,
-                                                         const std::u16string& title)
-    : plugins::LoadablePluginPlaceholder(render_frame, params, html_data)
+LoadablePluginPlaceholderQt::LoadablePluginPlaceholderQt(content::RenderFrame *render_frame,
+                                                         const blink::WebPluginParams &params,
+                                                         const std::string &html_data,
+                                                         const std::u16string &title)
+    : plugins::LoadablePluginPlaceholder(render_frame, params)
 {}
 
 LoadablePluginPlaceholderQt::~LoadablePluginPlaceholderQt()
diff --git a/src/core/renderer/render_frame_observer_qt.cpp b/src/core/renderer/render_frame_observer_qt.cpp
index e6489eefbb2..03fb0d9e881 100644
--- a/src/core/renderer/render_frame_observer_qt.cpp
+++ b/src/core/renderer/render_frame_observer_qt.cpp
@@ -51,7 +51,7 @@ bool RenderFrameObserverQt::OnAssociatedInterfaceRequestForFrame(const std::stri
     return m_associated_interfaces.TryBindInterface(interface_name, handle);
 }
 
-void RenderFrameObserverQt::WillDetach()
+void RenderFrameObserverQt::WillDetach(blink::DetachReason reason)
 {
     m_isFrameDetached = true;
 }
diff --git a/src/core/renderer/render_frame_observer_qt.h b/src/core/renderer/render_frame_observer_qt.h
index 8cab3bd9a6c..c3a1dd8ba1f 100644
--- a/src/core/renderer/render_frame_observer_qt.h
+++ b/src/core/renderer/render_frame_observer_qt.h
@@ -34,7 +34,7 @@ class RenderFrameObserverQt
         const std::string &interface_name,
         mojo::ScopedInterfaceEndpointHandle *handle) override;
     void OnDestruct() override;
-    void WillDetach() override;
+    void WillDetach(blink::DetachReason detach_reason) override;
 
     bool isFrameDetached() const;
 
diff --git a/src/core/renderer/user_resource_controller.cpp b/src/core/renderer/user_resource_controller.cpp
index eff304981c5..82579ea7d79 100644
--- a/src/core/renderer/user_resource_controller.cpp
+++ b/src/core/renderer/user_resource_controller.cpp
@@ -110,7 +110,7 @@ class UserResourceController::RenderFrameObserverHelper
     void DidCommitProvisionalLoad(ui::PageTransition transition) override;
     void DidDispatchDOMContentLoadedEvent() override;
     void DidFinishLoad() override;
-    void WillDetach() override;
+    void WillDetach(blink::DetachReason detach_reason) override;
     void OnDestruct() override;
     void AddScript(const QtWebEngineCore::UserScriptData &data) override;
     void RemoveScript(const QtWebEngineCore::UserScriptData &data) override;
@@ -231,7 +231,8 @@ void UserResourceController::RenderFrameObserverHelper::DidFinishLoad()
                                QtWebEngineCore::UserScriptData::AfterLoad));
 }
 
-void UserResourceController::RenderFrameObserverHelper::WillDetach()
+void UserResourceController::RenderFrameObserverHelper::WillDetach(
+        blink::DetachReason detach_reason)
 {
     m_runner.reset();
 }
diff --git a/src/core/renderer_host/user_resource_controller_host.cpp b/src/core/renderer_host/user_resource_controller_host.cpp
index f2a00fc72d3..a2917194234 100644
--- a/src/core/renderer_host/user_resource_controller_host.cpp
+++ b/src/core/renderer_host/user_resource_controller_host.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2016 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #include "user_resource_controller_host.h"
 
@@ -42,10 +43,9 @@ UserResourceControllerHost::WebContentsObserverHelper::WebContentsObserverHelper
 
 void UserResourceControllerHost::WebContentsObserverHelper::RenderFrameCreated(content::RenderFrameHost *renderFrameHost)
 {
-    content::WebContents *contents = web_contents();
     auto &remote = m_controllerHost->GetUserResourceControllerRenderFrame(renderFrameHost);
-    const QList<UserScript> scripts = m_controllerHost->m_perContentsScripts.value(contents);
-    for (const UserScript &script : scripts)
+    const auto scripts = m_controllerHost->m_perContentsScripts.constFind(web_contents());
+    for (const UserScript &script : *scripts)
         remote->AddScript(script.data());
 }
 
@@ -56,6 +56,12 @@ void UserResourceControllerHost::WebContentsObserverHelper::RenderFrameHostChang
         auto &remote = m_controllerHost->GetUserResourceControllerRenderFrame(oldHost);
         remote->ClearScripts();
     }
+    if (newHost) {
+        auto &remote = m_controllerHost->GetUserResourceControllerRenderFrame(newHost);
+        const auto scripts = m_controllerHost->m_perContentsScripts.constFind(web_contents());
+        for (const UserScript &script : *scripts)
+            remote->AddScript(script.data());
+    }
 }
 
 void UserResourceControllerHost::WebContentsObserverHelper::RenderFrameDeleted(
diff --git a/src/core/renderer_host/user_resource_controller_host.h b/src/core/renderer_host/user_resource_controller_host.h
index eb4404879d7..f843c732140 100644
--- a/src/core/renderer_host/user_resource_controller_host.h
+++ b/src/core/renderer_host/user_resource_controller_host.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2016 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 //
 //  W A R N I N G
diff --git a/src/core/renderer_host/web_channel_ipc_transport_host.cpp b/src/core/renderer_host/web_channel_ipc_transport_host.cpp
index 0824ed56d9d..6db578a27a0 100644
--- a/src/core/renderer_host/web_channel_ipc_transport_host.cpp
+++ b/src/core/renderer_host/web_channel_ipc_transport_host.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2018 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #include "web_channel_ipc_transport_host.h"
 
@@ -48,7 +49,7 @@ uint WebChannelIPCTransportHost::worldId() const
 void WebChannelIPCTransportHost::sendMessage(const QJsonObject &message)
 {
     QJsonDocument doc(message);
-    QByteArray json = doc.toJson(QJsonDocument::Compact);
+    const QByteArray json = doc.toJson(QJsonDocument::Compact);
     content::RenderFrameHost *frame = web_contents()->GetPrimaryMainFrame();
     qCDebug(log).nospace() << "sending webchannel message to " << frame << ": " << doc;
     GetWebChannelIPCTransportRemote(frame)->DispatchWebChannelMessage(
@@ -107,6 +108,16 @@ void WebChannelIPCTransportHost::RenderFrameCreated(content::RenderFrameHost *fr
     setWorldId(frame, m_worldId);
 }
 
+void WebChannelIPCTransportHost::RenderFrameHostChanged(content::RenderFrameHost *oldHost, content::RenderFrameHost *newHost)
+{
+    if (oldHost) {
+        if (oldHost->IsRenderFrameLive())
+            GetWebChannelIPCTransportRemote(oldHost)->ResetWorldId();
+    }
+    if (newHost) // this might set it again, but that is harmless
+        setWorldId(newHost, m_worldId);
+}
+
 void WebChannelIPCTransportHost::RenderFrameDeleted(content::RenderFrameHost *rfh)
 {
     m_renderFrames.erase(rfh);
diff --git a/src/core/renderer_host/web_channel_ipc_transport_host.h b/src/core/renderer_host/web_channel_ipc_transport_host.h
index 9e003c222e7..82421199ffe 100644
--- a/src/core/renderer_host/web_channel_ipc_transport_host.h
+++ b/src/core/renderer_host/web_channel_ipc_transport_host.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2018 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #ifndef WEB_CHANNEL_IPC_TRANSPORT_H
 #define WEB_CHANNEL_IPC_TRANSPORT_H
@@ -45,6 +46,7 @@ class WebChannelIPCTransportHost
 
     // WebContentsObserver
     void RenderFrameCreated(content::RenderFrameHost *frame) override;
+    void RenderFrameHostChanged(content::RenderFrameHost *oldHost, content::RenderFrameHost *newHost) override;
     void RenderFrameDeleted(content::RenderFrameHost *render_frame_host) override;
 
     // qtwebchannel::mojom::WebChannelTransportHost
diff --git a/src/core/renderer_host/web_engine_page_host.cpp b/src/core/renderer_host/web_engine_page_host.cpp
index ea9b363eab9..2a2672159eb 100644
--- a/src/core/renderer_host/web_engine_page_host.cpp
+++ b/src/core/renderer_host/web_engine_page_host.cpp
@@ -1,5 +1,6 @@
 // Copyright (C) 2016 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #include "web_engine_page_host.h"
 
diff --git a/src/core/renderer_host/web_engine_page_host.h b/src/core/renderer_host/web_engine_page_host.h
index 20b9145b219..8fb128aeb32 100644
--- a/src/core/renderer_host/web_engine_page_host.h
+++ b/src/core/renderer_host/web_engine_page_host.h
@@ -1,5 +1,6 @@
 // Copyright (C) 2016 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
 
 #ifndef WEB_ENGINE_PAGE_HOST_H
 #define WEB_ENGINE_PAGE_HOST_H
diff --git a/src/core/select_file_dialog_factory_qt.cpp b/src/core/select_file_dialog_factory_qt.cpp
index 1f897a805c3..f3f35c1aa6a 100644
--- a/src/core/select_file_dialog_factory_qt.cpp
+++ b/src/core/select_file_dialog_factory_qt.cpp
@@ -119,7 +119,7 @@ void SelectFileDialogQt::SelectFileImpl(Type type, const std::u16string &title,
     if (file_types) {
         for (const auto &type : file_types->extensions) {
             for (const auto &extension : type)
-                acceptedSuffixes.append("." + toQt(extension));
+                acceptedSuffixes.append(u'.' + toQt(extension));
         }
     }
 
diff --git a/src/core/tools/qwebengine_convert_dict/CMakeLists.txt b/src/core/tools/qwebengine_convert_dict/CMakeLists.txt
index 5e8a1de143e..4d36ca004fb 100644
--- a/src/core/tools/qwebengine_convert_dict/CMakeLists.txt
+++ b/src/core/tools/qwebengine_convert_dict/CMakeLists.txt
@@ -7,10 +7,14 @@ if(QT_FEATURE_webengine_spellchecker AND NOT CMAKE_CROSSCOMPILING)
         TARGET_DESCRIPTION "QtWebEngine Dictionary Conversion Tool"
         INSTALL_DIR ${INSTALL_LIBEXECDIR}
         TOOLS_TARGET WebEngineCore
+        DEFINES TOOLKIT_QT
         SOURCES main.cpp
         INCLUDE_DIRECTORIES
             ../../../3rdparty/chromium/third_party/abseil-cpp
     )
+    if(MSVC AND NOT CLANG)
+        target_compile_options(${dict_target_name} PRIVATE "/Zc:preprocessor")
+    endif()
     if(COMMAND qt_internal_return_unless_building_tools)
         qt_internal_return_unless_building_tools()
     endif()
diff --git a/src/core/tools/qwebengine_convert_dict/main.cpp b/src/core/tools/qwebengine_convert_dict/main.cpp
index a82947ddc35..5dc750ee261 100644
--- a/src/core/tools/qwebengine_convert_dict/main.cpp
+++ b/src/core/tools/qwebengine_convert_dict/main.cpp
@@ -7,7 +7,7 @@
 ** Modified work:
 ** Copyright (C) 2016 The Qt Company Ltd.
 **
-** SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+** SPDX-License-Identifier: BSD-3-Clause
 **
 ** This tool converts Hunspell .aff/.dic pairs to a combined binary dictionary
 ** format (.bdic). This format is more compact, and can be more efficiently
@@ -32,6 +32,8 @@
 #include <QDir>
 #include <QCoreApplication>
 
+using namespace Qt::StringLiterals;
+
 // see also src/core/type_conversion.h
 inline base::FilePath::StringType toFilePathString(const QString &str)
 {
@@ -68,11 +70,11 @@ template<class T>
 QTextStream &operator<<(QTextStream &out, base::span<T> span)
 {
     out << '[';
-    QString prefix;
+    QLatin1StringView prefix;
     for (const auto &element : span) {
         out << prefix;
         out << element;
-        prefix = QStringLiteral(",");
+        prefix = ","_L1;
     }
     out << ']';
     return out;
@@ -93,20 +95,20 @@ inline bool VerifyWords(const convert_dict::DicReader::WordList& org_words,
 
     int affix_ids[hunspell::BDict::MAX_AFFIXES_PER_WORD];
 
-    static const int buf_size = 128;
-    char buf[buf_size];
     for (size_t i = 0; i < org_words.size(); i++) {
-        int affix_matches = iter.Advance(buf, buf_size, affix_ids);
+        auto buf_size = org_words[i].first.size() + 1;
+        std::string buf(buf_size, '\0');
+        int affix_matches = iter.Advance(buf.data(), buf_size, affix_ids);
         if (affix_matches == 0) {
             out << "Found the end before we expected\n";
             return false;
         }
 
-        if (org_words[i].first != buf) {
+        if (buf.back() != '\0' || buf.compare(0, buf_size - 1, org_words[i].first) != 0) {
             out << "Word does not match!\n"
                 << "  Index:    " << i << "\n"
                 << "  Expected: " << QString::fromStdString(org_words[i].first) << "\n"
-                << "  Actual:   " << QString::fromUtf8(buf) << "\n";
+                << "  Actual:   " << QString::fromStdString(buf) << "\n";
             return false;
         }
 
@@ -118,7 +120,7 @@ inline bool VerifyWords(const convert_dict::DicReader::WordList& org_words,
                         [](int a, int b) { return a == b; })) {
             out << "Affixes do not match!\n"
                 << "  Index:    " << i << "\n"
-                << "  Word:     " << QString::fromUtf8(buf) << "\n"
+                << "  Word:     " << QString::fromStdString(buf) << "\n"
                 << "  Expected: " << expectedAffixes << "\n"
                 << "  Actual:   " << actualAffixes << "\n";
             return false;
@@ -131,8 +133,8 @@ inline bool VerifyWords(const convert_dict::DicReader::WordList& org_words,
 #if defined(Q_OS_DARWIN) && defined(QT_MAC_FRAMEWORK_BUILD)
 QString frameworkIcuDataPath()
 {
-    return QLibraryInfo::location(QLibraryInfo::LibrariesPath) +
-            QStringLiteral("/QtWebEngineCore.framework/Resources/");
+    return QLibraryInfo::location(QLibraryInfo::LibrariesPath)
+            + "/QtWebEngineCore.framework/Resources/"_L1;
 }
 #endif
 
@@ -155,15 +157,14 @@ int main(int argc, char *argv[])
     }
 #if defined(USE_ICU_FILE)
     bool icuDataDirFound = false;
-    QString icuDataDir = QLibraryInfo::path(QLibraryInfo::DataPath)
-            % QLatin1String("/resources");
+    QString icuDataDir = QLibraryInfo::path(QLibraryInfo::DataPath) % "/resources"_L1;
 
     // Try to look up the path to the ICU data directory via an environment variable
     // (e.g. for the case when the tool is ran during build phase, and regular installed
     // ICU data file is not available).
     const QString icuPossibleEnvDataDir = qEnvironmentVariable("QT_WEBENGINE_ICU_DATA_DIR");
     const QString appPath = QCoreApplication::applicationDirPath();
-    QLatin1String icuDataFilePath("/icudtl.dat");
+    const auto icuDataFilePath = "/icudtl.dat"_L1;
     if (!icuPossibleEnvDataDir.isEmpty() && QFileInfo::exists(icuPossibleEnvDataDir)) {
         icuDataDir = icuPossibleEnvDataDir;
         icuDataDirFound = true;
diff --git a/src/core/type_conversion.cpp b/src/core/type_conversion.cpp
index 2d4fb323dc4..1d821df8c1a 100644
--- a/src/core/type_conversion.cpp
+++ b/src/core/type_conversion.cpp
@@ -23,8 +23,6 @@ QImage toQImage(const SkBitmap &bitmap)
     QImage image;
     switch (bitmap.colorType()) {
     case kUnknown_SkColorType:
-    case kRGBA_F16_SkColorType:
-    case kRGBA_F32_SkColorType:
     case kRGBA_F16Norm_SkColorType:
     case kR8G8_unorm_SkColorType:
     case kA16_float_SkColorType:
@@ -32,6 +30,7 @@ QImage toQImage(const SkBitmap &bitmap)
     case kR16G16_float_SkColorType:
     case kR16G16_unorm_SkColorType:
     case kR8_unorm_SkColorType:
+    case kRGBA_10x6_SkColorType:
         qWarning("Unknown or unsupported skia image format");
         break;
     case kAlpha_8_SkColorType:
@@ -136,6 +135,36 @@ QImage toQImage(const SkBitmap &bitmap)
             break;
         }
         break;
+    case kRGBA_F16_SkColorType:
+        switch (bitmap.alphaType()) {
+        case kUnknown_SkAlphaType:
+            break;
+        case kUnpremul_SkAlphaType:
+            image = toQImage(bitmap, QImage::Format_RGBA16FPx4);
+            break;
+        case kOpaque_SkAlphaType:
+            image = toQImage(bitmap, QImage::Format_RGBX16FPx4);
+            break;
+        case kPremul_SkAlphaType:
+            image = toQImage(bitmap, QImage::Format_RGBA16FPx4_Premultiplied);
+            break;
+        }
+        break;
+    case kRGBA_F32_SkColorType:
+        switch (bitmap.alphaType()) {
+        case kUnknown_SkAlphaType:
+            break;
+        case kUnpremul_SkAlphaType:
+            image = toQImage(bitmap, QImage::Format_RGBA32FPx4);
+            break;
+        case kOpaque_SkAlphaType:
+            image = toQImage(bitmap, QImage::Format_RGBX32FPx4);
+            break;
+        case kPremul_SkAlphaType:
+            image = toQImage(bitmap, QImage::Format_RGBA32FPx4_Premultiplied);
+            break;
+        }
+        break;
     }
     return image;
 }
diff --git a/src/core/type_conversion.h b/src/core/type_conversion.h
index 0da8a6931b6..688ea014d34 100644
--- a/src/core/type_conversion.h
+++ b/src/core/type_conversion.h
@@ -209,11 +209,11 @@ QIcon toQIcon(const std::vector<SkBitmap> &bitmaps);
 
 inline QDateTime toQt(base::Time time)
 {
-    return QDateTime::fromMSecsSinceEpoch(time.ToJavaTime());
+    return QDateTime::fromMSecsSinceEpoch(time.InMillisecondsSinceUnixEpoch());
 }
 
 inline base::Time toTime(const QDateTime &dateTime) {
-    return base::Time::FromJavaTime(dateTime.toMSecsSinceEpoch());
+    return base::Time::FromMillisecondsSinceUnixEpoch(dateTime.toMSecsSinceEpoch());
 }
 
 inline QNetworkCookie toQt(const net::CanonicalCookie & cookie)
diff --git a/src/core/user_script.cpp b/src/core/user_script.cpp
index c33fb908178..a91d470ed24 100644
--- a/src/core/user_script.cpp
+++ b/src/core/user_script.cpp
@@ -62,7 +62,7 @@ QString UserScript::name() const
 void UserScript::setName(const QString &name)
 {
     m_name = name;
-    m_scriptData.url = GURL(QStringLiteral("userScript:%1").arg(name).toStdString());
+    m_scriptData.url = GURL("userScript:" + name.toStdString());
 }
 
 QString UserScript::sourceCode() const
diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp
index 4b21838a034..a34de4e501c 100644
--- a/src/core/web_contents_adapter.cpp
+++ b/src/core/web_contents_adapter.cpp
@@ -93,6 +93,8 @@
 #include "extensions/extension_web_contents_observer_qt.h"
 #endif
 
+using namespace Qt::StringLiterals;
+
 namespace QtWebEngineCore {
 
 #define CHECK_INITIALIZED(return_value)         \
@@ -659,13 +661,13 @@ void WebContentsAdapter::load(const QWebEngineHttpRequest &request)
     Q_UNUSED(guard);
 
     // Add URL scheme if missing from view-source URL.
-    if (request.url().scheme() == content::kViewSourceScheme) {
+    if (request.url().scheme() == QLatin1StringView(content::kViewSourceScheme)) {
         QUrl pageUrl = QUrl(request.url().toString().remove(0,
                                                            strlen(content::kViewSourceScheme) + 1));
         if (pageUrl.scheme().isEmpty()) {
             QUrl extendedUrl = QUrl::fromUserInput(pageUrl.toString());
-            extendedUrl = QUrl(QString("%1:%2").arg(content::kViewSourceScheme,
-                                                    extendedUrl.toString()));
+            extendedUrl = QUrl(QLatin1StringView(content::kViewSourceScheme) + u':'
+                               + extendedUrl.toString());
             gurl = toGurl(extendedUrl);
         }
     }
@@ -733,7 +735,7 @@ void WebContentsAdapter::setContent(const QByteArray &data, const QString &mimeT
 
     WebEngineSettings::get(m_adapterClient->webEngineSettings())->doApply();
 
-    QByteArray encodedData = data.toPercentEncoding();
+    const QByteArray encodedData = data.toPercentEncoding();
     std::string urlString;
     if (!mimeType.isEmpty())
         urlString = std::string("data:") + mimeType.toStdString() + std::string(",");
@@ -1069,7 +1071,7 @@ void WebContentsAdapter::runJavaScript(const QString &javaScript, quint32 worldI
     if (!rfh)
         return exit();
     if (!static_cast<content::RenderFrameHostImpl*>(rfh)->GetAssociatedLocalFrame()) {
-        qWarning() << "Local frame is gone, not running script";
+        qWarning("Local frame is gone, not running script");
         return exit();
     }
 
@@ -1346,7 +1348,8 @@ void WebContentsAdapter::wasHidden()
     m_webContents->WasHidden();
 }
 
-void WebContentsAdapter::printToPDF(const QPageLayout &pageLayout, const QPageRanges &pageRanges, const QString &filePath)
+void WebContentsAdapter::printToPDF(const QPageLayout &pageLayout, const QPageRanges &pageRanges,
+                                    const QString &filePath, quint64 frameId)
 {
 #if QT_CONFIG(webengine_printing_and_pdf)
     CHECK_INITIALIZED();
@@ -1356,17 +1359,15 @@ void WebContentsAdapter::printToPDF(const QPageLayout &pageLayout, const QPageRa
     content::WebContents *webContents = m_webContents.get();
     if (content::WebContents *guest = guestWebContents())
         webContents = guest;
-    PrintViewManagerQt::FromWebContents(webContents)->PrintToPDFFileWithCallback(pageLayout,
-                                                                                         pageRanges,
-                                                                                         true,
-                                                                                         filePath,
-                                                                                         std::move(callback));
+    PrintViewManagerQt::FromWebContents(webContents)
+            ->PrintToPDFFileWithCallback(pageLayout, pageRanges, true, filePath, frameId,
+                                         std::move(callback));
 #endif // QT_CONFIG(webengine_printing_and_pdf)
 }
 
 void WebContentsAdapter::printToPDFCallbackResult(
         std::function<void(QSharedPointer<QByteArray>)> &&callback, const QPageLayout &pageLayout,
-        const QPageRanges &pageRanges, bool colorMode, bool useCustomMargins)
+        const QPageRanges &pageRanges, bool colorMode, bool useCustomMargins, quint64 frameId)
 {
 #if QT_CONFIG(webengine_printing_and_pdf)
     CHECK_INITIALIZED();
@@ -1377,7 +1378,7 @@ void WebContentsAdapter::printToPDFCallbackResult(
     if (content::WebContents *guest = guestWebContents())
         webContents = guest;
     PrintViewManagerQt::FromWebContents(webContents)
-            ->PrintToPDFWithCallback(pageLayout, pageRanges, colorMode, useCustomMargins,
+            ->PrintToPDFWithCallback(pageLayout, pageRanges, colorMode, useCustomMargins, frameId,
                                      std::move(internalCallback));
     m_printCallbacks.emplace(m_nextRequestId++, std::move(callback));
 #else
@@ -1412,21 +1413,102 @@ QSizeF WebContentsAdapter::lastContentsSize() const
     return QSizeF();
 }
 
-void WebContentsAdapter::grantMediaAccessPermission(const QUrl &securityOrigin, WebContentsAdapterClient::MediaRequestFlags flags)
+void WebContentsAdapter::setPermission(const QUrl &origin, QWebEnginePermission::PermissionType permissionType, QWebEnginePermission::State state)
 {
+    if (QWebEnginePermission::isPersistent(permissionType)) {
+        // Do not check for initialization in this path so permissions can be set before first navigation
+        Q_ASSERT(m_profileAdapter);
+        if (!isInitialized()) {
+            m_profileAdapter->setPermission(origin, permissionType, state);
+        } else {
+            m_profileAdapter->setPermission(origin, permissionType, state, m_webContents.get()->GetPrimaryMainFrame());
+        }
+
+        return;
+    }
+
     CHECK_INITIALIZED();
-    // Let the permission manager remember the reply.
-    if (flags & WebContentsAdapterClient::MediaAudioCapture)
-        m_profileAdapter->permissionRequestReply(securityOrigin, ProfileAdapter::AudioCapturePermission, ProfileAdapter::AllowedPermission);
-    if (flags & WebContentsAdapterClient::MediaVideoCapture)
-        m_profileAdapter->permissionRequestReply(securityOrigin, ProfileAdapter::VideoCapturePermission, ProfileAdapter::AllowedPermission);
-    MediaCaptureDevicesDispatcher::GetInstance()->handleMediaAccessPermissionResponse(m_webContents.get(), securityOrigin, flags);
+
+    if (permissionType == QWebEnginePermission::PermissionType::MouseLock) {
+        switch (state) {
+        case QWebEnginePermission::State::Invalid:
+        case QWebEnginePermission::State::Ask:
+            // Do nothing
+            break;
+        case QWebEnginePermission::State::Denied:
+            grantMouseLockPermission(origin, false);
+            break;
+        case QWebEnginePermission::State::Granted:
+            grantMouseLockPermission(origin, true);
+            break;
+        }
+
+        return;
+    }
+
+    const WebContentsAdapterClient::MediaRequestFlags audioVideoCaptureFlags(
+        WebContentsAdapterClient::MediaVideoCapture |
+        WebContentsAdapterClient::MediaAudioCapture);
+    const WebContentsAdapterClient::MediaRequestFlags desktopAudioVideoCaptureFlags(
+        WebContentsAdapterClient::MediaDesktopVideoCapture |
+        WebContentsAdapterClient::MediaDesktopAudioCapture);
+
+    switch (state) {
+    case QWebEnginePermission::State::Invalid:
+    case QWebEnginePermission::State::Ask:
+        // Do nothing
+        return;
+    case QWebEnginePermission::State::Denied:
+        // Deny all media access
+        grantMediaAccessPermission(origin, WebContentsAdapterClient::MediaNone);
+        return;
+    case QWebEnginePermission::State::Granted:
+        // Enable only the requested capture type
+        break;
+    }
+
+    switch (permissionType) {
+    case QWebEnginePermission::PermissionType::MediaAudioVideoCapture:
+        grantMediaAccessPermission(origin, audioVideoCaptureFlags);
+        break;
+    case QWebEnginePermission::PermissionType::MediaAudioCapture:
+        grantMediaAccessPermission(origin, WebContentsAdapterClient::MediaAudioCapture);
+        break;
+    case QWebEnginePermission::PermissionType::MediaVideoCapture:
+        grantMediaAccessPermission(origin, WebContentsAdapterClient::MediaVideoCapture);
+        break;
+    case QWebEnginePermission::PermissionType::DesktopAudioVideoCapture:
+        grantMediaAccessPermission(origin, desktopAudioVideoCaptureFlags);
+        break;
+    case QWebEnginePermission::PermissionType::DesktopVideoCapture:
+        grantMediaAccessPermission(origin, WebContentsAdapterClient::MediaDesktopVideoCapture);
+        break;
+    default:
+        Q_UNREACHABLE();
+        break;
+    }
 }
 
-void WebContentsAdapter::grantFeaturePermission(const QUrl &securityOrigin, ProfileAdapter::PermissionType feature, ProfileAdapter::PermissionState allowed)
+QWebEnginePermission::State WebContentsAdapter::getPermissionState(const QUrl &origin, QWebEnginePermission::PermissionType permissionType)
 {
-    Q_ASSERT(m_profileAdapter);
-    m_profileAdapter->permissionRequestReply(securityOrigin, feature, allowed);
+    return m_profileAdapter->getPermissionState(origin, permissionType, m_webContents.get()->GetPrimaryMainFrame());
+}
+
+void WebContentsAdapter::grantMediaAccessPermission(const QUrl &origin, WebContentsAdapterClient::MediaRequestFlags flags)
+{
+    CHECK_INITIALIZED();
+    // Let the permission manager remember the reply.
+    if (flags & WebContentsAdapterClient::MediaAudioCapture)
+        m_profileAdapter->setPermission(origin,
+            QWebEnginePermission::PermissionType::MediaAudioCapture,
+            QWebEnginePermission::State::Granted,
+            m_webContents.get()->GetPrimaryMainFrame());
+    if (flags & WebContentsAdapterClient::MediaVideoCapture)
+        m_profileAdapter->setPermission(origin,
+            QWebEnginePermission::PermissionType::MediaVideoCapture,
+            QWebEnginePermission::State::Granted,
+            m_webContents.get()->GetPrimaryMainFrame());
+    MediaCaptureDevicesDispatcher::GetInstance()->handleMediaAccessPermissionResponse(m_webContents.get(), origin, flags);
 }
 
 void WebContentsAdapter::grantMouseLockPermission(const QUrl &securityOrigin, bool granted)
@@ -1488,6 +1570,11 @@ content::WebContents *WebContentsAdapter::guestWebContents() const
     return !innerWebContents.empty() ? innerWebContents[0] : nullptr;
 }
 
+WebContentsAdapterClient *WebContentsAdapter::adapterClient()
+{
+    return m_adapterClient;
+}
+
 #if QT_CONFIG(webengine_webchannel)
 QWebChannel *WebContentsAdapter::webChannel() const
 {
@@ -1535,7 +1622,8 @@ static QMimeData *mimeDataFromDropData(const content::DropData &dropData)
     if (!dropData.custom_data.empty()) {
         base::Pickle pickle;
         ui::WriteCustomDataToPickle(dropData.custom_data, &pickle);
-        mimeData->setData(QLatin1String(ui::kMimeTypeWebCustomData), QByteArray((const char*)pickle.data(), pickle.size()));
+        mimeData->setData(QLatin1StringView(ui::kMimeTypeWebCustomData),
+                          QByteArray((const char *)pickle.data(), pickle.size()));
     }
     return mimeData;
 }
@@ -1665,9 +1753,14 @@ static void fillDropDataFromMimeData(content::DropData *dropData, const QMimeDat
         dropData->html = toOptionalString16(mimeData->html());
     if (mimeData->hasText())
         dropData->text = toOptionalString16(mimeData->text());
-    if (mimeData->hasFormat(QLatin1String(ui::kMimeTypeWebCustomData))) {
-        QByteArray customData = mimeData->data(QLatin1String(ui::kMimeTypeWebCustomData));
-        ui::ReadCustomDataIntoMap(customData.constData(), customData.length(), &dropData->custom_data);
+    const QString mimeType = QString::fromLatin1(ui::kMimeTypeWebCustomData);
+    if (mimeData->hasFormat(mimeType)) {
+        const QByteArray customData = mimeData->data(mimeType);
+        const base::span custom_data(customData.constData(), (long unsigned)customData.length());
+        if (auto maybe_data = ui::ReadCustomDataIntoMap(base::as_bytes(custom_data)))
+            dropData->custom_data = *std::move(maybe_data);
+        else
+            dropData->custom_data.clear();
     }
 }
 
@@ -1764,11 +1857,12 @@ void WebContentsAdapter::waitForUpdateDragActionCalled()
     }
 }
 
-void WebContentsAdapter::updateDragAction(int action)
+void WebContentsAdapter::updateDragAction(int action, bool documentIsHandlingDrag)
 {
     CHECK_INITIALIZED();
     m_updateDragActionCalled = true;
     m_currentDropAction = action;
+    m_documentIsHandlingDrag = documentIsHandlingDrag;
 }
 
 void WebContentsAdapter::endDragging(QDropEvent *e, const QPointF &screenPos)
@@ -1778,6 +1872,7 @@ void WebContentsAdapter::endDragging(QDropEvent *e, const QPointF &screenPos)
     rvh->GetWidget()->FilterDropData(m_currentDropData.get());
     m_lastDragClientPos = e->position();
     m_lastDragScreenPos = screenPos;
+    m_currentDropData->document_is_handling_drag = m_documentIsHandlingDrag;
     rvh->GetWidget()->DragTargetDrop(*m_currentDropData, toGfx(m_lastDragClientPos), toGfx(m_lastDragScreenPos),
                                      toWeb(e->buttons()) | toWeb(e->modifiers()), base::DoNothing());
 
@@ -1875,7 +1970,7 @@ QString WebContentsAdapter::frameHtmlName(quint64 id) const
 {
     CHECK_INITIALIZED_AND_VALID_FRAME(id, ftn, QString());
     auto &maybeName = ftn->html_name();
-    return maybeName ? QString::fromStdString(*maybeName) : QString("");
+    return maybeName ? QString::fromStdString(*maybeName) : ""_L1;
 }
 
 QList<quint64> WebContentsAdapter::frameChildren(quint64 id) const
@@ -2137,7 +2232,7 @@ void WebContentsAdapter::discard()
 
     if (m_webContents->IsLoading()) {
         m_webContentsDelegate->didFailLoad(m_webContentsDelegate->url(webContents()), net::Error::ERR_ABORTED,
-                                           QStringLiteral("Discarded"));
+                                           u"Discarded"_s);
         m_webContentsDelegate->DidStopLoading();
     }
 
diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h
index 9663c77eda7..3a2db1c1e06 100644
--- a/src/core/web_contents_adapter.h
+++ b/src/core/web_contents_adapter.h
@@ -26,6 +26,7 @@
 #include <QtWebEngineCore/qwebenginecontextmenurequest.h>
 #include <QtWebEngineCore/qwebenginehttprequest.h>
 #include <QtWebEngineCore/qwebengineframe.h>
+#include <QtWebEngineCore/qwebenginepermission.h>
 
 #include "web_contents_adapter_client.h"
 
@@ -178,10 +179,12 @@ class Q_WEBENGINECORE_EXPORT WebContentsAdapter : public QEnableSharedFromThis<W
     void devToolsFrontendDestroyed(DevToolsFrontendQt *frontend);
     QString devToolsId();
 
-    void grantMediaAccessPermission(const QUrl &securityOrigin, WebContentsAdapterClient::MediaRequestFlags flags);
-    void grantMouseLockPermission(const QUrl &securityOrigin, bool granted);
+    void setPermission(const QUrl &origin, QWebEnginePermission::PermissionType permissionType, QWebEnginePermission::State state);
+    QWebEnginePermission::State getPermissionState(const QUrl &origin, QWebEnginePermission::PermissionType permissionType);
+
+    void grantMediaAccessPermission(const QUrl &origin, WebContentsAdapterClient::MediaRequestFlags flags);
+    void grantMouseLockPermission(const QUrl &origin, bool granted);
     void handlePendingMouseLockPermission();
-    void grantFeaturePermission(const QUrl &securityOrigin, ProfileAdapter::PermissionType feature, ProfileAdapter::PermissionState allowed);
 
     void setBackgroundColor(const QColor &color);
     QAccessibleInterface *browserAccessible();
@@ -202,14 +205,14 @@ class Q_WEBENGINECORE_EXPORT WebContentsAdapter : public QEnableSharedFromThis<W
                        Qt::DropActions allowedActions, const QPixmap &pixmap, const QPoint &offset);
     void enterDrag(QDragEnterEvent *e, const QPointF &screenPos);
     Qt::DropAction updateDragPosition(QDragMoveEvent *e, const QPointF &screenPos);
-    void updateDragAction(int action);
+    void updateDragAction(int action, bool documentIsHandlingDrag);
     void endDragging(QDropEvent *e, const QPointF &screenPos);
     void leaveDrag();
 #endif // QT_CONFIG(draganddrop)
-    void printToPDF(const QPageLayout&, const QPageRanges &, const QString&);
+    void printToPDF(const QPageLayout &, const QPageRanges &, const QString &, quint64 frameId);
     void printToPDFCallbackResult(std::function<void(QSharedPointer<QByteArray>)> &&,
-                                  const QPageLayout &, const QPageRanges &, bool colorMode = true,
-                                  bool useCustomMargins = true);
+                                  const QPageLayout &, const QPageRanges &, bool colorMode,
+                                  bool useCustomMargins, quint64 frameId);
     void didPrintPage(quint64 requestId, QSharedPointer<QByteArray> result);
 
     void replaceMisspelling(const QString &word);
@@ -235,6 +238,7 @@ class Q_WEBENGINECORE_EXPORT WebContentsAdapter : public QEnableSharedFromThis<W
     void initialize(content::SiteInstance *site);
     content::WebContents *webContents() const;
     content::WebContents *guestWebContents() const;
+    WebContentsAdapterClient *adapterClient();
     void updateRecommendedState();
     void setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor);
     QWebEngineUrlRequestInterceptor* requestInterceptor() const;
@@ -281,6 +285,7 @@ class Q_WEBENGINECORE_EXPORT WebContentsAdapter : public QEnableSharedFromThis<W
     LifecycleState m_lifecycleState = LifecycleState::Active;
     LifecycleState m_recommendedState = LifecycleState::Active;
     bool m_inspector = false;
+    bool m_documentIsHandlingDrag = false;
     QPointer<QWebEngineUrlRequestInterceptor> m_requestInterceptor;
 };
 
diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h
index 3c50ac84011..9ce5bebfc70 100644
--- a/src/core/web_contents_adapter_client.h
+++ b/src/core/web_contents_adapter_client.h
@@ -16,6 +16,7 @@
 #define WEB_CONTENTS_ADAPTER_CLIENT_H
 
 #include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h>
+#include <QtWebEngineCore/qwebenginepermission.h>
 
 #include "profile_adapter.h"
 
@@ -183,9 +184,10 @@ class Q_WEBENGINECORE_EXPORT WebContentsAdapterClient {
     virtual void didFetchDocumentMarkup(quint64 requestId, const QString& result) = 0;
     virtual void didFetchDocumentInnerText(quint64 requestId, const QString& result) = 0;
     virtual void printToPdf(const QString &filePath, const QPageLayout &layout,
-                            const QPageRanges &ranges) = 0;
+                            const QPageRanges &ranges, quint64 frameId) = 0;
     virtual void printToPdf(std::function<void(QSharedPointer<QByteArray>)> &&callback,
-                            const QPageLayout &layout, const QPageRanges &ranges) = 0;
+                            const QPageLayout &layout, const QPageRanges &ranges,
+                            quint64 frameId) = 0;
     virtual void didPrintPageToPdf(const QString &filePath, bool success) = 0;
     virtual bool passOnFocus(bool reverse) = 0;
     // returns the last QObject (QWidget/QQuickItem) based object in the accessibility
@@ -193,7 +195,7 @@ class Q_WEBENGINECORE_EXPORT WebContentsAdapterClient {
     virtual QObject *accessibilityParentObject() = 0;
     virtual void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID) = 0;
     virtual void authenticationRequired(QSharedPointer<AuthenticationDialogController>) = 0;
-    virtual void runFeaturePermissionRequest(ProfileAdapter::PermissionType, const QUrl &securityOrigin) = 0;
+    virtual void runFeaturePermissionRequest(QWebEnginePermission::PermissionType, const QUrl &securityOrigin) = 0;
     virtual void runMediaAccessPermissionRequest(const QUrl &securityOrigin, MediaRequestFlags requestFlags) = 0;
     virtual void runMouseLockPermissionRequest(const QUrl &securityOrigin) = 0;
     virtual void runRegisterProtocolHandlerRequest(QWebEngineRegisterProtocolHandlerRequest) = 0;
@@ -227,6 +229,7 @@ class Q_WEBENGINECORE_EXPORT WebContentsAdapterClient {
     virtual WebContentsAdapter* webContentsAdapter() = 0;
     virtual void releaseProfile() = 0;
     virtual void showWebAuthDialog(QWebEngineWebAuthUxRequest *request) = 0;
+    virtual QWebEnginePermission createFeaturePermissionObject(const QUrl &securityOrigin, QWebEnginePermission::PermissionType permissionType) = 0;
 };
 
 } // namespace QtWebEngineCore
diff --git a/src/core/web_contents_delegate_qt.cpp b/src/core/web_contents_delegate_qt.cpp
index 4df73fb6999..5371e9a5448 100644
--- a/src/core/web_contents_delegate_qt.cpp
+++ b/src/core/web_contents_delegate_qt.cpp
@@ -57,6 +57,8 @@
 #include <QTimer>
 #include <QWindow>
 
+using namespace Qt::StringLiterals;
+
 namespace QtWebEngineCore {
 
 static WebContentsAdapterClient::JavaScriptConsoleMessageLevel mapToJavascriptConsoleMessageLevel(blink::mojom::ConsoleMessageLevel log_level)
@@ -199,7 +201,8 @@ QUrl WebContentsDelegateQt::url(content::WebContents *source) const
         if (source->GetVisibleURL().SchemeIs(content::kViewSourceScheme) &&
             (url.has_password() || url.has_username() || url.has_ref())) {
             GURL strippedUrl = net::SimplifyUrlForRequest(url);
-            newUrl = QUrl(QString("%1:%2").arg(content::kViewSourceScheme, QString::fromStdString(strippedUrl.spec())));
+            newUrl = QUrl(QLatin1StringView(content::kViewSourceScheme) + u':'
+                          + QString::fromStdString(strippedUrl.spec()));
         }
         // If there is a visible entry there are special cases where we dont wan't to use the actual URL
         if (newUrl.isEmpty())
@@ -479,6 +482,14 @@ void WebContentsDelegateQt::DidStopLoading()
     m_loadingInfo.clear();
 }
 
+void WebContentsDelegateQt::emitLoadSucceeded(const QUrl &url)
+{
+    // Used by CustomURLLoader to emit LoadSucceeded bypassing the inner state of this delegate
+    m_viewClient->loadFinished(
+            QWebEngineLoadingInfo(url, QWebEngineLoadingInfo::LoadSucceededStatus));
+    m_viewClient->updateNavigationActions();
+}
+
 void WebContentsDelegateQt::didFailLoad(const QUrl &url, int errorCode, const QString &errorDescription)
 {
     m_viewClient->iconChanged(QUrl());
@@ -733,9 +744,9 @@ void WebContentsDelegateQt::selectClientCert(const QSharedPointer<ClientCertSele
     m_viewClient->selectClientCert(selectController);
 }
 
-void WebContentsDelegateQt::requestFeaturePermission(ProfileAdapter::PermissionType feature, const QUrl &requestingOrigin)
+void WebContentsDelegateQt::requestFeaturePermission(QWebEnginePermission::PermissionType permissionType, const QUrl &requestingOrigin)
 {
-    m_viewClient->runFeaturePermissionRequest(feature, requestingOrigin);
+    m_viewClient->runFeaturePermissionRequest(permissionType, requestingOrigin);
 }
 
 extern WebContentsAdapterClient::NavigationType pageTransitionToNavigationType(ui::PageTransition transition);
@@ -769,10 +780,13 @@ void WebContentsDelegateQt::launchExternalURL(const QUrl &url, ui::PageTransitio
 
     if (!navigationAllowedByPolicy || !navigationRequestAccepted) {
         QString errorDescription;
-        if (!navigationAllowedByPolicy)
-            errorDescription = QStringLiteral("Launching external protocol forbidden by WebEngineSettings::UnknownUrlSchemePolicy");
-        else
-            errorDescription = QStringLiteral("Launching external protocol suppressed by 'navigationRequested' API");
+        if (!navigationAllowedByPolicy) {
+            errorDescription = u"Launching external protocol forbidden by "
+                               "WebEngineSettings::UnknownUrlSchemePolicy"_s;
+        } else {
+            errorDescription = u"Launching external protocol suppressed by "
+                               "'navigationRequested' API"_s;
+        }
         didFailLoad(url, net::Error::ERR_ABORTED, errorDescription);
     }
 }
@@ -786,13 +800,24 @@ void WebContentsDelegateQt::BeforeUnloadFired(content::WebContents *tab, bool pr
         m_viewClient->windowCloseRejected();
 }
 
-bool WebContentsDelegateQt::CheckMediaAccessPermission(content::RenderFrameHost *, const GURL& security_origin, blink::mojom::MediaStreamType type)
+bool WebContentsDelegateQt::CheckMediaAccessPermission(content::RenderFrameHost *rfh,
+                                                       const url::Origin &security_origin,
+                                                       blink::mojom::MediaStreamType type)
 {
+    Q_ASSERT(rfh);
     switch (type) {
     case blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE:
-        return m_viewClient->profileAdapter()->checkPermission(toQt(security_origin), ProfileAdapter::AudioCapturePermission);
+        return m_viewClient->profileAdapter()->getPermissionState(
+            toQt(security_origin),
+            QWebEnginePermission::PermissionType::MediaAudioCapture,
+            rfh)
+                == QWebEnginePermission::State::Granted;
     case blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE:
-        return m_viewClient->profileAdapter()->checkPermission(toQt(security_origin), ProfileAdapter::VideoCapturePermission);
+        return m_viewClient->profileAdapter()->getPermissionState(
+            toQt(security_origin),
+            QWebEnginePermission::PermissionType::MediaVideoCapture,
+            rfh)
+                == QWebEnginePermission::State::Granted;
     default:
         LOG(INFO) << "WebContentsDelegateQt::CheckMediaAccessPermission: "
                   << "Unsupported media stream type checked " << type;
diff --git a/src/core/web_contents_delegate_qt.h b/src/core/web_contents_delegate_qt.h
index 51004878d65..8188b3dea9f 100644
--- a/src/core/web_contents_delegate_qt.h
+++ b/src/core/web_contents_delegate_qt.h
@@ -106,7 +106,9 @@ class WebContentsDelegateQt : public content::WebContentsDelegate
     void UpdateTargetURL(content::WebContents* source, const GURL& url) override;
     void RequestToLockMouse(content::WebContents *web_contents, bool user_gesture, bool last_unlocked_by_target) override;
     void BeforeUnloadFired(content::WebContents* tab, bool proceed, bool* proceed_to_fire_unload) override;
-    bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host, const GURL& security_origin, blink::mojom::MediaStreamType type) override;
+    bool CheckMediaAccessPermission(content::RenderFrameHost *render_frame_host,
+                                    const url::Origin &security_origin,
+                                    blink::mojom::MediaStreamType type) override;
     void RegisterProtocolHandler(content::RenderFrameHost* frame_host, const std::string& protocol, const GURL& url, bool user_gesture) override;
     void UnregisterProtocolHandler(content::RenderFrameHost* frame_host, const std::string& protocol, const GURL& url, bool user_gesture) override;
     bool TakeFocus(content::WebContents *source, bool reverse) override;
@@ -132,11 +134,12 @@ class WebContentsDelegateQt : public content::WebContentsDelegate
                                   content::RenderFrameHost *render_frame_host,
                                   bool is_full_page) override;
 
+    void emitLoadSucceeded(const QUrl &url);
     void didFailLoad(const QUrl &url, int errorCode, const QString &errorDescription);
     void overrideWebPreferences(content::WebContents *, blink::web_pref::WebPreferences*);
     void allowCertificateError(const QSharedPointer<CertificateErrorController> &);
     void selectClientCert(const QSharedPointer<ClientCertSelectController> &);
-    void requestFeaturePermission(ProfileAdapter::PermissionType feature, const QUrl &requestingOrigin);
+    void requestFeaturePermission(QWebEnginePermission::PermissionType permissionType, const QUrl &requestingOrigin);
     void launchExternalURL(const QUrl &url, ui::PageTransition page_transition, bool is_main_frame, bool has_user_gesture);
     FindTextHelper *findTextHelper();
 
diff --git a/src/core/web_contents_view_qt.cpp b/src/core/web_contents_view_qt.cpp
index 023f9e99f29..028cca6f831 100644
--- a/src/core/web_contents_view_qt.cpp
+++ b/src/core/web_contents_view_qt.cpp
@@ -227,6 +227,7 @@ static Qt::DropActions toQtDropActions(blink::DragOperationsMask ops)
 }
 
 void WebContentsViewQt::StartDragging(const content::DropData &drop_data,
+                                      const url::Origin& source_origin,
                                       blink::DragOperationsMask allowed_ops,
                                       const gfx::ImageSkia &image,
                                       const gfx::Vector2d &image_offset,
@@ -256,10 +257,10 @@ void WebContentsViewQt::StartDragging(const content::DropData &drop_data,
 #endif // QT_CONFIG(draganddrop)
 }
 
-void WebContentsViewQt::UpdateDragCursor(ui::mojom::DragOperation dragOperation)
+void WebContentsViewQt::UpdateDragOperation(ui::mojom::DragOperation dragOperation, bool document_is_handling_drag)
 {
 #if QT_CONFIG(draganddrop)
-    m_client->webContentsAdapter()->updateDragAction(int(dragOperation));
+    m_client->webContentsAdapter()->updateDragAction(int(dragOperation), document_is_handling_drag);
 #endif // QT_CONFIG(draganddrop)
 }
 
diff --git a/src/core/web_contents_view_qt.h b/src/core/web_contents_view_qt.h
index 8754250e641..78041a1506d 100644
--- a/src/core/web_contents_view_qt.h
+++ b/src/core/web_contents_view_qt.h
@@ -80,13 +80,17 @@ class WebContentsViewQt
 #endif
 
     // content::RenderViewHostDelegateView overrides:
-    void StartDragging(const content::DropData& drop_data, blink::DragOperationsMask allowed_ops,
+    void StartDragging(const content::DropData& drop_data,
+                       const url::Origin& source_origin,
+                       blink::DragOperationsMask allowed_ops,
                        const gfx::ImageSkia& image, const gfx::Vector2d& image_offset,
                        const gfx::Rect& drag_obj_rect,
                        const blink::mojom::DragEventSourceInfo &event_info,
                        content::RenderWidgetHostImpl *source_rwh) override;
 
-    void UpdateDragCursor(ui::mojom::DragOperation dragOperation) override;
+    void UpdateDragOperation(ui::mojom::DragOperation dragOperation, bool document_is_handling_drag) override;
+
+    void TransferDragSecurityInfo(content::WebContentsView *) override {}
 
     void ShowContextMenu(content::RenderFrameHost &, const content::ContextMenuParams &params) override;
 
diff --git a/src/core/web_engine_context.cpp b/src/core/web_engine_context.cpp
index faf9bd54229..f5cb8be2098 100644
--- a/src/core/web_engine_context.cpp
+++ b/src/core/web_engine_context.cpp
@@ -11,6 +11,7 @@
 #include "base/functional/bind.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
+#include "base/metrics/field_trial.h"
 #include "base/power_monitor/power_monitor.h"
 #include "base/power_monitor/power_monitor_device_source.h"
 #include "base/run_loop.h"
@@ -19,9 +20,6 @@
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "base/threading/thread_restrictions.h"
 #include "cc/base/switches.h"
-#if QT_CONFIG(webengine_webrtc) && QT_CONFIG(webengine_extensions)
-#include "chrome/browser/media/webrtc/webrtc_log_uploader.h"
-#endif
 #include "chrome/common/chrome_switches.h"
 #include "content/common/process_visibility_tracker.h"
 #include "content/gpu/gpu_child_thread.h"
@@ -97,7 +95,6 @@
 #include "devtools_manager_delegate_qt.h"
 #include "media_capture_devices_dispatcher.h"
 #include "net/webui_controller_factory_qt.h"
-#include "ozone/gl_context_qt.h"
 #include "profile_adapter.h"
 #include "type_conversion.h"
 #include "web_engine_library_info.h"
@@ -116,7 +113,9 @@
 #include <QtQuick/private/qsgrhisupport_p.h>
 #include <QLoggingCategory>
 
-#if QT_CONFIG(opengl)
+#if QT_CONFIG(opengl) && (defined(USE_OZONE) || defined(Q_OS_WIN))
+#include "ozone/gl_context_qt.h"
+
 #include <QOpenGLContext>
 #include <qopenglcontext_platform.h>
 
@@ -128,6 +127,8 @@ QT_END_NAMESPACE
 #define STRINGIFY_LITERAL(x) #x
 #define STRINGIFY_EXPANDED(x) STRINGIFY_LITERAL(x)
 
+using namespace Qt::StringLiterals;
+
 namespace QtWebEngineCore {
 
 Q_LOGGING_CATEGORY(webEngineContextLog, "qt.webenginecontext")
@@ -137,6 +138,8 @@ class GPUInfo
 public:
     enum Vendor {
         Unknown = -1,
+
+        // PCI-SIG-registered vendors
         AMD,
         Apple,
         ARM,
@@ -144,10 +147,20 @@ class GPUInfo
         ImgTec,
         Intel,
         Microsoft,
-        Mesa,
         Nvidia,
         Qualcomm,
-        Samsung
+        Samsung,
+        Broadcom,
+        VMware,
+        VirtIO,
+
+        // Khronos-registered vendors
+        Vivante,
+        VeriSilicon,
+        Kazan,
+        CodePlay,
+        Mesa,
+        PoCL,
     };
 
     static GPUInfo *instance()
@@ -159,7 +172,7 @@ class GPUInfo
     static Vendor vendorIdToVendor(quint64 vendorId)
     {
         // clang-format off
-        // Based on //third_party/dawn/src/dawn/gpu_info.json
+        // Based on //third_party/angle/src/gpu_info_util/SystemInfo.h
         static const std::map<quint64, Vendor> vendorIdMap = {
             {0x0, Unknown},
             {0x1002, AMD},
@@ -168,11 +181,19 @@ class GPUInfo
             {0x1AE0, Google},
             {0x1010, ImgTec},
             {0x8086, Intel},
-            {0x10005, Mesa},
             {0x1414, Microsoft},
             {0x10DE, Nvidia},
             {0x5143, Qualcomm},
-            {0x144D, Samsung}
+            {0x144D, Samsung},
+            {0x14E4, Broadcom},
+            {0x15AD, VMware},
+            {0x1AF4, VirtIO},
+            {0x10001, Vivante},
+            {0x10002, VeriSilicon},
+            {0x10003, Kazan},
+            {0x10004, CodePlay},
+            {0x10005, Mesa},
+            {0x10006, PoCL},
         };
         // clang-format on
 
@@ -180,27 +201,29 @@ class GPUInfo
         if (it != vendorIdMap.end())
             return it->second;
 
-        qWarning() << "Unknown Vendor ID:" << QString("0x%1").arg(vendorId, 0, 16);
+        qWarning("Unknown Vendor ID: 0x%llx", vendorId);
         return Unknown;
     }
 
-    static Vendor deviceNameToVendor(QString deviceName)
+    static Vendor deviceNameToVendor(QLatin1StringView deviceName)
     {
         // TODO: Test and add more vendors to the list.
-        if (deviceName.contains(QLatin1String("AMD"), Qt::CaseInsensitive))
+        if (deviceName.contains("AMD"_L1, Qt::CaseInsensitive))
             return AMD;
-        if (deviceName.contains(QLatin1String("Intel"), Qt::CaseInsensitive))
+        if (deviceName.contains("Intel"_L1, Qt::CaseInsensitive))
             return Intel;
-        if (deviceName.contains(QLatin1String("Nvidia"), Qt::CaseInsensitive))
+        if (deviceName.contains("Nvidia"_L1, Qt::CaseInsensitive))
             return Nvidia;
+        if (deviceName.contains("VMware"_L1, Qt::CaseInsensitive))
+            return VMware;
 
 #if defined(USE_OZONE)
-        if (deviceName.contains(QLatin1String("Mesa llvmpipe")))
+        if (deviceName.contains("Mesa llvmpipe"_L1))
             return Mesa;
 #endif
 
 #if defined(Q_OS_MACOS)
-        if (deviceName.contains(QLatin1String("Apple")))
+        if (deviceName.contains("Apple"_L1))
             return Apple;
 #endif
 
@@ -218,11 +241,19 @@ class GPUInfo
             {Google, "Google"},
             {ImgTec, "Img Tec"},
             {Intel, "Intel"},
-            {Mesa, "Mesa"},
             {Microsoft, "Microsoft"},
             {Nvidia, "Nvidia"},
             {Qualcomm, "Qualcomm"},
-            {Samsung, "Samsung"}
+            {Samsung, "Samsung"},
+            {Broadcom, "Broadcom"},
+            {VMware, "VMware"},
+            {VirtIO, "VirtIO"},
+            {Vivante, "Vivante"},
+            {VeriSilicon, "VeriSilicon"},
+            {Kazan, "Kazan"},
+            {CodePlay, "CodePlay"},
+            {Mesa, "Mesa"},
+            {PoCL, "PoCL"},
         };
         // clang-format on
 
@@ -261,8 +292,8 @@ class GPUInfo
                 const QRhiD3D11NativeHandles *handles =
                         static_cast<const QRhiD3D11NativeHandles *>(d3d11Rhi->nativeHandles());
                 Q_ASSERT(handles);
-                m_adapterLuid =
-                        QString("%1,%2").arg(handles->adapterLuidHigh).arg(handles->adapterLuidLow);
+                m_adapterLuid = QString::number(handles->adapterLuidHigh) % QLatin1Char(',')
+                        % QString::number(handles->adapterLuidLow);
             }
         }
 #elif defined(Q_OS_MACOS)
@@ -271,7 +302,7 @@ class GPUInfo
             QScopedPointer<QRhi> metalRhi(
                     QRhi::create(QRhi::Metal, &params, QRhi::Flags(), nullptr));
             if (metalRhi)
-                m_vendor = deviceNameToVendor(metalRhi->driverInfo().deviceName);
+                m_vendor = deviceNameToVendor(QLatin1StringView(metalRhi->driverInfo().deviceName));
         }
 #endif
 
@@ -282,7 +313,7 @@ class GPUInfo
             QScopedPointer<QRhi> glRhi(
                     QRhi::create(QRhi::OpenGLES2, &params, QRhi::Flags(), nullptr));
             if (glRhi)
-                m_vendor = deviceNameToVendor(glRhi->driverInfo().deviceName);
+                m_vendor = deviceNameToVendor(QLatin1StringView(glRhi->driverInfo().deviceName));
         }
 #endif
 
@@ -329,7 +360,7 @@ static bool usingSupportedSGBackend()
     QString device = QQuickWindow::sceneGraphBackend();
 
     for (int index = 0; index < args.count(); ++index) {
-        if (args.at(index).startsWith(QLatin1String("--device="))) {
+        if (args.at(index).startsWith("--device="_L1)) {
             device = args.at(index).mid(9);
             break;
         }
@@ -340,7 +371,7 @@ static bool usingSupportedSGBackend()
     if (device.isEmpty())
         device = qEnvironmentVariable("QMLSCENE_DEVICE");
 
-    return device.isEmpty() || device == QLatin1String("rhi");
+    return device.isEmpty() || device == "rhi"_L1;
 }
 
 #if QT_CONFIG(opengl)
@@ -360,23 +391,15 @@ bool usingSoftwareDynamicGL()
     wchar_t path[MAX_PATH];
     DWORD size = GetModuleFileName(handle, path, MAX_PATH);
     QFileInfo openGLModule(QString::fromWCharArray(path, size));
-    return openGLModule.fileName().contains(QLatin1String("opengl32sw"),Qt::CaseInsensitive);
+    return openGLModule.fileName().contains(QLatin1StringView("opengl32sw"), Qt::CaseInsensitive);
 #else
     return false;
 #endif
 }
 
-static bool openGLPlatformSupport()
-{
-    return QGuiApplicationPrivate::platformIntegration()->hasCapability(
-            QPlatformIntegration::OpenGL);
-}
-
 static std::string getGLType(bool enableGLSoftwareRendering, bool disableGpu)
 {
-    const bool tryGL =
-            usingSupportedSGBackend() && !usingSoftwareDynamicGL() && openGLPlatformSupport();
-    if (disableGpu || (!tryGL && !enableGLSoftwareRendering))
+    if (disableGpu || !usingSupportedSGBackend())
         return gl::kGLImplementationDisabledName;
 
 #if defined(Q_OS_MACOS)
@@ -389,6 +412,9 @@ static std::string getGLType(bool enableGLSoftwareRendering, bool disableGpu)
     }
 #endif
 
+    if (usingSoftwareDynamicGL() && !enableGLSoftwareRendering)
+        return gl::kGLImplementationDisabledName;
+
     if (!qt_gl_global_share_context() || !qt_gl_global_share_context()->isValid()) {
         qWarning("WebEngineContext is used before QtWebEngineQuick::initialize() or OpenGL context "
                  "creation failed.");
@@ -421,8 +447,9 @@ static std::string getGLType(bool enableGLSoftwareRendering, bool disableGpu)
 #else
 static std::string getGLType(bool /*enableGLSoftwareRendering*/, bool disableGpu)
 {
-    if (disableGpu)
+    if (disableGpu || !usingSupportedSGBackend())
         return gl::kGLImplementationDisabledName;
+
 #if defined(Q_OS_MACOS)
     return gl::kGLImplementationANGLEName;
 #elif defined(Q_OS_WIN)
@@ -472,28 +499,33 @@ void dummyGetPluginCallback(const std::vector<content::WebPluginInfo>&)
 static void logContext(const std::string &glType, base::CommandLine *cmd)
 {
     if (Q_UNLIKELY(webEngineContextLog().isDebugEnabled())) {
-        QStringList log;
-        log << "\n";
+        QString log;
+        log += u'\n';
 
-        log << "Chromium GL Backend:" << glType.c_str() << "\n";
-        log << "Chromium ANGLE Backend:" << getAngleType(glType, cmd).c_str() << "\n";
-        log << "Chromium Vulkan Backend:" << getVulkanType(cmd).c_str() << "\n";
-        log << "\n";
+        log += "Chromium GL Backend: "_L1 + QLatin1StringView(glType) + "\n"_L1;
+        log += "Chromium ANGLE Backend: "_L1 + QLatin1StringView(getAngleType(glType, cmd)) + u'\n';
+        log += "Chromium Vulkan Backend: "_L1 + QLatin1StringView(getVulkanType(cmd)) + u'\n';
+        log += u'\n';
 
-        log << "QSG RHI Backend:" << QSGRhiSupport::instance()->rhiBackendName() << "\n";
-        log << "QSG RHI Backend Supported:" << (usingSupportedSGBackend() ? "yes" : "no") << "\n";
-        log << "GPU Vendor:" << GPUInfo::vendorToString(GPUInfo::instance()->vendor()).c_str();
-        log << "\n";
+        log += "QSG RHI Backend: "_L1 + QSGRhiSupport::instance()->rhiBackendName() + u'\n';
+        log += "QSG RHI Backend Supported: "_L1 + (usingSupportedSGBackend() ? "yes"_L1 : "no"_L1)
+                + u'\n';
+        log += "GPU Vendor: "_L1
+                + QLatin1StringView(GPUInfo::vendorToString(GPUInfo::instance()->vendor())) + u'\n';
+        log += u'\n';
 
 #if QT_CONFIG(opengl)
 #if defined(USE_OZONE)
-        log << "Using GLX:" << (GLContextHelper::getGlxPlatformInterface() ? "yes" : "no") << "\n";
-        log << "Using EGL:" << (GLContextHelper::getEglPlatformInterface() ? "yes" : "no") << "\n";
+        log += "Using GLX: "_L1 + (GLContextHelper::getGlxPlatformInterface() ? "yes"_L1 : "no"_L1)
+                + u'\n';
+        log += "Using EGL: "_L1 + (GLContextHelper::getEglPlatformInterface() ? "yes"_L1 : "no"_L1)
+                + u'\n';
 #endif
-        log << "Using Shared GL:" << (qt_gl_global_share_context() ? "yes" : "no") << "\n";
+#if defined(USE_OZONE) || defined(Q_OS_WIN)
+        log += "Using Shared GL: "_L1 + (qt_gl_global_share_context() ? "yes"_L1 : "no"_L1) + u'\n';
         if (qt_gl_global_share_context()) {
-            log << "Using Software Dynamic GL:" << (usingSoftwareDynamicGL() ? "yes" : "no")
-                << "\n";
+            log += "Using Software Dynamic GL: "_L1
+                    + (usingSoftwareDynamicGL() ? "yes"_L1 : "no"_L1) + u'\n';
 
             const QSurfaceFormat sharedFormat = qt_gl_global_share_context()
                     ? qt_gl_global_share_context()->format()
@@ -503,23 +535,21 @@ static void logContext(const std::string &glType, base::CommandLine *cmd)
                             sharedFormat.profile());
             const auto type = QMetaEnum::fromType<QSurfaceFormat::RenderableType>().valueToKey(
                     sharedFormat.renderableType());
-            log << "Surface Type:" << type << "\n";
-            log << "Surface Profile:" << profile << "\n";
-            log << "Surface Version:"
-                << QString("%1.%2")
-                            .arg(sharedFormat.majorVersion())
-                            .arg(sharedFormat.minorVersion())
-                << "\n";
+            log += "Surface Type: "_L1 + QLatin1StringView(type) + u'\n';
+            log += "Surface Profile: "_L1 + QLatin1StringView(profile) + u'\n';
+            log += "Surface Version: "_L1 + QString::number(sharedFormat.majorVersion()) + u'.'
+                    + QString::number(sharedFormat.minorVersion()) + u'\n';
         }
-        log << "\n";
+        log += u'\n';
+#endif // defined(USE_OZONE) || defined(Q_OS_WIN)
 #endif // QT_CONFIG(opengl)
 
-        log << "Init Parameters:\n";
+        log += "Init Parameters:\n"_L1;
         const base::CommandLine::SwitchMap switchMap = cmd->GetSwitches();
         for (const auto &pair : switchMap)
-            log << " * " << toQt(pair.first) << toQt(pair.second) << "\n";
+            log += " *  "_L1 + toQt(pair.first) + u' ' + toQt(pair.second) + u'\n';
 
-        qCDebug(webEngineContextLog) << qPrintable(log.join(" "));
+        qCDebug(webEngineContextLog, "%ls", qUtf16Printable(log));
     }
 }
 
@@ -529,15 +559,16 @@ static void setupProxyPac(base::CommandLine *commandLine)
 {
     if (commandLine->HasSwitch(switches::kProxyPacUrl)) {
         QUrl pac_url(toQt(commandLine->GetSwitchValueASCII(switches::kProxyPacUrl)));
-        if (pac_url.isValid() && (pac_url.isLocalFile() ||
-                                  !pac_url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive))) {
+        if (pac_url.isValid()
+            && (pac_url.isLocalFile()
+                || !pac_url.scheme().compare("qrc"_L1, Qt::CaseInsensitive))) {
             QFile file;
             if (pac_url.isLocalFile())
                 file.setFileName(pac_url.toLocalFile());
             else
-                file.setFileName(pac_url.path().prepend(QChar(':')));
+                file.setFileName(pac_url.path().prepend(QLatin1Char(':')));
             if (file.exists() && file.open(QIODevice::ReadOnly | QIODevice::Text)) {
-                QByteArray ba = file.readAll();
+                const QByteArray ba = file.readAll();
                 commandLine->RemoveSwitch(switches::kProxyPacUrl);
                 commandLine->AppendSwitchASCII(switches::kProxyPacUrl,
                         ba.toBase64().prepend("data:application/x-javascript-config;base64,").toStdString());
@@ -564,16 +595,16 @@ static QStringList parseEnvCommandLine(const QString &cmdLine)
     for (const QChar c : cmdLine) {
         switch (state) {
         case Parse:
-            if (c == '"') {
+            if (c == QLatin1Char('"')) {
                 state = Quoted;
-            } else if (c != ' ' ) {
+            } else if (c != QLatin1Char(' ')) {
                 arg += c;
                 state = Unquoted;
             }
             // skips spaces
             break;
         case Quoted:
-            if (c == '"') {
+            if (c == QLatin1Char('"')) {
                 DCHECK(!arg.isEmpty());
                 state = Unquoted;
             } else {
@@ -582,10 +613,10 @@ static QStringList parseEnvCommandLine(const QString &cmdLine)
             }
             break;
         case Unquoted:
-            if (c == '"') {
+            if (c == QLatin1Char('"')) {
                 // skips quotes
                 state = Quoted;
-            } else if (c == ' ') {
+            } else if (c == QLatin1Char(' ')) {
                 arguments.append(arg);
                 arg.clear();
                 state = Parse;
@@ -657,11 +688,6 @@ void WebEngineContext::destroy()
     if (m_devtoolsServer)
         m_devtoolsServer->stop();
 
-#if QT_CONFIG(webengine_webrtc) && QT_CONFIG(webengine_extensions)
-    if (m_webrtcLogUploader)
-        m_webrtcLogUploader->Shutdown();
-#endif
-
     // Normally the GPU thread is shut down when the GpuProcessHost is destroyed
     // on IO thread (triggered by ~BrowserMainRunner). But by that time the UI
     // task runner is not working anymore so we need to do this earlier.
@@ -696,8 +722,11 @@ void WebEngineContext::destroy()
 
     // Destroy the main runner, this stops main message loop
     m_browserRunner.reset();
-    // gpu thread is no longer around, so no more cotnext is used, remove the helper
+
+#if QT_CONFIG(opengl) && (defined(USE_OZONE) || defined(Q_OS_WIN))
+    // gpu thread is no longer around, so no more context is used, remove the helper
     GLContextHelper::destroy();
+#endif
 
     // These would normally be in the content-runner, but we allocated them separately:
     m_mojoIpcSupport.reset();
@@ -709,10 +738,6 @@ void WebEngineContext::destroy()
 
     // Drop the false reference.
     m_handle->Release();
-
-#if QT_CONFIG(webengine_webrtc) && QT_CONFIG(webengine_extensions)
-    m_webrtcLogUploader.reset();
-#endif
 }
 
 WebEngineContext::~WebEngineContext()
@@ -836,7 +861,7 @@ static void initializeFeatureList(base::CommandLine *commandLine, std::vector<st
 
     commandLine->AppendSwitchASCII(switches::kEnableFeatures, enableFeaturesString);
     commandLine->AppendSwitchASCII(switches::kDisableFeatures, disableFeaturesString);
-    base::FeatureList::InitializeInstance(enableFeaturesString, disableFeaturesString);
+    base::FeatureList::InitInstance(enableFeaturesString, disableFeaturesString);
 }
 
 WebEngineContext::WebEngineContext()
@@ -890,7 +915,7 @@ WebEngineContext::WebEngineContext()
 #endif
     } else {
         parsedCommandLine->AppendSwitch(sandbox::policy::switches::kNoSandbox);
-        qInfo() << "Sandboxing disabled by user.";
+        qInfo("Sandboxing disabled by user.");
     }
 
     // Do not advertise a feature we have removed at compile time
@@ -901,9 +926,10 @@ WebEngineContext::WebEngineContext()
 
     enableFeatures.push_back(features::kNetworkServiceInProcess.name);
     enableFeatures.push_back(features::kTracingServiceInProcess.name);
-
-    // When enabled, event.movement is calculated in blink instead of in browser.
-    disableFeatures.push_back(features::kConsolidatedMovementXY.name);
+#if defined(Q_OS_MACOS) && BUILDFLAG(USE_SCK)
+    // The feature name should match the definition of kScreenCaptureKitMacScreen.
+    enableFeatures.push_back("ScreenCaptureKitMacScreen");
+#endif // defined(Q_OS_MACOS)
 
     // Avoid crashing when websites tries using this feature (since 83)
     disableFeatures.push_back(features::kInstalledApp.name);
@@ -915,6 +941,7 @@ WebEngineContext::WebEngineContext()
 
     // Explicitly tell Chromium about default-on features we do not support
     disableFeatures.push_back(features::kBackgroundFetch.name);
+    parsedCommandLine->AppendSwitchASCII(switches::kDisableBlinkFeatures, "WebOTP");
     disableFeatures.push_back(features::kWebOTP.name);
     disableFeatures.push_back(features::kWebPayments.name);
     disableFeatures.push_back(features::kWebUsb.name);
@@ -927,13 +954,33 @@ WebEngineContext::WebEngineContext()
     }
 
 #if defined(USE_OZONE)
-    if (GPUInfo::instance()->vendor() == GPUInfo::Nvidia) {
+    if (!isGbmSupported()) {
         disableFeatures.push_back(media::kVaapiVideoDecodeLinux.name);
         parsedCommandLine->AppendSwitch(switches::kDisableGpuMemoryBufferVideoFrames);
     }
 
 #if QT_CONFIG(webengine_vulkan)
-    if (QQuickWindow::graphicsApi() == QSGRendererInterface::Vulkan) {
+    if (QQuickWindow::graphicsApi() == QSGRendererInterface::OpenGL) {
+        // FIXME: We assume that ANGLE is explicitly enabled on Linux.
+        //        Make sure to reimplement fallback if ANGLE becomes the default.
+        bool usingANGLE = false;
+        if (parsedCommandLine->HasSwitch(switches::kUseGL))
+            usingANGLE = (parsedCommandLine->GetSwitchValueASCII(switches::kUseGL)
+                          == gl::kGLImplementationANGLEName);
+        if (usingANGLE && !isGbmSupported()) {
+            qWarning("Disable ANGLE because GBM is not supported with the current configuration. "
+                     "Fallback to Vulkan rendering in Chromium.");
+            parsedCommandLine->RemoveSwitch(switches::kUseANGLE);
+            parsedCommandLine->RemoveSwitch(switches::kUseGL);
+            parsedCommandLine->AppendSwitchASCII(switches::kUseGL,
+                                                 gl::kGLImplementationDesktopName);
+            parsedCommandLine->AppendSwitchASCII(switches::kUseVulkan,
+                                                 switches::kVulkanImplementationNameNative);
+            enableFeatures.push_back(features::kVulkan.name);
+        }
+    }
+
+    if (QQuickWindow::graphicsApi() == QSGRendererInterface::Vulkan && usingSupportedSGBackend()) {
         enableFeatures.push_back(features::kVulkan.name);
         parsedCommandLine->AppendSwitchASCII(switches::kUseVulkan,
                                              switches::kVulkanImplementationNameNative);
@@ -949,11 +996,10 @@ WebEngineContext::WebEngineContext()
                     found++;
             }
             if (found != requiredDeviceExtensions.size()) {
-                qWarning().nospace()
-                        << "Vulkan rendering may fail because " << deviceExtensionsVar
-                        << " environment variable is already set but it doesn't contain"
-                        << " some of the required Vulkan device extensions:\n"
-                        << qPrintable(requiredDeviceExtensions.join('\n'));
+                qWarning("Vulkan rendering may fail because %s environment variable is already "
+                         "set but it doesn't contain some of the required Vulkan device "
+                         "extensions:\n%s",
+                         deviceExtensionsVar, requiredDeviceExtensions.join('\n').constData());
             }
         } else {
             qputenv(deviceExtensionsVar, requiredDeviceExtensions.join(';'));
@@ -970,10 +1016,16 @@ WebEngineContext::WebEngineContext()
             parsedCommandLine->AppendSwitchASCII(switches::kUseAdapterLuid, luid.toStdString());
     }
 #endif
+    // We need the FieldTrialList to make sure Chromium features are provided to child processes
+    if (!base::FieldTrialList::GetInstance()) {
+        m_fieldTrialList.reset(new base::FieldTrialList());
+    }
 
     initializeFeatureList(parsedCommandLine, enableFeatures, disableFeatures);
 
+#if QT_CONFIG(opengl) && (defined(USE_OZONE) || defined(Q_OS_WIN))
     GLContextHelper::initialize();
+#endif
 
     // If user requested GL support instead of using Skia rendering to
     // bitmaps, use software rendering via software OpenGL. This might be less
@@ -1107,16 +1159,6 @@ printing::PrintJobManager* WebEngineContext::getPrintJobManager()
 }
 #endif
 
-#if QT_CONFIG(webengine_webrtc) && QT_CONFIG(webengine_extensions)
-WebRtcLogUploader *WebEngineContext::webRtcLogUploader()
-{
-    if (!m_webrtcLogUploader)
-        m_webrtcLogUploader = std::make_unique<WebRtcLogUploader>();
-    return m_webrtcLogUploader.get();
-}
-#endif
-
-
 base::CommandLine *WebEngineContext::initCommandLine(bool &useEmbeddedSwitches,
                                                      bool &enableGLSoftwareRendering)
 {
@@ -1130,7 +1172,7 @@ base::CommandLine *WebEngineContext::initCommandLine(bool &useEmbeddedSwitches,
     }
 
     base::CommandLine *parsedCommandLine = base::CommandLine::ForCurrentProcess();
-    int index = appArgs.indexOf(QRegularExpression(QLatin1String("--webEngineArgs"),
+    int index = appArgs.indexOf(QRegularExpression(u"--webEngineArgs"_s,
                                                    QRegularExpression::CaseInsensitiveOption));
     if (qEnvironmentVariableIsSet(kChromiumFlagsEnv)) {
         appArgs = appArgs.mid(0, 1); // Take application name and drop the rest
@@ -1145,27 +1187,26 @@ base::CommandLine *WebEngineContext::initCommandLine(bool &useEmbeddedSwitches,
         }
     }
 #if defined(QTWEBENGINE_EMBEDDED_SWITCHES)
-    useEmbeddedSwitches = !appArgs.contains(QStringLiteral("--disable-embedded-switches"));
+    useEmbeddedSwitches = !appArgs.contains("--disable-embedded-switches"_L1);
 #else
-    useEmbeddedSwitches = appArgs.contains(QStringLiteral("--enable-embedded-switches"));
+    useEmbeddedSwitches = appArgs.contains("--enable-embedded-switches"_L1);
 #endif
-    enableGLSoftwareRendering =
-            appArgs.removeAll(QStringLiteral("--enable-webgl-software-rendering"));
-    appArgs.removeAll(QStringLiteral("--disable-embedded-switches"));
-    appArgs.removeAll(QStringLiteral("--enable-embedded-switches"));
+    enableGLSoftwareRendering = appArgs.removeAll("--enable-webgl-software-rendering"_L1);
+    appArgs.removeAll("--disable-embedded-switches"_L1);
+    appArgs.removeAll("--enable-embedded-switches"_L1);
 
     bool isRemoteDebugPort =
             (-1
-             != appArgs.indexOf(QRegularExpression(QStringLiteral("--remote-debugging-port=.*"),
+             != appArgs.indexOf(QRegularExpression(u"--remote-debugging-port=.*"_s,
                                                    QRegularExpression::CaseInsensitiveOption)))
             || !qEnvironmentVariable("QTWEBENGINE_REMOTE_DEBUGGING").isEmpty();
     bool isRemoteAllowOrigins =
             (-1
-             != appArgs.indexOf(QRegularExpression(QStringLiteral("--remote-allow-origins=.*"),
+             != appArgs.indexOf(QRegularExpression(u"--remote-allow-origins=.*"_s,
                                                    QRegularExpression::CaseInsensitiveOption)));
 
     if (isRemoteDebugPort && !isRemoteAllowOrigins) {
-        appArgs.append(QStringLiteral("--remote-allow-origins=*"));
+        appArgs.append(u"--remote-allow-origins=*"_s);
         qWarning("Added {--remote-allow-origins=*} to command-line arguments "
                  "to avoid web socket connection errors during remote debugging.");
     }
@@ -1189,6 +1230,46 @@ bool WebEngineContext::closingDown()
     return m_closingDown;
 }
 
+#if defined(USE_OZONE)
+bool WebEngineContext::isGbmSupported()
+{
+    static bool supported = []() {
+        const static char kForceGbmEnv[] = "QTWEBENGINE_FORCE_USE_GBM";
+        if (Q_UNLIKELY(qEnvironmentVariableIsSet(kForceGbmEnv))) {
+            qWarning("%s environment variable is set and it is for debugging purposes only.",
+                     kForceGbmEnv);
+            bool ok;
+            int forceGbm = qEnvironmentVariableIntValue(kForceGbmEnv, &ok);
+            if (ok) {
+                qWarning("GBM support is force %s.", forceGbm != 0 ? "enabled" : "disabled");
+                return (forceGbm != 0);
+            }
+
+            qWarning("Ignoring invalid value of %s and do not force GBM. "
+                     "Use 0 to force disable or 1 to force enable.",
+                     kForceGbmEnv);
+        }
+
+        if (GPUInfo::instance()->vendor() == GPUInfo::Nvidia) {
+            // FIXME: This disables GBM for Nvidia. Remove this when Nvidia fixes its GBM support.
+            //
+            // "Buffer allocation and submission to DRM KMS using gbm is not currently supported."
+            // See: https://download.nvidia.com/XFree86/Linux-x86_64/570.86.16/README/kms.html"
+            //
+            // Chromium uses GBM to allocate scanout buffers. Scanout requires DRM KMS. If KMS is
+            // enabled, gbm_device and gbm_buffer are created without any issues but rendering to
+            // the buffer will malfunction. It is not known how to detect this problem before
+            // rendering so we just disable GBM for Nvidia.
+            return false;
+        }
+
+        return true;
+    }();
+
+    return supported;
+}
+#endif
+
 void WebEngineContext::registerMainThreadFactories()
 {
     content::UtilityProcessHost::RegisterUtilityMainThreadFactory(content::CreateInProcessUtilityThread);
@@ -1216,7 +1297,7 @@ const char *qWebEngineChromiumVersion() noexcept
 
 const char *qWebEngineChromiumSecurityPatchVersion() noexcept
 {
-    return "122.0.6261.128"; // FIXME: Remember to update
+    return "137.0.7151.68"; // FIXME: Remember to update
 }
 
 QT_END_NAMESPACE
diff --git a/src/core/web_engine_context.h b/src/core/web_engine_context.h
index 50b080db100..606c41ef238 100644
--- a/src/core/web_engine_context.h
+++ b/src/core/web_engine_context.h
@@ -16,6 +16,7 @@
 namespace base {
 class RunLoop;
 class CommandLine;
+class FieldTrialList;
 }
 
 namespace content {
@@ -47,7 +48,6 @@ struct SandboxInterfaceInfo;
 #endif
 
 QT_FORWARD_DECLARE_CLASS(QObject)
-class WebRtcLogUploader;
 
 namespace QtWebEngineCore {
 
@@ -67,15 +67,15 @@ class WebEngineContext : public base::RefCounted<WebEngineContext> {
     static ProxyAuthentication qProxyNetworkAuthentication(QString host, int port);
     static void flushMessages();
     static bool closingDown();
+#if defined(USE_OZONE)
+    static bool isGbmSupported();
+#endif
     ProfileAdapter *createDefaultProfileAdapter();
     ProfileAdapter *defaultProfileAdapter();
 
     QObject *globalQObject();
 #if QT_CONFIG(webengine_printing_and_pdf)
     printing::PrintJobManager* getPrintJobManager();
-#endif
-#if QT_CONFIG(webengine_webrtc) && QT_CONFIG(webengine_extensions)
-    WebRtcLogUploader *webRtcLogUploader();
 #endif
     void destroyProfileAdapter();
     void addProfileAdapter(ProfileAdapter *profileAdapter);
@@ -102,15 +102,13 @@ class WebEngineContext : public base::RefCounted<WebEngineContext> {
     std::unique_ptr<ProfileAdapter> m_defaultProfileAdapter;
     std::unique_ptr<DevToolsServerQt> m_devtoolsServer;
     QList<ProfileAdapter*> m_profileAdapters;
+    std::unique_ptr<base::FieldTrialList> m_fieldTrialList;
 #if QT_CONFIG(accessibility)
     std::unique_ptr<AccessibilityActivationObserver> m_accessibilityActivationObserver;
 #endif
 
 #if QT_CONFIG(webengine_printing_and_pdf)
     std::unique_ptr<printing::PrintJobManager> m_printJobManager;
-#endif
-#if QT_CONFIG(webengine_webrtc) && QT_CONFIG(webengine_extensions)
-    std::unique_ptr<WebRtcLogUploader> m_webrtcLogUploader;
 #endif
     static scoped_refptr<QtWebEngineCore::WebEngineContext> m_handle;
     static bool m_destroyed;
diff --git a/src/core/web_engine_library_info.cpp b/src/core/web_engine_library_info.cpp
index e7115389962..cd132b2ad11 100644
--- a/src/core/web_engine_library_info.cpp
+++ b/src/core/web_engine_library_info.cpp
@@ -34,6 +34,7 @@
 #error "No name defined for QtWebEngine's process"
 #endif
 
+using namespace Qt::StringLiterals;
 using namespace QtWebEngineCore;
 
 Q_LOGGING_CATEGORY(webEngineLibraryInfoLog, "qt.webengine.libraryinfo")
@@ -41,7 +42,8 @@ Q_LOGGING_CATEGORY(webEngineLibraryInfoLog, "qt.webengine.libraryinfo")
 namespace {
 
 QString fallbackDir() {
-    static QString directory = QDir::homePath() % QLatin1String("/.") % QCoreApplication::applicationName();
+    static const QString directory =
+            QDir::homePath() % "/."_L1 % QCoreApplication::applicationName();
     return directory;
 }
 
@@ -57,8 +59,8 @@ static QString getBundlePath(CFBundleRef frameworkBundle)
     // The following is a fix for QtWebEngineProcess crashes on OS X 10.7 and before.
     // We use it for the other OS X versions as well to make sure it works and because
     // the directory structure should be the same.
-    if (qApp->applicationName() == QLatin1String(qWebEngineProcessName())) {
-        path = QDir::cleanPath(qApp->applicationDirPath() % QLatin1String("/../../../.."));
+    if (qApp->applicationName() == QLatin1StringView(qWebEngineProcessName())) {
+        path = QDir::cleanPath(qApp->applicationDirPath() % "/../../../.."_L1);
     } else if (frameworkBundle) {
         CFURLRef bundleUrl = CFBundleCopyBundleURL(frameworkBundle);
         CFStringRef bundlePath = CFURLCopyFileSystemPath(bundleUrl, kCFURLPOSIXPathStyle);
@@ -75,8 +77,8 @@ static QString getResourcesPath(CFBundleRef frameworkBundle)
     // The following is a fix for QtWebEngineProcess crashes on OS X 10.7 and before.
     // We use it for the other OS X versions as well to make sure it works and because
     // the directory structure should be the same.
-    if (qApp->applicationName() == QLatin1String(qWebEngineProcessName())) {
-        path = getBundlePath(frameworkBundle) % QLatin1String("/Resources");
+    if (qApp->applicationName() == QLatin1StringView(qWebEngineProcessName())) {
+        path = getBundlePath(frameworkBundle) % "/Resources"_L1;
     } else if (frameworkBundle) {
         CFURLRef resourcesRelativeUrl = CFBundleCopyResourcesDirectoryURL(frameworkBundle);
         CFStringRef resourcesRelativePath = CFURLCopyFileSystemPath(resourcesRelativeUrl, kCFURLPOSIXPathStyle);
@@ -121,20 +123,18 @@ QString subProcessPath()
     static QString processPath;
     if (processPath.isEmpty()) {
 #if defined(Q_OS_WIN)
-        const QString processBinary = QLatin1String(qWebEngineProcessName()) % QLatin1String(".exe");
+        const QString processBinary = QLatin1StringView(qWebEngineProcessName()) % ".exe"_L1;
 #else
-        const QString processBinary = QLatin1String(qWebEngineProcessName());
+        const auto processBinary = QLatin1StringView(qWebEngineProcessName());
 #endif
 
         QStringList candidatePaths;
-        const QString fromEnv = qEnvironmentVariable("QTWEBENGINEPROCESS_PATH");
-        if (!fromEnv.isEmpty()) {
-            // Only search in QTWEBENGINEPROCESS_PATH if set
-            candidatePaths << fromEnv;
-        } else {
+        bool includeOverrideMessage = false;
+        if (QString fromEnv = qEnvironmentVariable("QTWEBENGINEPROCESS_PATH"); fromEnv.isEmpty()) {
+            includeOverrideMessage = true;
 #if defined(Q_OS_DARWIN) && defined(QT_MAC_FRAMEWORK_BUILD)
-            candidatePaths << getBundlePath(frameworkBundle()) % QStringLiteral("/Helpers/")
-                            % qWebEngineProcessName() % QStringLiteral(".app/Contents/MacOS/")
+            candidatePaths << getBundlePath(frameworkBundle()) % "/Helpers/"_L1
+                            % qWebEngineProcessName() % ".app/Contents/MacOS/"_L1
                             % qWebEngineProcessName();
 #else
             candidatePaths << QLibraryInfo::path(QLibraryInfo::LibraryExecutablesPath)
@@ -144,29 +144,30 @@ QString subProcessPath()
 #endif
             candidatePaths << QCoreApplication::applicationDirPath()
                               % QLatin1Char('/') % processBinary;
+        } else {
+            // Only search in QTWEBENGINEPROCESS_PATH if set
+            candidatePaths.append(std::move(fromEnv));
         }
 
         for (const QString &candidate : std::as_const(candidatePaths)) {
             if (QFileInfo::exists(candidate)) {
                 processPath = candidate;
-                qCDebug(webEngineLibraryInfoLog, "Qt WebEngine process path: %s",
-                        qPrintable(candidate));
+                qCDebug(webEngineLibraryInfoLog, "Qt WebEngine process path: %ls",
+                        qUtf16Printable(candidate));
                 break;
             }
         }
         if (processPath.isEmpty()) {
-            QStringList errorMessage;
-            errorMessage.append(
-                    QStringLiteral("The following paths were searched for Qt WebEngine Process:"));
+            QString errorMessage;
+            errorMessage += "The following paths were searched for Qt WebEngine Process:\n"_L1;
             for (const QString &candidate : std::as_const(candidatePaths))
-                errorMessage.append(QStringLiteral("  ") % candidate);
-            errorMessage.append(QStringLiteral("but could not find it."));
-            if (fromEnv.isEmpty()) {
-                errorMessage.append(
-                        QStringLiteral("You may override the default search path by using "
-                                       "QTWEBENGINEPROCESS_PATH environment variable."));
+                errorMessage += "  "_L1 + candidate + u'\n';
+            errorMessage += "but could not find it.\n"_L1;
+            if (includeOverrideMessage) {
+                errorMessage += "You may override the default search path by using "
+                                "QTWEBENGINEPROCESS_PATH environment variable.\n"_L1;
             }
-            qFatal("%s", qPrintable(errorMessage.join('\n')));
+            qFatal("%ls", qUtf16Printable(errorMessage));
         }
 
 #if defined(Q_OS_WIN)
@@ -189,51 +190,44 @@ QString localesPath()
     if (potentialLocalesPath.isEmpty()) {
         QStringList candidatePaths;
         const QString translationPakFilename =
-#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
-                QLatin1String(WebEngineLibraryInfo::getResolvedLocale() + ".pak");
-#else
-                QLatin1String((WebEngineLibraryInfo::getResolvedLocale() + ".pak").c_str());
-#endif
-        const QString fromEnv = qEnvironmentVariable("QTWEBENGINE_LOCALES_PATH");
-        if (!fromEnv.isEmpty()) {
-            // Only search in QTWEBENGINE_LOCALES_PATH if set
-            candidatePaths << fromEnv;
-        } else {
+                QLatin1StringView(WebEngineLibraryInfo::getResolvedLocale()) % ".pak"_L1;
+        bool includeOverrideMessage = false;
+        if (QString fromEnv = qEnvironmentVariable("QTWEBENGINE_LOCALES_PATH"); fromEnv.isEmpty()) {
+            includeOverrideMessage = true;
 #if defined(Q_OS_DARWIN) && defined(QT_MAC_FRAMEWORK_BUILD)
             candidatePaths << getResourcesPath(frameworkBundle()) % QDir::separator()
-                            % QLatin1String("qtwebengine_locales");
+                            % "qtwebengine_locales"_L1;
 #endif
             candidatePaths << QLibraryInfo::path(QLibraryInfo::TranslationsPath) % QDir::separator()
-                            % QLatin1String("qtwebengine_locales");
+                            % "qtwebengine_locales"_L1;
             candidatePaths << fallbackDir();
+        } else {
+            // Only search in QTWEBENGINE_LOCALES_PATH if set
+            candidatePaths.append(std::move(fromEnv));
         }
 
         for (const QString &candidate : std::as_const(candidatePaths)) {
             if (QFileInfo::exists(candidate % QDir::separator() % translationPakFilename)) {
                 potentialLocalesPath = candidate;
-                qCDebug(webEngineLibraryInfoLog, "Qt WebEngine locales path: %s",
-                        qPrintable(candidate));
+                qCDebug(webEngineLibraryInfoLog, "Qt WebEngine locales path: %ls",
+                        qUtf16Printable(candidate));
                 break;
             }
         }
 
         if (potentialLocalesPath.isEmpty()) {
-            QStringList warningMessage;
-            warningMessage.append(
-                    QStringLiteral("The following paths were searched for Qt WebEngine locales:"));
+            QString warningMessage;
+            warningMessage += "The following paths were searched for Qt WebEngine locales:\n"_L1;
             for (const QString &candidate : std::as_const(candidatePaths))
-                warningMessage.append(QStringLiteral("  ") % candidate);
-            warningMessage.append(
-                    QStringLiteral(
-                            "but could not find the translation file for the current locale: ")
-                    % translationPakFilename);
-            if (fromEnv.isEmpty()) {
-                warningMessage.append(
-                        QStringLiteral("You may override the default search paths by using "
-                                       "QTWEBENGINE_LOCALES_PATH environment variable."));
+                warningMessage += "  "_L1 % candidate + u'\n';
+            warningMessage += "but could not find the translation file for the current locale: "_L1
+                    + translationPakFilename + u'\n';
+            if (includeOverrideMessage) {
+                warningMessage += "You may override the default search paths by using "
+                                  "QTWEBENGINE_LOCALES_PATH environment variable.\n"_L1;
             }
-            warningMessage.append(QStringLiteral("Translations WILL NOT be correct."));
-            qWarning("%s", qPrintable(warningMessage.join('\n')));
+            warningMessage += "Translations WILL NOT be correct.\n"_L1;
+            qWarning("%ls", qUtf16Printable(warningMessage));
         }
     }
 
@@ -241,57 +235,70 @@ QString localesPath()
 }
 
 #if QT_CONFIG(webengine_spellchecker)
-QString dictionariesPath()
+QString dictionariesPath(bool showWarnings)
 {
     static QString potentialDictionariesPath;
+    static QString warningMessage;
     static bool initialized = false;
     QStringList candidatePaths;
     if (!initialized) {
         initialized = true;
 
-        const QString fromEnv = qEnvironmentVariable("QTWEBENGINE_DICTIONARIES_PATH");
-        if (!fromEnv.isEmpty()) {
-            // Only search in QTWEBENGINE_DICTIONARIES_PATH if set
-            candidatePaths << fromEnv;
-        } else {
+        bool includeOverrideMessage = false;
+        if (QString fromEnv = qEnvironmentVariable("QTWEBENGINE_DICTIONARIES_PATH");
+            fromEnv.isEmpty()) {
+            includeOverrideMessage = true;
             // First try to find dictionaries near the application.
 #ifdef Q_OS_DARWIN
             QString resourcesDictionariesPath = getMainApplicationResourcesPath()
-                    % QDir::separator() % QLatin1String("qtwebengine_dictionaries");
-            candidatePaths << resourcesDictionariesPath;
+                    % QDir::separator() % "qtwebengine_dictionaries"_L1;
+            candidatePaths.append(std::move(resourcesDictionariesPath));
 #endif
             QString applicationDictionariesPath = QCoreApplication::applicationDirPath()
-                    % QDir::separator() % QLatin1String("qtwebengine_dictionaries");
-            candidatePaths << applicationDictionariesPath;
+                    % QDir::separator() % "qtwebengine_dictionaries"_L1;
+            candidatePaths.append(std::move(applicationDictionariesPath));
 
             // Then try to find dictionaries near the installed library.
 #if defined(Q_OS_DARWIN) && defined(QT_MAC_FRAMEWORK_BUILD)
-            QString frameworkDictionariesPath = getResourcesPath(frameworkBundle())
-                    % QLatin1String("/qtwebengine_dictionaries");
-            candidatePaths << frameworkDictionariesPath;
+            QString frameworkDictionariesPath =
+                    getResourcesPath(frameworkBundle()) % "/qtwebengine_dictionaries"_L1;
+            candidatePaths.append(std::move(frameworkDictionariesPath));
 #endif
 
             QString libraryDictionariesPath = QLibraryInfo::path(QLibraryInfo::DataPath)
-                    % QDir::separator() % QLatin1String("qtwebengine_dictionaries");
-            candidatePaths << libraryDictionariesPath;
+                    % QDir::separator() % "qtwebengine_dictionaries"_L1;
+            candidatePaths.append(std::move(libraryDictionariesPath));
+        } else {
+            // Only search in QTWEBENGINE_DICTIONARIES_PATH if set
+            candidatePaths.append(std::move(fromEnv));
         }
 
         for (const QString &candidate : std::as_const(candidatePaths)) {
             if (QFileInfo::exists(candidate)) {
                 potentialDictionariesPath = candidate;
-                qCDebug(webEngineLibraryInfoLog, "Qt WebEngine dictionaries path: %s",
-                        qPrintable(candidate));
+                qCDebug(webEngineLibraryInfoLog, "Qt WebEngine dictionaries path: %ls",
+                        qUtf16Printable(candidate));
                 break;
             }
         }
-
         if (potentialDictionariesPath.isEmpty()) {
-            // return path for error message
-            potentialDictionariesPath = QCoreApplication::applicationDirPath() % QDir::separator()
-                    % QLatin1String("qtwebengine_dictionaries");
+            warningMessage +=
+                    "The following paths were searched for Qt WebEngine dictionaries:\n"_L1;
+            for (const QString &candidate : std::as_const(candidatePaths))
+                warningMessage += "  "_L1 + candidate + u'\n';
+            warningMessage += "but could not find it.\n"_L1;
+            if (includeOverrideMessage) {
+                warningMessage += "You may override the default search path by using "
+                                  "QTWEBENGINE_DICTIONARIES_PATH environment variable.\n"_L1;
+            }
+            warningMessage += "Spellchecking can not be enabled.\n"_L1;
         }
     }
 
+    if (showWarnings && !warningMessage.isEmpty()) {
+        qWarning("%ls", qUtf16Printable(warningMessage));
+    }
+
     return potentialDictionariesPath;
 }
 #endif // QT_CONFIG(webengine_spellchecker)
@@ -301,44 +308,48 @@ QString resourcesPath()
     static QString potentialResourcesPath;
     if (potentialResourcesPath.isEmpty()) {
         QStringList candidatePaths;
-        const QString resourcesPakFilename = QLatin1String("qtwebengine_resources.pak");
-        const QString fromEnv = qEnvironmentVariable("QTWEBENGINE_RESOURCES_PATH");
-        if (!fromEnv.isEmpty()) {
-            // Only search in QTWEBENGINE_RESOURCES_PATH if set
-            candidatePaths << fromEnv;
-        } else {
+#if !defined(QT_USE_DEBUG_RESOURCES)
+        const auto resourcesPakFilename = "qtwebengine_resources.pak"_L1;
+#else
+        const auto resourcesPakFilename = "qtwebengine_resources.debug.pak"_L1;
+#endif
+        bool includeOverrideMessage = false;
+        if (QString fromEnv = qEnvironmentVariable("QTWEBENGINE_RESOURCES_PATH");
+            fromEnv.isEmpty()) {
+            includeOverrideMessage = true;
 #if defined(Q_OS_DARWIN) && defined(QT_MAC_FRAMEWORK_BUILD)
             candidatePaths << getResourcesPath(frameworkBundle());
 #endif
             candidatePaths << QLibraryInfo::path(QLibraryInfo::DataPath) % QDir::separator()
-                            % QLatin1String("resources");
+                            % "resources"_L1;
             candidatePaths << QLibraryInfo::path(QLibraryInfo::DataPath);
             candidatePaths << QCoreApplication::applicationDirPath();
             candidatePaths << fallbackDir();
+        } else {
+            // Only search in QTWEBENGINE_RESOURCES_PATH if set
+            candidatePaths.append(std::move(fromEnv));
         }
 
         for (const QString &candidate : std::as_const(candidatePaths)) {
             if (QFileInfo::exists(candidate % QDir::separator() % resourcesPakFilename)) {
                 potentialResourcesPath = candidate;
-                qCDebug(webEngineLibraryInfoLog, "Qt WebEngine resources path: %s",
-                        qPrintable(candidate));
+                qCDebug(webEngineLibraryInfoLog, "Qt WebEngine resources path: %ls",
+                        qUtf16Printable(candidate));
                 break;
             }
         }
 
         if (potentialResourcesPath.isEmpty()) {
-            QStringList errorMessage;
-            errorMessage.append(QStringLiteral(
-                    "The following paths were searched for Qt WebEngine resources:"));
+            QString errorMessage;
+            errorMessage += "The following paths were searched for Qt WebEngine resources:\n"_L1;
             for (const QString &candidate : std::as_const(candidatePaths))
-                errorMessage.append(QStringLiteral("  ") % candidate);
-            errorMessage.append(QStringLiteral("but could not find any."));
-            if (fromEnv.isEmpty()) {
-                errorMessage.append(
-                        QStringLiteral("You may override the default search paths by using "
-                                       "QTWEBENGINE_RESOURCES_PATH environment variable."));
+                errorMessage += "  "_L1 + candidate + u'\n';
+            errorMessage += "but could not find any.\n"_L1;
+            if (includeOverrideMessage) {
+                errorMessage += "You may override the default search paths by using "
+                                "QTWEBENGINE_RESOURCES_PATH environment variable.\n"_L1;
             }
-            qFatal("%s", qPrintable(errorMessage.join('\n')));
+            qFatal("%ls", qUtf16Printable(errorMessage));
         }
     }
 
@@ -346,18 +357,29 @@ QString resourcesPath()
 }
 } // namespace
 
-base::FilePath WebEngineLibraryInfo::getPath(int key)
+base::FilePath WebEngineLibraryInfo::getPath(int key, bool showWarnings)
 {
     QString directory;
     switch (key) {
+#if !defined(QT_USE_DEBUG_RESOURCES)
     case QT_RESOURCES_PAK:
-        return toFilePath(resourcesPath() % QLatin1String("/qtwebengine_resources.pak"));
+        return toFilePath(resourcesPath() % "/qtwebengine_resources.pak"_L1);
     case QT_RESOURCES_100P_PAK:
-        return toFilePath(resourcesPath() % QLatin1String("/qtwebengine_resources_100p.pak"));
+        return toFilePath(resourcesPath() % "/qtwebengine_resources_100p.pak"_L1);
     case QT_RESOURCES_200P_PAK:
-        return toFilePath(resourcesPath() % QLatin1String("/qtwebengine_resources_200p.pak"));
+        return toFilePath(resourcesPath() % "/qtwebengine_resources_200p.pak"_L1);
     case QT_RESOURCES_DEVTOOLS_PAK:
-        return toFilePath(resourcesPath() % QLatin1String("/qtwebengine_devtools_resources.pak"));
+        return toFilePath(resourcesPath() % "/qtwebengine_devtools_resources.pak"_L1);
+#else
+    case QT_RESOURCES_PAK:
+        return toFilePath(resourcesPath() % "/qtwebengine_resources.debug.pak"_L1);
+    case QT_RESOURCES_100P_PAK:
+        return toFilePath(resourcesPath() % "/qtwebengine_resources_100p.debug.pak"_L1);
+    case QT_RESOURCES_200P_PAK:
+        return toFilePath(resourcesPath() % "/qtwebengine_resources_200p.debug.pak"_L1);
+    case QT_RESOURCES_DEVTOOLS_PAK:
+        return toFilePath(resourcesPath() % "/qtwebengine_devtools_resources.debug.pak"_L1);
+#endif
 #if defined(Q_OS_DARWIN) && defined(QT_MAC_FRAMEWORK_BUILD)
     case QT_FRAMEWORK_BUNDLE:
         return toFilePath(getBundlePath(frameworkBundle()));
@@ -382,7 +404,7 @@ base::FilePath WebEngineLibraryInfo::getPath(int key)
         return toFilePath(localesPath());
 #if QT_CONFIG(webengine_spellchecker)
     case base::DIR_APP_DICTIONARIES:
-        return toFilePath(dictionariesPath());
+        return toFilePath(dictionariesPath(showWarnings));
 #endif
     case base::DIR_ASSETS:
         return toFilePath(resourcesPath());
@@ -429,7 +451,7 @@ std::string WebEngineLibraryInfo::getApplicationLocale()
 bool WebEngineLibraryInfo::isRemoteDrivePath(const QString &path)
 {
     WCHAR wDriveLetter[4] = { 0 };
-    swprintf(wDriveLetter, L"%S", path.mid(0, 3).toStdString().c_str());
+    swprintf(wDriveLetter, 4, L"%S", path.mid(0, 3).toStdString().c_str());
     return GetDriveType(wDriveLetter) == DRIVE_REMOTE;
 }
 
diff --git a/src/core/web_engine_library_info.h b/src/core/web_engine_library_info.h
index b7503f2df0a..a675172af48 100644
--- a/src/core/web_engine_library_info.h
+++ b/src/core/web_engine_library_info.h
@@ -18,7 +18,7 @@ enum {
 
 class WebEngineLibraryInfo {
 public:
-    static base::FilePath getPath(int key);
+    static base::FilePath getPath(int key, bool showWarnings = false);
     // Called by localized_error in our custom chrome layer
     static std::u16string getApplicationName();
     static std::string getResolvedLocale();
diff --git a/src/core/web_engine_settings.cpp b/src/core/web_engine_settings.cpp
index e302998f085..6e159658ff9 100644
--- a/src/core/web_engine_settings.cpp
+++ b/src/core/web_engine_settings.cpp
@@ -25,6 +25,8 @@
 #include <QFont>
 #include <QTimer>
 
+using namespace Qt::StringLiterals;
+
 namespace QtWebEngineCore {
 
 QHash<QWebEngineSettings::WebAttribute, bool> WebEngineSettings::s_defaultAttributes;
@@ -60,12 +62,14 @@ blink::mojom::ImageAnimationPolicy
 toBlinkImageAnimationPolicy(QWebEngineSettings::ImageAnimationPolicy policy)
 {
     switch (policy) {
-    case QWebEngineSettings::AllowImageAnimation:
+    case QWebEngineSettings::ImageAnimationPolicy::Allow:
         return blink::mojom::ImageAnimationPolicy::kImageAnimationPolicyAllowed;
-    case QWebEngineSettings::AnimateImageOnce:
+    case QWebEngineSettings::ImageAnimationPolicy::AnimateOnce:
         return blink::mojom::ImageAnimationPolicy::kImageAnimationPolicyAnimateOnce;
-    case QWebEngineSettings::DisallowImageAnimation:
+    case QWebEngineSettings::ImageAnimationPolicy::Disallow:
         return blink::mojom::ImageAnimationPolicy::kImageAnimationPolicyNoAnimation;
+    case QWebEngineSettings::ImageAnimationPolicy::Inherited:
+        break;
     }
     return blink::mojom::ImageAnimationPolicy::kImageAnimationPolicyAllowed;
 }
@@ -74,7 +78,7 @@ WebEngineSettings::WebEngineSettings(WebEngineSettings *_parentSettings)
     : m_adapter(nullptr)
     , parentSettings(_parentSettings)
     , m_unknownUrlSchemePolicy(QWebEngineSettings::InheritedUnknownUrlSchemePolicy)
-    , m_imageAnimationPolicy(QWebEngineSettings::InheritedImageAnimationPolicy)
+    , m_imageAnimationPolicy(QWebEngineSettings::ImageAnimationPolicy::Inherited)
 {
     if (parentSettings)
         parentSettings->childSettings.insert(this);
@@ -217,13 +221,13 @@ void WebEngineSettings::setImageAnimationPolicy(QWebEngineSettings::ImageAnimati
 
 QWebEngineSettings::ImageAnimationPolicy WebEngineSettings::imageAnimationPolicy() const
 {
-    if (m_imageAnimationPolicy != QWebEngineSettings::InheritedImageAnimationPolicy)
+    if (m_imageAnimationPolicy != QWebEngineSettings::ImageAnimationPolicy::Inherited)
         return m_imageAnimationPolicy;
 
     if (parentSettings)
         return parentSettings->imageAnimationPolicy();
 
-    return QWebEngineSettings::AllowImageAnimation;
+    return QWebEngineSettings::ImageAnimationPolicy::Allow;
 }
 
 QWebEngineSettings::UnknownUrlSchemePolicy WebEngineSettings::unknownUrlSchemePolicy() const
@@ -331,9 +335,9 @@ void WebEngineSettings::initDefaults()
         s_defaultFontSizes.insert(QWebEngineSettings::DefaultFontSize, 16);
     }
 
-    m_defaultEncoding = QStringLiteral("ISO-8859-1");
+    m_defaultEncoding = u"ISO-8859-1"_s;
     m_unknownUrlSchemePolicy = QWebEngineSettings::InheritedUnknownUrlSchemePolicy;
-    m_imageAnimationPolicy = QWebEngineSettings::InheritedImageAnimationPolicy;
+    m_imageAnimationPolicy = QWebEngineSettings::ImageAnimationPolicy::Inherited;
 }
 
 void WebEngineSettings::scheduleApply()
diff --git a/src/core/web_event_factory.cpp b/src/core/web_event_factory.cpp
index 617eea2d0be..52100048da0 100644
--- a/src/core/web_event_factory.cpp
+++ b/src/core/web_event_factory.cpp
@@ -38,6 +38,7 @@
 #include "native_web_keyboard_event_qt.h"
 #include "render_widget_host_view_qt_delegate.h"
 
+#include <QtCore/private/qstringiterator_p.h>
 #include <QtGui/private/qtgui-config_p.h>
 
 #include <QCoreApplication>
@@ -51,6 +52,8 @@
 #endif
 #include <QWheelEvent>
 
+using namespace Qt::StringLiterals;
+
 namespace QtWebEngineCore {
 
 using namespace blink;
@@ -59,26 +62,26 @@ enum class KeyboardDriver { Unknown, Windows, Cocoa, Xkb, Evdev };
 
 static KeyboardDriver keyboardDriverImpl()
 {
-    QString platformName = QGuiApplication::platformName();
+    const QString platformName = QGuiApplication::platformName();
 
-    if (platformName == QLatin1String("windows"))
+    if (platformName == "windows"_L1)
         return KeyboardDriver::Windows;
 
-    if (platformName == QLatin1String("cocoa"))
+    if (platformName == "cocoa"_L1)
         return KeyboardDriver::Cocoa;
 
-    if (platformName == QLatin1String("xcb") || platformName == QLatin1String("wayland"))
+    if (platformName == "xcb"_L1 || platformName == "wayland"_L1)
         return KeyboardDriver::Xkb;
 
 #if QT_CONFIG(libinput)
     // Based on QEglFSIntegration::createInputHandlers and QLibInputKeyboard::processKey.
-    if (platformName == QLatin1String("eglfs") && !qEnvironmentVariableIntValue("QT_QPA_EGLFS_NO_LIBINPUT"))
+    if (platformName == "eglfs"_L1 && !qEnvironmentVariableIntValue("QT_QPA_EGLFS_NO_LIBINPUT"))
         return KeyboardDriver::Xkb;
 #endif
 
 #if QT_CONFIG(evdev)
     // Based on QEglFSIntegration::createInputHandlers.
-    if (platformName == QLatin1String("eglfs"))
+    if (platformName == "eglfs"_L1)
         return KeyboardDriver::Evdev;
 #endif
 
@@ -1249,9 +1252,9 @@ static WebInputEvent::Modifiers lockKeyModifiers(const quint32 nativeModifiers)
 {
     unsigned result = 0;
     if (keyboardDriver() == KeyboardDriver::Xkb) {
-        if (nativeModifiers & 0x42) /* Caps_Lock */
+        if (nativeModifiers & 0x2) /* XCB_MOD_MASK_LOCK */
             result |= WebInputEvent::kCapsLockOn;
-        if (nativeModifiers & 0x4d) /* Num_Lock */
+        if (nativeModifiers & 0x10) /* XCB_MOD_MASK_2 */
             result |= WebInputEvent::kNumLockOn;
     } else if (keyboardDriver() == KeyboardDriver::Windows) {
         if (nativeModifiers & 0x100) /* CapsLock */
@@ -1505,6 +1508,7 @@ WebGestureEvent WebEventFactory::toWebGestureEvent(QNativeGestureEvent *ev)
     case Qt::ZoomNativeGesture:
         webKitEvent.SetType(WebInputEvent::Type::kGesturePinchUpdate);
         webKitEvent.data.pinch_update.scale = static_cast<float>(ev->value() + 1.0);
+        webKitEvent.SetNeedsWheelEvent(true);
         break;
     case Qt::SmartZoomNativeGesture:
         webKitEvent.SetType(WebInputEvent::Type::kGestureDoubleTap);
@@ -1632,8 +1636,8 @@ bool WebEventFactory::coalesceWebWheelEvent(blink::WebMouseWheelEvent &webEvent,
     webEvent.SetPositionInScreen(static_cast<float>(ev->globalPosition().x()),
                                  static_cast<float>(ev->globalPosition().y()));
 
-    webEvent.wheel_ticks_x = ev->angleDelta().x() / static_cast<float>(QWheelEvent::DefaultDeltasPerStep);
-    webEvent.wheel_ticks_y = ev->angleDelta().y() / static_cast<float>(QWheelEvent::DefaultDeltasPerStep);
+    webEvent.wheel_ticks_x += ev->angleDelta().x() / static_cast<float>(QWheelEvent::DefaultDeltasPerStep);
+    webEvent.wheel_ticks_y += ev->angleDelta().y() / static_cast<float>(QWheelEvent::DefaultDeltasPerStep);
     setBlinkWheelEventDelta(webEvent);
 
     return true;
@@ -1672,16 +1676,17 @@ content::NativeWebKeyboardEvent WebEventFactory::toWebKeyboardEvent(QKeyEvent *e
     int qtKey = qtKeyForKeyEvent(ev);
     Qt::KeyboardModifiers qtModifiers =
             isBackTabWithoutModifier ? Qt::ShiftModifier : qtModifiersForEvent(ev);
-    QString qtText = qtTextForKeyEvent(ev, qtKey, qtModifiers);
+    const QString qtText = qtTextForKeyEvent(ev, qtKey, qtModifiers);
 
     webKitEvent.native_key_code = nativeKeyCodeForKeyEvent(ev);
     webKitEvent.windows_key_code = windowsKeyCodeForQtKey(qtKey, qtModifiers & Qt::KeypadModifier);
 
     if (qtKey >= Qt::Key_Escape)
         webKitEvent.dom_key = domKeyForQtKey(qtKey);
-    else if (!qtText.isEmpty())
-        webKitEvent.dom_key = ui::DomKey::FromCharacter(qtText.toUcs4().first());
-    else {
+    else if (!qtText.isEmpty()) {
+        QStringIterator it(qtText);
+        webKitEvent.dom_key = ui::DomKey::FromCharacter(it.next());
+    } else {
         QChar ch(qtKey);
         if (!(qtModifiers & Qt::ShiftModifier)) // No way to check for caps lock
             ch = ch.toLower();
diff --git a/src/gn/CMakeLists.txt b/src/gn/CMakeLists.txt
index 0fe3e4e05b8..fdf0b11a1ac 100644
--- a/src/gn/CMakeLists.txt
+++ b/src/gn/CMakeLists.txt
@@ -55,20 +55,25 @@ else()
     message(FATAL_ERROR "Unsupported gn platform !")
 endif()
 
+list(JOIN CMAKE_OSX_ARCHITECTURES "$<COMMA>" OSX_ARCH)
 add_custom_command(
     OUTPUT ${GN_EXECUTABLE}
     WORKING_DIRECTORY ${GN_BINARY_DIR}
     COMMAND ${Python3_EXECUTABLE} ${GN_SOURCE_DIR}/build/gen.py
         --no-last-commit-position
+        --no-static-libstdc++
         --out-path ${GN_BINARY_DIR}/$<CONFIG>
         --cc ${CMAKE_C_COMPILER}
         --cxx ${CMAKE_CXX_COMPILER}
         --ld ${GN_LINKER}
+        --allow-warnings
         --platform ${platform}
         --ar ${CMAKE_AR}
         --qt-version "${QT_REPO_MODULE_VERSION}.qtwebengine.qt.io"
-        $<$<PLATFORM_ID:Darwin>:--isysroot>
-        $<$<PLATFORM_ID:Darwin>:${CMAKE_OSX_SYSROOT}>
+        $<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${CMAKE_OSX_SYSROOT}>>:--isysroot>
+        $<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${CMAKE_OSX_SYSROOT}>>:${CMAKE_OSX_SYSROOT}>
+        $<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${OSX_ARCH}>>:--osx-architectures>
+        $<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${OSX_ARCH}>>:${OSX_ARCH}>
     COMMAND Ninja::ninja -C ${GN_BINARY_DIR}/$<CONFIG> ${GN_EXECUTABLE}
     VERBATIM
     USES_TERMINAL
diff --git a/src/pdf/CMakeLists.txt b/src/pdf/CMakeLists.txt
index 41018e7da3b..95931a261f8 100644
--- a/src/pdf/CMakeLists.txt
+++ b/src/pdf/CMakeLists.txt
@@ -38,6 +38,7 @@ qt_internal_add_module(Pdf
     PUBLIC_LIBRARIES
         Qt::Core
         Qt::Gui
+    SBOM_INCOMPLETE_3RD_PARTY_DEPENDENCIES
 )
 
 add_subdirectory(plugins/imageformats/pdf)
@@ -123,6 +124,7 @@ foreach(arch ${archs})
           enable_swiftshader_vulkan=false
           angle_enable_swiftshader=false
           dawn_use_swiftshader=false
+          use_cups=false
           use_dawn=false
           build_dawn_tests=false
           enable_ipc_fuzzer=false
@@ -251,6 +253,7 @@ add_gn_build_artifacts_to_target(
     CMAKE_TARGET Pdf
     NINJA_TARGET QtPdf
     MODULE pdf
+    DEPENDS Pdf_sync_headers
     BUILDDIR ${buildDir}
     COMPLETE_STATIC TRUE
     NINJA_STAMP QtPdf.stamp
diff --git a/src/pdf/doc/qtpdf.qdocconf b/src/pdf/doc/qtpdf.qdocconf
index d0340fe8317..d208188df8d 100644
--- a/src/pdf/doc/qtpdf.qdocconf
+++ b/src/pdf/doc/qtpdf.qdocconf
@@ -38,6 +38,7 @@ depends += qtcore \
            qtdoc \
            qmake \
            qtdesigner \
+           qtqml \
            qtquick \
            qtquickcontrols \
            qtcmake \
diff --git a/src/pdf/plugins/imageformats/pdf/qpdfiohandler.cpp b/src/pdf/plugins/imageformats/pdf/qpdfiohandler.cpp
index bb3e7c92930..a7fd9fa9e76 100644
--- a/src/pdf/plugins/imageformats/pdf/qpdfiohandler.cpp
+++ b/src/pdf/plugins/imageformats/pdf/qpdfiohandler.cpp
@@ -36,7 +36,8 @@ bool QPdfIOHandler::canRead() const
 bool QPdfIOHandler::canRead(QIODevice *device)
 {
     char buf[6];
-    device->peek(buf, 6);
+    if (device->peek(buf, 6) != 6)
+        return false;
     return (!qstrncmp(buf, "%PDF-", 5) || Q_UNLIKELY(!qstrncmp(buf, "\012%PDF-", 6)));
 }
 
diff --git a/src/pdf/qpdfdocumentrenderoptions.qdoc b/src/pdf/qpdfdocumentrenderoptions.qdoc
index ad8e7bfdb88..253f3e37adb 100644
--- a/src/pdf/qpdfdocumentrenderoptions.qdoc
+++ b/src/pdf/qpdfdocumentrenderoptions.qdoc
@@ -1,5 +1,5 @@
 // Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
 
 #include "qpdfdocumentrenderoptions.h"
 
diff --git a/src/pdf/qpdflinkmodel.cpp b/src/pdf/qpdflinkmodel.cpp
index 0a8b1e812f5..1652189176f 100644
--- a/src/pdf/qpdflinkmodel.cpp
+++ b/src/pdf/qpdflinkmodel.cpp
@@ -335,4 +335,4 @@ void QPdfLinkModel::onStatusChanged(QPdfDocument::Status status)
 
 QT_END_NAMESPACE
 
-#include "moc_qpdflinkmodel_p.cpp"
+#include "moc_qpdflinkmodel.cpp"
diff --git a/src/pdfquick/qquickpdfbookmarkmodel.cpp b/src/pdfquick/qquickpdfbookmarkmodel.cpp
index 81f8547ae73..b32af20c909 100644
--- a/src/pdfquick/qquickpdfbookmarkmodel.cpp
+++ b/src/pdfquick/qquickpdfbookmarkmodel.cpp
@@ -8,7 +8,7 @@ QT_BEGIN_NAMESPACE
 
 /*!
     \qmltype PdfBookmarkModel
-//!    \instantiates QQuickPdfBookmarkModel
+//!    \nativetype QQuickPdfBookmarkModel
     \inqmlmodule QtQuick.Pdf
     \ingroup pdf
     \brief A tree of links (anchors) within a PDF document, such as the table of contents.
diff --git a/src/pdfquick/qquickpdfdocument.cpp b/src/pdfquick/qquickpdfdocument.cpp
index 9770900db57..6bf1744f666 100644
--- a/src/pdfquick/qquickpdfdocument.cpp
+++ b/src/pdfquick/qquickpdfdocument.cpp
@@ -7,6 +7,7 @@
 #include <QtCore/qstandardpaths.h>
 #include <QtQml/qqmlcontext.h>
 #include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlinfo.h>
 #include <QtQuick/qquickitem.h>
 #include <QtQml/qqmlfile.h>
 
@@ -14,7 +15,7 @@ QT_BEGIN_NAMESPACE
 
 /*!
     \qmltype PdfDocument
-//!    \instantiates QQuickPdfDocument
+//!    \nativetype QQuickPdfDocument
     \inqmlmodule QtQuick.Pdf
     \ingroup pdf
     \brief A representation of a PDF document.
@@ -63,7 +64,13 @@ void QQuickPdfDocument::classBegin()
 
     This property holds a URL pointing to the PDF file to be loaded.
 
-    \note At this time, only local filesystem URLs are supported.
+    \note At this time, only local filesystem and
+    \l {The Qt Resource System} {resource} URLs are supported. Nevertheless,
+    the \c source property is a \l {QUrl}{URL}, not merely a filesystem path.
+    PdfDocument resolves it via QQmlContext::resolvedUrl(). You should
+    typically ensure that the URL starts with a \c {file://} scheme, unless you
+    mean to load the PDF file from resources, or it comes from some component
+    (such as \l {QtQuick.Controls::}{FileDialog}) that resolves it in advance.
 */
 void QQuickPdfDocument::setSource(QUrl source)
 {
@@ -80,6 +87,8 @@ void QQuickPdfDocument::setSource(QUrl source)
     m_resolvedSource = context ? context->resolvedUrl(source) : source;
     if (m_resolvedSource.isValid())
         m_doc->load(QQmlFile::urlToLocalFileOrQrc(m_resolvedSource));
+    else
+        qmlWarning(this) << QQuickPdfDocument::tr("Cannot open: %1").arg(m_resolvedSource.toString());
 }
 
 /*!
diff --git a/src/pdfquick/qquickpdflinkmodel.cpp b/src/pdfquick/qquickpdflinkmodel.cpp
index 469d13faff8..10d76630b25 100644
--- a/src/pdfquick/qquickpdflinkmodel.cpp
+++ b/src/pdfquick/qquickpdflinkmodel.cpp
@@ -10,7 +10,7 @@ QT_BEGIN_NAMESPACE
 
 /*!
     \qmltype PdfLinkModel
-//!    \instantiates QQuickPdfLinkModel
+//!    \nativetype QQuickPdfLinkModel
     \inqmlmodule QtQuick.Pdf
     \ingroup pdf
     \brief A representation of links within a PDF document.
diff --git a/src/pdfquick/qquickpdfpageimage.cpp b/src/pdfquick/qquickpdfpageimage.cpp
index 9ff0337a5d1..fe6dced8eca 100644
--- a/src/pdfquick/qquickpdfpageimage.cpp
+++ b/src/pdfquick/qquickpdfpageimage.cpp
@@ -12,7 +12,7 @@ Q_LOGGING_CATEGORY(qLcImg, "qt.pdf.image")
 
 /*!
     \qmltype PdfPageImage
-//!    \instantiates QQuickPdfPageImage
+//!    \nativetype QQuickPdfPageImage
     \inqmlmodule QtQuick.Pdf
     \ingroup pdf
     \inherits Image
diff --git a/src/pdfquick/qquickpdfpagenavigator.cpp b/src/pdfquick/qquickpdfpagenavigator.cpp
index 939d928e918..f5b423d4a18 100644
--- a/src/pdfquick/qquickpdfpagenavigator.cpp
+++ b/src/pdfquick/qquickpdfpagenavigator.cpp
@@ -8,7 +8,7 @@ QT_BEGIN_NAMESPACE
 
 /*!
     \qmltype PdfPageNavigator
-//!    \instantiates QQuickPdfPageNavigator
+//!    \nativetype QQuickPdfPageNavigator
     \inqmlmodule QtQuick.Pdf
     \ingroup pdf
     \brief History of the destinations visited within a PDF Document.
diff --git a/src/pdfquick/qquickpdfsearchmodel.cpp b/src/pdfquick/qquickpdfsearchmodel.cpp
index 896584ad756..87acf46aef8 100644
--- a/src/pdfquick/qquickpdfsearchmodel.cpp
+++ b/src/pdfquick/qquickpdfsearchmodel.cpp
@@ -10,7 +10,7 @@ Q_LOGGING_CATEGORY(qLcSearch, "qt.pdf.search")
 
 /*!
     \qmltype PdfSearchModel
-//!    \instantiates QQuickPdfSearchModel
+//!    \nativetype QQuickPdfSearchModel
     \inqmlmodule QtQuick.Pdf
     \ingroup pdf
     \brief A representation of text search results within a PDF Document.
diff --git a/src/pdfquick/qquickpdfselection.cpp b/src/pdfquick/qquickpdfselection.cpp
index 4776cb8b4ed..f93cb43940a 100644
--- a/src/pdfquick/qquickpdfselection.cpp
+++ b/src/pdfquick/qquickpdfselection.cpp
@@ -20,7 +20,7 @@ static const QRegularExpression WordDelimiter(QStringLiteral("\\s"));
 
 /*!
     \qmltype PdfSelection
-//!    \instantiates QQuickPdfSelection
+//!    \nativetype QQuickPdfSelection
     \inqmlmodule QtQuick.Pdf
     \ingroup pdf
     \inherits Item
@@ -470,9 +470,10 @@ void QQuickPdfSelection::setHold(bool hold)
 }
 
 /*!
-    \qmlproperty string PdfSelection::string
+    \qmlproperty string PdfSelection::text
 
-    The string found.
+    The text that was found in the rectangular area between \l from and \l to,
+    or all text on the \l page if selectAll() was called.
 */
 QString QQuickPdfSelection::text() const
 {
@@ -483,7 +484,7 @@ QString QQuickPdfSelection::text() const
 /*!
     \qmlmethod void PdfSelection::copyToClipboard()
 
-    Copies plain text from the \l string property to the system clipboard.
+    Copies plain text from the \l text property to the system clipboard.
 */
 void QQuickPdfSelection::copyToClipboard() const
 {
diff --git a/src/webenginequick/CMakeLists.txt b/src/webenginequick/CMakeLists.txt
index b7de1c2af35..50d038cc9fa 100644
--- a/src/webenginequick/CMakeLists.txt
+++ b/src/webenginequick/CMakeLists.txt
@@ -18,7 +18,7 @@ qt_internal_add_qml_module(WebEngineQuick
         api/qquickwebengineaction_p_p.h
         api/qquickwebengineclientcertificateselection.cpp api/qquickwebengineclientcertificateselection_p.h
         api/qquickwebenginedialogrequests.cpp api/qquickwebenginedialogrequests_p.h
-        api/qquickwebenginedownloadrequest.cpp api/qquickwebenginedownloadrequest_p.h
+        api/qquickwebenginedownloadrequest.cpp api/qquickwebenginedownloadrequest.h
         api/qquickwebenginefaviconprovider.cpp
         api/qquickwebenginefaviconprovider_p_p.h
         api/qquickwebenginenewwindowrequest.cpp api/qquickwebenginenewwindowrequest_p.h
diff --git a/src/webenginequick/api/qquickwebengineaction.cpp b/src/webenginequick/api/qquickwebengineaction.cpp
index 006715c701f..c918f647ee2 100644
--- a/src/webenginequick/api/qquickwebengineaction.cpp
+++ b/src/webenginequick/api/qquickwebengineaction.cpp
@@ -10,7 +10,7 @@ QT_BEGIN_NAMESPACE
 
 /*!
     \qmltype WebEngineAction
-    \instantiates QQuickWebEngineAction
+    \nativetype QQuickWebEngineAction
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.8
 
diff --git a/src/webenginequick/api/qquickwebengineclientcertificateselection.cpp b/src/webenginequick/api/qquickwebengineclientcertificateselection.cpp
index 46e531716c4..8c2ca3840ed 100644
--- a/src/webenginequick/api/qquickwebengineclientcertificateselection.cpp
+++ b/src/webenginequick/api/qquickwebengineclientcertificateselection.cpp
@@ -9,7 +9,7 @@ QT_BEGIN_NAMESPACE
 
 /*!
     \qmltype WebEngineClientCertificateOption
-    //! \instantiates QQuickWebEngineClientCertificateOption
+    //! \nativetype QQuickWebEngineClientCertificateOption
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.9
     \brief Represents a client certificate option.
@@ -79,7 +79,7 @@ void QQuickWebEngineClientCertificateOption::select()
 
 /*!
     \qmltype WebEngineClientCertificateSelection
-    //! \instantiates QQuickWebEngineClientCertificateSelection
+    //! \nativetype QQuickWebEngineClientCertificateSelection
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.9
     \brief Provides a selection of client certificates.
diff --git a/src/webenginequick/api/qquickwebenginedialogrequests.cpp b/src/webenginequick/api/qquickwebenginedialogrequests.cpp
index d49f173976c..9d6b5ed7000 100644
--- a/src/webenginequick/api/qquickwebenginedialogrequests.cpp
+++ b/src/webenginequick/api/qquickwebenginedialogrequests.cpp
@@ -35,7 +35,7 @@ ASSERT_ENUMS_MATCH(FilePickerController::Save,
 
 /*!
     \qmltype AuthenticationDialogRequest
-    //! \instantiates QQuickWebEngineAuthenticationDialogRequest
+    //! \nativetype QQuickWebEngineAuthenticationDialogRequest
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.4
 
@@ -186,8 +186,10 @@ void QQuickWebEngineAuthenticationDialogRequest::dialogAccept(const QString &use
     m_accepted = true;
     QSharedPointer<AuthenticationDialogController> controller
             = m_controller.toStrongRef();
-    if (controller)
-        controller->accept(user,password);
+    if (controller) {
+        controller->credentials(user, password);
+        controller->accept();
+    }
 }
 
 /*!
@@ -210,7 +212,7 @@ void QQuickWebEngineAuthenticationDialogRequest::dialogReject()
 
 /*!
     \qmltype JavaScriptDialogRequest
-    //! \instantiates QQuickWebEngineJavaScriptDialogRequest
+    //! \nativetype QQuickWebEngineJavaScriptDialogRequest
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.4
 
@@ -405,7 +407,7 @@ void QQuickWebEngineJavaScriptDialogRequest::dialogReject()
 
 /*!
     \qmltype ColorDialogRequest
-    //! \instantiates QQuickWebEngineColorDialogRequest
+    //! \nativetype QQuickWebEngineColorDialogRequest
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.4
 
@@ -524,7 +526,7 @@ void QQuickWebEngineColorDialogRequest::dialogReject()
 
 /*!
     \qmltype FileDialogRequest
-    //! \instantiates QQuickWebEngineFileDialogRequest
+    //! \nativetype QQuickWebEngineFileDialogRequest
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.4
 
@@ -680,7 +682,7 @@ void QQuickWebEngineFileDialogRequest::dialogReject()
 
 /*!
     \qmltype TooltipRequest
-    //! \instantiates QQuickWebEngineTooltipRequest
+    //! \nativetype QQuickWebEngineTooltipRequest
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.10
 
diff --git a/src/webenginequick/api/qquickwebenginedownloadrequest.cpp b/src/webenginequick/api/qquickwebenginedownloadrequest.cpp
index f745ba3b309..e4892977163 100644
--- a/src/webenginequick/api/qquickwebenginedownloadrequest.cpp
+++ b/src/webenginequick/api/qquickwebenginedownloadrequest.cpp
@@ -1,7 +1,8 @@
 // Copyright (C) 2021 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 
-#include "qquickwebenginedownloadrequest_p.h"
+#include "qquickwebenginedownloadrequest.h"
+#include "qquickwebengineview_p.h"
 #include "QtWebEngineCore/private/qwebenginedownloadrequest_p.h"
 
 #include "web_contents_adapter_client.h"
@@ -29,4 +30,4 @@ QQuickWebEngineView *QQuickWebEngineDownloadRequest::view() const
 
 QT_END_NAMESPACE
 
-#include "moc_qquickwebenginedownloadrequest_p.cpp"
+#include "moc_qquickwebenginedownloadrequest.cpp"
diff --git a/src/webenginequick/api/qquickwebenginedownloadrequest_p.h b/src/webenginequick/api/qquickwebenginedownloadrequest.h
similarity index 63%
rename from src/webenginequick/api/qquickwebenginedownloadrequest_p.h
rename to src/webenginequick/api/qquickwebenginedownloadrequest.h
index 42a0d88bade..291b59e08ba 100644
--- a/src/webenginequick/api/qquickwebenginedownloadrequest_p.h
+++ b/src/webenginequick/api/qquickwebenginedownloadrequest.h
@@ -1,27 +1,16 @@
 // Copyright (C) 2021 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 
-#ifndef QQUICKWEBENGINEDOWNLOADREQUEST_P_H
-#define QQUICKWEBENGINEDOWNLOADREQUEST_P_H
-
-//
-//  W A R N I N G
-//  -------------
-//
-// This file is not part of the Qt API.  It exists purely as an
-// implementation detail.  This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtWebEngineQuick/private/qtwebenginequickglobal_p.h>
-#include <QtWebEngineQuick/private/qquickwebengineview_p.h>
+#ifndef QQUICKWEBENGINEDOWNLOADREQUEST_H
+#define QQUICKWEBENGINEDOWNLOADREQUEST_H
+
 #include <QtWebEngineCore/qwebenginedownloadrequest.h>
+#include <QtWebEngineQuick/qtwebenginequickglobal.h>
 #include <QtQml/qqmlregistration.h>
 
 QT_BEGIN_NAMESPACE
 
+class QQuickWebEngineView;
 class QQuickWebEngineProfilePrivate;
 
 class Q_WEBENGINEQUICK_EXPORT QQuickWebEngineDownloadRequest : public QWebEngineDownloadRequest
@@ -43,4 +32,4 @@ class Q_WEBENGINEQUICK_EXPORT QQuickWebEngineDownloadRequest : public QWebEngine
 
 QT_END_NAMESPACE
 
-#endif // QQUICKWEBENGINEDOWNLOADREQUEST_P_H
+#endif // QQUICKWEBENGINEDOWNLOADREQUEST_H
diff --git a/src/webenginequick/api/qquickwebengineforeigntypes_p.h b/src/webenginequick/api/qquickwebengineforeigntypes_p.h
index 7ba3a412114..58e9a556139 100644
--- a/src/webenginequick/api/qquickwebengineforeigntypes_p.h
+++ b/src/webenginequick/api/qquickwebengineforeigntypes_p.h
@@ -32,6 +32,7 @@
 #include <QtWebEngineCore/qwebengineregisterprotocolhandlerrequest.h>
 #include <QtWebEngineCore/qwebenginefilesystemaccessrequest.h>
 #include <QtWebEngineCore/qwebenginewebauthuxrequest.h>
+#include <QtWebEngineCore/qwebenginepermission.h>
 
 QT_BEGIN_NAMESPACE
 
@@ -235,7 +236,31 @@ struct ForeginWebEngineWebAuthPinRequest
 {
     Q_GADGET
     QML_FOREIGN(QWebEngineWebAuthPinRequest)
-    QML_NAMED_ELEMENT(WebEngineWebAuthPinRequest)
+    QML_VALUE_TYPE(webEngineWebAuthPinRequest)
+    QML_ADDED_IN_VERSION(6, 8)
+    QML_UNCREATABLE("")
+};
+
+// To prevent the same type from being exported twice into qmltypes
+// (for value type and for the enums)
+class QWebEnginePermissionDerived : public QWebEnginePermission
+{
+    Q_GADGET
+};
+
+namespace ForeignWebEnginePermissionNamespace
+{
+    Q_NAMESPACE
+    QML_FOREIGN_NAMESPACE(QWebEnginePermissionDerived)
+    QML_NAMED_ELEMENT(WebEnginePermission)
+    QML_ADDED_IN_VERSION(6, 8)
+}
+
+struct ForeignWebEnginePermission
+{
+    Q_GADGET
+    QML_FOREIGN(QWebEnginePermission)
+    QML_VALUE_TYPE(webEnginePermission)
     QML_ADDED_IN_VERSION(6, 8)
     QML_UNCREATABLE("")
 };
diff --git a/src/webenginequick/api/qquickwebengineprofile.cpp b/src/webenginequick/api/qquickwebengineprofile.cpp
index ae55bb1bd96..49091dccf46 100644
--- a/src/webenginequick/api/qquickwebengineprofile.cpp
+++ b/src/webenginequick/api/qquickwebengineprofile.cpp
@@ -3,7 +3,7 @@
 
 #include "qquickwebengineprofile.h"
 #include "qquickwebengineprofile_p.h"
-#include "qquickwebenginedownloadrequest_p.h"
+#include "qquickwebenginedownloadrequest.h"
 #include "qquickwebenginesettings_p.h"
 #include "qquickwebenginescriptcollection_p.h"
 #include "qquickwebenginescriptcollection_p_p.h"
@@ -19,6 +19,7 @@
 #include <QtWebEngineCore/qwebenginenotification.h>
 #include <QtWebEngineCore/private/qwebenginedownloadrequest_p.h>
 #include <QtWebEngineCore/qwebengineurlscheme.h>
+#include <QtWebEngineCore/private/qwebenginepermission_p.h>
 
 #include <QtCore/qdir.h>
 #include <QtCore/qfileinfo.h>
@@ -101,17 +102,17 @@ QT_BEGIN_NAMESPACE
 
     This enum describes the policy for permission persistence:
 
-    \value  NoPersistentPermissions
+    \value  AskEveryTime
             The application will ask for permissions every time they're needed, regardless of
             whether they've been granted before or not. This is intended for backwards compatibility
             with existing applications, and otherwise not recommended.
-    \value  PersistentPermissionsInMemory
+    \value  StoreInMemory
             A request will be made only the first time a permission is needed. Any subsequent
             requests will be automatically granted or denied, depending on the initial user choice.
             This carries over to all pages that use the same QQuickWebEngineProfile instance, until the
             application is shut down. This is the setting applied if \c off-the-record is set
             or no persistent data path is available.
-    \value  PersistentPermissionsOnDisk
+    \value  StoreOnDisk
             Works the same way as \c PersistentPermissionsInMemory, but the permissions are saved to
             and restored from disk. This is the default setting.
 */
@@ -241,10 +242,16 @@ void QQuickWebEngineProfilePrivate::cleanDownloads()
     m_ongoingDownloads.clear();
 }
 
-void QQuickWebEngineProfilePrivate::downloadRequested(DownloadItemInfo &info)
+void QQuickWebEngineProfilePrivate::downloadRequested(const DownloadItemInfo &info)
 {
     Q_Q(QQuickWebEngineProfile);
 
+    if (!q->receivers(SIGNAL(downloadRequested(QQuickWebEngineDownloadRequest*)))) {
+        m_profileAdapter->acceptDownload(info.id, info.accepted, info.useDownloadTargetCallback, info.path,
+                                         info.savePageFormat);
+        return;
+    }
+
     Q_ASSERT(!m_ongoingDownloads.contains(info.id));
     QWebEngineDownloadRequestPrivate *itemPrivate =
             new QWebEngineDownloadRequestPrivate(m_profileAdapter);
@@ -261,6 +268,7 @@ void QQuickWebEngineProfilePrivate::downloadRequested(DownloadItemInfo &info)
     itemPrivate->savePageFormat = static_cast<QWebEngineDownloadRequest::SavePageFormat>(
                 info.savePageFormat);
     itemPrivate->isSavePageDownload = info.isSavePageDownload;
+    itemPrivate->useDownloadTargetCallback = info.useDownloadTargetCallback;
     if (info.page && info.page->clientType() == QtWebEngineCore::WebContentsAdapterClient::QmlClient)
         itemPrivate->adapterClient = info.page;
     else
@@ -274,17 +282,9 @@ void QQuickWebEngineProfilePrivate::downloadRequested(DownloadItemInfo &info)
     QQmlEngine::setObjectOwnership(download, QQmlEngine::JavaScriptOwnership);
     Q_EMIT q->downloadRequested(download);
 
-    QWebEngineDownloadRequest::DownloadState state = download->state();
-    info.path = QDir(download->downloadDirectory()).filePath(download->downloadFileName());
-    info.savePageFormat = itemPrivate->savePageFormat;
-    info.accepted = state != QWebEngineDownloadRequest::DownloadCancelled
-                      && state != QWebEngineDownloadRequest::DownloadRequested;
-
-    if (state == QWebEngineDownloadRequest::DownloadRequested) {
-        // Delete unaccepted downloads.
-        info.accepted = false;
-        delete download;
-    }
+    // Callbacks of automatically accepted save operations have to be called here
+    if (info.isSavePageDownload && info.accepted)
+        itemPrivate->answer();
 }
 
 void QQuickWebEngineProfilePrivate::downloadUpdated(const DownloadItemInfo &info)
@@ -339,7 +339,7 @@ QQuickWebEngineScriptCollection *QQuickWebEngineProfilePrivate::getUserScripts()
 }
 /*!
     \qmltype WebEngineProfile
-    \instantiates QQuickWebEngineProfile
+    \nativetype QQuickWebEngineProfile
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.1
     \brief Contains settings, scripts, and visited links common to multiple web engine views.
@@ -364,7 +364,7 @@ QQuickWebEngineScriptCollection *QQuickWebEngineProfilePrivate::getUserScripts()
     whether a profile is off-the-record.
 
     Each web engine view has an associated profile. Views that do not have a specific profile set
-    share a common default one.
+    share a common one, which is off-the-record by default.
 */
 
 /*!
@@ -403,7 +403,12 @@ QQuickWebEngineScriptCollection *QQuickWebEngineProfilePrivate::getUserScripts()
 */
 
 /*!
-    Constructs a new profile with the parent \a parent.
+    Constructs a new off-the-record profile with the parent \a parent.
+
+    An off-the-record profile leaves no record on the local machine, and has no
+    persistent data or cache. Thus, the HTTP cache can only be in memory and the
+    cookies can only be non-persistent. Trying to change these settings will
+    have no effect.
 */
 QQuickWebEngineProfile::QQuickWebEngineProfile(QObject *parent)
     : QObject(parent),
@@ -477,6 +482,11 @@ void QQuickWebEngineProfile::setStorageName(const QString &name)
     Whether the web engine profile is \e off-the-record.
     An off-the-record profile forces cookies, the HTTP cache, and other normally persistent data
     to be stored only in memory. Profile is off-the-record by default.
+
+    Changing a profile from \e off-the-record to disk-based behavior also requires setting a
+    proper storageName.
+
+    \sa storageName
 */
 
 
@@ -486,6 +496,11 @@ void QQuickWebEngineProfile::setStorageName(const QString &name)
     Whether the web engine profile is \e off-the-record.
     An off-the-record profile forces cookies, the HTTP cache, and other normally persistent data
     to be stored only in memory. Profile is off-the-record by default.
+
+    Changing a profile from \e off-the-record to disk-based behavior also requires setting a
+    proper storageName.
+
+    \sa setStorageName()
 */
 
 bool QQuickWebEngineProfile::isOffTheRecord() const
@@ -499,6 +514,23 @@ void QQuickWebEngineProfile::setOffTheRecord(bool offTheRecord)
     Q_D(QQuickWebEngineProfile);
     if (d->profileAdapter()->isOffTheRecord() == offTheRecord)
         return;
+
+    if (!offTheRecord && d->profileAdapter()->storageName().isEmpty()) {
+        qWarning("Storage name is empty. Cannot change profile from off-the-record "
+                 "to disk-based behavior until a proper storage name is set");
+        // Wait for the profile storage name is set
+        QObject::connect(
+                this, &QQuickWebEngineProfile::storageNameChanged, this,
+                [this]() {
+                    if (!storageName().isEmpty()) {
+                        qWarning("Switching to disk-based behavior");
+                        this->setOffTheRecord(false);
+                    }
+                },
+                Qt::SingleShotConnection);
+        return;
+    }
+
     ProfileAdapter::HttpCacheType oldCacheType = d->profileAdapter()->httpCacheType();
     ProfileAdapter::PersistentCookiesPolicy oldCookiePolicy = d->profileAdapter()->persistentCookiesPolicy();
     ProfileAdapter::PersistentPermissionsPolicy oldPermissionsPolicy = d->profileAdapter()->persistentPermissionsPolicy();
@@ -698,17 +730,17 @@ void QQuickWebEngineProfile::setPersistentCookiesPolicy(QQuickWebEngineProfile::
 
     This enumeration describes the policy for permission persistence:
 
-    \value  WebEngineProfile.NoPersistentPermissions
+    \value  WebEngineProfile.AskEveryTime
             The application will ask for permissions every time they're needed, regardless of
             whether they've been granted before or not. This is intended for backwards compatibility
             with existing applications, and otherwise not recommended.
-    \value  WebEngineProfile.PersistentPermissionsInMemory
+    \value  WebEngineProfile.StoreInMemory
             A request will be made only the first time a permission is needed. Any subsequent
             requests will be automatically granted or denied, depending on the initial user choice.
             This carries over to all pages using the same QWebEngineProfile instance, until the
             application is shut down. This is the setting applied if \c off-the-record is set
             or no persistent data path is available.
-    \value  WebEngineProfile.PersistentPermissionsOnDisk
+    \value  WebEngineProfile.StoreOnDisk
             Works the same way as \c PersistentPermissionsInMemory, but the permissions are saved to
             and restored from disk. This is the default setting.
 */
@@ -1113,6 +1145,152 @@ QWebEngineClientHints *QQuickWebEngineProfile::clientHints() const
     return d->m_clientHints.data();
 }
 
+/*!
+    \fn QQuickWebEngineProfile::queryPermission(const QUrl &securityOrigin, QWebEnginePermission::PermissionType permissionType) const
+
+    Returns a QWebEnginePermission object corresponding to a single permission for the provided \a securityOrigin and
+    \a permissionType. The object may be used to query for the current state of the permission, or to change it. It is not required
+    for a permission to already exist; the returned object may also be used to pre-grant a permission if a website is
+    known to use it.
+
+    \note This may only be used for persistent permission types. Calling it with a non-persistent \a permissionType will return an invalid object.
+    \since 6.8
+    \sa listAllPermissions(), listPermissionsForOrigin(), listPermissionsForPermissionType(), QWebEnginePermission::PermissionType
+ */
+
+/*!
+    \qmlmethod void WebEngineProfile::queryPermission(url securityOrigin, WebEnginePermission.PermissionType permissionType) const
+
+    Returns a webEnginePermission object corresponding to a single permission for the provided \a securityOrigin and
+    \a permissionType. The object may be used to query for the current state of the permission, or to change it. It is not required
+    for a permission to already exist; the returned object may also be used to pre-grant a permission if a website is
+    known to use it.
+
+    \note This may only be used for persistent permission types. Calling it with a non-persistent \a permissionType will return an invalid object.
+    \since 6.8
+    \sa listAllPermissions(), listPermissionsForOrigin(), listPermissionsForPermissionType()
+ */
+QWebEnginePermission QQuickWebEngineProfile::queryPermission(const QUrl &securityOrigin, QWebEnginePermission::PermissionType permissionType) const
+{
+    Q_D(const QQuickWebEngineProfile);
+
+    if (permissionType == QWebEnginePermission::PermissionType::Unsupported) {
+        qWarning("Attempting to get unsupported permission. Returned object will be in an invalid state.");
+        return QWebEnginePermission(new QWebEnginePermissionPrivate());
+    }
+
+    if (!QWebEnginePermission::isPersistent(permissionType)) {
+        qWarning() << "Attempting to get permission for permission type" << permissionType << ". Returned object will be in an invalid state.";
+        return QWebEnginePermission(new QWebEnginePermissionPrivate());
+    }
+
+    auto *pvt = new QWebEnginePermissionPrivate(securityOrigin, permissionType, nullptr, d->profileAdapter());
+    return QWebEnginePermission(pvt);
+}
+
+/*!
+    \qmlmethod list<webEnginePermission> WebEngineProfile::listAllPermissions() const
+
+    Returns a \l list of webEnginePermission objects, each one representing a single permission currently
+    present in the permissions store. The returned list contains all previously granted/denied permissions for this profile,
+    provided they are of a \e persistent type.
+
+    \note When the persistentPermissionPolicy property is set to \c AskEveryTime, this will return an empty list.
+    \since 6.8
+    \sa queryPermission(), listPermissionsForOrigin(), listPermissionsForPermissionType(), webEnginePermission::isPersistent()
+ */
+
+/*!
+    Returns a QList of QWebEnginePermission objects, each one representing a single permission currently
+    present in the permissions store. The returned list contains all previously granted/denied permissions for this profile,
+    provided they are of a \e persistent type.
+
+    \note When persistentPermissionPolicy() is set to \c AskEveryTime, this will return an empty list.
+    \since 6.8
+    \sa queryPermission(), listPermissionsForOrigin(), listPermissionsForPermissionType(), QWebEnginePermission::isPersistent()
+ */
+QList<QWebEnginePermission> QQuickWebEngineProfile::listAllPermissions() const
+{
+    Q_D(const QQuickWebEngineProfile);
+    if (persistentPermissionsPolicy() == PersistentPermissionsPolicy::AskEveryTime)
+        return QList<QWebEnginePermission>();
+    return d->profileAdapter()->listPermissions();
+}
+
+/*!
+    \qmlmethod list<webEnginePermission> WebEngineProfile::listPermissionsForOrigin(url securityOrigin) const
+
+    Returns a \l list of webEnginePermission objects, each one representing a single permission currently
+    present in the permissions store. The returned list contains all previously granted/denied permissions associated with a
+    specific \a securityOrigin for this profile, provided they are of a \e persistent type.
+
+    \note Since permissions are granted on a per-origin basis, the provided \a securityOrigin will be stripped to its
+    origin form, and the returned list will contain all permissions for the origin. Thus, passing https://www.example.com/some/page.html
+    is the same as passing just https://www.example.com/.
+    \note When persistentPermissionPolicy() is set to \c AskEveryTime, this will return an empty list.
+    \since 6.8
+    \sa queryPermission(), listAllPermissions(), listPermissionsForPermissionType(), webEnginePermission::isPersistent()
+ */
+
+/*!
+    Returns a QList of QWebEnginePermission objects, each one representing a single permission currently
+    present in the permissions store. The returned list contains all previously granted/denied permissions associated with a
+    specific \a securityOrigin for this profile, provided they are of a \e persistent type.
+
+    \note Since permissions are granted on a per-origin basis, the provided \a securityOrigin will be stripped to its
+    origin form, and the returned list will contain all permissions for the origin. Thus, passing https://www.example.com/some/page.html
+    is the same as passing just https://www.example.com/.
+    \since 6.8
+    \sa queryPermission(), listAllPermissions(), listPermissionsForPermissionType(), QWebEnginePermission::isPersistent()
+ */
+QList<QWebEnginePermission> QQuickWebEngineProfile::listPermissionsForOrigin(const QUrl &securityOrigin) const
+{
+    Q_D(const QQuickWebEngineProfile);
+    if (persistentPermissionsPolicy() == PersistentPermissionsPolicy::AskEveryTime)
+        return QList<QWebEnginePermission>();
+    return d->profileAdapter()->listPermissions(securityOrigin);
+}
+
+/*!
+    \qmlmethod list<webEnginePermission> WebEngineProfile::listPermissionsForPermissionType(WebEnginePermission.PermissionType permissionType) const
+
+    Returns a \l list of webEnginePermission objects, each one representing a single permission currently
+    present in the permissions store. The returned list contains all previously granted/denied permissions of the provided
+    \a permissionType. If the \permissionType is non-persistent, the list will be empty.
+
+    \note When persistentPermissionPolicy() is set to \c AskEveryTime, this will return an empty list.
+    \since 6.8
+    \sa queryPermission(), listAllPermissions(), listPermissionsForOrigin(), webEnginePermission::isPersistent()
+ */
+
+/*!
+    Returns a QList of QWebEnginePermission objects, each one representing a single permission currently
+    present in the permissions store. The returned list contains all previously granted/denied permissions of the provided
+    \a permissionType. If the \permissionType is non-persistent, the list will be empty.
+
+    \note When persistentPermissionPolicy() is set to \c AskEveryTime, this will return an empty list.
+    \since 6.8
+    \sa queryPermission(), listAllPermissions(), listPermissionsForOrigin(), QWebEnginePermission::PermissionType, QWebEnginePermission::isPersistent()
+ */
+QList<QWebEnginePermission> QQuickWebEngineProfile::listPermissionsForPermissionType(QWebEnginePermission::PermissionType permissionType) const
+{
+    Q_D(const QQuickWebEngineProfile);
+    if (persistentPermissionsPolicy() == PersistentPermissionsPolicy::AskEveryTime)
+        return QList<QWebEnginePermission>();
+
+    if (permissionType == QWebEnginePermission::PermissionType::Unsupported) {
+        qWarning("Attempting to get permission list for an unsupported type. Returned list will be empty.");
+        return QList<QWebEnginePermission>();
+    }
+
+    if (!QWebEnginePermission::isPersistent(permissionType)) {
+        qWarning() << "Attempting to get permission list for permission type" << permissionType << ". Returned list will be empty.";
+        return QList<QWebEnginePermission>();
+    }
+
+    return d->profileAdapter()->listPermissions(QUrl(), permissionType);
+}
+
 void QQuickWebEngineProfile::ensureQmlContext(const QObject *object)
 {
     if (!qmlContext(this)) {
diff --git a/src/webenginequick/api/qquickwebengineprofile.h b/src/webenginequick/api/qquickwebengineprofile.h
index 7ba9105ffa9..90425c5ca76 100644
--- a/src/webenginequick/api/qquickwebengineprofile.h
+++ b/src/webenginequick/api/qquickwebengineprofile.h
@@ -5,6 +5,7 @@
 #define QQUICKWEBENGINEPROFILE_H
 
 #include <QtWebEngineQuick/qtwebenginequickglobal.h>
+#include <QtWebEngineCore/qwebenginepermission.h>
 #include <QtCore/qobject.h>
 #include <QtCore/qscopedpointer.h>
 #include <QtCore/qstring.h>
@@ -33,13 +34,14 @@ class Q_WEBENGINEQUICK_EXPORT QQuickWebEngineProfile : public QObject {
     Q_PROPERTY(HttpCacheType httpCacheType READ httpCacheType WRITE setHttpCacheType NOTIFY httpCacheTypeChanged FINAL)
     Q_PROPERTY(QString httpAcceptLanguage READ httpAcceptLanguage WRITE setHttpAcceptLanguage NOTIFY httpAcceptLanguageChanged FINAL REVISION(1,1))
     Q_PROPERTY(PersistentCookiesPolicy persistentCookiesPolicy READ persistentCookiesPolicy WRITE setPersistentCookiesPolicy NOTIFY persistentCookiesPolicyChanged FINAL)
-    Q_PROPERTY(PersistentPermissionsPolicy persistentPermissionsPolicy READ persistentPermissionsPolicy WRITE setPersistentPermissionsPolicy NOTIFY persistentPermissionsPolicyChanged FINAL)
+    Q_PROPERTY(PersistentPermissionsPolicy persistentPermissionsPolicy READ persistentPermissionsPolicy WRITE setPersistentPermissionsPolicy NOTIFY persistentPermissionsPolicyChanged FINAL REVISION(6,8))
     Q_PROPERTY(int httpCacheMaximumSize READ httpCacheMaximumSize WRITE setHttpCacheMaximumSize NOTIFY httpCacheMaximumSizeChanged FINAL)
     Q_PROPERTY(QStringList spellCheckLanguages READ spellCheckLanguages WRITE setSpellCheckLanguages NOTIFY spellCheckLanguagesChanged FINAL REVISION(1,3))
     Q_PROPERTY(bool spellCheckEnabled READ isSpellCheckEnabled WRITE setSpellCheckEnabled NOTIFY spellCheckEnabledChanged FINAL REVISION(1,3))
     Q_PROPERTY(QQuickWebEngineScriptCollection *userScripts READ userScripts)
     Q_PROPERTY(QString downloadPath READ downloadPath WRITE setDownloadPath NOTIFY downloadPathChanged FINAL REVISION(1,5))
     Q_PROPERTY(bool isPushServiceEnabled READ isPushServiceEnabled WRITE setPushServiceEnabled NOTIFY pushServiceEnabledChanged FINAL REVISION(6,5))
+    Q_PROPERTY(QWebEngineClientHints *clientHints READ clientHints FINAL REVISION(6,8))
     QML_NAMED_ELEMENT(WebEngineProfile)
     QML_ADDED_IN_VERSION(1, 1)
     QML_EXTRA_VERSION(2, 0)
@@ -62,10 +64,10 @@ class Q_WEBENGINEQUICK_EXPORT QQuickWebEngineProfile : public QObject {
     };
     Q_ENUM(PersistentCookiesPolicy)
 
-    enum PersistentPermissionsPolicy : quint8 {
-        NoPersistentPermissions,
-        PersistentPermissionsInMemory,
-        PersistentPermissionsOnDisk,
+    enum class PersistentPermissionsPolicy : quint8 {
+        AskEveryTime = 0,
+        StoreInMemory,
+        StoreOnDisk,
     };
     Q_ENUM(PersistentPermissionsPolicy)
 
@@ -127,6 +129,11 @@ class Q_WEBENGINEQUICK_EXPORT QQuickWebEngineProfile : public QObject {
     QWebEngineClientCertificateStore *clientCertificateStore();
     QWebEngineClientHints *clientHints() const;
 
+    Q_REVISION(6,8) Q_INVOKABLE QWebEnginePermission queryPermission(const QUrl &securityOrigin, QWebEnginePermission::PermissionType permissionType) const;
+    Q_REVISION(6,8) Q_INVOKABLE QList<QWebEnginePermission> listAllPermissions() const;
+    Q_REVISION(6,8) Q_INVOKABLE QList<QWebEnginePermission> listPermissionsForOrigin(const QUrl &securityOrigin) const;
+    Q_REVISION(6,8) Q_INVOKABLE QList<QWebEnginePermission> listPermissionsForPermissionType(QWebEnginePermission::PermissionType permissionType) const;
+
     static QQuickWebEngineProfile *defaultProfile();
 
 Q_SIGNALS:
diff --git a/src/webenginequick/api/qquickwebengineprofile_p.h b/src/webenginequick/api/qquickwebengineprofile_p.h
index 477936f98c4..fde24763665 100644
--- a/src/webenginequick/api/qquickwebengineprofile_p.h
+++ b/src/webenginequick/api/qquickwebengineprofile_p.h
@@ -50,7 +50,7 @@ class QQuickWebEngineProfilePrivate : public QtWebEngineCore::ProfileAdapterClie
 
     void cleanDownloads();
 
-    void downloadRequested(DownloadItemInfo &info) override;
+    void downloadRequested(const DownloadItemInfo &info) override;
     void downloadUpdated(const DownloadItemInfo &info) override;
 
     void showNotification(QSharedPointer<QtWebEngineCore::UserNotificationController> &controller) override;
diff --git a/src/webenginequick/api/qquickwebenginescriptcollection.cpp b/src/webenginequick/api/qquickwebenginescriptcollection.cpp
index a58d9783219..09a8d181e95 100644
--- a/src/webenginequick/api/qquickwebenginescriptcollection.cpp
+++ b/src/webenginequick/api/qquickwebenginescriptcollection.cpp
@@ -3,12 +3,10 @@
 
 #include "qquickwebenginescriptcollection_p.h"
 #include "qquickwebenginescriptcollection_p_p.h"
-#include "qwebenginescriptcollection.h"
+#include <QtWebEngineCore/qwebenginescriptcollection.h>
 #include <QtWebEngineCore/private/qwebenginescriptcollection_p.h>
 #include <QtQml/qqmlinfo.h>
-#include <QtQml/private/qqmlengine_p.h>
-#include <QtQml/private/qv4scopedvalue_p.h>
-#include <QtQml/private/qv4arrayobject_p.h>
+#include <QtQml/qqmlengine.h>
 
 /*!
     \qmltype WebEngineScriptCollection
@@ -117,9 +115,10 @@ QQuickWebEngineScriptCollection::QQuickWebEngineScriptCollection(QQuickWebEngine
 QQuickWebEngineScriptCollection::~QQuickWebEngineScriptCollection() { }
 
 /*!
-    \qmlmethod void WebEngineScriptCollection::contains(WebEngineScript script)
+    \qmlmethod bool WebEngineScriptCollection::contains(WebEngineScript script)
     \since QtWebEngine 6.2
-    Checks if the specified \a script is in the collection.
+    Returns \c true if the specified \a script is in the collection, \c false
+    otherwise.
     \sa find()
 */
 
@@ -132,7 +131,7 @@ bool QQuickWebEngineScriptCollection::contains(const QWebEngineScript &value) co
     \qmlmethod list<WebEngineScript> WebEngineScriptCollection::find(string name)
     \since QtWebEngine 6.2
     Returns a list of all user script objects with the given \a name.
-    \sa find()
+    \sa contains()
 */
 QList<QWebEngineScript> QQuickWebEngineScriptCollection::find(const QString &name) const
 {
@@ -143,7 +142,7 @@ QList<QWebEngineScript> QQuickWebEngineScriptCollection::find(const QString &nam
     \qmlmethod void WebEngineScriptCollection::insert(WebEngineScript script)
     \since QtWebEngine 6.2
     Inserts a single \a script into the collection.
-    \sa find()
+    \sa remove()
 */
 void QQuickWebEngineScriptCollection::insert(const QWebEngineScript &s)
 {
@@ -154,7 +153,7 @@ void QQuickWebEngineScriptCollection::insert(const QWebEngineScript &s)
     \qmlmethod void WebEngineScriptCollection::insert(list<WebEngineScript> list)
     \since QtWebEngine 6.2
     Inserts a \a list of WebEngineScript values into the user script collection.
-    \sa find()
+    \sa remove()
 */
 void QQuickWebEngineScriptCollection::insert(const QList<QWebEngineScript> &list)
 {
@@ -197,15 +196,11 @@ QJSValue QQuickWebEngineScriptCollection::collection() const
     }
 
     const QList<QWebEngineScript> &list = d->toList();
-    QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(d->m_qmlEngine);
-    QV4::Scope scope(v4);
-    QV4::Scoped<QV4::ArrayObject> scriptArray(scope, v4->newArrayObject(list.size()));
-    int i = 0;
-    for (const auto &val : list) {
-        QV4::ScopedValue sv(scope, v4->fromVariant(QVariant::fromValue(val)));
-        scriptArray->put(i++, sv);
-    }
-    return QJSValuePrivate::fromReturnedValue(scriptArray.asReturnedValue());
+    QJSValue scriptArray = d->m_qmlEngine->newArray(list.size());
+    uint32_t i = 0;
+    for (const auto &val : list)
+        scriptArray.setProperty(i++, d->m_qmlEngine->toScriptValue(val));
+    return scriptArray;
 }
 
 void QQuickWebEngineScriptCollection::setCollection(const QJSValue &scripts)
diff --git a/src/webenginequick/api/qquickwebenginesettings.cpp b/src/webenginequick/api/qquickwebenginesettings.cpp
index 31ed7a661b0..abd8ea25bcb 100644
--- a/src/webenginequick/api/qquickwebenginesettings.cpp
+++ b/src/webenginequick/api/qquickwebenginesettings.cpp
@@ -14,7 +14,7 @@ QQuickWebEngineSettings::QQuickWebEngineSettings(QQuickWebEngineSettings *parent
 
 /*!
     \qmltype WebEngineSettings
-    //! \instantiates QQuickWebEngineSettings
+    //! \nativetype QQuickWebEngineSettings
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.1
     \brief Allows configuration of browser properties and attributes.
@@ -71,15 +71,18 @@ bool QQuickWebEngineSettings::javascriptCanOpenWindows() const
 /*!
     \qmlproperty bool WebEngineSettings::javascriptCanAccessClipboard
 
-    Allows JavaScript programs to read from or write to the clipboard.
-    Writing to the clipboard is always allowed if it is specifically requested by the user.
+    Allows JavaScript programs to write (copy) sanitized content to the clipboard. A
+    sanitized write is done with the \c{write} and \c{writeText} JavaScript Clipboard API
+    calls and must be accompanied by user action.
 
-    To enable also the pasting of clipboard content from JavaScript,
-    use javascriptCanPaste.
+    Unsanitized writes, and reading from the clipboard, are
+    enabled by \l{javascriptCanPaste}.
+
+    Prior to Chromium version 81, this setting enabled all clipboard writes.
 
     Since unrestricted clipboard access is a potential security concern, it is
     recommended that applications leave this disabled and instead respond to
-    \l{WebEngineView::ClipboardReadWrite}{ClipboardReadWrite} feature permission requests.
+    \l{webEnginePermission::permissionType}{ClipboardReadWrite} feature permission requests.
 
     Disabled by default.
 */
@@ -184,8 +187,6 @@ bool QQuickWebEngineSettings::errorPageEnabled() const
     Enables support for Pepper plugins, such as the Flash player.
 
     Disabled by default.
-
-   \sa {Pepper Plugin API}
 */
 bool QQuickWebEngineSettings::pluginsEnabled() const
 {
@@ -384,12 +385,17 @@ bool QQuickWebEngineSettings::webRTCPublicInterfacesOnly() const
     \qmlproperty bool WebEngineSettings::javascriptCanPaste
     \since QtWebEngine 1.7
 
-    Enables JavaScript \c{execCommand("paste")}.
-    This also requires enabling javascriptCanAccessClipboard.
+    Allows JavaScript programs to read (paste) from the clipboard and to write unsanitized
+    content. A sanitized write is done with the \c{write} and \c{writeText} JavaScript
+    Clipboard API calls and must be accompanied by user action; unsanitized writes are any
+    writes which do not meet these criteria.
+
+    For this setting to have any effect, \l{javascriptCanAccessClipboard} must also be
+    enabled.
 
     Since unrestricted clipboard access is a potential security concern, it is
     recommended that applications leave this disabled and instead respond to
-    \l{WebEngineView::ClipboardReadWrite}{ClipboardReadWrite} feature permission requests.
+    \l{webEnginePermission::permissionType}{ClipboardReadWrite} feature permission requests.
 
     Disabled by default.
 */
@@ -496,11 +502,12 @@ QString QQuickWebEngineSettings::defaultTextEncoding() const
     return d_ptr->defaultTextEncoding();
 }
 
-ASSERT_ENUMS_MATCH(QQuickWebEngineSettings::AllowImageAnimation,
-                   QWebEngineSettings::AllowImageAnimation)
-ASSERT_ENUMS_MATCH(QQuickWebEngineSettings::AnimateImageOnce, QWebEngineSettings::AnimateImageOnce)
-ASSERT_ENUMS_MATCH(QQuickWebEngineSettings::DisallowImageAnimation,
-                   QWebEngineSettings::DisallowImageAnimation)
+ASSERT_ENUMS_MATCH(QQuickWebEngineSettings::ImageAnimationPolicy::Allow,
+                   QWebEngineSettings::ImageAnimationPolicy::Allow)
+ASSERT_ENUMS_MATCH(QQuickWebEngineSettings::ImageAnimationPolicy::AnimateOnce,
+                   QWebEngineSettings::ImageAnimationPolicy::AnimateOnce)
+ASSERT_ENUMS_MATCH(QQuickWebEngineSettings::ImageAnimationPolicy::Disallow,
+                   QWebEngineSettings::ImageAnimationPolicy::Disallow)
 /*!
     \qmlproperty enumeration WebEngineSettings::imageAnimationPolicy
     \since QtWebEngine 6.8
@@ -508,14 +515,14 @@ ASSERT_ENUMS_MATCH(QQuickWebEngineSettings::DisallowImageAnimation,
     Specifies how an image animation should be handled when the image frames
     are rendered for animation.
 
-    \value WebEngineSettings.AllowImageAnimation
+    \value WebEngineSettings.ImageAnimationPolicy.Allow
            Allows all image animations when the image frames are rendered.
-    \value WebEngineSettings.AnimateImageOnce
+    \value WebEngineSettings.ImageAnimationPolicy.AnimateOnce
            Animate the image once when the image frames are rendered.
-    \value WebEngineSettings.DisallowImageAnimation
+    \value WebEngineSettings.ImageAnimationPolicy.Disallow
            Disallows all image animations when the image frames are rendered.
 
-    Default value is \c {WebEngineSettings.AllowImageAnimation}.
+    Default value is \c {WebEngineSettings.ImageAnimationPolicy.Allow}.
 */
 QQuickWebEngineSettings::ImageAnimationPolicy QQuickWebEngineSettings::imageAnimationPolicy() const
 {
diff --git a/src/webenginequick/api/qquickwebenginesettings_p.h b/src/webenginequick/api/qquickwebenginesettings_p.h
index ed3c778845f..3320c45d884 100644
--- a/src/webenginequick/api/qquickwebenginesettings_p.h
+++ b/src/webenginequick/api/qquickwebenginesettings_p.h
@@ -74,10 +74,10 @@ class Q_WEBENGINEQUICK_EXPORT QQuickWebEngineSettings : public QObject {
 
     Q_ENUM(UnknownUrlSchemePolicy)
 
-    enum ImageAnimationPolicy {
-        AllowImageAnimation = 1,
-        AnimateImageOnce,
-        DisallowImageAnimation
+    enum class ImageAnimationPolicy : uint8_t {
+        Allow = 1,
+        AnimateOnce,
+        Disallow,
     };
 
     Q_ENUM(ImageAnimationPolicy)
diff --git a/src/webenginequick/api/qquickwebenginesingleton.cpp b/src/webenginequick/api/qquickwebenginesingleton.cpp
index a51d2aca468..215d4689698 100644
--- a/src/webenginequick/api/qquickwebenginesingleton.cpp
+++ b/src/webenginequick/api/qquickwebenginesingleton.cpp
@@ -12,7 +12,7 @@ QT_BEGIN_NAMESPACE
 
 /*!
     \qmltype WebEngine
-    //! \instantiates QQuickWebEngineSingleton
+    //! \nativetype QQuickWebEngineSingleton
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.1
     \brief Provides access to the default settings and profiles shared by all web engine views.
@@ -65,7 +65,7 @@ QQuickWebEngineProfile *QQuickWebEngineSingleton::defaultProfile() const
 
 /*!
     \qmlmethod WebEngineScript WebEngine::script
-    //! \instantiates QWebEngineScript
+    //! \nativetype QWebEngineScript
     \since QtWebEngine 6.2
 
     Constructs WebEngineScript, which can be set up and inserted into user scripts' collection
diff --git a/src/webenginequick/api/qquickwebengineview.cpp b/src/webenginequick/api/qquickwebengineview.cpp
index f6bd3878044..c1c6e3d1274 100644
--- a/src/webenginequick/api/qquickwebengineview.cpp
+++ b/src/webenginequick/api/qquickwebengineview.cpp
@@ -41,6 +41,7 @@
 #include <QtWebEngineCore/qwebengineregisterprotocolhandlerrequest.h>
 #include <QtWebEngineCore/qwebenginescriptcollection.h>
 #include <QtWebEngineCore/qwebenginewebauthuxrequest.h>
+#include <QtWebEngineCore/qwebenginepermission.h>
 #include <QtWebEngineCore/private/qwebenginecontextmenurequest_p.h>
 #include <QtWebEngineCore/private/qwebenginedesktopmediarequest_p.h>
 #include <QtWebEngineCore/private/qwebenginehistory_p.h>
@@ -48,6 +49,7 @@
 #include <QtWebEngineCore/private/qwebenginescriptcollection_p.h>
 #include <QtWebEngineCore/private/qwebenginepage_p.h>
 #include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h>
+#include <QtWebEngineCore/private/qwebenginepermission_p.h>
 
 #include <QtCore/qloggingcategory.h>
 #include <QtCore/qmimedata.h>
@@ -259,8 +261,9 @@ class WebEngineQuickWidgetDelegate : public QtWebEngineCore::WidgetDelegate
 
     void InitAsPopup(const QRect &screenRect) override
     {
-        Q_UNUSED(screenRect);
-        Q_UNREACHABLE();
+        // note this is called when there is no windowing system
+        // otherwsie see RenderWidgetHostViewQtDelegateQuickWindow
+        m_contentItem->setPosition(screenRect.topLeft());
     }
 
     void Bind(WebContentsAdapterClient *client) override
@@ -484,29 +487,57 @@ void QQuickWebEngineViewPrivate::selectClientCert(
     Q_EMIT q->selectClientCertificate(certSelection);
 }
 
-static QQuickWebEngineView::Feature toFeature(QtWebEngineCore::ProfileAdapter::PermissionType type)
+#if QT_DEPRECATED_SINCE(6, 8)
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+static QQuickWebEngineView::Feature toDeprecatedFeature(QWebEnginePermission::PermissionType permissionType)
 {
-    switch (type) {
-    case QtWebEngineCore::ProfileAdapter::NotificationPermission:
+    switch (permissionType) {
+    case QWebEnginePermission::PermissionType::Notifications:
         return QQuickWebEngineView::Notifications;
-    case QtWebEngineCore::ProfileAdapter::GeolocationPermission:
+    case QWebEnginePermission::PermissionType::Geolocation:
         return QQuickWebEngineView::Geolocation;
-    case QtWebEngineCore::ProfileAdapter::ClipboardReadWrite:
+    case QWebEnginePermission::PermissionType::ClipboardReadWrite:
         return QQuickWebEngineView::ClipboardReadWrite;
-    case QtWebEngineCore::ProfileAdapter::LocalFontsPermission:
+    case QWebEnginePermission::PermissionType::LocalFontsAccess:
         return QQuickWebEngineView::LocalFontsAccess;
-    default:
+    case QWebEnginePermission::PermissionType::MediaAudioCapture:
+        return QQuickWebEngineView::MediaAudioCapture;
+    case QWebEnginePermission::PermissionType::MediaVideoCapture:
+        return QQuickWebEngineView::MediaVideoCapture;
+    case QWebEnginePermission::PermissionType::MediaAudioVideoCapture:
+        return QQuickWebEngineView::MediaAudioVideoCapture;
+    case QWebEnginePermission::PermissionType::DesktopVideoCapture:
+        return QQuickWebEngineView::DesktopVideoCapture;
+    case QWebEnginePermission::PermissionType::DesktopAudioVideoCapture:
+        return QQuickWebEngineView::DesktopAudioVideoCapture;
+    case QWebEnginePermission::PermissionType::MouseLock:
+    case QWebEnginePermission::PermissionType::Unsupported:
         break;
     }
+
     Q_UNREACHABLE();
     return QQuickWebEngineView::Feature(-1);
 }
+QT_WARNING_POP
+#endif // QT_DEPRECATED_SINCE(6, 8)
 
-
-void QQuickWebEngineViewPrivate::runFeaturePermissionRequest(QtWebEngineCore::ProfileAdapter::PermissionType permission, const QUrl &url)
+void QQuickWebEngineViewPrivate::runFeaturePermissionRequest(QWebEnginePermission::PermissionType permissionType, const QUrl &securityOrigin)
 {
     Q_Q(QQuickWebEngineView);
-    Q_EMIT q->featurePermissionRequested(url, toFeature(permission));
+
+    if (QWebEnginePermission::isPersistent(permissionType)) {
+        Q_EMIT q->permissionRequested(createFeaturePermissionObject(securityOrigin, permissionType));
+#if QT_DEPRECATED_SINCE(6, 8)
+        QT_WARNING_PUSH
+        QT_WARNING_DISABLE_DEPRECATED
+        Q_EMIT q->featurePermissionRequested(securityOrigin, toDeprecatedFeature(permissionType));
+        QT_WARNING_POP
+#endif // QT_DEPRECATED_SINCE(6, 8)
+        return;
+    }
+
+    Q_UNREACHABLE();
 }
 
 void QQuickWebEngineViewPrivate::showColorDialog(QSharedPointer<ColorChooserController> controller)
@@ -784,19 +815,41 @@ void QQuickWebEngineViewPrivate::runMediaAccessPermissionRequest(const QUrl &sec
     Q_Q(QQuickWebEngineView);
     if (!requestFlags)
         return;
-    QQuickWebEngineView::Feature feature;
+    QWebEnginePermission::PermissionType permissionType;
     if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture) && requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture))
-        feature = QQuickWebEngineView::MediaAudioVideoCapture;
+        permissionType = QWebEnginePermission::PermissionType::MediaAudioVideoCapture;
     else if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture))
-        feature = QQuickWebEngineView::MediaAudioCapture;
+        permissionType = QWebEnginePermission::PermissionType::MediaAudioCapture;
     else if (requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture))
-        feature = QQuickWebEngineView::MediaVideoCapture;
+        permissionType = QWebEnginePermission::PermissionType::MediaVideoCapture;
     else if (requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopAudioCapture) &&
              requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture))
-        feature = QQuickWebEngineView::DesktopAudioVideoCapture;
+        permissionType = QWebEnginePermission::PermissionType::DesktopAudioVideoCapture;
     else // if (requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture))
-        feature = QQuickWebEngineView::DesktopVideoCapture;
-    Q_EMIT q->featurePermissionRequested(securityOrigin, feature);
+        permissionType = QWebEnginePermission::PermissionType::DesktopVideoCapture;
+    Q_EMIT q->permissionRequested(createFeaturePermissionObject(securityOrigin, permissionType));
+
+#if QT_DEPRECATED_SINCE(6, 8)
+    QT_WARNING_PUSH
+    QT_WARNING_DISABLE_DEPRECATED
+    QQuickWebEngineView::Feature deprecatedFeature;
+
+    if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture)
+            && requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture))
+        deprecatedFeature = QQuickWebEngineView::MediaAudioVideoCapture;
+    else if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture))
+        deprecatedFeature = QQuickWebEngineView::MediaAudioCapture;
+    else if (requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture))
+        deprecatedFeature = QQuickWebEngineView::MediaVideoCapture;
+    else if (requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopAudioCapture)
+            && requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture))
+        deprecatedFeature = QQuickWebEngineView::DesktopAudioVideoCapture;
+    else // if (requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture))
+        deprecatedFeature = QQuickWebEngineView::DesktopVideoCapture;
+
+    Q_EMIT q->featurePermissionRequested(securityOrigin, deprecatedFeature);
+    QT_WARNING_POP
+#endif // QT_DEPRECATED_SINCE(6, 8)
 }
 
 void QQuickWebEngineViewPrivate::runMouseLockPermissionRequest(const QUrl &securityOrigin)
@@ -846,7 +899,7 @@ void QQuickWebEngineViewPrivate::printRequestedByFrame(quint64 frameId)
 {
     Q_Q(QQuickWebEngineView);
     QTimer::singleShot(0, q, [this, q, frameId]() {
-        Q_EMIT q->printRequestedByFrame(QWebEngineFrame(this, frameId));
+        Q_EMIT q->printRequestedByFrame(QWebEngineFrame(this->adapter, frameId));
     });
 }
 
@@ -1318,16 +1371,17 @@ void QQuickWebEngineViewPrivate::runJavaScript(
 }
 
 void QQuickWebEngineViewPrivate::printToPdf(const QString &filePath, const QPageLayout &layout,
-                                            const QPageRanges &ranges)
+                                            const QPageRanges &ranges, quint64 frameId)
 {
-    adapter->printToPDF(layout, ranges, filePath);
+    adapter->printToPDF(layout, ranges, filePath, frameId);
 }
 
 void QQuickWebEngineViewPrivate::printToPdf(
         std::function<void(QSharedPointer<QByteArray>)> &&callback, const QPageLayout &layout,
-        const QPageRanges &ranges)
+        const QPageRanges &ranges, quint64 frameId)
 {
-    adapter->printToPDFCallbackResult(std::move(callback), layout, ranges);
+    adapter->printToPDFCallbackResult(std::move(callback), layout, ranges, /*colorMode*/ true,
+                                      /*useCustomMargins*/ true, frameId);
 }
 
 void QQuickWebEngineViewPrivate::didPrintPageToPdf(const QString &filePath, bool success)
@@ -1396,8 +1450,9 @@ QQuickWebEngineViewPrivate::createTouchHandleDelegate(const QMap<int, QImage> &i
 {
     Q_Q(QQuickWebEngineView);
     // lifecycle managed by Chromium's TouchHandleDrawable
-    QQuickWebEngineTouchHandle *handle = new QQuickWebEngineTouchHandle();
+    QQuickWebEngineTouchHandle *handle(nullptr);
     if (m_touchHandleDelegate) {
+        handle = new QQuickWebEngineTouchHandle();
         QQmlContext *qmlContext = QQmlEngine::contextForObject(q);
         QQmlContext *context = new QQmlContext(qmlContext, handle);
         context->setContextObject(handle);
@@ -1408,7 +1463,9 @@ QQuickWebEngineViewPrivate::createTouchHandleDelegate(const QMap<int, QImage> &i
         handle->setItem(item, false);
     } else {
         QQuickItem *item = ui()->createTouchHandle();
-        Q_ASSERT(item);
+        if (!item) {
+            return nullptr;
+        }
         QQmlEngine *engine = qmlEngine(item);
         Q_ASSERT(engine);
         QQuickWebEngineTouchHandleProvider *touchHandleProvider =
@@ -1416,6 +1473,7 @@ QQuickWebEngineViewPrivate::createTouchHandleDelegate(const QMap<int, QImage> &i
                         engine->imageProvider(QQuickWebEngineTouchHandleProvider::identifier()));
         Q_ASSERT(touchHandleProvider);
         touchHandleProvider->init(images);
+        handle = new QQuickWebEngineTouchHandle();
         handle->setItem(item, true);
     }
     return handle;
@@ -1465,6 +1523,12 @@ void QQuickWebEngineViewPrivate::showWebAuthDialog(QWebEngineWebAuthUxRequest *r
     Q_EMIT q->webAuthUxRequested(request);
 }
 
+QWebEnginePermission QQuickWebEngineViewPrivate::createFeaturePermissionObject(const QUrl &securityOrigin, QWebEnginePermission::PermissionType permissionType)
+{
+    auto *returnPrivate = new QWebEnginePermissionPrivate(securityOrigin, permissionType, adapter, profileAdapter());
+    return QWebEnginePermission(returnPrivate);
+}
+
 bool QQuickWebEngineView::isLoading() const
 {
     Q_D(const QQuickWebEngineView);
@@ -1586,7 +1650,7 @@ void QQuickWebEngineView::printToPdf(const QString& filePath, PrintedPageSizeId
     QPageLayout pageLayout(layoutSize, layoutOrientation, QMarginsF(0.0, 0.0, 0.0, 0.0));
     QPageRanges ranges;
     d->ensureContentsAdapter();
-    d->adapter->printToPDF(pageLayout, ranges, filePath);
+    d->printToPdf(filePath, pageLayout, ranges, WebContentsAdapter::kUseMainFrameId);
 #else
     Q_UNUSED(filePath);
     Q_UNUSED(pageSizeId);
@@ -1613,7 +1677,8 @@ void QQuickWebEngineView::printToPdf(const QJSValue &callback, PrintedPageSizeId
         callback.call(args);
     };
 
-    d->printToPdf(std::move(wrappedCallback), pageLayout, ranges);
+    d->printToPdf(std::move(wrappedCallback), pageLayout, ranges,
+                  WebContentsAdapter::kUseMainFrameId);
 #else
     Q_UNUSED(pageSizeId);
     Q_UNUSED(orientation);
@@ -1759,55 +1824,50 @@ void QQuickWebEngineView::setDevToolsView(QQuickWebEngineView *devToolsView)
     Q_EMIT devToolsViewChanged();
 }
 
+#if QT_DEPRECATED_SINCE(6, 8)
+QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
 void QQuickWebEngineView::grantFeaturePermission(const QUrl &securityOrigin, QQuickWebEngineView::Feature feature, bool granted)
 {
-    if (!granted && ((feature >= MediaAudioCapture && feature <= MediaAudioVideoCapture) ||
-                     (feature >= DesktopVideoCapture && feature <= DesktopAudioVideoCapture))) {
-         d_ptr->adapter->grantMediaAccessPermission(securityOrigin, WebContentsAdapterClient::MediaNone);
-         return;
-    }
+    Q_D(QQuickWebEngineView);
+    QWebEnginePermission::PermissionType permissionType;
 
     switch (feature) {
-    case MediaAudioCapture:
-        d_ptr->adapter->grantMediaAccessPermission(securityOrigin, WebContentsAdapterClient::MediaAudioCapture);
+    case QQuickWebEngineView::Notifications:
+        permissionType = QWebEnginePermission::PermissionType::Notifications;
         break;
-    case MediaVideoCapture:
-        d_ptr->adapter->grantMediaAccessPermission(securityOrigin, WebContentsAdapterClient::MediaVideoCapture);
+    case QQuickWebEngineView::Geolocation:
+        permissionType = QWebEnginePermission::PermissionType::Geolocation;
         break;
-    case MediaAudioVideoCapture:
-        d_ptr->adapter->grantMediaAccessPermission(securityOrigin, WebContentsAdapterClient::MediaRequestFlags(WebContentsAdapterClient::MediaAudioCapture                                                                                                               | WebContentsAdapterClient::MediaVideoCapture));
+    case QQuickWebEngineView::MediaAudioCapture:
+        permissionType = QWebEnginePermission::PermissionType::MediaAudioCapture;
         break;
-    case DesktopVideoCapture:
-        d_ptr->adapter->grantMediaAccessPermission(securityOrigin, WebContentsAdapterClient::MediaDesktopVideoCapture);
+    case QQuickWebEngineView::MediaVideoCapture:
+        permissionType = QWebEnginePermission::PermissionType::MediaVideoCapture;
         break;
-    case DesktopAudioVideoCapture:
-        d_ptr->adapter->grantMediaAccessPermission(
-            securityOrigin,
-            WebContentsAdapterClient::MediaRequestFlags(
-                WebContentsAdapterClient::MediaDesktopAudioCapture |
-                WebContentsAdapterClient::MediaDesktopVideoCapture));
+    case QQuickWebEngineView::MediaAudioVideoCapture:
+        permissionType = QWebEnginePermission::PermissionType::MediaAudioVideoCapture;
         break;
-    case Geolocation:
-        d_ptr->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::GeolocationPermission,
-                                               granted ? ProfileAdapter::AllowedPermission : ProfileAdapter::DeniedPermission);
+    case QQuickWebEngineView::DesktopVideoCapture:
+        permissionType = QWebEnginePermission::PermissionType::DesktopVideoCapture;
         break;
-    case Notifications:
-        d_ptr->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::NotificationPermission,
-                                               granted ? ProfileAdapter::AllowedPermission : ProfileAdapter::DeniedPermission);
+    case QQuickWebEngineView::DesktopAudioVideoCapture:
+        permissionType = QWebEnginePermission::PermissionType::DesktopAudioVideoCapture;
         break;
-    case ClipboardReadWrite:
-        d_ptr->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::ClipboardReadWrite,
-                                               granted ? ProfileAdapter::AllowedPermission
-                                                       : ProfileAdapter::DeniedPermission);
+    case QQuickWebEngineView::ClipboardReadWrite:
+        permissionType = QWebEnginePermission::PermissionType::ClipboardReadWrite;
         break;
-    case LocalFontsAccess:
-        d_ptr->adapter->grantFeaturePermission(securityOrigin, ProfileAdapter::LocalFontsPermission,
-                                               granted ? ProfileAdapter::AllowedPermission : ProfileAdapter::DeniedPermission);
+    case QQuickWebEngineView::LocalFontsAccess:
+        permissionType = QWebEnginePermission::PermissionType::LocalFontsAccess;
         break;
     default:
         Q_UNREACHABLE();
     }
+
+    d->adapter->setPermission(securityOrigin, permissionType,
+        granted ? QWebEnginePermission::State::Granted : QWebEnginePermission::State::Denied);
 }
+QT_WARNING_POP
+#endif // QT_DEPRECATED_SINCE(6, 8)
 
 void QQuickWebEngineView::setActiveFocusOnPress(bool arg)
 {
@@ -2542,14 +2602,14 @@ QQmlComponent *QQuickWebEngineView::touchHandleDelegate() const
 QWebEngineFrame QQuickWebEngineView::mainFrame()
 {
     Q_D(QQuickWebEngineView);
-    return QWebEngineFrame(d, d->adapter->mainFrameId());
+    return QWebEngineFrame(d->adapter, d->adapter->mainFrameId());
 }
 
 QWebEngineFrame QQuickWebEngineView::findFrameByName(const QString &name)
 {
     Q_D(QQuickWebEngineView);
     auto maybeId = d->adapter->findFrameIdByName(name);
-    return QWebEngineFrame(d, maybeId.value_or(WebContentsAdapter::kInvalidFrameId));
+    return QWebEngineFrame(d->adapter, maybeId.value_or(WebContentsAdapter::kInvalidFrameId));
 }
 
 void QQuickWebEngineView::save(const QString &filePath,
diff --git a/src/webenginequick/api/qquickwebengineview_p.h b/src/webenginequick/api/qquickwebengineview_p.h
index a310e09345b..95635d19c9b 100644
--- a/src/webenginequick/api/qquickwebengineview_p.h
+++ b/src/webenginequick/api/qquickwebengineview_p.h
@@ -20,6 +20,7 @@
 #include <QtWebEngineCore/qwebenginedesktopmediarequest.h>
 #include <QtWebEngineCore/qwebenginedownloadrequest.h>
 #include <QtWebEngineCore/qwebengineframe.h>
+#include <QtWebEngineCore/qwebenginepermission.h>
 #include <QtWebEngineQuick/private/qtwebenginequickglobal_p.h>
 #include <QtGui/qcolor.h>
 #include <QtQml/qqmlregistration.h>
@@ -166,19 +167,20 @@ QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
     Q_ENUM(NewViewDestination)
 QT_WARNING_POP
 #endif
-
+#if QT_DEPRECATED_SINCE(6, 8)
     enum Feature {
-        MediaAudioCapture,
-        MediaVideoCapture,
-        MediaAudioVideoCapture,
-        Geolocation,
-        DesktopVideoCapture,
-        DesktopAudioVideoCapture,
-        Notifications,
-        ClipboardReadWrite,
-        LocalFontsAccess,
+        MediaAudioCapture Q_DECL_ENUMERATOR_DEPRECATED_X("Use QWebEnginePermission::PermissionType::MediaAudioCapture instead"),
+        MediaVideoCapture Q_DECL_ENUMERATOR_DEPRECATED_X("Use QWebEnginePermission::PermissionType::MediaVideoCapture instead"),
+        MediaAudioVideoCapture Q_DECL_ENUMERATOR_DEPRECATED_X("Use QWebEnginePermission::PermissionType::MediaAudioVideoCapture instead"),
+        Geolocation Q_DECL_ENUMERATOR_DEPRECATED_X("Use QWebEnginePermission::PermissionType::Geolocation instead"),
+        DesktopVideoCapture Q_DECL_ENUMERATOR_DEPRECATED_X("Use QWebEnginePermission::PermissionType::DesktopVideoCapture instead"),
+        DesktopAudioVideoCapture Q_DECL_ENUMERATOR_DEPRECATED_X("Use QWebEnginePermission::PermissionType::DesktopAudioVideoCapture instead"),
+        Notifications Q_DECL_ENUMERATOR_DEPRECATED_X("Use QWebEnginePermission::PermissionType::Notifications instead"),
+        ClipboardReadWrite Q_DECL_ENUMERATOR_DEPRECATED_X("Use QWebEnginePermission::PermissionType::ClipboardReadWrite instead"),
+        LocalFontsAccess Q_DECL_ENUMERATOR_DEPRECATED_X("Use QWebEnginePermission::PermissionType::LocalFontsAccess instead"),
     };
     Q_ENUM(Feature)
+#endif
 
     enum WebAction {
         NoWebAction = - 1,
@@ -489,7 +491,11 @@ public Q_SLOTS:
     void stop();
     Q_REVISION(1,1) void findText(const QString &subString, FindFlags options = { }, const QJSValue &callback = QJSValue());
     Q_REVISION(1,1) void fullScreenCancelled();
+#if QT_DEPRECATED_SINCE(6, 8)
+    QT_MOC_COMPAT QT_DEPRECATED_VERSION_X_6_8(
+        "Setting permissions through WebEngineView has been deprecated. Please use WebEnginePermission instead.")
     Q_REVISION(1,1) void grantFeaturePermission(const QUrl &securityOrigin, QQuickWebEngineView::Feature, bool granted);
+#endif // QT_DEPRECATED_SINCE(6, 8)
     Q_REVISION(1,2) void setActiveFocusOnPress(bool arg);
     Q_REVISION(1,2) void triggerWebAction(WebAction action);
     Q_REVISION(1,3) void printToPdf(const QString &filePath, PrintedPageSizeId pageSizeId = PrintedPageSizeId::A4, PrintedPageOrientation orientation = PrintedPageOrientation::Portrait);
@@ -515,9 +521,13 @@ private Q_SLOTS:
     Q_REVISION(1,1) void certificateError(const QWebEngineCertificateError &error);
     Q_REVISION(1,1) void fullScreenRequested(const QWebEngineFullScreenRequest &request);
     Q_REVISION(1,1) void isFullScreenChanged();
+#if QT_DEPRECATED_SINCE(6, 8)
+    QT_MOC_COMPAT QT_DEPRECATED_VERSION_X_6_8(
+        "The signal has been deprecated; please use permissionRequested instead.")
     Q_REVISION(1, 1)
     void featurePermissionRequested(const QUrl &securityOrigin,
                                     QQuickWebEngineView::Feature feature);
+#endif // QT_DEPRECATED_SINCE(6, 8)
     Q_REVISION(1,1) void zoomFactorChanged(qreal arg);
     Q_REVISION(1,1) void profileChanged();
     Q_REVISION(1,1) void webChannelChanged();
@@ -562,6 +572,7 @@ private Q_SLOTS:
     Q_REVISION(6, 7) void webAuthUxRequested(QWebEngineWebAuthUxRequest *request);
     Q_REVISION(6,7) void desktopMediaRequested(const QWebEngineDesktopMediaRequest &request);
     Q_REVISION(6, 8) void printRequestedByFrame(QWebEngineFrame frame);
+    Q_REVISION(6,8) void permissionRequested(QWebEnginePermission permissionRequest);
 
 protected:
     void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
diff --git a/src/webenginequick/api/qquickwebengineview_p_p.h b/src/webenginequick/api/qquickwebengineview_p_p.h
index 5a968aaa44a..d781575971b 100644
--- a/src/webenginequick/api/qquickwebengineview_p_p.h
+++ b/src/webenginequick/api/qquickwebengineview_p_p.h
@@ -97,10 +97,10 @@ class Q_WEBENGINEQUICK_EXPORT QQuickWebEngineViewPrivate : public QtWebEngineCor
                        const std::function<void(const QVariant &)> &callback) override;
     void didFetchDocumentMarkup(quint64, const QString&) override { }
     void didFetchDocumentInnerText(quint64, const QString&) override { }
-    void printToPdf(const QString &filePath, const QPageLayout &layout,
-                    const QPageRanges &ranges) override;
+    void printToPdf(const QString &filePath, const QPageLayout &layout, const QPageRanges &ranges,
+                    quint64 frameId) override;
     void printToPdf(std::function<void(QSharedPointer<QByteArray>)> &&callback,
-                    const QPageLayout &layout, const QPageRanges &ranges) override;
+                    const QPageLayout &layout, const QPageRanges &ranges, quint64 frameId) override;
     void didPrintPageToPdf(const QString &filePath, bool success) override;
     bool passOnFocus(bool reverse) override;
     void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID) override;
@@ -114,7 +114,7 @@ class Q_WEBENGINEQUICK_EXPORT QQuickWebEngineViewPrivate : public QtWebEngineCor
     void allowCertificateError(const QWebEngineCertificateError &error) override;
     void selectClientCert(const QSharedPointer<QtWebEngineCore::ClientCertSelectController>
                                   &selectController) override;
-    void runFeaturePermissionRequest(QtWebEngineCore::ProfileAdapter::PermissionType permission, const QUrl &securityOrigin) override;
+    void runFeaturePermissionRequest(QWebEnginePermission::PermissionType permissionType, const QUrl &securityOrigin) override;
     void renderProcessTerminated(RenderProcessTerminationStatus terminationStatus, int exitCode) override;
     void requestGeometryChange(const QRect &geometry, const QRect &frameGeometry) override;
     void updateScrollPosition(const QPointF &position) override;
@@ -139,6 +139,7 @@ class Q_WEBENGINEQUICK_EXPORT QQuickWebEngineViewPrivate : public QtWebEngineCor
                            const QRect &bounds, bool autoselectFirstSuggestion) override;
     void hideAutofillPopup() override;
     void showWebAuthDialog(QWebEngineWebAuthUxRequest *request) override;
+    QWebEnginePermission createFeaturePermissionObject(const QUrl &securityOrigin, QWebEnginePermission::PermissionType permissionType) override;
 
     void updateAction(QQuickWebEngineView::WebAction) const;
     bool adoptWebContents(QtWebEngineCore::WebContentsAdapter *webContents);
diff --git a/src/webenginequick/doc/src/context_menu_request.qdoc b/src/webenginequick/doc/src/context_menu_request.qdoc
index e7d732d5e28..e253ffb05e0 100644
--- a/src/webenginequick/doc/src/context_menu_request.qdoc
+++ b/src/webenginequick/doc/src/context_menu_request.qdoc
@@ -3,7 +3,7 @@
 
 /*!
     \qmltype ContextMenuRequest
-    //! \instantiates QQuickWebEngineContextMenuRequest
+    //! \nativetype QQuickWebEngineContextMenuRequest
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.4
 
diff --git a/src/webenginequick/doc/src/fullscreen_request.qdoc b/src/webenginequick/doc/src/fullscreen_request.qdoc
index 60da2748c2a..204c5d623c4 100644
--- a/src/webenginequick/doc/src/fullscreen_request.qdoc
+++ b/src/webenginequick/doc/src/fullscreen_request.qdoc
@@ -3,7 +3,7 @@
 
 /*!
     \qmltype FullScreenRequest
-    \instantiates QWebEngineFullScreenRequest
+    \nativetype QWebEngineFullScreenRequest
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.1
 
diff --git a/src/webenginequick/doc/src/loading_info.qdoc b/src/webenginequick/doc/src/loading_info.qdoc
index c97799e2485..4ed745166d6 100644
--- a/src/webenginequick/doc/src/loading_info.qdoc
+++ b/src/webenginequick/doc/src/loading_info.qdoc
@@ -3,7 +3,7 @@
 
 /*!
     \qmltype WebEngineLoadingInfo
-    \instantiates QWebEngineLoadingInfo
+    \nativetype QWebEngineLoadingInfo
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.0
 
diff --git a/src/webenginequick/doc/src/navigation_history.qdoc b/src/webenginequick/doc/src/navigation_history.qdoc
index 94876799da1..42afc070ebb 100644
--- a/src/webenginequick/doc/src/navigation_history.qdoc
+++ b/src/webenginequick/doc/src/navigation_history.qdoc
@@ -3,7 +3,7 @@
 
 /*!
     \qmltype WebEngineHistoryModel
-    \instantiates QWebEngineHistoryModel
+    \nativetype QWebEngineHistoryModel
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.1
 
@@ -24,7 +24,7 @@
 
 /*!
     \qmltype WebEngineHistory
-    \instantiates QWebEngineHistory
+    \nativetype QWebEngineHistory
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.1
 
diff --git a/src/webenginequick/doc/src/qtwebengine-qmlmodule.qdoc b/src/webenginequick/doc/src/qtwebengine-qmlmodule.qdoc
index ecf3a4a6edc..5966fef4741 100644
--- a/src/webenginequick/doc/src/qtwebengine-qmlmodule.qdoc
+++ b/src/webenginequick/doc/src/qtwebengine-qmlmodule.qdoc
@@ -7,6 +7,7 @@
     \brief Provides QML types for rendering web content within a QML application.
     \ingroup qtwebengine-modules
     \ingroup qmlmodules
+    \noautolist
 
     To link against the module using build with qmake,
     add the following QT variable to your qmake .pro file:
@@ -26,4 +27,10 @@
     Where the content of main.qml is simply:
 
     \snippet minimal/main.qml Minimal Example
+
+    \section1 QML object types
+    \generatelist qmltypesbymodule QtWebEngine
+
+    \section1 QML value types
+    \generatelist qmlvaluetypesbymodule QtWebEngine
 */
diff --git a/src/webenginequick/doc/src/quota_request.qdoc b/src/webenginequick/doc/src/quota_request.qdoc
index 01f4ec28606..e96f7a21486 100644
--- a/src/webenginequick/doc/src/quota_request.qdoc
+++ b/src/webenginequick/doc/src/quota_request.qdoc
@@ -3,7 +3,7 @@
 
 /*!
     \qmltype QuotaRequest
-    \instantiates QWebEngineQuotaRequest
+    \nativetype QWebEngineQuotaRequest
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.7
     \deprecated [6.5] Requesting host quota is no longer supported by Chromium.
diff --git a/src/webenginequick/doc/src/register_protocol_handler_request.qdoc b/src/webenginequick/doc/src/register_protocol_handler_request.qdoc
index d69f4d264e6..412a7e656d5 100644
--- a/src/webenginequick/doc/src/register_protocol_handler_request.qdoc
+++ b/src/webenginequick/doc/src/register_protocol_handler_request.qdoc
@@ -3,7 +3,7 @@
 
 /*!
     \qmltype RegisterProtocolHandlerRequest
-    \instantiates QWebEngineRegisterProtocolHandlerRequest
+    \nativetype QWebEngineRegisterProtocolHandlerRequest
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.7
     \brief The RegisterProtocolHandlerRequest type enables accepting
diff --git a/src/webenginequick/doc/src/touch_selection_menu_request.qdoc b/src/webenginequick/doc/src/touch_selection_menu_request.qdoc
index 9ca6ed36bfb..83743502781 100644
--- a/src/webenginequick/doc/src/touch_selection_menu_request.qdoc
+++ b/src/webenginequick/doc/src/touch_selection_menu_request.qdoc
@@ -3,7 +3,7 @@
 
 /*!
     \qmltype TouchSelectionMenuRequest
-    //! \instantiates QQuickWebEngineTouchSelectionMenuRequest
+    //! \nativetype QQuickWebEngineTouchSelectionMenuRequest
     \inqmlmodule QtWebEngine
     \since QtWebEngine 6.3
 
diff --git a/src/webenginequick/doc/src/webengine_certificate_error.qdoc b/src/webenginequick/doc/src/webengine_certificate_error.qdoc
index 93bad9fb113..6253a41facd 100644
--- a/src/webenginequick/doc/src/webengine_certificate_error.qdoc
+++ b/src/webenginequick/doc/src/webengine_certificate_error.qdoc
@@ -3,7 +3,7 @@
 
 /*!
     \qmltype WebEngineCertificateError
-    \instantiates QWebEngineCertificateError
+    \nativetype QWebEngineCertificateError
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.1
 
diff --git a/src/webenginequick/doc/src/webengine_desktop_media_request.qdoc b/src/webenginequick/doc/src/webengine_desktop_media_request.qdoc
new file mode 100644
index 00000000000..c4d28acba80
--- /dev/null
+++ b/src/webenginequick/doc/src/webengine_desktop_media_request.qdoc
@@ -0,0 +1,67 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+    \qmltype WebEngineDesktopMediaRequest
+    \nativetype QWebEngineDesktopMediaRequest
+    \inqmlmodule QtWebEngine
+    \since 6.7
+    \brief A request for populating a dialog with available sources for screen capturing.
+
+    To allow web applications to capture contents of a display, applications must connect
+    to WebEngineView::desktopMediaRequested, which takes a WebEngineDesktopMediaRequest
+    instance as an argument.
+
+    If a web application requests access to the contents of a display,
+    WebEngineView::desktopMediaRequested will be emitted with a
+    WebEngineDesktopMediaRequest instance as an argument which holds references to
+    \l {ListModel}{ListModels} for available windows and screens that can be captured.
+
+    The data model's \e DisplayRole specifies the name of the source which is the title of a
+    window or the number of the display.
+    The model is dynamically updated if the available list of sources has changed;
+    e.g when a window is opened/closed.
+
+    The signal handler needs to then either call \l selectScreen() or \l selectWindow() to accept
+    the request and start screensharing.
+
+    \sa WebEngineView::desktopMediaRequested
+*/
+
+/*!
+    \qmlproperty ListModel WebEngineDesktopMediaRequest::screensModel
+
+    A ListModel containing a list of available screens.
+
+    \sa windowsModel
+*/
+
+/*!
+    \qmlproperty ListModel WebEngineDesktopMediaRequest::windowsModel
+
+    A ListModel containing a list of available windows.
+
+    \sa screensModel
+*/
+
+/*!
+    \qmlmethod void WebEngineDesktopMediaRequest::selectWindow(QModelIndex index)
+
+    Selects the window at the \a index to be captured.
+
+    \sa WebEngineDesktopMediaRequest::selectScreen()
+*/
+
+/*!
+    \qmlmethod void WebEngineDesktopMediaRequest::selectScreen(QModelIndex index)
+
+    Selects the screen at the \a index to be captured.
+
+    \sa WebEngineDesktopMediaRequest::selectWindow()
+*/
+
+/*!
+    \qmlmethod void WebEngineDesktopMediaRequest::cancel()
+
+    Rejects a request. Screen capturing will be aborted.
+*/
diff --git a/src/webenginequick/doc/src/webengine_download_request.qdoc b/src/webenginequick/doc/src/webengine_download_request.qdoc
index 96d84e0a3c2..bc93edb62cf 100644
--- a/src/webenginequick/doc/src/webengine_download_request.qdoc
+++ b/src/webenginequick/doc/src/webengine_download_request.qdoc
@@ -3,7 +3,7 @@
 
 /*!
     \qmltype WebEngineDownloadRequest
-    \instantiates QWebEngineDownloadRequest
+    \nativetype QWebEngineDownloadRequest
     \inqmlmodule QtWebEngine
     \brief Provides information about a download.
 
diff --git a/src/webenginequick/doc/src/webengine_permission.qdoc b/src/webenginequick/doc/src/webengine_permission.qdoc
new file mode 100644
index 00000000000..ff278470f4e
--- /dev/null
+++ b/src/webenginequick/doc/src/webengine_permission.qdoc
@@ -0,0 +1,146 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+    \qmlvaluetype webEnginePermission
+    \nativetype QWebEnginePermission
+    \inqmlmodule QtWebEngine
+    \since 6.8
+    \brief An object used to access and modify the state of a single permission that's been
+    granted or denied to a specific origin URL.
+
+    The typical usage pattern is as follows:
+    \list 1
+        \li A website requests a specific feature, triggering the WebEngineView::permissionRequested signal;
+        \li The signal handler triggers a prompt asking the user whether they want to grant the permission;
+        \li When the user has made their decision, the application calls \l grant() or \l deny();
+    \endlist
+
+    Alternatively, an application interested in modifying already granted permissions may use WebEngineProfile::listAllPermissions()
+    to get a list of existing permissions associated with a profile, or WebEngineProfile::queryPermission() to get
+    a webEnginePermission object for a specific permission.
+
+    The \l origin property can be used to query which origin the webEnginePermission is associated with, while the
+    \l permissionType property describes the associated feature. A website origin is the combination of its scheme, hostname,
+    and port. Permissions are granted on a per-origin basis; thus, if the web page \c{https://www.example.com:12345/some/page.html}
+    requests a permission, it will be granted to the origin \c{https://www.example.com:12345/}.
+
+    The \l permissionType enumeration describes all the permission types Qt WebEngine supports. Only some permission types
+    are remembered between browsing sessions; they are \e persistent. Non-persistent permissions query the user every time a
+    website requests them. You can check whether a permission type is persistent at runtime
+    using the static method WebEnginePermission::isPersistent().
+
+    Persistent permissions are stored inside the active WebEngineProfile, and their lifetime depends on the value of
+    WebEngineProfile::persistentPermissionsPolicy. By default, named profiles store their permissions on disk, whereas
+    off-the-record ones store them in memory (and destroy them when the profile is destroyed). A stored permission will not
+    query the user the next time a website requests it; instead it will be automatically granted or denied, depending on
+    the resolution the user picked initially. To erase a stored permission, call \l reset() on it.
+
+    A non-persistent permission, on the other hand, is only usable until the related WebEngineView performs a navigation to
+    a different URL, or is destroyed.
+
+    You can check whether a WebEnginePermission is in a valid state using its \l isValid property. For invalid objects, calls to \l grant(),
+    \l deny(), or \l reset() will do nothing, while calls to \l state will always return WebEnginePermission::Invalid.
+
+    \sa WebEngineView::permissionRequested, WebEngineProfile::queryPermission(),
+    WebEngineProfile::listAllPermissions()
+*/
+
+/*!
+    \qmlproperty url webEnginePermission::origin
+    \brief The URL of the permission's associated origin.
+
+    A website origin is the combination of its scheme, hostname, and port. Permissions are granted on a
+    per-origin basis; thus, if the web page \c{https://www.example.com:12345/some/page.html}
+    requests a permission, it will be granted to the origin \c{https://www.example.com:12345/}.
+*/
+
+/*!
+    \qmlproperty enumeration webEnginePermission::permissionType
+    \brief The permission type associated with this permission.
+
+    \value WebEnginePermission.MediaAudioCapture Access to a microphone, or another audio source. This permission is \e not persistent.
+    \value WebEnginePermission.MediaVideoCapture Access to a webcam, or another video source. This permission is \e not persistent.
+    \value WebEnginePermission.MediaAudioVideoCapture Combination of \e MediaAudioCapture and \e MediaVideoCapture. This permission is \e not persistent.
+    \value WebEnginePermission.DesktopVideoCapture Access to the contents of the user's screen. This permission is \e not persistent.
+    \value WebEnginePermission.DesktopAudioVideoCapture Access to the contents of the user's screen, and application audio. This permission is \e not persistent.
+    \value WebEnginePermission.Notifications Allows the website to send notifications to the user. This permission is persistent.
+    \value WebEnginePermission.Geolocation Access to the user's physical location. This permission is persistent.
+    \value WebEnginePermission.ClipboardReadWrite Access to the user's clipboard. This permission is persistent.
+    \value WebEnginePermission.LocalFontsAccess Access to the fonts installed on the user's machine. Only available on desktops. This permission is persistent.
+    \value WebEnginePermission.Unsupported An unsupported feature type.
+    \omitvalue WebEnginePermission.MouseLock
+
+    \note Non-persistent permission types are ones that will never be remembered by the underlying storage, and will trigger
+    a permission request every time a website tries to use them.
+*/
+
+/*!
+    \qmlproperty enumeration webEnginePermission::state
+    \brief The current state of the permission.
+
+    \value WebEnginePermission.Invalid Object is in an invalid state, and any attempts to modify the described permission will fail.
+    \value WebEnginePermission.Ask Either the permission has not been requested before, or the \l permissionType is not persistent.
+    \value WebEnginePermission.Granted Permission has already been granted.
+    \value WebEnginePermission.Denied Permission has already been denied.
+
+    If a permission for the specified \l permissionType and \l origin has already been granted or denied,
+    the return value is WebEnginePermission.Granted, or WebEnginePermission.Denied, respectively.
+    When this is the first time the permission is requested,
+    the return value is WebEnginePermission.Ask. If the object is in an invalid state, the returned
+    value is WebEnginePermission.Invalid.
+
+    \sa isValid, isPersistent
+*/
+
+/*!
+    \qmlproperty bool webEnginePermission::isValid
+    \brief Indicates whether attempts to change the permission's state will be successful.
+
+    An invalid webEnginePermission is either:
+    \list
+        \li One whose \l permissionType is unsupported;
+        \li One whose \l permissionType is non-persistent, and the user has navigated away from the web page that triggered the request;
+        \li One whose \l permissionType is persistent, but the associated profile has been destroyed;
+        \li One whose \l origin is invalid.
+    \endlist
+
+    \sa isPersistent
+*/
+
+/*!
+    \qmlmethod void webEnginePermission::grant()
+
+    Allows the associated origin to access the requested per. Does nothing when \l isValid evaluates to false.
+
+    \sa deny, reset, isValid
+*/
+
+/*!
+    \qmlmethod void webEnginePermission::deny()
+
+    Stops the associated origin from accessing the requested feature. Does nothing when \l isValid evaluates to false.
+
+    \sa grant, reset, isValid
+*/
+
+/*!
+    \qmlmethod void webEnginePermission::reset()
+
+    Removes the permission from the profile's underlying storage. By default, permissions are stored on disk (except for
+    off-the-record profiles, where permissions are stored in memory and are destroyed with the profile).
+    This means that an already granted/denied permission will not be requested twice, but will get automatically
+    granted/denied every subsequent time a website requests it. Calling reset() allows the query to be displayed
+    again the next time the website requests it.
+
+    Does nothing when \l isValid evaluates to false.
+
+    \sa grant, deny, isValid, WebEngineProfile::persistentPermissionsPolicy
+*/
+
+/*!
+    \qmlmethod void webEnginePermission::isPersistent(WebEnginePermission.PermissionType permissionType)
+
+    Returns whether a \a permissionType is \e persistent, meaning that a permission's state will be remembered
+    and the user will not be queried the next time the website requests the same permission.
+*/
diff --git a/src/webenginequick/doc/src/webengineframe.qdoc b/src/webenginequick/doc/src/webengineframe.qdoc
index a2368479a2f..5d6a5599a92 100644
--- a/src/webenginequick/doc/src/webengineframe.qdoc
+++ b/src/webenginequick/doc/src/webengineframe.qdoc
@@ -2,8 +2,8 @@
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
 
 /*!
-    \qmltype webEngineFrame
-    \instantiates QQuickWebEngineFrame
+    \qmlvaluetype webEngineFrame
+    \nativetype QWebEngineFrame
     \brief webEngineFrame provides information about and control over a page frame.
     \since 6.8
     \ingroup qmlvaluetypes
@@ -64,6 +64,12 @@
     If the frame could not be found, returns a default size with dimensions (-1, -1).
 */
 
+/*!
+    \qmlproperty bool webEngineFrame::isMainFrame
+
+    Returns \c{true} if this object represents the page's main frame; \c{false} otherwise.
+*/
+
 /*!
     \qmlmethod void webEngineFrame::runJavaScript(string script, variant callback)
     \qmlmethod void webEngineFrame::runJavaScript(string script, uint worldId, variant callback)
@@ -97,3 +103,27 @@
     For more information about injecting scripts, see \l {Script Injection}.
     For an alternative way to inject scripts, see WebEngineView::userScripts.
 */
+
+/*!
+    \qmlmethod void webEngineFrame::printToPdf(string filePath)
+
+    Prints the frame's current content to a PDF document and stores it
+    under \a filePath. The resulting document will have A4 page size and
+    portrait orientation.
+
+    This method issues an asynchronous request for printing the web page into a
+    PDF and returns immediately. To be informed about the result of the
+    request, connect to the signal \l WebEngineView::pdfPrintingFinished().
+
+    \sa WebEngineView::pdfPrintingFinished()
+*/
+
+/*!
+    \qmlmethod void webEngineFrame::printToPdf(variant callback)
+
+    Prints the frame's current content to a PDF document and returns it in a byte array. The
+    resulting document will have A4 page size and portrait orientation.
+
+    The \a callback must take a string parameter. This string will contain the document's data upon
+    successful printing and an empty string otherwise.
+*/
diff --git a/src/webenginequick/doc/src/webenginescript.qdoc b/src/webenginequick/doc/src/webenginescript.qdoc
index 9708ffbf814..f669ceaaed8 100644
--- a/src/webenginequick/doc/src/webenginescript.qdoc
+++ b/src/webenginequick/doc/src/webenginescript.qdoc
@@ -3,7 +3,7 @@
 
 /*!
     \qmltype WebEngineScript
-    \instantiates QWebEngineScript
+    \nativetype QWebEngineScript
     \brief Enables the programmatic injection of scripts in the JavaScript engine.
     \since QtWebEngine 1.1
     \ingroup qmlvaluetypes
@@ -90,7 +90,7 @@
 */
 
 /*!
-    \qmlproperty int WebEngineScript::runOnSubframes
+    \qmlproperty bool WebEngineScript::runsOnSubFrames
 
     Set this property to \c true if the script is executed on every frame in the page, or \c false
     if it is only ran for the main frame.
diff --git a/src/webenginequick/doc/src/webengineview_lgpl.qdoc b/src/webenginequick/doc/src/webengineview_lgpl.qdoc
index 82bf605ca86..8c6b99dd7ea 100644
--- a/src/webenginequick/doc/src/webengineview_lgpl.qdoc
+++ b/src/webenginequick/doc/src/webengineview_lgpl.qdoc
@@ -8,7 +8,7 @@
 
 /*!
     \qmltype WebEngineView
-    \instantiates QQuickWebEngineView
+    \nativetype QQuickWebEngineView
     \inherits Item
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.0
@@ -111,10 +111,9 @@
     \section2 Platform Features
 
     Web pages can request access to platform features, such as geolocation or audio and video
-    capture devices. The \l featurePermissionRequested() signal is emitted when a web page requests
-    to make use of a resource. The supported platform features are described by the \l Feature
-    property. If users grant the permission, the \l grantFeaturePermission() method is used to set
-    it to \e granted.
+    capture devices. The \l permissionRequested() signal is emitted when a web page requests
+    to make use of a resource. The supported platform features are described by the QWebEnginePermission::Feature
+    property.
 
     \section2 Rendering to OpenGL Surface
 
@@ -445,6 +444,7 @@
 /*!
     \qmlmethod void WebEngineView::grantFeaturePermission(url securityOrigin, Feature feature, bool granted)
     \since QtWebEngine 1.1
+    \deprecated [6.8] Use webEnginePermission.grant() or webEnginePermission.deny() instead.
 
     Sets or unsets the permission, depending on \a granted, for the web site
     identified by \a securityOrigin to use \a feature.
@@ -501,6 +501,7 @@
 /*!
     \qmlsignal WebEngineView::featurePermissionRequested(url securityOrigin, Feature feature)
     \since QtWebEngine 1.1
+    \deprecated [6.8] Use \l permissionRequested() instead.
 
     This signal is emitted when the web site identified by \a securityOrigin requests
     to make use of the resource or device identified by \a feature.
@@ -508,6 +509,15 @@
     \sa grantFeaturePermission()
 */
 
+/*!
+    \qmlsignal WebEngineView::permissionRequested(webEnginePermission permission)
+    \since QtWebEngine 6.8
+
+    This signal is emitted when a web site fires a permission request (e.g. geolocation access,
+    permission to send notifications). The \a permission object can queried for the requesting URL
+    and the \c{WebEnginePermission.PermissionType} it's asking for, as well as to grant or deny permission.
+*/
+
 /*!
     \qmlsignal WebEngineView::loadingChanged(WebEngineLoadingInfo loadingInfo)
 
@@ -844,6 +854,7 @@
 
 /*!
     \qmlproperty enumeration WebEngineView::Feature
+    \deprecated [6.8] Replaced by WebEnginePermission.PermissionType.
 
     Describes the platform feature access categories that the user may be asked to grant or deny
     access to:
@@ -864,9 +875,9 @@
     \value  WebEngineView.Notifications
             Web notifications for the end-user.
     \value  WebEngineView.ClipboardReadWrite
-            Read and write access for the clipboard. If both \l{WebEngineSettings::JavascriptCanPaste}
-            {JavascriptCanPaste} and \l{WebEngineSettings::JavascriptCanAccessClipboard}
-            {JavascriptCanAccessClipboard} settings are enabled, this permission will always be granted
+            Read and write access for the clipboard. If both \l{WebEngineSettings::}
+            {javascriptCanPaste} and \l{WebEngineSettings::}
+            {javascriptCanAccessClipboard} settings are enabled, this permission will always be granted
             automatically and no feature requests will be made.
             (Added in Qt 6.8)
     \value  WebEngineView.LocalFontsAccess
@@ -1449,7 +1460,7 @@
 
 /*!
     \qmltype FindTextResult
-    \instantiates QWebEngineFindTextResult
+    \nativetype QWebEngineFindTextResult
     \inqmlmodule QtWebEngine
     \since QtWebEngine 1.10
 
@@ -1615,6 +1626,17 @@
     \sa QWebEngineWebAuthUxRequest
 */
 
+/*!
+    \qmlsignal WebEngineView::desktopMediaRequested(WebEngineDesktopMediaRequest request)
+    \since QtWebEngine 6.7
+
+    This signal is emitted when a web application requests access to the contents of a display.
+
+    The \a request argument holds references to data models for windows and screens available
+    for capturing. To accept the request, the signal handler can call either
+    WebEngineDesktopMediaRequest::selectScreen() or WebEngineDesktopMediaRequest::selectWindow().
+*/
+
 /*!
     \qmlsignal WebEngineView::zoomFactorChanged(qreal factor);
     \since QtWebEngine 6.8
diff --git a/src/webenginequick/render_widget_host_view_qt_delegate_quickwindow.cpp b/src/webenginequick/render_widget_host_view_qt_delegate_quickwindow.cpp
index 090b0928109..52538b652c1 100644
--- a/src/webenginequick/render_widget_host_view_qt_delegate_quickwindow.cpp
+++ b/src/webenginequick/render_widget_host_view_qt_delegate_quickwindow.cpp
@@ -7,13 +7,32 @@
 
 namespace QtWebEngineCore {
 
+struct ItemTransform {
+    qreal rotation = 0.;
+    qreal scale = 1.;
+};
+
+// Helper function to calculate the cumulative rotation and scale.
+static inline struct ItemTransform getTransformValuesFromItemTree(QQuickItem *item)
+{
+    struct ItemTransform returnValue;
+
+    while (item) {
+        returnValue.rotation += item->rotation();
+        returnValue.scale *= item->scale();
+        item = item->parentItem();
+    }
+
+    return returnValue;
+}
+
 static inline QPoint getOffset(QQuickItem *item)
 {
     // get parent window (scene) offset
     QPointF offset = item->mapFromScene(QPoint(0, 0));
     offset = item->mapToGlobal(offset);
     // get local offset
-    offset -= item->mapToScene(QPoint(0, 0));
+    offset -= item->mapToGlobal(QPoint(0, 0));
     return offset.toPoint();
 }
 
@@ -32,10 +51,11 @@ static inline QPointF transformPoint(const QPointF &point, const QTransform &tra
 
 RenderWidgetHostViewQtDelegateQuickWindow::RenderWidgetHostViewQtDelegateQuickWindow(
         RenderWidgetHostViewQtDelegateItem *realDelegate, QWindow *parent)
-    : QQuickWindow(parent), m_realDelegate(realDelegate), m_virtualParent(nullptr), m_rotated(false)
+    : QQuickWindow(), m_realDelegate(realDelegate), m_virtualParent(nullptr), m_transformed(false)
 {
     setFlags(Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus);
     realDelegate->setParentItem(contentItem());
+    setTransientParent(parent);
 }
 
 RenderWidgetHostViewQtDelegateQuickWindow::~RenderWidgetHostViewQtDelegateQuickWindow()
@@ -56,9 +76,13 @@ void RenderWidgetHostViewQtDelegateQuickWindow::setVirtualParent(QQuickItem *vir
 // chromium knows nothing about local transformation
 void RenderWidgetHostViewQtDelegateQuickWindow::InitAsPopup(const QRect &rect)
 {
-    m_rotated = m_virtualParent->rotation() > 0 || m_virtualParent->parentItem()->rotation() > 0;
-    if (m_rotated) {
-        // code below tries to cover the case where webengine view is rotated,
+    // To decide if there is a scale or rotation, we check it from the transfrom
+    // to also cover the case where the scale is higher up in the item tree.
+    QTransform transform = m_virtualParent->itemTransform(nullptr, nullptr);
+    m_transformed = transform.isRotating() || transform.isScaling();
+
+    if (m_transformed) {
+        // code below tries to cover the case where webengine view is rotated or scaled,
         // the code assumes the rotation is in the form of  90, 180, 270 degrees
         // to archive that we keep chromium unaware of transformation and we transform
         // just the window content.
@@ -67,7 +91,6 @@ void RenderWidgetHostViewQtDelegateQuickWindow::InitAsPopup(const QRect &rect)
         QPointF offset = m_virtualParent->mapFromScene(QPoint(0, 0));
         offset = m_virtualParent->mapToGlobal(offset);
         // get local transform
-        QTransform transform = m_virtualParent->itemTransform(nullptr, nullptr);
         QPointF tl = transformPoint(rect.topLeft(), transform, offset, m_virtualParent);
         QPointF br = transformPoint(rect.bottomRight(), transform, offset, m_virtualParent);
         QRectF popupRect(tl, br);
@@ -80,7 +103,13 @@ void RenderWidgetHostViewQtDelegateQuickWindow::InitAsPopup(const QRect &rect)
         m_realDelegate->setX(-rect.width() / 2.0 + geometry().width() / 2.0);
         m_realDelegate->setY(-rect.height() / 2.0 + geometry().height() / 2.0);
         m_realDelegate->setTransformOrigin(QQuickItem::Center);
-        m_realDelegate->setRotation(m_virtualParent->parentItem()->rotation());
+
+        // We need to read the values for scale and rotation from the item tree as it is not
+        // sufficient to only use the virtual parent item and its parent for the case that the
+        // scale or rotation is applied higher up the item tree.
+        struct ItemTransform transformValues = getTransformValuesFromItemTree(m_virtualParent);
+        m_realDelegate->setRotation(transformValues.rotation);
+        m_realDelegate->setScale(transformValues.scale);
     } else {
         QRect geometry(rect);
         geometry.moveTo(rect.topLeft() - getOffset(m_virtualParent));
@@ -93,13 +122,13 @@ void RenderWidgetHostViewQtDelegateQuickWindow::InitAsPopup(const QRect &rect)
 
 void RenderWidgetHostViewQtDelegateQuickWindow::Resize(int width, int height)
 {
-    if (!m_rotated)
+    if (!m_transformed)
         QQuickWindow::resize(width, height);
 }
 
 void RenderWidgetHostViewQtDelegateQuickWindow::MoveWindow(const QPoint &screenPos)
 {
-    if (!m_rotated)
+    if (!m_transformed)
         QQuickWindow::setPosition(screenPos - getOffset(m_virtualParent));
 }
 
diff --git a/src/webenginequick/render_widget_host_view_qt_delegate_quickwindow_p.h b/src/webenginequick/render_widget_host_view_qt_delegate_quickwindow_p.h
index 3559bd2f080..691fceb5257 100644
--- a/src/webenginequick/render_widget_host_view_qt_delegate_quickwindow_p.h
+++ b/src/webenginequick/render_widget_host_view_qt_delegate_quickwindow_p.h
@@ -49,7 +49,7 @@ class RenderWidgetHostViewQtDelegateQuickWindow : public QQuickWindow , public W
     QPointer<RenderWidgetHostViewQtDelegateItem> m_realDelegate;
     QQuickItem *m_virtualParent;
     QRect m_rect;
-    bool m_rotated;
+    bool m_transformed;
 };
 
 } // namespace QtWebEngineCore
diff --git a/src/webenginequick/ui/AlertDialog.qml b/src/webenginequick/ui/AlertDialog.qml
index e4c17b05621..f149c0e6fc7 100644
--- a/src/webenginequick/ui/AlertDialog.qml
+++ b/src/webenginequick/ui/AlertDialog.qml
@@ -8,29 +8,11 @@ import QtQuick.Layouts
 Dialog {
     property alias text: message.text
     property bool handled: false
-    signal accepted()
-    signal rejected()
     title: qsTr("Alert Dialog")
     modal: false
     anchors.centerIn: parent
     objectName: "alertDialog"
 
-    //handle the case where users simply closes the dialog
-    onVisibleChanged: {
-        if (visible == false && handled == false) {
-            handled = true;
-            rejected();
-        } else {
-            handled = false;
-        }
-    }
-
-    function acceptDialog() {
-        accepted();
-        handled = true;
-        close();
-    }
-
     ColumnLayout {
         id: rootLayout
         anchors.fill: parent
@@ -58,7 +40,7 @@ Dialog {
         Button {
             Layout.alignment: Qt.AlignHCenter
             text: qsTr("OK")
-            onClicked: acceptDialog()
+            onClicked: accept()
         }
     }
 }
diff --git a/src/webenginequick/ui/AuthenticationDialog.qml b/src/webenginequick/ui/AuthenticationDialog.qml
index d0611b84f51..925102f94e0 100644
--- a/src/webenginequick/ui/AuthenticationDialog.qml
+++ b/src/webenginequick/ui/AuthenticationDialog.qml
@@ -8,33 +8,15 @@ import QtQuick.Layouts
 Dialog {
     property alias text: message.text
     property bool handled: false
-    signal accepted(string user, string password)
-    signal rejected()
+    signal credentials(string user, string password)
     title: qsTr("Authentication Required")
     modal: false
     anchors.centerIn: parent
     objectName: "authenticationDialog"
 
-    //handle the case where users simply closes the dialog
-    onVisibleChanged: {
-        if (visible == false && handled == false) {
-            handled = true;
-            rejected();
-        } else {
-            handled = false;
-        }
-    }
-
     function acceptDialog() {
-        accepted(userField.text, passwordField.text);
-        handled = true;
-        close();
-    }
-
-    function rejectDialog() {
-        rejected();
-        handled = true;
-        close();
+        credentials(userField.text, passwordField.text);
+        accept()
     }
 
     ColumnLayout {
@@ -90,7 +72,7 @@ Dialog {
             Button {
                 id: cancelButton
                 text: qsTr("Cancel")
-                onClicked: rejectDialog()
+                onClicked: reject()
             }
             Button {
                 text: qsTr("Log In")
diff --git a/src/webenginequick/ui/ConfirmDialog.qml b/src/webenginequick/ui/ConfirmDialog.qml
index cfffe7c4d54..db7bcfbf1f4 100644
--- a/src/webenginequick/ui/ConfirmDialog.qml
+++ b/src/webenginequick/ui/ConfirmDialog.qml
@@ -8,35 +8,11 @@ import QtQuick.Layouts
 Dialog {
     property alias text: message.text
     property bool handled: false
-    signal accepted()
-    signal rejected()
     title: qsTr("Confirm Dialog")
     modal: false
     anchors.centerIn: parent
     objectName: "confirmDialog"
 
-    //handle the case where users simply closes the dialog
-    onVisibleChanged: {
-        if (visible == false && handled == false) {
-            handled = true;
-            rejected();
-        } else {
-            handled = false;
-        }
-    }
-
-    function acceptDialog() {
-        accepted();
-        handled = true;
-        close();
-    }
-
-    function rejectDialog() {
-        rejected();
-        handled = true;
-        close();
-    }
-
     ColumnLayout {
         id: rootLayout
         anchors.fill: parent
@@ -66,11 +42,11 @@ Dialog {
             spacing: 8
             Button {
                 text: qsTr("OK")
-                onClicked: acceptDialog()
+                onClicked: accept()
             }
             Button {
                 text: qsTr("Cancel")
-                onClicked: rejectDialog()
+                onClicked: reject()
             }
         }
     }
diff --git a/src/webenginequick/ui/PromptDialog.qml b/src/webenginequick/ui/PromptDialog.qml
index 275deace862..027dd5ece32 100644
--- a/src/webenginequick/ui/PromptDialog.qml
+++ b/src/webenginequick/ui/PromptDialog.qml
@@ -10,34 +10,15 @@ Dialog {
     property alias prompt: field.text
     property bool handled: false
     signal input(string text)
-    signal accepted()
-    signal rejected()
     title: qsTr("Prompt Dialog")
     modal: false
     anchors.centerIn: parent
     objectName: "promptDialog"
 
-    //handle the case where users simply closes the dialog
-    onVisibleChanged: {
-        if (visible == false && handled == false) {
-            handled = true;
-            rejected();
-        } else {
-            handled = false;
-        }
-    }
 
     function acceptDialog() {
         input(field.text);
-        accepted();
-        handled = true;
-        close();
-    }
-
-    function rejectDialog() {
-        rejected();
-        handled = true;
-        close();
+        accept();
     }
 
     ColumnLayout {
@@ -72,7 +53,7 @@ Dialog {
             }
             Button {
                 text: qsTr("Cancel")
-                onClicked: rejectDialog()
+                onClicked: reject()
             }
         }
     }
diff --git a/src/webenginequick/ui_delegates_manager.cpp b/src/webenginequick/ui_delegates_manager.cpp
index a4a22fedd7d..22debc0bbbf 100644
--- a/src/webenginequick/ui_delegates_manager.cpp
+++ b/src/webenginequick/ui_delegates_manager.cpp
@@ -111,6 +111,10 @@ UIDelegatesManager::~UIDelegatesManager()
 bool UIDelegatesManager::ensureComponentLoaded(ComponentType type)
 {
     QQmlEngine* engine = qmlEngine(m_view);
+
+    if (!engine)
+        return false;
+
     if (m_importDirs.isEmpty() && !initializeImportDirs(m_importDirs, engine))
         return false;
 
@@ -128,8 +132,6 @@ bool UIDelegatesManager::ensureComponentLoaded(ComponentType type)
 #else // Unconditionally reload the components each time.
     fprintf(stderr, "%s: %s\n", Q_FUNC_INFO, qPrintable(fileName));
 #endif
-    if (!engine)
-        return false;
 
     for (const QString &importDir : std::as_const(m_importDirs)) {
         const QString componentFilePath = importDir % QLatin1Char('/') % fileName;
@@ -153,6 +155,7 @@ bool UIDelegatesManager::ensureComponentLoaded(ComponentType type)
         }
         return true;
     }
+    qWarning("Default ui delegate %s can not be found.", qPrintable(fileName));
     return false;
 }
 
@@ -334,12 +337,17 @@ void UIDelegatesManager::showDialog(QSharedPointer<AuthenticationDialogControlle
 
     QQmlProperty acceptSignal(authenticationDialog, QStringLiteral("onAccepted"));
     QQmlProperty rejectSignal(authenticationDialog, QStringLiteral("onRejected"));
+    QQmlProperty credentialsSignal(authenticationDialog, QStringLiteral("onCredentials"));
     CHECK_QML_SIGNAL_PROPERTY(acceptSignal, authenticationDialogComponent->url());
     CHECK_QML_SIGNAL_PROPERTY(rejectSignal, authenticationDialogComponent->url());
 
-    static int acceptIndex = dialogController->metaObject()->indexOfSlot("accept(QString,QString)");
+    static int acceptIndex = dialogController->metaObject()->indexOfSlot("accept()");
+    static int credentialsIndex =
+            dialogController->metaObject()->indexOfSlot("credentials(QString,QString)");
     static int deleteLaterIndex = authenticationDialog->metaObject()->indexOfSlot("deleteLater()");
     QObject::connect(authenticationDialog, acceptSignal.method(), dialogController.data(), dialogController->metaObject()->method(acceptIndex));
+    QObject::connect(authenticationDialog, credentialsSignal.method(), dialogController.data(),
+                     dialogController->metaObject()->method(credentialsIndex));
     QObject::connect(authenticationDialog, acceptSignal.method(), authenticationDialog, authenticationDialog->metaObject()->method(deleteLaterIndex));
     static int rejectIndex = dialogController->metaObject()->indexOfSlot("reject()");
     QObject::connect(authenticationDialog, rejectSignal.method(), dialogController.data(), dialogController->metaObject()->method(rejectIndex));
diff --git a/src/webenginewidgets/api/qwebengineview.cpp b/src/webenginewidgets/api/qwebengineview.cpp
index e72ad8fa96f..4a96a92fce2 100644
--- a/src/webenginewidgets/api/qwebengineview.cpp
+++ b/src/webenginewidgets/api/qwebengineview.cpp
@@ -141,6 +141,11 @@ class WebEngineQuickWidget : public QQuickWidget, public WidgetDelegate
     void Destroy() override
     {
         deleteLater();
+
+        // The event loop may be exited at this point.
+        // Ensure deferred deletion in this scenario.
+        if (QThread::currentThread()->loopLevel() == 0)
+            QCoreApplication::sendPostedEvents(this, QEvent::DeferredDelete);
     }
 
     bool ActiveFocusOnPress() override
@@ -194,6 +199,11 @@ class WebEngineQuickWidget : public QQuickWidget, public WidgetDelegate
             qApp->notify(parentWidget, ev);
         }
     }
+    void SetCursor(const QCursor &cursor) override
+    {
+        if (auto parentWidget = QQuickWidget::parentWidget())
+            parentWidget->setCursor(cursor);
+    }
 
 protected:
     void closeEvent(QCloseEvent *event) override
@@ -429,6 +439,7 @@ void QWebEngineViewPrivate::widgetChanged(QtWebEngineCore::WebEngineQuickWidget
 {
     Q_Q(QWebEngineView);
 
+    bool hasFocus = oldWidget ? oldWidget->hasFocus() : false;
     if (oldWidget) {
         q->layout()->removeWidget(oldWidget);
         oldWidget->hide();
@@ -447,7 +458,7 @@ void QWebEngineViewPrivate::widgetChanged(QtWebEngineCore::WebEngineQuickWidget
 #endif
         q->layout()->addWidget(newWidget);
         q->setFocusProxy(newWidget);
-        if (oldWidget && oldWidget == QApplication::focusWidget())
+        if (hasFocus)
             newWidget->setFocus();
         newWidget->show();
     }
@@ -1500,7 +1511,8 @@ void QWebEngineView::print(QPrinter *printer)
     };
     dPage->adapter->printToPDFCallbackResult(std::move(callback), printer->pageLayout(),
                                              printer->pageRanges(),
-                                             printer->colorMode() == QPrinter::Color, false);
+                                             printer->colorMode() == QPrinter::Color, false,
+                                             QtWebEngineCore::WebContentsAdapter::kUseMainFrameId);
 #else
     Q_UNUSED(printer);
     Q_EMIT printFinished(false);
diff --git a/src/webenginewidgets/doc/snippets/push-notifications/commands b/src/webenginewidgets/doc/snippets/push-notifications/commands
index aee9761c10f..077538e2e55 100644
--- a/src/webenginewidgets/doc/snippets/push-notifications/commands
+++ b/src/webenginewidgets/doc/snippets/push-notifications/commands
@@ -11,7 +11,7 @@ npm install web-push express
 //! [1]
 
 //! [2]
-./node_odules/.bin/web-push generate-vapid-keys
+./node_modules/.bin/web-push generate-vapid-keys
 //! [2]
 
 //! [3]
diff --git a/src/webenginewidgets/doc/src/qtwebkitportingguide.qdoc b/src/webenginewidgets/doc/src/qtwebkitportingguide.qdoc
index 37b73c010cc..f3ac9fa555f 100644
--- a/src/webenginewidgets/doc/src/qtwebkitportingguide.qdoc
+++ b/src/webenginewidgets/doc/src/qtwebkitportingguide.qdoc
@@ -92,13 +92,13 @@
     \b {Qt \WebKit}
     \code
         QWebPage page;
-        connect(page.mainFrame(), SIGNAL(urlChanged(const QUrl&)), SLOT(mySlotName()));
+        connect(page.mainFrame(), SIGNAL(urlChanged(QUrl)), SLOT(mySlotName()));
         page.mainFrame()->load(url);
     \endcode
     \b {\QWE}
     \code
         QWebEnginePage page;
-        connect(&page, SIGNAL(urlChanged(const QUrl&)), SLOT(mySlotName()));
+        connect(&page, SIGNAL(urlChanged(QUrl)), SLOT(mySlotName()));
         page.load(url);
     \endcode
 
diff --git a/tests/auto/REUSE.toml b/tests/auto/REUSE.toml
new file mode 100644
index 00000000000..2a40d95a96e
--- /dev/null
+++ b/tests/auto/REUSE.toml
@@ -0,0 +1,13 @@
+version = 1
+
+[[annotations]]
+path = ["core/qwebengineframe/tst_qwebengineframe.cpp",
+        "core/qwebenginesettings/tst_qwebenginesettings.cpp",
+        "widgets/accessibility/tst_accessibility.cpp",
+        "widgets/qwebenginehistory/tst_qwebenginehistory.cpp",
+        "widgets/qwebenginepage/tst_qwebenginepage.cpp",
+        "widgets/qwebenginescript/tst_qwebenginescript.cpp",
+        "widgets/qwebengineview/tst_qwebengineview.cpp"]
+precedence = "aggregate"
+comment = "the license is not reuse readable"
+SPDX-License-Identifier = "LGPL-2.0-or-later"
diff --git a/tests/auto/core/certificateerror/tst_certificateerror.cpp b/tests/auto/core/certificateerror/tst_certificateerror.cpp
index 61201e250bf..25e5b89f52e 100644
--- a/tests/auto/core/certificateerror/tst_certificateerror.cpp
+++ b/tests/auto/core/certificateerror/tst_certificateerror.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 #include <httpsserver.h>
 #include <util.h>
 
@@ -9,6 +9,9 @@
 
 #include <QtTest/QtTest>
 
+#include <QtCore/qoperatingsystemversion.h>
+#include <QtCore/qsystemdetection.h>
+
 class tst_CertificateError : public QObject
 {
     Q_OBJECT
@@ -68,6 +71,16 @@ void tst_CertificateError::handleError_data()
 
 void tst_CertificateError::handleError()
 {
+#ifdef Q_OS_MACOS
+#if !QT_MACOS_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(150000, 180000)
+    if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSSequoia
+        && QSslSocket::activeBackend() == QLatin1String("securetransport")) {
+        // Built with SDK < 15, with file-based keychains that no longer work on macOS >= 15.
+        QSKIP("SecureTransport will block the test server while accessing the login keychain");
+    }
+#endif
+#endif // Q_OS_MACOS
+
     HttpsServer server(":/resources/server.pem", ":/resources/server.key", "");
     server.setExpectError(false);
     QVERIFY(server.start());
diff --git a/tests/auto/core/devtools/tst_devtools.cpp b/tests/auto/core/devtools/tst_devtools.cpp
index 57a2b83a357..7284f0d51e5 100644
--- a/tests/auto/core/devtools/tst_devtools.cpp
+++ b/tests/auto/core/devtools/tst_devtools.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QtTest/QtTest>
 
diff --git a/tests/auto/core/getdomainandregistry/tst_getdomainandregistry.cpp b/tests/auto/core/getdomainandregistry/tst_getdomainandregistry.cpp
index e9e0bf105d0..f5b8910e8d2 100644
--- a/tests/auto/core/getdomainandregistry/tst_getdomainandregistry.cpp
+++ b/tests/auto/core/getdomainandregistry/tst_getdomainandregistry.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QtTest/QtTest>
 #include <QtWebEngineCore/qtwebenginecoreglobal.h>
diff --git a/tests/auto/core/origins/tst_origins.cpp b/tests/auto/core/origins/tst_origins.cpp
index 81385701f74..06af1123e2a 100644
--- a/tests/auto/core/origins/tst_origins.cpp
+++ b/tests/auto/core/origins/tst_origins.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <util.h>
 #include "httpserver.h"
diff --git a/tests/auto/core/qtversion/tst_qtversion.cpp b/tests/auto/core/qtversion/tst_qtversion.cpp
index 44c2d4e5c90..66df04412c4 100644
--- a/tests/auto/core/qtversion/tst_qtversion.cpp
+++ b/tests/auto/core/qtversion/tst_qtversion.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QtTest/QtTest>
 #include <QtWebEngineCore/qwebenginepage.h>
diff --git a/tests/auto/core/qwebengineclientcertificatestore/tst_qwebengineclientcertificatestore.cpp b/tests/auto/core/qwebengineclientcertificatestore/tst_qwebengineclientcertificatestore.cpp
index 7d82a56405f..42c394a847b 100644
--- a/tests/auto/core/qwebengineclientcertificatestore/tst_qwebengineclientcertificatestore.cpp
+++ b/tests/auto/core/qwebengineclientcertificatestore/tst_qwebengineclientcertificatestore.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <httpsserver.h>
 #include <util.h>
@@ -10,6 +10,9 @@
 #include <QtWebEngineCore/qwebenginecertificateerror.h>
 #include <QtWebEngineCore/qwebenginesettings.h>
 
+#include <QtCore/qoperatingsystemversion.h>
+#include <QtCore/qsystemdetection.h>
+
 class tst_QWebEngineClientCertificateStore : public QObject
 {
     Q_OBJECT
@@ -52,22 +55,22 @@ void tst_QWebEngineClientCertificateStore::addAndListCertificates()
 {
     // Load QSslCertificate
     QFile certFile(":/resources/certificate.crt");
-    certFile.open(QIODevice::ReadOnly);
+    QVERIFY2(certFile.open(QIODevice::ReadOnly), qPrintable(certFile.errorString()));
     const QSslCertificate cert(certFile.readAll(), QSsl::Pem);
 
     // Load QSslKey
     QFile keyFile(":/resources/privatekey.key");
-    keyFile.open(QIODevice::ReadOnly);
+    QVERIFY2(keyFile.open(QIODevice::ReadOnly), qPrintable(keyFile.errorString()));
     const QSslKey sslKey(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, "");
 
     // Load second QSslCertificate
     QFile certFileSecond(":/resources/certificate1.crt");
-    certFileSecond.open(QIODevice::ReadOnly);
+    QVERIFY2(certFileSecond.open(QIODevice::ReadOnly), qPrintable(certFileSecond.errorString()));
     const QSslCertificate certSecond(certFileSecond.readAll(), QSsl::Pem);
 
     // Load second QSslKey
     QFile keyFileSecond(":/resources/privatekey1.key");
-    keyFileSecond.open(QIODevice::ReadOnly);
+    QVERIFY2(keyFileSecond.open(QIODevice::ReadOnly), qPrintable(keyFileSecond.errorString()));
     const QSslKey sslKeySecond(keyFileSecond.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, "");
 
     // Add certificates to in-memory store
@@ -115,6 +118,16 @@ void tst_QWebEngineClientCertificateStore::clientAuthentication()
     QFETCH(bool, in_memory);
     QFETCH(bool, add_more_in_memory_certificates);
 
+#ifdef Q_OS_MACOS
+#if !QT_MACOS_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(150000, 180000)
+    if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSSequoia
+        && QSslSocket::activeBackend() == QLatin1String("securetransport")) {
+        // Built with SDK < 15, with file-based keychains that no longer work on macOS >= 15.
+        QSKIP("SecureTransport will block the test server while accessing the login keychain");
+    }
+#endif
+#endif // Q_OS_MACOS
+
     HttpsServer server(":/resources/server.pem", ":/resources/server.key", ":resources/ca.pem");
     server.setExpectError(false);
     QVERIFY(server.start());
@@ -125,11 +138,11 @@ void tst_QWebEngineClientCertificateStore::clientAuthentication()
     });
 
     QFile certFile(client_certificate);
-    certFile.open(QIODevice::ReadOnly);
+    QVERIFY2(certFile.open(QIODevice::ReadOnly), qPrintable(certFile.errorString()));
     const QSslCertificate cert(certFile.readAll(), QSsl::Pem);
 
     QFile keyFile(client_key);
-    keyFile.open(QIODevice::ReadOnly);
+    QVERIFY2(keyFile.open(QIODevice::ReadOnly), qPrintable(keyFile.errorString()));
     const QSslKey sslKey(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, "");
 
     if (in_memory)
diff --git a/tests/auto/core/qwebenginecookiestore/tst_qwebenginecookiestore.cpp b/tests/auto/core/qwebenginecookiestore/tst_qwebenginecookiestore.cpp
index 3fff2cd459c..e0db542c827 100644
--- a/tests/auto/core/qwebenginecookiestore/tst_qwebenginecookiestore.cpp
+++ b/tests/auto/core/qwebenginecookiestore/tst_qwebenginecookiestore.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <util.h>
 #include <QtTest/QtTest>
@@ -79,8 +79,8 @@ void tst_QWebEngineCookieStore::cookieSignals()
     QWebEngineCookieStore *client = m_profile->cookieStore();
 
     QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
-    QSignalSpy cookieAddedSpy(client, SIGNAL(cookieAdded(const QNetworkCookie &)));
-    QSignalSpy cookieRemovedSpy(client, SIGNAL(cookieRemoved(const QNetworkCookie &)));
+    QSignalSpy cookieAddedSpy(client, SIGNAL(cookieAdded(QNetworkCookie)));
+    QSignalSpy cookieRemovedSpy(client, SIGNAL(cookieRemoved(QNetworkCookie)));
 
     page.load(QUrl("qrc:///resources/index.html"));
 
@@ -108,8 +108,8 @@ void tst_QWebEngineCookieStore::setAndDeleteCookie()
     QWebEngineCookieStore *client = m_profile->cookieStore();
 
     QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
-    QSignalSpy cookieAddedSpy(client, SIGNAL(cookieAdded(const QNetworkCookie &)));
-    QSignalSpy cookieRemovedSpy(client, SIGNAL(cookieRemoved(const QNetworkCookie &)));
+    QSignalSpy cookieAddedSpy(client, SIGNAL(cookieAdded(QNetworkCookie)));
+    QSignalSpy cookieRemovedSpy(client, SIGNAL(cookieRemoved(QNetworkCookie)));
 
     QNetworkCookie cookie1(QNetworkCookie::parseCookies(QByteArrayLiteral("khaos=I9GX8CWI; Domain=.example.com; Path=/docs")).first());
     QNetworkCookie cookie2(QNetworkCookie::parseCookies(QByteArrayLiteral("Test%20Cookie=foobar; domain=example.com; Path=/")).first());
@@ -155,8 +155,8 @@ void tst_QWebEngineCookieStore::setInvalidCookie()
     QWebEngineCookieStore *client = m_profile->cookieStore();
 
     QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
-    QSignalSpy cookieAddedSpy(client, SIGNAL(cookieAdded(const QNetworkCookie &)));
-    QSignalSpy cookieRemovedSpy(client, SIGNAL(cookieRemoved(const QNetworkCookie &)));
+    QSignalSpy cookieAddedSpy(client, SIGNAL(cookieAdded(QNetworkCookie)));
+    QSignalSpy cookieRemovedSpy(client, SIGNAL(cookieRemoved(QNetworkCookie)));
 
     QNetworkCookie goodCookie(
             QNetworkCookie::parseCookies(
@@ -186,8 +186,8 @@ void tst_QWebEngineCookieStore::batchCookieTasks()
     QWebEngineCookieStore *client = m_profile->cookieStore();
 
     QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
-    QSignalSpy cookieAddedSpy(client, SIGNAL(cookieAdded(const QNetworkCookie &)));
-    QSignalSpy cookieRemovedSpy(client, SIGNAL(cookieRemoved(const QNetworkCookie &)));
+    QSignalSpy cookieAddedSpy(client, SIGNAL(cookieAdded(QNetworkCookie)));
+    QSignalSpy cookieRemovedSpy(client, SIGNAL(cookieRemoved(QNetworkCookie)));
 
     QNetworkCookie cookie1(QNetworkCookie::parseCookies(QByteArrayLiteral("khaos=I9GX8CWI; Domain=.example.com; Path=/docs")).first());
     QNetworkCookie cookie2(QNetworkCookie::parseCookies(QByteArrayLiteral("Test%20Cookie=foobar; domain=example.com; Path=/")).first());
@@ -230,8 +230,8 @@ void tst_QWebEngineCookieStore::basicFilter()
     client->setCookieFilter([&](const QWebEngineCookieStore::FilterRequest &){ ++accessTested; return true;});
 
     QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
-    QSignalSpy cookieAddedSpy(client, SIGNAL(cookieAdded(const QNetworkCookie &)));
-    QSignalSpy cookieRemovedSpy(client, SIGNAL(cookieRemoved(const QNetworkCookie &)));
+    QSignalSpy cookieAddedSpy(client, SIGNAL(cookieAdded(QNetworkCookie)));
+    QSignalSpy cookieRemovedSpy(client, SIGNAL(cookieRemoved(QNetworkCookie)));
 
     page.load(QUrl("qrc:///resources/index.html"));
 
@@ -284,9 +284,9 @@ void tst_QWebEngineCookieStore::basicFilterOverHTTP()
     });
 
     QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
-    QSignalSpy cookieAddedSpy(client, SIGNAL(cookieAdded(const QNetworkCookie &)));
-    QSignalSpy cookieRemovedSpy(client, SIGNAL(cookieRemoved(const QNetworkCookie &)));
-    QSignalSpy serverSpy(&httpServer, SIGNAL(newRequest(HttpReqRep *)));
+    QSignalSpy cookieAddedSpy(client, SIGNAL(cookieAdded(QNetworkCookie)));
+    QSignalSpy cookieRemovedSpy(client, SIGNAL(cookieRemoved(QNetworkCookie)));
+    QSignalSpy serverSpy(&httpServer, SIGNAL(newRequest(HttpReqRep*)));
 
     QUrl firstPartyUrl = httpServer.url("/test.html");
     page.load(firstPartyUrl);
diff --git a/tests/auto/core/qwebengineframe/CMakeLists.txt b/tests/auto/core/qwebengineframe/CMakeLists.txt
index d02b4307d2c..0f4d24289ed 100644
--- a/tests/auto/core/qwebengineframe/CMakeLists.txt
+++ b/tests/auto/core/qwebengineframe/CMakeLists.txt
@@ -8,6 +8,7 @@ qt_internal_add_test(tst_qwebengineframe
         tst_qwebengineframe.cpp
     LIBRARIES
         Qt::WebEngineCore
+        Qt::WebEngineCorePrivate
         Qt::WebEngineWidgets
         Test::Util
 )
@@ -19,4 +20,6 @@ qt_internal_add_resource(tst_qwebengineframe "tst_qwebengineframe"
         "resources/frameset.html"
         "resources/iframes.html"
         "resources/nesting-iframe.html"
+        "resources/printing-inner-document.html"
+        "resources/printing-outer-document.html"
 )
diff --git a/tests/auto/core/qwebengineframe/resources/printing-inner-document.html b/tests/auto/core/qwebengineframe/resources/printing-inner-document.html
new file mode 100644
index 00000000000..2e5a53af5e0
--- /dev/null
+++ b/tests/auto/core/qwebengineframe/resources/printing-inner-document.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<html>
+    <head><title>Printing Inner Document</title></head>
+    <body>
+        <h1>Inner Header</h1>
+    </body>
+</html>
diff --git a/tests/auto/core/qwebengineframe/resources/printing-outer-document.html b/tests/auto/core/qwebengineframe/resources/printing-outer-document.html
new file mode 100644
index 00000000000..c5947371e2d
--- /dev/null
+++ b/tests/auto/core/qwebengineframe/resources/printing-outer-document.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<html>
+    <head><title>Printing Outer Document</title></head>
+    <body>
+        <h1>Outer Header</h1>
+        <iframe name="inner" src="printing-inner-document.html"></iframe>
+    </body>
+</html>
diff --git a/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp b/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp
index ce0b61ee27b..fdfefea20c5 100644
--- a/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp
+++ b/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp
@@ -22,6 +22,7 @@
 #include <QtTest/QtTest>
 
 #include <QtWebEngineCore/qwebengineframe.h>
+#include <QtWebEngineCore/private/qtwebenginecore-config_p.h>
 
 class tst_QWebEngineFrame : public QObject
 {
@@ -37,8 +38,14 @@ private Q_SLOTS:
     void childrenOfInvalidFrame();
     void url();
     void size();
+    void isMainFrame();
     void runJavaScript();
+#if QT_CONFIG(webengine_printing_and_pdf)
     void printRequestedByFrame();
+    void printToPdfFile();
+    void printToPdfFileFailures();
+    void printToPdfFunction();
+#endif
 
 private:
 };
@@ -177,6 +184,19 @@ void tst_QWebEngineFrame::size()
     QCOMPARE(frame1.size(), QSizeF());
 }
 
+void tst_QWebEngineFrame::isMainFrame()
+{
+    QWebEnginePage page;
+    QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) };
+    page.load(QUrl("qrc:/resources/frameset.html"));
+    QTRY_COMPARE(loadSpy.size(), 1);
+    auto frame = page.mainFrame();
+    QVERIFY(frame.isMainFrame());
+    for (auto child : frame.children()) {
+        QVERIFY(!child.isMainFrame());
+    }
+}
+
 void tst_QWebEngineFrame::runJavaScript()
 {
     QWebEnginePage page;
@@ -190,6 +210,7 @@ void tst_QWebEngineFrame::runJavaScript()
     QCOMPARE(result, QString("test-subframe0"));
 }
 
+#if QT_CONFIG(webengine_printing_and_pdf)
 void tst_QWebEngineFrame::printRequestedByFrame()
 {
     QWebEnginePage page;
@@ -208,6 +229,123 @@ void tst_QWebEngineFrame::printRequestedByFrame()
     QCOMPARE(*framePtr, *oFrame2);
 }
 
+void tst_QWebEngineFrame::printToPdfFile()
+{
+    QTemporaryDir tempDir(QDir::tempPath() + "/tst_qwebengineframe-XXXXXX");
+    QVERIFY(tempDir.isValid());
+
+    QWebEnginePage page;
+    QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) };
+    page.load(QUrl("qrc:/resources/printing-outer-document.html"));
+    QTRY_COMPARE(loadSpy.size(), 1);
+
+    auto outerFrame = page.mainFrame();
+    auto maybeInnerFrame = page.findFrameByName("inner");
+    QVERIFY(maybeInnerFrame);
+    auto innerFrame = *maybeInnerFrame;
+
+    QSignalSpy savePdfSpy{ &page, SIGNAL(pdfPrintingFinished(QString,bool)) };
+
+    QString outerPath = tempDir.path() + "/outer.pdf";
+    outerFrame.printToPdf(outerPath);
+    QTRY_COMPARE(savePdfSpy.size(), 1);
+
+    QList<QVariant> outerArgs = savePdfSpy.takeFirst();
+    QCOMPARE(outerArgs.at(0).toString(), outerPath);
+    QVERIFY(outerArgs.at(1).toBool());
+
+    QString innerPath = tempDir.path() + "/inner.pdf";
+    innerFrame.printToPdf(innerPath);
+    QTRY_COMPARE(savePdfSpy.size(), 1);
+
+    QList<QVariant> innerArgs = savePdfSpy.takeFirst();
+    QCOMPARE(innerArgs.at(0).toString(), innerPath);
+    QVERIFY(innerArgs.at(1).toBool());
+
+    // The outer document encompasses more elements so its PDF should be larger. This is a
+    // roundabout way to check that we aren't just printing the same document twice.
+    auto outerSize = QFileInfo(outerPath).size();
+    auto innerSize = QFileInfo(innerPath).size();
+    QCOMPARE_GT(outerSize, innerSize);
+    QCOMPARE_GT(innerSize, 0);
+}
+
+void tst_QWebEngineFrame::printToPdfFileFailures()
+{
+    QTemporaryDir tempDir(QDir::tempPath() + "/tst_qwebengineframe-XXXXXX");
+    QVERIFY(tempDir.isValid());
+
+    QWebEnginePage page;
+    QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) };
+    page.load(QUrl("qrc:/resources/printing-outer-document.html"));
+    QTRY_COMPARE(loadSpy.size(), 1);
+
+    auto maybeInnerFrame = page.findFrameByName("inner");
+    QVERIFY(maybeInnerFrame);
+    auto innerFrame = *maybeInnerFrame;
+
+    QSignalSpy savePdfSpy{ &page, SIGNAL(pdfPrintingFinished(QString,bool)) };
+
+#if !defined(Q_OS_WIN)
+    auto badPath = tempDir.path() + "/print_//2_failed.pdf";
+#else
+    auto badPath = tempDir.path() + "/print_|2_failed.pdf";
+#endif
+    innerFrame.printToPdf(badPath);
+    QTRY_COMPARE(savePdfSpy.size(), 1);
+
+    QList<QVariant> badPathArgs = savePdfSpy.takeFirst();
+    QCOMPARE(badPathArgs.at(0).toString(), badPath);
+    QVERIFY(!badPathArgs.at(1).toBool());
+
+    page.triggerAction(QWebEnginePage::WebAction::Reload);
+    QTRY_COMPARE(loadSpy.size(), 2);
+
+    QVERIFY(!innerFrame.isValid());
+    QString invalidFramePath = tempDir.path() + "/invalidFrame.pdf";
+    innerFrame.printToPdf(invalidFramePath);
+    QTRY_COMPARE(savePdfSpy.size(), 1);
+
+    QList<QVariant> invalidFrameArgs = savePdfSpy.takeFirst();
+    QCOMPARE(invalidFrameArgs.at(0).toString(), invalidFramePath);
+    QVERIFY(!invalidFrameArgs.at(1).toBool());
+}
+
+void tst_QWebEngineFrame::printToPdfFunction()
+{
+    QWebEnginePage page;
+    QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) };
+    page.load(QUrl("qrc:/resources/printing-outer-document.html"));
+    QTRY_COMPARE(loadSpy.size(), 1);
+
+    auto outerFrame = page.mainFrame();
+    auto maybeInnerFrame = page.findFrameByName("inner");
+    QVERIFY(maybeInnerFrame);
+    auto innerFrame = *maybeInnerFrame;
+
+    CallbackSpy<QByteArray> outerSpy;
+    outerFrame.printToPdf(outerSpy.ref());
+    auto outerPdfData = outerSpy.waitForResult();
+    QCOMPARE_GT(outerPdfData.size(), 0);
+
+    CallbackSpy<QByteArray> innerSpy;
+    innerFrame.printToPdf(innerSpy.ref());
+    auto innerPdfData = innerSpy.waitForResult();
+    QCOMPARE_GT(innerPdfData.size(), 0);
+    QCOMPARE_GT(outerPdfData.size(), innerPdfData.size());
+
+    page.triggerAction(QWebEnginePage::WebAction::Reload);
+    QTRY_COMPARE(loadSpy.size(), 2);
+    QVERIFY(!innerFrame.isValid());
+
+    CallbackSpy<QByteArray> invalidSpy;
+    innerFrame.printToPdf(invalidSpy.ref());
+    auto invalidPdfData = invalidSpy.waitForResult();
+    QVERIFY(invalidSpy.wasCalled());
+    QCOMPARE(invalidPdfData.size(), 0);
+}
+#endif
+
 QTEST_MAIN(tst_QWebEngineFrame)
 
 #include "tst_qwebengineframe.moc"
diff --git a/tests/auto/core/qwebengineglobalsettings/tst_qwebengineglobalsettings.cpp b/tests/auto/core/qwebengineglobalsettings/tst_qwebengineglobalsettings.cpp
index 5b6b64778d2..c7e9df51a7f 100644
--- a/tests/auto/core/qwebengineglobalsettings/tst_qwebengineglobalsettings.cpp
+++ b/tests/auto/core/qwebengineglobalsettings/tst_qwebengineglobalsettings.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QtTest>
 #include <widgetutil.h>
@@ -32,8 +32,6 @@ private Q_SLOTS:
     void dnsOverHttps();
 };
 
-Q_LOGGING_CATEGORY(lc, "qt.webengine.tests")
-
 void tst_QWebEngineGlobalSettings::dnsOverHttps_data()
 {
     QTest::addColumn<QWebEngineGlobalSettings::SecureDnsMode>("dnsMode");
diff --git a/tests/auto/core/qwebengineloadinginfo/tst_qwebengineloadinginfo.cpp b/tests/auto/core/qwebengineloadinginfo/tst_qwebengineloadinginfo.cpp
index ccae7436bdd..010932ea093 100644
--- a/tests/auto/core/qwebengineloadinginfo/tst_qwebengineloadinginfo.cpp
+++ b/tests/auto/core/qwebengineloadinginfo/tst_qwebengineloadinginfo.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QtTest/QtTest>
 #include <QtWebEngineCore/qwebengineprofile.h>
diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp
index 7cea14c0cc9..e366bab969e 100644
--- a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp
+++ b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp
@@ -1,7 +1,5 @@
 // Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <util.h>
 #include <QtTest/QtTest>
@@ -159,7 +157,7 @@ class TestRequestInterceptor : public QWebEngineUrlRequestInterceptor
     {
         QList<RequestInfo> infos;
 
-        foreach (auto requestInfo, requestInfos) {
+        for (const auto &requestInfo : requestInfos) {
             if (shouldSkipRequest(requestInfo))
                 continue;
 
@@ -172,7 +170,7 @@ class TestRequestInterceptor : public QWebEngineUrlRequestInterceptor
 
     bool hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceType type)
     {
-        foreach (auto requestInfo, requestInfos) {
+        for (const auto &requestInfo : requestInfos) {
             if (shouldSkipRequest(requestInfo))
                 continue;
 
@@ -572,43 +570,43 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrlHttp()
     // Stylesheet
     QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeStylesheet));
     infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeStylesheet);
-    foreach (auto info, infos)
+    for (const RequestInfo &info : std::as_const(infos))
         QCOMPARE(info.firstPartyUrl, firstPartyUrl);
 
     // Script
     QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeScript));
     infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeScript);
-    foreach (auto info, infos)
+    for (const RequestInfo &info : std::as_const(infos))
         QCOMPARE(info.firstPartyUrl, firstPartyUrl);
 
     // Image
     QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeImage));
     infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeImage);
-    foreach (auto info, infos)
+    for (const RequestInfo &info : std::as_const(infos))
         QCOMPARE(info.firstPartyUrl, firstPartyUrl);
 
     // FontResource
     QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeFontResource));
     infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeFontResource);
-    foreach (auto info, infos)
+    for (const RequestInfo &info : std::as_const(infos))
         QCOMPARE(info.firstPartyUrl, firstPartyUrl);
 
     // Media
     QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeMedia));
     infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeMedia);
-    foreach (auto info, infos)
+    for (const RequestInfo &info : std::as_const(infos))
         QCOMPARE(info.firstPartyUrl, firstPartyUrl);
 
     // Favicon
     QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeFavicon));
     infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeFavicon);
-    foreach (auto info, infos)
+    for (const RequestInfo &info : std::as_const(infos))
         QCOMPARE(info.firstPartyUrl, firstPartyUrl);
 
     // XMLHttpRequest
     QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeXhr));
     infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeXhr);
-    foreach (auto info, infos)
+    for (const RequestInfo &info : std::as_const(infos))
         QCOMPARE(info.firstPartyUrl, firstPartyUrl);
 }
 
@@ -724,43 +722,43 @@ void tst_QWebEngineUrlRequestInterceptor::initiator()
     // Stylesheet
     QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeStylesheet));
     infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeStylesheet);
-    foreach (auto info, infos)
+    for (const RequestInfo &info : std::as_const(infos))
         QVERIFY(interceptor.requestInitiatorUrls[info.requestUrl].contains(info.initiator));
 
     // Script
     QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeScript));
     infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeScript);
-    foreach (auto info, infos)
+    for (const RequestInfo &info : std::as_const(infos))
         QVERIFY(interceptor.requestInitiatorUrls[info.requestUrl].contains(info.initiator));
 
     // Image
     QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeImage));
     infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeImage);
-    foreach (auto info, infos)
+    for (const RequestInfo &info : std::as_const(infos))
         QVERIFY(interceptor.requestInitiatorUrls[info.requestUrl].contains(info.initiator));
 
     // FontResource
     QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeFontResource));
     infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeFontResource);
-    foreach (auto info, infos)
+    for (const RequestInfo &info : std::as_const(infos))
         QVERIFY(interceptor.requestInitiatorUrls[info.requestUrl].contains(info.initiator));
 
     // Media
     QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeMedia));
     infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeMedia);
-    foreach (auto info, infos)
+    for (const RequestInfo &info : std::as_const(infos))
         QVERIFY(interceptor.requestInitiatorUrls[info.requestUrl].contains(info.initiator));
 
     // Favicon
     QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeFavicon));
     infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeFavicon);
-    foreach (auto info, infos)
+    for (const RequestInfo &info : std::as_const(infos))
         QVERIFY(interceptor.requestInitiatorUrls[info.requestUrl].contains(info.initiator));
 
     // XMLHttpRequest
     QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeXhr));
     infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeXhr);
-    foreach (auto info, infos)
+    for (const RequestInfo &info : std::as_const(infos))
         QVERIFY(interceptor.requestInitiatorUrls[info.requestUrl].contains(info.initiator));
 }
 
@@ -793,7 +791,7 @@ void tst_QWebEngineUrlRequestInterceptor::jsServiceWorker()
     // Service Worker
     QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeServiceWorker));
     infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeServiceWorker);
-    foreach (auto info, infos)
+    for (const RequestInfo &info : std::as_const(infos))
         QCOMPARE(info.firstPartyUrl, firstPartyUrl);
 
     QVERIFY(server.stop());
@@ -802,17 +800,17 @@ void tst_QWebEngineUrlRequestInterceptor::jsServiceWorker()
 void tst_QWebEngineUrlRequestInterceptor::replaceInterceptor_data()
 {
     QTest::addColumn<bool>("firstInterceptIsInPage");
-    QTest::addColumn<bool>("keepInterceptionPoint");
+    QTest::addColumn<bool>("secondInterceptIsInPage");
     QTest::newRow("page")         << true << true;
     QTest::newRow("page-profile") << true << false;
-    QTest::newRow("profile")      << false << true;
-    QTest::newRow("profile-page") << false << false;
+    QTest::newRow("profile")      << false << false;
+    QTest::newRow("profile-page") << false << true;
 }
 
 void tst_QWebEngineUrlRequestInterceptor::replaceInterceptor()
 {
     QFETCH(bool, firstInterceptIsInPage);
-    QFETCH(bool, keepInterceptionPoint);
+    QFETCH(bool, secondInterceptIsInPage);
 
     HttpServer server;
     server.setResourceDirs({ ":/resources" });
@@ -837,17 +835,14 @@ void tst_QWebEngineUrlRequestInterceptor::replaceInterceptor()
         requestsOnReplace.push_back(interceptors[currentInterceptorIndex].requestInfos.size());
 
         bool isFirstReinstall = currentInterceptorIndex == 0;
-        bool interceptInPage = keepInterceptionPoint ? firstInterceptIsInPage : (isFirstReinstall ^ firstInterceptIsInPage);
+        bool interceptInPage = isFirstReinstall ? firstInterceptIsInPage : secondInterceptIsInPage;
         setInterceptor(&interceptors[++currentInterceptorIndex], interceptInPage);
-        if (!keepInterceptionPoint)
-            setInterceptor(nullptr, !interceptInPage);
+        setInterceptor(nullptr, !interceptInPage);
 
         if (isFirstReinstall) {
             page.triggerAction(QWebEnginePage::Reload);
         } else {
-            page.runJavaScript("fetch('http://nonexistent.invalid').catch(() => {})", [&, interceptInPage] (const QVariant &) {
-                requestsOnReplace.push_back(interceptors.back().requestInfos.size());
-                setInterceptor(nullptr, interceptInPage);
+            page.runJavaScript("fetch('http://nonexistent.invalid').catch(() => {})", [&fetchFinished] (const QVariant &) {
                 fetchFinished = true;
             });
         }
@@ -856,6 +851,10 @@ void tst_QWebEngineUrlRequestInterceptor::replaceInterceptor()
     page.setUrl(server.url("/favicon.html"));
     QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 2, 20000);
     QTRY_VERIFY(fetchFinished);
+    QTRY_VERIFY(!interceptors.back().requestInfos.isEmpty());
+    setInterceptor(nullptr, true);
+    setInterceptor(nullptr, false);
+    requestsOnReplace.push_back(interceptors.back().requestInfos.size());
 
     QString s; QDebug d(&s);
     for (auto i = 0u; i < interceptors.size(); ++i) {
@@ -962,7 +961,8 @@ class TestPostRequestInterceptor : public QWebEngineUrlRequestInterceptor
             info.d_ptr->appendFileToResourceRequestBodyForTest(":/resources/postBodyFile.txt");
         }
 
-        requestBodyDevice->open(QIODevice::ReadOnly);
+        QVERIFY2(requestBodyDevice->open(QIODevice::ReadOnly),
+                 qPrintable(requestBodyDevice->errorString()));
 
         const QString webKitBoundary = requestBodyDevice->read(40);
         QVERIFY(webKitBoundary.contains("------WebKitFormBoundary"));
diff --git a/tests/auto/core/qwebengineurlrequestjob/tst_qwebengineurlrequestjob.cpp b/tests/auto/core/qwebengineurlrequestjob/tst_qwebengineurlrequestjob.cpp
index d92aad5c4bd..8552ba441b7 100644
--- a/tests/auto/core/qwebengineurlrequestjob/tst_qwebengineurlrequestjob.cpp
+++ b/tests/auto/core/qwebengineurlrequestjob/tst_qwebengineurlrequestjob.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QtTest/QtTest>
 #include <util.h>
@@ -77,7 +77,7 @@ class AdditionalResponseHeadersHandler : public QWebEngineUrlSchemeHandler
         job->setAdditionalResponseHeaders(additionalResponseHeaders);
 
         QFile *file = new QFile(QStringLiteral(":additionalResponseHeadersScript.html"), job);
-        file->open(QIODevice::ReadOnly);
+        QVERIFY2(file->open(QIODevice::ReadOnly), qPrintable(file->errorString()));
 
         job->reply(QByteArrayLiteral("text/html"), file);
     }
@@ -106,12 +106,13 @@ class RequestBodyHandler : public QWebEngineUrlSchemeHandler
         QCOMPARE(job->requestMethod(), QByteArrayLiteral("POST"));
 
         QIODevice *requestBodyDevice = job->requestBody();
-        requestBodyDevice->open(QIODevice::ReadOnly);
+        QVERIFY2(requestBodyDevice->open(QIODevice::ReadOnly),
+                 qPrintable(requestBodyDevice->errorString()));
         QByteArray requestBody = requestBodyDevice->readAll();
         requestBodyDevice->close();
 
         QBuffer *buf = new QBuffer(job);
-        buf->open(QBuffer::ReadWrite);
+        QVERIFY2(buf->open(QBuffer::ReadWrite), qPrintable(buf->errorString()));
         buf->write(requestBody);
         job->reply(QByteArrayLiteral("text/plain"), buf);
         buf->close();
diff --git a/tests/auto/core/webenginedriver/browser/main.cpp b/tests/auto/core/webenginedriver/browser/main.cpp
index 4b8f3513fe2..b43b06f2e59 100644
--- a/tests/auto/core/webenginedriver/browser/main.cpp
+++ b/tests/auto/core/webenginedriver/browser/main.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QtWebEngineCore/qwebenginepage.h>
 #include <QtWebEngineWidgets/qwebengineview.h>
diff --git a/tests/auto/core/webenginedriver/tst_webenginedriver.cpp b/tests/auto/core/webenginedriver/tst_webenginedriver.cpp
index cd3098b25cb..66e14e68307 100644
--- a/tests/auto/core/webenginedriver/tst_webenginedriver.cpp
+++ b/tests/auto/core/webenginedriver/tst_webenginedriver.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QtTest/QtTest>
 
diff --git a/tests/auto/httpserver/data/notification.html b/tests/auto/httpserver/data/notification.html
index 1d1e9c4119d..11ebf534e58 100644
--- a/tests/auto/httpserver/data/notification.html
+++ b/tests/auto/httpserver/data/notification.html
@@ -3,9 +3,7 @@
 <head>
 <title>Desktop Notifications Demo</title>
 <script>
-    function resetPermission() { document.Notification = 'default' }
-
-    function getPermission() { return document.Notification }
+    function getPermission() { return Notification.permission }
 
     function sendNotification(title, body) {
         let notification = new Notification(title, { body: body, dir: 'rtl', lang: 'de', tag: 'tst' })
diff --git a/tests/auto/httpserver/httpreqrep.cpp b/tests/auto/httpserver/httpreqrep.cpp
index 8b338ce4eb5..65a1df36559 100644
--- a/tests/auto/httpserver/httpreqrep.cpp
+++ b/tests/auto/httpserver/httpreqrep.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 #include "httpreqrep.h"
 
 HttpReqRep::HttpReqRep(QTcpSocket *socket, QObject *parent)
diff --git a/tests/auto/httpserver/httpreqrep.h b/tests/auto/httpserver/httpreqrep.h
index 774c08eb126..2b90b774b16 100644
--- a/tests/auto/httpserver/httpreqrep.h
+++ b/tests/auto/httpserver/httpreqrep.h
@@ -1,5 +1,5 @@
 // Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 #ifndef HTTPREQREP_H
 #define HTTPREQREP_H
 
diff --git a/tests/auto/httpserver/httpserver.cpp b/tests/auto/httpserver/httpserver.cpp
index e08af77e773..8a1d8e6f685 100644
--- a/tests/auto/httpserver/httpserver.cpp
+++ b/tests/auto/httpserver/httpserver.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 #include "httpserver.h"
 
 #include <QFile>
diff --git a/tests/auto/httpserver/httpserver.h b/tests/auto/httpserver/httpserver.h
index 201eef4c6f5..a1c2e3791f0 100644
--- a/tests/auto/httpserver/httpserver.h
+++ b/tests/auto/httpserver/httpserver.h
@@ -1,5 +1,5 @@
 // Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 #ifndef HTTPSERVER_H
 #define HTTPSERVER_H
 
diff --git a/tests/auto/httpserver/httpsserver.h b/tests/auto/httpserver/httpsserver.h
index d029851aa8d..237314965db 100644
--- a/tests/auto/httpserver/httpsserver.h
+++ b/tests/auto/httpserver/httpsserver.h
@@ -1,5 +1,5 @@
 // Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 #ifndef HTTPSSERVER_H
 #define HTTPSSERVER_H
 
diff --git a/tests/auto/httpserver/proxy_server.cpp b/tests/auto/httpserver/proxy_server.cpp
index 3384153118d..e47214ee43f 100644
--- a/tests/auto/httpserver/proxy_server.cpp
+++ b/tests/auto/httpserver/proxy_server.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include "proxy_server.h"
 #include <QDataStream>
diff --git a/tests/auto/httpserver/proxy_server.h b/tests/auto/httpserver/proxy_server.h
index 6be0c4e1ab1..8f7cd4cc6c2 100644
--- a/tests/auto/httpserver/proxy_server.h
+++ b/tests/auto/httpserver/proxy_server.h
@@ -1,5 +1,5 @@
 // Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #ifndef PROXY_SERVER_H
 #define PROXY_SERVER_H
diff --git a/tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp b/tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp
index a1804e17954..7c9216534ae 100644
--- a/tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp
+++ b/tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 
 #include <QtTest/QtTest>
diff --git a/tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp b/tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp
index d222bff0cd2..1e90b221d2d 100644
--- a/tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp
+++ b/tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 
 #include <QtTest/QtTest>
@@ -62,7 +62,7 @@ struct TemporaryPdf: public QTemporaryFile
 
 TemporaryPdf::TemporaryPdf():QTemporaryFile(QStringLiteral("qpdfdocument"))
 {
-    open();
+    QVERIFY2(open(), qPrintable(errorString()));
     pageLayout = QPageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF());
 
     {
diff --git a/tests/auto/pdf/qpdfpagenavigator/tst_qpdfpagenavigator.cpp b/tests/auto/pdf/qpdfpagenavigator/tst_qpdfpagenavigator.cpp
index 327a9f36a57..4044757660e 100644
--- a/tests/auto/pdf/qpdfpagenavigator/tst_qpdfpagenavigator.cpp
+++ b/tests/auto/pdf/qpdfpagenavigator/tst_qpdfpagenavigator.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 
 #include <QtTest/QtTest>
diff --git a/tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp b/tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp
index 39d32df0b72..ca14338cc14 100644
--- a/tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp
+++ b/tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QPdfDocument>
 #include <QPdfPageRenderer>
diff --git a/tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp b/tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp
index cf71b148e04..f5e946d1945 100644
--- a/tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp
+++ b/tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 
 #include <QtTest/QtTest>
diff --git a/tests/auto/pdfquick/multipageview/tst_multipageview.cpp b/tests/auto/pdfquick/multipageview/tst_multipageview.cpp
index c5e0b30dbba..71c09077c36 100644
--- a/tests/auto/pdfquick/multipageview/tst_multipageview.cpp
+++ b/tests/auto/pdfquick/multipageview/tst_multipageview.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QSignalSpy>
 #include <QTest>
diff --git a/tests/auto/pdfquick/pdfpageimage/tst_pdfpageimage.cpp b/tests/auto/pdfquick/pdfpageimage/tst_pdfpageimage.cpp
index d2c9c8709f6..c8ad4bdaa8b 100644
--- a/tests/auto/pdfquick/pdfpageimage/tst_pdfpageimage.cpp
+++ b/tests/auto/pdfquick/pdfpageimage/tst_pdfpageimage.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QRegularExpression>
 #include <QSignalSpy>
@@ -11,10 +11,12 @@
 
 using namespace Qt::StringLiterals;
 
-Q_LOGGING_CATEGORY(lcTests, "qt.pdf.tests")
-
 // #define DEBUG_WRITE_OUTPUT
 
+#ifdef DEBUG_WRITE_OUTPUT
+Q_LOGGING_CATEGORY(lcTests, "qt.pdf.tests")
+#endif
+
 class tst_PdfPageImage : public QQuickDataTest
 {
     Q_OBJECT
diff --git a/tests/auto/pdfquick/shared/util.cpp b/tests/auto/pdfquick/shared/util.cpp
index c540ebfa66e..1bf9b45b764 100644
--- a/tests/auto/pdfquick/shared/util.cpp
+++ b/tests/auto/pdfquick/shared/util.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include "util.h"
 #include <QtQuick/QQuickItem>
diff --git a/tests/auto/pdfquick/shared/util.h b/tests/auto/pdfquick/shared/util.h
index 9ceb711afe8..2788e6104a6 100644
--- a/tests/auto/pdfquick/shared/util.h
+++ b/tests/auto/pdfquick/shared/util.h
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #ifndef QUICK_VISUAL_TEST_UTIL_H
 #define QUICK_VISUAL_TEST_UTIL_H
diff --git a/tests/auto/quick/dialogs/WebView.qml b/tests/auto/quick/dialogs/WebView.qml
index 45fafb42d1c..6db0e5777b6 100644
--- a/tests/auto/quick/dialogs/WebView.qml
+++ b/tests/auto/quick/dialogs/WebView.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtWebEngine
diff --git a/tests/auto/quick/dialogs/testhandler.cpp b/tests/auto/quick/dialogs/testhandler.cpp
index f4585263075..0596626f1fa 100644
--- a/tests/auto/quick/dialogs/testhandler.cpp
+++ b/tests/auto/quick/dialogs/testhandler.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include "testhandler.h"
 
diff --git a/tests/auto/quick/dialogs/testhandler.h b/tests/auto/quick/dialogs/testhandler.h
index c72e8184119..fef41d075f3 100644
--- a/tests/auto/quick/dialogs/testhandler.h
+++ b/tests/auto/quick/dialogs/testhandler.h
@@ -1,5 +1,5 @@
 // Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #ifndef TESTHANDLER_H
 #define TESTHANDLER_H
diff --git a/tests/auto/quick/dialogs/tst_dialogs.cpp b/tests/auto/quick/dialogs/tst_dialogs.cpp
index 2b861efa62a..6f54019c7ff 100644
--- a/tests/auto/quick/dialogs/tst_dialogs.cpp
+++ b/tests/auto/quick/dialogs/tst_dialogs.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include "testhandler.h"
 #include <quickutil.h>
diff --git a/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp b/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp
index a9638bee471..b025c4eaafd 100644
--- a/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp
+++ b/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QNetworkAccessManager>
 #include <QNetworkReply>
@@ -17,6 +17,8 @@ static const QUrl s_inspectorServerHttpBaseUrl("http://localhost:" INSPECTOR_SER
 class tst_InspectorServer : public QObject {
     Q_OBJECT
 public:
+    static void initMain();
+
     tst_InspectorServer();
 
 private Q_SLOTS:
@@ -36,11 +38,15 @@ private Q_SLOTS:
     QScopedPointer<QQmlComponent> m_component;
 };
 
+void tst_InspectorServer::initMain()
+{
+    QtWebEngineQuick::initialize();
+}
+
 tst_InspectorServer::tst_InspectorServer()
 {
     qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--remote-allow-origins=*");
     qputenv("QTWEBENGINE_REMOTE_DEBUGGING", INSPECTOR_SERVER_PORT);
-    QtWebEngineQuick::initialize();
     QQuickWebEngineProfile::defaultProfile()->setOffTheRecord(true);
     prepareWebViewComponent();
 }
diff --git a/tests/auto/quick/publicapi/tst_publicapi.cpp b/tests/auto/quick/publicapi/tst_publicapi.cpp
index c95c45bb4ef..10b07f36050 100644
--- a/tests/auto/quick/publicapi/tst_publicapi.cpp
+++ b/tests/auto/quick/publicapi/tst_publicapi.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QMetaEnum>
 #include <QMetaMethod>
@@ -8,7 +8,6 @@
 #include <QMetaType>
 #include <QQmlListProperty>
 #include <QtTest/QtTest>
-#include <QtWebEngineQuick/QQuickWebEngineProfile>
 #include <QtWebEngineCore/QWebEngineCertificateError>
 #include <QtWebEngineCore/QWebEngineDesktopMediaRequest>
 #include <QtWebEngineCore/QWebEngineFileSystemAccessRequest>
@@ -20,17 +19,20 @@
 #include <QtWebEngineCore/QWebEngineNotification>
 #include <QtWebEngineCore/QWebEngineQuotaRequest>
 #include <QtWebEngineCore/QWebEngineRegisterProtocolHandlerRequest>
+#include <QtWebEngineCore/QWebEngineClientHints>
 #include <QtWebEngineCore/QWebEngineContextMenuRequest>
 #include <QtWebEngineCore/QWebEngineDownloadRequest>
 #include <QtWebEngineCore/QWebEngineScript>
 #include <QtWebEngineCore/QWebEngineLoadingInfo>
 #include <QtWebEngineCore/QWebEngineWebAuthUxRequest>
 #include <QtWebEngineCore/QWebEngineFrame>
+#include <QtWebEngineCore/QWebEnginePermission>
+#include <QtWebEngineQuick/QQuickWebEngineDownloadRequest>
+#include <QtWebEngineQuick/QQuickWebEngineProfile>
 #include <private/qquickwebengineview_p.h>
 #include <private/qquickwebengineaction_p.h>
 #include <private/qquickwebengineclientcertificateselection_p.h>
 #include <private/qquickwebenginedialogrequests_p.h>
-#include <private/qquickwebenginedownloadrequest_p.h>
 #include <private/qquickwebenginenewwindowrequest_p.h>
 #include <private/qquickwebenginesettings_p.h>
 #include <private/qquickwebenginesingleton_p.h>
@@ -72,16 +74,19 @@ static const QList<const QMetaObject *> typesToCheck = QList<const QMetaObject *
     << &QWebEngineNavigationRequest::staticMetaObject
     << &QWebEngineNewWindowRequest::staticMetaObject
     << &QWebEngineNotification::staticMetaObject
+    << &QWebEnginePermission::staticMetaObject
     << &QWebEngineQuotaRequest::staticMetaObject
     << &QWebEngineRegisterProtocolHandlerRequest::staticMetaObject
     << &QQuickWebEngineTouchSelectionMenuRequest::staticMetaObject
     << &QWebEngineWebAuthUxRequest::staticMetaObject
     << &QWebEngineWebAuthPinRequest::staticMetaObject
     << &QWebEngineFrame::staticMetaObject
+    << &QWebEngineClientHints::staticMetaObject
     ;
 
 static QList<QMetaEnum> knownEnumNames = QList<QMetaEnum>()
     << QWebEngineDownloadRequest::staticMetaObject.enumerator(QWebEngineDownloadRequest::staticMetaObject.indexOfEnumerator("SavePageFormat"))
+    << QWebEnginePermission::staticMetaObject.enumerator(QWebEnginePermission::staticMetaObject.indexOfEnumerator("PermissionType"))
     ;
 
 static const QStringList hardcodedTypes = QStringList()
@@ -95,7 +100,9 @@ static const QStringList hardcodedTypes = QStringList()
     << "Qt::LayoutDirection"
     << "QQuickWebEngineScriptCollection*"
     << "QQmlComponent*"
-    << "QMultiMap<QByteArray,QByteArray>";
+    << "QMultiMap<QByteArray,QByteArray>"
+    << "QList<QWebEnginePermission>"
+    ;
 
 static const QStringList expectedAPI = QStringList()
     << "QQuickWebEngineAction.text --> QString"
@@ -131,6 +138,7 @@ static const QStringList expectedAPI = QStringList()
     << "QWebEngineCertificateError.CertificateSymantecLegacy --> Type"
     << "QWebEngineCertificateError.SslObsoleteVersion --> Type"
     << "QWebEngineCertificateError.SslPinnedKeyNotInCertificateChain --> Type"
+    << "QWebEngineCertificateError.Ok --> Type"
     << "QWebEngineCertificateError.defer() --> void"
     << "QWebEngineCertificateError.description --> QString"
     << "QWebEngineCertificateError.type --> QWebEngineCertificateError::Type"
@@ -139,6 +147,17 @@ static const QStringList expectedAPI = QStringList()
     << "QWebEngineCertificateError.overridable --> bool"
     << "QWebEngineCertificateError.rejectCertificate() --> void"
     << "QWebEngineCertificateError.url --> QUrl"
+    << "QWebEngineClientHints.arch --> QString"
+    << "QWebEngineClientHints.platform --> QString"
+    << "QWebEngineClientHints.model --> QString"
+    << "QWebEngineClientHints.mobile --> bool"
+    << "QWebEngineClientHints.fullVersion --> QString"
+    << "QWebEngineClientHints.platformVersion --> QString"
+    << "QWebEngineClientHints.bitness --> QString"
+    << "QWebEngineClientHints.fullVersionList --> QVariantMap"
+    << "QWebEngineClientHints.wow64 --> bool"
+    << "QWebEngineClientHints.isAllClientHintsEnabled --> bool"
+    << "QWebEngineClientHints.resetAll() --> void"
     << "QQuickWebEngineClientCertificateOption.issuer --> QString"
     << "QQuickWebEngineClientCertificateOption.subject --> QString"
     << "QQuickWebEngineClientCertificateOption.effectiveDate --> QDateTime"
@@ -353,6 +372,29 @@ static const QStringList expectedAPI = QStringList()
     << "QWebEngineNewWindowRequest.InNewDialog --> DestinationType"
     << "QWebEngineNewWindowRequest.InNewTab --> DestinationType"
     << "QWebEngineNewWindowRequest.InNewWindow --> DestinationType"
+    << "QWebEnginePermission.PermissionType.Unsupported --> PermissionType"
+    << "QWebEnginePermission.PermissionType.MediaAudioCapture --> PermissionType"
+    << "QWebEnginePermission.PermissionType.MediaVideoCapture --> PermissionType"
+    << "QWebEnginePermission.PermissionType.MediaAudioVideoCapture --> PermissionType"
+    << "QWebEnginePermission.PermissionType.DesktopVideoCapture --> PermissionType"
+    << "QWebEnginePermission.PermissionType.DesktopAudioVideoCapture --> PermissionType"
+    << "QWebEnginePermission.PermissionType.MouseLock --> PermissionType"
+    << "QWebEnginePermission.PermissionType.Notifications --> PermissionType"
+    << "QWebEnginePermission.PermissionType.Geolocation --> PermissionType"
+    << "QWebEnginePermission.PermissionType.ClipboardReadWrite --> PermissionType"
+    << "QWebEnginePermission.PermissionType.LocalFontsAccess --> PermissionType"
+    << "QWebEnginePermission.State.Invalid --> State"
+    << "QWebEnginePermission.State.Ask --> State"
+    << "QWebEnginePermission.State.Granted --> State"
+    << "QWebEnginePermission.State.Denied --> State"
+    << "QWebEnginePermission.origin --> QUrl"
+    << "QWebEnginePermission.permissionType --> QWebEnginePermission::PermissionType"
+    << "QWebEnginePermission.state --> QWebEnginePermission::State"
+    << "QWebEnginePermission.isValid --> bool"
+    << "QWebEnginePermission.grant() --> void"
+    << "QWebEnginePermission.deny() --> void"
+    << "QWebEnginePermission.reset() --> void"
+    << "QWebEnginePermission.isPersistent(QWebEnginePermission::PermissionType) --> bool"
     << "QQuickWebEngineNewWindowRequest.openIn(QQuickWebEngineView*) --> void"
     << "QQuickWebEngineProfile.AllowPersistentCookies --> PersistentCookiesPolicy"
     << "QQuickWebEngineProfile.DiskHttpCache --> HttpCacheType"
@@ -360,17 +402,22 @@ static const QStringList expectedAPI = QStringList()
     << "QQuickWebEngineProfile.MemoryHttpCache --> HttpCacheType"
     << "QQuickWebEngineProfile.NoCache --> HttpCacheType"
     << "QQuickWebEngineProfile.NoPersistentCookies --> PersistentCookiesPolicy"
-    << "QQuickWebEngineProfile.NoPersistentPermissions --> PersistentPermissionsPolicy"
-    << "QQuickWebEngineProfile.PersistentPermissionsInMemory --> PersistentPermissionsPolicy"
-    << "QQuickWebEngineProfile.PersistentPermissionsOnDisk --> PersistentPermissionsPolicy"
+    << "QQuickWebEngineProfile.PersistentPermissionsPolicy.AskEveryTime --> PersistentPermissionsPolicy"
+    << "QQuickWebEngineProfile.PersistentPermissionsPolicy.StoreInMemory --> PersistentPermissionsPolicy"
+    << "QQuickWebEngineProfile.PersistentPermissionsPolicy.StoreOnDisk --> PersistentPermissionsPolicy"
     << "QQuickWebEngineProfile.cachePath --> QString"
     << "QQuickWebEngineProfile.cachePathChanged() --> void"
     << "QQuickWebEngineProfile.clearHttpCache() --> void"
     << "QQuickWebEngineProfile.clearHttpCacheCompleted() --> void"
+    << "QQuickWebEngineProfile.clientHints --> QWebEngineClientHints*"
     << "QQuickWebEngineProfile.downloadFinished(QQuickWebEngineDownloadRequest*) --> void"
     << "QQuickWebEngineProfile.downloadRequested(QQuickWebEngineDownloadRequest*) --> void"
     << "QQuickWebEngineProfile.downloadPath --> QString"
     << "QQuickWebEngineProfile.downloadPathChanged() --> void"
+    << "QQuickWebEngineProfile.queryPermission(QUrl,QWebEnginePermission::PermissionType) --> QWebEnginePermission"
+    << "QQuickWebEngineProfile.listAllPermissions() --> QList<QWebEnginePermission>"
+    << "QQuickWebEngineProfile.listPermissionsForOrigin(QUrl) --> QList<QWebEnginePermission>"
+    << "QQuickWebEngineProfile.listPermissionsForPermissionType(QWebEnginePermission::PermissionType) --> QList<QWebEnginePermission>"
     << "QQuickWebEngineProfile.persistentPermissionsPolicy --> QQuickWebEngineProfile::PersistentPermissionsPolicy"
     << "QQuickWebEngineProfile.persistentPermissionsPolicyChanged() --> void"
     << "QQuickWebEngineProfile.presentNotification(QWebEngineNotification*) --> void"
@@ -537,10 +584,20 @@ static const QStringList expectedAPI = QStringList()
     << "QQuickWebEngineView.B10 --> PrintedPageSizeId"
     << "QQuickWebEngineView.Back --> WebAction"
     << "QQuickWebEngineView.C5E --> PrintedPageSizeId"
+#if QT_DEPRECATED_SINCE(6, 8)
+    << "QQuickWebEngineView.ClipboardReadWrite --> Feature"
+    << "QQuickWebEngineView.DesktopAudioVideoCapture --> Feature"
+    << "QQuickWebEngineView.DesktopVideoCapture --> Feature"
+    << "QQuickWebEngineView.Geolocation --> Feature"
+    << "QQuickWebEngineView.LocalFontsAccess --> Feature"
+    << "QQuickWebEngineView.MediaAudioCapture --> Feature"
+    << "QQuickWebEngineView.MediaAudioVideoCapture --> Feature"
+    << "QQuickWebEngineView.MediaVideoCapture --> Feature"
+    << "QQuickWebEngineView.Notifications --> Feature"
+#endif
     << "QQuickWebEngineView.CertificateErrorDomain --> ErrorDomain"
     << "QQuickWebEngineView.ChangeTextDirectionLTR --> WebAction"
     << "QQuickWebEngineView.ChangeTextDirectionRTL --> WebAction"
-    << "QQuickWebEngineView.ClipboardReadWrite --> Feature"
     << "QQuickWebEngineView.Comm10E --> PrintedPageSizeId"
     << "QQuickWebEngineView.ConnectionErrorDomain --> ErrorDomain"
     << "QQuickWebEngineView.Copy --> WebAction"
@@ -552,8 +609,6 @@ static const QStringList expectedAPI = QStringList()
     << "QQuickWebEngineView.Custom --> PrintedPageSizeId"
     << "QQuickWebEngineView.Cut --> WebAction"
     << "QQuickWebEngineView.DLE --> PrintedPageSizeId"
-    << "QQuickWebEngineView.DesktopAudioVideoCapture --> Feature"
-    << "QQuickWebEngineView.DesktopVideoCapture --> Feature"
     << "QQuickWebEngineView.DnsErrorDomain --> ErrorDomain"
     << "QQuickWebEngineView.DoublePostcard --> PrintedPageSizeId"
     << "QQuickWebEngineView.DownloadImageToDisk --> WebAction"
@@ -608,7 +663,6 @@ static const QStringList expectedAPI = QStringList()
     << "QQuickWebEngineView.Folio --> PrintedPageSizeId"
     << "QQuickWebEngineView.Forward --> WebAction"
     << "QQuickWebEngineView.FtpErrorDomain --> ErrorDomain"
-    << "QQuickWebEngineView.Geolocation --> Feature"
     << "QQuickWebEngineView.HttpErrorDomain --> ErrorDomain"
     << "QQuickWebEngineView.Imperial10x11 --> PrintedPageSizeId"
     << "QQuickWebEngineView.Imperial10x13 --> PrintedPageSizeId"
@@ -653,12 +707,7 @@ static const QStringList expectedAPI = QStringList()
     << "QQuickWebEngineView.LoadStartedStatus --> LoadStatus"
     << "QQuickWebEngineView.LoadStoppedStatus --> LoadStatus"
     << "QQuickWebEngineView.LoadSucceededStatus --> LoadStatus"
-    << "QQuickWebEngineView.LocalFontsAccess --> Feature"
-    << "QQuickWebEngineView.MediaAudioCapture --> Feature"
-    << "QQuickWebEngineView.MediaAudioVideoCapture --> Feature"
-    << "QQuickWebEngineView.MediaVideoCapture --> Feature"
     << "QQuickWebEngineView.NoErrorDomain --> ErrorDomain"
-    << "QQuickWebEngineView.Notifications --> Feature"
     << "QQuickWebEngineView.NoWebAction --> WebAction"
     << "QQuickWebEngineView.NormalTerminationStatus --> RenderProcessTerminationStatus"
     << "QQuickWebEngineView.Note --> PrintedPageSizeId"
@@ -734,7 +783,9 @@ static const QStringList expectedAPI = QStringList()
     << "QQuickWebEngineView.goBack() --> void"
     << "QQuickWebEngineView.goBackOrForward(int) --> void"
     << "QQuickWebEngineView.goForward() --> void"
+#if QT_DEPRECATED_SINCE(6, 8)
     << "QQuickWebEngineView.grantFeaturePermission(QUrl,QQuickWebEngineView::Feature,bool) --> void"
+#endif
     << "QQuickWebEngineView.history --> QWebEngineHistory*"
     << "QQuickWebEngineView.icon --> QUrl"
     << "QQuickWebEngineView.iconChanged() --> void"
@@ -769,6 +820,7 @@ static const QStringList expectedAPI = QStringList()
     << "QQuickWebEngineView.NewViewInDialog --> NewViewDestination"
     << "QQuickWebEngineView.NewViewInTab --> NewViewDestination"
     << "QQuickWebEngineView.NewViewInWindow --> NewViewDestination"
+    << "QQuickWebEngineView.permissionRequested(QWebEnginePermission) --> void"
     << "QQuickWebEngineView.pdfPrintingFinished(QString,bool) --> void"
     << "QQuickWebEngineView.printRequested() --> void"
     << "QQuickWebEngineView.printRequestedByFrame(QWebEngineFrame) --> void"
@@ -886,14 +938,17 @@ static const QStringList expectedAPI = QStringList()
     << "QWebEngineWebAuthPinRequest.error --> QWebEngineWebAuthUxRequest::PinEntryError"
     << "QWebEngineWebAuthPinRequest.minPinLength --> int"
     << "QWebEngineWebAuthPinRequest.remainingAttempts --> int"
-    << "QQuickWebEngineSettings.AllowImageAnimation --> ImageAnimationPolicy"
-    << "QQuickWebEngineSettings.AnimateImageOnce --> ImageAnimationPolicy"
-    << "QQuickWebEngineSettings.DisallowImageAnimation --> ImageAnimationPolicy"
+    << "QQuickWebEngineSettings.ImageAnimationPolicy.Allow --> ImageAnimationPolicy"
+    << "QQuickWebEngineSettings.ImageAnimationPolicy.AnimateOnce --> ImageAnimationPolicy"
+    << "QQuickWebEngineSettings.ImageAnimationPolicy.Disallow --> ImageAnimationPolicy"
     << "QQuickWebEngineSettings.imageAnimationPolicy --> QQuickWebEngineSettings::ImageAnimationPolicy"
     << "QQuickWebEngineSettings.imageAnimationPolicyChanged() --> void"
     << "QWebEngineFrame.htmlName --> QString"
+    << "QWebEngineFrame.isMainFrame --> bool"
     << "QWebEngineFrame.isValid --> bool"
     << "QWebEngineFrame.name --> QString"
+    << "QWebEngineFrame.printToPdf(QJSValue) --> void"
+    << "QWebEngineFrame.printToPdf(QString) --> void"
     << "QWebEngineFrame.runJavaScript(QString) --> void"
     << "QWebEngineFrame.runJavaScript(QString,uint) --> void"
     << "QWebEngineFrame.runJavaScript(QString,QJSValue) --> void"
@@ -1018,4 +1073,3 @@ void tst_publicapi::publicAPI()
 QTEST_MAIN(tst_publicapi)
 
 #include "tst_publicapi.moc"
-
diff --git a/tests/auto/quick/qmltests/CMakeLists.txt b/tests/auto/quick/qmltests/CMakeLists.txt
index daae6d60dcd..fa927fbc313 100644
--- a/tests/auto/quick/qmltests/CMakeLists.txt
+++ b/tests/auto/quick/qmltests/CMakeLists.txt
@@ -12,6 +12,7 @@ qt_internal_add_test(tst_qmltests
         Qt::QuickTest
         Qt::TestPrivate
         Qt::WebEngineQuick
+        Qt::WebEngineCorePrivate
         Test::HttpServer
         Test::Util
 )
@@ -63,6 +64,11 @@ set(testList
     tst_save.qml
 )
 
+# QTBUG-128652
+if(CMAKE_SYSTEM_PROCESSOR MATCHES "(arm|aarch).*64" AND LINUX)
+    list(REMOVE_ITEM testList tst_faviconDatabase.qml)
+endif()
+
 if(QT_FEATURE_webengine_webchannel)
     list(APPEND testList tst_webchannel.qml)
 endif()
diff --git a/tests/auto/quick/qmltests/data/TestWebEngineView.qml b/tests/auto/quick/qmltests/data/TestWebEngineView.qml
index 415985471d8..aefeb9af56d 100644
--- a/tests/auto/quick/qmltests/data/TestWebEngineView.qml
+++ b/tests/auto/quick/qmltests/data/TestWebEngineView.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/titleupdate.js b/tests/auto/quick/qmltests/data/titleupdate.js
index 720e836761f..94bfd90dfd9 100644
--- a/tests/auto/quick/qmltests/data/titleupdate.js
+++ b/tests/auto/quick/qmltests/data/titleupdate.js
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 function updateTitle()
 {
diff --git a/tests/auto/quick/qmltests/data/tst_action.qml b/tests/auto/quick/qmltests/data/tst_action.qml
index 9e49c2dbfe1..52891197dbb 100644
--- a/tests/auto/quick/qmltests/data/tst_action.qml
+++ b/tests/auto/quick/qmltests/data/tst_action.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_activeFocusOnPress.qml b/tests/auto/quick/qmltests/data/tst_activeFocusOnPress.qml
index 77968f6b637..d9ca1e8e66f 100644
--- a/tests/auto/quick/qmltests/data/tst_activeFocusOnPress.qml
+++ b/tests/auto/quick/qmltests/data/tst_activeFocusOnPress.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_audioMuted.qml b/tests/auto/quick/qmltests/data/tst_audioMuted.qml
index 85f813f0c28..11527f8480c 100644
--- a/tests/auto/quick/qmltests/data/tst_audioMuted.qml
+++ b/tests/auto/quick/qmltests/data/tst_audioMuted.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_basicProfiles.qml b/tests/auto/quick/qmltests/data/tst_basicProfiles.qml
index 97a25cdd87e..23c94428860 100644
--- a/tests/auto/quick/qmltests/data/tst_basicProfiles.qml
+++ b/tests/auto/quick/qmltests/data/tst_basicProfiles.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
@@ -8,7 +8,7 @@ import Qt.labs.platform
 
 Item {
     WebEngineProfile { id: otrProfile; /* MEMO implicit offTheRecord: true */ }
-    WebEngineProfile { id: nonOtrProfile; offTheRecord: false }
+    WebEngineProfile { id: nonOtrProfile; offTheRecord: false; storageName: 'Test' }
 
     function getPath(path, offset = 1) { return path.substr(path.indexOf(':') + offset, path.length) }
     property string appDataLocation: getPath(getPath(StandardPaths.writableLocation(StandardPaths.AppDataLocation).toString(), 3))
@@ -16,6 +16,7 @@ Item {
     property string downloadLocation: getPath(getPath(StandardPaths.writableLocation(StandardPaths.DownloadLocation).toString(), 3))
 
     TestCase {
+        id: basicProfileTest
         name: "BasicProfiles"
 
         function test_defaultProfile() {
@@ -60,15 +61,9 @@ Item {
 
         function test_nonOtrProfile() {
             let p = nonOtrProfile
+            compare(p.storageName, 'Test')
             verify(!p.offTheRecord)
 
-            compare(p.storageName, '')
-            compare(p.cachePath, '')
-            compare(getPath(p.persistentStoragePath), appDataLocation + '/QtWebEngine/UnknownProfile')
-            compare(p.httpCacheType, WebEngineProfile.MemoryHttpCache)
-            compare(p.httpCacheMaximumSize, 0)
-            compare(p.persistentCookiesPolicy, WebEngineProfile.NoPersistentCookies)
-
             compare(getPath(p.downloadPath), downloadLocation)
             compare(p.httpAcceptLanguage, '')
             verify(p.httpUserAgent !== '')
@@ -77,7 +72,6 @@ Item {
 
             compare(p.userScripts.collection, [])
 
-            p.storageName = 'Test'
             compare(p.storageName, 'Test')
             compare(getPath(p.cachePath), cacheLocation + '/QtWebEngine/' + p.storageName)
             compare(getPath(p.persistentStoragePath), appDataLocation + '/QtWebEngine/' + p.storageName)
@@ -86,5 +80,50 @@ Item {
             compare(p.httpCacheMaximumSize, 0)
             compare(p.persistentCookiesPolicy, WebEngineProfile.AllowPersistentCookies)
         }
+
+        function test_nonOtrProfileInitializationOrderCheck_data() {
+            return [
+                   { tag: "normal", isReverseOrder: false },
+                   { tag: "reverse", isReverseOrder: true },
+            ];
+        }
+
+        function test_nonOtrProfileInitializationOrderCheck(row) {
+           var isReverseOrder = row.isReverseOrder
+
+            var profileSetupQml = "";
+            if (row.isReverseOrder) {
+                profileSetupQml = "import QtWebEngine\n
+                                   WebEngineProfile {\n
+                                        storageName: 'ReverseOrder'\n
+                                        offTheRecord: false\n
+                                   }"
+            } else {
+                profileSetupQml = "import QtWebEngine\n
+                                   WebEngineProfile {\n
+                                        offTheRecord: false\n
+                                        storageName: 'NormalOrder'\n
+                                   }"
+            }
+
+            var p = Qt.createQmlObject(profileSetupQml, basicProfileTest);
+
+            compare(p.storageName, isReverseOrder ? 'ReverseOrder' : 'NormalOrder')
+            verify(!p.offTheRecord)
+
+            compare(getPath(p.downloadPath), downloadLocation)
+            compare(p.httpAcceptLanguage, '')
+            verify(p.httpUserAgent !== '')
+            compare(p.spellCheckEnabled, false)
+            compare(p.spellCheckLanguages, [])
+
+            compare(p.userScripts.collection, [])
+            compare(getPath(p.cachePath), cacheLocation + '/QtWebEngine/' + p.storageName)
+            compare(getPath(p.persistentStoragePath), appDataLocation + '/QtWebEngine/' + p.storageName)
+
+            compare(p.httpCacheType, WebEngineProfile.DiskHttpCache)
+            compare(p.httpCacheMaximumSize, 0)
+            compare(p.persistentCookiesPolicy, WebEngineProfile.AllowPersistentCookies)
+        }
     }
 }
diff --git a/tests/auto/quick/qmltests/data/tst_certificateError.qml b/tests/auto/quick/qmltests/data/tst_certificateError.qml
index 220ef9ac832..6c8e680e046 100644
--- a/tests/auto/quick/qmltests/data/tst_certificateError.qml
+++ b/tests/auto/quick/qmltests/data/tst_certificateError.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_contextMenu.qml b/tests/auto/quick/qmltests/data/tst_contextMenu.qml
index 58e27b8ba6d..9a34d3f339f 100644
--- a/tests/auto/quick/qmltests/data/tst_contextMenu.qml
+++ b/tests/auto/quick/qmltests/data/tst_contextMenu.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_datalist.qml b/tests/auto/quick/qmltests/data/tst_datalist.qml
index f739639b2b3..9e4d766ef42 100644
--- a/tests/auto/quick/qmltests/data/tst_datalist.qml
+++ b/tests/auto/quick/qmltests/data/tst_datalist.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtQuick.Controls
@@ -105,10 +105,13 @@ TestWebEngineView {
             keyClick(Qt.Key_Escape);
             tryVerify(function() { return listView() == null; });
 
-            // Key Down should open the popup and select the first suggestion.
+            // The first Key Down opens the popup.
             keyClick(Qt.Key_Down);
             tryVerify(function() { return listView() != null; });
-            compare(listView().currentIndex, 0);
+
+            // The second Key Down selects the first suggestion.
+            keyClick(Qt.Key_Down);
+            tryCompare(listView(), "currentIndex", 0);
             verify(listView().currentItem);
         }
 
@@ -120,10 +123,13 @@ TestWebEngineView {
             // Make sure there is no open popup yet.
             verify(!listView());
 
-            // Key Down should open the popup and select the first suggestion.
+            // The first Key Down opens the popup.
             keyClick(Qt.Key_Down);
             tryVerify(function() { return listView() != null; });
-            compare(listView().currentIndex, 0);
+
+            // The second Key Down selects the first suggestion.
+            keyClick(Qt.Key_Down);
+            tryCompare(listView(), "currentIndex", 0);
 
             // Test keyboard navigation in list.
             keyClick(Qt.Key_Up);
diff --git a/tests/auto/quick/qmltests/data/tst_desktopBehaviorLoadHtml.qml b/tests/auto/quick/qmltests/data/tst_desktopBehaviorLoadHtml.qml
index 6cb2841ec8c..5e2dd19c255 100644
--- a/tests/auto/quick/qmltests/data/tst_desktopBehaviorLoadHtml.qml
+++ b/tests/auto/quick/qmltests/data/tst_desktopBehaviorLoadHtml.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_download.qml b/tests/auto/quick/qmltests/data/tst_download.qml
index 61a363c3972..a0cb8970b48 100644
--- a/tests/auto/quick/qmltests/data/tst_download.qml
+++ b/tests/auto/quick/qmltests/data/tst_download.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_dragHandlerUnderView.qml b/tests/auto/quick/qmltests/data/tst_dragHandlerUnderView.qml
index c22bd44c285..35332b4e42a 100644
--- a/tests/auto/quick/qmltests/data/tst_dragHandlerUnderView.qml
+++ b/tests/auto/quick/qmltests/data/tst_dragHandlerUnderView.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_favicon.qml b/tests/auto/quick/qmltests/data/tst_favicon.qml
index 15f116e5db3..ce2995424d6 100644
--- a/tests/auto/quick/qmltests/data/tst_favicon.qml
+++ b/tests/auto/quick/qmltests/data/tst_favicon.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
@@ -19,8 +19,9 @@ TestWebEngineView {
     }
 
     property QtObject nonOTRProfile: WebEngineProfile {
-        persistentStoragePath: tempDir.path() + '/WebEngineFavicon'
         offTheRecord: false
+        storageName: 'WebEngineFavicon'
+        persistentStoragePath: tempDir.path() + '/WebEngineFavicon'
     }
 
     function removeFaviconProviderPrefix(url) {
@@ -68,8 +69,7 @@ TestWebEngineView {
             webEngineView.url = url
             verify(webEngineView.waitForLoadSucceeded())
 
-            iconChangedSpy.wait()
-            compare(iconChangedSpy.count, 1)
+            tryCompare(iconChangedSpy, "count", 1)
 
             tryCompare(favicon, "status", Image.Ready)
             compare(favicon.width, 32)
@@ -91,8 +91,7 @@ TestWebEngineView {
             webEngineView.url = url
             verify(webEngineView.waitForLoadSucceeded())
 
-            iconChangedSpy.wait()
-            compare(iconChangedSpy.count, 1)
+            tryCompare(iconChangedSpy, "count", 1)
 
             tryCompare(favicon, "status", Image.Ready)
             compare(favicon.width, 32)
@@ -298,8 +297,7 @@ TestWebEngineView {
             webEngineView.url = url
             verify(webEngineView.waitForLoadSucceeded())
 
-            iconChangedSpy.wait()
-            compare(iconChangedSpy.count, 1)
+            tryCompare(iconChangedSpy, "count", 1)
 
             iconUrl = removeFaviconProviderPrefix(webEngineView.icon)
             // Touch icon is ignored
@@ -352,10 +350,9 @@ TestWebEngineView {
             webEngineView.url = url
             verify(webEngineView.waitForLoadSucceeded())
 
-            iconChangedSpy.wait()
+            tryCompare(iconChangedSpy, "count", 1)
             iconUrl = removeFaviconProviderPrefix(webEngineView.icon)
             compare(iconUrl, Qt.resolvedUrl("icons/qt144.png"))
-            compare(iconChangedSpy.count, 1)
             tryCompare(favicon, "status", Image.Ready)
             compare(favicon.width, 144)
             compare(favicon.height, 144)
@@ -376,8 +373,7 @@ TestWebEngineView {
             webEngineView.url = url
             verify(webEngineView.waitForLoadSucceeded())
 
-            iconChangedSpy.wait()
-            compare(iconChangedSpy.count, 1)
+            tryCompare(iconChangedSpy, "count", 1)
             tryCompare(favicon, "status", Image.Ready)
             compare(favicon.width, 32)
             compare(favicon.height, 32)
@@ -517,8 +513,7 @@ TestWebEngineView {
             webEngineView.url = row.url
             verify(webEngineView.waitForLoadSucceeded())
 
-            iconChangedSpy.wait()
-            compare(iconChangedSpy.count, 1)
+            tryCompare(iconChangedSpy, "count", 1)
 
             var iconUrl = removeFaviconProviderPrefix(webEngineView.icon)
             compare(iconUrl, row.expectedIconUrl)
diff --git a/tests/auto/quick/qmltests/data/tst_faviconDatabase.qml b/tests/auto/quick/qmltests/data/tst_faviconDatabase.qml
index 28439061920..d0292851194 100644
--- a/tests/auto/quick/qmltests/data/tst_faviconDatabase.qml
+++ b/tests/auto/quick/qmltests/data/tst_faviconDatabase.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
@@ -19,8 +19,9 @@ TestWebEngineView {
     }
 
     property QtObject nonOTRProfile: WebEngineProfile {
-        persistentStoragePath: tempDir.path() + '/WebEngineFavicon'
         offTheRecord: false
+        storageName: 'WebEngineFavicon'
+        persistentStoragePath: tempDir.path() + '/WebEngineFavicon'
     }
 
     function getFaviconPixel(faviconImage) {
@@ -101,8 +102,7 @@ TestWebEngineView {
             webEngineView.url = Qt.resolvedUrl("favicon.html"); // favicon.png -> 165
             verify(webEngineView.waitForLoadSucceeded());
 
-            iconChangedSpy.wait();
-            compare(iconChangedSpy.count, 1);
+            tryCompare(iconChangedSpy, "count", 1);
 
             var previousIcon = webEngineView.icon;
             iconChangedSpy.clear();
@@ -148,8 +148,9 @@ TestWebEngineView {
                     TestWebEngineView {\n
                         TempDir { id: tempDir }
                         profile: WebEngineProfile {\n
-                            persistentStoragePath: tempDir.path() + '/WebEngineFavicon1'\n
                             offTheRecord: false\n
+                            storageName: 'WebEngineFavicon1'\n
+                            persistentStoragePath: tempDir.path() + '/WebEngineFavicon1'\n
                         }\n
                     }", testCase);
 
@@ -160,8 +161,9 @@ TestWebEngineView {
                     TestWebEngineView {\n
                         TempDir { id: tempDir }
                         profile: WebEngineProfile {\n
-                            persistentStoragePath: tempDir.path() + '/WebEngineFavicon2'\n
                             offTheRecord: false\n
+                            storageName: 'WebEngineFavicon2'\n
+                            persistentStoragePath: tempDir.path() + '/WebEngineFavicon2'\n
                         }\n
                     }", testCase);
 
@@ -213,4 +215,3 @@ TestWebEngineView {
         }
     }
 }
-
diff --git a/tests/auto/quick/qmltests/data/tst_filePicker.qml b/tests/auto/quick/qmltests/data/tst_filePicker.qml
index a7b59b2e9bb..8c769a04291 100644
--- a/tests/auto/quick/qmltests/data/tst_filePicker.qml
+++ b/tests/auto/quick/qmltests/data/tst_filePicker.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_filesystem.qml b/tests/auto/quick/qmltests/data/tst_filesystem.qml
index fa0da4457b9..5ed05b419dd 100644
--- a/tests/auto/quick/qmltests/data/tst_filesystem.qml
+++ b/tests/auto/quick/qmltests/data/tst_filesystem.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_findText.qml b/tests/auto/quick/qmltests/data/tst_findText.qml
index 597cff73e97..8bf50d9138e 100644
--- a/tests/auto/quick/qmltests/data/tst_findText.qml
+++ b/tests/auto/quick/qmltests/data/tst_findText.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
@@ -105,6 +105,27 @@ TestWebEngineView {
             compare(findTextSpy.signalArguments[0][0].activeMatch, 1)
         }
 
+        function test_toggleCaseSensitivity() {
+            var findFlags = 0
+            webEngineView.url = Qt.resolvedUrl("test1.html")
+            verify(webEngineView.waitForLoadSucceeded())
+
+            webEngineView.clear()
+            webEngineView.findText("heLLo", findFlags, webEngineView.findTextCallback)
+            tryCompare(webEngineView, "matchCount", 1)
+            verify(!findFailed)
+            tryCompare(findTextSpy, "count", 1)
+            compare(findTextSpy.signalArguments[0][0].numberOfMatches, 1)
+            compare(findTextSpy.signalArguments[0][0].activeMatch, 1)
+
+            findTextSpy.clear()
+            webEngineView.clear()
+            findFlags = WebEngineView.FindCaseSensitively
+            webEngineView.findText("heLLo", findFlags, webEngineView.findTextCallback)
+            tryCompare(findTextSpy, "count", 1)
+            compare(findTextSpy.signalArguments[0][0].numberOfMatches, 0)
+        }
+
         function test_findTextManyMatches() {
             var findFlags = 0
             webEngineView.url = Qt.resolvedUrl("test4.html")
diff --git a/tests/auto/quick/qmltests/data/tst_focusOnNavigation.qml b/tests/auto/quick/qmltests/data/tst_focusOnNavigation.qml
index f070e4bc5ed..b440e572099 100644
--- a/tests/auto/quick/qmltests/data/tst_focusOnNavigation.qml
+++ b/tests/auto/quick/qmltests/data/tst_focusOnNavigation.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_geopermission.qml b/tests/auto/quick/qmltests/data/tst_geopermission.qml
index c474033a977..774b2fdc7ad 100644
--- a/tests/auto/quick/qmltests/data/tst_geopermission.qml
+++ b/tests/auto/quick/qmltests/data/tst_geopermission.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
@@ -12,23 +12,25 @@ TestWebEngineView {
 
     property bool deniedGeolocation: false
     property bool geoPermissionRequested: false
+    property var permissionObject: undefined
 
-    profile.persistentPermissionsPolicy: WebEngineProfile.NoPersistentPermissions
+    profile.persistentPermissionsPolicy: WebEngineProfile.PersistentPermissionsPolicy.AskEveryTime
 
     SignalSpy {
-        id: featurePermissionSpy
+        id: permissionSpy
         target: webEngineView
-        signalName: "featurePermissionRequested"
+        signalName: "permissionRequested"
     }
 
-    onFeaturePermissionRequested: function(securityOrigin, feature) {
-        if (feature === WebEngineView.Geolocation) {
+    onPermissionRequested: function(perm) {
+        if (perm.permissionType === WebEnginePermission.PermissionType.Geolocation) {
             geoPermissionRequested = true
+            permissionObject = perm
             if (deniedGeolocation) {
-                webEngineView.grantFeaturePermission(securityOrigin, feature, false)
+                perm.deny()
             }
             else {
-                webEngineView.grantFeaturePermission(securityOrigin, feature, true)
+                perm.grant()
             }
         }
     }
@@ -56,24 +58,27 @@ TestWebEngineView {
         }
 
         function init() {
+            if (permissionObject != undefined) {
+                permissionObject.reset()
+            }
             deniedGeolocation = false
-            featurePermissionSpy.clear()
+            permissionSpy.clear()
         }
 
         function test_geoPermissionRequest() {
-            compare(featurePermissionSpy.count, 0)
+            compare(permissionSpy.count, 0)
             webEngineView.url = Qt.resolvedUrl("geolocation.html")
-            featurePermissionSpy.wait()
+            tryCompare(permissionSpy, "count", 1)
             verify(geoPermissionRequested)
-            compare(featurePermissionSpy.count, 1)
             tryVerify(isHandled, 5000)
             verify(getErrorMessage() === "")
         }
 
         function test_deniedGeolocationByUser() {
+            compare(permissionSpy.count, 0)
             deniedGeolocation = true
             webEngineView.url = Qt.resolvedUrl("geolocation.html")
-            featurePermissionSpy.wait()
+            tryCompare(permissionSpy, "count", 1)
             tryVerify(isHandled, 5000)
             compare(getErrorMessage(), "User denied Geolocation")
         }
diff --git a/tests/auto/quick/qmltests/data/tst_getUserMedia.qml b/tests/auto/quick/qmltests/data/tst_getUserMedia.qml
index 653f7a5df3d..ebb49f9df3d 100644
--- a/tests/auto/quick/qmltests/data/tst_getUserMedia.qml
+++ b/tests/auto/quick/qmltests/data/tst_getUserMedia.qml
@@ -1,9 +1,10 @@
 // Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
 import QtWebEngine
+import Test.Shared as Shared
 
 TestWebEngineView {
     id: webEngineView
@@ -11,9 +12,10 @@ TestWebEngineView {
     height: 400
 
     settings.screenCaptureEnabled: true
-    profile.persistentPermissionsPolicy: WebEngineProfile.NoPersistentPermissions
+    profile.persistentPermissionsPolicy: WebEngineProfile.PersistentPermissionsPolicy.AskEveryTime
 
     TestCase {
+        id: testCase
         name: "GetUserMedia"
         when: windowShown
 
@@ -22,17 +24,17 @@ TestWebEngineView {
                 {
                     tag: "device audio",
                     constraints: { audio: true },
-                    feature: WebEngineView.MediaAudioCapture,
+                    feature: WebEnginePermission.PermissionType.MediaAudioCapture,
                 },
                 {
                     tag: "device video",
                     constraints: { video: true },
-                    feature: WebEngineView.MediaVideoCapture,
+                    feature: WebEnginePermission.PermissionType.MediaVideoCapture,
                 },
                 {
                     tag: "device audio+video",
                     constraints: { audio: true, video: true },
-                    feature: WebEngineView.MediaAudioVideoCapture,
+                    feature: WebEnginePermission.PermissionType.MediaAudioVideoCapture,
                 },
                 {
                     tag: "desktop video",
@@ -43,7 +45,7 @@ TestWebEngineView {
                             }
                         }
                     },
-                    feature: WebEngineView.DesktopVideoCapture,
+                    feature: WebEnginePermission.PermissionType.DesktopVideoCapture,
                 },
                 {
                     tag: "desktop audio+video",
@@ -59,7 +61,7 @@ TestWebEngineView {
                             }
                         }
                     },
-                    feature: WebEngineView.DesktopAudioVideoCapture,
+                    feature: WebEnginePermission.PermissionType.DesktopAudioVideoCapture,
                 }
             ]
         }
@@ -69,9 +71,9 @@ TestWebEngineView {
 
             // 1. Rejecting request on QML side should reject promise on JS side.
             jsGetUserMedia(row.constraints)
-            tryVerify(function(){ return gotFeatureRequest(row.feature) })
+            verifyPermissionType(row.feature)
             rejectPendingRequest()
-            tryVerify(function(){ return !jsPromiseFulfilled() && jsPromiseRejected() })
+            tryVerify(jsPromiseRejected)
 
             // 2. Accepting request on QML side should either fulfill or reject the
             // Promise on JS side. Due to the potential lack of physical media devices
@@ -79,15 +81,15 @@ TestWebEngineView {
             // always be fulfilled, however in this case an error should be returned to
             // JS instead of leaving the Promise in limbo.
             jsGetUserMedia(row.constraints)
-            tryVerify(function(){ return gotFeatureRequest(row.feature) })
+            verifyPermissionType(row.feature)
             acceptPendingRequest()
-            tryVerify(function(){ return jsPromiseFulfilled() || jsPromiseRejected() });
+            tryVerify(jsPromiseSettled)
 
             // 3. Media feature permissions are not remembered.
             jsGetUserMedia(row.constraints);
-            tryVerify(function(){ return gotFeatureRequest(row.feature) })
+            verifyPermissionType(row.feature)
             acceptPendingRequest()
-            tryVerify(function(){ return jsPromiseFulfilled() || jsPromiseRejected() });
+            tryVerify(jsPromiseSettled)
         }
     }
 
@@ -116,65 +118,99 @@ TestWebEngineView {
     ////
     // synchronous permission requests
 
-    property variant requestedFeature
-    property variant requestedSecurityOrigin
+    property variant permissionObject
+    property bool isDesktopMediaRequestHandled: false
+    property bool gotEmptyDesktopMediaRequest: false
 
-    onFeaturePermissionRequested: function(securityOrigin, feature) {
-        requestedFeature = feature
-        requestedSecurityOrigin = securityOrigin
+    onPermissionRequested: function(perm) {
+        permissionObject = perm
     }
 
-    function gotFeatureRequest(expectedFeature) {
-        return requestedFeature == expectedFeature
+    onDesktopMediaRequested: function(request) {
+        gotEmptyDesktopMediaRequest = request.screensModel.rowCount() == 0
+        if (gotEmptyDesktopMediaRequest)
+            request.cancel()
+        else
+            request.selectScreen(request.screensModel.index(0, 0))
+        isDesktopMediaRequestHandled = true
+    }
+
+    function verifyPermissionType(expectedFeature) {
+        // When webrtc is disabled, desktop media requests come through as non-desktop.
+        var isDesktopPermission = Shared.TestEnvironment.hasWebRTC() &&
+                (expectedFeature == WebEnginePermission.PermissionType.DesktopAudioVideoCapture ||
+                 expectedFeature == WebEnginePermission.PermissionType.DesktopVideoCapture)
+
+        if (isDesktopPermission) {
+            testCase.tryVerify(function() { return isDesktopMediaRequestHandled })
+
+            // Request has been cancelled
+            if (gotEmptyDesktopMediaRequest) {
+                testCase.compare(permissionObject, undefined)
+                return
+            }
+        }
+
+        testCase.tryVerify(function() { return permissionObject != undefined })
+        testCase.compare(permissionObject.permissionType, expectedFeature)
     }
 
     function acceptPendingRequest() {
-        webEngineView.grantFeaturePermission(requestedSecurityOrigin, requestedFeature, true)
-        requestedFeature = undefined
-        requestedSecurityOrigin = undefined
+        if (permissionObject)
+            permissionObject.grant()
+        resetRequestState()
+    }
+
+    function resetRequestState() {
+        permissionObject = undefined
+        isDesktopMediaRequestHandled = false
+        gotEmptyDesktopMediaRequest = false
     }
 
     function rejectPendingRequest() {
-        webEngineView.grantFeaturePermission(requestedSecurityOrigin, requestedFeature, false)
-        requestedFeature = undefined
-        requestedSecurityOrigin = undefined
+        if (permissionObject)
+            permissionObject.deny()
+        resetRequestState()
     }
 
     ////
-    // synchronous JavaScript evaluation
-
-    signal runJavaScriptFinished(variant result)
+    // Intercept promise callback results
 
     SignalSpy {
-        id: spyOnRunJavaScriptFinished
+        id: promiseMessageSpy
         target: webEngineView
-        signalName: "runJavaScriptFinished"
+        signalName: "javaScriptConsoleMessage"
     }
 
-    function runJavaScriptSync(code) {
-        spyOnRunJavaScriptFinished.clear()
-        runJavaScript(code, runJavaScriptFinished)
-        spyOnRunJavaScriptFinished.wait()
-        return spyOnRunJavaScriptFinished.signalArguments[0][0]
+    function jsPromiseSettled()
+    {
+        return promiseMessageSpy.count > 0;
+    }
+
+    function jsPromiseFulfilled()
+    {
+        if (!jsPromiseSettled())
+            return false;
+
+        return promiseMessageSpy.signalArguments[0][1] === "fulfilled"
+    }
+
+    function jsPromiseRejected()
+    {
+        if (!jsPromiseSettled())
+            return false;
+
+        return promiseMessageSpy.signalArguments[0][1] === "rejected"
     }
 
     ////
     // JavaScript snippets
 
     function jsGetUserMedia(constraints) {
+        promiseMessageSpy.clear();
         runJavaScript(
-            "var promiseFulfilled = false;" +
-            "var promiseRejected = false;" +
             "navigator.mediaDevices.getUserMedia(" + JSON.stringify(constraints) + ")" +
-            ".then(stream => { promiseFulfilled = true})" +
-            ".catch(err => { promiseRejected = true})")
-    }
-
-    function jsPromiseFulfilled() {
-        return runJavaScriptSync("promiseFulfilled")
-    }
-
-    function jsPromiseRejected() {
-        return runJavaScriptSync("promiseRejected")
+            ".then(stream => { console.info('fulfilled') })" +
+            ".catch(err => { console.info('rejected') })")
     }
 }
diff --git a/tests/auto/quick/qmltests/data/tst_inputMethod.qml b/tests/auto/quick/qmltests/data/tst_inputMethod.qml
index cf79e8a4d89..d8ed8e55e90 100644
--- a/tests/auto/quick/qmltests/data/tst_inputMethod.qml
+++ b/tests/auto/quick/qmltests/data/tst_inputMethod.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_inputTextDirection.qml b/tests/auto/quick/qmltests/data/tst_inputTextDirection.qml
index 2141db4c80a..52675983a6b 100644
--- a/tests/auto/quick/qmltests/data/tst_inputTextDirection.qml
+++ b/tests/auto/quick/qmltests/data/tst_inputTextDirection.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_javaScriptDialogs.qml b/tests/auto/quick/qmltests/data/tst_javaScriptDialogs.qml
index 6e91b2e7742..49ba5ae471c 100644
--- a/tests/auto/quick/qmltests/data/tst_javaScriptDialogs.qml
+++ b/tests/auto/quick/qmltests/data/tst_javaScriptDialogs.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_keyboardEvents.qml b/tests/auto/quick/qmltests/data/tst_keyboardEvents.qml
index 0f69a7e81a7..6a67b698316 100644
--- a/tests/auto/quick/qmltests/data/tst_keyboardEvents.qml
+++ b/tests/auto/quick/qmltests/data/tst_keyboardEvents.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_keyboardModifierMapping.qml b/tests/auto/quick/qmltests/data/tst_keyboardModifierMapping.qml
index d0bc75619b4..33be0329a2e 100644
--- a/tests/auto/quick/qmltests/data/tst_keyboardModifierMapping.qml
+++ b/tests/auto/quick/qmltests/data/tst_keyboardModifierMapping.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_linkHovered.qml b/tests/auto/quick/qmltests/data/tst_linkHovered.qml
index a11bd2450bd..593beb7356a 100644
--- a/tests/auto/quick/qmltests/data/tst_linkHovered.qml
+++ b/tests/auto/quick/qmltests/data/tst_linkHovered.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_loadFail.qml b/tests/auto/quick/qmltests/data/tst_loadFail.qml
index 8e9224bbf92..b6d3419f7c6 100644
--- a/tests/auto/quick/qmltests/data/tst_loadFail.qml
+++ b/tests/auto/quick/qmltests/data/tst_loadFail.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_loadHtml.qml b/tests/auto/quick/qmltests/data/tst_loadHtml.qml
index 8f94cd4a27d..db2035842e9 100644
--- a/tests/auto/quick/qmltests/data/tst_loadHtml.qml
+++ b/tests/auto/quick/qmltests/data/tst_loadHtml.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_loadProgress.qml b/tests/auto/quick/qmltests/data/tst_loadProgress.qml
index 2c06a02070f..30995d276ad 100644
--- a/tests/auto/quick/qmltests/data/tst_loadProgress.qml
+++ b/tests/auto/quick/qmltests/data/tst_loadProgress.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_loadRecursionCrash.qml b/tests/auto/quick/qmltests/data/tst_loadRecursionCrash.qml
index c0eb5932b91..d6e6e108aaa 100644
--- a/tests/auto/quick/qmltests/data/tst_loadRecursionCrash.qml
+++ b/tests/auto/quick/qmltests/data/tst_loadRecursionCrash.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_loadUrl.qml b/tests/auto/quick/qmltests/data/tst_loadUrl.qml
index 25a62c8787c..e43c8d5d983 100644
--- a/tests/auto/quick/qmltests/data/tst_loadUrl.qml
+++ b/tests/auto/quick/qmltests/data/tst_loadUrl.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
@@ -265,7 +265,6 @@ TestWebEngineView {
             compare(loadRequest.status, WebEngineView.LoadStoppedStatus);
             compare(loadRequest.status, WebEngineLoadingInfo.LoadStoppedStatus);
             compare(loadRequest.url, stoppedUrl);
-            compare(loadRequest.activeUrl, initialUrl);
             webEngineView.clear();
         }
 
diff --git a/tests/auto/quick/qmltests/data/tst_mouseClick.qml b/tests/auto/quick/qmltests/data/tst_mouseClick.qml
index c0c6a696798..cc06416c171 100644
--- a/tests/auto/quick/qmltests/data/tst_mouseClick.qml
+++ b/tests/auto/quick/qmltests/data/tst_mouseClick.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_mouseMove.qml b/tests/auto/quick/qmltests/data/tst_mouseMove.qml
index 5ded24c57c5..6ee58fcb030 100644
--- a/tests/auto/quick/qmltests/data/tst_mouseMove.qml
+++ b/tests/auto/quick/qmltests/data/tst_mouseMove.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_navigationHistory.qml b/tests/auto/quick/qmltests/data/tst_navigationHistory.qml
index 2ea76c3872a..54d2ffd4bcc 100644
--- a/tests/auto/quick/qmltests/data/tst_navigationHistory.qml
+++ b/tests/auto/quick/qmltests/data/tst_navigationHistory.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_navigationRequested.qml b/tests/auto/quick/qmltests/data/tst_navigationRequested.qml
index 31c0cf44e31..785cfa27abd 100644
--- a/tests/auto/quick/qmltests/data/tst_navigationRequested.qml
+++ b/tests/auto/quick/qmltests/data/tst_navigationRequested.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_newViewRequest.qml b/tests/auto/quick/qmltests/data/tst_newViewRequest.qml
index 68350d10737..67142677144 100644
--- a/tests/auto/quick/qmltests/data/tst_newViewRequest.qml
+++ b/tests/auto/quick/qmltests/data/tst_newViewRequest.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_notification.qml b/tests/auto/quick/qmltests/data/tst_notification.qml
index 0bb33b41ebd..7ebc628dcf9 100644
--- a/tests/auto/quick/qmltests/data/tst_notification.qml
+++ b/tests/auto/quick/qmltests/data/tst_notification.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
@@ -13,23 +13,24 @@ TestWebEngineView {
 
     property bool permissionRequested: false
     property bool grantPermission: false
-    property url securityOrigin: ''
+    property var permissionObject
 
-    profile.persistentPermissionsPolicy: WebEngineProfile.NoPersistentPermissions
-
-    signal consoleMessage(string message)
+    profile.persistentPermissionsPolicy: WebEngineProfile.PersistentPermissionsPolicy.AskEveryTime
 
     SignalSpy {
         id: spyRequest
         target: view
-        signalName: 'featurePermissionRequested'
+        signalName: 'permissionRequested'
     }
 
-    onFeaturePermissionRequested: function(securityOrigin, feature) {
-        if (feature === WebEngineView.Notifications) {
+    onPermissionRequested: function(perm) {
+        if (perm.permissionType === WebEnginePermission.PermissionType.Notifications) {
             view.permissionRequested = true
-            view.securityOrigin = securityOrigin
-            view.grantFeaturePermission(securityOrigin, feature, grantPermission)
+            view.permissionObject = perm
+            if (grantPermission)
+                perm.grant()
+            else
+                perm.deny()
         }
     }
 
@@ -38,11 +39,13 @@ TestWebEngineView {
         when: windowShown
 
         function resolverUrl(html) {
-            console.log(Shared.HttpServer.sharedDataDir())
             return Qt.resolvedUrl(Shared.HttpServer.sharedDataDir() + "/" + html)
         }
 
         function init() {
+            if (permissionObject != undefined) {
+                permissionObject.reset()
+            }
             permissionRequested = false
             spyRequest.clear()
         }
@@ -60,16 +63,14 @@ TestWebEngineView {
             view.url = resolverUrl('notification.html')
             verify(view.waitForLoadSucceeded())
 
-            view.runJavaScript('resetPermission()')
             let result = {}
 
             view.runJavaScript('getPermission()', function (permission) { result.permission = permission })
             tryCompare(result, 'permission', 'default')
 
             view.runJavaScript('requestPermission()')
-            spyRequest.wait()
+            tryCompare(spyRequest, "count", 1)
             verify(permissionRequested)
-            compare(spyRequest.count, 1)
 
             view.runJavaScript('getPermission()', function (permission) { result.permission = permission })
             tryCompare(result, 'permission', data.permission)
@@ -82,7 +83,7 @@ TestWebEngineView {
             view.waitForLoadSucceeded()
 
             view.runJavaScript('requestPermission()')
-            spyRequest.wait()
+            tryCompare(spyRequest, "count", 1)
             verify(permissionRequested)
 
             let title = 'Title', message = 'Message', notification = null
@@ -93,7 +94,7 @@ TestWebEngineView {
             compare(notification.title, title)
             compare(notification.message, message)
             compare(notification.direction, Qt.RightToLeft)
-            compare(notification.origin, securityOrigin)
+            compare(notification.origin, permissionObject.origin)
             compare(notification.tag, 'tst')
             compare(notification.language, 'de')
         }
diff --git a/tests/auto/quick/qmltests/data/tst_properties.qml b/tests/auto/quick/qmltests/data/tst_properties.qml
index 13d40ed116d..e3d6ed6254f 100644
--- a/tests/auto/quick/qmltests/data/tst_properties.qml
+++ b/tests/auto/quick/qmltests/data/tst_properties.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_runJavaScript.qml b/tests/auto/quick/qmltests/data/tst_runJavaScript.qml
index f16cd9c419f..542a3aac697 100644
--- a/tests/auto/quick/qmltests/data/tst_runJavaScript.qml
+++ b/tests/auto/quick/qmltests/data/tst_runJavaScript.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_scrollPosition.qml b/tests/auto/quick/qmltests/data/tst_scrollPosition.qml
index cc7d15e4c77..e90f879139d 100644
--- a/tests/auto/quick/qmltests/data/tst_scrollPosition.qml
+++ b/tests/auto/quick/qmltests/data/tst_scrollPosition.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_settings.qml b/tests/auto/quick/qmltests/data/tst_settings.qml
index f47674aa7fa..4cd0bf6a7a2 100644
--- a/tests/auto/quick/qmltests/data/tst_settings.qml
+++ b/tests/auto/quick/qmltests/data/tst_settings.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_titleChanged.qml b/tests/auto/quick/qmltests/data/tst_titleChanged.qml
index 66a7c115f79..16dafa5932d 100644
--- a/tests/auto/quick/qmltests/data/tst_titleChanged.qml
+++ b/tests/auto/quick/qmltests/data/tst_titleChanged.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_unhandledKeyEventPropagation.qml b/tests/auto/quick/qmltests/data/tst_unhandledKeyEventPropagation.qml
index 76363fa7113..75d0d38d9ac 100644
--- a/tests/auto/quick/qmltests/data/tst_unhandledKeyEventPropagation.qml
+++ b/tests/auto/quick/qmltests/data/tst_unhandledKeyEventPropagation.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_userScriptCollection.qml b/tests/auto/quick/qmltests/data/tst_userScriptCollection.qml
index 94c9937715e..fbf4f083332 100644
--- a/tests/auto/quick/qmltests/data/tst_userScriptCollection.qml
+++ b/tests/auto/quick/qmltests/data/tst_userScriptCollection.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_userScripts.qml b/tests/auto/quick/qmltests/data/tst_userScripts.qml
index 30704f47b4f..a64de389d8e 100644
--- a/tests/auto/quick/qmltests/data/tst_userScripts.qml
+++ b/tests/auto/quick/qmltests/data/tst_userScripts.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_viewSource.qml b/tests/auto/quick/qmltests/data/tst_viewSource.qml
index d4449f7de55..a79de3e3a2d 100644
--- a/tests/auto/quick/qmltests/data/tst_viewSource.qml
+++ b/tests/auto/quick/qmltests/data/tst_viewSource.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/data/tst_webchannel.qml b/tests/auto/quick/qmltests/data/tst_webchannel.qml
index 780b559341b..0000a212557 100644
--- a/tests/auto/quick/qmltests/data/tst_webchannel.qml
+++ b/tests/auto/quick/qmltests/data/tst_webchannel.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
diff --git a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/AlertDialog.qml b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/AlertDialog.qml
index 7d7efda0c0c..424c3fdb26a 100644
--- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/AlertDialog.qml
+++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/AlertDialog.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 // Both dialogs are basically expected to behave in the same way from an API point of view
 ConfirmDialog { }
diff --git a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/ConfirmDialog.qml b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/ConfirmDialog.qml
index 6125d0b9862..db7c2d05aa0 100644
--- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/ConfirmDialog.qml
+++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/ConfirmDialog.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQml
 import QtTest
diff --git a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/DirectoryPicker.qml b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/DirectoryPicker.qml
index 71da28843fc..da423e44128 100644
--- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/DirectoryPicker.qml
+++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/DirectoryPicker.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import "../../TestParams"
diff --git a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/FilePicker.qml b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/FilePicker.qml
index 247088bcba6..6dac9973e17 100644
--- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/FilePicker.qml
+++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/FilePicker.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import "../../TestParams"
diff --git a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/Menu.qml b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/Menu.qml
index cd7ed48211c..35e3b274f94 100644
--- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/Menu.qml
+++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/Menu.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQml
 import "../../TestParams"
diff --git a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/MenuItem.qml b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/MenuItem.qml
index 67dab1bba40..8d3a2df5153 100644
--- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/MenuItem.qml
+++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/MenuItem.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQml
 
diff --git a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/PromptDialog.qml b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/PromptDialog.qml
index 81a63d91843..62e48906751 100644
--- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/PromptDialog.qml
+++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/PromptDialog.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQml
 import QtTest
diff --git a/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml b/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml
index 67d67dc4039..342933365d6 100644
--- a/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml
+++ b/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 pragma Singleton
 import QtQuick
diff --git a/tests/auto/quick/qmltests/mock-delegates/TestParams/JSDialogParams.qml b/tests/auto/quick/qmltests/mock-delegates/TestParams/JSDialogParams.qml
index 1033b509efc..4f983119721 100644
--- a/tests/auto/quick/qmltests/mock-delegates/TestParams/JSDialogParams.qml
+++ b/tests/auto/quick/qmltests/mock-delegates/TestParams/JSDialogParams.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 pragma Singleton
 import QtQml
 
diff --git a/tests/auto/quick/qmltests/mock-delegates/TestParams/MenuParams.qml b/tests/auto/quick/qmltests/mock-delegates/TestParams/MenuParams.qml
index d8a01764c90..8b6bcb28587 100644
--- a/tests/auto/quick/qmltests/mock-delegates/TestParams/MenuParams.qml
+++ b/tests/auto/quick/qmltests/mock-delegates/TestParams/MenuParams.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 pragma Singleton
 import QtQml
diff --git a/tests/auto/quick/qmltests/tst_qmltests.cpp b/tests/auto/quick/qmltests/tst_qmltests.cpp
index 9e928157e9f..a603c61bcda 100644
--- a/tests/auto/quick/qmltests/tst_qmltests.cpp
+++ b/tests/auto/quick/qmltests/tst_qmltests.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <httpserver.h>
 
@@ -17,8 +17,12 @@
 #include <QtTest/qtest.h>
 #include <QtWebEngineQuick/qquickwebengineprofile.h>
 #include <QtWebEngineQuick/qtwebenginequickglobal.h>
+#include <QtWebEngineCore/qtwebenginecore-config.h>
+#include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h>
 #include <qt_webengine_quicktest.h>
 
+#include <cstdio>
+
 #if defined(Q_OS_LINUX) && defined(QT_DEBUG)
 #include <fcntl.h>
 #include <signal.h>
@@ -75,7 +79,7 @@ static void stackTrace()
 
     fprintf(stderr, "\n========= Received signal, dumping stack ==============\n");
     char cmd[512];
-    qsnprintf(cmd, 512, "gdb --pid %d 2>/dev/null <<EOF\n"
+    std::snprintf(cmd, 512, "gdb --pid %d 2>/dev/null <<EOF\n"
                         "set prompt\n"
                         "set height 0\n"
                         "thread apply all where full\n"
@@ -96,6 +100,14 @@ static void sigSegvHandler(int signum)
 }
 #endif
 
+class TestEnvironment : public QObject
+{
+    Q_OBJECT
+
+public:
+    Q_INVOKABLE bool hasWebRTC() const { return QT_CONFIG(webengine_webrtc); }
+};
+
 class TempDir : public QObject {
     Q_OBJECT
 
@@ -252,6 +264,9 @@ int main(int argc, char **argv)
     qmlRegisterType<TestInputEvent>("Test.util", 1, 0, "TestInputEvent");
 
     QTEST_SET_MAIN_SOURCE_PATH
+    qmlRegisterSingletonType<TestEnvironment>(
+            "Test.Shared", 1, 0, "TestEnvironment",
+            [&](QQmlEngine *, QJSEngine *) { return new TestEnvironment; });
     qmlRegisterSingletonType<HttpServer>("Test.Shared", 1, 0, "HttpServer", [&] (QQmlEngine *, QJSEngine *) {
         auto server = new HttpServer;
         server->setResourceDirs(
diff --git a/tests/auto/quick/qquickwebenginedefaultsurfaceformat/tst_qquickwebenginedefaultsurfaceformat.cpp b/tests/auto/quick/qquickwebenginedefaultsurfaceformat/tst_qquickwebenginedefaultsurfaceformat.cpp
index b4c95d67126..5b5ef04f252 100644
--- a/tests/auto/quick/qquickwebenginedefaultsurfaceformat/tst_qquickwebenginedefaultsurfaceformat.cpp
+++ b/tests/auto/quick/qquickwebenginedefaultsurfaceformat/tst_qquickwebenginedefaultsurfaceformat.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include "testwindow.h"
 #include "quickutil.h"
@@ -90,8 +90,8 @@ void tst_QQuickWebEngineDefaultSurfaceFormat::customDefaultSurfaceFormat()
     format.setProfile( QSurfaceFormat::CoreProfile );
     QSurfaceFormat::setDefaultFormat( format );
 
-    QGuiApplication app(argc, argv);
     QtWebEngineQuick::initialize();
+    QGuiApplication app(argc, argv);
 
     initEngineAndViewComponent();
     initWindow();
diff --git a/tests/auto/quick/qquickwebengineview/BLACKLIST b/tests/auto/quick/qquickwebengineview/BLACKLIST
index d4a35a76a57..5293f7259d9 100644
--- a/tests/auto/quick/qquickwebengineview/BLACKLIST
+++ b/tests/auto/quick/qquickwebengineview/BLACKLIST
@@ -1,2 +1,5 @@
 [javascriptClipboard]
 ubuntu-20.04
+
+[htmlSelectPopup]
+ubuntu-24.04 offscreen # QTBUG-128653
diff --git a/tests/auto/quick/qquickwebengineview/html/resources/hello.js b/tests/auto/quick/qquickwebengineview/html/resources/hello.js
new file mode 100644
index 00000000000..6f5e79ebf4d
--- /dev/null
+++ b/tests/auto/quick/qquickwebengineview/html/resources/hello.js
@@ -0,0 +1,7 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+function sayHello()
+{
+    console.warn("hello");
+}
diff --git a/tests/auto/quick/qquickwebengineview/html/resources/hi.js b/tests/auto/quick/qquickwebengineview/html/resources/hi.js
new file mode 100644
index 00000000000..30e7cc972d1
--- /dev/null
+++ b/tests/auto/quick/qquickwebengineview/html/resources/hi.js
@@ -0,0 +1,8 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+function sayHi()
+{
+    // Placeholder
+    console.warn("hi");
+}
diff --git a/tests/auto/quick/qquickwebengineview/html/script2.html b/tests/auto/quick/qquickwebengineview/html/script2.html
new file mode 100644
index 00000000000..5faf58daab3
--- /dev/null
+++ b/tests/auto/quick/qquickwebengineview/html/script2.html
@@ -0,0 +1,4 @@
+<html><head>
+<script language="javascript" type="text/javascript" src="resources/hello.js"></script>
+<script language="javascript" type="text/javascript" src="resources/hi.js"></script>
+</head></html>
diff --git a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp
index dbfa1cb3350..eae1b1c9d1b 100644
--- a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp
+++ b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp
@@ -1,11 +1,11 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
+#include "QtWidgets/qapplication.h"
 #include "testwindow.h"
 #include "quickutil.h"
 #include "util.h"
+#include "visualutil.h"
 
 #include <QScopedPointer>
 #include <QtCore/qelapsedtimer.h>
@@ -15,11 +15,11 @@
 #include <QtGui/qpa/qwindowsysteminterface.h>
 #include <QtQml/QQmlEngine>
 #include <QtTest/QtTest>
+#include <QtWebEngineQuick/QQuickWebEngineDownloadRequest>
 #include <QtWebEngineQuick/QQuickWebEngineProfile>
 #include <QtGui/private/qinputmethod_p.h>
 #include <QtWebEngineQuick/private/qquickwebenginescriptcollection_p.h>
 #include <QtWebEngineQuick/private/qquickwebenginesettings_p.h>
-#include <QtWebEngineQuick/private/qquickwebenginedownloadrequest_p.h>
 #include <QtWebEngineQuick/private/qquickwebengineview_p.h>
 #include <QtWebEngineCore/private/qtwebenginecore-config_p.h>
 #include <qpa/qplatforminputcontext.h>
@@ -27,6 +27,8 @@
 
 #include <functional>
 
+using namespace Qt::StringLiterals;
+
 class tst_QQuickWebEngineView : public QObject {
     Q_OBJECT
 public:
@@ -76,10 +78,12 @@ private Q_SLOTS:
 #if QT_CONFIG(accessibility)
     void focusChild_data();
     void focusChild();
+    void accessibilityRect();
 #endif
     void htmlSelectPopup();
     void savePage_data();
     void savePage();
+    void javaScriptConsoleMessage();
 
 private:
     inline QQuickWebEngineView *newWebEngineView();
@@ -98,8 +102,8 @@ private Q_SLOTS:
 
 tst_QQuickWebEngineView::tst_QQuickWebEngineView()
 {
-    QtWebEngineQuick::initialize();
-    QQuickWebEngineProfile::defaultProfile()->setOffTheRecord(true);
+
+    QVERIFY(QQuickWebEngineProfile::defaultProfile()->isOffTheRecord());
 
     m_testSourceDirPath = QDir(QT_TESTCASE_SOURCEDIR).canonicalPath();
     if (!m_testSourceDirPath.endsWith(QLatin1Char('/')))
@@ -445,7 +449,11 @@ void tst_QQuickWebEngineView::transparentWebEngineViews()
 
 void tst_QQuickWebEngineView::inputMethod()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
     m_window->show();
+    m_window->requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(m_window.get()));
     QQuickItem *input;
 
     QQuickWebEngineView *view = webEngineView();
@@ -547,8 +555,11 @@ void tst_QQuickWebEngineView::interruptImeTextComposition_data()
 
 void tst_QQuickWebEngineView::interruptImeTextComposition()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
     m_window->show();
-    QTRY_VERIFY(qApp->focusObject());
+    m_window->requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(m_window.get()));
     QQuickItem *input;
 
     QQuickWebEngineView *view = webEngineView();
@@ -597,8 +608,11 @@ void tst_QQuickWebEngineView::interruptImeTextComposition()
 
 void tst_QQuickWebEngineView::inputContextQueryInput()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
     m_window->show();
-    QTRY_VERIFY(qApp->focusObject());
+    m_window->requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(m_window.get()));
     TestInputContext testContext;
 
     QQuickWebEngineView *view = webEngineView();
@@ -614,7 +628,7 @@ void tst_QQuickWebEngineView::inputContextQueryInput()
     QTest::mouseClick(view->window(), Qt::LeftButton, {}, textInputCenter);
     QTRY_COMPARE(testContext.infos.size(), 2);
     QCOMPARE(evaluateJavaScriptSync(view, "document.activeElement.id").toString(), QStringLiteral("input1"));
-    foreach (const InputMethodInfo &info, testContext.infos) {
+    for (const InputMethodInfo &info : std::as_const(testContext.infos)) {
         QCOMPARE(info.cursorPosition, 0);
         QCOMPARE(info.anchorPosition, 0);
         QCOMPARE(info.surroundingText, QStringLiteral(""));
@@ -709,7 +723,7 @@ void tst_QQuickWebEngineView::inputContextQueryInput()
         QGuiApplication::sendEvent(qApp->focusObject(), &event);
     }
     QTRY_COMPARE(testContext.infos.size(), 2);
-    foreach (const InputMethodInfo &info, testContext.infos) {
+    for (const InputMethodInfo &info : std::as_const(testContext.infos)) {
         QCOMPARE(info.cursorPosition, 0);
         QCOMPARE(info.anchorPosition, 0);
         QCOMPARE(info.surroundingText, QStringLiteral("QtWebEngine!"));
@@ -742,8 +756,11 @@ void tst_QQuickWebEngineView::inputContextQueryInput()
 
 void tst_QQuickWebEngineView::inputMethodHints()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
     m_window->show();
-    QTRY_VERIFY(qApp->focusObject());
+    m_window->requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(m_window.get()));
     QQuickItem *input;
 
     QQuickWebEngineView *view = webEngineView();
@@ -799,7 +816,7 @@ void tst_QQuickWebEngineView::setZoomFactor()
     view->setZoomFactor(2.5);
     QCOMPARE(view->zoomFactor(), 2.5);
 
-    const QUrl url1 = urlFromTestPath("html/basic_page.html"), url2 = urlFromTestPath("html/basic_page2.html");
+    const QUrl url1 = urlFromTestPath("html/basic_page.html");
 
     view->setUrl(url1);
     QVERIFY(waitForLoadSucceeded(view));
@@ -816,20 +833,22 @@ void tst_QQuickWebEngineView::setZoomFactor()
     view2->setParentItem(m_window->contentItem());
 
     // try loading different url and check new values after load
-    for (auto &&p : {
-            qMakePair(view, 2.5), // navigating away to different url should keep zoom
-            qMakePair(view2.get(), 1.0), // same url navigation in diffent page shouldn't be affected
-        }) {
-        auto &&view = p.first; auto zoomFactor = p.second;
-        view->setUrl(url2);
-        QVERIFY(waitForLoadSucceeded(view));
-        QCOMPARE(view->zoomFactor(), zoomFactor);
-    }
+    const QUrl url2 = urlFromTestPath("html/basic_page2.html");
+
+    // navigating away to different url should keep zoom
+    view->setUrl(url2);
+    QVERIFY(waitForLoadSucceeded(view));
+    QCOMPARE(view->zoomFactor(), 2.5);
 
-    // should have no influence on first page
+    // same url navigation in different view shouldn't be affected
+    view2->setUrl(url2);
+    QVERIFY(waitForLoadSucceeded(view2.get()));
+    QCOMPARE(view2->zoomFactor(), 1.0);
+
+    // should have no influence on first view
     view2->setZoomFactor(3.5);
-    for (auto &&p : { qMakePair(view, 2.5), qMakePair(view2.get(), 3.5), })
-        QCOMPARE(p.first->zoomFactor(), p.second);
+    QCOMPARE(view->zoomFactor(), 2.5);
+    QCOMPARE(view2->zoomFactor(), 3.5);
 }
 
 void tst_QQuickWebEngineView::printToPdf()
@@ -843,7 +862,7 @@ void tst_QQuickWebEngineView::printToPdf()
     view->setUrl(urlFromTestPath("html/basic_page.html"));
     QVERIFY(waitForLoadSucceeded(view));
 
-    QSignalSpy savePdfSpy(view, SIGNAL(pdfPrintingFinished(const QString&, bool)));
+    QSignalSpy savePdfSpy(view, SIGNAL(pdfPrintingFinished(QString,bool)));
     QString path = tempDir.path() + "/print_success.pdf";
     view->printToPdf(path, QQuickWebEngineView::A4, QQuickWebEngineView::Portrait);
     QTRY_VERIFY2(savePdfSpy.size() == 1, "Printing to PDF file failed without signal");
@@ -1152,9 +1171,9 @@ void tst_QQuickWebEngineView::javascriptClipboard()
                 "if (result.state == 'prompt') accessPrompt = true;"
             "})"));
 
-    QTRY_COMPARE(evaluateJavaScriptSync(view, "accessGranted").toBool(), javascriptCanAccessClipboard && javascriptCanPaste);
+    QTRY_COMPARE(evaluateJavaScriptSync(view, "accessGranted").toBool(), javascriptCanAccessClipboard);
     QTRY_COMPARE(evaluateJavaScriptSync(view, "accessDenied").toBool(), false);
-    QTRY_COMPARE(evaluateJavaScriptSync(view, "accessPrompt").toBool(), !javascriptCanAccessClipboard || !javascriptCanPaste);
+    QTRY_COMPARE(evaluateJavaScriptSync(view, "accessPrompt").toBool(), !javascriptCanAccessClipboard);
 
     evaluateJavaScriptSync(view,
         QStringLiteral(
@@ -1251,6 +1270,74 @@ void tst_QQuickWebEngineView::focusChild()
     // <html> -> <body> -> <input>
     QCOMPARE(traverseToWebDocumentAccessibleInterface(iface)->child(0)->child(0), iface->focusChild());
 }
+
+void tst_QQuickWebEngineView::accessibilityRect()
+{
+    auto *engine = new QQmlEngine(this);
+    auto *component = new QQmlComponent(engine, this);
+    component->setData(QByteArrayLiteral("import QtQuick\n"
+                                           "import QtWebEngine\n"
+                                           "Window {\n"
+                                           " visible: true; width: 600; height: 400\n"
+                                           " Text { id: textId; text: \"text\"; width: 100; Accessible.focusable: true; Accessible.name: \"text\" }\n"
+                                           " WebEngineView { anchors.left: textId.right; anchors.top: textId.bottom; anchors.right: parent.right; anchors.bottom: parent.bottom }\n"
+                                           "}")
+                         , QUrl());
+    QObject *rootObject = component->create();
+    QVERIFY(rootObject);
+
+    QQuickWebEngineView *webView = rootObject->findChild<QQuickWebEngineView*>();
+    QVERIFY(webView);
+
+    webView->loadHtml("<html><body bgcolor=\"red\"><input type='text' id='input1' /></body></html>");
+    QVERIFY(waitForLoadSucceeded(webView));
+
+    QAccessibleInterface *rootObjectIface = QAccessible::queryAccessibleInterface(rootObject);
+    QVERIFY(rootObjectIface);
+    QCOMPARE(rootObjectIface->childCount(), 2);
+
+    QAccessibleInterface *textIface = rootObjectIface->child(0);
+    QVERIFY(textIface);
+    QCOMPARE(textIface->role(), QAccessible::StaticText);
+
+    QCOMPARE(textIface->rect().width(), 100);
+    QVERIFY(textIface->rect().height() > 0);
+
+    // It takes a while for the webIface to get its width, unfortunately we can't have a
+    // QTRY_COMPARE since it seems in some platforms the iface gets recreated and we end up
+    // accessible the wrong pointer, so roll up our own try+compare
+    QAccessibleInterface *webIface = nullptr;
+    QElapsedTimer t;
+    t.start();
+    bool isWebIfaceOfCorrectWidth = false;
+    while (!isWebIfaceOfCorrectWidth && t.elapsed() < 5000) {
+        QTest::qWait(100);
+        webIface = rootObjectIface->child(1)->child(0);
+        QVERIFY(webIface);
+        QCOMPARE(webIface->role(), QAccessible::WebDocument);
+        isWebIfaceOfCorrectWidth = webIface->rect().width() == 500;
+    }
+
+    QVERIFY(isWebIfaceOfCorrectWidth);
+    QCOMPARE(webIface->rect().height(), 400 - textIface->rect().height());
+    QCOMPARE(webIface->rect().x(), textIface->rect().x() + textIface->rect().width());
+    QCOMPARE(webIface->rect().y(), textIface->rect().y() + textIface->rect().height());
+
+    // Set active focus on the input field.
+    webView->runJavaScript("document.getElementById('input1').focus();");
+    QTRY_COMPARE(evaluateJavaScriptSync(webView, "document.activeElement.id").toString(), QStringLiteral("input1"));
+
+    // Check that children of the web rect are inside it
+    QAccessibleInterface *inputIface = webIface->focusChild();
+    QVERIFY(inputIface);
+    QTRY_COMPARE(inputIface->role(), QAccessible::EditableText);
+    QVERIFY(webIface->rect().contains(inputIface->rect()));
+
+    delete rootObject;
+    delete component;
+    delete engine;
+}
+
 #endif // QT_CONFIG(accessibility)
 
 void tst_QQuickWebEngineView::htmlSelectPopup()
@@ -1272,7 +1359,14 @@ void tst_QQuickWebEngineView::htmlSelectPopup()
 
     makeTouch(view.window(), elementCenter(&view, "select"));
     QPointer<QQuickWindow> popup;
-    QTRY_VERIFY((popup = m_window->findChild<QQuickWindow *>()));
+    auto findPopup = [](QQuickView *view) -> QQuickWindow * {
+        for (auto window : QApplication::topLevelWindows()) {
+            if (window->transientParent() == view)
+                return dynamic_cast<QQuickWindow *>(window);
+        }
+        return nullptr;
+    };
+    QTRY_VERIFY((popup = findPopup(m_window.get())));
     QCOMPARE(activeElementId(&view), QStringLiteral("select"));
 
     makeTouch(popup, QPoint(popup->width() / 2, popup->height() / 2));
@@ -1360,6 +1454,82 @@ void tst_QQuickWebEngineView::savePage()
              originalData);
 }
 
+class TestJSMessageHandler
+{
+public:
+    inline static QList<QtMsgType> levels;
+    inline static QStringList messages;
+    inline static QList<int> lineNumbers;
+    inline static QStringList sourceIDs;
+
+    static void handler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
+    {
+        if (strcmp(context.category, "js") != 0) {
+            m_originalHandler(type, context, msg);
+            return;
+        }
+
+        levels.append(type);
+        messages.append(msg);
+        lineNumbers.append(context.line);
+        sourceIDs.append(context.file);
+    }
+
+    TestJSMessageHandler() { m_originalHandler = qInstallMessageHandler(handler); }
+    ~TestJSMessageHandler() { qInstallMessageHandler(m_originalHandler); }
+
+private:
+    inline static QtMessageHandler m_originalHandler = nullptr;
+};
+
+void tst_QQuickWebEngineView::javaScriptConsoleMessage()
+{
+    QQuickWebEngineView *view = webEngineView();
+
+    // Test QQuickWebEngineView::javaScriptConsoleMessage() signal.
+    {
+        QSignalSpy jsSpy(
+                view,
+                SIGNAL(javaScriptConsoleMessage(QQuickWebEngineView::JavaScriptConsoleMessageLevel,
+                                                const QString &, int, const QString &)));
+        view->setUrl(urlFromTestPath("html/script2.html"));
+        QVERIFY(waitForLoadSucceeded(view));
+
+        runJavaScript("sayHello()");
+        QTRY_COMPARE(jsSpy.size(), 1);
+        QCOMPARE(jsSpy.last().at(0).toInt(), QWebEnginePage::WarningMessageLevel);
+        QCOMPARE(jsSpy.last().at(1).toString(), "hello"_L1);
+        QCOMPARE(jsSpy.last().at(2).toInt(), 6);
+        QCOMPARE(jsSpy.last().at(3).toString(), urlFromTestPath("html/resources/hello.js"));
+
+        runJavaScript("sayHi()");
+        QTRY_COMPARE(jsSpy.size(), 2);
+        QCOMPARE(jsSpy.last().at(0).toInt(), QWebEnginePage::WarningMessageLevel);
+        QCOMPARE(jsSpy.last().at(1).toString(), "hi"_L1);
+        QCOMPARE(jsSpy.last().at(2).toInt(), 7);
+        QCOMPARE(jsSpy.last().at(3).toString(), urlFromTestPath("html/resources/hi.js"));
+    }
+
+    // Test default QQuickWebEngineViewPrivate::javaScriptConsoleMessage() handler.
+    {
+        TestJSMessageHandler handler;
+        view->setUrl(urlFromTestPath("html/script2.html"));
+        QVERIFY(waitForLoadSucceeded(view));
+
+        evaluateJavaScriptSync(view, "sayHello()");
+        QCOMPARE(handler.levels.last(), QtMsgType::QtWarningMsg);
+        QCOMPARE(handler.messages.last(), "hello"_L1);
+        QCOMPARE(handler.lineNumbers.last(), 6);
+        QCOMPARE(handler.sourceIDs.last(), urlFromTestPath("html/resources/hello.js"));
+
+        evaluateJavaScriptSync(view, "sayHi()");
+        QCOMPARE(handler.levels.last(), QtMsgType::QtWarningMsg);
+        QCOMPARE(handler.messages.last(), "hi"_L1);
+        QCOMPARE(handler.lineNumbers.last(), 7);
+        QCOMPARE(handler.sourceIDs.last(), urlFromTestPath("html/resources/hi.js"));
+    }
+}
+
 #if QT_CONFIG(accessibility)
 static QByteArrayList params = QByteArrayList()
     << "--force-renderer-accessibility";
diff --git a/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp b/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp
index 3644ac48187..b7cc8e7deb7 100644
--- a/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp
+++ b/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <quickutil.h>
 #include <QtTest/QtTest>
diff --git a/tests/auto/quick/qtbug-70248/tst_qtbug-70248.cpp b/tests/auto/quick/qtbug-70248/tst_qtbug-70248.cpp
index cf5c187c35c..7368309c959 100644
--- a/tests/auto/quick/qtbug-70248/tst_qtbug-70248.cpp
+++ b/tests/auto/quick/qtbug-70248/tst_qtbug-70248.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include "qtwebenginequickglobal.h"
 #include <QQuickWebEngineProfile>
@@ -11,14 +11,18 @@
 class tst_qtbug_70248: public QObject {
     Q_OBJECT
 public:
-    tst_qtbug_70248(){}
+    static void initMain();
 private slots:
     void test();
 };
 
-void tst_qtbug_70248::test()
+void tst_qtbug_70248::initMain()
 {
     QtWebEngineQuick::initialize();
+}
+
+void tst_qtbug_70248::test()
+{
     QScopedPointer<QQmlApplicationEngine> engine;
     QQuickWebEngineProfile::defaultProfile()->setOffTheRecord(true);
     engine.reset(new QQmlApplicationEngine());
diff --git a/tests/auto/quick/uidelegates/tst_uidelegates.cpp b/tests/auto/quick/uidelegates/tst_uidelegates.cpp
index fb8734f83a7..25702f4b870 100644
--- a/tests/auto/quick/uidelegates/tst_uidelegates.cpp
+++ b/tests/auto/quick/uidelegates/tst_uidelegates.cpp
@@ -1,8 +1,9 @@
 // Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include "testwindow.h"
 #include "quickutil.h"
+#include "visualutil.h"
 
 #include <QScopedPointer>
 #include <QtQml/QQmlEngine>
@@ -16,6 +17,8 @@ class tst_UIDelegates : public QObject
 {
     Q_OBJECT
 public:
+    static void initMain();
+
     tst_UIDelegates();
 
 private Q_SLOTS:
@@ -39,9 +42,13 @@ private Q_SLOTS:
     QScopedPointer<QQmlComponent> m_component;
 };
 
-tst_UIDelegates::tst_UIDelegates()
+void tst_UIDelegates::initMain()
 {
     QtWebEngineQuick::initialize();
+}
+
+tst_UIDelegates::tst_UIDelegates()
+{
     static QQmlEngine *engine = new QQmlEngine(this);
     m_component.reset(new QQmlComponent(engine, this));
     m_component->setData(QByteArrayLiteral("import QtQuick\n"
@@ -101,11 +108,14 @@ void tst_UIDelegates::javaScriptDialog_data()
 
 void tst_UIDelegates::javaScriptDialog()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
     QFETCH(QString, javaScriptCode);
     QFETCH(QString, expectedObjectName);
 
     m_window->show();
-    QTRY_VERIFY(qApp->focusObject());
+    m_window->requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(m_window.get()));
     QQuickWebEngineView *view = webEngineView();
 
     view->loadHtml("<html><body>"
@@ -118,8 +128,11 @@ void tst_UIDelegates::javaScriptDialog()
 
 void tst_UIDelegates::fileDialog()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
     m_window->show();
-    QTRY_VERIFY(qApp->focusObject());
+    m_window->requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(m_window.get()));
     QQuickWebEngineView *view = webEngineView();
 
     view->loadHtml("<html><body>"
@@ -134,8 +147,11 @@ void tst_UIDelegates::fileDialog()
 
 void tst_UIDelegates::contextMenu()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
     m_window->show();
-    QTRY_VERIFY(qApp->focusObject());
+    m_window->requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(m_window.get()));
     QQuickWebEngineView *view = webEngineView();
 
     view->loadHtml("<html><body>"
@@ -148,8 +164,11 @@ void tst_UIDelegates::contextMenu()
 
 void tst_UIDelegates::tooltip()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
     m_window->show();
-    QTRY_VERIFY(qApp->focusObject());
+    m_window->requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(m_window.get()));
     QQuickWebEngineView *view = webEngineView();
 
     view->loadHtml("<html><body>"
@@ -171,8 +190,11 @@ void tst_UIDelegates::tooltip()
 
 void tst_UIDelegates::colorDialog()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
     m_window->show();
-    QTRY_VERIFY(qApp->focusObject());
+    m_window->requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(m_window.get()));
     QQuickWebEngineView *view = webEngineView();
 
     view->loadHtml("<html><body>"
@@ -203,6 +225,8 @@ void tst_UIDelegates::authenticationDialog_data()
 
 void tst_UIDelegates::authenticationDialog()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
     QFETCH(QUrl, url);
     QFETCH(QByteArray, response);
 
@@ -212,7 +236,8 @@ void tst_UIDelegates::authenticationDialog()
     QVERIFY(server.start());
 
     m_window->show();
-    QTRY_VERIFY(qApp->focusObject());
+    m_window->requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(m_window.get()));
     QQuickWebEngineView *view = webEngineView();
     view->loadHtml("<html><body>"
                    "</body></html>");
diff --git a/tests/auto/util/qt_webengine_quicktest.h b/tests/auto/util/qt_webengine_quicktest.h
index bd98693de1f..4d68f90f0e8 100644
--- a/tests/auto/util/qt_webengine_quicktest.h
+++ b/tests/auto/util/qt_webengine_quicktest.h
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #ifndef QT_WEBENGINE_QUICKTEST_H
 #define QT_WEBENGINE_QUICKTEST_H
diff --git a/tests/auto/util/quickutil.h b/tests/auto/util/quickutil.h
index 687cb94dc85..c3542478f18 100644
--- a/tests/auto/util/quickutil.h
+++ b/tests/auto/util/quickutil.h
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #ifndef UTIL_H
 #define UTIL_H
diff --git a/tests/auto/util/testwindow.h b/tests/auto/util/testwindow.h
index f9ffd381afd..dfce32b43fd 100644
--- a/tests/auto/util/testwindow.h
+++ b/tests/auto/util/testwindow.h
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #ifndef TESTWINDOW_H
 #define TESTWINDOW_H
diff --git a/tests/auto/util/util.h b/tests/auto/util/util.h
index 5533eed80a8..eab5fa91359 100644
--- a/tests/auto/util/util.h
+++ b/tests/auto/util/util.h
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 // Functions and macros that really need to be in QTestLib
 
diff --git a/tests/auto/util/visualutil.h b/tests/auto/util/visualutil.h
new file mode 100644
index 00000000000..ca967e71dda
--- /dev/null
+++ b/tests/auto/util/visualutil.h
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef VISUALUTIL_H
+#define VISUALUTIL_H
+
+// Separate utils that require linking with GuiPrivate
+
+#include <QtTest/QtTest>
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/qpa/qplatformintegration.h>
+
+#define SKIP_IF_NO_WINDOW_ACTIVATION() \
+do { \
+    if (!(QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))) \
+        QSKIP("Window activation is not supported on this platform"); \
+} while (false)
+
+#endif /* VISUALUTIL_H */
diff --git a/tests/auto/util/widgetutil.h b/tests/auto/util/widgetutil.h
index 67d09ee4f43..b673a811f88 100644
--- a/tests/auto/util/widgetutil.h
+++ b/tests/auto/util/widgetutil.h
@@ -1,5 +1,5 @@
 // Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 // Functions and macros that really need to be in QTestLib
 
diff --git a/tests/auto/widgets/accessibility/tst_accessibility.cpp b/tests/auto/widgets/accessibility/tst_accessibility.cpp
index 1579b61e2fe..200f9a5fb3e 100644
--- a/tests/auto/widgets/accessibility/tst_accessibility.cpp
+++ b/tests/auto/widgets/accessibility/tst_accessibility.cpp
@@ -94,13 +94,13 @@ void tst_Accessibility::noPage()
 void tst_Accessibility::hierarchy()
 {
     QWebEngineView webView;
+    QSignalSpy spyFinished(&webView, &QWebEngineView::loadFinished);
     webView.setHtml("<html><body>" \
         "Hello world" \
         "<input type='text' value='some text'></input>" \
         "</body></html>");
     webView.show();
-    QSignalSpy spyFinished(&webView, &QWebEngineView::loadFinished);
-    QVERIFY(spyFinished.wait());
+    QTRY_VERIFY(spyFinished.size());
 
     QAccessibleInterface *view = QAccessible::queryAccessibleInterface(&webView);
     QVERIFY(view);
@@ -378,6 +378,7 @@ void tst_Accessibility::roles_data()
     QTest::newRow("ax::mojom::Role::kDialog") << QString("<div role='dialog'></div>") << 0 << QAccessible::Dialog;
     //QTest::newRow("ax::mojom::Role::kDirectory") << QString("<ul role='directory'></ul>") << 0 << QAccessible::List; // FIXME: Aria role 'directory' should work
     QTest::newRow("ax::mojom::Role::kDisclosureTriangle") << QString("<details><summary>a</summary></details>") << 1 << QAccessible::Button;
+    QTest::newRow("ax::mojom::Role::kDisclosureTriangleGroup") << QString("<details name='groupName'><summary>a</summary></details>") << 1 << QAccessible::Button;
     QTest::newRow("ax::mojom::Role::kGenericContainer") << QString("<div>a</div>") << 0 << QAccessible::Section;
     QTest::newRow("ax::mojom::Role::kDocCover") << QString("<div role='doc-cover'></div>") << 0 << QAccessible::Graphic;
     QTest::newRow("ax::mojom::Role::kDocBackLink") << QString("<div role='doc-backlink'></div>") << 0 << QAccessible::Link;
@@ -581,28 +582,27 @@ void tst_Accessibility::crossTreeParent()
     webView.show();
     QVERIFY(spyFinished.wait());
     QAccessibleInterface *view = QAccessible::queryAccessibleInterface(&webView);
-    QAccessibleInterface *document = view->child(0);
-    QCOMPARE(document->role(), QAccessible::WebDocument);
-    QTRY_COMPARE(document->childCount(), 1);
-    QAccessibleInterface *p = document->child(0);
+    QCOMPARE(view->child(0)->role(), QAccessible::WebDocument);
+    QTRY_COMPARE(view->child(0)->childCount(), 1);
+    QAccessibleInterface *p = view->child(0)->child(0);
     QVERIFY(p);
-    QCOMPARE(p->parent(), document);
+    QCOMPARE(p->parent(), view->child(0));
     p = p->child(0);
     QVERIFY(p);
     QCOMPARE(p->role(), QAccessible::WebDocument);
-    QCOMPARE(p->parent()->parent(), document);
+    QCOMPARE(p->parent()->parent(), view->child(0));
     QTRY_COMPARE(p->childCount(), 1);
     p = p->child(0);
     QVERIFY(p);
     QAccessibleInterface *subdocument = p;
     QCOMPARE(p->role(), QAccessible::WebDocument);
-    QCOMPARE(p->parent()->parent()->parent(), document);
+    QCOMPARE(p->parent()->parent()->parent(), view->child(0));
     p = p->child(0);
     QVERIFY(p);
     QVERIFY(p->object());
     QCOMPARE(p->role(), QAccessible::Paragraph);
     QCOMPARE(p->parent(), subdocument);
-    QCOMPARE(p->parent()->parent()->parent()->parent(), document);
+    QCOMPARE(p->parent()->parent()->parent()->parent(), view->child(0));
     QCOMPARE(p->parent()->parent()->parent()->parent()->parent(), view);
     QCOMPARE(p->object()->objectName(), QStringLiteral("my_id"));
 }
diff --git a/tests/auto/widgets/defaultsurfaceformat/tst_defaultsurfaceformat.cpp b/tests/auto/widgets/defaultsurfaceformat/tst_defaultsurfaceformat.cpp
index c53f6f5b324..3fdba71c2f0 100644
--- a/tests/auto/widgets/defaultsurfaceformat/tst_defaultsurfaceformat.cpp
+++ b/tests/auto/widgets/defaultsurfaceformat/tst_defaultsurfaceformat.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <qtest.h>
 #include <util.h>
diff --git a/tests/auto/widgets/favicon/tst_favicon.cpp b/tests/auto/widgets/favicon/tst_favicon.cpp
index c70aa1182a4..e4b5619a6fd 100644
--- a/tests/auto/widgets/favicon/tst_favicon.cpp
+++ b/tests/auto/widgets/favicon/tst_favicon.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QtTest/QtTest>
 #include <util.h>
diff --git a/tests/auto/widgets/loadsignals/tst_loadsignals.cpp b/tests/auto/widgets/loadsignals/tst_loadsignals.cpp
index 6140b3766bd..b465f985823 100644
--- a/tests/auto/widgets/loadsignals/tst_loadsignals.cpp
+++ b/tests/auto/widgets/loadsignals/tst_loadsignals.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QtTest/QtTest>
 
diff --git a/tests/auto/widgets/offscreen/tst_offscreen.cpp b/tests/auto/widgets/offscreen/tst_offscreen.cpp
index 553dc653bc9..258178dd746 100644
--- a/tests/auto/widgets/offscreen/tst_offscreen.cpp
+++ b/tests/auto/widgets/offscreen/tst_offscreen.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QTest>
 #include <QSignalSpy>
diff --git a/tests/auto/widgets/printing/tst_printing.cpp b/tests/auto/widgets/printing/tst_printing.cpp
index 605fb57b5c7..69c957fc9ac 100644
--- a/tests/auto/widgets/printing/tst_printing.cpp
+++ b/tests/auto/widgets/printing/tst_printing.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h>
 #include <QtWebEngineCore/qtwebenginecore-config.h>
@@ -159,7 +159,7 @@ void tst_Printing::printFromPdfViewer()
         rectf rect;
         return pdfPage->search(ustring::from_latin1("Hello Paper World"), rect, page::search_from_top,
                             case_sensitive);
-    }, 10000);
+    }, 15000);
     QVERIFY(ok);
 }
 #endif
diff --git a/tests/auto/widgets/proxy/tst_proxy.cpp b/tests/auto/widgets/proxy/tst_proxy.cpp
index 3dc72618c9b..c16b8cf5aac 100644
--- a/tests/auto/widgets/proxy/tst_proxy.cpp
+++ b/tests/auto/widgets/proxy/tst_proxy.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include "proxy_server.h"
 #include <QTest>
diff --git a/tests/auto/widgets/proxypac/proxyserver.cpp b/tests/auto/widgets/proxypac/proxyserver.cpp
index f7a85974784..a7bbd6934f0 100644
--- a/tests/auto/widgets/proxypac/proxyserver.cpp
+++ b/tests/auto/widgets/proxypac/proxyserver.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include "proxyserver.h"
 #include <QDataStream>
diff --git a/tests/auto/widgets/proxypac/proxyserver.h b/tests/auto/widgets/proxypac/proxyserver.h
index c95856da954..4d622175ec1 100644
--- a/tests/auto/widgets/proxypac/proxyserver.h
+++ b/tests/auto/widgets/proxypac/proxyserver.h
@@ -1,5 +1,5 @@
 // Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #ifndef PROXY_SERVER_H
 #define PROXY_SERVER_H
diff --git a/tests/auto/widgets/proxypac/tst_proxypac.cpp b/tests/auto/widgets/proxypac/tst_proxypac.cpp
index 43ccbf028a3..19654d1a963 100644
--- a/tests/auto/widgets/proxypac/tst_proxypac.cpp
+++ b/tests/auto/widgets/proxypac/tst_proxypac.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include "proxy_server.h"
 #include <QTest>
diff --git a/tests/auto/widgets/qtbug_110287/tst_qtbug_110287.cpp b/tests/auto/widgets/qtbug_110287/tst_qtbug_110287.cpp
index 9453ae9b845..904d0849361 100644
--- a/tests/auto/widgets/qtbug_110287/tst_qtbug_110287.cpp
+++ b/tests/auto/widgets/qtbug_110287/tst_qtbug_110287.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QNetworkAccessManager>
 #include <QNetworkRequest>
diff --git a/tests/auto/widgets/qwebenginedownloadrequest/CMakeLists.txt b/tests/auto/widgets/qwebenginedownloadrequest/CMakeLists.txt
index 5b76909b159..13b86a95666 100644
--- a/tests/auto/widgets/qwebenginedownloadrequest/CMakeLists.txt
+++ b/tests/auto/widgets/qwebenginedownloadrequest/CMakeLists.txt
@@ -9,6 +9,7 @@ qt_internal_add_test(tst_qwebenginedownloadrequest
         tst_qwebenginedownloadrequest.cpp
     LIBRARIES
         Qt::WebEngineWidgets
+        Qt::GuiPrivate
         Test::HttpServer
         Test::Util
 )
diff --git a/tests/auto/widgets/qwebenginedownloadrequest/tst_qwebenginedownloadrequest.cpp b/tests/auto/widgets/qwebenginedownloadrequest/tst_qwebenginedownloadrequest.cpp
index c81a27b3aaf..15e96bc0dcd 100644
--- a/tests/auto/widgets/qwebenginedownloadrequest/tst_qwebenginedownloadrequest.cpp
+++ b/tests/auto/widgets/qwebenginedownloadrequest/tst_qwebenginedownloadrequest.cpp
@@ -1,7 +1,8 @@
 // Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <util.h>
+#include <visualutil.h>
 
 #include <QCoreApplication>
 #include <QSignalSpy>
@@ -16,6 +17,38 @@
 #include <QWebEngineView>
 #include <httpserver.h>
 
+using namespace Qt::StringLiterals;
+
+// Based on PageWithPaintListeners in tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
+// TODO: Factor PageWithPaintListeners out to tests/auto/util/util.h
+class TestPage : public QWebEnginePage
+{
+    Q_OBJECT
+public:
+    TestPage(QWebEngineProfile *profile) : QWebEnginePage(profile)
+    {
+        QObject::connect(this, &QWebEnginePage::loadFinished, [this]() {
+            const QString jsLCPObserver = QStringLiteral(
+                    "new PerformanceObserver((list) => {"
+                    "    const entries = list.getEntries();"
+                    "    const lastEntry = entries[entries.length - 1];"
+                    "    console.log('largestContentfulPaint: ' + lastEntry.element);"
+                    "}).observe({type: 'largest-contentful-paint', buffered: true});");
+            runJavaScript(jsLCPObserver);
+        });
+    }
+
+    void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel, const QString &message, int,
+                                  const QString &) override
+    {
+        if (message == "largestContentfulPaint: [object HTMLBodyElement]"_L1)
+            emit htmlBodyElementPainted();
+    }
+
+signals:
+    void htmlBodyElementPainted();
+};
+
 class tst_QWebEngineDownloadRequest : public QObject
 {
     Q_OBJECT
@@ -44,10 +77,12 @@ private Q_SLOTS:
     void downloadPage_data();
     void downloadPage();
     void downloadViaSetUrl();
-    void downloadFileNot1();
-    void downloadFileNot2();
+    void downloadAnswerLater_data();
+    void downloadAnswerLater();
+    void downloadFileCancel();
     void downloadDeleted();
     void downloadDeletedByProfile();
+    void downloadRequestedWithoutHandler();
     void downloadUniqueFilename_data();
     void downloadUniqueFilename();
     void downloadUniqueFilenameWithTimestamp();
@@ -58,6 +93,7 @@ private Q_SLOTS:
     void downloadToDirectoryWithFileName();
     void downloadDataUrls_data();
     void downloadDataUrls();
+    void pauseDownload();
 
 private:
     void saveLink(QPoint linkPos);
@@ -66,7 +102,7 @@ private Q_SLOTS:
 
     HttpServer *m_server;
     QWebEngineProfile *m_profile;
-    QWebEnginePage *m_page;
+    TestPage *m_page;
     QWebEngineView *m_view;
     QSet<QWebEngineDownloadRequest *> m_requestedDownloads;
     QSet<QWebEngineDownloadRequest *> m_finishedDownloads;
@@ -91,11 +127,12 @@ void tst_QWebEngineDownloadRequest::initTestCase()
             m_finishedDownloads.insert(item);
         });
     });
-    m_page = new QWebEnginePage(m_profile);
+    m_page = new TestPage(m_profile);
     m_view = new QWebEngineView;
     m_view->setPage(m_page);
     m_view->resize(640, 480);
     m_view->show();
+    m_view->window()->windowHandle()->requestActivate();
 }
 
 void tst_QWebEngineDownloadRequest::init()
@@ -357,6 +394,8 @@ void tst_QWebEngineDownloadRequest::downloadLink_data()
 
 void tst_QWebEngineDownloadRequest::downloadLink()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
     QFETCH(UserAction, userAction);
     QFETCH(bool, anchorHasDownloadAttribute);
     QFETCH(QByteArray, fileName);
@@ -452,10 +491,12 @@ void tst_QWebEngineDownloadRequest::downloadLink()
     // The only variation being whether the <a> element has a "download"
     // attribute or not.
     QSignalSpy loadSpy(m_page, &QWebEnginePage::loadFinished);
+    QSignalSpy paintSpy(m_page, &TestPage::htmlBodyElementPainted);
     m_view->load(m_server->url());
     QTRY_COMPARE(loadSpy.size(), 1);
     QCOMPARE(loadSpy.takeFirst().value(0).toBool(), true);
     QCOMPARE(indexRequestCount, 1);
+    QTRY_COMPARE(paintSpy.size(), 1);
 
     simulateUserAction(QPoint(10, 10), userAction);
 
@@ -492,6 +533,8 @@ void tst_QWebEngineDownloadRequest::downloadTwoLinks_data()
 
 void tst_QWebEngineDownloadRequest::downloadTwoLinks()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
     QFETCH(UserAction, action1);
     QFETCH(UserAction, action2);
 
@@ -550,9 +593,11 @@ void tst_QWebEngineDownloadRequest::downloadTwoLinks()
     });
 
     QSignalSpy loadSpy(m_page, &QWebEnginePage::loadFinished);
+    QSignalSpy paintSpy(m_page, &TestPage::htmlBodyElementPainted);
     m_view->load(m_server->url());
     QTRY_COMPARE(loadSpy.size(), 1);
     QCOMPARE(loadSpy.takeFirst().value(0).toBool(), true);
+    QTRY_COMPARE(paintSpy.size(), 1);
 
     // Trigger downloads
     simulateUserAction(QPoint(10, 10), action1);
@@ -681,6 +726,7 @@ void tst_QWebEngineDownloadRequest::downloadViaSetUrl()
     QList<QUrl> downloadUrls;
     ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadRequest *item) {
         downloadUrls.append(item->url());
+        item->cancel();
     });
 
     // Set up the test scenario by trying to load some unrelated HTML.
@@ -710,29 +756,88 @@ void tst_QWebEngineDownloadRequest::downloadViaSetUrl()
     }
 }
 
-void tst_QWebEngineDownloadRequest::downloadFileNot1()
+void tst_QWebEngineDownloadRequest::downloadRequestedWithoutHandler()
+{
+    ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) {
+        rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/html"));
+        rr->setResponseBody(QByteArrayLiteral("<html><body>Hello</body></html>"));
+        rr->sendResponse();
+    });
+
+    QTemporaryDir tmpDir;
+    QVERIFY(tmpDir.isValid());
+    QString downloadPath = tmpDir.filePath("test.html");
+
+    QWebEngineProfile profile;
+    QWebEnginePage page(&profile);
+
+    // Load some HTML
+    QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished);
+    page.load(m_server->url());
+    QTRY_COMPARE(loadSpy.size(), 1);
+    QCOMPARE(loadSpy.takeFirst().value(0).toBool(), true);
+
+    // Save and verify
+    page.save(downloadPath, QWebEngineDownloadRequest::SingleHtmlSaveFormat);
+    QFile file(downloadPath);
+    QTRY_VERIFY(file.exists());
+}
+
+void tst_QWebEngineDownloadRequest::downloadAnswerLater_data()
+{
+    QTest::addColumn<bool>("answer");
+    QTest::addColumn<bool>("accept");
+
+    QTest::newRow("accept") << true << true;
+    QTest::newRow("cancel") << true << false;
+    QTest::newRow("ignore") << false << true;
+}
+
+void tst_QWebEngineDownloadRequest::downloadAnswerLater()
 {
-    // Trigger file download via download() but don't accept().
+    QFETCH(bool, answer);
+    QFETCH(bool, accept);
 
     ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) {
-        rr->sendResponse(404);
+        rr->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment"));
+        rr->setResponseBody(QByteArrayLiteral("a"));
+        rr->sendResponse();
     });
 
-    QPointer<QWebEngineDownloadRequest> downloadItem;
+    QTemporaryDir tmpDir;
+    QVERIFY(tmpDir.isValid());
+    m_profile->setDownloadPath(tmpDir.path());
+
+    QPointer<QWebEngineDownloadRequest> downloadRequest;
     int downloadCount = 0;
-    ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadRequest *item) {
-        QVERIFY(item);
-        QCOMPARE(item->state(), QWebEngineDownloadRequest::DownloadRequested);
-        downloadItem = item;
+    ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadRequest *request) {
+        QVERIFY(request);
+        QCOMPARE(request->state(), QWebEngineDownloadRequest::DownloadRequested);
+        downloadRequest = request;
         downloadCount++;
     });
 
     m_page->download(m_server->url(QByteArrayLiteral("/file")));
     QTRY_COMPARE(downloadCount, 1);
-    QVERIFY(!downloadItem);
+    QVERIFY(downloadRequest);
+
+    if (answer) {
+        if (accept) {
+            downloadRequest->accept();
+            QCOMPARE(downloadRequest->state(), QWebEngineDownloadRequest::DownloadInProgress);
+            QTRY_COMPARE(m_finishedDownloads.size(), 1);
+            QCOMPARE(downloadRequest->state(), QWebEngineDownloadRequest::DownloadCompleted);
+        } else {
+            downloadRequest->cancel();
+            QCOMPARE(downloadRequest->state(), QWebEngineDownloadRequest::DownloadCancelled);
+        }
+    }
+
+    QVERIFY(downloadRequest);
+    delete downloadRequest;
 }
 
-void tst_QWebEngineDownloadRequest::downloadFileNot2()
+void tst_QWebEngineDownloadRequest::downloadFileCancel()
 {
     // Trigger file download via download() but call cancel() instead of accept().
 
@@ -921,13 +1026,13 @@ void tst_QWebEngineDownloadRequest::downloadUniqueFilenameWithTimestamp()
     // Create the first empty file without uniquifier.
     {
         QFile file(m_profile->downloadPath() + "/" + fileName);
-        file.open(QIODevice::ReadWrite);
+        QVERIFY2(file.open(QIODevice::ReadWrite), qPrintable(file.errorString()));
     }
 
     // Create 99 empty files with uniquifier.
     for (int i = 1; i < 100; i++) {
         QFile file(m_profile->downloadPath() + "/" + baseName + " (" + QString::number(i) + ")." + extension);
-        file.open(QIODevice::ReadWrite);
+        QVERIFY2(file.open(QIODevice::ReadWrite), qPrintable(file.errorString()));
     }
 
     // Create 100th (kMaxUniqueFiles) empty file with uniquifier.
@@ -1249,6 +1354,8 @@ void tst_QWebEngineDownloadRequest::downloadDataUrls_data()
 
 void tst_QWebEngineDownloadRequest::downloadDataUrls()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
     QFETCH(QByteArray, htmlData);
     QFETCH(QString, expectedFileName);
     // Set up HTTP server
@@ -1270,17 +1377,69 @@ void tst_QWebEngineDownloadRequest::downloadDataUrls()
         QCOMPARE(item->state(), QWebEngineDownloadRequest::DownloadRequested);
         QCOMPARE(item->downloadFileName(), expectedFileName);
         downloadRequestCount++;
+        item->cancel();
     });
 
     QSignalSpy loadSpy(m_page, &QWebEnginePage::loadFinished);
+    QSignalSpy paintSpy(m_page, &TestPage::htmlBodyElementPainted);
     m_view->load(m_server->url());
     QTRY_COMPARE(loadSpy.size(), 1);
     QCOMPARE(loadSpy.takeFirst().value(0).toBool(), true);
+    QTRY_COMPARE(paintSpy.size(), 1);
 
     // Trigger download
     simulateUserAction(QPoint(10, 10), UserAction::ClickLink);
     QTRY_COMPARE(downloadRequestCount, 1);
 }
 
+void tst_QWebEngineDownloadRequest::pauseDownload()
+{
+    const int fileSize = 1024 * 1024 * 512;
+
+    // Set up HTTP server
+    ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) {
+        if (rr->requestMethod() == "GET" && rr->requestPath() == "/") {
+            rr->setResponseHeader(QByteArrayLiteral("content-type"),
+                                  QByteArrayLiteral("application/octet-stream"));
+            static const QByteArray bigfile(fileSize, '0');
+            rr->setResponseBody(bigfile);
+            rr->sendResponse();
+        }
+    });
+
+    // Set up profile and download handler
+    QTemporaryDir tmpDir;
+    QVERIFY(tmpDir.isValid());
+    m_profile->setDownloadPath(tmpDir.path());
+
+    bool firstBytesReceived = true;
+    int pausedCount = 0;
+    ScopedConnection sc2 = connect(
+            m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadRequest *item) {
+                QCOMPARE(item->state(), QWebEngineDownloadRequest::DownloadRequested);
+                connect(item, &QWebEngineDownloadRequest::receivedBytesChanged, [item, &firstBytesReceived] {
+                    if (firstBytesReceived) {
+                        firstBytesReceived = false;
+                        item->pause();
+                    }
+                });
+                connect(item, &QWebEngineDownloadRequest::isPausedChanged, [item, &pausedCount]() {
+                    if (item->isPaused()) {
+                        pausedCount++;
+                        item->resume();
+                    }
+                });
+                item->accept();
+            });
+
+    QSignalSpy loadSpy(m_page, &QWebEnginePage::loadFinished);
+    m_view->load(m_server->url());
+    QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 10000);
+    QTRY_COMPARE_WITH_TIMEOUT(pausedCount, 1, 10000);
+    QTRY_COMPARE_WITH_TIMEOUT(m_finishedDownloads.size(), 1, 10000);
+    QTRY_COMPARE(m_finishedDownloads.values()[0]->isPaused(), false);
+    QTRY_COMPARE(m_finishedDownloads.values()[0]->receivedBytes(), fileSize);
+}
+
 QTEST_MAIN(tst_QWebEngineDownloadRequest)
 #include "tst_qwebenginedownloadrequest.moc"
diff --git a/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp b/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp
index ad66e972c61..b4084a22d56 100644
--- a/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp
+++ b/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp
@@ -145,7 +145,7 @@ void tst_QWebEngineHistory::count()
   */
 void tst_QWebEngineHistory::back()
 {
-    QSignalSpy titleChangedSpy(page, SIGNAL(titleChanged(const QString&)));
+    QSignalSpy titleChangedSpy(page, SIGNAL(titleChanged(QString)));
 
     for (int i = histsize;i > 1;i--) {
         QTRY_COMPARE(toPlainTextSync(page), QString("page") + QString::number(i));
@@ -171,7 +171,7 @@ void tst_QWebEngineHistory::forward()
         QTRY_COMPARE(loadFinishedSpy->size(), histBackCount);
     }
 
-    QSignalSpy titleChangedSpy(page, SIGNAL(titleChanged(const QString&)));
+    QSignalSpy titleChangedSpy(page, SIGNAL(titleChanged(QString)));
     for (int i = 1;i < histsize;i++) {
         QTRY_COMPARE(toPlainTextSync(page), QString("page") + QString::number(i));
         hist->forward();
diff --git a/tests/auto/widgets/qwebenginepage/BLACKLIST b/tests/auto/widgets/qwebenginepage/BLACKLIST
index 52def48d170..01d1ffe3d9b 100644
--- a/tests/auto/widgets/qwebenginepage/BLACKLIST
+++ b/tests/auto/widgets/qwebenginepage/BLACKLIST
@@ -13,3 +13,7 @@ macos
 
 [backgroundColor]
 macos
+
+[dynamicFrame]
+ubuntu-22.04
+
diff --git a/tests/auto/widgets/qwebenginepage/CMakeLists.txt b/tests/auto/widgets/qwebenginepage/CMakeLists.txt
index f63d6211c08..a0470e25a37 100644
--- a/tests/auto/widgets/qwebenginepage/CMakeLists.txt
+++ b/tests/auto/widgets/qwebenginepage/CMakeLists.txt
@@ -9,6 +9,7 @@ qt_internal_add_test(tst_qwebenginepage
         tst_qwebenginepage.cpp
     LIBRARIES
         Qt::CorePrivate
+        Qt::GuiPrivate
         Qt::NetworkPrivate
         Qt::WebEngineCorePrivate
         Qt::WebEngineWidgets
@@ -29,6 +30,8 @@ set(tst_qwebenginepage_resource_files
     "resources/frame_c.html"
     "resources/framedindex.html"
     "resources/fullscreen.html"
+    "resources/hello.js"
+    "resources/hi.js"
     "resources/iframe.html"
     "resources/iframe2.html"
     "resources/iframe3.html"
@@ -39,6 +42,7 @@ set(tst_qwebenginepage_resource_files
     "resources/path with spaces.txt"
     "resources/reload.html"
     "resources/script.html"
+    "resources/script2.html"
     "resources/style.css"
     "resources/test1.html"
     "resources/test2.html"
diff --git a/tests/auto/widgets/qwebenginepage/resources/hello.js b/tests/auto/widgets/qwebenginepage/resources/hello.js
new file mode 100644
index 00000000000..6f5e79ebf4d
--- /dev/null
+++ b/tests/auto/widgets/qwebenginepage/resources/hello.js
@@ -0,0 +1,7 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+function sayHello()
+{
+    console.warn("hello");
+}
diff --git a/tests/auto/widgets/qwebenginepage/resources/hi.js b/tests/auto/widgets/qwebenginepage/resources/hi.js
new file mode 100644
index 00000000000..30e7cc972d1
--- /dev/null
+++ b/tests/auto/widgets/qwebenginepage/resources/hi.js
@@ -0,0 +1,8 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+function sayHi()
+{
+    // Placeholder
+    console.warn("hi");
+}
diff --git a/tests/auto/widgets/qwebenginepage/resources/script2.html b/tests/auto/widgets/qwebenginepage/resources/script2.html
new file mode 100644
index 00000000000..7d511503771
--- /dev/null
+++ b/tests/auto/widgets/qwebenginepage/resources/script2.html
@@ -0,0 +1,4 @@
+<html><head>
+<script language="javascript" type="text/javascript" src="hello.js"></script>
+<script language="javascript" type="text/javascript" src="hi.js"></script>
+</head></html>
diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
index f6eac2880b5..08b17db83fd 100644
--- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
+++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
@@ -20,6 +20,7 @@
 */
 
 #include <widgetutil.h>
+#include <visualutil.h>
 #include <QtNetwork/private/qtnetworkglobal_p.h>
 #include <QtWebEngineCore/qtwebenginecore-config.h>
 #include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h>
@@ -41,6 +42,8 @@
 #    include <QStateMachine>
 #endif
 #include <QtGui/QClipboard>
+#include <QtGui/qpa/qplatformintegration.h>
+#include <QtGui/private/qguiapplication_p.h>
 #include <QtTest/QtTest>
 #include <QTextCharFormat>
 #if QT_CONFIG(webengine_webchannel)
@@ -72,11 +75,14 @@
 #include <qwebengineurlscheme.h>
 #include <qwebengineurlschemehandler.h>
 #include <qwebengineview.h>
+#include <qwebenginepermission.h>
 #include <qimagewriter.h>
 #include <QColorSpace>
 #include <QQuickRenderControl>
 #include <QQuickWindow>
 
+using namespace Qt::StringLiterals;
+
 static void removeRecursive(const QString& dirname)
 {
     QDir dir(dirname);
@@ -134,6 +140,7 @@ private Q_SLOTS:
     void backActionUpdate();
     void localStorageVisibility();
     void consoleOutput();
+    void javaScriptConsoleMessage();
     void userAgentNewlineStripping();
     void renderWidgetHostViewNotShowTopLevel();
     void getUserMediaRequest_data();
@@ -267,6 +274,7 @@ private Q_SLOTS:
     void renderProcessCrashed();
     void renderProcessPid();
     void backgroundColor();
+    void popupOnTransparentBackground();
     void audioMuted();
     void closeContents();
     void isSafeRedirect_data();
@@ -451,12 +459,12 @@ Q_OBJECT
         return true;
     }
 public Q_SLOTS:
-    void requestPermission(const QUrl &origin, QWebEnginePage::Feature feature)
+    void requestPermission(QWebEnginePermission permission)
     {
         if (m_allowGeolocation)
-            setFeaturePermission(origin, feature, PermissionGrantedByUser);
+            permission.grant();
         else
-            setFeaturePermission(origin, feature, PermissionDeniedByUser);
+            permission.deny();
     }
 
 public:
@@ -485,11 +493,11 @@ void tst_QWebEnginePage::geolocationRequestJS()
     QWebEngineView view;
     JSTestPage *newPage = new JSTestPage(&view);
     view.setPage(newPage);
-    newPage->profile()->setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions);
+    newPage->profile()->setPersistentPermissionsPolicy(QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime);
     newPage->setGeolocationPermission(allowed);
 
-    connect(newPage, SIGNAL(featurePermissionRequested(const QUrl&, QWebEnginePage::Feature)),
-            newPage, SLOT(requestPermission(const QUrl&, QWebEnginePage::Feature)));
+    connect(newPage, SIGNAL(permissionRequested(QWebEnginePermission)),
+            newPage, SLOT(requestPermission(QWebEnginePermission)));
 
     QSignalSpy spyLoadFinished(newPage, SIGNAL(loadFinished(bool)));
     newPage->setHtml(QString("<html><body>test</body></html>"), QUrl("qrc://secure/origin"));
@@ -609,7 +617,7 @@ class ConsolePage : public QWebEnginePage
         sourceIDs.append(sourceID);
     }
 
-    QList<int> levels;
+    QList<JavaScriptConsoleMessageLevel> levels;
     QStringList messages;
     QList<int> lineNumbers;
     QStringList sourceIDs;
@@ -624,6 +632,78 @@ void tst_QWebEnginePage::consoleOutput()
     QCOMPARE(page.lineNumbers.at(0), 1);
 }
 
+class TestJSMessageHandler
+{
+public:
+    inline static QList<QtMsgType> levels;
+    inline static QStringList messages;
+    inline static QList<int> lineNumbers;
+    inline static QStringList sourceIDs;
+
+    static void handler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
+    {
+        if (strcmp(context.category, "js") != 0) {
+            m_originalHandler(type, context, msg);
+            return;
+        }
+
+        levels.append(type);
+        messages.append(msg);
+        lineNumbers.append(context.line);
+        sourceIDs.append(context.file);
+    }
+
+    TestJSMessageHandler() { m_originalHandler = qInstallMessageHandler(handler); }
+    ~TestJSMessageHandler() { qInstallMessageHandler(m_originalHandler); }
+
+private:
+    inline static QtMessageHandler m_originalHandler = nullptr;
+};
+
+void tst_QWebEnginePage::javaScriptConsoleMessage()
+{
+    // Test overridden QWebEnginePage::javaScriptConsoleMessage().
+    {
+        ConsolePage page;
+        QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
+        page.load(QUrl("qrc:///resources/script2.html"));
+        QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000);
+
+        evaluateJavaScriptSync(&page, "sayHello()");
+        QCOMPARE(page.levels.last(), QWebEnginePage::WarningMessageLevel);
+        QCOMPARE(page.messages.last(), "hello"_L1);
+        QCOMPARE(page.lineNumbers.last(), 6);
+        QCOMPARE(page.sourceIDs.last(), "qrc:///resources/hello.js"_L1);
+
+        evaluateJavaScriptSync(&page, "sayHi()");
+        QCOMPARE(page.levels.last(), QWebEnginePage::WarningMessageLevel);
+        QCOMPARE(page.messages.last(), "hi"_L1);
+        QCOMPARE(page.lineNumbers.last(), 7);
+        QCOMPARE(page.sourceIDs.last(), "qrc:///resources/hi.js"_L1);
+    }
+
+    // Test default QWebEnginePage::javaScriptConsoleMessage() handler.
+    {
+        TestJSMessageHandler handler;
+        QWebEnginePage page;
+        QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
+        page.load(QUrl("qrc:///resources/script2.html"));
+        QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000);
+
+        evaluateJavaScriptSync(&page, "sayHello()");
+        QCOMPARE(handler.levels.last(), QtMsgType::QtWarningMsg);
+        QCOMPARE(handler.messages.last(), "hello"_L1);
+        QCOMPARE(handler.lineNumbers.last(), 6);
+        QCOMPARE(handler.sourceIDs.last(), "qrc:///resources/hello.js"_L1);
+
+        evaluateJavaScriptSync(&page, "sayHi()");
+        QCOMPARE(handler.levels.last(), QtMsgType::QtWarningMsg);
+        QCOMPARE(handler.messages.last(), "hi"_L1);
+        QCOMPARE(handler.lineNumbers.last(), 7);
+        QCOMPARE(handler.sourceIDs.last(), "qrc:///resources/hi.js"_L1);
+    }
+}
+
 class TestPage : public QWebEnginePage {
     Q_OBJECT
 public:
@@ -1198,6 +1278,23 @@ void tst_QWebEnginePage::findText()
         QTRY_VERIFY(m_view->selectedText().isEmpty());
     }
 
+    // Toggling case sensitivity without changing the text that's being looked up
+    // will clear previously found text
+    {
+        auto *callbackSpy = new CallbackSpy<QWebEngineFindTextResult>();
+        QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished);
+        m_view->findText("FOO", {}, callbackSpy->ref());
+        QVERIFY(callbackSpy->waitForResult().numberOfMatches() > 0);
+        QTRY_COMPARE(signalSpy.size(), 1);
+        delete callbackSpy;
+        callbackSpy = new CallbackSpy<QWebEngineFindTextResult>();
+        m_view->findText("FOO", QWebEnginePage::FindCaseSensitively, callbackSpy->ref());
+        QVERIFY(callbackSpy->waitForResult().numberOfMatches() == 0);
+        QVERIFY(callbackSpy->wasCalled());
+        QTRY_COMPARE(signalSpy.size(), 2);
+        delete callbackSpy;
+    }
+
     // Select whole page contents again.
     m_view->page()->triggerAction(QWebEnginePage::SelectAll);
     QTRY_COMPARE(m_view->hasSelection(), true);
@@ -1654,28 +1751,31 @@ Q_OBJECT
 
 public:
     GetUserMediaTestPage()
-        : m_gotRequest(false)
+        : m_gotDesktopMediaRequest(false)
+        , m_gotEmptyDesktopMediaRequest(false)
         , m_loadSucceeded(false)
+        , m_permission(nullptr)
     {
-        connect(this, &QWebEnginePage::featurePermissionRequested, this, &GetUserMediaTestPage::onFeaturePermissionRequested);
+        connect(this, &QWebEnginePage::permissionRequested, this, &GetUserMediaTestPage::onPermissionRequested);
+        connect(this, &QWebEnginePage::desktopMediaRequested, this,
+                &GetUserMediaTestPage::onDesktopMediaRequested);
         connect(this, &QWebEnginePage::loadFinished, [this](bool success){
             m_loadSucceeded = success;
         });
-        profile()->setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions);
+        profile()->setPersistentPermissionsPolicy(QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime);
         // We need to load content from a resource in order for the securityOrigin to be valid.
         load(QUrl("qrc:///resources/content.html"));
     }
 
     void jsGetMedia(const QString &call)
     {
+        m_jsPromiseFulfilled = false;
+        m_jsPromiseRejected = false;
         evaluateJavaScriptSync(this,
-            QStringLiteral(
-                "var promiseFulfilled = false;"
-                "var promiseRejected = false;"
-                "navigator.mediaDevices.%1"
-                ".then(stream => { promiseFulfilled = true})"
-                ".catch(err => { promiseRejected = true})")
-            .arg(call));
+                               QStringLiteral("navigator.mediaDevices.%1"
+                                              ".then(stream => { console.info('fulfilled') })"
+                                              ".catch(err => { console.info('rejected') })")
+                                       .arg(call));
     }
 
     void jsGetUserMedia(const QString &constraints)
@@ -1683,87 +1783,113 @@ Q_OBJECT
         jsGetMedia(QStringLiteral("getUserMedia(%1)").arg(constraints));
     }
 
-    bool jsPromiseFulfilled()
+    bool jsPromiseSettled() { return m_jsPromiseFulfilled || m_jsPromiseRejected; }
+
+    bool jsPromiseFulfilled() { return m_jsPromiseFulfilled; }
+
+    bool jsPromiseRejected() { return m_jsPromiseRejected; }
+
+    void rejectPendingRequest()
     {
-        return evaluateJavaScriptSync(this, QStringLiteral("promiseFulfilled")).toBool();
+        if (m_permission)
+            m_permission->deny();
+        resetRequestState();
     }
 
-    bool jsPromiseRejected()
+    void acceptPendingRequest()
     {
-        return evaluateJavaScriptSync(this, QStringLiteral("promiseRejected")).toBool();
+        if (m_permission)
+            m_permission->grant();
+        resetRequestState();
     }
 
-    void rejectPendingRequest()
+    bool gotExpectedRequests(bool isDesktopPermission,
+                             QWebEnginePermission::PermissionType permissionType) const
     {
-        setFeaturePermission(m_requestSecurityOrigin, m_requestedFeature, QWebEnginePage::PermissionDeniedByUser);
-        m_gotRequest = false;
+#if !QT_CONFIG(webengine_webrtc)
+        // When webrtc is disabled, these come through as media requests and not desktop requests.
+        isDesktopPermission = false;
+#endif
+        if (isDesktopPermission != m_gotDesktopMediaRequest)
+            return false;
+        if (isDesktopPermission && m_gotEmptyDesktopMediaRequest)
+            return !m_permission;
+        return m_permission && m_permission->permissionType() == permissionType;
     }
-    void acceptPendingRequest()
+
+    bool loadSucceeded() const
     {
-        setFeaturePermission(m_requestSecurityOrigin, m_requestedFeature, QWebEnginePage::PermissionGrantedByUser);
-        m_gotRequest = false;
+        return m_loadSucceeded;
     }
 
-    bool gotFeatureRequest(QWebEnginePage::Feature feature)
+private Q_SLOTS:
+    void onPermissionRequested(QWebEnginePermission permission)
     {
-        return m_gotRequest && m_requestedFeature == feature;
+        m_permission.reset(new QWebEnginePermission(permission));
     }
 
-    bool gotFeatureRequest() const
+    void onDesktopMediaRequested(QWebEngineDesktopMediaRequest request)
     {
-        return m_gotRequest;
+        m_gotDesktopMediaRequest = true;
+        m_gotEmptyDesktopMediaRequest = request.screensModel()->rowCount() == 0;
+        // On destruction, the request will automatically select screen 0, or cancel
+        // if no screens are available.
     }
 
-    bool loadSucceeded() const
+private:
+    void resetRequestState()
     {
-        return m_loadSucceeded;
+        m_gotDesktopMediaRequest = false;
+        m_gotEmptyDesktopMediaRequest = false;
+        m_permission.reset();
     }
 
-private Q_SLOTS:
-    void onFeaturePermissionRequested(const QUrl &securityOrigin, QWebEnginePage::Feature feature)
+    void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel, const QString &message, int,
+                                  const QString &) override
     {
-        m_requestedFeature = feature;
-        m_requestSecurityOrigin = securityOrigin;
-        m_gotRequest = true;
+        m_jsPromiseFulfilled = (message == "fulfilled");
+        m_jsPromiseRejected = (message == "rejected");
     }
 
-private:
-    bool m_gotRequest;
+    bool m_jsPromiseFulfilled;
+    bool m_jsPromiseRejected;
+    bool m_gotDesktopMediaRequest;
+    bool m_gotEmptyDesktopMediaRequest;
     bool m_loadSucceeded;
-    QWebEnginePage::Feature m_requestedFeature;
-    QUrl m_requestSecurityOrigin;
-
+    std::unique_ptr<QWebEnginePermission> m_permission;
 };
 
 void tst_QWebEnginePage::getUserMediaRequest_data()
 {
     QTest::addColumn<QString>("call");
-    QTest::addColumn<QWebEnginePage::Feature>("feature");
+    QTest::addColumn<QWebEnginePermission::PermissionType>("permissionType");
+    using PT = QWebEnginePermission::PermissionType;
 
-    QTest::addRow("device audio")
-        << "getUserMedia({audio: true})" << QWebEnginePage::MediaAudioCapture;
-    QTest::addRow("device video")
-        << "getUserMedia({video: true})" << QWebEnginePage::MediaVideoCapture;
+    QTest::addRow("device audio") << "getUserMedia({audio: true})" << PT::MediaAudioCapture;
+    QTest::addRow("device video") << "getUserMedia({video: true})" << PT::MediaVideoCapture;
     QTest::addRow("device audio+video")
-        << "getUserMedia({audio: true, video: true})" << QWebEnginePage::MediaAudioVideoCapture;
+            << "getUserMedia({audio: true, video: true})" << PT::MediaAudioVideoCapture;
     QTest::addRow("desktop video")
-        << "getUserMedia({video: { mandatory: { chromeMediaSource: 'desktop' }}})"
-        << QWebEnginePage::DesktopVideoCapture;
+            << "getUserMedia({video: { mandatory: { chromeMediaSource: 'desktop' }}})"
+            << PT::DesktopVideoCapture;
     QTest::addRow("desktop audio+video")
-        << "getUserMedia({audio: { mandatory: { chromeMediaSource: 'desktop' }}, video: { mandatory: { chromeMediaSource: 'desktop' }}})"
-        << QWebEnginePage::DesktopAudioVideoCapture;
-    QTest::addRow("display video")
-        << "getDisplayMedia()" << QWebEnginePage::DesktopVideoCapture;
+            << "getUserMedia({audio: { mandatory: { chromeMediaSource: 'desktop' }}, video: { "
+               "mandatory: { chromeMediaSource: 'desktop' }}})"
+            << PT::DesktopAudioVideoCapture;
+    QTest::addRow("display video") << "getDisplayMedia()" << PT::DesktopVideoCapture;
 }
 
 void tst_QWebEnginePage::getUserMediaRequest()
 {
     QFETCH(QString, call);
-    QFETCH(QWebEnginePage::Feature, feature);
+    QFETCH(QWebEnginePermission::PermissionType, permissionType);
 
+    bool isDesktopPermission =
+            permissionType == QWebEnginePermission::PermissionType::DesktopVideoCapture
+            || permissionType == QWebEnginePermission::PermissionType::DesktopAudioVideoCapture;
     GetUserMediaTestPage page;
     QWebEngineView view;
-    if (feature == QWebEnginePage::DesktopVideoCapture || feature == QWebEnginePage::DesktopAudioVideoCapture) {
+    if (isDesktopPermission) {
         // Desktop capture needs to be on a desktop.
         view.setPage(&page);
         view.resize(640, 480);
@@ -1776,9 +1902,9 @@ void tst_QWebEnginePage::getUserMediaRequest()
 
     // 1. Rejecting request on C++ side should reject promise on JS side.
     page.jsGetMedia(call);
-    QTRY_VERIFY(page.gotFeatureRequest(feature));
+    QTRY_VERIFY(page.gotExpectedRequests(isDesktopPermission, permissionType));
     page.rejectPendingRequest();
-    QTRY_VERIFY(!page.jsPromiseFulfilled() && page.jsPromiseRejected());
+    QTRY_VERIFY(page.jsPromiseRejected());
 
     // 2. Accepting request on C++ side should either fulfill or reject the
     // Promise on JS side. Due to the potential lack of physical media devices
@@ -1786,15 +1912,15 @@ void tst_QWebEnginePage::getUserMediaRequest()
     // always be fulfilled, however in this case an error should be returned to
     // JS instead of leaving the Promise in limbo.
     page.jsGetMedia(call);
-    QTRY_VERIFY(page.gotFeatureRequest(feature));
+    QTRY_VERIFY(page.gotExpectedRequests(isDesktopPermission, permissionType));
     page.acceptPendingRequest();
-    QTRY_VERIFY(page.jsPromiseFulfilled() || page.jsPromiseRejected());
+    QTRY_VERIFY(page.jsPromiseSettled());
 
-    // 3. Media feature permissions are not remembered.
+    // 3. Media permissions are not remembered.
     page.jsGetMedia(call);
-    QTRY_VERIFY(page.gotFeatureRequest(feature));
+    QTRY_VERIFY(page.gotExpectedRequests(isDesktopPermission, permissionType));
     page.acceptPendingRequest();
-    QTRY_VERIFY(page.jsPromiseFulfilled() || page.jsPromiseRejected());
+    QTRY_VERIFY(page.jsPromiseSettled());
 }
 
 void tst_QWebEnginePage::getUserMediaRequestDesktopAudio()
@@ -1808,11 +1934,11 @@ void tst_QWebEnginePage::getUserMediaRequestDesktopAudio()
 
     page.jsGetUserMedia(
         QStringLiteral("{audio: { mandatory: { chromeMediaSource: 'desktop' }}}"));
-    QTRY_VERIFY(!page.jsPromiseFulfilled() && page.jsPromiseRejected());
+    QTRY_VERIFY(page.jsPromiseRejected());
 
     page.jsGetUserMedia(
         QStringLiteral("{audio: { mandatory: { chromeMediaSource: 'desktop' }}, video: true}"));
-    QTRY_VERIFY(!page.jsPromiseFulfilled() && page.jsPromiseRejected());
+    QTRY_VERIFY(page.jsPromiseRejected());
 }
 
 void tst_QWebEnginePage::getUserMediaRequestSettingDisabled()
@@ -1825,7 +1951,7 @@ void tst_QWebEnginePage::getUserMediaRequestSettingDisabled()
     // asking for permission first.
 
     page.jsGetUserMedia(QStringLiteral("{video: { mandatory: { chromeMediaSource: 'desktop' }}}"));
-    QTRY_VERIFY(!page.jsPromiseFulfilled() && page.jsPromiseRejected());
+    QTRY_VERIFY(page.jsPromiseRejected());
 }
 
 // Try to trigger any possible race condition between the UI thread (permission
@@ -1833,7 +1959,7 @@ void tst_QWebEnginePage::getUserMediaRequestSettingDisabled()
 void tst_QWebEnginePage::getUserMediaRequestDesktopVideoManyPages()
 {
     const QString constraints = QStringLiteral("{video: { mandatory: { chromeMediaSource: 'desktop' }}}");
-    const QWebEnginePage::Feature feature = QWebEnginePage::DesktopVideoCapture;
+    const QWebEnginePermission::PermissionType permissionType = QWebEnginePermission::PermissionType::DesktopVideoCapture;
     std::vector<GetUserMediaTestPage> pages(10);
 
     // Desktop capture needs to be on a desktop
@@ -1854,11 +1980,11 @@ void tst_QWebEnginePage::getUserMediaRequestDesktopVideoManyPages()
     for (GetUserMediaTestPage &page : pages)
         page.jsGetUserMedia(constraints);
     for (GetUserMediaTestPage &page : pages)
-        QTRY_VERIFY(page.gotFeatureRequest(feature));
+        QTRY_VERIFY(page.gotExpectedRequests(/*isDesktopPermission*/ true, permissionType));
     for (GetUserMediaTestPage &page : pages)
         page.acceptPendingRequest();
     for (GetUserMediaTestPage &page : pages)
-        QTRY_VERIFY(page.jsPromiseFulfilled() || page.jsPromiseRejected());
+        QTRY_VERIFY(page.jsPromiseSettled());
 }
 
 // Try to trigger any possible race condition between the UI or audio/device
@@ -1866,7 +1992,7 @@ void tst_QWebEnginePage::getUserMediaRequestDesktopVideoManyPages()
 void tst_QWebEnginePage::getUserMediaRequestDesktopVideoManyRequests()
 {
     const QString constraints = QStringLiteral("{video: { mandatory: { chromeMediaSource: 'desktop' }}}");
-    const QWebEnginePage::Feature feature = QWebEnginePage::DesktopVideoCapture;
+    const QWebEnginePermission::PermissionType permissionType = QWebEnginePermission::PermissionType::DesktopVideoCapture;
     GetUserMediaTestPage page;
 
     // Desktop capture needs to be on a desktop
@@ -1880,9 +2006,9 @@ void tst_QWebEnginePage::getUserMediaRequestDesktopVideoManyRequests()
     page.settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true);
     for (int i = 0; i != 100; ++i) {
         page.jsGetUserMedia(constraints);
-        QTRY_VERIFY(page.gotFeatureRequest(feature));
+        QTRY_VERIFY(page.gotExpectedRequests(/*isDesktopPermission*/ true, permissionType));
         page.acceptPendingRequest();
-        QTRY_VERIFY(page.jsPromiseFulfilled() || page.jsPromiseRejected());
+        QTRY_VERIFY(page.jsPromiseSettled());
     }
 }
 
@@ -2637,7 +2763,7 @@ void tst_QWebEnginePage::setContent_data()
     QTest::newRow("UTF-8 plain text") << "text/plain; charset=utf-8" << str.toUtf8() << str;
 
     QBuffer out16;
-    out16.open(QIODevice::WriteOnly);
+    QVERIFY2(out16.open(QIODevice::WriteOnly), qPrintable(out16.errorString()));
     QTextStream stream16(&out16);
     stream16.setEncoding(QStringConverter::Utf16);
     stream16 << str;
@@ -3233,13 +3359,13 @@ void tst_QWebEnginePage::toPlainTextLoadFinishedRace()
 
 void tst_QWebEnginePage::setZoomFactor()
 {
-    TestBasePage page, page2;
+    TestBasePage page;
 
     QCOMPARE(page.zoomFactor(), 1.0);
     page.setZoomFactor(2.5);
     QCOMPARE(page.zoomFactor(), 2.5);
 
-    const QUrl url1("qrc:/resources/test1.html"), url2(QUrl("qrc:/resources/test2.html"));
+    const QUrl url1("qrc:/resources/test1.html");
 
     page.load(url1);
     QTRY_COMPARE(page.loadSpy.size(), 1);
@@ -3253,22 +3379,26 @@ void tst_QWebEnginePage::setZoomFactor()
     QCOMPARE(page.zoomFactor(), 2.5);
 
     // try loading different url and check new values after load
+    const QUrl url2(QUrl("qrc:/resources/test2.html"));
+
+    // navigating away to different url should keep zoom
     page.loadSpy.clear();
-    for (auto &&p : {
-            qMakePair(&page, 2.5), // navigating away to different url should keep zoom
-            qMakePair(&page2, 1.0), // same url navigation in diffent page shouldn't be affected
-        }) {
-        auto &&page = *p.first; auto zoomFactor = p.second;
-        page.load(url2);
-        QTRY_COMPARE(page.loadSpy.size(), 1);
-        QVERIFY(page.loadSpy.last().first().toBool());
-        QCOMPARE(page.zoomFactor(), zoomFactor);
-    }
+    page.load(url2);
+    QTRY_COMPARE(page.loadSpy.size(), 1);
+    QVERIFY(page.loadSpy.last().first().toBool());
+    QCOMPARE(page.zoomFactor(), 2.5);
+
+    // same url navigation in different page shouldn't be affected
+    TestBasePage page2;
+    page2.load(url2);
+    QTRY_COMPARE(page2.loadSpy.size(), 1);
+    QVERIFY(page2.loadSpy.last().first().toBool());
+    QCOMPARE(page2.zoomFactor(), 1.0);
 
     // should have no influence on first page
     page2.setZoomFactor(3.5);
-    for (auto &&p : { qMakePair(&page, 2.5), qMakePair(&page2, 3.5), })
-        QCOMPARE(p.first->zoomFactor(), p.second);
+    QCOMPARE(page.zoomFactor(), 2.5);
+    QCOMPARE(page2.zoomFactor(), 3.5);
 }
 
 void tst_QWebEnginePage::mouseButtonTranslation()
@@ -3310,13 +3440,19 @@ void tst_QWebEnginePage::mouseButtonTranslation()
 
 void tst_QWebEnginePage::mouseMovementProperties()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
+    if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
+        QSKIP("Wayland: Can't manipulating the mouse cursor in auto test.");
+
     QWebEngineView view;
     ConsolePage page;
     view.setPage(&page);
     view.resize(640, 480);
     QTest::mouseMove(&view, QPoint(10, 10));
     view.show();
-    QVERIFY(QTest::qWaitForWindowExposed(&view));
+    view.window()->windowHandle()->requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(&view));
 
     QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool)));
     page.setHtml(QStringLiteral(
@@ -3795,20 +3931,32 @@ void tst_QWebEnginePage::dynamicFrame()
 
 struct NotificationPage : ConsolePage {
     Q_OBJECT
-    const QWebEnginePage::PermissionPolicy policy;
+    const QWebEnginePermission::State policy;
 
 public:
-    NotificationPage(QWebEnginePage::PermissionPolicy ppolicy) : policy(ppolicy) {
+    NotificationPage(QWebEnginePermission::State ppolicy) : policy(ppolicy) {
         connect(this, &QWebEnginePage::loadFinished, [load = spyLoad.ref()] (bool result) mutable { load(result); });
 
-        connect(this, &QWebEnginePage::featurePermissionRequested,
-                [this] (const QUrl &origin, QWebEnginePage::Feature feature) {
-            if (feature != QWebEnginePage::Notifications)
+        connect(this, &QWebEnginePage::permissionRequested,
+                [this] (QWebEnginePermission permission) {
+            if (permission.permissionType() != QWebEnginePermission::PermissionType::Notifications)
                 return;
             if (spyRequest.wasCalled())
                 QFAIL("request executed twise!");
-            setFeaturePermission(origin, feature, policy);
-            spyRequest.ref()(origin);
+            switch (policy) {
+            case QWebEnginePermission::State::Granted:
+                permission.grant();
+                break;
+            case QWebEnginePermission::State::Denied:
+                permission.deny();
+                break;
+            case QWebEnginePermission::State::Ask:
+                permission.reset();
+                break;
+            case QWebEnginePermission::State::Invalid:
+                break;
+            }
+            spyRequest.ref()(permission.origin());
         });
 
         load(QStringLiteral("qrc:///shared/notification.html"));
@@ -3819,7 +3967,6 @@ struct NotificationPage : ConsolePage {
 
     QString getPermission() { return evaluateJavaScriptSync(this, "getPermission()").toString(); }
     void requestPermission() { runJavaScript("requestPermission()"); }
-    void resetPermission() { runJavaScript("resetPermission()"); }
     void sendNotification(const QString &title, const QString &body) {
         runJavaScript("sendNotification('" + title + "', '" + body + "')");
     }
@@ -3828,41 +3975,50 @@ struct NotificationPage : ConsolePage {
 void tst_QWebEnginePage::notificationPermission_data()
 {
     QTest::addColumn<bool>("setOnInit");
-    QTest::addColumn<QWebEnginePage::PermissionPolicy>("policy");
+    QTest::addColumn<QWebEnginePermission::State>("policy");
     QTest::addColumn<QString>("permission");
-    QTest::newRow("denyOnInit")  << true  << QWebEnginePage::PermissionDeniedByUser << "denied";
-    QTest::newRow("deny")        << false << QWebEnginePage::PermissionDeniedByUser << "denied";
-    QTest::newRow("grant")       << false << QWebEnginePage::PermissionGrantedByUser << "granted";
-    QTest::newRow("grantOnInit") << true  << QWebEnginePage::PermissionGrantedByUser << "granted";
+    QTest::newRow("denyOnInit")  << true  << QWebEnginePermission::State::Denied << "denied";
+    QTest::newRow("deny")        << false << QWebEnginePermission::State::Denied << "denied";
+    QTest::newRow("grant")       << false << QWebEnginePermission::State::Granted << "granted";
+    QTest::newRow("grantOnInit") << true  << QWebEnginePermission::State::Granted << "granted";
 }
 
 void tst_QWebEnginePage::notificationPermission()
 {
     QFETCH(bool, setOnInit);
-    QFETCH(QWebEnginePage::PermissionPolicy, policy);
+    QFETCH(QWebEnginePermission::State, policy);
     QFETCH(QString, permission);
 
     QWebEngineProfile otr;
-    otr.setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions);
+    otr.setPersistentPermissionsPolicy(QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime);
     QWebEnginePage page(&otr, nullptr);
 
     QUrl baseUrl("https://www.example.com/somepage.html");
 
     bool permissionRequested = false, errorState = false;
-    connect(&page, &QWebEnginePage::featurePermissionRequested, &page, [&] (const QUrl &o, QWebEnginePage::Feature f) {
-        if (f != QWebEnginePage::Notifications)
+    connect(&page, &QWebEnginePage::permissionRequested, &page, [&] (QWebEnginePermission permission) {
+        if (permission.permissionType() != QWebEnginePermission::PermissionType::Notifications)
             return;
-        if (permissionRequested || o != baseUrl.url(QUrl::RemoveFilename)) {
-            qWarning() << "Unexpected case. Can't proceed." << setOnInit << permissionRequested << o;
+        if (permissionRequested || permission.origin() != baseUrl.url(QUrl::RemoveFilename)) {
+            qWarning() << "Unexpected case. Can't proceed." << setOnInit << permissionRequested << permission.origin();
             errorState = true;
             return;
         }
         permissionRequested = true;
-        page.setFeaturePermission(o, f, policy);
+
+        if (policy == QWebEnginePermission::State::Granted)
+            permission.grant();
+        else
+            permission.deny();
     });
 
-    if (setOnInit)
-        page.setFeaturePermission(baseUrl, QWebEnginePage::Notifications, policy);
+    QWebEnginePermission permissionObject = otr.queryPermission(baseUrl, QWebEnginePermission::PermissionType::Notifications);
+    if (setOnInit) {
+        if (policy == QWebEnginePermission::State::Granted)
+            permissionObject.grant();
+        else
+            permissionObject.deny();
+    }
 
     QSignalSpy spy(&page, &QWebEnginePage::loadFinished);
     page.setHtml(QString("<html><body>Test</body></html>"), baseUrl);
@@ -3871,7 +4027,10 @@ void tst_QWebEnginePage::notificationPermission()
     QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), setOnInit ? permission : QLatin1String("default"));
 
     if (!setOnInit) {
-        page.setFeaturePermission(baseUrl, QWebEnginePage::Notifications, policy);
+        if (policy == QWebEnginePermission::State::Granted)
+            permissionObject.grant();
+        else
+            permissionObject.deny();
         QTRY_COMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), permission);
     }
 
@@ -3885,10 +4044,9 @@ void tst_QWebEnginePage::notificationPermission()
 
 void tst_QWebEnginePage::sendNotification()
 {
-    NotificationPage page(QWebEnginePage::PermissionGrantedByUser);
+    NotificationPage page(QWebEnginePermission::State::Granted);
     QVERIFY(page.spyLoad.waitForResult());
 
-    page.resetPermission();
     page.requestPermission();
     auto origin = page.spyRequest.waitForResult();
     QVERIFY(page.spyRequest.wasCalled());
@@ -3937,21 +4095,25 @@ void tst_QWebEnginePage::clipboardReadWritePermissionInitialState_data()
 {
     QTest::addColumn<bool>("canAccessClipboard");
     QTest::addColumn<bool>("canPaste");
-    QTest::addColumn<QString>("permission");
-    QTest::newRow("access and paste should grant") << true << true << "granted";
-    QTest::newRow("no access should prompt") << false << true << "prompt";
-    QTest::newRow("no paste should prompt") << true << false << "prompt";
-    QTest::newRow("no access or paste should prompt") << false << false << "prompt";
+    QTest::addColumn<QString>("readPermission");
+    QTest::addColumn<QString>("writePermission");
+    QTest::newRow("access and paste should grant both") << true << true << "granted" << "granted";
+    QTest::newRow("paste only should prompt for both") << false << true << "prompt" << "prompt";
+    QTest::newRow("access only should grant for write only")
+            << true << false << "prompt" << "granted";
+    QTest::newRow("no access or paste should prompt for both")
+            << false << false << "prompt" << "prompt";
 }
 
 void tst_QWebEnginePage::clipboardReadWritePermissionInitialState()
 {
     QFETCH(bool, canAccessClipboard);
     QFETCH(bool, canPaste);
-    QFETCH(QString, permission);
+    QFETCH(QString, readPermission);
+    QFETCH(QString, writePermission);
 
     QWebEngineProfile otr;
-    otr.setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions);
+    otr.setPersistentPermissionsPolicy(QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime);
     QWebEngineView view(&otr);
     QWebEnginePage &page = *view.page();
     view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true);
@@ -3965,54 +4127,54 @@ void tst_QWebEnginePage::clipboardReadWritePermissionInitialState()
     QTRY_COMPARE(spy.size(), 1);
 
     evaluateJavaScriptSync(&page, clipboardPermissionQuery("readPermission", "clipboard-read"));
-    QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("readPermission")), permission);
+    QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("readPermission")), readPermission);
     evaluateJavaScriptSync(&page, clipboardPermissionQuery("writePermission", "clipboard-write"));
-    QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("writePermission")), permission);
+    QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("writePermission")), writePermission);
 }
 
 void tst_QWebEnginePage::clipboardReadWritePermission_data()
 {
     QTest::addColumn<bool>("canAccessClipboard");
-    QTest::addColumn<QWebEnginePage::PermissionPolicy>("initialPolicy");
+    QTest::addColumn<QWebEnginePermission::State>("initialPolicy");
     QTest::addColumn<QString>("initialPermission");
-    QTest::addColumn<QWebEnginePage::PermissionPolicy>("requestPolicy");
+    QTest::addColumn<QWebEnginePermission::State>("requestPolicy");
     QTest::addColumn<QString>("finalPermission");
 
     QTest::newRow("noAccessGrantGrant")
-            << false << QWebEnginePage::PermissionGrantedByUser << "granted"
-            << QWebEnginePage::PermissionGrantedByUser << "granted";
+            << false << QWebEnginePermission::State::Granted << "granted"
+            << QWebEnginePermission::State::Granted << "granted";
     QTest::newRow("noAccessGrantDeny")
-            << false << QWebEnginePage::PermissionGrantedByUser << "granted"
-            << QWebEnginePage::PermissionDeniedByUser << "denied";
+            << false << QWebEnginePermission::State::Granted << "granted"
+            << QWebEnginePermission::State::Denied << "denied";
     QTest::newRow("noAccessDenyGrant")
-            << false << QWebEnginePage::PermissionDeniedByUser << "denied"
-            << QWebEnginePage::PermissionGrantedByUser << "granted";
-    QTest::newRow("noAccessDenyDeny") << false << QWebEnginePage::PermissionDeniedByUser << "denied"
-                                      << QWebEnginePage::PermissionDeniedByUser << "denied";
-    QTest::newRow("noAccessAskGrant") << false << QWebEnginePage::PermissionUnknown << "prompt"
-                                      << QWebEnginePage::PermissionGrantedByUser << "granted";
+            << false << QWebEnginePermission::State::Denied << "denied"
+            << QWebEnginePermission::State::Granted << "granted";
+    QTest::newRow("noAccessDenyDeny") << false << QWebEnginePermission::State::Denied << "denied"
+                                      << QWebEnginePermission::State::Denied << "denied";
+    QTest::newRow("noAccessAskGrant") << false << QWebEnginePermission::State::Ask << "prompt"
+                                      << QWebEnginePermission::State::Granted << "granted";
 
     // All policies are ignored and overridden by setting JsCanAccessClipboard and JsCanPaste to
     // true
     QTest::newRow("accessGrantGrant")
-            << true << QWebEnginePage::PermissionGrantedByUser << "granted"
-            << QWebEnginePage::PermissionGrantedByUser << "granted";
-    QTest::newRow("accessDenyDeny") << true << QWebEnginePage::PermissionDeniedByUser << "granted"
-                                    << QWebEnginePage::PermissionDeniedByUser << "granted";
-    QTest::newRow("accessAskAsk") << true << QWebEnginePage::PermissionUnknown << "granted"
-                                  << QWebEnginePage::PermissionUnknown << "granted";
+            << true << QWebEnginePermission::State::Granted << "granted"
+            << QWebEnginePermission::State::Granted << "granted";
+    QTest::newRow("accessDenyDeny") << true << QWebEnginePermission::State::Denied << "granted"
+                                    << QWebEnginePermission::State::Denied << "granted";
+    QTest::newRow("accessAskAsk") << true << QWebEnginePermission::State::Ask << "granted"
+                                  << QWebEnginePermission::State::Ask << "granted";
 }
 
 void tst_QWebEnginePage::clipboardReadWritePermission()
 {
     QFETCH(bool, canAccessClipboard);
-    QFETCH(QWebEnginePage::PermissionPolicy, initialPolicy);
+    QFETCH(QWebEnginePermission::State, initialPolicy);
     QFETCH(QString, initialPermission);
-    QFETCH(QWebEnginePage::PermissionPolicy, requestPolicy);
+    QFETCH(QWebEnginePermission::State, requestPolicy);
     QFETCH(QString, finalPermission);
 
     QWebEngineProfile otr;
-    otr.setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions);
+    otr.setPersistentPermissionsPolicy(QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime);
     QWebEngineView view(&otr);
     QWebEnginePage &page = *view.page();
     view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true);
@@ -4026,20 +4188,45 @@ void tst_QWebEnginePage::clipboardReadWritePermission()
     bool errorState = false;
 
     // if JavascriptCanAccessClipboard is true, this never fires
-    connect(&page, &QWebEnginePage::featurePermissionRequested, &page,
-            [&](const QUrl &o, QWebEnginePage::Feature f) {
-                if (f != QWebEnginePage::ClipboardReadWrite)
+    connect(&page, &QWebEnginePage::permissionRequested, &page,
+            [&](QWebEnginePermission permission) {
+                if (permission.permissionType() != QWebEnginePermission::PermissionType::ClipboardReadWrite)
                     return;
-                if (o != baseUrl.url(QUrl::RemoveFilename)) {
-                    qWarning() << "Unexpected case. Can't proceed." << o;
+                if (permission.origin() != baseUrl.url(QUrl::RemoveFilename)) {
+                    qWarning() << "Unexpected case. Can't proceed." << permission.origin();
                     errorState = true;
                     return;
                 }
                 permissionRequestCount++;
-                page.setFeaturePermission(o, f, requestPolicy);
+                switch (requestPolicy) {
+                case QWebEnginePermission::State::Granted:
+                    permission.grant();
+                    break;
+                case QWebEnginePermission::State::Denied:
+                    permission.deny();
+                    break;
+                case QWebEnginePermission::State::Ask:
+                    permission.reset();
+                    break;
+                default:
+                    break;
+                }
             });
 
-    page.setFeaturePermission(baseUrl, QWebEnginePage::ClipboardReadWrite, initialPolicy);
+    QWebEnginePermission permissionObject = otr.queryPermission(baseUrl, QWebEnginePermission::PermissionType::ClipboardReadWrite);
+    switch (initialPolicy) {
+    case QWebEnginePermission::State::Granted:
+        permissionObject.grant();
+        break;
+    case QWebEnginePermission::State::Denied:
+        permissionObject.deny();
+        break;
+    case QWebEnginePermission::State::Ask:
+        permissionObject.reset();
+        break;
+    case QWebEnginePermission::State::Invalid:
+        break;
+    }
 
     QSignalSpy spy(&page, &QWebEnginePage::loadFinished);
     page.setHtml(QString("<html><body>Test</body></html>"), baseUrl);
@@ -4099,31 +4286,35 @@ void tst_QWebEnginePage::contentsSize()
 
 void tst_QWebEnginePage::localFontAccessPermission_data()
 {
-    QTest::addColumn<QWebEnginePage::PermissionPolicy>("policy");
+    QTest::addColumn<QWebEnginePermission::State>("policy");
     QTest::addColumn<bool>("ignore");
     QTest::addColumn<bool>("shouldBeEmpty");
 
-    QTest::newRow("ignore")         << QWebEnginePage::PermissionDeniedByUser  << true  << true;
-    QTest::newRow("setDeny")        << QWebEnginePage::PermissionDeniedByUser  << false << true;
-    QTest::newRow("setGrant")       << QWebEnginePage::PermissionGrantedByUser << false << false;
+    QTest::newRow("ignore")   << QWebEnginePermission::State::Denied  << true  << true;
+    QTest::newRow("setDeny")  << QWebEnginePermission::State::Denied  << false << true;
+    QTest::newRow("setGrant") << QWebEnginePermission::State::Granted << false << false;
 }
 
 void tst_QWebEnginePage::localFontAccessPermission() {
-    QFETCH(QWebEnginePage::PermissionPolicy, policy);
+    QFETCH(QWebEnginePermission::State, policy);
     QFETCH(bool, ignore);
     QFETCH(bool, shouldBeEmpty);
 
     QWebEngineView view;
     QWebEnginePage page(&view);
-    page.profile()->setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions);
+    page.profile()->setPersistentPermissionsPolicy(QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime);
     view.setPage(&page);
 
-    connect(&page, &QWebEnginePage::featurePermissionRequested, &page, [&] (const QUrl &o, QWebEnginePage::Feature f) {
-        if (f != QWebEnginePage::LocalFontsAccess)
+    connect(&page, &QWebEnginePage::permissionRequested, &page, [&] (QWebEnginePermission permission) {
+        if (permission.permissionType() != QWebEnginePermission::PermissionType::LocalFontsAccess)
             return;
 
-        if (!ignore)
-            page.setFeaturePermission(o, f, policy);
+        if (!ignore) {
+            if (policy == QWebEnginePermission::State::Granted)
+                permission.grant();
+            else
+                permission.deny();
+        }
     });
 
     QSignalSpy spy(&page, &QWebEnginePage::loadFinished);
@@ -4605,7 +4796,7 @@ void tst_QWebEnginePage::discardAbortsPendingLoadAndPreservesCommittedLoad()
 
     connect(&page, &QWebEnginePage::loadStarted,
             [&]() { page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); });
-    QString url2 = QStringLiteral("about:blank");
+    QString url2 = QStringLiteral("qrc:/resources/test1.html");
     page.setUrl(url2);
     QTRY_COMPARE(loadStartedSpy.size(), 1);
     loadStartedSpy.clear();
@@ -5242,6 +5433,27 @@ void tst_QWebEnginePage::backgroundColor()
     QTRY_COMPARE(view.grab().toImage().pixelColor(center), Qt::green);
 }
 
+void tst_QWebEnginePage::popupOnTransparentBackground()
+{
+    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(
+                QPlatformIntegration::WindowActivation))
+        QSKIP("Cannot test on platforms without window activation capability");
+
+    QWebEngineView view;
+    view.resize(640, 480);
+    view.page()->setBackgroundColor(Qt::transparent);
+    view.show();
+    QVERIFY(QTest::qWaitForWindowExposed(&view));
+    QSignalSpy spyLoadFinished(&view, SIGNAL(loadFinished(bool)));
+    view.setHtml(QLatin1String("<html><head></head><body><select id='foo' name='meow'>"
+                               "<option>fran</option><option>troz</option>"
+                               "</select></body></html>"));
+    QTRY_COMPARE(spyLoadFinished.size(), 1);
+    makeClick(view.windowHandle(), false, elementCenter(view.page(), "foo"));
+    QPointer<QWidget> popup;
+    QTRY_VERIFY((popup = QApplication::activePopupWidget()));
+}
+
 void tst_QWebEnginePage::audioMuted()
 {
     QWebEngineProfile profile;
@@ -5380,11 +5592,11 @@ void tst_QWebEnginePage::clientHints_data()
     QTest::addColumn<QString>("platformVersion");
     QTest::addColumn<QString>("bitness");
     QTest::addColumn<bool>("isWOW64");
-    QTest::addColumn<QHash<QString, QString>>("fullVersionList");
+    QTest::addColumn<QVariantMap>("fullVersionList");
 
-    QTest::newRow("Modify values") << true << "Abc" << "AmigaOS" << "Ultra" << true << "1.99" << "3" << "x64" << true << QHash<QString, QString>({{"APITest", "1.0.0"}, {"App", "5.0"}});
-    QTest::newRow("Empty values") << true << "" << "" << "" << false << "" << "" << "" << false << QHash<QString, QString>();
-    QTest::newRow("Disable headers") << false << "" << "" << "" << false << "" << "" << "" << false << QHash<QString, QString>();
+    QTest::newRow("Modify values") << true << "Abc" << "AmigaOS" << "Ultra" << true << "1.99" << "3" << "x64" << true << QVariantMap({{"APITest", "1.0.0"}, {"App", "5.0"}});
+    QTest::newRow("Empty values") << true << "" << "" << "" << false << "" << "" << "" << false << QVariantMap();
+    QTest::newRow("Disable headers") << false << "" << "" << "" << false << "" << "" << "" << false << QVariantMap();
 }
 
 void tst_QWebEnginePage::clientHints()
@@ -5398,8 +5610,7 @@ void tst_QWebEnginePage::clientHints()
     QFETCH(QString, platformVersion);
     QFETCH(QString, bitness);
     QFETCH(bool, isWOW64);
-    typedef QHash<QString, QString> brandVersionPairs;
-    QFETCH(brandVersionPairs, fullVersionList);
+    QFETCH(QVariantMap, fullVersionList);
 
     QWebEnginePage page;
     QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
@@ -5724,22 +5935,23 @@ void tst_QWebEnginePage::chooseDesktopMedia()
     QWebEnginePage page;
     QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool)));
     page.settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true);
-    page.profile()->setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions);
+    page.profile()->setPersistentPermissionsPolicy(QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime);
 
     bool desktopMediaRequested = false;
+    bool emptyDesktopMediaRequested = false;
     bool permissionRequested = false;
 
     connect(&page, &QWebEnginePage::desktopMediaRequested,
-            [&](const QWebEngineDesktopMediaRequest &) {
+            [&](const QWebEngineDesktopMediaRequest &request) {
                 desktopMediaRequested = true;
+                emptyDesktopMediaRequested = request.screensModel()->rowCount() == 0;
             });
 
-    connect(&page, &QWebEnginePage::featurePermissionRequested,
-            [&](const QUrl &securityOrigin, QWebEnginePage::Feature feature) {
+    connect(&page, &QWebEnginePage::permissionRequested,
+            [&](QWebEnginePermission permission) {
                 permissionRequested = true;
                 // Handle permission to 'complete' the media request
-                page.setFeaturePermission(securityOrigin, feature,
-                                          QWebEnginePage::PermissionGrantedByUser);
+                permission.grant();
             });
 
     page.load(QUrl(server.url()));
@@ -5752,7 +5964,7 @@ void tst_QWebEnginePage::chooseDesktopMedia()
                                "})()").arg(extensionId));
 
     QTRY_VERIFY(desktopMediaRequested);
-    QTRY_VERIFY(permissionRequested);
+    QTRY_VERIFY(permissionRequested || emptyDesktopMediaRequested);
 #endif // QT_CONFIG(webengine_extensions) && QT_CONFIG(webengine_webrtc)
 }
 
diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
index ef069ac1c69..eefb418638f 100644
--- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
+++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <util.h>
 #include <QtCore/qbuffer.h>
@@ -26,6 +26,8 @@
 #include <map>
 #include <mutex>
 
+using namespace Qt::StringLiterals;
+
 class tst_QWebEngineProfile : public QObject
 {
     Q_OBJECT
@@ -58,6 +60,9 @@ private Q_SLOTS:
     void badDeleteOrder();
     void permissionPersistence_data();
     void permissionPersistence();
+    void queryPermission_data();
+    void queryPermission();
+    void listPermissions();
     void qtbug_71895(); // this should be the last test
 };
 
@@ -170,7 +175,7 @@ class TestServer : public HttpServer
         }
 
         QFile file(resourceDir.filePath(path));
-        file.open(QIODevice::ReadOnly);
+        QVERIFY2(file.open(QIODevice::ReadOnly), qPrintable(file.errorString()));
         QByteArray data = file.readAll();
         rr->setResponseBody(data);
         QMimeDatabase db;
@@ -202,6 +207,12 @@ void tst_QWebEngineProfile::clearDataFromCache()
     // Wait for GET /favicon.ico
     QTRY_COMPARE(serverSpy.size(), 3);
 
+#if defined(Q_OS_WIN)
+    // FIXME: A http cache entry might be still in use after all the wait above and this blocks
+    //        clearing the http cache. Find a better way to wait for cache entries.
+    QTest::qWait(500);
+#endif
+
     QVERIFY(cacheDir.exists("Cache"));
     qint64 sizeBeforeClear = totalSize(cacheDir);
     QCOMPARE_GT(sizeBeforeClear, 0);
@@ -662,7 +673,7 @@ class XhrStatusUrlSchemeHandler : public QWebEngineUrlSchemeHandler
         QString path = job->requestUrl().path();
         if (path == "/") {
             QBuffer *buffer = new QBuffer(job);
-            buffer->open(QBuffer::ReadWrite);
+            QVERIFY2(buffer->open(QBuffer::ReadWrite), qPrintable(buffer->errorString()));
             buffer->write(QByteArrayLiteral(R"(
 <html>
   <body>
@@ -686,7 +697,7 @@ class XhrStatusUrlSchemeHandler : public QWebEngineUrlSchemeHandler
             job->reply("text/html", buffer);
         } else if (path == "/qwebchannel.js") {
             QFile *file = new QFile(":/qtwebchannel/qwebchannel.js", job);
-            file->open(QFile::ReadOnly);
+            QVERIFY2(file->open(QFile::ReadOnly), qPrintable(file->errorString()));
             job->reply("application/javascript", file);
         } else if (path == "/ok") {
             QBuffer *buffer = new QBuffer(job);
@@ -838,6 +849,12 @@ void tst_QWebEngineProfile::httpAcceptLanguage()
     // Test changing an existing page and profile
     QWebEngineProfile::defaultProfile()->setHttpAcceptLanguage(testLang);
     QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("navigator.languages")).toStringList(), QStringList(testLang));
+
+    // Test language list with quality values
+    QWebEngineProfile::defaultProfile()->setHttpAcceptLanguage(
+            u"en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7"_s);
+    QCOMPARE(evaluateJavaScriptSync(&page, u"navigator.languages"_s).toStringList(),
+             QStringList({u"en-US"_s, u"en"_s, u"zh-CN"_s, u"zh"_s}));
 }
 
 void tst_QWebEngineProfile::downloadItem()
@@ -845,7 +862,7 @@ void tst_QWebEngineProfile::downloadItem()
     qRegisterMetaType<QWebEngineDownloadRequest *>();
     QWebEngineProfile testProfile;
     QWebEnginePage page(&testProfile);
-    QSignalSpy downloadSpy(&testProfile, SIGNAL(downloadRequested(QWebEngineDownloadRequest *)));
+    QSignalSpy downloadSpy(&testProfile, SIGNAL(downloadRequested(QWebEngineDownloadRequest*)));
     page.load(QUrl::fromLocalFile(QCoreApplication::applicationFilePath()));
     QTRY_COMPARE(downloadSpy.size(), 1);
 }
@@ -1022,10 +1039,10 @@ void tst_QWebEngineProfile::permissionPersistence_data()
     QTest::addColumn<QWebEngineProfile::PersistentPermissionsPolicy>("policy");
     QTest::addColumn<bool>("granted");
 
-    QTest::newRow("noPersistenceNotificationsNoGrant")      << QWebEngineProfile::NoPersistentPermissions       << false;
-    QTest::newRow("noPersistenceNotificationsGrant")        << QWebEngineProfile::NoPersistentPermissions       << true;
-    QTest::newRow("memoryPersistenceNotificationsNoGrant")  << QWebEngineProfile::PersistentPermissionsInMemory << false;
-    QTest::newRow("diskPersistenceNotificationsGrant")      << QWebEngineProfile::PersistentPermissionsOnDisk   << true;
+    QTest::newRow("noPersistenceNotificationsNoGrant")      << QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime  << false;
+    QTest::newRow("noPersistenceNotificationsGrant")        << QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime  << true;
+    QTest::newRow("memoryPersistenceNotificationsNoGrant")  << QWebEngineProfile::PersistentPermissionsPolicy::StoreInMemory << false;
+    QTest::newRow("diskPersistenceNotificationsGrant")      << QWebEngineProfile::PersistentPermissionsPolicy::StoreOnDisk   << true;
 }
 
 void tst_QWebEngineProfile::permissionPersistence()
@@ -1051,8 +1068,12 @@ void tst_QWebEngineProfile::permissionPersistence()
 
     QVariant variant = granted ? "granted" : "denied";
     QVariant defaultVariant = "default";
-    page->setFeaturePermission(server.url("/hedgehog.html"), QWebEnginePage::Notifications,
-        granted ? QWebEnginePage::PermissionGrantedByUser : QWebEnginePage::PermissionDeniedByUser);
+
+    QWebEnginePermission permissionObject = profile->queryPermission(server.url("/hedgehog.html"), QWebEnginePermission::PermissionType::Notifications);
+    if (granted)
+        permissionObject.grant();
+    else
+        permissionObject.deny();
     QCOMPARE(evaluateJavaScriptSync(page.get(), "Notification.permission"), variant);
 
     page.reset();
@@ -1060,7 +1081,7 @@ void tst_QWebEngineProfile::permissionPersistence()
     loadSpy.reset();
 
     bool expectSame = false;
-    if (policy == QWebEngineProfile::PersistentPermissionsOnDisk) {
+    if (policy == QWebEngineProfile::PersistentPermissionsPolicy::StoreOnDisk) {
         expectSame = true;
 
         // File is written asynchronously, wait for it to be created
@@ -1077,20 +1098,126 @@ void tst_QWebEngineProfile::permissionPersistence()
     QTRY_COMPARE(evaluateJavaScriptSync(page.get(), "Notification.permission"),
         expectSame ? variant : defaultVariant);
 
-    page->setFeaturePermission(server.url("/hedgehog.html"), QWebEnginePage::Notifications, QWebEnginePage::PermissionUnknown);
+    // Re-acquire the permission, since deleting the Profile makes it invalid
+    permissionObject = profile->queryPermission(server.url("/hedgehog.html"), QWebEnginePermission::PermissionType::Notifications);
+    permissionObject.reset();
     QCOMPARE(evaluateJavaScriptSync(page.get(), "Notification.permission"), defaultVariant);
 
     page.reset();
     profile.reset();
     loadSpy.reset();
 
-    if (policy == QWebEngineProfile::PersistentPermissionsOnDisk) {
+    if (policy == QWebEngineProfile::PersistentPermissionsPolicy::StoreOnDisk) {
         // Wait for file to be written to before deleting
         QTest::qWait(1000);
         storageDir.remove("permissions.json");
     }
 
-    QVERIFY(server.stop());
+    QTRY_VERIFY(server.stop());
+}
+
+void tst_QWebEngineProfile::queryPermission_data()
+{
+    QTest::addColumn<QWebEnginePermission::PermissionType>("permissionType");
+    QTest::addColumn<QUrl>("url");
+    QTest::addColumn<bool>("expectedValid");
+
+    QTest::newRow("badUrl")
+        << QWebEnginePermission::PermissionType::Notifications << QUrl(QStringLiteral("//:bad-url"))                     << false;
+    QTest::newRow("badFeature")
+        << QWebEnginePermission::PermissionType::Unsupported   << QUrl(QStringLiteral("qrc:/resources/permission.html")) << false;
+    QTest::newRow("transientFeature")
+        << QWebEnginePermission::PermissionType::MouseLock     << QUrl(QStringLiteral("qrc:/resources/permission.html")) << true;
+    QTest::newRow("good")
+        << QWebEnginePermission::PermissionType::Notifications << QUrl(QStringLiteral("qrc:/resources/permission.html")) << true;
+}
+
+void tst_QWebEngineProfile::queryPermission()
+{
+    QFETCH(QWebEnginePermission::PermissionType, permissionType);
+    QFETCH(QUrl, url);
+    QFETCH(bool, expectedValid);
+
+    QWebEngineProfile profile;
+    // In-memory is the default for otr profiles
+    QVERIFY(profile.persistentPermissionsPolicy() == QWebEngineProfile::PersistentPermissionsPolicy::StoreInMemory);
+
+    QWebEnginePermission permission = profile.queryPermission(url, permissionType);
+    bool valid = permission.isValid();
+    QVERIFY(valid == expectedValid);
+    if (!valid)
+        QVERIFY(permission.state() == QWebEnginePermission::State::Invalid);
+
+    // Verify that we can grant a valid permission, and we can't grant an invalid one...
+    permission.grant();
+    QVERIFY(permission.state() == (valid ? QWebEnginePermission::State::Granted : QWebEnginePermission::State::Invalid));
+
+    // ...and that doing so twice doesn't mess up the state...
+    permission.grant();
+    QVERIFY(permission.state() == (valid ? QWebEnginePermission::State::Granted : QWebEnginePermission::State::Invalid));
+
+    // ...and that the same thing applies to denying them...
+    permission.deny();
+    QVERIFY(permission.state() == (valid ? QWebEnginePermission::State::Denied : QWebEnginePermission::State::Invalid));
+    permission.deny();
+    QVERIFY(permission.state() == (valid ? QWebEnginePermission::State::Denied : QWebEnginePermission::State::Invalid));
+
+    // ...and that resetting works
+    permission.reset();
+    QVERIFY(permission.state() == (valid ? QWebEnginePermission::State::Ask : QWebEnginePermission::State::Invalid));
+    permission.reset();
+    QVERIFY(permission.state() == (valid ? QWebEnginePermission::State::Ask : QWebEnginePermission::State::Invalid));
+}
+
+void tst_QWebEngineProfile::listPermissions()
+{
+    QWebEngineProfile profile;
+    // In-memory is the default for otr profiles
+    QVERIFY(profile.persistentPermissionsPolicy() == QWebEngineProfile::PersistentPermissionsPolicy::StoreInMemory);
+
+    QUrl commonUrl = QUrl(QStringLiteral("http://www.bing.com/maps"));
+    QWebEnginePermission::PermissionType commonType = QWebEnginePermission::PermissionType::Notifications;
+
+    // First, set several permissions at once
+    profile.queryPermission(commonUrl, QWebEnginePermission::PermissionType::Geolocation).deny();
+    profile.queryPermission(commonUrl, QWebEnginePermission::PermissionType::Unsupported).grant(); // Invalid
+    profile.queryPermission(commonUrl, commonType).grant();
+    profile.queryPermission(QUrl(QStringLiteral("http://www.google.com/translate")), commonType).grant();
+
+    QList<QWebEnginePermission> permissionsListAll = profile.listAllPermissions();
+    QList<QWebEnginePermission> permissionsListUrl = profile.listPermissionsForOrigin(commonUrl);
+    QList<QWebEnginePermission> permissionsListFeature = profile.listPermissionsForPermissionType(commonType);
+
+    // Order of returned permissions is not guaranteed, so we must iterate until we find the one we need
+    auto findInList = [](QList<QWebEnginePermission> list, const QUrl &url,
+        QWebEnginePermission::PermissionType permissionType, QWebEnginePermission::State state)
+    {
+        bool found = false;
+        for (auto &permission : list) {
+            if (permission.origin().adjusted(QUrl::RemovePath) == url.adjusted(QUrl::RemovePath)
+                    && permission.permissionType() == permissionType && permission.state() == state) {
+                found = true;
+                break;
+            }
+        }
+        return found;
+    };
+
+    // Check full list
+    QVERIFY(permissionsListAll.size() == 3);
+    QVERIFY(findInList(permissionsListAll, commonUrl, QWebEnginePermission::PermissionType::Geolocation, QWebEnginePermission::State::Denied));
+    QVERIFY(findInList(permissionsListAll, commonUrl, commonType, QWebEnginePermission::State::Granted));
+    QVERIFY(findInList(permissionsListAll, QUrl(QStringLiteral("http://www.google.com")), commonType, QWebEnginePermission::State::Granted));
+
+    // Check list filtered by URL
+    QVERIFY(permissionsListUrl.size() == 2);
+    QVERIFY(findInList(permissionsListUrl, commonUrl, QWebEnginePermission::PermissionType::Geolocation, QWebEnginePermission::State::Denied));
+    QVERIFY(findInList(permissionsListAll, commonUrl, commonType, QWebEnginePermission::State::Granted));
+
+    // Check list filtered by feature
+    QVERIFY(permissionsListFeature.size() == 2);
+    QVERIFY(findInList(permissionsListAll, commonUrl, commonType, QWebEnginePermission::State::Granted));
+    QVERIFY(findInList(permissionsListAll, QUrl(QStringLiteral("http://www.google.com")), commonType, QWebEnginePermission::State::Granted));
 }
 
 void tst_QWebEngineProfile::qtbug_71895()
diff --git a/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp b/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp
index 9ba13589f2a..1dfa94565e7 100644
--- a/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp
+++ b/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp
@@ -69,6 +69,7 @@ private Q_SLOTS:
     void webChannelResettingAndUnsetting();
     void webChannelWithExistingQtObject();
     void navigation();
+    void navigation2();
     void webChannelWithBadString();
     void webChannelWithJavaScriptDisabled();
 #endif
@@ -358,7 +359,10 @@ class TestObject : public QObject
 static QString readFile(const QString &path)
 {
     QFile file(path);
-    file.open(QFile::ReadOnly);
+    if (!file.open(QFile::ReadOnly)) {
+        qWarning("Failed to read file %s: %s", qPrintable(path), qPrintable(file.errorString()));
+        return QString();
+    }
     QByteArray contents = file.readAll();
     file.close();
     return contents;
@@ -577,6 +581,81 @@ void tst_QWebEngineScript::navigation()
     QCOMPARE(testObject.text(), url1);
 }
 
+void tst_QWebEngineScript::navigation2()
+{
+    QWebEngineProfile profile("navigation2");
+    QWebEnginePage page(&profile, nullptr);
+    QWebChannel channel;
+    page.setWebChannel(&channel);
+    QWebEngineScript s1;
+    s1.setInjectionPoint(QWebEngineScript::DocumentCreation);
+    // Check webchannel is installed before DocumentCreation scripts are run
+    // onload shouldn't have run, and neither should wasready
+    s1.setWorldId(QWebEngineScript::MainWorld);
+    s1.setSourceCode("document.passCreation = 0;" \
+                     "if (typeof qt !== undefined) document.passCreation++;" \
+                     "if (document.onloadran) document.passCreation++;" \
+                     "if (document.wasready) document.passCreation++;");
+    page.scripts().insert(s1);
+    QWebEngineScript s2;
+    s2.setInjectionPoint(QWebEngineScript::DocumentReady);
+    // onload shouldn't have run
+    s2.setWorldId(QWebEngineScript::MainWorld);
+    s2.setSourceCode("document.passReady = 0;" \
+                     "if (typeof qt !== undefined) document.passReady++;" \
+                     "if (document.passCreation > 0) document.passReady++;" \
+                     "if (document.passDeferred > 0) document.passReady++;" \
+                     "if (document.onloadran) document.passReady++;" \
+                     "if (document.wasready) document.passReady++;");
+    page.scripts().insert(s2);
+    QWebEngineScript s3;
+    s3.setInjectionPoint(QWebEngineScript::Deferred);
+    // all should have run
+    s3.setWorldId(QWebEngineScript::MainWorld);
+    s3.setSourceCode("document.passDeferred = 0;" \
+                     "if (typeof qt !== undefined) document.passDeferred++;" \
+                     "if (document.passCreation > 0) document.passDeferred++;" \
+                     "if (document.passReady > 0) document.passDeferred++;" \
+                     "if (document.onloadran) document.passDeferred++;" \
+                     "if (document.wasready) document.passDeferred++;");
+    page.scripts().insert(s3);
+
+
+    QString html("<html><head><script>" \
+                 "  document.onloadran = false; document.wasready = false;"\
+                 "  document.addEventListener(\"readystatechange\", (x) => { "\
+                 "      if (x.target.readyState === \"interactive\") document.wasready= true;"\
+                 "  });"\
+                 "  function bodyload() { document.onloadran = true; };"\
+                 "</script></head>" \
+                 "<body onload='bodyload()'><p>hello world</p></body></html>");
+    page.setHtml(html, QUrl("about:blank"));
+    QTRY_COMPARE(evaluateJavaScriptSyncInWorld(&page, "document.passCreation", QWebEngineScript::MainWorld),
+                 QVariant(1));
+    QTRY_COMPARE(evaluateJavaScriptSyncInWorld(&page, "document.passReady", QWebEngineScript::MainWorld),
+                 QVariant(3));
+    QTRY_COMPARE(evaluateJavaScriptSyncInWorld(&page, "document.passDeferred", QWebEngineScript::MainWorld),
+                 QVariant(5));
+
+    QString url2 = QStringLiteral("chrome://gpu/");
+    page.setUrl(url2);
+    QTRY_COMPARE(evaluateJavaScriptSyncInWorld(&page, "document.passCreation", QWebEngineScript::MainWorld),
+                 QVariant(1));
+    QTRY_COMPARE(evaluateJavaScriptSyncInWorld(&page, "document.passReady", QWebEngineScript::MainWorld),
+                 QVariant(2));
+    QTRY_COMPARE(evaluateJavaScriptSyncInWorld(&page, "document.passDeferred", QWebEngineScript::MainWorld),
+                 QVariant(3));
+
+    QString url3 = QStringLiteral("qrc:/resources/test_iframe_main.html");
+    page.setUrl(url3);
+    QTRY_COMPARE(evaluateJavaScriptSyncInWorld(&page, "document.passCreation", QWebEngineScript::MainWorld),
+                 QVariant(1));
+    QTRY_COMPARE(evaluateJavaScriptSyncInWorld(&page, "document.passReady", QWebEngineScript::MainWorld),
+                 QVariant(2));
+    QTRY_COMPARE(evaluateJavaScriptSyncInWorld(&page, "document.passDeferred", QWebEngineScript::MainWorld),
+                 QVariant(3));
+}
+
 // Try to set TestObject::text to an invalid UTF-16 string.
 //
 // See QTBUG-61969.
diff --git a/tests/auto/widgets/qwebengineview/BLACKLIST b/tests/auto/widgets/qwebengineview/BLACKLIST
index 26f2da4bbe8..f9ad091840f 100644
--- a/tests/auto/widgets/qwebengineview/BLACKLIST
+++ b/tests/auto/widgets/qwebengineview/BLACKLIST
@@ -10,3 +10,6 @@ windows
 [horizontalScrollbarTest]
 macos
 rhel # flaky
+
+[setCursorOnEmbeddedView]
+macos # QTest::mousemove is not reliable
diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
index f4ed06e147d..d3100750983 100644
--- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
+++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
@@ -19,11 +19,10 @@
     Boston, MA 02110-1301, USA.
 */
 
-#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
-
 #include <QtWebEngineCore/private/qtwebenginecore-config_p.h>
 #include <qtest.h>
 #include <util.h>
+#include <visualutil.h>
 #include <private/qinputmethod_p.h>
 #include <qpainter.h>
 #include <qpagelayout.h>
@@ -189,6 +188,8 @@ private Q_SLOTS:
     void datalist();
     void longKeyEventText();
     void pageWithPaintListeners();
+    void deferredDelete();
+    void setCursorOnEmbeddedView();
 };
 
 // This will be called before the first test function is executed.
@@ -286,20 +287,20 @@ void tst_QWebEngineView::pageWithPaintListeners()
 
     page.setHtml(empty);
     QTest::qWait(500); // empty page should not trigger
-    QVERIFY(firstContentfulPaintSpy.size() == 0);
-    QVERIFY(largestContentfulPaintSpy.size() == 0);
+    QCOMPARE(firstContentfulPaintSpy.size(), 0);
+    QCOMPARE(largestContentfulPaintSpy.size(), 0);
 
     page.setHtml(backgroundColor);
-    QTRY_VERIFY(firstContentfulPaintSpy.size() == 1);
+    QTRY_COMPARE(firstContentfulPaintSpy.size(), 1);
 
     page.setHtml(text);
-    QTRY_VERIFY(firstContentfulPaintSpy.size() == 2);
-    QTRY_VERIFY(largestContentfulPaintSpy.size() == 1);
+    QTRY_COMPARE(firstContentfulPaintSpy.size(), 2);
+    QTRY_COMPARE(largestContentfulPaintSpy.size(), 1);
 
-#if !QT_CONFIG(webengine_embedded_build)
-    // Embedded builds have different scrollbars that are only painted on hover
+#if !QT_CONFIG(webengine_embedded_build) && !defined(Q_OS_MACOS)
+    // Embedded builds and macOS have different scrollbars that are only painted on hover
     page.setHtml(scrollBars);
-    QTRY_VERIFY(firstContentfulPaintSpy.size() == 3);
+    QTRY_COMPARE(firstContentfulPaintSpy.size(), 3);
 #endif
 }
 
@@ -1240,6 +1241,8 @@ void tst_QWebEngineView::focusOnNavigation()
 
 void tst_QWebEngineView::focusInternalRenderWidgetHostViewQuickItem()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
     // Create a container widget, that will hold a line edit that has initial focus, and a web
     // engine view.
     QScopedPointer<QWidget> containerWidget(new QWidget);
@@ -1261,7 +1264,8 @@ void tst_QWebEngineView::focusInternalRenderWidgetHostViewQuickItem()
     containerWidget->resize(300, 200);
     containerWidget->setLayout(layout);
     containerWidget->show();
-    QVERIFY(QTest::qWaitForWindowExposed(containerWidget.data()));
+    containerWidget->window()->windowHandle()->requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(containerWidget.data()));
 
     // Load the content, and check that focus is not set.
     QSignalSpy loadSpy(webView, SIGNAL(loadFinished(bool)));
@@ -1607,6 +1611,8 @@ class WebViewWithUrlBar : public QWidget {
 
 void tst_QWebEngineView::keyboardFocusAfterPopup()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
     const QString html = QStringLiteral(
         "<html>"
         "  <body onload=\"document.getElementById('input1').focus()\">"
@@ -1618,6 +1624,8 @@ void tst_QWebEngineView::keyboardFocusAfterPopup()
     connect(window.lineEdit, &QLineEdit::editingFinished, [&] { window.webView->setHtml(html); });
     window.webView->settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true);
     window.show();
+    window.window()->windowHandle()->requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(&window));
 
     // Focus will initially go to the QLineEdit.
     QTRY_COMPARE(QApplication::focusWidget(), window.lineEdit);
@@ -1845,6 +1853,12 @@ void tst_QWebEngineView::postData()
 
 void tst_QWebEngineView::inputFieldOverridesShortcuts()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
+    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(
+                QPlatformIntegration::WindowActivation))
+        QSKIP("Cannot test on platforms without window activation capability");
+
     QWebEngineView view;
     bool actionTriggered = false;
     QAction *action = new QAction(&view);
@@ -1860,6 +1874,7 @@ void tst_QWebEngineView::inputFieldOverridesShortcuts()
     QVERIFY(loadFinishedSpy.wait());
 
     view.show();
+    view.window()->windowHandle()->requestActivate();
     QVERIFY(QTest::qWaitForWindowActive(&view));
 
     auto inputFieldValue = [&view] () -> QString {
@@ -2053,9 +2068,12 @@ void tst_QWebEngineView::softwareInputPanel()
 
 void tst_QWebEngineView::inputContextQueryInput()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
     QWebEngineView view;
     view.resize(640, 480);
     view.show();
+    view.window()->windowHandle()->requestActivate();
 
     // testContext will be destroyed before the view, so no events are sent accidentally
     // when the view is destroyed.
@@ -2067,7 +2085,7 @@ void tst_QWebEngineView::inputContextQueryInput()
                  "  <input type='text' id='input1' value='' size='50'/>"
                  "</body></html>");
     QTRY_COMPARE(loadFinishedSpy.size(), 1);
-    QVERIFY(QTest::qWaitForWindowExposed(&view));
+    QVERIFY(QTest::qWaitForWindowActive(&view));
     QCOMPARE(testContext.infos.size(), 0);
 
     // Set focus on an input field.
@@ -2075,7 +2093,7 @@ void tst_QWebEngineView::inputContextQueryInput()
     QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter);
     QTRY_COMPARE(testContext.infos.size(), 2);
     QCOMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input1"));
-    foreach (const InputMethodInfo &info, testContext.infos) {
+    for (const InputMethodInfo &info : std::as_const(testContext.infos)) {
         QCOMPARE(info.cursorPosition, 0);
         QCOMPARE(info.anchorPosition, 0);
         QCOMPARE(info.surroundingText, QStringLiteral(""));
@@ -2174,7 +2192,7 @@ void tst_QWebEngineView::inputContextQueryInput()
         QApplication::sendEvent(view.focusProxy(), &event);
     }
     QTRY_COMPARE(testContext.infos.size(), 2);
-    foreach (const InputMethodInfo &info, testContext.infos) {
+    for (const InputMethodInfo &info : std::as_const(testContext.infos)) {
         QCOMPARE(info.cursorPosition, 0);
         QCOMPARE(info.anchorPosition, 0);
         QCOMPARE(info.surroundingText, QStringLiteral("QtWebEngine!"));
@@ -2560,10 +2578,13 @@ void tst_QWebEngineView::emptyInputMethodEvent()
 
 void tst_QWebEngineView::imeComposition()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
     QWebEngineView view;
     view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true);
     view.resize(640, 480);
     view.show();
+    view.window()->windowHandle()->requestActivate();
 
     QSignalSpy selectionChangedSpy(&view, SIGNAL(selectionChanged()));
     QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool)));
@@ -2571,7 +2592,7 @@ void tst_QWebEngineView::imeComposition()
                  "  <input type='text' id='input1' value='QtWebEngine inputMethod'/>"
                  "</body></html>");
     QVERIFY(loadFinishedSpy.wait());
-    QVERIFY(QTest::qWaitForWindowExposed(&view));
+    QVERIFY(QTest::qWaitForWindowActive(&view));
 
     evaluateJavaScriptSync(view.page(), "var inputEle = document.getElementById('input1'); inputEle.focus(); inputEle.select();");
     QTRY_COMPARE(selectionChangedSpy.size(), 1);
@@ -2779,17 +2800,20 @@ void tst_QWebEngineView::imeComposition()
 
 void tst_QWebEngineView::newlineInTextarea()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
     QWebEngineView view;
     view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true);
     view.resize(640, 480);
     view.show();
+    view.window()->windowHandle()->requestActivate();
 
     QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool)));
     view.page()->setHtml("<html><body>"
                          "  <textarea rows='5' cols='1' id='input1'></textarea>"
                          "</body></html>");
     QVERIFY(loadFinishedSpy.wait());
-    QVERIFY(QTest::qWaitForWindowExposed(&view));
+    QVERIFY(QTest::qWaitForWindowActive(&view));
 
     evaluateJavaScriptSync(view.page(), "var inputEle = document.getElementById('input1'); inputEle.focus(); inputEle.select();");
     QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString().isEmpty());
@@ -2947,8 +2971,8 @@ void tst_QWebEngineView::imeJSInputEvents()
 
     QTRY_COMPARE(logLines().size(), 4);
     QCOMPARE(logLines()[0], QStringLiteral("[object CompositionEvent] compositionstart "));
-    QCOMPARE(logLines()[1], QStringLiteral("[object InputEvent] beforeinput preedit"));
-    QCOMPARE(logLines()[2], QStringLiteral("[object CompositionEvent] compositionupdate preedit"));
+    QCOMPARE(logLines()[1], QStringLiteral("[object CompositionEvent] compositionupdate preedit"));
+    QCOMPARE(logLines()[2], QStringLiteral("[object InputEvent] beforeinput preedit"));
     QCOMPARE(logLines()[3], QStringLiteral("[object InputEvent] input preedit"));
 
     {
@@ -2960,8 +2984,8 @@ void tst_QWebEngineView::imeJSInputEvents()
     }
 
     QTRY_COMPARE(logLines().size(), 9);
-    QCOMPARE(logLines()[4], QStringLiteral("[object InputEvent] beforeinput commit"));
-    QCOMPARE(logLines()[5], QStringLiteral("[object CompositionEvent] compositionupdate commit"));
+    QCOMPARE(logLines()[4], QStringLiteral("[object CompositionEvent] compositionupdate commit"));
+    QCOMPARE(logLines()[5], QStringLiteral("[object InputEvent] beforeinput commit"));
     QCOMPARE(logLines()[6], QStringLiteral("[object TextEvent] textInput commit"));
     QCOMPARE(logLines()[7], QStringLiteral("[object InputEvent] input commit"));
     QCOMPARE(logLines()[8], QStringLiteral("[object CompositionEvent] compositionend commit"));
@@ -2979,8 +3003,8 @@ void tst_QWebEngineView::imeJSInputEvents()
 
     QTRY_COMPARE(logLines().size(), 4);
     QCOMPARE(logLines()[0], QStringLiteral("[object CompositionEvent] compositionstart "));
-    QCOMPARE(logLines()[1], QStringLiteral("[object InputEvent] beforeinput preedit"));
-    QCOMPARE(logLines()[2], QStringLiteral("[object CompositionEvent] compositionupdate preedit"));
+    QCOMPARE(logLines()[1], QStringLiteral("[object CompositionEvent] compositionupdate preedit"));
+    QCOMPARE(logLines()[2], QStringLiteral("[object InputEvent] beforeinput preedit"));
     QCOMPARE(logLines()[3], QStringLiteral("[object InputEvent] input preedit"));
 
     {
@@ -2991,8 +3015,8 @@ void tst_QWebEngineView::imeJSInputEvents()
     }
 
     QTRY_COMPARE(logLines().size(), 9);
-    QCOMPARE(logLines()[4], QStringLiteral("[object InputEvent] beforeinput "));
-    QCOMPARE(logLines()[5], QStringLiteral("[object CompositionEvent] compositionupdate "));
+    QCOMPARE(logLines()[4], QStringLiteral("[object CompositionEvent] compositionupdate "));
+    QCOMPARE(logLines()[5], QStringLiteral("[object InputEvent] beforeinput "));
     QCOMPARE(logLines()[6], QStringLiteral("[object TextEvent] textInput "));
     QCOMPARE(logLines()[7], QStringLiteral("[object InputEvent] input null"));
     QCOMPARE(logLines()[8], QStringLiteral("[object CompositionEvent] compositionend "));
@@ -3026,18 +3050,21 @@ void tst_QWebEngineView::imeCompositionQueryEvent_data()
 
 void tst_QWebEngineView::imeCompositionQueryEvent()
 {
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
     QWebEngineView view;
     view.resize(640, 480);
     view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true);
 
     view.show();
+    view.window()->windowHandle()->requestActivate();
 
     QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool)));
     view.setHtml("<html><body>"
                  "  <input type='text' id='input1' />"
                  "</body></html>");
     QVERIFY(loadFinishedSpy.wait());
-    QVERIFY(QTest::qWaitForWindowExposed(&view));
+    QVERIFY(QTest::qWaitForWindowActive(&view));
 
     evaluateJavaScriptSync(view.page(), "document.getElementById('input1').focus()");
     QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input1"));
@@ -3894,10 +3921,13 @@ void tst_QWebEngineView::datalist()
     QTest::keyClick(view.windowHandle(), Qt::Key_Escape);
     QTRY_VERIFY(!listView());
 
-    // Key Down should open the popup and select the first suggestion.
+    // The first Key Down opens the popup.
     QTest::keyClick(view.windowHandle(), Qt::Key_Down);
     QTRY_VERIFY(listView());
-    QCOMPARE(listView()->currentIndex().row(), 0);
+
+    // The second Key Down selects the first suggestion.
+    QTest::keyClick(view.windowHandle(), Qt::Key_Down);
+    QTRY_COMPARE(listView()->currentIndex().row(), 0);
 
     // Test keyboard navigation in list.
     QTest::keyClick(view.windowHandle(), Qt::Key_Up);
@@ -4003,5 +4033,87 @@ void tst_QWebEngineView::longKeyEventText()
     QTRY_VERIFY(consoleMessageSpy.size());
 }
 
+void tst_QWebEngineView::deferredDelete()
+{
+    // TODO: Remove this workaround when temporary qt_desktopWidget is removed from
+    //       qapplication.cpp.
+    const size_t desktopWidget = QApplication::allWidgets().size();
+    QVERIFY(desktopWidget <= 1);
+
+    {
+        QWebEngineView view;
+        QSignalSpy loadFinishedSpy(view.page(), &QWebEnginePage::loadFinished);
+        view.load(QUrl("chrome://qt"));
+        view.show();
+        QTRY_VERIFY(loadFinishedSpy.size());
+        // QWebEngineView and WebEngineQuickWidget
+        QCOMPARE(QApplication::allWidgets().size(), desktopWidget + 2);
+    }
+
+    QCOMPARE(QApplication::allWidgets().size(), desktopWidget);
+}
+
+// QTBUG-111927
+void tst_QWebEngineView::setCursorOnEmbeddedView()
+{
+    SKIP_IF_NO_WINDOW_ACTIVATION();
+
+    if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
+        QSKIP("Wayland: Can't manipulate the mouse cursor in auto test.");
+
+    const QString html(QStringLiteral("<html><body"
+                                      " style=\"cursor:pointer;"
+                                      "        background-color:green;"
+                                      "        text-align:center;\">"
+                                      "Pointer"
+                                      "</body>"
+                                      "</html>"));
+    QWidget parentWidget;
+    QWebEngineView view(&parentWidget);
+    PageWithPaintListeners page;
+    view.setPage(&page);
+
+    // Move the view to it's parent rightBottom corner
+    parentWidget.resize(600, 600);
+    view.resize(150, 150);
+    view.move(450, 450);
+
+    QSignalSpy firstPaintSpy(&page, &PageWithPaintListeners::largestContentfulPaint);
+    view.setHtml(html);
+    parentWidget.show();
+    view.show();
+    parentWidget.window()->windowHandle()->requestActivate();
+
+    QVERIFY(QTest::qWaitForWindowActive(&parentWidget));
+
+    QTRY_VERIFY(firstPaintSpy.size());
+
+    const QPoint step = QPoint(25, 25);
+    QPoint cursorPos = view.pos() - step;
+
+    // Single QTest::mouseMove may not move the cursor on macOS.
+    for (int i = 0; i < 5; i++) {
+        QTest::mouseMove(&parentWidget, cursorPos);
+        cursorPos += step;
+    }
+
+    QQuickWidget *webEngineQuickWidget = qobject_cast<QQuickWidget *>(view.focusProxy());
+    QVERIFY(webEngineQuickWidget);
+    QTRY_COMPARE(webEngineQuickWidget->hasFocus(), true);
+
+    QQuickItem *root = webEngineQuickWidget->rootObject();
+    // The root item should not has focus, otherwise it would handle mouse events
+    // instead of the RenderWidgetHostViewQtDelegateItem.
+    QVERIFY(!root->hasFocus());
+
+    QCOMPARE(root->childItems().size(), 1);
+    QQuickItem *renderWidgetHostViewQtDelegateItem = root->childItems().at(0);
+    QVERIFY(renderWidgetHostViewQtDelegateItem);
+    QTRY_COMPARE(renderWidgetHostViewQtDelegateItem->hasFocus(), true);
+
+    QTRY_COMPARE(renderWidgetHostViewQtDelegateItem->cursor().shape(), Qt::PointingHandCursor);
+    QTRY_COMPARE(view.cursor().shape(), Qt::PointingHandCursor);
+}
+
 QTEST_MAIN(tst_QWebEngineView)
 #include "tst_qwebengineview.moc"
diff --git a/tests/auto/widgets/schemes/tst_schemes.cpp b/tests/auto/widgets/schemes/tst_schemes.cpp
index 188c112e415..19bab06dafe 100644
--- a/tests/auto/widgets/schemes/tst_schemes.cpp
+++ b/tests/auto/widgets/schemes/tst_schemes.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QtTest/QtTest>
 
diff --git a/tests/auto/widgets/shutdown/tst_shutdown.cpp b/tests/auto/widgets/shutdown/tst_shutdown.cpp
index c2b31bb808f..88cbc6aa734 100644
--- a/tests/auto/widgets/shutdown/tst_shutdown.cpp
+++ b/tests/auto/widgets/shutdown/tst_shutdown.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 
 #include <QtTest/QtTest>
diff --git a/tests/auto/widgets/spellchecking/dict/en-US.dic b/tests/auto/widgets/spellchecking/dict/en-US.dic
index 63e9164ccd5..93c7198a385 100644
--- a/tests/auto/widgets/spellchecking/dict/en-US.dic
+++ b/tests/auto/widgets/spellchecking/dict/en-US.dic
@@ -10,3 +10,4 @@ she/Q
 they/Q
 we/Q
 you/Q
+very-long-word-to-test-old-fixed-size-buffer-limit-QTBUG-132564-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Q
diff --git a/tests/auto/widgets/spellchecking/tst_spellchecking.cpp b/tests/auto/widgets/spellchecking/tst_spellchecking.cpp
index c643a56bacb..f5525c9f151 100644
--- a/tests/auto/widgets/spellchecking/tst_spellchecking.cpp
+++ b/tests/auto/widgets/spellchecking/tst_spellchecking.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <util.h>
 #include <QtTest/QtTest>
diff --git a/tests/auto/widgets/touchinput/tst_touchinput.cpp b/tests/auto/widgets/touchinput/tst_touchinput.cpp
index 42178558ccc..abc56a8e6cc 100644
--- a/tests/auto/widgets/touchinput/tst_touchinput.cpp
+++ b/tests/auto/widgets/touchinput/tst_touchinput.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <util.h>
 
diff --git a/tests/manual/quick/geopermission/CMakeLists.txt b/tests/manual/quick/geopermission/CMakeLists.txt
index 088f248e1ac..e6f91356074 100644
--- a/tests/manual/quick/geopermission/CMakeLists.txt
+++ b/tests/manual/quick/geopermission/CMakeLists.txt
@@ -18,13 +18,6 @@ qt_internal_add_manual_test(tst_geopermission
         Qt::WebEngineQuick
 )
 
-if(WIN32)
-    set_property(
-        TARGET tst_geopermission
-        APPEND PROPERTY
-            SOURCES tst_geopermission.exe.manifest)
-endif()
-
 set_target_properties(tst_geopermission PROPERTIES
     WIN32_EXECUTABLE TRUE
     MACOSX_BUNDLE TRUE
@@ -56,8 +49,13 @@ if (APPLE)
 
     if (NOT CMAKE_GENERATOR STREQUAL "Xcode")
         # Need to sign application for location permissions to work
+        if(QT_FEATURE_debug_and_release)
+            set(exe_path "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/")
+        else()
+            unset(exe_path)
+        endif()
         add_custom_command(TARGET tst_geopermission
-            POST_BUILD COMMAND codesign -s - tst_geopermission.app)
+            POST_BUILD COMMAND codesign --force -s - ${exe_path}tst_geopermission.app)
     endif()
 endif()
 
diff --git a/tests/manual/quick/geopermission/main.cpp b/tests/manual/quick/geopermission/main.cpp
index e0ff6f3e7f3..b0e9a7eda9d 100644
--- a/tests/manual/quick/geopermission/main.cpp
+++ b/tests/manual/quick/geopermission/main.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QtWebEngineQuick/qtwebenginequickglobal.h>
 
diff --git a/tests/manual/quick/geopermission/tst_geopermission.qml b/tests/manual/quick/geopermission/tst_geopermission.qml
index 36317c176ae..ff9c79f1d06 100644
--- a/tests/manual/quick/geopermission/tst_geopermission.qml
+++ b/tests/manual/quick/geopermission/tst_geopermission.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtTest
@@ -13,14 +13,14 @@ WebEngineView {
     property bool deniedGeolocation: false
     property bool geoPermissionRequested: false
 
-    onFeaturePermissionRequested: function(securityOrigin, feature) {
-        if (feature === WebEngineView.Geolocation) {
+    onPermissionRequested: function(perm) {
+        if (perm.permissionType === WebEnginePermission.PermissionType.Geolocation) {
             geoPermissionRequested = true
             if (deniedGeolocation) {
-                webEngineView.grantFeaturePermission(securityOrigin, feature, false)
+                perm.deny()
             }
             else {
-                webEngineView.grantFeaturePermission(securityOrigin, feature, true)
+                perm.grant()
             }
         }
     }
diff --git a/tests/manual/quick/pdf/bookmarks-list.qml b/tests/manual/quick/pdf/bookmarks-list.qml
index 2be0d68484b..e0af43054e0 100644
--- a/tests/manual/quick/pdf/bookmarks-list.qml
+++ b/tests/manual/quick/pdf/bookmarks-list.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 import QtQuick
 import QtQuick.Controls
 import QtQuick.Dialogs
diff --git a/tests/manual/quick/pdf/bookmarks.qml b/tests/manual/quick/pdf/bookmarks.qml
index e12629b31ca..86ae4e34761 100644
--- a/tests/manual/quick/pdf/bookmarks.qml
+++ b/tests/manual/quick/pdf/bookmarks.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 import QtQuick
 import QtQuick.Controls
 import QtQuick.Dialogs
diff --git a/tests/manual/quick/pdf/gridview.qml b/tests/manual/quick/pdf/gridview.qml
index 773e72388db..69b85470d42 100644
--- a/tests/manual/quick/pdf/gridview.qml
+++ b/tests/manual/quick/pdf/gridview.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 import QtQuick
 import QtQuick.Pdf
 
diff --git a/tests/manual/quick/pdf/listview.qml b/tests/manual/quick/pdf/listview.qml
index d01be9e86e3..1c997fdce7e 100644
--- a/tests/manual/quick/pdf/listview.qml
+++ b/tests/manual/quick/pdf/listview.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 import QtQuick
 import QtQuick.Pdf
 
diff --git a/tests/manual/quick/pdf/multipleDocuments.qml b/tests/manual/quick/pdf/multipleDocuments.qml
index 055808ab60d..7826c9a8f84 100644
--- a/tests/manual/quick/pdf/multipleDocuments.qml
+++ b/tests/manual/quick/pdf/multipleDocuments.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 import QtQuick
 import QtQuick.Controls
 import QtQuick.Dialogs
diff --git a/tests/manual/quick/pdf/pdfPageView.qml b/tests/manual/quick/pdf/pdfPageView.qml
index 22c0d5ac238..edf3aa05bf9 100644
--- a/tests/manual/quick/pdf/pdfPageView.qml
+++ b/tests/manual/quick/pdf/pdfPageView.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 import QtQuick
 import QtQuick.Controls
 import QtQuick.Dialogs
diff --git a/tests/manual/quick/pdf/pessimizedListView.qml b/tests/manual/quick/pdf/pessimizedListView.qml
index 1b514668e62..6e1083fd3b2 100644
--- a/tests/manual/quick/pdf/pessimizedListView.qml
+++ b/tests/manual/quick/pdf/pessimizedListView.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 import QtQuick
 import QtQuick.Controls
 import QtQuick.Dialogs
diff --git a/tests/manual/quick/pdf/simplest.qml b/tests/manual/quick/pdf/simplest.qml
index 3f39bb21341..0736f38aee6 100644
--- a/tests/manual/quick/pdf/simplest.qml
+++ b/tests/manual/quick/pdf/simplest.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 import QtQuick
 
 Image {
diff --git a/tests/manual/quick/pdf/underscoredLinks.qml b/tests/manual/quick/pdf/underscoredLinks.qml
index 514008ca26b..f23d9a5a1fc 100644
--- a/tests/manual/quick/pdf/underscoredLinks.qml
+++ b/tests/manual/quick/pdf/underscoredLinks.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 import QtQuick
 import QtQuick.Controls
 import QtQuick.Dialogs
diff --git a/tests/manual/quick/pdf/withdoc.qml b/tests/manual/quick/pdf/withdoc.qml
index 0a2a8663087..bcd1a9b5843 100644
--- a/tests/manual/quick/pdf/withdoc.qml
+++ b/tests/manual/quick/pdf/withdoc.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 import QtQuick
 import QtQuick.Controls
 import QtQuick.Dialogs
@@ -125,6 +125,10 @@ Window {
                             image.sourceSize.height = image.implicitHeight / image.zoomFactor
                         }
                     }
+                    Shortcut {
+                        sequence: StandardKey.SelectAll
+                        onActivated: selection.selectAll()
+                    }
                     Shortcut {
                         sequence: "Ctrl+0"
                         onActivated: image.sourceSize = undefined
diff --git a/tests/manual/quick/touchbrowser/AddressBar.qml b/tests/manual/quick/touchbrowser/AddressBar.qml
index 42188c94eac..36a83d87289 100644
--- a/tests/manual/quick/touchbrowser/AddressBar.qml
+++ b/tests/manual/quick/touchbrowser/AddressBar.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtQuick.Controls
diff --git a/tests/manual/quick/touchbrowser/MockTouchPoint.qml b/tests/manual/quick/touchbrowser/MockTouchPoint.qml
index 895e12e70a6..8583d934fc3 100644
--- a/tests/manual/quick/touchbrowser/MockTouchPoint.qml
+++ b/tests/manual/quick/touchbrowser/MockTouchPoint.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 import QtQuick
 
 Item {
diff --git a/tests/manual/quick/touchbrowser/main.cpp b/tests/manual/quick/touchbrowser/main.cpp
index 1f4d7d86900..7db3f40a169 100644
--- a/tests/manual/quick/touchbrowser/main.cpp
+++ b/tests/manual/quick/touchbrowser/main.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include "touchmockingapplication.h"
 #include "utils.h"
diff --git a/tests/manual/quick/touchbrowser/main.qml b/tests/manual/quick/touchbrowser/main.qml
index 83ede7d75b9..ed60b36e53e 100644
--- a/tests/manual/quick/touchbrowser/main.qml
+++ b/tests/manual/quick/touchbrowser/main.qml
@@ -1,5 +1,5 @@
 // Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 import QtQuick
 import QtQuick.Layouts
diff --git a/tests/manual/touchmocking/touchmockingapplication.cpp b/tests/manual/touchmocking/touchmockingapplication.cpp
index feedae5cd69..d1536466296 100644
--- a/tests/manual/touchmocking/touchmockingapplication.cpp
+++ b/tests/manual/touchmocking/touchmockingapplication.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include "touchmockingapplication.h"
 
diff --git a/tests/manual/touchmocking/touchmockingapplication.h b/tests/manual/touchmocking/touchmockingapplication.h
index 4f6e744fc65..e42159451af 100644
--- a/tests/manual/touchmocking/touchmockingapplication.h
+++ b/tests/manual/touchmocking/touchmockingapplication.h
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #ifndef TOUCHMOCKINGAPPLICATION_H
 #define TOUCHMOCKINGAPPLICATION_H
diff --git a/tests/manual/touchmocking/utils.h b/tests/manual/touchmocking/utils.h
index 12d493d3f03..2e6fb1b0571 100644
--- a/tests/manual/touchmocking/utils.h
+++ b/tests/manual/touchmocking/utils.h
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #ifndef UTILS_H
 #define UTILS_H
diff --git a/tests/manual/widgets/geolocation/CMakeLists.txt b/tests/manual/widgets/geolocation/CMakeLists.txt
index 2ca8c2f524b..25704bfdff2 100644
--- a/tests/manual/widgets/geolocation/CMakeLists.txt
+++ b/tests/manual/widgets/geolocation/CMakeLists.txt
@@ -49,7 +49,12 @@ if (APPLE)
 
     if (NOT CMAKE_GENERATOR STREQUAL "Xcode")
         # Need to sign application for location permissions to work
+        if(QT_FEATURE_debug_and_release)
+            set(exe_path "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/")
+        else()
+            unset(exe_path)
+        endif()
         add_custom_command(TARGET geolocation
-            POST_BUILD COMMAND codesign -s - geolocation.app)
+            POST_BUILD COMMAND codesign --force -s - ${exe_path}geolocation.app)
     endif()
 endif()
diff --git a/tests/manual/widgets/geolocation/main.cpp b/tests/manual/widgets/geolocation/main.cpp
index f33cf5798b7..9e471650b01 100644
--- a/tests/manual/widgets/geolocation/main.cpp
+++ b/tests/manual/widgets/geolocation/main.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QApplication>
 #include <QFormLayout>
@@ -16,18 +16,15 @@ class GeoPermissionWebView : public QWebEngineView {
   Q_OBJECT
 
 public slots:
-    void handleFeaturePermissionRequested(const QUrl &securityOrigin,
-                                               QWebEnginePage::Feature feature)
+    void handlePermissionRequested(QWebEnginePermission permission)
     {
         qWarning("Feature Permission");
         QString title = tr("Permission Request");
         QString question = QLatin1String("Allow access to geolocation?");
         if (!question.isEmpty() && QMessageBox::question(window(), title, question) == QMessageBox::Yes)
-            page()->setFeaturePermission(securityOrigin, feature,
-                                         QWebEnginePage::PermissionGrantedByUser);
+            permission.grant();
         else
-            page()->setFeaturePermission(securityOrigin, feature,
-                                         QWebEnginePage::PermissionDeniedByUser);
+            permission.deny();
     }
 
 };
@@ -38,8 +35,8 @@ int main(int argc, char *argv[])
     QMainWindow w;
     GeoPermissionWebView webview;
     QWebEnginePage page;
-    QObject::connect(&page, &QWebEnginePage::featurePermissionRequested, &webview,
-            &GeoPermissionWebView::handleFeaturePermissionRequested);
+    QObject::connect(&page, &QWebEnginePage::permissionRequested, &webview,
+            &GeoPermissionWebView::handlePermissionRequested);
     webview.setPage(&page);
     page.load(QUrl("qrc:/geolocation.html"));
     w.setCentralWidget(&webview);
diff --git a/tests/manual/widgets/inputmethods/colorpicker.cpp b/tests/manual/widgets/inputmethods/colorpicker.cpp
index cc0840bcd6c..6ea8e808607 100644
--- a/tests/manual/widgets/inputmethods/colorpicker.cpp
+++ b/tests/manual/widgets/inputmethods/colorpicker.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include "colorpicker.h"
 
diff --git a/tests/manual/widgets/inputmethods/colorpicker.h b/tests/manual/widgets/inputmethods/colorpicker.h
index 0b6b3257a5c..719aa93eb20 100644
--- a/tests/manual/widgets/inputmethods/colorpicker.h
+++ b/tests/manual/widgets/inputmethods/colorpicker.h
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #ifndef COLORPICKER_H
 #define COLORPICKER_H
diff --git a/tests/manual/widgets/inputmethods/controlview.cpp b/tests/manual/widgets/inputmethods/controlview.cpp
index 86bf8cca967..85d7cfa0fe5 100644
--- a/tests/manual/widgets/inputmethods/controlview.cpp
+++ b/tests/manual/widgets/inputmethods/controlview.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include "controlview.h"
 
diff --git a/tests/manual/widgets/inputmethods/controlview.h b/tests/manual/widgets/inputmethods/controlview.h
index caa08593ffd..c686adbec57 100644
--- a/tests/manual/widgets/inputmethods/controlview.h
+++ b/tests/manual/widgets/inputmethods/controlview.h
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #ifndef CONTROLVIEW_H
 #define CONTROLVIEW_H
diff --git a/tests/manual/widgets/inputmethods/main.cpp b/tests/manual/widgets/inputmethods/main.cpp
index 2378e95aea7..4bba9f78208 100644
--- a/tests/manual/widgets/inputmethods/main.cpp
+++ b/tests/manual/widgets/inputmethods/main.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QApplication>
 #include <QFormLayout>
diff --git a/tests/manual/widgets/inputmethods/referenceview.cpp b/tests/manual/widgets/inputmethods/referenceview.cpp
index 27e784fbcd2..24957eaa954 100644
--- a/tests/manual/widgets/inputmethods/referenceview.cpp
+++ b/tests/manual/widgets/inputmethods/referenceview.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include "referenceview.h"
 
diff --git a/tests/manual/widgets/inputmethods/referenceview.h b/tests/manual/widgets/inputmethods/referenceview.h
index d943a93d01c..62c43faeb5e 100644
--- a/tests/manual/widgets/inputmethods/referenceview.h
+++ b/tests/manual/widgets/inputmethods/referenceview.h
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #ifndef REFERENCEVIEW_H
 #define REFERENCEVIEW_H
diff --git a/tests/manual/widgets/inputmethods/testview.cpp b/tests/manual/widgets/inputmethods/testview.cpp
index d57b22cc59c..e0e84d72b2a 100644
--- a/tests/manual/widgets/inputmethods/testview.cpp
+++ b/tests/manual/widgets/inputmethods/testview.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include "testview.h"
 
diff --git a/tests/manual/widgets/inputmethods/testview.h b/tests/manual/widgets/inputmethods/testview.h
index b99e60d75f3..5ba894bf3d9 100644
--- a/tests/manual/widgets/inputmethods/testview.h
+++ b/tests/manual/widgets/inputmethods/testview.h
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #ifndef TESTVIEW_H
 #define TESTVIEW_H
diff --git a/tests/manual/widgets/inputmethods/webview.cpp b/tests/manual/widgets/inputmethods/webview.cpp
index 915d73a7f8f..febd05049cf 100644
--- a/tests/manual/widgets/inputmethods/webview.cpp
+++ b/tests/manual/widgets/inputmethods/webview.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include "webview.h"
 #include <QWebEngineSettings>
diff --git a/tests/manual/widgets/inputmethods/webview.h b/tests/manual/widgets/inputmethods/webview.h
index a46dcb2f625..ec5a295d1ed 100644
--- a/tests/manual/widgets/inputmethods/webview.h
+++ b/tests/manual/widgets/inputmethods/webview.h
@@ -1,5 +1,5 @@
 // Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 #ifndef WEBVIEW_H
 #define WEBVIEW_H
 
diff --git a/tests/manual/widgets/touchbrowser/main.cpp b/tests/manual/widgets/touchbrowser/main.cpp
index 18baf79e89f..0aa3e791797 100644
--- a/tests/manual/widgets/touchbrowser/main.cpp
+++ b/tests/manual/widgets/touchbrowser/main.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include "touchmockingapplication.h"
 #include "utils.h"
diff --git a/tests/manual/widgets/webgl/main.cpp b/tests/manual/widgets/webgl/main.cpp
index 7037c34dba1..cca1dbb160a 100644
--- a/tests/manual/widgets/webgl/main.cpp
+++ b/tests/manual/widgets/webgl/main.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QtCore/QDebug>
 #include <QtCore/QLoggingCategory>
@@ -24,7 +24,7 @@ class MainWindow : public QMainWindow
     Q_OBJECT
 public:
     explicit MainWindow(QWidget *parent = nullptr);
-    QSize sizeHint() const;
+    QSize sizeHint() const override;
 
 private:
     QWebEngineView *view = nullptr;
@@ -103,9 +103,6 @@ int main(int argc, char *argv[])
     const QString gles3 = QStringLiteral("gles3"); // ANGLE on Windows.
     const QString softwareGL = QStringLiteral("software");
 
-    QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
-    QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
-
     QString glType = qEnvironmentVariable("QTWEBENGINE_GL_TYPE");
     if (glType.isEmpty()) {
         if (isWindows())
diff --git a/tests/manual/widgets/webrtc/main.cpp b/tests/manual/widgets/webrtc/main.cpp
index 328e4ae3674..e788f3a2936 100644
--- a/tests/manual/widgets/webrtc/main.cpp
+++ b/tests/manual/widgets/webrtc/main.cpp
@@ -1,5 +1,5 @@
 // Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
 
 #include <QApplication>
 #include <QByteArray>
@@ -8,6 +8,7 @@
 #include <QHttpServer>
 #include <QListView>
 #include <QMessageBox>
+#include <QTcpServer>
 #include <QWebEnginePage>
 #include <QWebEngineProfile>
 #include <QWebEngineSettings>
@@ -28,59 +29,65 @@ class Page : public QWebEnginePage
 public:
     Page(QWebEngineProfile *profile, QObject *parent = nullptr);
 private slots:
-    void handlePermissionRequest(const QUrl &origin, Feature feature);
+    void handlePermissionRequest(QWebEnginePermission permission);
     void handleDesktopMediaRequest(const QWebEngineDesktopMediaRequest &request);
 };
 
 Page::Page(QWebEngineProfile *profile, QObject *parent) : QWebEnginePage(profile, parent)
 {
     settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true);
-    connect(this, &QWebEnginePage::featurePermissionRequested, this,
+    connect(this, &QWebEnginePage::permissionRequested, this,
             &Page::handlePermissionRequest);
     connect(this, &QWebEnginePage::desktopMediaRequested, this, &Page::handleDesktopMediaRequest);
 }
 
-void Page::handlePermissionRequest(const QUrl &origin, Feature feature)
+void Page::handlePermissionRequest(QWebEnginePermission permission)
 {
     if (QMessageBox::question(QApplication::activeWindow(), tr("Permission request"),
                               tr("allow access?"))
         == QMessageBox::Yes)
-        setFeaturePermission(origin, feature, PermissionGrantedByUser);
+        permission.grant();
     else
-        setFeaturePermission(origin, feature, PermissionDeniedByUser);
+        permission.deny();
 }
 
 void Page::handleDesktopMediaRequest(const QWebEngineDesktopMediaRequest &request)
 {
-        Ui::MediaPickerDialog mediaPickerDialog;
-        QDialog dialog;
-        dialog.setModal(true);
-        mediaPickerDialog.setupUi(&dialog);
-
-        auto *screensView = mediaPickerDialog.screensView;
-        auto *windowsView = mediaPickerDialog.windowsView;
-        auto *screensModel = request.screensModel();
-        auto *windowsModel = request.windowsModel();
-
-        screensView->setModel(screensModel);
-        windowsView->setModel(windowsModel);
-
-        if (dialog.exec() == QDialog::Accepted) {
-            if (mediaPickerDialog.tabWidget->currentIndex() == 0)
-                request.selectWindow(windowsView->selectionModel()->selectedIndexes().first());
-            else
-                request.selectScreen(screensView->selectionModel()->selectedIndexes().first());
+    Ui::MediaPickerDialog mediaPickerDialog;
+    QDialog dialog;
+    dialog.setModal(true);
+    mediaPickerDialog.setupUi(&dialog);
+
+    auto *screensView = mediaPickerDialog.screensView;
+    auto *windowsView = mediaPickerDialog.windowsView;
+    auto *screensModel = request.screensModel();
+    auto *windowsModel = request.windowsModel();
+
+    screensView->setModel(screensModel);
+    windowsView->setModel(windowsModel);
+
+    if (dialog.exec() == QDialog::Accepted) {
+        if (mediaPickerDialog.tabWidget->currentIndex() == 0) {
+            auto list = windowsView->selectionModel()->selectedIndexes();
+            if (!list.empty()) {
+                request.selectWindow(list.first());
+                return;
+            }
         } else {
-            request.cancel();
+            auto list = screensView->selectionModel()->selectedIndexes();
+            if (!list.empty()) {
+                request.selectScreen(list.first());
+                return;
+            }
         }
+    }
+    request.cancel();
 }
 
 int main(int argc, char *argv[])
 {
     QApplication app(argc, argv);
 
-    QHttpServer server;
-
     QFile file(":index.html");
 
     if (!file.open(QIODeviceBase::ReadOnly)) {
@@ -94,11 +101,12 @@ int main(int argc, char *argv[])
         return 0;
     }
 
-    server.route("/index.html", [data]() {
-        return data;
-    });
+    QHttpServer httpServer;
+    httpServer.route("/index.html", [data]() { return data; });
 
-    server.listen(QHostAddress::Any, 3000);
+    auto tcpServer = new QTcpServer(&httpServer);
+    tcpServer->listen(QHostAddress::Any, 3000);
+    httpServer.bind(tcpServer);
 
     QWebEngineView view;
     Page *page = new Page(QWebEngineProfile::defaultProfile(), &view);
diff --git a/tools/scripts/gn_find_mocables.py b/tools/scripts/gn_find_mocables.py
index 68f648889c2..31383e6ada9 100644
--- a/tools/scripts/gn_find_mocables.py
+++ b/tools/scripts/gn_find_mocables.py
@@ -28,7 +28,7 @@
             line = line.partition("//")[0]
         if re.match(".*Q_OBJECT", line):
             mocables.add(f)
-        im = re.search('#include "(moc_\w+.cpp)"', line)
+        im = re.search(r'#include "(moc_\w+.cpp)"', line)
         if im:
             includedMocs.add(im.group(1))
 
diff --git a/tools/scripts/init-repository.py b/tools/scripts/init-repository.py
index e6ec0c44053..0b973ebd6eb 100755
--- a/tools/scripts/init-repository.py
+++ b/tools/scripts/init-repository.py
@@ -16,7 +16,6 @@
 import version_resolver as resolver
 
 chromium_src = os.environ.get('CHROMIUM_SRC_DIR')
-ninja_src = os.path.join(qtwebengine_root, 'src/3rdparty_upstream/ninja')
 gn_src = os.path.join(qtwebengine_root, 'src/3rdparty_upstream/gn')
 use_external_chromium = False
 
@@ -39,7 +38,6 @@
         chromium_src = os.path.join(qtwebengine_root, 'src/3rdparty_upstream/chromium')
     if args.snapshot or not chromium_src:
         chromium_src = os.path.join(qtwebengine_root, 'src/3rdparty/chromium')
-        ninja_src = os.path.join(qtwebengine_root, 'src/3rdparty/ninja')
         gn_src = os.path.join(qtwebengine_root, 'src/3rdparty/gn')
         args.snapshot = True
     print('CHROMIUM_SRC_DIR not set, using Chromium in' + chromium_src)
@@ -63,27 +61,16 @@ def updateLastChange():
 
 def initUpstreamSubmodules():
     gn_url = 'https://gn.googlesource.com/gn'
-    ninja_url = 'https://github.com/martine/ninja.git'
     chromium_url = 'https://chromium.googlesource.com/chromium/src.git'
-    ninja_shasum = 'refs/tags/' + resolver.currentNinjaVersion()
     chromium_ref = 'refs/tags/' + resolver.currentVersion()
     os.chdir(qtwebengine_root)
 
     current_submodules = subprocess.check_output(['git', 'submodule']).decode()
     if not 'src/3rdparty_upstream/gn' in current_submodules:
         subprocess.call(['git', 'submodule', 'add', gn_url, 'src/3rdparty_upstream/gn'])
-    if not 'src/3rdparty_upstream/ninja' in current_submodules:
-        subprocess.call(['git', 'submodule', 'add', ninja_url, 'src/3rdparty_upstream/ninja'])
     if not use_external_chromium and not 'src/3rdparty_upstream/chromium' in current_submodules:
         subprocess.call(['git', 'submodule', 'add', chromium_url, 'src/3rdparty_upstream/chromium'])
 
-    ninjaSubmodule = GitSubmodule.Submodule()
-    ninjaSubmodule.path = 'src/3rdparty_upstream/ninja'
-    ninjaSubmodule.ref = ninja_shasum
-    ninjaSubmodule.url = ninja_url
-    ninjaSubmodule.os = 'all'
-    ninjaSubmodule.initialize()
-
     gnSubmodule = GitSubmodule.Submodule()
     gnSubmodule.path = 'src/3rdparty_upstream/gn'
     gnSubmodule.ref = 'master'
@@ -103,7 +90,6 @@ def initUpstreamSubmodules():
 
     # Unstage repositories so we do not accidentally commit them.
     subprocess.call(['git', 'reset', '-q', 'HEAD', 'src/3rdparty_upstream/gn'])
-    subprocess.call(['git', 'reset', '-q', 'HEAD', 'src/3rdparty_upstream/ninja'])
     subprocess.call(['git', 'reset', '-q', 'HEAD', 'src/3rdparty_upstream/chromium'])
 
 def initSnapshot():
diff --git a/tools/scripts/take_snapshot.py b/tools/scripts/take_snapshot.py
index e312dd64c7a..70ce7e5bf49 100755
--- a/tools/scripts/take_snapshot.py
+++ b/tools/scripts/take_snapshot.py
@@ -188,6 +188,10 @@ def isInChromiumBlacklist(file_path):
               and not file_path.startswith('third_party/node/node_modules/js-tokens/')
               and not file_path.startswith('third_party/node/node_modules/jsesc/')
               and not file_path.startswith('third_party/node/node_modules/jsonschema/')
+              and not file_path.startswith('third_party/node/node_modules/@lit/reactive-element/')
+              and not file_path.startswith('third_party/node/node_modules/lit-element/')
+              and not file_path.startswith('third_party/node/node_modules/lit-html/')
+              and not file_path.startswith('third_party/node/node_modules/lit/')
               and not file_path.startswith('third_party/node/node_modules/lodash.camelcase/')
               and not file_path.startswith('third_party/node/node_modules/lodash.sortby/')
               and not file_path.startswith('third_party/node/node_modules/minimatch/')
@@ -271,6 +275,7 @@ def isInChromiumBlacklist(file_path):
             or '/fuzzer/' in file_path
             or '/fuzzers/' in file_path
             or '/fuzzing/' in file_path
+            and not file_path.endswith('internals_fuzzing.idl')
         ))
         or ('/test' in file_path
          and ('/testdata/' in file_path
@@ -377,21 +382,6 @@ def exportGn():
             copyFile(f, os.path.join(third_party_gn, f))
     print("")
 
-def exportNinja():
-    third_party_upstream_ninja = os.path.join(third_party_upstream, 'ninja')
-    third_party_ninja = os.path.join(third_party, 'ninja')
-    os.makedirs(third_party_ninja);
-    print('exporting contents of:' + third_party_upstream_ninja)
-    os.chdir(third_party_upstream_ninja)
-    files = listFilesInCurrentRepository()
-    print('copying files to ' + third_party_ninja)
-    for i in range(len(files)):
-        printProgress(i+1, len(files))
-        f = files[i].decode()
-        if not isInGitBlacklist(f):
-            copyFile(f, os.path.join(third_party_ninja, f))
-    print("")
-
 def exportChromium():
     third_party_upstream_chromium = os.path.join(third_party_upstream, 'chromium')
     third_party_chromium = os.path.join(third_party, 'chromium')
@@ -456,7 +446,6 @@ def exportChromium():
 clearDirectory(third_party)
 
 exportGn()
-exportNinja()
 exportChromium()
 
 print('done.')
diff --git a/tools/scripts/version_resolver.py b/tools/scripts/version_resolver.py
index a29ee34e8f4..ad71f06e281 100644
--- a/tools/scripts/version_resolver.py
+++ b/tools/scripts/version_resolver.py
@@ -43,9 +43,8 @@ def get_recursedeps(self):
         return self.local_scope["recursedeps"]
 
 
-chromium_version = '118.0.5993.220'
-chromium_branch = '5993'
-ninja_version = 'v1.8.2'
+chromium_version = '122.0.6261.171'
+chromium_branch = '6261'
 
 json_url = 'http://omahaproxy.appspot.com/all.json'
 
@@ -72,9 +71,6 @@ def get_recursedeps(self):
 def currentVersion():
     return chromium_version
 
-def currentNinjaVersion():
-    return ninja_version
-
 def readReleaseChannels():
     response = urllib2.urlopen(json_url)
     raw_json = response.read().strip()
