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..eb2bd1e90dc 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: 136.0.7103.114 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) + +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..5788efea56a 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) @@ -872,7 +873,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 +887,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 +926,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 +946,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 +1145,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 +1165,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 +1195,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 +1208,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 +1227,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 +1301,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 +1352,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 +1439,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= + -DCMAKE_PREFIX_PATH:PATH= + -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..cceb2502a34 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,9 @@ machine_type: instructions: Build: + - 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 @@ - + viewer.qml resources/document-open.svg resources/edit-clear.svg 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 #include +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 ¤t, 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 ", + "Steven Garrity ", + "Lapo Calamandrei ", + "Ryan Collier ", + "Rodney Dawes ", + "Andreas Nilsson ", + "Tuomas Kuosmanen ", + "Garrett LeSage ", + "Jakub Steiner "] +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 ", "Steven Garrity ", "Lapo Calamandrei ", 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 #include -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 ", + "Steven Garrity ", + "Lapo Calamandrei ", + "Ryan Collier ", + "Rodney Dawes ", + "Andreas Nilsson ", + "Tuomas Kuosmanen ", + "Garrett LeSage ", + "Jakub Steiner "] +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 ", "Steven Garrity ", "Lapo Calamandrei ", 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}/$/") + 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 @@ + + + + + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + LSMinimumSystemVersion + ${CMAKE_OSX_DEPLOYMENT_TARGET} + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleDevelopmentRegion + English + NSSupportsAutomaticGraphicsSwitching + + NSLocationUsageDescription + Permission Browser would like to give web sites access to your location for demo purposes. + NSMicrophoneUsageDescription + Permission Browser would like to give web sites access to your computer's microphone for demo purposes. + NSCameraUsageDescription + Permission Browser would like to give web sites access to your computer's camera for demo purposes. + + 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 +#include +#include + +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 +#include +#include +#include +#include + +PermissionDialog::PermissionDialog(const QWebEngineProfile *profile, QWidget *parent) + : QDialog(parent), m_profile(profile) +{ + setupUi(this); + + auto metaEnum = QMetaEnum::fromType(); + 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()); +} + +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("Invalid"); + m_grantButton->setEnabled(false); + m_denyButton->setEnabled(false); + break; + case QWebEnginePermission::State::Ask: + m_stateLabel->setText("Waiting for response"); + break; + case QWebEnginePermission::State::Granted: + m_stateLabel->setText("Granted"); + break; + case QWebEnginePermission::State::Denied: + m_stateLabel->setText("Denied"); + break; + } + + m_typeLabel->setText(QMetaEnum::fromType().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(); + 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(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(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(); + if (policy == m_profile->persistentPermissionsPolicy()) + return; + + for (int i = m_layout->count() - 1; i >= 0; i--) { + PermissionWidget *widget = qobject_cast(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(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 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 +#include +#include +#include + +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 @@ + + + MainWindow + + + + 0 + 0 + 1600 + 900 + + + + Permission Browser + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + :3rdparty/go-previous.png:3rdparty/go-previous.png + + + + + + + + + + + :3rdparty/go-next.png:3rdparty/go-next.png + + + + + + + + + + + :3rdparty/view-refresh.png:3rdparty/view-refresh.png + + + + + + + + + + + + + + + + + 336 + 16777215 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Permissions: + + + + + + + Qt::Horizontal + + + + 87 + 20 + + + + + + + + New + + + + + + + Delete All + + + + + + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Persistence policy: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 0 + 25 + + + + 0 + + + QComboBox::AdjustToContents + + + + + + + + + + + 0 + 10 + + + + Qt::Horizontal + + + + + + + Stored permissions + + + + + + + + 320 + 0 + + + + + 320 + 16777215 + + + + QFrame::StyledPanel + + + 2 + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + true + + + + + + + + 0 + 10 + + + + Qt::Horizontal + + + + + + + Pending permission + + + + + + + + 0 + 104 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + 2 + + + + + + + + + + + + + + + + m_urlLineEdit + returnPressed() + m_refreshButton + click() + + + 509 + 28 + + + 1024 + 27 + + + + + 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 @@ + + + + + + + + + + + + + + + + + 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 @@ + + + resources/3rdparty/view-refresh.png + resources/3rdparty/go-next.png + resources/3rdparty/go-previous.png + + 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 @@ + + + PermissionDialog + + + + 0 + 0 + 400 + 110 + + + + Permission + + + + + + Origin + + + + + + + false + + + + + + + Permission type + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + true + + + Add + + + + + + + Cancel + + + + + + + + + + + + m_originLineEdit + m_addButton + m_cancelButton + + + + + m_cancelButton + clicked() + PermissionDialog + reject() + + + 349 + 89 + + + 334 + 67 + + + + + m_addButton + clicked() + PermissionDialog + accept() + + + 264 + 88 + + + 218 + 68 + + + + + 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 @@ + + + PermissionWidget + + + + 0 + 0 + 300 + 104 + + + + + 0 + 0 + + + + + 300 + 0 + + + + + 310 + 16777215 + + + + Form + + + + + + QLayout::SetDefaultConstraint + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 10 + + + + + Origin: + + + + + + + Empty + + + + + + + Type: + + + + + + + Empty + + + + + + + State: + + + + + + + Empty + + + + + + + + + + + Grant + + + + + + + Deny + + + + + + + Delete + + + + + + + + + + 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 ", + "Steven Garrity ", + "Lapo Calamandrei ", + "Ryan Collier ", + "Rodney Dawes ", + "Andreas Nilsson ", + "Tuomas Kuosmanen ", + "Garrett LeSage ", + "Jakub Steiner "] +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 ", + "Steven Garrity ", + "Lapo Calamandrei ", + "Ryan Collier ", + "Rodney Dawes ", + "Andreas Nilsson ", + "Tuomas Kuosmanen ", + "Garrett LeSage ", + "Jakub Steiner "] +} 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 @@ + + + + + Permissions example page + + + +

Permission Browser Example

+
+

Clipboard

+
+ + +
+
+ + +
+
+
+

Notifications

+
+ +
+
+
+

Local fonts

+ + +
+ + 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 ", + "Steven Garrity ", + "Lapo Calamandrei ", + "Ryan Collier ", + "Rodney Dawes ", + "Andreas Nilsson ", + "Tuomas Kuosmanen ", + "Garrett LeSage ", + "Jakub Steiner "] +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 ", "Steven Garrity ", "Lapo Calamandrei ", 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 #include #include +#include #include 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 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..5f5c3ab297f 160000 --- a/src/3rdparty +++ b/src/3rdparty @@ -1 +1 @@ -Subproject commit f3033f5aa9be1e98096c55972166d0be6cb64920 +Subproject commit 5f5c3ab297fe330253f0630b1e9b6f2112c5cb41 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= - -DCMAKE_PREFIX_PATH:PATH= - -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..487d0a004e2 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 @@ -209,6 +200,28 @@ foreach(arch ${archs}) 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 +356,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 +375,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 +417,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 +438,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 +521,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 +585,24 @@ target_include_directories(WebEngineCore PRIVATE ${buildDir}/$/${arch}/gen ${buildDir}/$/${arch}/gen/third_party/perfetto ${buildDir}/$/${arch}/gen/third_party/perfetto/build_config + ${buildDir}/$/${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 +643,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}/$/${arch}/gen + ${buildDir}/$/${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..3d2a153a39e 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" diff --git a/src/core/api/Qt6WebEngineCoreDeploySupport.cmake b/src/core/api/Qt6WebEngineCoreDeploySupport.cmake index e67eb212b3d..b6f8b146380 100644 --- a/src/core/api/Qt6WebEngineCoreDeploySupport.cmake +++ b/src/core/api/Qt6WebEngineCoreDeploySupport.cmake @@ -74,24 +74,44 @@ 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() + + # 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() @@ -117,8 +137,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 +184,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(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(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 #include #include +#include #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 QWebEngineCertificateError::certificateChain() const { - return d->certificateChain(); + if (Q_LIKELY(d)) + return d->certificateChain(); + return QList(); } 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 +#include 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 QWebEngineClientHints::fullVersionList() const +QVariantMap QWebEngineClientHints::fullVersionList() const { - QHash 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 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 &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 #include -#include +#include +#include +#include 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 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 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 &); - 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 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 #include 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 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 +#include +#include #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 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::children() const { + LOCK_ADAPTER(adapter, {}); QList 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::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 &callback) @@ -143,7 +166,8 @@ void QWebEngineFrame::runJavaScript(const QString &script, void QWebEngineFrame::runJavaScript(const QString &script, quint32 worldId, const std::function &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 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 &callback) +{ + LOCK_ADAPTER(adapter, ); + std::function wrappedCallback = [callback](QSharedPointer 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)> wrappedCallback; + if (!callback.isUndefined()) { + const QObject *holdingObject = adapter->adapterClient()->holdingQObject(); + wrappedCallback = [holdingObject, callback](QSharedPointer 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 #include #include +#include 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 children() const; - QUrl url() const; - QSizeF size() const; - - void runJavaScript(const QString &script, - const std::function &callback); - void runJavaScript(const QString &script, quint32 worldId, - const std::function &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 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 &callback); + Q_WEBENGINECORE_EXPORT void + runJavaScript(const QString &script, quint32 worldId, + const std::function &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 &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 adapter, quint64 id); - QtWebEngineCore::WebContentsAdapterClient *m_adapterClient; + QWeakPointer 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::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 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)> &&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(QSharedPointeraccept(networkAuth.user(), networkAuth.password()); + controller->credentials(networkAuth.user(), networkAuth.password()); + controller->accept(); } void QWebEnginePagePrivate::releaseProfile() @@ -587,50 +592,111 @@ void QWebEnginePagePrivate::showColorDialog(QSharedPointerpermissionRequested(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("") - + linkText + QStringLiteral(""); + const QString html = + "' + linkText + ""_L1; data->setHtml(html); data->setUrls(QList() << 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(""); + const QString html = ""_L1; data->setHtml(html); data->setUrls(QList() << 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("")); - else - data->setHtml(QStringLiteral("")); + 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 + + ">'; + data->setHtml(html); data->setUrls(QList() << 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 &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 &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 QWebEnginePage::findFrameByName(const QString &name) +std::optional 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 #include #include +#include +#include #include #include #include @@ -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 &resultCallback = std::function()); +#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 findFrameByName(const QString &name); + std::optional 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 &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)> &&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) 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 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 +#include +#include +#include + +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 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 +#include +#include + +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::ProfileAdapter *); + + QUrl origin; + QWebEnginePermission::PermissionType permissionType; + + QWeakPointer webContentsAdapter; + QPointer profileAdapter; +}; + +QT_END_NAMESPACE + +#endif // QWEBENGINEPERMISSION_P_H diff --git a/src/core/api/qwebengineprofile.cpp b/src/core/api/qwebengineprofile.cpp index a9477929ba9..45b5a349d26 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 +#include #include 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(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( - 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 QWebEngineProfile::listAllPermissions() const +{ + Q_D(const QWebEngineProfile); + if (persistentPermissionsPolicy() == PersistentPermissionsPolicy::AskEveryTime) + return QList(); + 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 QWebEngineProfile::listPermissionsForOrigin(const QUrl &securityOrigin) const +{ + Q_D(const QWebEngineProfile); + if (persistentPermissionsPolicy() == PersistentPermissionsPolicy::AskEveryTime) + return QList(); + 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 QWebEngineProfile::listPermissionsForPermissionType(QWebEnginePermission::PermissionType permissionType) const +{ + Q_D(const QWebEngineProfile); + if (persistentPermissionsPolicy() == PersistentPermissionsPolicy::AskEveryTime) + return QList(); + + if (permissionType == QWebEnginePermission::PermissionType::Unsupported) { + qWarning("Attempting to get permission list for an unsupported type. Returned list will be empty."); + return QList(); + } + + if (!QWebEnginePermission::isPersistent(permissionType)) { + qWarning() << "Attempting to get permission list for permission type" << permissionType << ". Returned list will be empty."; + return QList(); + } + + 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 +#include #include #include @@ -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 iconAvailableCallback) const; void requestIconForIconURL(const QUrl &url, int desiredSizeInPixel, std::function iconAvailableCallback) const; + QWebEnginePermission queryPermission(const QUrl &securityOrigin, QWebEnginePermission::PermissionType permissionType) const; + QList listAllPermissions() const; + QList listPermissionsForOrigin(const QUrl &securityOrigin) const; + QList 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 &) 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 #include +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(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(name.size()) }; + std::string_view namePiece{ name.data(), static_cast(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 loginDelegate); - void dialogFinished(bool accepted, const QString &user = QString(), const QString &password = QString()); + void dialogFinished(bool accepted); base::WeakPtr 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 &values, - const std::vector &labels) +void AutofillClientQt::UpdateAutofillPopupDataListValues( + base::span datalist) { - Q_UNUSED(labels); - - if (values.empty()) + if (datalist.empty()) HideAutofillPopup(autofill::PopupHidingReason::kNoSuggestions); } @@ -99,12 +97,6 @@ std::vector AutofillClientQt::GetPopupSuggestions() const return {}; } -void AutofillClientQt::UpdatePopup(const std::vector &, autofill::PopupType, autofill::AutofillSuggestionTriggerSource) -{ - // Called by password_manager component only. - NOTIMPLEMENTED(); -} - void AutofillClientQt::HideAutofillPopup(autofill::PopupHidingReason) { adapterClient()->hideAutofillPopup(); @@ -130,13 +122,6 @@ scoped_refptr AutofillClientQt::GetURLLoaderFac return nullptr; } -void AutofillClientQt::PropagateAutofillPredictionsDeprecated(autofill::AutofillDriver *, - const std::vector &) -{ - // 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 #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 @@ -46,20 +46,18 @@ class AutofillClientQt : public autofill::ContentAutofillClient, void ShowAutofillPopup(const autofill::AutofillClient::PopupOpenArgs &open_args, base::WeakPtr delegate) override; - void UpdateAutofillPopupDataListValues(const std::vector &values, - const std::vector &labels) override; + void UpdateAutofillPopupDataListValues( + base::span datalist) override; void PinPopupView() override; PopupOpenArgs GetReopenPopupArgs( autofill::AutofillSuggestionTriggerSource trigger_source) const override; std::vector GetPopupSuggestions() const override; - void UpdatePopup(const std::vector& suggestions, - autofill::PopupType popup_type, - autofill::AutofillSuggestionTriggerSource trigger_source) override; + void UpdatePopup(const std::vector &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 &) override; bool IsOffTheRecord() override; scoped_refptr 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 #include @@ -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 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 +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 source_url) +void ClipboardQt::WriteHTML(base::StringPiece markup, absl::optional 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("")); + markup_string += ""_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(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 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 filenames) getUncommittedData()->setUrls(urls); } -void ClipboardQt::WriteUnsanitizedHTML(base::StringPiece markup, absl::optional 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 GetStandardFormats(ui::ClipboardBuffer buffer, const ui::DataTransferEndpoint *data_dst) const override; - const ui::DataTransferEndpoint *GetSource(ui::ClipboardBuffer buffer) const override; + absl::optional GetSource(ui::ClipboardBuffer buffer) const override; + void ReadFilenames(ui::ClipboardBuffer buffer, const ui::DataTransferEndpoint *data_dst, std::vector *result) const override; @@ -48,7 +49,8 @@ class ClipboardQt : public ui::Clipboard std::unique_ptr data_src) override; void WriteText(base::StringPiece text) override; - void WriteHTML(base::StringPiece markup, absl::optional source_url) override; + void WriteHTML(base::StringPiece markup, absl::optional 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 data) override; void WriteSvg(base::StringPiece markup) override; void WriteFilenames(std::vector filenames) override; - void WriteUnsanitizedHTML(base::StringPiece markup, absl::optional source_url) override; base::flat_map> 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 diff --git a/src/core/common/extensions/extensions_api_provider_qt.cpp b/src/core/common/extensions/extensions_api_provider_qt.cpp index 1c8e89747a5..5a0f6d89169 100644 --- a/src/core/common/extensions/extensions_api_provider_qt.cpp +++ b/src/core/common/extensions/extensions_api_provider_qt.cpp @@ -57,7 +57,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..b5e67aabf4c 100644 --- a/src/core/common/extensions/extensions_api_provider_qt.h +++ b/src/core/common/extensions/extensions_api_provider_qt.h @@ -18,7 +18,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/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 #include #include #include 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::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..041835dd0c2 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; @@ -136,8 +138,8 @@ class Q_WEBENGINECORE_EXPORT Compositor virtual void releaseResources(); protected: - Compositor(Type type) : m_type(type) { } - virtual ~Compositor() { if (m_binding) unbind(); } + Compositor(Type type); + virtual ~Compositor(); private: template 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( - 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( + 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..aef0a8158f7 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 *e } } auto surface = m_backBuffer->beginWriteSkia(); + if (!surface) + return nullptr; + *end_semaphores = m_backBuffer->takeEndWriteSkiaSemaphores(); return surface; } @@ -209,10 +215,12 @@ NativeSkiaOutputDevice::Buffer::Buffer(NativeSkiaOutputDevice *parent) NativeSkiaOutputDevice::Buffer::~Buffer() { + DCHECK(!textureCleanupCallback); + 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 +230,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 +282,7 @@ bool NativeSkiaOutputDevice::Buffer::initialize() SkSurface *NativeSkiaOutputDevice::Buffer::beginWriteSkia() { DCHECK(!m_scopedSkiaWriteAccess); + DCHECK(!m_scopedOverlayReadAccess); DCHECK(!m_presentCount); DCHECK(m_endSemaphores.empty()); @@ -270,7 +294,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..d2101c0fbf2 100644 --- a/src/core/compositor/native_skia_output_device.h +++ b/src/core/compositor/native_skia_output_device.h @@ -164,18 +164,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 m_middleBuffer; std::unique_ptr m_backBuffer; viz::OutputSurfaceFrame m_frame; bool m_readyToUpdate = false; - bool m_requiresAlpha; scoped_refptr m_gpuTaskRunner; - - const raw_ptr m_factory; - const raw_ptr m_representationFactory; - const raw_ptr 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(gfx::BufferFormat::RGBA_8888)] = skColorType; capabilities_.sk_color_types[static_cast(gfx::BufferFormat::RGBX_8888)] = skColorType; @@ -37,43 +39,56 @@ QSGTexture *NativeSkiaOutputDeviceDirect3D11::texture(QQuickWindow *win, uint32_ absl::optional 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 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 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( + QSGRendererInterface *ri = win->rendererInterface(); + ID3D11Device *device = static_cast( 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 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(gfx::BufferFormat::RGBA_8888)] = skColorType; capabilities_.sk_color_types[static_cast(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..d418bfe5c97 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 @@ -8,8 +12,135 @@ #include #include +#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(nativePixmap.GetDmaBufPlaneSize(0)); + uint16_t width = base::checked_cast(nativePixmap.GetBufferSize().width()); + uint16_t height = base::checked_cast(nativePixmap.GetBufferSize().height()); + uint16_t stride = base::checked_cast(nativePixmap.GetDmaBufPitch(0)); + + auto *connection = x11::Connection::Get(); + const x11::Pixmap pixmapId = connection->GenerateId(); + 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(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) + NativeSkiaOutputDeviceOpenGL::NativeSkiaOutputDeviceOpenGL( scoped_refptr contextState, bool requiresAlpha, gpu::MemoryTracker *memoryTracker, viz::SkiaOutputSurfaceDependency *dependency, @@ -20,7 +151,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(gfx::BufferFormat::RGBA_8888)] = skColorType; capabilities_.sk_color_types[static_cast(gfx::BufferFormat::RGBX_8888)] = skColorType; capabilities_.sk_color_types[static_cast(gfx::BufferFormat::BGRA_8888)] = skColorType; @@ -40,10 +180,49 @@ QSGTexture *NativeSkiaOutputDeviceOpenGL::texture(QQuickWindow *win, uint32_t te #if defined(USE_OZONE) scoped_refptr nativePixmap = m_frontBuffer->nativePixmap(); +#if BUILDFLAG(ENABLE_VULKAN) + GrVkImageInfo vkImageInfo; + if (!nativePixmap) { + if (m_isNativeBufferSupported) { + qWarning("No native pixmap."); + return nullptr; + } + + sk_sp 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,12 +241,181 @@ 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(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); + + m_frontBuffer->textureCleanupCallback = [glFun, glTexture, display, glxPixmap, + pixmapId]() { + glFun->glDeleteTextures(1, &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(nativePixmap->GetDmaBufOffset(0)), + EGL_DMA_BUF_PLANE0_PITCH_EXT, nativePixmap->GetDmaBufPitch(0), + EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, static_cast(modifier & 0xffffffff), + EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, static_cast(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); + + m_frontBuffer->textureCleanupCallback = [glFun, eglFun, glTexture, eglImage]() { + glFun->glDeleteTextures(1, &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); + + m_frontBuffer->textureCleanupCallback = [glTexture, glMemoryObject]() { + QOpenGLContext *glContext = QOpenGLContext::currentContext(); + if (!glContext) + return; + auto glFun = glContext->functions(); + Q_ASSERT(glFun->glGetError() == GL_NO_ERROR); + + static PFNGLDELETEMEMORYOBJECTSEXTPROC glDeleteMemoryObjectsEXT = + (PFNGLDELETEMEMORYOBJECTSEXTPROC)glContext->getProcAddress( + "glDeleteMemoryObjectsEXT"); + + glDeleteMemoryObjectsEXT(1, &glMemoryObject); + glFun->glDeleteTextures(1, &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); @@ -78,7 +426,7 @@ QSGTexture *NativeSkiaOutputDeviceOpenGL::texture(QQuickWindow *win, uint32_t te auto glFun = glContext->functions(); glFun->glDeleteTextures(1, &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 #include @@ -11,17 +12,17 @@ #include #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(gfx::BufferFormat::RGBA_8888)] = skColorType; capabilities_.sk_color_types[static_cast(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 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 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(size().width()); - importedImageCreateInfo.extent.height = static_cast(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(size().width()), + .height = static_cast(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(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(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..c5cef0c7606 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", @@ -599,8 +611,10 @@ repack("qtwebengine_repack_resources") { "//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", 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 @@ -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 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()) { - SpellCheckHostChromeImpl::Create(render_process_host->GetID(), std::move(host_receiver)); + if (auto host_receiver = receiver.As()) { + SpellCheckInitializationHostImpl::Create(render_process_host->GetID(), + std::move(host_receiver)); return; } #endif @@ -434,6 +435,15 @@ void ContentBrowserClientQt::RegisterBrowserInterfaceBindersForFrame( mojo::BinderMapWithContext *map) { map->Add(base::BindRepeating(&BindNetworkHintsHandler)); +#if QT_CONFIG(webengine_spellchecker) + map->Add(base::BindRepeating( + [](content::RenderFrameHost *frame_host, + mojo::PendingReceiver receiver) { + SpellCheckHostChromeImpl::Create(frame_host->GetProcess()->GetID(), + std::move(receiver)); + })); +#endif + #if BUILDFLAG(ENABLE_EXTENSIONS) map->Add(base::BindRepeating(&BindMimeHandlerService)); map->Add(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( - base::BindRepeating(&extensions::EventRouter::BindForRenderer, render_process_host->GetID())); - associated_registry->AddInterface( - base::BindRepeating(&extensions::ExtensionsGuestView::CreateForComponents, render_process_host->GetID())); - associated_registry->AddInterface( - base::BindRepeating(&extensions::ExtensionsGuestView::CreateForExtensions, render_process_host->GetID())); +#if BUILDFLAG(ENABLE_EXTENSIONS) && BUILDFLAG(ENABLE_EXTENSIONS_LEGACY_IPC) + associated_registry->AddInterface(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( - base::BindRepeating( - [](content::RenderFrameHost *render_frame_host, - mojo::PendingAssociatedReceiver receiver) { - autofill::ContentAutofillDriverFactory::BindAutofillDriver(std::move(receiver), render_frame_host); - }, &rfh)); -#if BUILDFLAG(ENABLE_PDF) + associated_registry.AddInterface(base::BindRepeating( + [](content::RenderFrameHost *render_frame_host, + mojo::PendingAssociatedReceiver receiver) { + autofill::ContentAutofillDriverFactory::BindAutofillDriver(render_frame_host, + std::move(receiver)); + }, + &rfh)); +#if BUILDFLAG(ENABLE_PDF) && BUILDFLAG(ENABLE_EXTENSIONS) associated_registry.AddInterface( 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(base::BindRepeating( + &extensions::ExtensionsGuestView::CreateForComponents, rfh.GetGlobalId())); + associated_registry.AddInterface(base::BindRepeating( + &extensions::ExtensionsGuestView::CreateForExtensions, rfh.GetGlobalId())); +#endif } bool ContentBrowserClientQt::CanCreateWindow( @@ -764,7 +777,8 @@ std::vector> ContentBrowserClientQt::CreateURLLoaderThrottles( const network::ResourceRequest &request, content::BrowserContext *browser_context, const base::RepeatingCallback & /*wc_getter*/, - content::NavigationUIData * /*navigation_ui_data*/, int frame_tree_node_id) + content::NavigationUIData * /*navigation_ui_data*/, int frame_tree_node_id, + absl::optional navigation_id) { std::vector> result; result.push_back(std::make_unique( @@ -883,13 +897,10 @@ bool ContentBrowserClientQt::HasErrorPage(int httpStatusCode, content::WebConten } std::unique_ptr ContentBrowserClientQt::CreateLoginDelegate( - const net::AuthChallengeInfo &authInfo, - content::WebContents *web_contents, - const content::GlobalRequestID & /*request_id*/, - bool /*is_main_frame*/, - const GURL &url, - scoped_refptr /*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 /*response_headers*/, bool first_auth_attempt, LoginAuthRequiredCallback auth_required_callback) { auto loginDelegate = std::make_unique(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 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 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 response_headers, - bool first_auth_attempt, - LoginAuthRequiredCallback auth_required_callback) override; + std::unique_ptr + 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 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 *out_factory) override; - std::vector> CreateURLLoaderThrottles( - const network::ResourceRequest &request, content::BrowserContext *browser_context, - const base::RepeatingCallback &wc_getter, - content::NavigationUIData *navigation_ui_data, int frame_tree_node_id) override; + std::vector> + CreateURLLoaderThrottles(const network::ResourceRequest &request, + content::BrowserContext *browser_context, + const base::RepeatingCallback &wc_getter, + content::NavigationUIData *navigation_ui_data, int frame_tree_node_id, + absl::optional navigation_id) override; std::vector> 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 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& 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 GetPluginMimeTypesWithExternalHandlers(content::BrowserContext *browser_context) override; std::unique_ptr 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 #include #include +#include #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 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 { 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 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 BasicStartupComplete() override; @@ -31,7 +36,9 @@ class ContentMainDelegateQt : public content::ContentMainDelegate private: ContentClientQt m_contentClient; std::unique_ptr m_browserClient; +#if defined(USE_OZONE) || BUILDFLAG(IS_WIN) std::unique_ptr m_gpuClient; +#endif std::unique_ptr 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 + namespace QtWebEngineCore { namespace { DesktopMediaList::Type toMediaListType(DesktopMediaType type) @@ -39,15 +41,21 @@ std::unique_ptr createMediaList(DesktopMediaType type) case DesktopMediaList::Type::kScreen: { std::unique_ptr screenCapturer = webrtc::DesktopCapturer::CreateScreenCapturer(options); - std::unique_ptr capturer = - std::make_unique(std::move(screenCapturer)); + if (!screenCapturer) { + qWarning("Screen capturing is not available. Media list will be empty."); + return nullptr; + } + auto capturer = std::make_unique(std::move(screenCapturer)); return std::make_unique(listType, std::move(capturer)); } case DesktopMediaList::Type::kWindow: { std::unique_ptr windowCapturer = webrtc::DesktopCapturer::CreateWindowCapturer(options); - std::unique_ptr capturer = - std::make_unique(std::move(windowCapturer)); + if (!windowCapturer) { + qWarning("Window capturing is not available. Media list will be empty."); + return nullptr; + } + auto capturer = std::make_unique(std::move(windowCapturer)); return std::make_unique( 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 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 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 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 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 doneCallback; QScopedPointer screens; QScopedPointer 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 #include -#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 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 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-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 #include #include +#include #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 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(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(info.savePageFormat), + content::SavePackagePathPickedParams params; + params.file_path = toFilePath(path); + params.save_type = static_cast(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(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 +#include #include +#include + +#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 m_pendingDownloads; + std::map m_pendingSaves; base::WeakPtrFactory 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 +ExtensionsBrowserClientQt::GetControlledFrameEmbedderURLLoader( + int frame_tree_node_id, content::BrowserContext *browser_context) +{ + return mojo::PendingRemote(); +} } // 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 + GetControlledFrameEmbedderURLLoader(int frame_tree_node_id, + content::BrowserContext *browser_context) override; + private: // Support for extension APIs. std::unique_ptr 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& files, - void* params) +void FileEntryPickerQt::MultiFilesSelected(const std::vector &files, + void *params) { Q_UNUSED(params); - std::move(m_filesSelectedCallback).Run(files); - delete this; -} - -void FileEntryPickerQt::MultiFilesSelectedWithExtraInfo( - const std::vector &files, - void *params) -{ std::vector 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 &files, - void *params) override; - void MultiFilesSelectedWithExtraInfo( - const std::vector &files, - void *params) override; + void FileSelected(const ui::SelectedFileInfo &file, int index, void *params) override; + void MultiFilesSelected(const std::vector &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 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 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 #include @@ -17,6 +18,8 @@ #include #include +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 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 &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_qt.h b/src/core/file_system_access/file_system_access_permission_context_qt.h index 06fbfae3f0e..b569c81c020 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 @@ -53,6 +53,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/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 m_quickCallbacks; QMap> 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 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_store_data.cpp b/src/core/net/client_cert_store_data.cpp index 0de6885df85..5f9c8243af9 100644 --- a/src/core/net/client_cert_store_data.cpp +++ b/src/core/net/client_cert_store_data.cpp @@ -92,8 +92,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/cookie_monster_delegate_qt.cpp b/src/core/net/cookie_monster_delegate_qt.cpp index d107c520c41..da5f50c75cc 100644 --- a/src/core/net/cookie_monster_delegate_qt.cpp +++ b/src/core/net/cookie_monster_delegate_qt.cpp @@ -50,7 +50,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/custom_url_loader_factory.cpp b/src/core/net/custom_url_loader_factory.cpp index 954253b54de..b91a1289bda 100644 --- a/src/core/net/custom_url_loader_factory.cpp +++ b/src/core/net/custom_url_loader_factory.cpp @@ -28,6 +28,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 @@ -358,10 +359,9 @@ class CustomURLLoader : public network::mojom::URLLoader void notifySuccess() override { if (m_webContents) { - WebContentsAdapterClient *client = WebContentsViewQt::from(static_cast(m_webContents)->GetView())->client(); - QWebEngineLoadingInfo info(toQt(m_request.url), QWebEngineLoadingInfo::LoadSucceededStatus); - client->loadFinished(std::move(info)); - client->updateNavigationActions(); + WebContentsDelegateQt *delegate = + static_cast(m_webContents->GetDelegate()); + delegate->emitLoadSucceeded(toQt(m_request.url)); } } void notifyReadyRead() override @@ -534,4 +534,3 @@ mojo::PendingRemote CreateCustomURLLoaderFacto } } // namespace QtWebEngineCore - 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..319d3a56633 100644 --- a/src/core/net/plugin_response_interceptor_url_loader_throttle.cpp +++ b/src/core/net/plugin_response_interceptor_url_loader_throttle.cpp @@ -30,6 +30,9 @@ #include namespace { + +constexpr uint32_t kFullPageMimeHandlerDataPipeSize = 512U; + void ClearAllButFrameAncestors(network::mojom::URLResponseHead *response_head) { response_head->headers->RemoveHeader("Content-Security-Policy"); @@ -121,11 +124,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 dummy_new_loader; std::ignore = dummy_new_loader.InitWithNewPipeAndPassReceiver(); @@ -133,20 +132,20 @@ void PluginResponseInterceptorURLLoaderThrottle::WillProcessResponse(const GURL mojo::PendingReceiver 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(payload.size()); CHECK_EQ(MOJO_RESULT_OK, @@ -186,11 +185,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/proxy_config_service_qt.cpp b/src/core/net/proxy_config_service_qt.cpp index fcce08550a9..d74ec699df8 100644 --- a/src/core/net/proxy_config_service_qt.cpp +++ b/src/core/net/proxy_config_service_qt.cpp @@ -27,7 +27,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/proxying_restricted_cookie_manager_qt.cpp b/src/core/net/proxying_restricted_cookie_manager_qt.cpp index d4d5cc4ab3c..29e6de968f5 100644 --- a/src/core/net/proxying_restricted_cookie_manager_qt.cpp +++ b/src/core/net/proxying_restricted_cookie_manager_qt.cpp @@ -67,13 +67,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()); } @@ -121,7 +122,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 +130,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 +138,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..ba30a448e9f 100644 --- a/src/core/net/proxying_restricted_cookie_manager_qt.h +++ b/src/core/net/proxying_restricted_cookie_manager_qt.h @@ -29,6 +29,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 +55,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..ea5e1ebd656 100644 --- a/src/core/net/proxying_url_loader_factory_qt.cpp +++ b/src/core/net/proxying_url_loader_factory_qt.cpp @@ -65,6 +65,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 +374,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/qrc_url_scheme_handler.cpp b/src/core/net/qrc_url_scheme_handler.cpp index a8b4e4388a2..876c7d2031f 100644 --- a/src/core/net/qrc_url_scheme_handler.cpp +++ b/src/core/net/qrc_url_scheme_handler.cpp @@ -12,19 +12,21 @@ #include +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(':' + requestPath, job); + const QUrl requestUrl = job->requestUrl(); + const QString requestPath = requestUrl.path(); + auto file = std::make_unique(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 +35,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/resource_request_body_qt.cpp b/src/core/net/resource_request_body_qt.cpp index d0d54784dd9..e41da9e6806 100644 --- a/src/core/net/resource_request_body_qt.cpp +++ b/src/core/net/resource_request_body_qt.cpp @@ -9,6 +9,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 +60,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 +112,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(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 +163,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/ssl_host_state_delegate_qt.cpp b/src/core/net/ssl_host_state_delegate_qt.cpp index 41967f14efc..809a95c8dc4 100644 --- a/src/core/net/ssl_host_state_delegate_qt.cpp +++ b/src/core/net/ssl_host_state_delegate_qt.cpp @@ -127,9 +127,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..f210f028a18 100644 --- a/src/core/net/ssl_host_state_delegate_qt.h +++ b/src/core/net/ssl_host_state_delegate_qt.h @@ -39,11 +39,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 m_certPolicyforHost; diff --git a/src/core/net/system_network_context_manager.cpp b/src/core/net/system_network_context_manager.cpp index 439d1066c29..78098529da5 100644 --- a/src/core/net/system_network_context_manager.cpp +++ b/src/core/net/system_network_context_manager.cpp @@ -236,10 +236,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/url_request_custom_job_proxy.cpp b/src/core/net/url_request_custom_job_proxy.cpp index 54faddc6227..21d73730d4b 100644 --- a/src/core/net/url_request_custom_job_proxy.cpp +++ b/src/core/net/url_request_custom_job_proxy.cpp @@ -55,7 +55,9 @@ void URLRequestCustomJobProxy::reply(std::string contentType, QIODevice *device, m_client->m_charset = qcontentType.mid(cidx + 8).trimmed().toStdString(); qcontentType = qcontentType.first(sidx); } else { - qWarning() << "QWebEngineUrlRequestJob::reply(): Unrecognized content-type format with ';'" << qcontentType; + qWarning("QWebEngineUrlRequestJob::reply(): Unrecognized content-type format with ';' " + "%s", + qcontentType.constData()); } } m_client->m_mimeType = qcontentType.trimmed().toStdString(); diff --git a/src/core/net/version_ui_qt.cpp b/src/core/net/version_ui_qt.cpp index 61a89596a2d..a49dea62aaa 100644 --- a/src/core/net/version_ui_qt.cpp +++ b/src/core/net/version_ui_qt.cpp @@ -9,12 +9,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 +25,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 +40,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/webui_controller_factory_qt.cpp b/src/core/net/webui_controller_factory_qt.cpp index ed35a3e36fe..2acd05cae7a 100644 --- a/src/core/net/webui_controller_factory_qt.cpp +++ b/src/core/net/webui_controller_factory_qt.cpp @@ -14,8 +14,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/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 #include @@ -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 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 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(getProcAddress("eglCreateImage")); eglDestroyImage = reinterpret_cast(getProcAddress("eglDestroyImage")); - eglGetError = reinterpret_cast(getProcAddress("eglGetError")); eglExportDMABUFImageMESA = reinterpret_cast( getProcAddress("eglExportDMABUFImageMESA")); eglExportDMABUFImageQueryMESA = reinterpret_cast( getProcAddress("eglExportDMABUFImageQueryMESA")); + eglGetCurrentContext = + reinterpret_cast(getProcAddress("eglGetCurrentContext")); + eglGetCurrentDisplay = + reinterpret_cast(getProcAddress("eglGetCurrentDisplay")); + eglGetCurrentSurface = + reinterpret_cast(getProcAddress("eglGetCurrentSurface")); + eglGetError = reinterpret_cast(getProcAddress("eglGetError")); + eglMakeCurrent = reinterpret_cast(getProcAddress("eglMakeCurrent")); eglQueryString = reinterpret_cast(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 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( + GLContextHelper::getGlXGetProcAddress())); + gl::InitializeStaticGLBindingsGLX(); + gl::SetGLGetProcAddressProc(&EGL_GetProcAddress); + } +#endif + + return res; +} + +bool GLOzoneANGLEQt::InitializeExtensionSettingsOneOffPlatform(gl::GLDisplay *display) +{ + bool res = GLOzoneEGL::InitializeExtensionSettingsOneOffPlatform( + static_cast(display)); + +#if BUILDFLAG(IS_OZONE_X11) + if (GLContextHelper::getGlxPlatformInterface()) { + gl::SetGLGetProcAddressProc(reinterpret_cast( + 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 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 GLOzoneANGLEQt::CreateOffscreenGLSurface(gl::GLDisplay *display, + const gfx::Size &size) +{ + gl::GLDisplayEGL *eglDisplay = display->GetAs(); + + 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(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 +GLOzoneANGLEQt::ImportNativePixmap(scoped_refptr 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 CreateViewGLSurface(gl::GLDisplay *display, + gfx::AcceleratedWidget window) override; + scoped_refptr CreateOffscreenGLSurface(gl::GLDisplay *display, + const gfx::Size &size) override; + bool CanImportNativePixmap() override; + std::unique_ptr + ImportNativePixmap(scoped_refptr 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 GLOzoneEGLQt::CreateOffscreenGLSurface(gl::GLDispla gl::EGLDisplayPlatform GLOzoneEGLQt::GetNativeDisplay() { static void *display = GLContextHelper::getNativeDisplay(); - static gl::EGLDisplayPlatform platform(display ? reinterpret_cast(display) : EGL_DEFAULT_DISPLAY); + static gl::EGLDisplayPlatform platform(display ? reinterpret_cast(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(); 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 -CreateOffscreenGLSurfaceWithFormat(GLDisplay *display, const gfx::Size& size, GLSurfaceFormat format) -{ - scoped_refptr 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(); - 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 -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 CreateOffscreenGLSurface(GLDisplay *display, const gfx::Size &size) +{ + scoped_refptr 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(); + 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 +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 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 #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 ¶ms) { 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(); 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 #include -#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() }); } 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() }); + m_impls.push_back({ gl::GLImplementationParts(gl::kGLImplementationDesktopGL), + std::make_unique() }); } + + m_impls.push_back({ gl::GLImplementationParts(gl::kGLImplementationEGLANGLE), + std::make_unique() }); + m_impls.push_back({ gl::GLImplementationParts(gl::kGLImplementationDisabled), nullptr }); } std::vector SurfaceFactoryQt::GetAllowedGLImplementations() { - return m_impl; + std::vector 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 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 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 m_impl; - std::unique_ptr m_ozone; + std::vector>> 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 #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(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(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(permissionType); + Q_FALLTHROUGH(); + case QWebEnginePermission::PermissionType::Unsupported: + return blink::PermissionType::NUM; } - LOG(INFO) << "Unexpected unsupported Blink permission type: " << static_cast(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( base::CommandLine::ForCurrentProcess())); - QString userPrefStorePath = profileAdapter->dataPath(); + QString userPrefStorePath; + userPrefStorePath += profileAdapter->dataPath(); auto prefRegistry = base::MakeRefCounted(); 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(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 = toG/service/https://github.com/url(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 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(https://github.com/origin), GURL())); +} + +QList PermissionManagerQt::listPermissions(const QUrl &origin, QWebEnginePermission::PermissionType permissionType) { - return GetPermissionStatus(toBlink(type), toGurl(https://github.com/origin), GURL()) == blink::mojom::PermissionStatus::GRANTED; + Q_ASSERT(origin.isEmpty() || permissionType == QWebEnginePermission::PermissionType::Unsupported); + QList returnList; + GURL gorigin = toGurl(https://github.com/origin).DeprecatedGetOriginAsURL(); + std::string originSpec = gorigin.spec(); + + if (!origin.isEmpty() && !gorigin.is_valid()) + return returnList; + + std::vector 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 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( @@ -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 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 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 #include "profile_adapter.h" #include +#include 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 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 &)> 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 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 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 callback; }; @@ -77,20 +87,37 @@ class PermissionManagerQt : public content::PermissionControllerDelegate base::OnceCallback&)> callback; }; struct Subscription { - ProfileAdapter::PermissionType type; + QWebEnginePermission::PermissionType type; QUrl origin; base::RepeatingCallback 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 m_requests; std::vector m_multiRequests; + std::vector m_permissionTypes; + std::map>> m_transientPermissions; std::map m_subscribers; content::PermissionControllerDelegate::SubscriptionId::Generator subscription_id_generator_; int m_requestIdCount; + int m_transientWriteCount; std::unique_ptr m_prefService; QPointer 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(browser_context); + + const url::Origin origin = url::Origin::Create(url); + std::set movableStdStringSet; + auto it = profile->profileAdapter()->persistentNotifications().constBegin(); + const auto end = profile->profileAdapter()->persistentNotifications().constEnd(); + while (it != end) { + if (it.value()->isShown() && origin.IsSameOriginWith(toGurl(https://github.com/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( base::CommandLine::ForCurrentProcess())); - QString userPrefStorePath = profileAdapter.dataPath(); + QString userPrefStorePath; + userPrefStorePath += profileAdapter.dataPath(); if (!profileAdapter.isOffTheRecord() && !userPrefStorePath.isEmpty() && const_cast(&profileAdapter)->ensureDataPathExists()) { userPrefStorePath += QDir::separator(); - userPrefStorePath += QStringLiteral("user_prefs.json"); + userPrefStorePath += "user_prefs.json"_L1; factory.set_user_prefs(base::MakeRefCounted(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 + +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 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())); @@ -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(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(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 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 #include +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 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(profile()->GetPermissionControllerDelegate())->setPermission(origin, permissionType, state, rfh); +} + +QWebEnginePermission::State ProfileAdapter::getPermissionState(const QUrl &origin, QWebEnginePermission::PermissionType permissionType, + content::RenderFrameHost *rfh) { - static_cast(profile()->GetPermissionControllerDelegate())->permissionRequestReply(origin, type, reply); + return static_cast(profile()->GetPermissionControllerDelegate())->getPermissionState(origin, permissionType, rfh); } -bool ProfileAdapter::checkPermission(const QUrl &origin, PermissionType type) +QList ProfileAdapter::listPermissions(const QUrl &origin, QWebEnginePermission::PermissionType permissionType) { - return static_cast(profile()->GetPermissionControllerDelegate())->checkPermission(origin, type); + if (persistentPermissionsPolicy() == ProfileAdapter::PersistentPermissionsPolicy::AskEveryTime) + return QList(); + + return static_cast(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 #include #include +#include #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 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 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 &) { } 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 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(storage); - storage_impl->ResetURLLoaderFactories(); - storage_impl->ResetNetworkContext(); - }, this)); + m_profile->ForEachLoadedStoragePartition([this](content::StoragePartition *storage) { + storage->SetNetworkContextCreatedObserver(this); + auto storage_impl = static_cast(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(); - 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 m_resourceContext; scoped_refptr m_cookieDelegate; QPointer 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 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(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 #include #include #include @@ -24,6 +25,8 @@ #include #include +using namespace Qt::StringLiterals; + namespace QtWebEngineCore { static inline int firstAvailableId(const QMap &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(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( + 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..0c38f0f7a02 100644 --- a/src/core/render_widget_host_view_qt_delegate_item.cpp +++ b/src/core/render_widget_host_view_qt_delegate_item.cpp @@ -14,6 +14,8 @@ #include #endif +using namespace Qt::StringLiterals; + namespace QtWebEngineCore { RenderWidgetHostViewQtDelegateItem::RenderWidgetHostViewQtDelegateItem(RenderWidgetHostViewQtDelegateClient *client, bool isPopup) @@ -30,12 +32,13 @@ RenderWidgetHostViewQtDelegateItem::RenderWidgetHostViewQtDelegateItem(RenderWid setFocus(true); setActiveFocusOnTab(true); } - bind(client->compositorId()); + bind(client->compositorId()); // Compositor::Observer } RenderWidgetHostViewQtDelegateItem::~RenderWidgetHostViewQtDelegateItem() { releaseTextureResources(); + unbind(); // Compositor::Observer if (m_widgetDelegate) { m_widgetDelegate->Unbind(); m_widgetDelegate->Destroy(); @@ -53,8 +56,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 +129,8 @@ void RenderWidgetHostViewQtDelegateItem::readyToSwap() void RenderWidgetHostViewQtDelegateItem::updateCursor(const QCursor &cursor) { + if (m_widgetDelegate) + m_widgetDelegate->SetCursor(cursor); setCursor(cursor); } @@ -317,9 +322,18 @@ 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(); + + auto comp = compositor(); + if (comp && comp->type() == Compositor::Type::Native) { + comp->releaseTexture(); + comp->releaseResources(); + } + } + if (value.window) { m_windowConnections.append(connect(value.window, &QQuickWindow::beforeRendering, this, &RenderWidgetHostViewQtDelegateItem::onBeforeRendering, Qt::DirectConnection)); @@ -345,6 +359,8 @@ void RenderWidgetHostViewQtDelegateItem::itemChange(ItemChange change, const Ite if (!m_isPopup) onHide(); } + } else if (change == QQuickItem::ItemDevicePixelRatioHasChanged) { + m_client->visualPropertiesChanged(); } } @@ -354,6 +370,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; 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..65fbeeb175e 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 *) { } }; 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( - base::BindRepeating( - &extensions::MimeHandlerViewContainerManager::BindReceiver, - render_frame->GetRoutingID())); + base::BindRepeating(&extensions::MimeHandlerViewContainerManager::BindReceiver, + base::Unretained(render_frame))); auto registry = std::make_unique(); 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(render_frame, associated_interfaces); + auto password_generation_agent = + std::make_unique(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 &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(render_frame)->GetPepperHost()->GetPluginInfo( - params.url, params.mime_type.Utf8(), &found, &info, &mime_type); + static_cast(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 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())); + extension_dispatcher_.reset(new extensions::Dispatcher( + std::make_unique(), + std::vector>())); 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& 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 &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 ¶ms, + 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..1a6b9e935fa 100644 --- a/src/core/renderer_host/user_resource_controller_host.cpp +++ b/src/core/renderer_host/user_resource_controller_host.cpp @@ -42,10 +42,9 @@ UserResourceControllerHost::WebContentsObserverHelper::WebContentsObserverHelper void UserResourceControllerHost::WebContentsObserverHelper::RenderFrameCreated(content::RenderFrameHost *renderFrameHost) { - content::WebContents *contents = web_contents(); auto &remote = m_controllerHost->GetUserResourceControllerRenderFrame(renderFrameHost); - const QList 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 +55,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/web_channel_ipc_transport_host.cpp b/src/core/renderer_host/web_channel_ipc_transport_host.cpp index 0824ed56d9d..41ef98c8bb9 100644 --- a/src/core/renderer_host/web_channel_ipc_transport_host.cpp +++ b/src/core/renderer_host/web_channel_ipc_transport_host.cpp @@ -48,7 +48,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 +107,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..16ff5178933 100644 --- a/src/core/renderer_host/web_channel_ipc_transport_host.h +++ b/src/core/renderer_host/web_channel_ipc_transport_host.h @@ -45,6 +45,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/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 #include +using namespace Qt::StringLiterals; + // see also src/core/type_conversion.h inline base::FilePath::StringType toFilePathString(const QString &str) { @@ -68,11 +70,11 @@ template QTextStream &operator<<(QTextStream &out, base::span 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 &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(https://github.com/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(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)> &&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 WebContentsAdapter::frameChildren(quint64 id) const @@ -2137,7 +2232,7 @@ void WebContentsAdapter::discard() if (m_webContents->IsLoading()) { m_webContentsDelegate->didFailLoad(m_webContentsDelegate->url(https://github.com/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 #include #include +#include #include "web_contents_adapter_client.h" @@ -178,10 +179,12 @@ class Q_WEBENGINECORE_EXPORT WebContentsAdapter : public QEnableSharedFromThis)> &&, - 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 result); void replaceMisspelling(const QString &word); @@ -235,6 +238,7 @@ class Q_WEBENGINECORE_EXPORT WebContentsAdapter : public QEnableSharedFromThis 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 +#include #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)> &&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) = 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 #include +using namespace Qt::StringLiterals; + namespace QtWebEngineCore { static WebContentsAdapterClient::JavaScriptConsoleMessageLevel mapToJavascriptConsoleMessageLevel(blink::mojom::ConsoleMessageLevel log_level) @@ -199,7 +201,8 @@ QUrl WebContentsDelegateQt::url(/service/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 QSharedPointerselectClientCert(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 &); void selectClientCert(const QSharedPointer &); - 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 ¶ms) override; diff --git a/src/core/web_engine_context.cpp b/src/core/web_engine_context.cpp index faf9bd54229..8dc83fcc99e 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 #include -#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) && (defined(USE_OZONE) || defined(Q_OS_WIN)) +#include "ozone/gl_context_qt.h" + #include #include @@ -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 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(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 metalRhi( QRhi::create(QRhi::Metal, ¶ms, 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 glRhi( QRhi::create(QRhi::OpenGLES2, ¶ms, 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&) 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().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(https://github.com/toQt(commandLine-%3EGetSwitchValueASCII(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::vectorAppendSwitchASCII(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(); - 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 "136.0.7103.114"; // 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 { 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 { std::unique_ptr m_defaultProfileAdapter; std::unique_ptr m_devtoolsServer; QList m_profileAdapters; + std::unique_ptr m_fieldTrialList; #if QT_CONFIG(accessibility) std::unique_ptr m_accessibilityActivationObserver; #endif #if QT_CONFIG(webengine_printing_and_pdf) std::unique_ptr m_printJobManager; -#endif -#if QT_CONFIG(webengine_webrtc) && QT_CONFIG(webengine_extensions) - std::unique_ptr m_webrtcLogUploader; #endif static scoped_refptr 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..bb704926d33 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,44 @@ 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 { + const auto resourcesPakFilename = "qtwebengine_resources.pak"_L1; + 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 +353,18 @@ QString resourcesPath() } } // namespace -base::FilePath WebEngineLibraryInfo::getPath(int key) +base::FilePath WebEngineLibraryInfo::getPath(int key, bool showWarnings) { QString directory; switch (key) { 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); #if defined(Q_OS_DARWIN) && defined(QT_MAC_FRAMEWORK_BUILD) case QT_FRAMEWORK_BUNDLE: return toFilePath(getBundlePath(frameworkBundle())); @@ -382,7 +389,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 +436,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 #include +using namespace Qt::StringLiterals; + namespace QtWebEngineCore { QHash 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 #include #include @@ -51,6 +52,8 @@ #endif #include +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(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(ev->globalPosition().x()), static_cast(ev->globalPosition().y())); - webEvent.wheel_ticks_x = ev->angleDelta().x() / static_cast(QWheelEvent::DefaultDeltasPerStep); - webEvent.wheel_ticks_y = ev->angleDelta().y() / static_cast(QWheelEvent::DefaultDeltasPerStep); + webEvent.wheel_ticks_x += ev->angleDelta().x() / static_cast(QWheelEvent::DefaultDeltasPerStep); + webEvent.wheel_ticks_y += ev->angleDelta().y() / static_cast(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 "$" 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}/$ --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" - $<$:--isysroot> - $<$:${CMAKE_OSX_SYSROOT}> + $<$,$>:--isysroot> + $<$,$>:${CMAKE_OSX_SYSROOT}> + $<$,$>:--osx-architectures> + $<$,$>:${OSX_ARCH}> COMMAND Ninja::ninja -C ${GN_BINARY_DIR}/$ ${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 -// 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 #include #include +#include #include #include @@ -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 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 -#include +#ifndef QQUICKWEBENGINEDOWNLOADREQUEST_H +#define QQUICKWEBENGINEDOWNLOADREQUEST_H + #include +#include #include 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 #include #include +#include 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..47f8a1e95a4 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 #include #include +#include #include #include @@ -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( 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 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 QQuickWebEngineProfile::listAllPermissions() const +{ + Q_D(const QQuickWebEngineProfile); + if (persistentPermissionsPolicy() == PersistentPermissionsPolicy::AskEveryTime) + return QList(); + return d->profileAdapter()->listPermissions(); +} + +/*! + \qmlmethod list 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 QQuickWebEngineProfile::listPermissionsForOrigin(const QUrl &securityOrigin) const +{ + Q_D(const QQuickWebEngineProfile); + if (persistentPermissionsPolicy() == PersistentPermissionsPolicy::AskEveryTime) + return QList(); + return d->profileAdapter()->listPermissions(securityOrigin); +} + +/*! + \qmlmethod list 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 QQuickWebEngineProfile::listPermissionsForPermissionType(QWebEnginePermission::PermissionType permissionType) const +{ + Q_D(const QQuickWebEngineProfile); + if (persistentPermissionsPolicy() == PersistentPermissionsPolicy::AskEveryTime) + return QList(); + + if (permissionType == QWebEnginePermission::PermissionType::Unsupported) { + qWarning("Attempting to get permission list for an unsupported type. Returned list will be empty."); + return QList(); + } + + if (!QWebEnginePermission::isPersistent(permissionType)) { + qWarning() << "Attempting to get permission list for permission type" << permissionType << ". Returned list will be empty."; + return QList(); + } + + 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 +#include #include #include #include @@ -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 listAllPermissions() const; + Q_REVISION(6,8) Q_INVOKABLE QList listPermissionsForOrigin(const QUrl &securityOrigin) const; + Q_REVISION(6,8) Q_INVOKABLE QList 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 &controller) override; diff --git a/src/webenginequick/api/qquickwebenginescriptcollection.cpp b/src/webenginequick/api/qquickwebenginescriptcollection.cpp index a58d9783219..f5328a134e4 100644 --- a/src/webenginequick/api/qquickwebenginescriptcollection.cpp +++ b/src/webenginequick/api/qquickwebenginescriptcollection.cpp @@ -117,9 +117,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 +133,7 @@ bool QQuickWebEngineScriptCollection::contains(const QWebEngineScript &value) co \qmlmethod list 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 QQuickWebEngineScriptCollection::find(const QString &name) const { @@ -143,7 +144,7 @@ QList 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 +155,7 @@ void QQuickWebEngineScriptCollection::insert(const QWebEngineScript &s) \qmlmethod void WebEngineScriptCollection::insert(list 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 &list) { 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..0fa9db0f060 100644 --- a/src/webenginequick/api/qquickwebengineview.cpp +++ b/src/webenginequick/api/qquickwebengineview.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,7 @@ #include #include #include +#include #include #include @@ -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 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)) + 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)) - feature = QQuickWebEngineView::DesktopVideoCapture; - Q_EMIT q->featurePermissionRequested(securityOrigin, feature); + 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)> &&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) @@ -1465,6 +1519,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 +1646,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 +1673,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 +1820,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 +2598,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 #include #include +#include #include #include #include @@ -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 &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)> &&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 &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..b507d760b79 100644 --- a/src/webenginequick/render_widget_host_view_qt_delegate_quickwindow.cpp +++ b/src/webenginequick/render_widget_host_view_qt_delegate_quickwindow.cpp @@ -7,6 +7,25 @@ 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 @@ -32,7 +51,7 @@ 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(parent), m_realDelegate(realDelegate), m_virtualParent(nullptr), m_transformed(false) { setFlags(Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus); realDelegate->setParentItem(contentItem()); @@ -56,9 +75,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 +90,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 +102,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 +121,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 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..f4036059d6a 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; @@ -334,12 +336,17 @@ void UIDelegatesManager::showDialog(QSharedPointerurl()); 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/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 #include @@ -9,6 +9,9 @@ #include +#include +#include + 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 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 #include 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 #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 #include 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 #include @@ -10,6 +10,9 @@ #include #include +#include +#include + 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..a75002ae51c 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 #include 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 @@ + + + Printing Inner Document + +

Inner Header

+ + 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 @@ + + + Printing Outer Document + +

Outer Header

+ + + diff --git a/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp b/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp index ce0b61ee27b..0e036ac7449 100644 --- a/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp +++ b/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp @@ -22,6 +22,7 @@ #include #include +#include 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 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 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 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 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 outerSpy; + outerFrame.printToPdf(outerSpy.ref()); + auto outerPdfData = outerSpy.waitForResult(); + QCOMPARE_GT(outerPdfData.size(), 0); + + CallbackSpy 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 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 #include @@ -32,8 +32,6 @@ private Q_SLOTS: void dnsOverHttps(); }; -Q_LOGGING_CATEGORY(lc, "qt.webengine.tests") - void tst_QWebEngineGlobalSettings::dnsOverHttps_data() { QTest::addColumn("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 #include 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 #include @@ -159,7 +157,7 @@ class TestRequestInterceptor : public QWebEngineUrlRequestInterceptor { QList 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("firstInterceptIsInPage"); - QTest::addColumn("keepInterceptionPoint"); + QTest::addColumn("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('https://github.com/service/http://nonexistent.invalid/').catch(() => {})", [&, interceptInPage] (const QVariant &) { - requestsOnReplace.push_back(interceptors.back().requestInfos.size()); - setInterceptor(nullptr, interceptInPage); + page.runJavaScript("fetch('https://github.com/service/http://nonexistent.invalid/').catch(() => {})", [&fetchFinished] (const QVariant &) { fetchFinished = true; }); } @@ -856,6 +851,10 @@ void tst_QWebEngineUrlRequestInterceptor::replaceInterceptor() page.setUrl(server.url("https://github.com/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 #include @@ -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 #include 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 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 @@ Desktop Notifications Demo + + diff --git a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp index dbfa1cb3350..1d85c495cbe 100644 --- a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp +++ b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp @@ -1,7 +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 - -#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "testwindow.h" #include "quickutil.h" @@ -15,11 +13,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include @@ -27,6 +25,8 @@ #include +using namespace Qt::StringLiterals; + class tst_QQuickWebEngineView : public QObject { Q_OBJECT public: @@ -76,10 +76,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 +100,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('/'))) @@ -614,7 +616,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 +711,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!")); @@ -799,7 +801,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 +818,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() @@ -1152,9 +1156,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 +1255,74 @@ void tst_QQuickWebEngineView::focusChild() // -> -> 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(); + QVERIFY(webView); + + webView->loadHtml(""); + 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() @@ -1360,6 +1432,82 @@ void tst_QQuickWebEngineView::savePage() originalData); } +class TestJSMessageHandler +{ +public: + inline static QList levels; + inline static QStringList messages; + inline static QList 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 #include 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 @@ -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 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..419f2f877fb 100644 --- a/tests/auto/quick/uidelegates/tst_uidelegates.cpp +++ b/tests/auto/quick/uidelegates/tst_uidelegates.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 "testwindow.h" #include "quickutil.h" @@ -16,6 +16,8 @@ class tst_UIDelegates : public QObject { Q_OBJECT public: + static void initMain(); + tst_UIDelegates(); private Q_SLOTS: @@ -39,9 +41,13 @@ private Q_SLOTS: QScopedPointer 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" 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/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("" \ "Hello world" \ "" \ ""); 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("
") << 0 << QAccessible::Dialog; //QTest::newRow("ax::mojom::Role::kDirectory") << QString("
    ") << 0 << QAccessible::List; // FIXME: Aria role 'directory' should work QTest::newRow("ax::mojom::Role::kDisclosureTriangle") << QString("
    a
    ") << 1 << QAccessible::Button; + QTest::newRow("ax::mojom::Role::kDisclosureTriangleGroup") << QString("
    a
    ") << 1 << QAccessible::Button; QTest::newRow("ax::mojom::Role::kGenericContainer") << QString("
    a
    ") << 0 << QAccessible::Section; QTest::newRow("ax::mojom::Role::kDocCover") << QString("
    ") << 0 << QAccessible::Graphic; QTest::newRow("ax::mojom::Role::kDocBackLink") << QString("
    ") << 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 #include 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 #include 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 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 #include 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 #include @@ -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 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 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 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 #include diff --git a/tests/auto/widgets/qwebenginedownloadrequest/tst_qwebenginedownloadrequest.cpp b/tests/auto/widgets/qwebenginedownloadrequest/tst_qwebenginedownloadrequest.cpp index c81a27b3aaf..b01d7b8c933 100644 --- a/tests/auto/widgets/qwebenginedownloadrequest/tst_qwebenginedownloadrequest.cpp +++ b/tests/auto/widgets/qwebenginedownloadrequest/tst_qwebenginedownloadrequest.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 @@ -16,6 +16,38 @@ #include #include +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 +76,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 +92,7 @@ private Q_SLOTS: void downloadToDirectoryWithFileName(); void downloadDataUrls_data(); void downloadDataUrls(); + void pauseDownload(); private: void saveLink(QPoint linkPos); @@ -66,7 +101,7 @@ private Q_SLOTS: HttpServer *m_server; QWebEngineProfile *m_profile; - QWebEnginePage *m_page; + TestPage *m_page; QWebEngineView *m_view; QSet m_requestedDownloads; QSet m_finishedDownloads; @@ -91,7 +126,7 @@ 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); @@ -452,10 +487,12 @@ void tst_QWebEngineDownloadRequest::downloadLink() // The only variation being whether the 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); @@ -550,9 +587,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 +720,7 @@ void tst_QWebEngineDownloadRequest::downloadViaSetUrl() QList 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 +750,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("Hello")); + 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("answer"); + QTest::addColumn("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 downloadItem; + QTemporaryDir tmpDir; + QVERIFY(tmpDir.isValid()); + m_profile->setDownloadPath(tmpDir.path()); + + QPointer 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(https://github.com/QByteArrayLiteral(%22/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 +1020,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. @@ -1270,17 +1369,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/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 @@ + + + + diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index f6eac2880b5..9fd9d2a58c0 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -41,6 +41,8 @@ # include #endif #include +#include +#include #include #include #if QT_CONFIG(webengine_webchannel) @@ -72,11 +74,14 @@ #include #include #include +#include #include #include #include #include +using namespace Qt::StringLiterals; + static void removeRecursive(const QString& dirname) { QDir dir(dirname); @@ -134,6 +139,7 @@ private Q_SLOTS: void backActionUpdate(); void localStorageVisibility(); void consoleOutput(); + void javaScriptConsoleMessage(); void userAgentNewlineStripping(); void renderWidgetHostViewNotShowTopLevel(); void getUserMediaRequest_data(); @@ -267,6 +273,7 @@ private Q_SLOTS: void renderProcessCrashed(); void renderProcessPid(); void backgroundColor(); + void popupOnTransparentBackground(); void audioMuted(); void closeContents(); void isSafeRedirect_data(); @@ -451,12 +458,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 +492,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("test"), QUrl("qrc://secure/origin")); @@ -609,7 +616,7 @@ class ConsolePage : public QWebEnginePage sourceIDs.append(sourceID); } - QList levels; + QList levels; QStringList messages; QList lineNumbers; QStringList sourceIDs; @@ -624,6 +631,78 @@ void tst_QWebEnginePage::consoleOutput() QCOMPARE(page.lineNumbers.at(0), 1); } +class TestJSMessageHandler +{ +public: + inline static QList levels; + inline static QStringList messages; + inline static QList 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 +1277,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(); + 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(); + 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 +1750,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 +1782,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 m_permission; }; void tst_QWebEnginePage::getUserMediaRequest_data() { QTest::addColumn("call"); - QTest::addColumn("feature"); + QTest::addColumn("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 +1901,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 +1911,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 +1933,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 +1950,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 +1958,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 pages(10); // Desktop capture needs to be on a desktop @@ -1854,11 +1979,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 +1991,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 +2005,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 +2762,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 +3358,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 +3378,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() @@ -3795,20 +3924,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 +3960,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 +3968,50 @@ struct NotificationPage : ConsolePage { void tst_QWebEnginePage::notificationPermission_data() { QTest::addColumn("setOnInit"); - QTest::addColumn("policy"); + QTest::addColumn("policy"); QTest::addColumn("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(/service/qurl::RemoveFilename)) { - qWarning() << "Unexpected case. Can't proceed." << setOnInit << permissionRequested << o; + if (permissionRequested || permission.origin() != baseUrl.url(/service/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("Test"), baseUrl); @@ -3871,7 +4020,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 +4037,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 +4088,25 @@ void tst_QWebEnginePage::clipboardReadWritePermissionInitialState_data() { QTest::addColumn("canAccessClipboard"); QTest::addColumn("canPaste"); - QTest::addColumn("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("readPermission"); + QTest::addColumn("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 +4120,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("canAccessClipboard"); - QTest::addColumn("initialPolicy"); + QTest::addColumn("initialPolicy"); QTest::addColumn("initialPermission"); - QTest::addColumn("requestPolicy"); + QTest::addColumn("requestPolicy"); QTest::addColumn("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 +4181,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(/service/qurl::RemoveFilename)) { - qWarning() << "Unexpected case. Can't proceed." << o; + if (permission.origin() != baseUrl.url(/service/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("Test"), baseUrl); @@ -4099,31 +4279,35 @@ void tst_QWebEnginePage::contentsSize() void tst_QWebEnginePage::localFontAccessPermission_data() { - QTest::addColumn("policy"); + QTest::addColumn("policy"); QTest::addColumn("ignore"); QTest::addColumn("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 +4789,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 +5426,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("")); + QTRY_COMPARE(spyLoadFinished.size(), 1); + makeClick(view.windowHandle(), false, elementCenter(view.page(), "foo")); + QPointer popup; + QTRY_VERIFY((popup = QApplication::activePopupWidget())); +} + void tst_QWebEnginePage::audioMuted() { QWebEngineProfile profile; @@ -5380,11 +5585,11 @@ void tst_QWebEnginePage::clientHints_data() QTest::addColumn("platformVersion"); QTest::addColumn("bitness"); QTest::addColumn("isWOW64"); - QTest::addColumn>("fullVersionList"); + QTest::addColumn("fullVersionList"); - QTest::newRow("Modify values") << true << "Abc" << "AmigaOS" << "Ultra" << true << "1.99" << "3" << "x64" << true << QHash({{"APITest", "1.0.0"}, {"App", "5.0"}}); - QTest::newRow("Empty values") << true << "" << "" << "" << false << "" << "" << "" << false << QHash(); - QTest::newRow("Disable headers") << false << "" << "" << "" << false << "" << "" << "" << false << QHash(); + 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 +5603,7 @@ void tst_QWebEnginePage::clientHints() QFETCH(QString, platformVersion); QFETCH(QString, bitness); QFETCH(bool, isWOW64); - typedef QHash brandVersionPairs; - QFETCH(brandVersionPairs, fullVersionList); + QFETCH(QVariantMap, fullVersionList); QWebEnginePage page; QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); @@ -5724,22 +5928,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 +5957,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..97e5bd6017f 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 #include @@ -26,6 +26,8 @@ #include #include +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"( @@ -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() @@ -1022,10 +1039,10 @@ void tst_QWebEngineProfile::permissionPersistence_data() QTest::addColumn("policy"); QTest::addColumn("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("https://github.com/hedgehog.html"), QWebEnginePage::Notifications, - granted ? QWebEnginePage::PermissionGrantedByUser : QWebEnginePage::PermissionDeniedByUser); + + QWebEnginePermission permissionObject = profile->queryPermission(server.url("https://github.com/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("https://github.com/hedgehog.html"), QWebEnginePage::Notifications, QWebEnginePage::PermissionUnknown); + // Re-acquire the permission, since deleting the Profile makes it invalid + permissionObject = profile->queryPermission(server.url("https://github.com/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("permissionType"); + QTest::addColumn("url"); + QTest::addColumn("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 permissionsListAll = profile.listAllPermissions(); + QList permissionsListUrl = profile.listPermissionsForOrigin(commonUrl); + QList 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 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("" \ + "

    hello world

    "); + 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..a6383c63023 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp @@ -19,8 +19,6 @@ Boston, MA 02110-1301, USA. */ -#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses - #include #include #include @@ -189,6 +187,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 +286,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 } @@ -1618,6 +1618,7 @@ void tst_QWebEngineView::keyboardFocusAfterPopup() connect(window.lineEdit, &QLineEdit::editingFinished, [&] { window.webView->setHtml(html); }); window.webView->settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true); window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); // Focus will initially go to the QLineEdit. QTRY_COMPARE(QApplication::focusWidget(), window.lineEdit); @@ -2068,6 +2069,7 @@ void tst_QWebEngineView::inputContextQueryInput() ""); QTRY_COMPARE(loadFinishedSpy.size(), 1); QVERIFY(QTest::qWaitForWindowExposed(&view)); + QTRY_VERIFY(qApp->focusObject()); QCOMPARE(testContext.infos.size(), 0); // Set focus on an input field. @@ -2075,7 +2077,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 +2176,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!")); @@ -2947,8 +2949,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 +2962,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 +2981,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 +2993,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 ")); @@ -3894,10 +3896,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 +4008,85 @@ 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() +{ + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Wayland: Can't manipulate the mouse cursor in auto test."); + + const QString html(QStringLiteral("" + "Pointer" + "" + "")); + 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(); + + QVERIFY(QTest::qWaitForWindowExposed(&parentWidget)); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + 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(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 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 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 #include 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 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}/$/") + 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 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}/$/") + 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 #include @@ -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 #include 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 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 #include @@ -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 #include @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -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()