summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf2
-rw-r--r--LICENSE.FDL450
-rw-r--r--LICENSE.LGPLv3174
-rw-r--r--coin/qt-installer-package-config.json16
-rw-r--r--configure.json4
-rw-r--r--configure.pri13
-rw-r--r--examples/examples.pro4
-rw-r--r--examples/pdf/multipage/main.cpp73
-rw-r--r--examples/pdf/multipage/multipage.pro14
-rw-r--r--examples/pdf/multipage/resources/document-open.svg13
-rw-r--r--examples/pdf/multipage/resources/edit-clear.svg15
-rw-r--r--examples/pdf/multipage/resources/edit-copy.svg15
-rw-r--r--examples/pdf/multipage/resources/go-down-search.svg13
-rw-r--r--examples/pdf/multipage/resources/go-next-view-page.svg13
-rw-r--r--examples/pdf/multipage/resources/go-previous-view-page.svg13
-rw-r--r--examples/pdf/multipage/resources/go-up-search.svg8
-rw-r--r--examples/pdf/multipage/resources/rotate-left.svg6
-rw-r--r--examples/pdf/multipage/resources/rotate-right.svg6
-rw-r--r--examples/pdf/multipage/resources/test.pdfbin0 -> 80045 bytes
-rw-r--r--examples/pdf/multipage/resources/zoom-fit-best.svg13
-rw-r--r--examples/pdf/multipage/resources/zoom-fit-width.svg13
-rw-r--r--examples/pdf/multipage/resources/zoom-in.svg13
-rw-r--r--examples/pdf/multipage/resources/zoom-original.svg13
-rw-r--r--examples/pdf/multipage/resources/zoom-out.svg13
-rw-r--r--examples/pdf/multipage/viewer.qml328
-rw-r--r--examples/pdf/multipage/viewer.qrc20
-rw-r--r--examples/pdf/pdf.pro3
-rw-r--r--examples/pdf/pdfviewer/main.cpp72
-rw-r--r--examples/pdf/pdfviewer/pdfviewer.pro14
-rw-r--r--examples/pdf/pdfviewer/resources/document-open.svg13
-rw-r--r--examples/pdf/pdfviewer/resources/edit-clear.svg15
-rw-r--r--examples/pdf/pdfviewer/resources/edit-copy.svg15
-rw-r--r--examples/pdf/pdfviewer/resources/go-down-search.svg13
-rw-r--r--examples/pdf/pdfviewer/resources/go-next-view-page.svg13
-rw-r--r--examples/pdf/pdfviewer/resources/go-previous-view-page.svg13
-rw-r--r--examples/pdf/pdfviewer/resources/go-up-search.svg8
-rw-r--r--examples/pdf/pdfviewer/resources/rotate-left.svg6
-rw-r--r--examples/pdf/pdfviewer/resources/rotate-right.svg6
-rw-r--r--examples/pdf/pdfviewer/resources/test.pdfbin0 -> 80045 bytes
-rw-r--r--examples/pdf/pdfviewer/resources/zoom-fit-best.svg13
-rw-r--r--examples/pdf/pdfviewer/resources/zoom-fit-width.svg13
-rw-r--r--examples/pdf/pdfviewer/resources/zoom-in.svg13
-rw-r--r--examples/pdf/pdfviewer/resources/zoom-original.svg13
-rw-r--r--examples/pdf/pdfviewer/resources/zoom-out.svg13
-rw-r--r--examples/pdf/pdfviewer/viewer.qml302
-rw-r--r--examples/pdf/pdfviewer/viewer.qrc20
-rw-r--r--examples/pdfwidgets/pdfviewer/doc/src/pdfviewer.qdoc108
-rw-r--r--examples/pdfwidgets/pdfviewer/images/busy.pngbin0 -> 172 bytes
-rw-r--r--examples/pdfwidgets/pdfviewer/images/fileopen.pngbin0 -> 1771 bytes
-rw-r--r--examples/pdfwidgets/pdfviewer/images/go-next-24.pngbin0 -> 782 bytes
-rw-r--r--examples/pdfwidgets/pdfviewer/images/go-previous-24.pngbin0 -> 797 bytes
-rw-r--r--examples/pdfwidgets/pdfviewer/images/zoom-in-24.pngbin0 -> 1302 bytes
-rw-r--r--examples/pdfwidgets/pdfviewer/images/zoom-in-32.pngbin0 -> 1873 bytes
-rw-r--r--examples/pdfwidgets/pdfviewer/images/zoom-out-24.pngbin0 -> 1247 bytes
-rw-r--r--examples/pdfwidgets/pdfviewer/images/zoom-out-32.pngbin0 -> 1749 bytes
-rw-r--r--examples/pdfwidgets/pdfviewer/main.cpp51
-rw-r--r--examples/pdfwidgets/pdfviewer/mainwindow.cpp162
-rw-r--r--examples/pdfwidgets/pdfviewer/mainwindow.h90
-rw-r--r--examples/pdfwidgets/pdfviewer/mainwindow.ui281
-rw-r--r--examples/pdfwidgets/pdfviewer/pageselector.cpp111
-rw-r--r--examples/pdfwidgets/pdfviewer/pageselector.h72
-rw-r--r--examples/pdfwidgets/pdfviewer/pdfviewer.pro23
-rw-r--r--examples/pdfwidgets/pdfviewer/resources.qrc12
-rw-r--r--examples/pdfwidgets/pdfviewer/zoomselector.cpp97
-rw-r--r--examples/pdfwidgets/pdfviewer/zoomselector.h63
-rw-r--r--examples/pdfwidgets/pdfwidgets.pro2
-rw-r--r--mkspecs/features/functions.prf1
-rw-r--r--mkspecs/features/gn_generator.prf16
-rw-r--r--src/buildtools/config/common.pri21
-rw-r--r--src/buildtools/config/ios.pri66
-rw-r--r--src/buildtools/config/linux.pri20
-rw-r--r--src/buildtools/config/mac_osx.pri2
-rw-r--r--src/buildtools/config/pdf.pri36
-rw-r--r--src/buildtools/config/support.pri26
-rw-r--r--src/buildtools/config/windows.pri2
-rw-r--r--src/buildtools/configure.json34
-rw-r--r--src/core/api/qwebengineurlrequestinfo.h2
-rw-r--r--src/core/configure.json7
-rw-r--r--src/core/content_browser_client_qt.cpp415
-rw-r--r--src/core/content_browser_client_qt.h29
-rw-r--r--src/core/core_chromium.pri30
-rw-r--r--src/core/core_gn_config.pri1
-rw-r--r--src/core/extensions/extension_web_contents_observer_qt.cpp36
-rw-r--r--src/core/extensions/extension_web_contents_observer_qt.h3
-rw-r--r--src/core/extensions/extensions_browser_client_qt.cpp162
-rw-r--r--src/core/login_delegate_qt.cpp6
-rw-r--r--src/core/net/cookie_monster_delegate_qt.cpp169
-rw-r--r--src/core/net/cookie_monster_delegate_qt.h46
-rw-r--r--src/core/net/custom_protocol_handler.cpp62
-rw-r--r--src/core/net/custom_protocol_handler.h89
-rw-r--r--src/core/net/custom_url_loader_factory.cpp447
-rw-r--r--src/core/net/custom_url_loader_factory.h (renamed from src/core/net/url_request_context_getter_qt.h)40
-rw-r--r--src/core/net/network_delegate_qt.cpp305
-rw-r--r--src/core/net/network_delegate_qt.h102
-rw-r--r--src/core/net/plugin_response_interceptor_url_loader_throttle.cpp135
-rw-r--r--src/core/net/plugin_response_interceptor_url_loader_throttle.h (renamed from src/core/net/url_request_notification.h)62
-rw-r--r--src/core/net/proxy_config_monitor.cpp125
-rw-r--r--src/core/net/proxy_config_monitor.h104
-rw-r--r--src/core/net/proxy_config_service_qt.cpp32
-rw-r--r--src/core/net/proxy_config_service_qt.h12
-rw-r--r--src/core/net/proxying_restricted_cookie_manager_qt.cpp231
-rw-r--r--src/core/net/proxying_restricted_cookie_manager_qt.h (renamed from src/core/net/restricted_cookie_manager_qt.h)53
-rw-r--r--src/core/net/proxying_url_loader_factory_qt.cpp560
-rw-r--r--src/core/net/proxying_url_loader_factory_qt.h107
-rw-r--r--src/core/net/restricted_cookie_manager_qt.cpp185
-rw-r--r--src/core/net/system_network_context_manager.cpp351
-rw-r--r--src/core/net/system_network_context_manager.h187
-rw-r--r--src/core/net/url_request_context_getter_qt.cpp69
-rw-r--r--src/core/net/url_request_custom_job.cpp267
-rw-r--r--src/core/net/url_request_custom_job.h95
-rw-r--r--src/core/net/url_request_custom_job_delegate.cpp22
-rw-r--r--src/core/net/url_request_custom_job_proxy.cpp85
-rw-r--r--src/core/net/url_request_custom_job_proxy.h23
-rw-r--r--src/core/net/url_request_notification.cpp194
-rw-r--r--src/core/profile_adapter.cpp49
-rw-r--r--src/core/profile_io_data_qt.cpp535
-rw-r--r--src/core/profile_io_data_qt.h42
-rw-r--r--src/core/profile_qt.cpp15
-rw-r--r--src/core/qtwebengine.gni1
-rw-r--r--src/core/renderer/content_renderer_client_qt.cpp136
-rw-r--r--src/core/renderer/content_renderer_client_qt.h30
-rw-r--r--src/core/renderer/content_settings_observer_qt.cpp52
-rw-r--r--src/core/renderer/content_settings_observer_qt.h12
-rw-r--r--src/core/renderer/print_web_view_helper_delegate_qt.cpp12
-rw-r--r--src/core/renderer/print_web_view_helper_delegate_qt.h9
-rw-r--r--src/core/renderer/render_frame_observer_qt.cpp21
-rw-r--r--src/core/renderer/render_frame_observer_qt.h11
-rw-r--r--src/core/renderer/render_thread_observer_qt.cpp6
-rw-r--r--src/core/renderer/render_thread_observer_qt.h7
-rw-r--r--src/core/renderer/render_view_observer_qt.cpp16
-rw-r--r--src/core/renderer/render_view_observer_qt.h7
-rw-r--r--src/core/renderer/user_resource_controller.cpp47
-rw-r--r--src/core/renderer/user_resource_controller.h3
-rw-r--r--src/core/renderer/web_channel_ipc_transport.cpp41
-rw-r--r--src/core/renderer/web_channel_ipc_transport.h8
-rw-r--r--src/core/renderer_host/render_view_observer_host_qt.cpp7
-rw-r--r--src/core/renderer_host/render_view_observer_host_qt.h10
-rw-r--r--src/core/renderer_host/resource_dispatcher_host_delegate_qt.cpp186
-rw-r--r--src/core/renderer_host/resource_dispatcher_host_delegate_qt.h77
-rw-r--r--src/core/renderer_host/user_resource_controller_host.cpp43
-rw-r--r--src/core/renderer_host/user_resource_controller_host.h3
-rw-r--r--src/core/renderer_host/web_channel_ipc_transport_host.cpp14
-rw-r--r--src/core/renderer_host/web_channel_ipc_transport_host.h6
-rw-r--r--src/core/web_contents_adapter.cpp11
-rw-r--r--src/core/web_contents_adapter.h1
-rw-r--r--src/core/web_contents_adapter_client.h1
-rw-r--r--src/core/web_contents_delegate_qt.cpp9
-rw-r--r--src/core/web_engine_context.cpp59
-rw-r--r--src/pdf/api/qpdfbookmarkmodel.h98
-rw-r--r--src/pdf/api/qpdfdestination.h84
-rw-r--r--src/pdf/api/qpdfdestination_p.h71
-rw-r--r--src/pdf/api/qpdfdocument.h136
-rw-r--r--src/pdf/api/qpdfdocument_p.h112
-rw-r--r--src/pdf/api/qpdfdocumentrenderoptions.h99
-rw-r--r--src/pdf/api/qpdflinkmodel_p.h106
-rw-r--r--src/pdf/api/qpdflinkmodel_p_p.h91
-rw-r--r--src/pdf/api/qpdfnamespace.h72
-rw-r--r--src/pdf/api/qpdfpagenavigation.h91
-rw-r--r--src/pdf/api/qpdfpagerenderer.h90
-rw-r--r--src/pdf/api/qpdfsearchmodel.h97
-rw-r--r--src/pdf/api/qpdfsearchmodel_p.h86
-rw-r--r--src/pdf/api/qpdfsearchresult.h75
-rw-r--r--src/pdf/api/qpdfsearchresult_p.h70
-rw-r--r--src/pdf/api/qpdfselection.h83
-rw-r--r--src/pdf/api/qpdfselection_p.h59
-rw-r--r--src/pdf/api/qtpdfglobal.h59
-rw-r--r--src/pdf/configure.json64
-rw-r--r--src/pdf/doc/qtpdf.qdocconf58
-rw-r--r--src/pdf/doc/snippets/qtpdf-build.cmake2
-rw-r--r--src/pdf/doc/snippets/qtpdf_build_snippet.qdoc35
-rw-r--r--src/pdf/doc/src/qtpdf-examples.qdoc37
-rw-r--r--src/pdf/doc/src/qtpdf-index.qdoc76
-rw-r--r--src/pdf/doc/src/qtpdf-module.qdoc50
-rw-r--r--src/pdf/gn_run.pro70
-rw-r--r--src/pdf/jsbridge.cpp38
-rw-r--r--src/pdf/pdf.pro16
-rw-r--r--src/pdf/pdfcore.pro93
-rw-r--r--src/pdf/pdfcore_generator.pro15
-rw-r--r--src/pdf/qpdfbookmarkmodel.cpp363
-rw-r--r--src/pdf/qpdfdestination.cpp140
-rw-r--r--src/pdf/qpdfdocument.cpp772
-rw-r--r--src/pdf/qpdfdocumentrenderoptions.qdoc139
-rw-r--r--src/pdf/qpdflinkmodel.cpp251
-rw-r--r--src/pdf/qpdfnamespace.qdoc74
-rw-r--r--src/pdf/qpdfpagenavigation.cpp314
-rw-r--r--src/pdf/qpdfpagerenderer.cpp359
-rw-r--r--src/pdf/qpdfsearchmodel.cpp310
-rw-r--r--src/pdf/qpdfsearchresult.cpp74
-rw-r--r--src/pdf/qpdfselection.cpp138
-rw-r--r--src/pdf/qtpdf.gni7
-rw-r--r--src/pdf/quick/plugin.cpp99
-rw-r--r--src/pdf/quick/plugins.qmltypes52
-rw-r--r--src/pdf/quick/qml/PdfMultiPageView.qml342
-rw-r--r--src/pdf/quick/qml/PdfPageView.qml273
-rw-r--r--src/pdf/quick/qml/PdfScrollablePageView.qml274
-rw-r--r--src/pdf/quick/qmldir4
-rw-r--r--src/pdf/quick/qquickpdfdocument.cpp306
-rw-r--r--src/pdf/quick/qquickpdfdocument_p.h137
-rw-r--r--src/pdf/quick/qquickpdflinkmodel.cpp132
-rw-r--r--src/pdf/quick/qquickpdflinkmodel_p.h87
-rw-r--r--src/pdf/quick/qquickpdfnavigationstack.cpp266
-rw-r--r--src/pdf/quick/qquickpdfnavigationstack_p.h102
-rw-r--r--src/pdf/quick/qquickpdfsearchmodel.cpp277
-rw-r--r--src/pdf/quick/qquickpdfsearchmodel_p.h110
-rw-r--r--src/pdf/quick/qquickpdfselection.cpp268
-rw-r--r--src/pdf/quick/qquickpdfselection_p.h122
-rw-r--r--src/pdf/quick/quick.pro34
-rw-r--r--src/pdf/quick/resources.qrc7
-rw-r--r--src/pdfwidgets/configure.json28
-rw-r--r--src/pdfwidgets/pdfwidgets.pro12
-rw-r--r--src/pdfwidgets/qpdfview.cpp493
-rw-r--r--src/pdfwidgets/qpdfview.h122
-rw-r--r--src/pdfwidgets/qpdfview_p.h119
-rw-r--r--src/pdfwidgets/qtpdfwidgetsglobal.h59
-rw-r--r--src/plugins/imageformats/imageformats.pro2
-rw-r--r--src/plugins/imageformats/pdf/main.cpp74
-rw-r--r--src/plugins/imageformats/pdf/pdf.json4
-rw-r--r--src/plugins/imageformats/pdf/pdf.pro11
-rw-r--r--src/plugins/imageformats/pdf/qpdfiohandler.cpp244
-rw-r--r--src/plugins/imageformats/pdf/qpdfiohandler_p.h89
-rw-r--r--src/plugins/plugins.pro3
-rw-r--r--src/process/QtWebEngineProcess.entitlements (renamed from src/process/Entitlements_mac.plist)0
-rw-r--r--src/process/process.pro5
-rw-r--r--src/src.pro19
-rw-r--r--src/tools/qwebengine_convert_dict/main.cpp32
-rw-r--r--src/webengine/api/qquickwebenginehistory.cpp14
-rw-r--r--src/webengine/api/qquickwebenginehistory_p.h1
-rw-r--r--src/webengine/api/qquickwebenginetestsupport.cpp2
-rw-r--r--src/webengine/api/qquickwebengineview.cpp12
-rw-r--r--src/webengine/api/qquickwebengineview_p.h7
-rw-r--r--src/webengine/api/qquickwebengineview_p_p.h1
-rw-r--r--src/webengine/doc/src/webengineview_lgpl.qdoc24
-rw-r--r--src/webengine/plugin/plugin.cpp2
-rw-r--r--src/webengine/plugin/plugins.qmltypes6
-rw-r--r--src/webenginewidgets/api/qwebenginepage.cpp31
-rw-r--r--src/webenginewidgets/api/qwebenginepage.h3
-rw-r--r--src/webenginewidgets/api/qwebenginepage_p.h1
-rw-r--r--sync.profile3
-rw-r--r--tests/auto/auto.pro5
-rw-r--r--tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp8
-rw-r--r--tests/auto/pdf/pdf.pro8
-rw-r--r--tests/auto/pdf/qpdfbookmarkmodel/pdf-sample.bookmarks.pdfbin0 -> 8925 bytes
-rw-r--r--tests/auto/pdf/qpdfbookmarkmodel/pdf-sample.bookmarks_pages.pdfbin0 -> 27523 bytes
-rw-r--r--tests/auto/pdf/qpdfbookmarkmodel/qpdfbookmarkmodel.pro5
-rw-r--r--tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp287
-rw-r--r--tests/auto/pdf/qpdfdocument/BLACKLIST6
-rw-r--r--tests/auto/pdf/qpdfdocument/pdf-sample.metadata.pdfbin0 -> 9298 bytes
-rw-r--r--tests/auto/pdf/qpdfdocument/pdf-sample.protected.pdfbin0 -> 9138 bytes
-rw-r--r--tests/auto/pdf/qpdfdocument/qpdfdocument.pro6
-rw-r--r--tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp386
-rw-r--r--tests/auto/pdf/qpdfpagenavigation/pdf-sample.pagenavigation.pdfbin0 -> 27523 bytes
-rw-r--r--tests/auto/pdf/qpdfpagenavigation/qpdfpagenavigation.pro5
-rw-r--r--tests/auto/pdf/qpdfpagenavigation/tst_qpdfpagenavigation.cpp200
-rw-r--r--tests/auto/pdf/qpdfpagerenderer/pdf-sample.pagerenderer.pdfbin0 -> 27523 bytes
-rw-r--r--tests/auto/pdf/qpdfpagerenderer/qpdfpagerenderer.pro5
-rw-r--r--tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp184
-rw-r--r--tests/auto/pdf/qpdfsearchmodel/qpdfsearchmodel.pro5
-rw-r--r--tests/auto/pdf/qpdfsearchmodel/test.pdfbin0 -> 80045 bytes
-rw-r--r--tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp69
-rw-r--r--tests/auto/quick/publicapi/tst_publicapi.cpp3
-rw-r--r--tests/auto/quick/qmltests/data/tst_navigationHistory.qml7
-rw-r--r--tests/auto/quick/qquickwebengineview/BLACKLIST4
-rw-r--r--tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp4
-rw-r--r--tests/auto/widgets/accessibility/tst_accessibility.cpp2
-rw-r--r--tests/auto/widgets/origins/BLACKLIST3
-rw-r--r--tests/auto/widgets/origins/tst_origins.cpp22
-rw-r--r--tests/auto/widgets/proxypac/proxypac.pro4
-rw-r--r--tests/auto/widgets/qwebenginepage/BLACKLIST10
-rw-r--r--tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp73
-rw-r--r--tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp45
-rw-r--r--tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp97
-rw-r--r--tests/auto/widgets/schemes/tst_schemes.cpp76
-rw-r--r--tests/auto/widgets/spellchecking/tst_spellchecking.cpp8
-rw-r--r--tests/auto/widgets/util.h2
-rw-r--r--tests/manual/quick/pdf/listview.qml95
-rw-r--r--tests/manual/quick/pdf/pessimizedListView.qml174
-rw-r--r--tests/manual/quick/pdf/simplest.qml66
-rw-r--r--tests/manual/quick/pdf/test.pdfbin0 -> 80045 bytes
-rw-r--r--tests/manual/quick/pdf/withdoc.qml206
-rwxr-xr-xtools/scripts/take_snapshot.py1
280 files changed, 17948 insertions, 2998 deletions
diff --git a/.qmake.conf b/.qmake.conf
index adf09de55..a942dacce 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -5,4 +5,4 @@ QTWEBENGINE_OUT_ROOT = $$shadowed($$PWD)
load(qt_build_config)
CONFIG += warning_clean
-MODULE_VERSION = 5.14.2
+MODULE_VERSION = 5.15.0
diff --git a/LICENSE.FDL b/LICENSE.FDL
new file mode 100644
index 000000000..086177c0a
--- /dev/null
+++ b/LICENSE.FDL
@@ -0,0 +1,450 @@
+ GNU Free Documentation License
+ Version 1.3, 3 November 2008
+
+
+ Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
+ <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+0. PREAMBLE
+
+The purpose of this License is to make a manual, textbook, or other
+functional and useful document "free" in the sense of freedom: to
+assure everyone the effective freedom to copy and redistribute it,
+with or without modifying it, either commercially or noncommercially.
+Secondarily, this License preserves for the author and publisher a way
+to get credit for their work, while not being considered responsible
+for modifications made by others.
+
+This License is a kind of "copyleft", which means that derivative
+works of the document must themselves be free in the same sense. It
+complements the GNU General Public License, which is a copyleft
+license designed for free software.
+
+We have designed this License in order to use it for manuals for free
+software, because free software needs free documentation: a free
+program should come with manuals providing the same freedoms that the
+software does. But this License is not limited to software manuals;
+it can be used for any textual work, regardless of subject matter or
+whether it is published as a printed book. We recommend this License
+principally for works whose purpose is instruction or reference.
+
+
+1. APPLICABILITY AND DEFINITIONS
+
+This License applies to any manual or other work, in any medium, that
+contains a notice placed by the copyright holder saying it can be
+distributed under the terms of this License. Such a notice grants a
+world-wide, royalty-free license, unlimited in duration, to use that
+work under the conditions stated herein. The "Document", below,
+refers to any such manual or work. Any member of the public is a
+licensee, and is addressed as "you". You accept the license if you
+copy, modify or distribute the work in a way requiring permission
+under copyright law.
+
+A "Modified Version" of the Document means any work containing the
+Document or a portion of it, either copied verbatim, or with
+modifications and/or translated into another language.
+
+A "Secondary Section" is a named appendix or a front-matter section of
+the Document that deals exclusively with the relationship of the
+publishers or authors of the Document to the Document's overall
+subject (or to related matters) and contains nothing that could fall
+directly within that overall subject. (Thus, if the Document is in
+part a textbook of mathematics, a Secondary Section may not explain
+any mathematics.) The relationship could be a matter of historical
+connection with the subject or with related matters, or of legal,
+commercial, philosophical, ethical or political position regarding
+them.
+
+The "Invariant Sections" are certain Secondary Sections whose titles
+are designated, as being those of Invariant Sections, in the notice
+that says that the Document is released under this License. If a
+section does not fit the above definition of Secondary then it is not
+allowed to be designated as Invariant. The Document may contain zero
+Invariant Sections. If the Document does not identify any Invariant
+Sections then there are none.
+
+The "Cover Texts" are certain short passages of text that are listed,
+as Front-Cover Texts or Back-Cover Texts, in the notice that says that
+the Document is released under this License. A Front-Cover Text may
+be at most 5 words, and a Back-Cover Text may be at most 25 words.
+
+A "Transparent" copy of the Document means a machine-readable copy,
+represented in a format whose specification is available to the
+general public, that is suitable for revising the document
+straightforwardly with generic text editors or (for images composed of
+pixels) generic paint programs or (for drawings) some widely available
+drawing editor, and that is suitable for input to text formatters or
+for automatic translation to a variety of formats suitable for input
+to text formatters. A copy made in an otherwise Transparent file
+format whose markup, or absence of markup, has been arranged to thwart
+or discourage subsequent modification by readers is not Transparent.
+An image format is not Transparent if used for any substantial amount
+of text. A copy that is not "Transparent" is called "Opaque".
+
+Examples of suitable formats for Transparent copies include plain
+ASCII without markup, Texinfo input format, LaTeX input format, SGML
+or XML using a publicly available DTD, and standard-conforming simple
+HTML, PostScript or PDF designed for human modification. Examples of
+transparent image formats include PNG, XCF and JPG. Opaque formats
+include proprietary formats that can be read and edited only by
+proprietary word processors, SGML or XML for which the DTD and/or
+processing tools are not generally available, and the
+machine-generated HTML, PostScript or PDF produced by some word
+processors for output purposes only.
+
+The "Title Page" means, for a printed book, the title page itself,
+plus such following pages as are needed to hold, legibly, the material
+this License requires to appear in the title page. For works in
+formats which do not have any title page as such, "Title Page" means
+the text near the most prominent appearance of the work's title,
+preceding the beginning of the body of the text.
+
+The "publisher" means any person or entity that distributes copies of
+the Document to the public.
+
+A section "Entitled XYZ" means a named subunit of the Document whose
+title either is precisely XYZ or contains XYZ in parentheses following
+text that translates XYZ in another language. (Here XYZ stands for a
+specific section name mentioned below, such as "Acknowledgements",
+"Dedications", "Endorsements", or "History".) To "Preserve the Title"
+of such a section when you modify the Document means that it remains a
+section "Entitled XYZ" according to this definition.
+
+The Document may include Warranty Disclaimers next to the notice which
+states that this License applies to the Document. These Warranty
+Disclaimers are considered to be included by reference in this
+License, but only as regards disclaiming warranties: any other
+implication that these Warranty Disclaimers may have is void and has
+no effect on the meaning of this License.
+
+2. VERBATIM COPYING
+
+You may copy and distribute the Document in any medium, either
+commercially or noncommercially, provided that this License, the
+copyright notices, and the license notice saying this License applies
+to the Document are reproduced in all copies, and that you add no
+other conditions whatsoever to those of this License. You may not use
+technical measures to obstruct or control the reading or further
+copying of the copies you make or distribute. However, you may accept
+compensation in exchange for copies. If you distribute a large enough
+number of copies you must also follow the conditions in section 3.
+
+You may also lend copies, under the same conditions stated above, and
+you may publicly display copies.
+
+
+3. COPYING IN QUANTITY
+
+If you publish printed copies (or copies in media that commonly have
+printed covers) of the Document, numbering more than 100, and the
+Document's license notice requires Cover Texts, you must enclose the
+copies in covers that carry, clearly and legibly, all these Cover
+Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
+the back cover. Both covers must also clearly and legibly identify
+you as the publisher of these copies. The front cover must present
+the full title with all words of the title equally prominent and
+visible. You may add other material on the covers in addition.
+Copying with changes limited to the covers, as long as they preserve
+the title of the Document and satisfy these conditions, can be treated
+as verbatim copying in other respects.
+
+If the required texts for either cover are too voluminous to fit
+legibly, you should put the first ones listed (as many as fit
+reasonably) on the actual cover, and continue the rest onto adjacent
+pages.
+
+If you publish or distribute Opaque copies of the Document numbering
+more than 100, you must either include a machine-readable Transparent
+copy along with each Opaque copy, or state in or with each Opaque copy
+a computer-network location from which the general network-using
+public has access to download using public-standard network protocols
+a complete Transparent copy of the Document, free of added material.
+If you use the latter option, you must take reasonably prudent steps,
+when you begin distribution of Opaque copies in quantity, to ensure
+that this Transparent copy will remain thus accessible at the stated
+location until at least one year after the last time you distribute an
+Opaque copy (directly or through your agents or retailers) of that
+edition to the public.
+
+It is requested, but not required, that you contact the authors of the
+Document well before redistributing any large number of copies, to
+give them a chance to provide you with an updated version of the
+Document.
+
+
+4. MODIFICATIONS
+
+You may copy and distribute a Modified Version of the Document under
+the conditions of sections 2 and 3 above, provided that you release
+the Modified Version under precisely this License, with the Modified
+Version filling the role of the Document, thus licensing distribution
+and modification of the Modified Version to whoever possesses a copy
+of it. In addition, you must do these things in the Modified Version:
+
+A. Use in the Title Page (and on the covers, if any) a title distinct
+ from that of the Document, and from those of previous versions
+ (which should, if there were any, be listed in the History section
+ of the Document). You may use the same title as a previous version
+ if the original publisher of that version gives permission.
+B. List on the Title Page, as authors, one or more persons or entities
+ responsible for authorship of the modifications in the Modified
+ Version, together with at least five of the principal authors of the
+ Document (all of its principal authors, if it has fewer than five),
+ unless they release you from this requirement.
+C. State on the Title page the name of the publisher of the
+ Modified Version, as the publisher.
+D. Preserve all the copyright notices of the Document.
+E. Add an appropriate copyright notice for your modifications
+ adjacent to the other copyright notices.
+F. Include, immediately after the copyright notices, a license notice
+ giving the public permission to use the Modified Version under the
+ terms of this License, in the form shown in the Addendum below.
+G. Preserve in that license notice the full lists of Invariant Sections
+ and required Cover Texts given in the Document's license notice.
+H. Include an unaltered copy of this License.
+I. Preserve the section Entitled "History", Preserve its Title, and add
+ to it an item stating at least the title, year, new authors, and
+ publisher of the Modified Version as given on the Title Page. If
+ there is no section Entitled "History" in the Document, create one
+ stating the title, year, authors, and publisher of the Document as
+ given on its Title Page, then add an item describing the Modified
+ Version as stated in the previous sentence.
+J. Preserve the network location, if any, given in the Document for
+ public access to a Transparent copy of the Document, and likewise
+ the network locations given in the Document for previous versions
+ it was based on. These may be placed in the "History" section.
+ You may omit a network location for a work that was published at
+ least four years before the Document itself, or if the original
+ publisher of the version it refers to gives permission.
+K. For any section Entitled "Acknowledgements" or "Dedications",
+ Preserve the Title of the section, and preserve in the section all
+ the substance and tone of each of the contributor acknowledgements
+ and/or dedications given therein.
+L. Preserve all the Invariant Sections of the Document,
+ unaltered in their text and in their titles. Section numbers
+ or the equivalent are not considered part of the section titles.
+M. Delete any section Entitled "Endorsements". Such a section
+ may not be included in the Modified Version.
+N. Do not retitle any existing section to be Entitled "Endorsements"
+ or to conflict in title with any Invariant Section.
+O. Preserve any Warranty Disclaimers.
+
+If the Modified Version includes new front-matter sections or
+appendices that qualify as Secondary Sections and contain no material
+copied from the Document, you may at your option designate some or all
+of these sections as invariant. To do this, add their titles to the
+list of Invariant Sections in the Modified Version's license notice.
+These titles must be distinct from any other section titles.
+
+You may add a section Entitled "Endorsements", provided it contains
+nothing but endorsements of your Modified Version by various
+parties--for example, statements of peer review or that the text has
+been approved by an organization as the authoritative definition of a
+standard.
+
+You may add a passage of up to five words as a Front-Cover Text, and a
+passage of up to 25 words as a Back-Cover Text, to the end of the list
+of Cover Texts in the Modified Version. Only one passage of
+Front-Cover Text and one of Back-Cover Text may be added by (or
+through arrangements made by) any one entity. If the Document already
+includes a cover text for the same cover, previously added by you or
+by arrangement made by the same entity you are acting on behalf of,
+you may not add another; but you may replace the old one, on explicit
+permission from the previous publisher that added the old one.
+
+The author(s) and publisher(s) of the Document do not by this License
+give permission to use their names for publicity for or to assert or
+imply endorsement of any Modified Version.
+
+
+5. COMBINING DOCUMENTS
+
+You may combine the Document with other documents released under this
+License, under the terms defined in section 4 above for modified
+versions, provided that you include in the combination all of the
+Invariant Sections of all of the original documents, unmodified, and
+list them all as Invariant Sections of your combined work in its
+license notice, and that you preserve all their Warranty Disclaimers.
+
+The combined work need only contain one copy of this License, and
+multiple identical Invariant Sections may be replaced with a single
+copy. If there are multiple Invariant Sections with the same name but
+different contents, make the title of each such section unique by
+adding at the end of it, in parentheses, the name of the original
+author or publisher of that section if known, or else a unique number.
+Make the same adjustment to the section titles in the list of
+Invariant Sections in the license notice of the combined work.
+
+In the combination, you must combine any sections Entitled "History"
+in the various original documents, forming one section Entitled
+"History"; likewise combine any sections Entitled "Acknowledgements",
+and any sections Entitled "Dedications". You must delete all sections
+Entitled "Endorsements".
+
+
+6. COLLECTIONS OF DOCUMENTS
+
+You may make a collection consisting of the Document and other
+documents released under this License, and replace the individual
+copies of this License in the various documents with a single copy
+that is included in the collection, provided that you follow the rules
+of this License for verbatim copying of each of the documents in all
+other respects.
+
+You may extract a single document from such a collection, and
+distribute it individually under this License, provided you insert a
+copy of this License into the extracted document, and follow this
+License in all other respects regarding verbatim copying of that
+document.
+
+
+7. AGGREGATION WITH INDEPENDENT WORKS
+
+A compilation of the Document or its derivatives with other separate
+and independent documents or works, in or on a volume of a storage or
+distribution medium, is called an "aggregate" if the copyright
+resulting from the compilation is not used to limit the legal rights
+of the compilation's users beyond what the individual works permit.
+When the Document is included in an aggregate, this License does not
+apply to the other works in the aggregate which are not themselves
+derivative works of the Document.
+
+If the Cover Text requirement of section 3 is applicable to these
+copies of the Document, then if the Document is less than one half of
+the entire aggregate, the Document's Cover Texts may be placed on
+covers that bracket the Document within the aggregate, or the
+electronic equivalent of covers if the Document is in electronic form.
+Otherwise they must appear on printed covers that bracket the whole
+aggregate.
+
+
+8. TRANSLATION
+
+Translation is considered a kind of modification, so you may
+distribute translations of the Document under the terms of section 4.
+Replacing Invariant Sections with translations requires special
+permission from their copyright holders, but you may include
+translations of some or all Invariant Sections in addition to the
+original versions of these Invariant Sections. You may include a
+translation of this License, and all the license notices in the
+Document, and any Warranty Disclaimers, provided that you also include
+the original English version of this License and the original versions
+of those notices and disclaimers. In case of a disagreement between
+the translation and the original version of this License or a notice
+or disclaimer, the original version will prevail.
+
+If a section in the Document is Entitled "Acknowledgements",
+"Dedications", or "History", the requirement (section 4) to Preserve
+its Title (section 1) will typically require changing the actual
+title.
+
+
+9. TERMINATION
+
+You may not copy, modify, sublicense, or distribute the Document
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense, or distribute it is void, and
+will automatically terminate your rights under this License.
+
+However, if you cease all violation of this License, then your license
+from a particular copyright holder is reinstated (a) provisionally,
+unless and until the copyright holder explicitly and finally
+terminates your license, and (b) permanently, if the copyright holder
+fails to notify you of the violation by some reasonable means prior to
+60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, receipt of a copy of some or all of the same material does
+not give you any rights to use it.
+
+
+10. FUTURE REVISIONS OF THIS LICENSE
+
+The Free Software Foundation may publish new, revised versions of the
+GNU Free Documentation 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. See
+http://www.gnu.org/copyleft/.
+
+Each version of the License is given a distinguishing version number.
+If the Document specifies that a particular numbered version of this
+License "or any later version" applies to it, you have the option of
+following the terms and conditions either of that specified version or
+of any later version that has been published (not as a draft) by the
+Free Software Foundation. If the Document does not specify a version
+number of this License, you may choose any version ever published (not
+as a draft) by the Free Software Foundation. If the Document
+specifies that a proxy can decide which future versions of this
+License can be used, that proxy's public statement of acceptance of a
+version permanently authorizes you to choose that version for the
+Document.
+
+11. RELICENSING
+
+"Massive Multiauthor Collaboration Site" (or "MMC Site") means any
+World Wide Web server that publishes copyrightable works and also
+provides prominent facilities for anybody to edit those works. A
+public wiki that anybody can edit is an example of such a server. A
+"Massive Multiauthor Collaboration" (or "MMC") contained in the site
+means any set of copyrightable works thus published on the MMC site.
+
+"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0
+license published by Creative Commons Corporation, a not-for-profit
+corporation with a principal place of business in San Francisco,
+California, as well as future copyleft versions of that license
+published by that same organization.
+
+"Incorporate" means to publish or republish a Document, in whole or in
+part, as part of another Document.
+
+An MMC is "eligible for relicensing" if it is licensed under this
+License, and if all works that were first published under this License
+somewhere other than this MMC, and subsequently incorporated in whole or
+in part into the MMC, (1) had no cover texts or invariant sections, and
+(2) were thus incorporated prior to November 1, 2008.
+
+The operator of an MMC Site may republish an MMC contained in the site
+under CC-BY-SA on the same site at any time before August 1, 2009,
+provided the MMC is eligible for relicensing.
+
+
+ADDENDUM: How to use this License for your documents
+
+To use this License in a document you have written, include a copy of
+the License in the document and put the following copyright and
+license notices just after the title page:
+
+ Copyright (c) YEAR YOUR NAME.
+ Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.3
+ or any later version published by the Free Software Foundation;
+ with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
+ A copy of the license is included in the section entitled "GNU
+ Free Documentation License".
+
+If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
+replace the "with...Texts." line with this:
+
+ with the Invariant Sections being LIST THEIR TITLES, with the
+ Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
+
+If you have Invariant Sections without Cover Texts, or some other
+combination of the three, merge those two alternatives to suit the
+situation.
+
+If your document contains nontrivial examples of program code, we
+recommend releasing these examples in parallel under your choice of
+free software license, such as the GNU General Public License,
+to permit their use in free software.
diff --git a/LICENSE.LGPLv3 b/LICENSE.LGPLv3
new file mode 100644
index 000000000..e200fb5b2
--- /dev/null
+++ b/LICENSE.LGPLv3
@@ -0,0 +1,174 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+
+ The Qt Toolkit is Copyright (C) 2015 The Qt Company Ltd.
+ Contact: http://www.qt.io/licensing/
+
+ You may use, distribute and copy the Qt Toolkit under the terms of
+ GNU Lesser General Public License version 3, which is displayed below.
+ This license makes reference to the version 3 of the GNU General
+ Public License, which you can find in the LICENSE.GPLv3 file.
+
+-------------------------------------------------------------------------
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright © 2007 Free Software Foundation, Inc. <http://fsf.org/>
+Everyone is permitted to copy and distribute verbatim copies of this
+licensedocument, but changing it is not allowed.
+
+This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+0. Additional Definitions.
+
+ As used herein, “this License” refers to version 3 of the GNU Lesser
+General Public License, and the “GNU GPL” refers to version 3 of the
+GNU General Public License.
+
+ “The Library” refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An “Application” is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A “Combined Work” is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the “Linked
+Version”.
+
+ The “Minimal Corresponding Source” for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The “Corresponding Application Code” for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort
+ to ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this
+ license document.
+
+4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that, taken
+together, effectively do not restrict modification of the portions of
+the Library contained in the Combined Work and reverse engineering for
+debugging such modifications, if you also do each of the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this
+ license document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of
+ this License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with
+ the Library. A suitable mechanism is one that (a) uses at run
+ time a copy of the Library already present on the user's
+ computer system, and (b) will operate properly with a modified
+ version of the Library that is interface-compatible with the
+ Linked Version.
+
+ e) Provide Installation Information, but only if you would
+ otherwise be required to provide such information under section 6
+ of the GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the Application
+ with a modified version of the Linked Version. (If you use option
+ 4d0, the Installation Information must accompany the Minimal
+ Corresponding Source and Corresponding Application Code. If you
+ use option 4d1, you must provide the Installation Information in
+ the manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.)
+
+5. Combined Libraries.
+
+ 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 that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities, conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of
+ it is a work based on the Library, and explaining where to find
+ the accompanying uncombined form of the same work.
+
+6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser 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
+as you received it specifies that a certain numbered version of the
+GNU Lesser General Public License “or any later version” applies to
+it, you have the option of following the terms and conditions either
+of that published version or of any later version published by the
+Free Software Foundation. If the Library as you received it does not
+specify a version number of the GNU Lesser General Public License,
+you may choose any version of the GNU Lesser General Public License
+ever published by the Free Software Foundation.
+
+If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the Library.
diff --git a/coin/qt-installer-package-config.json b/coin/qt-installer-package-config.json
new file mode 100644
index 000000000..6776a79af
--- /dev/null
+++ b/coin/qt-installer-package-config.json
@@ -0,0 +1,16 @@
+{
+ "version": "1",
+ "module-split": {
+ "qtpdf": [
+ "**/include/*QtPdf*/**/*",
+ "**/lib/cmake/Qt5Gui/*",
+ "**/lib/cmake/Qt5Pdf/*",
+ "**/lib/cmake/Qt5PdfWidgets/*",
+ "**/lib/pkgconfig/Qt5Pdf*",
+ "**/lib/libQt5Pdf*",
+ "**/mkspecs/modules/qt_lib_pdf*",
+ "**/plugins/**/*",
+ "**/qml/QtQuick/**/*"
+ ]
+ }
+}
diff --git a/configure.json b/configure.json
index 8caad24f0..a5dbad35b 100644
--- a/configure.json
+++ b/configure.json
@@ -3,6 +3,8 @@
"src/buildtools",
"src/core",
"src/webengine",
- "src/webenginewidgets"
+ "src/webenginewidgets",
+ "src/pdf",
+ "src/pdfwidgets"
]
}
diff --git a/configure.pri b/configure.pri
index 3a144e3f8..f89df5273 100644
--- a/configure.pri
+++ b/configure.pri
@@ -114,7 +114,7 @@ defineTest(qtwebengine_platformError) {
defineTest(qtConfTest_detectPlatform) {
QT_FOR_CONFIG += gui-private
- !linux:!win32:!macos {
+ !linux:!win32:!macos:!ios {
qtwebengine_platformError("Unknown platform. Qt WebEngine only supports Linux, Windows, and macOS.")
} else {
linux:qtwebengine_isLinuxPlatformSupported() {
@@ -126,6 +126,9 @@ defineTest(qtConfTest_detectPlatform) {
macos:qtwebengine_isMacOsPlatformSupported() {
$${1}.platform = "macos"
}
+ ios:qtwebengine_isMacOsPlatformSupported() {
+ $${1}.platform = "ios"
+ }
}
!isEmpty(platformError) {
@@ -423,21 +426,21 @@ defineTest(qtwebengine_isWindowsPlatformSupported) {
defineTest(qtwebengine_isMacOsPlatformSupported) {
# FIXME: Try to get it back down to 8.2 for building on OS X 10.11
!qtwebengine_isMinXcodeVersion(8, 3, 3) {
- qtwebengine_platformError("Using Xcode version $$QMAKE_XCODE_VERSION, but at least version 8.3.3 is required to build Qt WebEngine.")
+ qtwebengine_platformError("Using Xcode version $$QMAKE_XCODE_VERSION, but at least version 8.3.3 is required to build Qt WebEngine or Qt Pdf.")
return(false)
}
!clang|intel_icc {
- qtwebengine_platformError("Qt WebEngine on macOS requires Clang.")
+ qtwebengine_platformError("Qt WebEngine and Qt Pdf requires Clang.")
return(false)
}
# We require macOS 10.12 (darwin version 16.0.0) or newer.
darwin_major_version = $$section(QMAKE_HOST.version, ., 0, 0)
lessThan(darwin_major_version, 16) {
- qtwebengine_platformError("Building Qt WebEngine requires macOS version 10.12 or newer.")
+ qtwebengine_platformError("Building Qt WebEngine or Qt Pdf requires macOS version 10.12 or newer.")
return(false)
}
!qtwebengine_isMinOSXSDKVersion(10, 12): {
- qtwebengine_platformError("Building Qt WebEngine requires a macOS SDK version of 10.12 or newer. Current version is $${WEBENGINE_OSX_SDK_PRODUCT_VERSION}.")
+ qtwebengine_platformError("Building Qt WebEngine or Qt Pdf requires a macOS SDK version of 10.12 or newer. Current version is $${WEBENGINE_OSX_SDK_PRODUCT_VERSION}.")
return(false)
}
return(true)
diff --git a/examples/examples.pro b/examples/examples.pro
index 3dac9b0b7..b4f411aeb 100644
--- a/examples/examples.pro
+++ b/examples/examples.pro
@@ -3,3 +3,7 @@ TEMPLATE=subdirs
qtHaveModule(webengine): SUBDIRS += webengine
qtHaveModule(webenginewidgets): SUBDIRS += webenginewidgets
+
+qtHaveModule(pdfwidgets): SUBDIRS += pdfwidgets
+
+qtHaveModule(quick): qtHaveModule(pdf): qtHaveModule(pdfwidgets): SUBDIRS += pdf
diff --git a/examples/pdf/multipage/main.cpp b/examples/pdf/multipage/main.cpp
new file mode 100644
index 000000000..35aaa8c64
--- /dev/null
+++ b/examples/pdf/multipage/main.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char* argv[])
+{
+ QCoreApplication::setApplicationName("Qt Quick Multi-page PDF Viewer Example");
+ QCoreApplication::setOrganizationName("QtProject");
+ QCoreApplication::setApplicationVersion(QT_VERSION_STR);
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ QApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl(QStringLiteral("qrc:///pdfviewer/viewer.qml")));
+ if (app.arguments().count() > 1) {
+ QUrl toLoad = QUrl::fromUserInput(app.arguments().at(1));
+ engine.rootObjects().first()->setProperty("source", toLoad);
+ } else {
+ engine.rootObjects().first()->setProperty("source", QStringLiteral("resources/test.pdf"));
+ }
+
+
+ return app.exec();
+}
diff --git a/examples/pdf/multipage/multipage.pro b/examples/pdf/multipage/multipage.pro
new file mode 100644
index 000000000..5df9e653d
--- /dev/null
+++ b/examples/pdf/multipage/multipage.pro
@@ -0,0 +1,14 @@
+TEMPLATE = app
+
+QT += qml quick pdf widgets svg
+
+SOURCES += main.cpp
+
+RESOURCES += \
+ viewer.qrc
+EXAMPLE_FILES = \
+ viewer.qml
+
+target.path = $$[QT_INSTALL_EXAMPLES]/pdf/multipage
+INSTALLS += target
+
diff --git a/examples/pdf/multipage/resources/document-open.svg b/examples/pdf/multipage/resources/document-open.svg
new file mode 100644
index 000000000..bf23123a3
--- /dev/null
+++ b/examples/pdf/multipage/resources/document-open.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="m4 4v24h24l-1-1h-22v-13h5l3-3h14v16l1 1v-21h-10l-3-3z"
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/multipage/resources/edit-clear.svg b/examples/pdf/multipage/resources/edit-clear.svg
new file mode 100644
index 000000000..1c35aaf04
--- /dev/null
+++ b/examples/pdf/multipage/resources/edit-clear.svg
@@ -0,0 +1,15 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path
+ style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 8 3 L 0.94335938 10.056641 L 0 11 L 0.94335938 11.943359 L 8 19 L 20.333984 19 L 22 19 L 22 3 L 20.333984 3 L 8 3 z M 11.320312 7 L 14 9.6796875 L 16.679688 7 L 18 8.3203125 L 15.320312 11 L 18 13.679688 L 16.679688 15 L 14 12.320312 L 11.320312 15 L 10 13.679688 L 12.679688 11 L 10 8.3203125 L 11.320312 7 z "
+ class="ColorScheme-Text"
+ transform="translate(1,1)"
+ />
+</svg>
diff --git a/examples/pdf/multipage/resources/edit-copy.svg b/examples/pdf/multipage/resources/edit-copy.svg
new file mode 100644
index 000000000..9dd16877d
--- /dev/null
+++ b/examples/pdf/multipage/resources/edit-copy.svg
@@ -0,0 +1,15 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path
+ style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="m4 3v1 13h1 2 1v1 1h6l4-4v-1-7-1h-2v-3h-1-10-1m1 1h10v2h-7v1 9h-1-2v-12m4 3h8v7h-3-1v1 3h-4v-11"
+ class="ColorScheme-Text"
+ transform="translate(1,1)"
+ />
+</svg>
diff --git a/examples/pdf/multipage/resources/go-down-search.svg b/examples/pdf/multipage/resources/go-down-search.svg
new file mode 100644
index 000000000..ae17ab93b
--- /dev/null
+++ b/examples/pdf/multipage/resources/go-down-search.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 4.7070312 8 L 4 8.7070312 L 10.125 14.832031 L 12 16.707031 L 13.875 14.832031 L 20 8.7070312 L 19.292969 8 L 13.167969 14.125 L 12 15.292969 L 10.832031 14.125 L 4.7070312 8 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/multipage/resources/go-next-view-page.svg b/examples/pdf/multipage/resources/go-next-view-page.svg
new file mode 100644
index 000000000..e453ddbec
--- /dev/null
+++ b/examples/pdf/multipage/resources/go-next-view-page.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 8.7070312 4 L 8 4.7070312 L 14.125 10.832031 L 15.292969 12 L 14.125 13.167969 L 8 19.292969 L 8.7070312 20 L 14.832031 13.875 L 16.707031 12 L 14.832031 10.125 L 8.7070312 4 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/multipage/resources/go-previous-view-page.svg b/examples/pdf/multipage/resources/go-previous-view-page.svg
new file mode 100644
index 000000000..b032309e9
--- /dev/null
+++ b/examples/pdf/multipage/resources/go-previous-view-page.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 15.292969 4 L 9.1679688 10.125 L 7.2929688 12 L 9.1679688 13.875 L 15.292969 20 L 16 19.292969 L 9.875 13.167969 L 8.7070312 12 L 9.875 10.832031 L 16 4.7070312 L 15.292969 4 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/multipage/resources/go-up-search.svg b/examples/pdf/multipage/resources/go-up-search.svg
new file mode 100644
index 000000000..5cc155873
--- /dev/null
+++ b/examples/pdf/multipage/resources/go-up-search.svg
@@ -0,0 +1,8 @@
+<svg height="24" width="24" xmlns="http://www.w3.org/2000/svg">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ <path d="M4.707 16L4 15.293l8-8 8 8-.707.707L12 8.707" class="ColorScheme-Text" fill="currentColor"/>
+</svg>
diff --git a/examples/pdf/multipage/resources/rotate-left.svg b/examples/pdf/multipage/resources/rotate-left.svg
new file mode 100644
index 000000000..90ce53c9d
--- /dev/null
+++ b/examples/pdf/multipage/resources/rotate-left.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">
+ <g color="#000" font-weight="400" fill="#474747">
+ <path d="M2 9v1c0 .265.093.53.281.719l3.72 3.719 3.718-3.72c.188-.187.281-.453.281-.718V9H9c-.265 0-.53.093-.719.281l-2.28 2.281-2.282-2.28A1.015 1.015 0 0 0 3 9z"/>
+ <path d="M8.5 3A3.515 3.515 0 0 0 5 6.5V12h2V6.5C7 5.66 7.66 5 8.5 5H13V3z"/>
+ </g>
+</svg>
diff --git a/examples/pdf/multipage/resources/rotate-right.svg b/examples/pdf/multipage/resources/rotate-right.svg
new file mode 100644
index 000000000..7383d1c84
--- /dev/null
+++ b/examples/pdf/multipage/resources/rotate-right.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">
+ <g font-weight="400" fill="#474747">
+ <path d="M3 3v2h4.5C8.34 5 9 5.66 9 6.5V12h2V6.5C11 4.579 9.421 3 7.5 3z"/>
+ <path d="M6 9h1c.257 0 .529.13.719.313L10 11.592l2.281-2.28C12.471 9.13 12.743 9 13 9h1v1c0 .31-.09.552-.281.75L10 14.406 6.281 10.75C6.091 10.552 6 10.31 6 10z"/>
+ </g>
+</svg>
diff --git a/examples/pdf/multipage/resources/test.pdf b/examples/pdf/multipage/resources/test.pdf
new file mode 100644
index 000000000..a9dc1bc29
--- /dev/null
+++ b/examples/pdf/multipage/resources/test.pdf
Binary files differ
diff --git a/examples/pdf/multipage/resources/zoom-fit-best.svg b/examples/pdf/multipage/resources/zoom-fit-best.svg
new file mode 100644
index 000000000..adf302621
--- /dev/null
+++ b/examples/pdf/multipage/resources/zoom-fit-best.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#4d4d4d;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 4 4 L 4 5 L 4 8 L 5 8 L 5 5 L 8 5 L 8 4 L 5 4 L 4 4 z M 12 4 L 10 6 L 14 6 L 12 4 z M 16 4 L 16 5 L 19 5 L 19 8 L 20 8 L 20 5 L 20 4 L 19 4 L 16 4 z M 7 7 L 7 17 L 17 17 L 17 7 L 7 7 z M 8 8 L 16 8 L 16 16 L 8 16 L 8 8 z M 6 10 L 4 12 L 6 14 L 6 10 z M 18 10 L 18 14 L 20 12 L 18 10 z M 4 16 L 4 19 L 4 20 L 8 20 L 8 19 L 5 19 L 5 16 L 4 16 z M 19 16 L 19 19 L 16 19 L 16 20 L 20 20 L 20 19 L 20 16 L 19 16 z M 10 18 L 12 20 L 14 18 L 10 18 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/multipage/resources/zoom-fit-width.svg b/examples/pdf/multipage/resources/zoom-fit-width.svg
new file mode 100644
index 000000000..985ee5205
--- /dev/null
+++ b/examples/pdf/multipage/resources/zoom-fit-width.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#4d4d4d;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 7 7 L 7 17 L 17 17 L 17 7 L 7 7 z M 8 8 L 16 8 L 16 16 L 8 16 L 8 8 z M 6 10 L 4 12 L 6 14 L 6 10 z M 18 10 L 18 14 L 20 12 L 18 10 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/multipage/resources/zoom-in.svg b/examples/pdf/multipage/resources/zoom-in.svg
new file mode 100644
index 000000000..efdc9f17d
--- /dev/null
+++ b/examples/pdf/multipage/resources/zoom-in.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 4 4 L 4 6 L 5 6 L 5 5 L 6 5 L 6 4 L 4 4 z M 9 4 L 9 5 L 11 5 L 11 4 L 9 4 z M 13 4 L 13 5 L 15 5 L 15 4 L 13 4 z M 18 4 L 18 5 L 19 5 L 19 6 L 20 6 L 20 4 L 18 4 z M 12 8 L 12 9 L 14.292969 9 L 11 12.292969 L 11.707031 13 L 15 9.7070312 L 15 12 L 16 12 L 16 8 L 15 8 L 12 8 z M 4 9 L 4 11 L 5 11 L 5 9 L 4 9 z M 19 9 L 19 11 L 20 11 L 20 9 L 19 9 z M 19 13 L 19 15 L 20 15 L 20 13 L 19 13 z M 4 14 L 4 20 L 10 20 L 10 14 L 4 14 z M 5 15 L 9 15 L 9 19 L 5 19 L 5 15 z M 19 18 L 19 19 L 18 19 L 18 20 L 20 20 L 20 18 L 19 18 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/multipage/resources/zoom-original.svg b/examples/pdf/multipage/resources/zoom-original.svg
new file mode 100644
index 000000000..1b4080a03
--- /dev/null
+++ b/examples/pdf/multipage/resources/zoom-original.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 4 4 L 4 5 L 4 7 L 5 7 L 5 5 L 7 5 L 7 4 L 5 4 L 4 4 z M 17 4 L 17 5 L 19 5 L 19 7 L 20 7 L 20 5 L 20 4 L 19 4 L 17 4 z M 6 6 L 6 18 L 18 18 L 18 6 L 6 6 z M 7 7 L 17 7 L 17 17 L 7 17 L 7 7 z M 4 17 L 4 19 L 4 20 L 7 20 L 7 19 L 5 19 L 5 17 L 4 17 z M 19 17 L 19 19 L 17 19 L 17 20 L 20 20 L 20 17 L 19 17 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/multipage/resources/zoom-out.svg b/examples/pdf/multipage/resources/zoom-out.svg
new file mode 100644
index 000000000..fcde9e526
--- /dev/null
+++ b/examples/pdf/multipage/resources/zoom-out.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 4 4 L 4 11 L 5 11 L 5 5 L 19 5 L 19 19 L 13 19 L 13 20 L 20 20 L 20 19 L 20 5 L 20 4 L 5 4 L 4 4 z M 15.292969 8 L 12 11.292969 L 12 9 L 11 9 L 11 13 L 12 13 L 15 13 L 15 12 L 12.707031 12 L 16 8.7070312 L 15.292969 8 z M 4 14 L 4 16 L 5 16 L 5 15 L 6 15 L 6 14 L 4 14 z M 8 14 L 8 15 L 9 15 L 9 16 L 10 16 L 10 14 L 8 14 z M 4 18 L 4 20 L 6 20 L 6 19 L 5 19 L 5 18 L 4 18 z M 9 18 L 9 19 L 8 19 L 8 20 L 10 20 L 10 18 L 9 18 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/multipage/viewer.qml b/examples/pdf/multipage/viewer.qml
new file mode 100644
index 000000000..9e5f92407
--- /dev/null
+++ b/examples/pdf/multipage/viewer.qml
@@ -0,0 +1,328 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Layouts 1.14
+import QtQuick.Pdf 5.15
+import QtQuick.Shapes 1.14
+import QtQuick.Window 2.14
+import Qt.labs.platform 1.1 as Platform
+
+ApplicationWindow {
+ id: root
+ width: 800
+ height: 1024
+ color: "lightgrey"
+ title: document.title
+ visible: true
+ property string source // for main.cpp
+
+ header: ToolBar {
+ RowLayout {
+ anchors.fill: parent
+ anchors.rightMargin: 6
+ ToolButton {
+ action: Action {
+ shortcut: StandardKey.Open
+ icon.source: "resources/document-open.svg"
+ onTriggered: fileDialog.open()
+ }
+ }
+ ToolButton {
+ action: Action {
+ shortcut: StandardKey.ZoomIn
+ enabled: view.renderScale < 10
+ icon.source: "resources/zoom-in.svg"
+ onTriggered: view.renderScale *= Math.sqrt(2)
+ }
+ }
+ ToolButton {
+ action: Action {
+ shortcut: StandardKey.ZoomOut
+ enabled: view.renderScale > 0.1
+ icon.source: "resources/zoom-out.svg"
+ onTriggered: view.renderScale /= Math.sqrt(2)
+ }
+ }
+ ToolButton {
+ action: Action {
+ icon.source: "resources/zoom-fit-width.svg"
+ onTriggered: view.scaleToWidth(root.contentItem.width, root.contentItem.height)
+ }
+ }
+ ToolButton {
+ action: Action {
+ icon.source: "resources/zoom-fit-best.svg"
+ onTriggered: view.scaleToPage(root.contentItem.width, root.contentItem.height)
+ }
+ }
+ ToolButton {
+ action: Action {
+ shortcut: "Ctrl+0"
+ icon.source: "resources/zoom-original.svg"
+ onTriggered: view.resetScale()
+ }
+ }
+ ToolButton {
+ action: Action {
+ shortcut: "Ctrl+L"
+ icon.source: "resources/rotate-left.svg"
+ onTriggered: view.pageRotation -= 90
+ }
+ }
+ ToolButton {
+ action: Action {
+ shortcut: "Ctrl+R"
+ icon.source: "resources/rotate-right.svg"
+ onTriggered: view.pageRotation += 90
+ }
+ }
+ ToolButton {
+ action: Action {
+ icon.source: "resources/go-previous-view-page.svg"
+ enabled: view.backEnbled
+ onTriggered: view.back()
+ }
+ ToolTip.visible: enabled && hovered
+ ToolTip.delay: 2000
+ ToolTip.text: "go back"
+ }
+ SpinBox {
+ id: currentPageSB
+ from: 1
+ to: document.pageCount
+ editable: true
+ onValueModified: view.goToPage(value - 1)
+ Shortcut {
+ sequence: StandardKey.MoveToPreviousPage
+ onActivated: currentPageSB.value--
+ }
+ Shortcut {
+ sequence: StandardKey.MoveToNextPage
+ onActivated: currentPageSB.value++
+ }
+ }
+ ToolButton {
+ action: Action {
+ icon.source: "resources/go-next-view-page.svg"
+ enabled: view.forwardEnabled
+ onTriggered: view.forward()
+ }
+ ToolTip.visible: enabled && hovered
+ ToolTip.delay: 2000
+ ToolTip.text: "go forward"
+ }
+ ToolButton {
+ action: Action {
+ shortcut: StandardKey.Copy
+ icon.source: "resources/edit-copy.svg"
+ enabled: view.selectedText !== ""
+ onTriggered: view.copySelectionToClipboard()
+ }
+ }
+ Shortcut {
+ sequence: StandardKey.Find
+ onActivated: searchField.forceActiveFocus()
+ }
+ Shortcut {
+ sequence: StandardKey.Quit
+ onActivated: Qt.quit()
+ }
+ }
+ }
+
+ Platform.FileDialog {
+ id: fileDialog
+ title: "Open a PDF file"
+ nameFilters: [ "PDF files (*.pdf)" ]
+ onAccepted: document.source = file
+ }
+
+ Dialog {
+ id: passwordDialog
+ title: "Password"
+ standardButtons: Dialog.Ok | Dialog.Cancel
+ modal: true
+ closePolicy: Popup.CloseOnEscape
+ anchors.centerIn: parent
+ width: 300
+
+ TextField {
+ id: passwordField
+ placeholderText: qsTr("Please provide the password")
+ echoMode: TextInput.Password
+ width: parent.width
+ onAccepted: passwordDialog.accept()
+ }
+ onAccepted: document.password = passwordField.text
+ }
+
+ Dialog {
+ id: errorDialog
+ title: "Error loading " + document.source
+ standardButtons: Dialog.Ok
+ modal: true
+ closePolicy: Popup.CloseOnEscape
+ anchors.centerIn: parent
+ width: 300
+
+ Label {
+ id: errorField
+ text: document.error
+ }
+ }
+
+ PdfDocument {
+ id: document
+ source: Qt.resolvedUrl(root.source)
+ onStatusChanged: {
+ if (status === PdfDocument.Error) errorDialog.open()
+ view.document = (status === PdfDocument.Ready ? document : undefined)
+ }
+ onPasswordRequired: {
+ passwordDialog.open()
+ passwordField.forceActiveFocus()
+ }
+ }
+
+ PdfMultiPageView {
+ id: view
+ anchors.fill: parent
+ anchors.leftMargin: searchDrawer.position * searchDrawer.width
+ document: root.document
+ searchString: searchField.text
+ onCurrentPageChanged: currentPageSB.value = view.currentPage + 1
+ }
+
+ Drawer {
+ id: searchDrawer
+ edge: Qt.LeftEdge
+ modal: false
+ width: 300
+ y: root.header.height
+ height: view.height
+ dim: false
+ clip: true
+ ListView {
+ id: searchResultsList
+ anchors.fill: parent
+ anchors.margins: 2
+ model: view.searchModel
+ ScrollBar.vertical: ScrollBar { }
+ delegate: ItemDelegate {
+ width: parent ? parent.width : 0
+ text: "page " + (page + 1) + ": " + context
+ highlighted: ListView.isCurrentItem
+ onClicked: {
+ searchResultsList.currentIndex = index
+ view.goToLocation(page, location, 0)
+ view.searchModel.currentResult = indexOnPage
+ }
+ }
+ }
+ }
+
+ footer: ToolBar {
+ height: footerRow.implicitHeight
+ RowLayout {
+ id: footerRow
+ anchors.fill: parent
+ ToolButton {
+ action: Action {
+ icon.source: "resources/go-up-search.svg"
+ shortcut: StandardKey.FindPrevious
+ onTriggered: view.searchBack()
+ }
+ ToolTip.visible: enabled && hovered
+ ToolTip.delay: 2000
+ ToolTip.text: "find previous"
+ }
+ TextField {
+ id: searchField
+ placeholderText: "search"
+ Layout.minimumWidth: 150
+ Layout.fillWidth: true
+ onAccepted: searchDrawer.open()
+ Image {
+ visible: searchField.text !== ""
+ source: "resources/edit-clear.svg"
+ anchors {
+ right: parent.right
+ top: parent.top
+ bottom: parent.bottom
+ margins: 3
+ rightMargin: 5
+ }
+ TapHandler {
+ onTapped: searchField.clear()
+ }
+ }
+ }
+ ToolButton {
+ action: Action {
+ icon.source: "resources/go-down-search.svg"
+ shortcut: StandardKey.FindNext
+ onTriggered: view.searchForward()
+ }
+ ToolTip.visible: enabled && hovered
+ ToolTip.delay: 2000
+ ToolTip.text: "find next"
+ }
+ Label {
+ id: statusLabel
+ property size implicitPointSize: document.pagePointSize(view.currentPage)
+ text: "page " + (currentPageSB.value) + " of " + document.pageCount +
+ " scale " + view.renderScale.toFixed(2) +
+ " original " + implicitPointSize.width.toFixed(1) + "x" + implicitPointSize.height.toFixed(1) + " pt"
+ visible: document.pageCount > 0
+ }
+ }
+ }
+}
diff --git a/examples/pdf/multipage/viewer.qrc b/examples/pdf/multipage/viewer.qrc
new file mode 100644
index 000000000..1b6fa52f7
--- /dev/null
+++ b/examples/pdf/multipage/viewer.qrc
@@ -0,0 +1,20 @@
+<RCC>
+ <qresource prefix="/pdfviewer">
+ <file>viewer.qml</file>
+ <file>resources/document-open.svg</file>
+ <file>resources/edit-clear.svg</file>
+ <file>resources/edit-copy.svg</file>
+ <file>resources/go-down-search.svg</file>
+ <file>resources/go-next-view-page.svg</file>
+ <file>resources/go-previous-view-page.svg</file>
+ <file>resources/go-up-search.svg</file>
+ <file>resources/rotate-left.svg</file>
+ <file>resources/rotate-right.svg</file>
+ <file>resources/test.pdf</file>
+ <file>resources/zoom-in.svg</file>
+ <file>resources/zoom-fit-best.svg</file>
+ <file>resources/zoom-fit-width.svg</file>
+ <file>resources/zoom-original.svg</file>
+ <file>resources/zoom-out.svg</file>
+ </qresource>
+</RCC>
diff --git a/examples/pdf/pdf.pro b/examples/pdf/pdf.pro
new file mode 100644
index 000000000..7130f3560
--- /dev/null
+++ b/examples/pdf/pdf.pro
@@ -0,0 +1,3 @@
+TEMPLATE=subdirs
+
+SUBDIRS += pdfviewer multipage
diff --git a/examples/pdf/pdfviewer/main.cpp b/examples/pdf/pdfviewer/main.cpp
new file mode 100644
index 000000000..5f65e3061
--- /dev/null
+++ b/examples/pdf/pdfviewer/main.cpp
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char* argv[])
+{
+ QCoreApplication::setApplicationName("Qt Quick PDF Viewer Example");
+ QCoreApplication::setOrganizationName("QtProject");
+ QCoreApplication::setApplicationVersion(QT_VERSION_STR);
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ QApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl(QStringLiteral("qrc:///pdfviewer/viewer.qml")));
+ if (app.arguments().count() > 1) {
+ QUrl toLoad = QUrl::fromUserInput(app.arguments().at(1));
+ engine.rootObjects().first()->setProperty("source", toLoad);
+ } else {
+ engine.rootObjects().first()->setProperty("source", QStringLiteral("resources/test.pdf"));
+ }
+
+ return app.exec();
+}
diff --git a/examples/pdf/pdfviewer/pdfviewer.pro b/examples/pdf/pdfviewer/pdfviewer.pro
new file mode 100644
index 000000000..b8817c9be
--- /dev/null
+++ b/examples/pdf/pdfviewer/pdfviewer.pro
@@ -0,0 +1,14 @@
+TEMPLATE = app
+
+QT += qml quick pdf widgets svg
+
+SOURCES += main.cpp
+
+RESOURCES += \
+ viewer.qrc
+EXAMPLE_FILES = \
+ viewer.qml
+
+target.path = $$[QT_INSTALL_EXAMPLES]/pdf/pdfviewer
+INSTALLS += target
+
diff --git a/examples/pdf/pdfviewer/resources/document-open.svg b/examples/pdf/pdfviewer/resources/document-open.svg
new file mode 100644
index 000000000..bf23123a3
--- /dev/null
+++ b/examples/pdf/pdfviewer/resources/document-open.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="m4 4v24h24l-1-1h-22v-13h5l3-3h14v16l1 1v-21h-10l-3-3z"
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/pdfviewer/resources/edit-clear.svg b/examples/pdf/pdfviewer/resources/edit-clear.svg
new file mode 100644
index 000000000..1c35aaf04
--- /dev/null
+++ b/examples/pdf/pdfviewer/resources/edit-clear.svg
@@ -0,0 +1,15 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path
+ style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 8 3 L 0.94335938 10.056641 L 0 11 L 0.94335938 11.943359 L 8 19 L 20.333984 19 L 22 19 L 22 3 L 20.333984 3 L 8 3 z M 11.320312 7 L 14 9.6796875 L 16.679688 7 L 18 8.3203125 L 15.320312 11 L 18 13.679688 L 16.679688 15 L 14 12.320312 L 11.320312 15 L 10 13.679688 L 12.679688 11 L 10 8.3203125 L 11.320312 7 z "
+ class="ColorScheme-Text"
+ transform="translate(1,1)"
+ />
+</svg>
diff --git a/examples/pdf/pdfviewer/resources/edit-copy.svg b/examples/pdf/pdfviewer/resources/edit-copy.svg
new file mode 100644
index 000000000..9dd16877d
--- /dev/null
+++ b/examples/pdf/pdfviewer/resources/edit-copy.svg
@@ -0,0 +1,15 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path
+ style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="m4 3v1 13h1 2 1v1 1h6l4-4v-1-7-1h-2v-3h-1-10-1m1 1h10v2h-7v1 9h-1-2v-12m4 3h8v7h-3-1v1 3h-4v-11"
+ class="ColorScheme-Text"
+ transform="translate(1,1)"
+ />
+</svg>
diff --git a/examples/pdf/pdfviewer/resources/go-down-search.svg b/examples/pdf/pdfviewer/resources/go-down-search.svg
new file mode 100644
index 000000000..ae17ab93b
--- /dev/null
+++ b/examples/pdf/pdfviewer/resources/go-down-search.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 4.7070312 8 L 4 8.7070312 L 10.125 14.832031 L 12 16.707031 L 13.875 14.832031 L 20 8.7070312 L 19.292969 8 L 13.167969 14.125 L 12 15.292969 L 10.832031 14.125 L 4.7070312 8 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/pdfviewer/resources/go-next-view-page.svg b/examples/pdf/pdfviewer/resources/go-next-view-page.svg
new file mode 100644
index 000000000..e453ddbec
--- /dev/null
+++ b/examples/pdf/pdfviewer/resources/go-next-view-page.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 8.7070312 4 L 8 4.7070312 L 14.125 10.832031 L 15.292969 12 L 14.125 13.167969 L 8 19.292969 L 8.7070312 20 L 14.832031 13.875 L 16.707031 12 L 14.832031 10.125 L 8.7070312 4 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/pdfviewer/resources/go-previous-view-page.svg b/examples/pdf/pdfviewer/resources/go-previous-view-page.svg
new file mode 100644
index 000000000..b032309e9
--- /dev/null
+++ b/examples/pdf/pdfviewer/resources/go-previous-view-page.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 15.292969 4 L 9.1679688 10.125 L 7.2929688 12 L 9.1679688 13.875 L 15.292969 20 L 16 19.292969 L 9.875 13.167969 L 8.7070312 12 L 9.875 10.832031 L 16 4.7070312 L 15.292969 4 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/pdfviewer/resources/go-up-search.svg b/examples/pdf/pdfviewer/resources/go-up-search.svg
new file mode 100644
index 000000000..5cc155873
--- /dev/null
+++ b/examples/pdf/pdfviewer/resources/go-up-search.svg
@@ -0,0 +1,8 @@
+<svg height="24" width="24" xmlns="http://www.w3.org/2000/svg">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ <path d="M4.707 16L4 15.293l8-8 8 8-.707.707L12 8.707" class="ColorScheme-Text" fill="currentColor"/>
+</svg>
diff --git a/examples/pdf/pdfviewer/resources/rotate-left.svg b/examples/pdf/pdfviewer/resources/rotate-left.svg
new file mode 100644
index 000000000..90ce53c9d
--- /dev/null
+++ b/examples/pdf/pdfviewer/resources/rotate-left.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">
+ <g color="#000" font-weight="400" fill="#474747">
+ <path d="M2 9v1c0 .265.093.53.281.719l3.72 3.719 3.718-3.72c.188-.187.281-.453.281-.718V9H9c-.265 0-.53.093-.719.281l-2.28 2.281-2.282-2.28A1.015 1.015 0 0 0 3 9z"/>
+ <path d="M8.5 3A3.515 3.515 0 0 0 5 6.5V12h2V6.5C7 5.66 7.66 5 8.5 5H13V3z"/>
+ </g>
+</svg>
diff --git a/examples/pdf/pdfviewer/resources/rotate-right.svg b/examples/pdf/pdfviewer/resources/rotate-right.svg
new file mode 100644
index 000000000..7383d1c84
--- /dev/null
+++ b/examples/pdf/pdfviewer/resources/rotate-right.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">
+ <g font-weight="400" fill="#474747">
+ <path d="M3 3v2h4.5C8.34 5 9 5.66 9 6.5V12h2V6.5C11 4.579 9.421 3 7.5 3z"/>
+ <path d="M6 9h1c.257 0 .529.13.719.313L10 11.592l2.281-2.28C12.471 9.13 12.743 9 13 9h1v1c0 .31-.09.552-.281.75L10 14.406 6.281 10.75C6.091 10.552 6 10.31 6 10z"/>
+ </g>
+</svg>
diff --git a/examples/pdf/pdfviewer/resources/test.pdf b/examples/pdf/pdfviewer/resources/test.pdf
new file mode 100644
index 000000000..a9dc1bc29
--- /dev/null
+++ b/examples/pdf/pdfviewer/resources/test.pdf
Binary files differ
diff --git a/examples/pdf/pdfviewer/resources/zoom-fit-best.svg b/examples/pdf/pdfviewer/resources/zoom-fit-best.svg
new file mode 100644
index 000000000..adf302621
--- /dev/null
+++ b/examples/pdf/pdfviewer/resources/zoom-fit-best.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#4d4d4d;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 4 4 L 4 5 L 4 8 L 5 8 L 5 5 L 8 5 L 8 4 L 5 4 L 4 4 z M 12 4 L 10 6 L 14 6 L 12 4 z M 16 4 L 16 5 L 19 5 L 19 8 L 20 8 L 20 5 L 20 4 L 19 4 L 16 4 z M 7 7 L 7 17 L 17 17 L 17 7 L 7 7 z M 8 8 L 16 8 L 16 16 L 8 16 L 8 8 z M 6 10 L 4 12 L 6 14 L 6 10 z M 18 10 L 18 14 L 20 12 L 18 10 z M 4 16 L 4 19 L 4 20 L 8 20 L 8 19 L 5 19 L 5 16 L 4 16 z M 19 16 L 19 19 L 16 19 L 16 20 L 20 20 L 20 19 L 20 16 L 19 16 z M 10 18 L 12 20 L 14 18 L 10 18 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/pdfviewer/resources/zoom-fit-width.svg b/examples/pdf/pdfviewer/resources/zoom-fit-width.svg
new file mode 100644
index 000000000..985ee5205
--- /dev/null
+++ b/examples/pdf/pdfviewer/resources/zoom-fit-width.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#4d4d4d;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 7 7 L 7 17 L 17 17 L 17 7 L 7 7 z M 8 8 L 16 8 L 16 16 L 8 16 L 8 8 z M 6 10 L 4 12 L 6 14 L 6 10 z M 18 10 L 18 14 L 20 12 L 18 10 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/pdfviewer/resources/zoom-in.svg b/examples/pdf/pdfviewer/resources/zoom-in.svg
new file mode 100644
index 000000000..efdc9f17d
--- /dev/null
+++ b/examples/pdf/pdfviewer/resources/zoom-in.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 4 4 L 4 6 L 5 6 L 5 5 L 6 5 L 6 4 L 4 4 z M 9 4 L 9 5 L 11 5 L 11 4 L 9 4 z M 13 4 L 13 5 L 15 5 L 15 4 L 13 4 z M 18 4 L 18 5 L 19 5 L 19 6 L 20 6 L 20 4 L 18 4 z M 12 8 L 12 9 L 14.292969 9 L 11 12.292969 L 11.707031 13 L 15 9.7070312 L 15 12 L 16 12 L 16 8 L 15 8 L 12 8 z M 4 9 L 4 11 L 5 11 L 5 9 L 4 9 z M 19 9 L 19 11 L 20 11 L 20 9 L 19 9 z M 19 13 L 19 15 L 20 15 L 20 13 L 19 13 z M 4 14 L 4 20 L 10 20 L 10 14 L 4 14 z M 5 15 L 9 15 L 9 19 L 5 19 L 5 15 z M 19 18 L 19 19 L 18 19 L 18 20 L 20 20 L 20 18 L 19 18 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/pdfviewer/resources/zoom-original.svg b/examples/pdf/pdfviewer/resources/zoom-original.svg
new file mode 100644
index 000000000..1b4080a03
--- /dev/null
+++ b/examples/pdf/pdfviewer/resources/zoom-original.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 4 4 L 4 5 L 4 7 L 5 7 L 5 5 L 7 5 L 7 4 L 5 4 L 4 4 z M 17 4 L 17 5 L 19 5 L 19 7 L 20 7 L 20 5 L 20 4 L 19 4 L 17 4 z M 6 6 L 6 18 L 18 18 L 18 6 L 6 6 z M 7 7 L 17 7 L 17 17 L 7 17 L 7 7 z M 4 17 L 4 19 L 4 20 L 7 20 L 7 19 L 5 19 L 5 17 L 4 17 z M 19 17 L 19 19 L 17 19 L 17 20 L 20 20 L 20 17 L 19 17 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/pdfviewer/resources/zoom-out.svg b/examples/pdf/pdfviewer/resources/zoom-out.svg
new file mode 100644
index 000000000..fcde9e526
--- /dev/null
+++ b/examples/pdf/pdfviewer/resources/zoom-out.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 4 4 L 4 11 L 5 11 L 5 5 L 19 5 L 19 19 L 13 19 L 13 20 L 20 20 L 20 19 L 20 5 L 20 4 L 5 4 L 4 4 z M 15.292969 8 L 12 11.292969 L 12 9 L 11 9 L 11 13 L 12 13 L 15 13 L 15 12 L 12.707031 12 L 16 8.7070312 L 15.292969 8 z M 4 14 L 4 16 L 5 16 L 5 15 L 6 15 L 6 14 L 4 14 z M 8 14 L 8 15 L 9 15 L 9 16 L 10 16 L 10 14 L 8 14 z M 4 18 L 4 20 L 6 20 L 6 19 L 5 19 L 5 18 L 4 18 z M 9 18 L 9 19 L 8 19 L 8 20 L 10 20 L 10 18 L 9 18 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/pdfviewer/viewer.qml b/examples/pdf/pdfviewer/viewer.qml
new file mode 100644
index 000000000..e3bb4b474
--- /dev/null
+++ b/examples/pdf/pdfviewer/viewer.qml
@@ -0,0 +1,302 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Layouts 1.14
+import QtQuick.Pdf 5.15
+import QtQuick.Shapes 1.14
+import QtQuick.Window 2.14
+import Qt.labs.animation 1.0
+import Qt.labs.platform 1.1 as Platform
+
+ApplicationWindow {
+ id: root
+ width: 800
+ height: 1024
+ color: "lightgrey"
+ title: document.title
+ visible: true
+ property string source // for main.cpp
+ property real scaleStep: Math.sqrt(2)
+
+ header: ToolBar {
+ RowLayout {
+ anchors.fill: parent
+ anchors.rightMargin: 6
+ ToolButton {
+ action: Action {
+ shortcut: StandardKey.Open
+ icon.source: "resources/document-open.svg"
+ onTriggered: fileDialog.open()
+ }
+ }
+ ToolButton {
+ action: Action {
+ shortcut: StandardKey.ZoomIn
+ enabled: view.sourceSize.width < 10000
+ icon.source: "resources/zoom-in.svg"
+ onTriggered: view.renderScale *= root.scaleStep
+ }
+ }
+ ToolButton {
+ action: Action {
+ shortcut: StandardKey.ZoomOut
+ enabled: view.sourceSize.width > 50
+ icon.source: "resources/zoom-out.svg"
+ onTriggered: view.renderScale /= root.scaleStep
+ }
+ }
+ ToolButton {
+ action: Action {
+ icon.source: "resources/zoom-fit-width.svg"
+ onTriggered: view.scaleToWidth(root.contentItem.width, root.contentItem.height)
+ }
+ }
+ ToolButton {
+ action: Action {
+ icon.source: "resources/zoom-fit-best.svg"
+ onTriggered: view.scaleToPage(root.contentItem.width, root.contentItem.height)
+ }
+ }
+ ToolButton {
+ action: Action {
+ shortcut: "Ctrl+0"
+ icon.source: "resources/zoom-original.svg"
+ onTriggered: view.resetScale()
+ }
+ }
+ ToolButton {
+ action: Action {
+ shortcut: "Ctrl+L"
+ icon.source: "resources/rotate-left.svg"
+ onTriggered: view.pageRotation -= 90
+ }
+ }
+ ToolButton {
+ action: Action {
+ shortcut: "Ctrl+R"
+ icon.source: "resources/rotate-right.svg"
+ onTriggered: view.pageRotation += 90
+ }
+ }
+ ToolButton {
+ action: Action {
+ icon.source: "resources/go-previous-view-page.svg"
+ enabled: view.backEnabled
+ onTriggered: view.back()
+ }
+ ToolTip.visible: enabled && hovered
+ ToolTip.delay: 2000
+ ToolTip.text: "go back"
+ }
+ SpinBox {
+ id: currentPageSB
+ from: 1
+ to: document.pageCount
+ editable: true
+ value: view.currentPage + 1
+ onValueModified: view.goToPage(value - 1)
+ Shortcut {
+ sequence: StandardKey.MoveToPreviousPage
+ onActivated: view.goToPage(currentPageSB.value - 2)
+ }
+ Shortcut {
+ sequence: StandardKey.MoveToNextPage
+ onActivated: view.goToPage(currentPageSB.value)
+ }
+ }
+ ToolButton {
+ action: Action {
+ icon.source: "resources/go-next-view-page.svg"
+ enabled: view.forwardEnabled
+ onTriggered: view.forward()
+ }
+ ToolTip.visible: enabled && hovered
+ ToolTip.delay: 2000
+ ToolTip.text: "go forward"
+ }
+ ToolButton {
+ action: Action {
+ shortcut: StandardKey.Copy
+ icon.source: "resources/edit-copy.svg"
+ enabled: view.selectedText !== ""
+ onTriggered: view.copySelectionToClipboard()
+ }
+ }
+ Shortcut {
+ sequence: StandardKey.Find
+ onActivated: searchField.forceActiveFocus()
+ }
+ Shortcut {
+ sequence: StandardKey.Quit
+ onActivated: Qt.quit()
+ }
+ }
+ }
+
+ Platform.FileDialog {
+ id: fileDialog
+ title: "Open a PDF file"
+ nameFilters: [ "PDF files (*.pdf)" ]
+ onAccepted: document.source = file
+ }
+
+ Dialog {
+ id: errorDialog
+ title: "Error loading " + document.source
+ standardButtons: Dialog.Ok
+ modal: true
+ closePolicy: Popup.CloseOnEscape
+ anchors.centerIn: parent
+ width: 300
+
+ Label {
+ id: errorField
+ text: document.error
+ }
+ }
+
+ PdfScrollablePageView {
+ id: view
+ anchors.fill: parent
+ document: PdfDocument {
+ id: document
+ source: Qt.resolvedUrl(root.source)
+ onStatusChanged: if (status === PdfDocument.Error) errorDialog.open()
+ }
+ searchString: searchField.text
+ }
+
+ Drawer {
+ id: searchDrawer
+ edge: Qt.LeftEdge
+ modal: false
+ width: 300
+ y: root.header.height
+ height: view.height
+ dim: false
+ clip: true
+ ListView {
+ id: searchResultsList
+ anchors.fill: parent
+ anchors.margins: 2
+ model: view.searchModel
+ ScrollBar.vertical: ScrollBar { }
+ delegate: ItemDelegate {
+ width: parent ? parent.width : 0
+ text: "page " + (page + 1) + ": " + context
+ highlighted: ListView.isCurrentItem
+ onClicked: {
+ searchResultsList.currentIndex = index
+ view.goToLocation(page, location, 0)
+ view.searchModel.currentResult = indexOnPage
+ }
+ }
+ }
+ }
+
+ footer: ToolBar {
+ height: footerRow.implicitHeight
+ RowLayout {
+ id: footerRow
+ anchors.fill: parent
+ ToolButton {
+ action: Action {
+ icon.source: "resources/go-up-search.svg"
+ shortcut: StandardKey.FindPrevious
+ onTriggered: view.searchBack()
+ }
+ ToolTip.visible: enabled && hovered
+ ToolTip.delay: 2000
+ ToolTip.text: "find previous"
+ }
+ TextField {
+ id: searchField
+ placeholderText: "search"
+ Layout.minimumWidth: 150
+ Layout.maximumWidth: 300
+ Layout.fillWidth: true
+ onAccepted: searchDrawer.open()
+ Image {
+ visible: searchField.text !== ""
+ source: "resources/edit-clear.svg"
+ anchors {
+ right: parent.right
+ top: parent.top
+ bottom: parent.bottom
+ margins: 3
+ rightMargin: 5
+ }
+ TapHandler {
+ onTapped: searchField.clear()
+ }
+ }
+ }
+ ToolButton {
+ action: Action {
+ icon.source: "resources/go-down-search.svg"
+ shortcut: StandardKey.FindNext
+ onTriggered: view.searchForward()
+ }
+ ToolTip.visible: enabled && hovered
+ ToolTip.delay: 2000
+ ToolTip.text: "find next"
+ }
+ Label {
+ Layout.fillWidth: true
+ property size implicitPointSize: document.pagePointSize(view.currentPage)
+ text: "page " + (view.currentPage + 1) + " of " + document.pageCount +
+ " scale " + view.renderScale.toFixed(2) +
+ " original " + implicitPointSize.width.toFixed(1) + "x" + implicitPointSize.height.toFixed(1) + "pts"
+ visible: document.status === PdfDocument.Ready
+ }
+ }
+ }
+}
diff --git a/examples/pdf/pdfviewer/viewer.qrc b/examples/pdf/pdfviewer/viewer.qrc
new file mode 100644
index 000000000..1b6fa52f7
--- /dev/null
+++ b/examples/pdf/pdfviewer/viewer.qrc
@@ -0,0 +1,20 @@
+<RCC>
+ <qresource prefix="/pdfviewer">
+ <file>viewer.qml</file>
+ <file>resources/document-open.svg</file>
+ <file>resources/edit-clear.svg</file>
+ <file>resources/edit-copy.svg</file>
+ <file>resources/go-down-search.svg</file>
+ <file>resources/go-next-view-page.svg</file>
+ <file>resources/go-previous-view-page.svg</file>
+ <file>resources/go-up-search.svg</file>
+ <file>resources/rotate-left.svg</file>
+ <file>resources/rotate-right.svg</file>
+ <file>resources/test.pdf</file>
+ <file>resources/zoom-in.svg</file>
+ <file>resources/zoom-fit-best.svg</file>
+ <file>resources/zoom-fit-width.svg</file>
+ <file>resources/zoom-original.svg</file>
+ <file>resources/zoom-out.svg</file>
+ </qresource>
+</RCC>
diff --git a/examples/pdfwidgets/pdfviewer/doc/src/pdfviewer.qdoc b/examples/pdfwidgets/pdfviewer/doc/src/pdfviewer.qdoc
new file mode 100644
index 000000000..9c7b3deed
--- /dev/null
+++ b/examples/pdfwidgets/pdfviewer/doc/src/pdfviewer.qdoc
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \example pdfviewer
+ \ingroup qtpdf-examples
+
+ \title PDF Viewer Example
+ \brief Renders PDF documents.
+
+ \image pdfviewer.png
+
+ \e {PDF Viewer} demonstrates how to use the \l QPdfDocument class to render
+ PDF documents and the \l QPdfPageNavigation 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}.
+
+ \include examples-run.qdocinc
+
+ \section1 Creating the Main Window
+
+ The MainWindow class inherits the QMainWindow class:
+
+ \quotefromfile pdfviewer/mainwindow.h
+ \skipto public QMainWindow
+ \printuntil ~MainWindow()
+
+ The class declares public and private slots that match the actions of the
+ selectors:
+
+ \printuntil on_actionContinuous_triggered()
+
+ The actual layout of the main window is specified in a \c{.ui} file. The
+ widgets and actions are available at runtime in the \c ui member variable.
+
+ \printuntil Ui::
+
+ The \c m_zoomSelector variable holds the zoom selector and the
+ \c m_pageSelector holds the page selector. The \c m_document
+ variable is an instance of the QPdfDocument class that contains
+ the PDF document.
+
+ \printuntil }
+
+ The actual setup of the different objects is done in the MainWindow
+ constructor:
+
+ \quotefromfile pdfviewer/mainwindow.cpp
+ \skipto MainWindow::
+ \printuntil {
+
+ The constructor first calls \c setupUi() to construct the zoom and page
+ selectors according to the UI file. We set the maximum width of the
+ selectors.
+
+ \printuntil addWidget(m_pageSelector)
+
+ We use the \l QPdfPageNavigation class to handle the navigation through a
+ PDF document:
+
+ \printuntil setPageNavigation
+
+ We connect the \c zoomModeChanged and \c zoomFactor changed signals of the
+ PDF view to the functions that reset the zoom selector:
+
+ \printuntil reset()
+
+ We then load the PDF document to the viewer:
+
+ \dots
+ \skipto pdfView
+ \printuntil ;
+
+ Finally, we connect the \c zoomFactorChanged signal to the function that
+ sets the value of the zoom selector:
+
+ \printuntil }
+
+ \section1 Files and Attributions
+*/
diff --git a/examples/pdfwidgets/pdfviewer/images/busy.png b/examples/pdfwidgets/pdfviewer/images/busy.png
new file mode 100644
index 000000000..69056c479
--- /dev/null
+++ b/examples/pdfwidgets/pdfviewer/images/busy.png
Binary files differ
diff --git a/examples/pdfwidgets/pdfviewer/images/fileopen.png b/examples/pdfwidgets/pdfviewer/images/fileopen.png
new file mode 100644
index 000000000..33e0d6394
--- /dev/null
+++ b/examples/pdfwidgets/pdfviewer/images/fileopen.png
Binary files differ
diff --git a/examples/pdfwidgets/pdfviewer/images/go-next-24.png b/examples/pdfwidgets/pdfviewer/images/go-next-24.png
new file mode 100644
index 000000000..9a55ef3d8
--- /dev/null
+++ b/examples/pdfwidgets/pdfviewer/images/go-next-24.png
Binary files differ
diff --git a/examples/pdfwidgets/pdfviewer/images/go-previous-24.png b/examples/pdfwidgets/pdfviewer/images/go-previous-24.png
new file mode 100644
index 000000000..2ea769eb8
--- /dev/null
+++ b/examples/pdfwidgets/pdfviewer/images/go-previous-24.png
Binary files differ
diff --git a/examples/pdfwidgets/pdfviewer/images/zoom-in-24.png b/examples/pdfwidgets/pdfviewer/images/zoom-in-24.png
new file mode 100644
index 000000000..d29b142b6
--- /dev/null
+++ b/examples/pdfwidgets/pdfviewer/images/zoom-in-24.png
Binary files differ
diff --git a/examples/pdfwidgets/pdfviewer/images/zoom-in-32.png b/examples/pdfwidgets/pdfviewer/images/zoom-in-32.png
new file mode 100644
index 000000000..34d70af37
--- /dev/null
+++ b/examples/pdfwidgets/pdfviewer/images/zoom-in-32.png
Binary files differ
diff --git a/examples/pdfwidgets/pdfviewer/images/zoom-out-24.png b/examples/pdfwidgets/pdfviewer/images/zoom-out-24.png
new file mode 100644
index 000000000..19703474f
--- /dev/null
+++ b/examples/pdfwidgets/pdfviewer/images/zoom-out-24.png
Binary files differ
diff --git a/examples/pdfwidgets/pdfviewer/images/zoom-out-32.png b/examples/pdfwidgets/pdfviewer/images/zoom-out-32.png
new file mode 100644
index 000000000..b83220661
--- /dev/null
+++ b/examples/pdfwidgets/pdfviewer/images/zoom-out-32.png
Binary files differ
diff --git a/examples/pdfwidgets/pdfviewer/main.cpp b/examples/pdfwidgets/pdfviewer/main.cpp
new file mode 100644
index 000000000..20fa6f8cd
--- /dev/null
+++ b/examples/pdfwidgets/pdfviewer/main.cpp
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mainwindow.h"
+#include <QApplication>
+#include <QUrl>
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ MainWindow w;
+ QStringList args = a.arguments();
+ w.show();
+ if (args.length() > 1)
+ w.open(QUrl::fromLocalFile(args[1]));
+
+ return a.exec();
+}
diff --git a/examples/pdfwidgets/pdfviewer/mainwindow.cpp b/examples/pdfwidgets/pdfviewer/mainwindow.cpp
new file mode 100644
index 000000000..5f9bf389f
--- /dev/null
+++ b/examples/pdfwidgets/pdfviewer/mainwindow.cpp
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mainwindow.h"
+#include "ui_mainwindow.h"
+
+#include "pageselector.h"
+#include "zoomselector.h"
+
+#include <QFileDialog>
+#include <QMessageBox>
+#include <QPdfBookmarkModel>
+#include <QPdfDocument>
+#include <QPdfPageNavigation>
+#include <QtMath>
+
+const qreal zoomMultiplier = qSqrt(2.0);
+
+Q_LOGGING_CATEGORY(lcExample, "qt.examples.pdfviewer")
+
+MainWindow::MainWindow(QWidget *parent)
+ : QMainWindow(parent)
+ , ui(new Ui::MainWindow)
+ , m_zoomSelector(new ZoomSelector(this))
+ , m_pageSelector(new PageSelector(this))
+ , m_document(new QPdfDocument(this))
+{
+ ui->setupUi(this);
+
+ m_zoomSelector->setMaximumWidth(150);
+ ui->mainToolBar->insertWidget(ui->actionZoom_In, m_zoomSelector);
+
+ m_pageSelector->setMaximumWidth(150);
+ ui->mainToolBar->addWidget(m_pageSelector);
+
+ m_pageSelector->setPageNavigation(ui->pdfView->pageNavigation());
+
+ connect(m_zoomSelector, &ZoomSelector::zoomModeChanged, ui->pdfView, &QPdfView::setZoomMode);
+ connect(m_zoomSelector, &ZoomSelector::zoomFactorChanged, ui->pdfView, &QPdfView::setZoomFactor);
+ m_zoomSelector->reset();
+
+ QPdfBookmarkModel *bookmarkModel = new QPdfBookmarkModel(this);
+ bookmarkModel->setDocument(m_document);
+
+ ui->bookmarkView->setModel(bookmarkModel);
+ connect(ui->bookmarkView, SIGNAL(activated(QModelIndex)), this, SLOT(bookmarkSelected(QModelIndex)));
+
+ ui->tabWidget->setTabEnabled(1, false); // disable 'Pages' tab for now
+
+ ui->pdfView->setDocument(m_document);
+
+ connect(ui->pdfView, &QPdfView::zoomFactorChanged,
+ m_zoomSelector, &ZoomSelector::setZoomFactor);
+}
+
+MainWindow::~MainWindow()
+{
+ delete ui;
+}
+
+void MainWindow::open(const QUrl &docLocation)
+{
+ if (docLocation.isLocalFile()) {
+ m_document->load(docLocation.toLocalFile());
+ const auto documentTitle = m_document->metaData(QPdfDocument::Title).toString();
+ setWindowTitle(!documentTitle.isEmpty() ? documentTitle : QStringLiteral("PDF Viewer"));
+ } else {
+ qCDebug(lcExample) << docLocation << "is not a valid local file";
+ QMessageBox::critical(this, tr("Failed to open"), tr("%1 is not a valid local file").arg(docLocation.toString()));
+ }
+ qCDebug(lcExample) << docLocation;
+}
+
+void MainWindow::bookmarkSelected(const QModelIndex &index)
+{
+ if (!index.isValid())
+ return;
+
+ const int page = index.data(QPdfBookmarkModel::PageNumberRole).toInt();
+ ui->pdfView->pageNavigation()->setCurrentPage(page);
+}
+
+void MainWindow::on_actionOpen_triggered()
+{
+ QUrl toOpen = QFileDialog::getOpenFileUrl(this, tr("Choose a PDF"), QUrl(), "Portable Documents (*.pdf)");
+ if (toOpen.isValid())
+ open(toOpen);
+}
+
+void MainWindow::on_actionQuit_triggered()
+{
+ QApplication::quit();
+}
+
+void MainWindow::on_actionAbout_triggered()
+{
+ QMessageBox::about(this, tr("About PdfViewer"),
+ tr("An example using QPdfDocument"));
+}
+
+void MainWindow::on_actionAbout_Qt_triggered()
+{
+ QMessageBox::aboutQt(this);
+}
+
+void MainWindow::on_actionZoom_In_triggered()
+{
+ ui->pdfView->setZoomFactor(ui->pdfView->zoomFactor() * zoomMultiplier);
+}
+
+void MainWindow::on_actionZoom_Out_triggered()
+{
+ ui->pdfView->setZoomFactor(ui->pdfView->zoomFactor() / zoomMultiplier);
+}
+
+void MainWindow::on_actionPrevious_Page_triggered()
+{
+ ui->pdfView->pageNavigation()->goToPreviousPage();
+}
+
+void MainWindow::on_actionNext_Page_triggered()
+{
+ ui->pdfView->pageNavigation()->goToNextPage();
+}
+
+void MainWindow::on_actionContinuous_triggered()
+{
+ ui->pdfView->setPageMode(ui->actionContinuous->isChecked() ? QPdfView::MultiPage : QPdfView::SinglePage);
+}
diff --git a/examples/pdfwidgets/pdfviewer/mainwindow.h b/examples/pdfwidgets/pdfviewer/mainwindow.h
new file mode 100644
index 000000000..afdfcade4
--- /dev/null
+++ b/examples/pdfwidgets/pdfviewer/mainwindow.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QLoggingCategory>
+#include <QMainWindow>
+
+Q_DECLARE_LOGGING_CATEGORY(lcExample)
+
+QT_BEGIN_NAMESPACE
+namespace Ui {
+class MainWindow;
+}
+
+class QPdfDocument;
+class QPdfView;
+QT_END_NAMESPACE
+
+class PageSelector;
+class ZoomSelector;
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ explicit MainWindow(QWidget *parent = nullptr);
+ ~MainWindow();
+
+public slots:
+ void open(const QUrl &docLocation);
+
+private slots:
+ void bookmarkSelected(const QModelIndex &index);
+
+ // action handlers
+ void on_actionOpen_triggered();
+ void on_actionQuit_triggered();
+ void on_actionAbout_triggered();
+ void on_actionAbout_Qt_triggered();
+ void on_actionZoom_In_triggered();
+ void on_actionZoom_Out_triggered();
+ void on_actionPrevious_Page_triggered();
+ void on_actionNext_Page_triggered();
+ void on_actionContinuous_triggered();
+
+private:
+ Ui::MainWindow *ui;
+ ZoomSelector *m_zoomSelector;
+ PageSelector *m_pageSelector;
+
+ QPdfDocument *m_document;
+};
+
+#endif // MAINWINDOW_H
diff --git a/examples/pdfwidgets/pdfviewer/mainwindow.ui b/examples/pdfwidgets/pdfviewer/mainwindow.ui
new file mode 100644
index 000000000..2651525a5
--- /dev/null
+++ b/examples/pdfwidgets/pdfviewer/mainwindow.ui
@@ -0,0 +1,281 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>700</width>
+ <height>600</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>PDF Viewer</string>
+ </property>
+ <property name="unifiedTitleAndToolBarOnMac">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="centralWidget">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="widget" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QSplitter" name="splitter">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="tabPosition">
+ <enum>QTabWidget::West</enum>
+ </property>
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <property name="documentMode">
+ <bool>false</bool>
+ </property>
+ <widget class="QWidget" name="bookmarkTab">
+ <attribute name="title">
+ <string>Bookmarks</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>2</number>
+ </property>
+ <property name="topMargin">
+ <number>2</number>
+ </property>
+ <property name="rightMargin">
+ <number>2</number>
+ </property>
+ <property name="bottomMargin">
+ <number>2</number>
+ </property>
+ <item>
+ <widget class="QTreeView" name="bookmarkView">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="headerHidden">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="pagesTab">
+ <attribute name="title">
+ <string>Pages</string>
+ </attribute>
+ </widget>
+ </widget>
+ <widget class="QPdfView" name="pdfView" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>10</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QMenuBar" name="menuBar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>700</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <widget class="QMenu" name="menuFile">
+ <property name="title">
+ <string>File</string>
+ </property>
+ <addaction name="actionOpen"/>
+ <addaction name="actionQuit"/>
+ </widget>
+ <widget class="QMenu" name="menuHelp">
+ <property name="title">
+ <string>Help</string>
+ </property>
+ <addaction name="actionAbout"/>
+ <addaction name="actionAbout_Qt"/>
+ </widget>
+ <widget class="QMenu" name="menuView">
+ <property name="title">
+ <string>View</string>
+ </property>
+ <addaction name="actionZoom_In"/>
+ <addaction name="actionZoom_Out"/>
+ <addaction name="actionPrevious_Page"/>
+ <addaction name="actionNext_Page"/>
+ <addaction name="separator"/>
+ <addaction name="actionContinuous"/>
+ </widget>
+ <addaction name="menuFile"/>
+ <addaction name="menuView"/>
+ <addaction name="menuHelp"/>
+ </widget>
+ <widget class="QToolBar" name="mainToolBar">
+ <property name="movable">
+ <bool>false</bool>
+ </property>
+ <property name="floatable">
+ <bool>false</bool>
+ </property>
+ <attribute name="toolBarArea">
+ <enum>TopToolBarArea</enum>
+ </attribute>
+ <attribute name="toolBarBreak">
+ <bool>false</bool>
+ </attribute>
+ <addaction name="actionOpen"/>
+ <addaction name="separator"/>
+ <addaction name="actionZoom_Out"/>
+ <addaction name="actionZoom_In"/>
+ <addaction name="separator"/>
+ </widget>
+ <widget class="QStatusBar" name="statusBar"/>
+ <action name="actionOpen">
+ <property name="icon">
+ <iconset resource="resources.qrc">
+ <normaloff>:/icons/images/fileopen.png</normaloff>:/icons/images/fileopen.png</iconset>
+ </property>
+ <property name="text">
+ <string>Open...</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+O</string>
+ </property>
+ </action>
+ <action name="actionQuit">
+ <property name="text">
+ <string>Quit</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Q</string>
+ </property>
+ </action>
+ <action name="actionAbout">
+ <property name="text">
+ <string>About</string>
+ </property>
+ </action>
+ <action name="actionAbout_Qt">
+ <property name="text">
+ <string>About Qt</string>
+ </property>
+ </action>
+ <action name="actionZoom_In">
+ <property name="icon">
+ <iconset resource="resources.qrc">
+ <normaloff>:/icons/images/zoom-in-24.png</normaloff>:/icons/images/zoom-in-24.png</iconset>
+ </property>
+ <property name="text">
+ <string>Zoom In</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+=</string>
+ </property>
+ </action>
+ <action name="actionZoom_Out">
+ <property name="icon">
+ <iconset resource="resources.qrc">
+ <normaloff>:/icons/images/zoom-out-24.png</normaloff>:/icons/images/zoom-out-24.png</iconset>
+ </property>
+ <property name="text">
+ <string>Zoom Out</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+-</string>
+ </property>
+ </action>
+ <action name="actionPrevious_Page">
+ <property name="text">
+ <string>Previous Page</string>
+ </property>
+ <property name="shortcut">
+ <string>PgUp</string>
+ </property>
+ </action>
+ <action name="actionNext_Page">
+ <property name="text">
+ <string>Next Page</string>
+ </property>
+ <property name="shortcut">
+ <string>PgDown</string>
+ </property>
+ </action>
+ <action name="actionContinuous">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Continuous</string>
+ </property>
+ </action>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <customwidgets>
+ <customwidget>
+ <class>QPdfView</class>
+ <extends>QWidget</extends>
+ <header location="global">qpdfview.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources>
+ <include location="resources.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/examples/pdfwidgets/pdfviewer/pageselector.cpp b/examples/pdfwidgets/pdfviewer/pageselector.cpp
new file mode 100644
index 000000000..cd3c4ba0e
--- /dev/null
+++ b/examples/pdfwidgets/pdfviewer/pageselector.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "pageselector.h"
+
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPdfPageNavigation>
+#include <QToolButton>
+
+PageSelector::PageSelector(QWidget *parent)
+ : QWidget(parent)
+ , m_pageNavigation(nullptr)
+{
+ QHBoxLayout *layout = new QHBoxLayout(this);
+
+ m_previousPageButton = new QToolButton(this);
+ m_previousPageButton->setText("<");
+ m_previousPageButton->setEnabled(false);
+
+ m_pageNumberEdit = new QLineEdit(this);
+ m_pageNumberEdit->setAlignment(Qt::AlignRight);
+
+ m_pageCountLabel = new QLabel(this);
+ m_pageCountLabel->setText("0");
+
+ m_nextPageButton = new QToolButton(this);
+ m_nextPageButton->setText(">");
+ m_nextPageButton->setEnabled(false);
+
+ layout->addWidget(m_previousPageButton);
+ layout->addWidget(m_pageNumberEdit);
+ layout->addWidget(m_pageCountLabel);
+ layout->addWidget(m_nextPageButton);
+}
+
+void PageSelector::setPageNavigation(QPdfPageNavigation *pageNavigation)
+{
+ m_pageNavigation = pageNavigation;
+
+ connect(m_previousPageButton, &QToolButton::clicked, m_pageNavigation, &QPdfPageNavigation::goToPreviousPage);
+ connect(m_pageNavigation, &QPdfPageNavigation::canGoToPreviousPageChanged, m_previousPageButton, &QToolButton::setEnabled);
+
+ connect(m_pageNavigation, &QPdfPageNavigation::currentPageChanged, this, &PageSelector::onCurrentPageChanged);
+ connect(m_pageNavigation, &QPdfPageNavigation::pageCountChanged, this, [this](int pageCount){ m_pageCountLabel->setText(QString::fromLatin1("/ %1").arg(pageCount)); });
+
+ connect(m_pageNumberEdit, &QLineEdit::editingFinished, this, &PageSelector::pageNumberEdited);
+
+ connect(m_nextPageButton, &QToolButton::clicked, m_pageNavigation, &QPdfPageNavigation::goToNextPage);
+ connect(m_pageNavigation, &QPdfPageNavigation::canGoToNextPageChanged, m_nextPageButton, &QToolButton::setEnabled);
+
+ onCurrentPageChanged(m_pageNavigation->currentPage());
+}
+
+void PageSelector::onCurrentPageChanged(int page)
+{
+ if (m_pageNavigation->pageCount() == 0)
+ m_pageNumberEdit->setText(QString::number(0));
+ else
+ m_pageNumberEdit->setText(QString::number(page + 1));
+}
+
+void PageSelector::pageNumberEdited()
+{
+ if (!m_pageNavigation)
+ return;
+
+ const QString text = m_pageNumberEdit->text();
+
+ bool ok = false;
+ const int pageNumber = text.toInt(&ok);
+
+ if (!ok)
+ onCurrentPageChanged(m_pageNavigation->currentPage());
+ else
+ m_pageNavigation->setCurrentPage(qBound(0, pageNumber - 1, m_pageNavigation->pageCount() - 1));
+}
diff --git a/examples/pdfwidgets/pdfviewer/pageselector.h b/examples/pdfwidgets/pdfviewer/pageselector.h
new file mode 100644
index 000000000..7a7283f99
--- /dev/null
+++ b/examples/pdfwidgets/pdfviewer/pageselector.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PAGESELECTOR_H
+#define PAGESELECTOR_H
+
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QLabel;
+class QLineEdit;
+class QPdfDocument;
+class QPdfPageNavigation;
+class QToolButton;
+QT_END_NAMESPACE
+
+class PageSelector : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit PageSelector(QWidget *parent = nullptr);
+
+ void setPageNavigation(QPdfPageNavigation *pageNavigation);
+
+private slots:
+ void onCurrentPageChanged(int page);
+ void pageNumberEdited();
+
+private:
+ QPdfPageNavigation *m_pageNavigation;
+
+ QLineEdit *m_pageNumberEdit;
+ QLabel *m_pageCountLabel;
+ QToolButton *m_previousPageButton;
+ QToolButton *m_nextPageButton;
+};
+
+#endif // PAGESELECTOR_H
diff --git a/examples/pdfwidgets/pdfviewer/pdfviewer.pro b/examples/pdfwidgets/pdfviewer/pdfviewer.pro
new file mode 100644
index 000000000..ad0607ea5
--- /dev/null
+++ b/examples/pdfwidgets/pdfviewer/pdfviewer.pro
@@ -0,0 +1,23 @@
+TEMPLATE = app
+TARGET = pdfviewer
+QT += core gui widgets pdfwidgets
+
+SOURCES += \
+ main.cpp \
+ mainwindow.cpp \
+ pageselector.cpp \
+ zoomselector.cpp
+
+HEADERS += \
+ mainwindow.h \
+ pageselector.h \
+ zoomselector.h
+
+FORMS += \
+ mainwindow.ui
+
+RESOURCES += \
+ resources.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/pdfwidgets/pdfviewer
+INSTALLS += target
diff --git a/examples/pdfwidgets/pdfviewer/resources.qrc b/examples/pdfwidgets/pdfviewer/resources.qrc
new file mode 100644
index 000000000..02d9655b4
--- /dev/null
+++ b/examples/pdfwidgets/pdfviewer/resources.qrc
@@ -0,0 +1,12 @@
+<RCC>
+ <qresource prefix="/icons">
+ <file>images/fileopen.png</file>
+ <file>images/go-next-24.png</file>
+ <file>images/go-previous-24.png</file>
+ <file>images/zoom-in-24.png</file>
+ <file>images/zoom-in-32.png</file>
+ <file>images/zoom-out-24.png</file>
+ <file>images/zoom-out-32.png</file>
+ <file>images/busy.png</file>
+ </qresource>
+</RCC>
diff --git a/examples/pdfwidgets/pdfviewer/zoomselector.cpp b/examples/pdfwidgets/pdfviewer/zoomselector.cpp
new file mode 100644
index 000000000..0205489aa
--- /dev/null
+++ b/examples/pdfwidgets/pdfviewer/zoomselector.cpp
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "zoomselector.h"
+
+#include <QLineEdit>
+
+ZoomSelector::ZoomSelector(QWidget *parent)
+ : QComboBox(parent)
+{
+ setEditable(true);
+
+ addItem(QLatin1String("Fit Width"));
+ addItem(QLatin1String("Fit Page"));
+ addItem(QLatin1String("12%"));
+ addItem(QLatin1String("25%"));
+ addItem(QLatin1String("33%"));
+ addItem(QLatin1String("50%"));
+ addItem(QLatin1String("66%"));
+ addItem(QLatin1String("75%"));
+ addItem(QLatin1String("100%"));
+ addItem(QLatin1String("125%"));
+ addItem(QLatin1String("150%"));
+ addItem(QLatin1String("200%"));
+ addItem(QLatin1String("400%"));
+
+ connect(this, static_cast<void(QComboBox::*)(const QString &)>(&QComboBox::currentIndexChanged),
+ this, &ZoomSelector::onCurrentTextChanged);
+
+ connect(lineEdit(), &QLineEdit::editingFinished,
+ this, [this](){onCurrentTextChanged(lineEdit()->text()); });
+}
+
+void ZoomSelector::setZoomFactor(qreal zoomFactor)
+{
+ setCurrentText(QString::number(qRound(zoomFactor * 100)) + QLatin1String("%"));
+}
+
+void ZoomSelector::reset()
+{
+ setCurrentIndex(8); // 100%
+}
+
+void ZoomSelector::onCurrentTextChanged(const QString &text)
+{
+ if (text == QLatin1String("Fit Width")) {
+ emit zoomModeChanged(QPdfView::FitToWidth);
+ } else if (text == QLatin1String("Fit Page")) {
+ emit zoomModeChanged(QPdfView::FitInView);
+ } else {
+ qreal factor = 1.0;
+
+ QString withoutPercent(text);
+ withoutPercent.remove(QLatin1Char('%'));
+
+ bool ok = false;
+ const int zoomLevel = withoutPercent.toInt(&ok);
+ if (ok)
+ factor = zoomLevel / 100.0;
+
+ emit zoomModeChanged(QPdfView::CustomZoom);
+ emit zoomFactorChanged(factor);
+ }
+}
diff --git a/examples/pdfwidgets/pdfviewer/zoomselector.h b/examples/pdfwidgets/pdfviewer/zoomselector.h
new file mode 100644
index 000000000..c58d09970
--- /dev/null
+++ b/examples/pdfwidgets/pdfviewer/zoomselector.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ZOOMSELECTOR_H
+#define ZOOMSELECTOR_H
+
+#include <QComboBox>
+#include <QPdfView>
+
+class ZoomSelector : public QComboBox
+{
+ Q_OBJECT
+
+public:
+ explicit ZoomSelector(QWidget *parent = nullptr);
+
+public slots:
+ void setZoomFactor(qreal zoomFactor);
+
+ void reset();
+
+signals:
+ void zoomModeChanged(QPdfView::ZoomMode zoomMode);
+ void zoomFactorChanged(qreal zoomFactor);
+
+private slots:
+ void onCurrentTextChanged(const QString &text);
+};
+
+#endif // ZOOMSELECTOR_H
diff --git a/examples/pdfwidgets/pdfwidgets.pro b/examples/pdfwidgets/pdfwidgets.pro
new file mode 100644
index 000000000..a12dc5b86
--- /dev/null
+++ b/examples/pdfwidgets/pdfwidgets.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS += pdfviewer
diff --git a/mkspecs/features/functions.prf b/mkspecs/features/functions.prf
index 512e2523b..db0b072a7 100644
--- a/mkspecs/features/functions.prf
+++ b/mkspecs/features/functions.prf
@@ -74,6 +74,7 @@ defineReplace(gnPath) {
defineReplace(gnArgs) {
linux: include($$QTWEBENGINE_ROOT/src/buildtools/config/linux.pri)
macos: include($$QTWEBENGINE_ROOT/src/buildtools/config/mac_osx.pri)
+ ios: include($$QTWEBENGINE_ROOT/src/buildtools/config/ios.pri)
win32: include($$QTWEBENGINE_ROOT/src/buildtools/config/windows.pri)
isEmpty(gn_args): error(No gn_args found please make sure you have valid configuration.)
return($$gn_args)
diff --git a/mkspecs/features/gn_generator.prf b/mkspecs/features/gn_generator.prf
index 863169c4d..6ff09d851 100644
--- a/mkspecs/features/gn_generator.prf
+++ b/mkspecs/features/gn_generator.prf
@@ -12,13 +12,18 @@ defineReplace(getTargetType) {
defineReplace(filter_flag_values) {
value_to_check = $$1
- macos:equals(value_to_check, "$(EXPORT_ARCH_ARGS)") {
+ if (macos|ios):equals(value_to_check, "$(EXPORT_ARCH_ARGS)") {
# EXPORT_ARCH_ARGS comes from qtbase/mkspecs/features/mac/default_post.prf which is a way
# to figure out the architectures to pass to the compiler at Makefile time. Because this
# variable expansion is not supported by GN, we filter it out. GN takes care of assigning
# the architecture itself.
return("")
}
+ if (ios) {
+ equals(value_to_check, "$(EXPORT_QMAKE_XARCH_LFLAGS)"): return("")
+ equals(value_to_check, "$(EXPORT_QMAKE_XARCH_CFLAGS)"): return("")
+ }
+
return($$value_to_check)
}
@@ -183,7 +188,7 @@ GN_CONTENTS += "$${TARGET_TYPE}(\"$$TARGET\") {"
}
GN_CONTENTS += " configs += [ \":$${TARGET}_config\" ]"
-GN_CONTENTS += " configs += [ \"//build/config:precompiled_headers\" ]"
+!isEmpty(GN_PRECOMPILED_HEADERS): GN_CONTENTS += " configs += [ \"//build/config:precompiled_headers\" ]"
# Source files to compile
GN_CONTENTS += " sources = ["
@@ -239,11 +244,10 @@ GN_CONTENTS += " \":generate_cpp_mocs\","
GN_CONTENTS += " ]"
GN_CONTENTS += " }"
GN_CONTENTS += "}"
-GN_CONTENTS += ""
-GN_CONTENTS += "if (!defined(core_include_dirs)) {"\
- " core_include_dirs = []"\
- "}"
!isEmpty(GN_CORE_INCLUDE_DIRS) {
+ GN_CONTENTS += " if (!defined(core_include_dirs)) {"\
+ " core_include_dirs = []"\
+ " }"
GN_CONTENTS += "core_include_dirs += ["
for (inc, GN_CORE_INCLUDE_DIRS): GN_CONTENTS += " \"$$inc\","
GN_CONTENTS += "]"
diff --git a/src/buildtools/config/common.pri b/src/buildtools/config/common.pri
index 97d39535c..9cd8cb089 100644
--- a/src/buildtools/config/common.pri
+++ b/src/buildtools/config/common.pri
@@ -39,7 +39,7 @@ greaterThan(QMAKE_JUMBO_MERGE_LIMIT,0) {
gn_args += jumbo_build_excluded="[\"browser\"]"
}
-qtConfig(webengine-printing-and-pdf) {
+qtConfig(build-qtwebengine-core):qtConfig(webengine-printing-and-pdf) {
gn_args += enable_basic_printing=true enable_print_preview=true
gn_args += enable_pdf=true
} else {
@@ -47,27 +47,31 @@ qtConfig(webengine-printing-and-pdf) {
gn_args += enable_pdf=false
}
-qtConfig(webengine-pepper-plugins) {
+qtConfig(build-qtwebengine-core):qtConfig(webengine-pepper-plugins) {
gn_args += enable_plugins=true
} else {
gn_args += enable_plugins=false
}
-qtConfig(webengine-spellchecker) {
+qtConfig(build-qtwebengine-core):qtConfig(webengine-spellchecker) {
gn_args += enable_spellcheck=true
} else {
gn_args += enable_spellcheck=false
}
-qtConfig(webengine-webrtc) {
+qtConfig(build-qtwebengine-core):qtConfig(webengine-webrtc) {
gn_args += enable_webrtc=true
} else {
gn_args += enable_webrtc=false audio_processing_in_audio_service_supported=false
}
-qtConfig(webengine-proprietary-codecs): gn_args += proprietary_codecs=true ffmpeg_branding=\"Chrome\"
+qtConfig(build-qtwebengine-core):qtConfig(webengine-proprietary-codecs) {
+ gn_args += proprietary_codecs=true ffmpeg_branding=\"Chrome\"
+} else {
+ gn_args += proprietary_codecs=false
+}
-qtConfig(webengine-extensions) {
+qtConfig(build-qtwebengine-core):qtConfig(webengine-extensions) {
gn_args += enable_extensions=true
} else {
gn_args += enable_extensions=false
@@ -122,13 +126,13 @@ optimize_size: gn_args += optimize_for_size=true
sanitize_undefined: gn_args += is_ubsan=true is_ubsan_vptr=true
}
-qtConfig(webengine-v8-snapshot):qtConfig(webengine-v8-snapshot-support) {
+qtConfig(build-qtwebengine-core):qtConfig(webengine-v8-snapshot):qtConfig(webengine-v8-snapshot-support) {
gn_args += v8_use_snapshot=true
} else {
gn_args += v8_use_snapshot=false
}
-qtConfig(webengine-kerberos) {
+qtConfig(build-qtwebengine-core):qtConfig(webengine-kerberos) {
gn_args += use_kerberos=true
} else {
gn_args += use_kerberos=false
@@ -139,3 +143,4 @@ ccache {
}
qtConfig(force_asserts): gn_args += dcheck_always_on=true
+
diff --git a/src/buildtools/config/ios.pri b/src/buildtools/config/ios.pri
new file mode 100644
index 000000000..5dc7faf9d
--- /dev/null
+++ b/src/buildtools/config/ios.pri
@@ -0,0 +1,66 @@
+load(functions)
+
+include($$QTWEBENGINE_OUT_ROOT/src/buildtools/qtbuildtools-config.pri)
+include($$QTWEBENGINE_OUT_ROOT/src/pdf/qtpdf-config.pri)
+QT_FOR_CONFIG += buildtools-private pdf-private
+
+clang_dir = $$which($${QMAKE_CXX})
+clang_dir = $$clean_path("$$dirname(clang_dir)/../")
+
+gn_args += \
+use_qt=true \
+closure_compile=false \
+is_component_build=false \
+is_shared=true \
+is_debug=true \
+enable_message_center=false \
+enable_nacl=false \
+enable_remoting=false \
+enable_reporting=false \
+enable_resource_whitelist_generation=false \
+enable_swiftshader=false \
+enable_web_speech=false \
+has_native_accessibility=false \
+enable_debugallocation=false \
+use_allocator_shim=false \
+use_allocator=\"none\" \
+use_custom_libcxx=false \
+v8_use_external_startup_data=false \
+v8_use_snapshot=false \
+toolkit_views=false \
+treat_warnings_as_errors=false \
+safe_browsing_mode=0 \
+optimize_webui=false \
+forbid_non_component_debug_builds=false \
+clang_use_chrome_plugins=false \
+use_xcode_clang=true \
+clang_base_path=\"$${clang_dir}\" \
+ios_enable_code_signing=false \
+target_os=\"ios\" \
+ios_deployment_target=\"$${QMAKE_IOS_DEPLOYMENT_TARGET}\" \
+enable_ios_bitcode=true \
+use_jumbo_build=false
+
+device:simulator {
+ # we do fat libray
+ gn_args+= \
+ target_cpu=\"$${QMAKE_APPLE_DEVICE_ARCHS}\" \
+ use_qt_fat_lib=true \
+ arm_use_neon=false\
+ # note this adds one arch of simulator at the moment, see also additional_target_cpus
+ target_sysroot=\"$$xcodeSDKInfo(Path, $$device.sdk)\" \
+ additional_target_sysroot=[\"$$xcodeSDKInfo(Path, $$simulator.sdk)\"]
+} else {
+ simulator {
+ equals(QMAKE_APPLE_SIMULATOR_ARCHS,"x86_64") {
+ gn_args+=target_cpu=\"x64\"
+ } else {
+ gn_args+=target_cpu=\"$${QMAKE_APPLE_SIMULATOR_ARCHS}\"
+ }
+ gn_args+=target_sysroot=\"$$xcodeSDKInfo(Path, $$simulator.sdk)\"
+ }
+ device {
+ gn_args+=target_cpu=\"$${QMAKE_APPLE_DEVICE_ARCHS}\"
+ gn_args+=target_sysroot=\"$$xcodeSDKInfo(Path, $$device.sdk)\"
+ }
+}
diff --git a/src/buildtools/config/linux.pri b/src/buildtools/config/linux.pri
index ee08f81bc..add563f28 100644
--- a/src/buildtools/config/linux.pri
+++ b/src/buildtools/config/linux.pri
@@ -5,7 +5,7 @@ defineReplace(extractCFlag) {
return($$qtwebengine_extractCFlag($$1))
}
-QT_FOR_CONFIG += gui-private webenginecore-private
+QT_FOR_CONFIG += gui-private webenginecore-private pdf-private
gn_args += \
use_cups=false \
@@ -26,7 +26,7 @@ gn_args += \
ozone_platform=\"qt\" \
ozone_extra_path=\"$$QTWEBENGINE_ROOT/src/core/ozone/ozone_extra.gni\"
-qtConfig(webengine-embedded-build) {
+qtConfig(build-qtwebengine-core):qtConfig(webengine-embedded-build) {
gn_args += is_desktop_linux=false
}
@@ -92,7 +92,7 @@ contains(QT_ARCH, "arm") {
}
}
- qtConfig(webengine-arm-thumb) {
+ qtConfig(build-qtwebengine-core):qtConfig(webengine-arm-thumb) {
gn_args += arm_use_thumb=true # this adds -mthumb
} else {
gn_args += arm_use_thumb=false
@@ -158,11 +158,15 @@ host_build {
qtConfig(webengine-system-zlib) {
qtConfig(webengine-system-minizip): gn_args += use_system_zlib=true use_system_minizip=true
- qtConfig(webengine-printing-and-pdf): gn_args += pdfium_use_system_zlib=true
+ qtConfig(build-qtpdf) || qtConfig(webengine-printing-and-pdf) {
+ gn_args += pdfium_use_system_zlib=true
+ }
}
qtConfig(webengine-system-png) {
gn_args += use_system_libpng=true
- qtConfig(webengine-printing-and-pdf): gn_args += pdfium_use_system_libpng=true
+ qtConfig(build-qtpdf) || qtConfig(webengine-printing-and-pdf) {
+ gn_args += pdfium_use_system_libpng=true
+ }
}
qtConfig(webengine-system-jpeg) {
gn_args += use_system_libjpeg=true
@@ -180,19 +184,19 @@ host_build {
gn_args += use_system_harfbuzz=false
}
gn_args += use_glib=false
- qtConfig(webengine-pulseaudio) {
+ qtConfig(build-qtwebengine-core):qtConfig(webengine-pulseaudio) {
gn_args += use_pulseaudio=true
} else {
gn_args += use_pulseaudio=false
}
- qtConfig(webengine-alsa) {
+ qtConfig(build-qtwebengine-core):qtConfig(webengine-alsa) {
gn_args += use_alsa=true
} else {
gn_args += use_alsa=false
}
!packagesExist(libpci): gn_args += use_libpci=false
- qtConfig(webengine-ozone-x11) {
+ qtConfig(build-qtwebengine-core):qtConfig(webengine-ozone-x11) {
gn_args += ozone_platform_x11=true
packagesExist(xscrnsaver): gn_args += use_xscrnsaver=true
qtConfig(webengine-webrtc): gn_args += rtc_use_x11=true
diff --git a/src/buildtools/config/mac_osx.pri b/src/buildtools/config/mac_osx.pri
index 3f2fe9c0a..a7ed61214 100644
--- a/src/buildtools/config/mac_osx.pri
+++ b/src/buildtools/config/mac_osx.pri
@@ -31,7 +31,7 @@ gn_args += \
mac_sdk_min=\"$${QMAKE_MAC_SDK_VERSION}\" \
use_external_popup_menu=false
-qtConfig(webengine-spellchecker) {
+qtConfig(build-qtwebengine-core):qtConfig(webengine-spellchecker) {
qtConfig(webengine-native-spellchecker): gn_args += use_browser_spellchecker=true
else: gn_args += use_browser_spellchecker=false
} else {
diff --git a/src/buildtools/config/pdf.pri b/src/buildtools/config/pdf.pri
new file mode 100644
index 000000000..4a1cf08e0
--- /dev/null
+++ b/src/buildtools/config/pdf.pri
@@ -0,0 +1,36 @@
+include($$QTWEBENGINE_OUT_ROOT/src/pdf/qtpdf-config.pri)
+QT_FOR_CONFIG += pdf-private
+
+qtConfig(build-qtpdf) {
+ qtConfig(pdf-v8) {
+ gn_args += pdf_enable_v8=true
+ } else {
+ gn_args += pdf_enable_v8=false
+ }
+ qtConfig(pdf-xfa) {
+ gn_args += pdf_enable_xfa=true
+ } else {
+ gn_args += pdf_enable_xfa=false
+ }
+ qtConfig(pdf-xfa-bmp) {
+ gn_args += pdf_enable_xfa_bmp=true
+ } else {
+ gn_args += pdf_enable_xfa_bmp=false
+ }
+ qtConfig(pdf-xfa-gif) {
+ gn_args += pdf_enable_xfa_gif=true
+ } else {
+ gn_args += pdf_enable_xfa_gif=false
+ }
+ qtConfig(pdf-xfa-png) {
+ gn_args += pdf_enable_xfa_png=true
+ } else {
+ gn_args += pdf_enable_xfa_png=false
+ }
+ qtConfig(pdf-xfa-tiff) {
+ gn_args += pdf_enable_xfa_tiff=true
+ } else {
+ gn_args += pdf_enable_xfa_tiff=false
+ }
+}
+
diff --git a/src/buildtools/config/support.pri b/src/buildtools/config/support.pri
index 5bdd808d4..8077b12ff 100644
--- a/src/buildtools/config/support.pri
+++ b/src/buildtools/config/support.pri
@@ -6,11 +6,6 @@ defineTest(qtwebengine_skipBuild) {
defineReplace(qtwebengine_checkError) {
- static {
- qtwebengine_skipBuild("Static builds of QtWebEngine are not supported.")
- return(false)
- }
-
!qtHaveModule(gui) {
qtwebengine_skipBuild("QtWebEngine requires QtGui.")
return(false)
@@ -67,7 +62,8 @@ defineReplace(qtwebengine_checkError) {
}
linux:!qtwebengine_checkErrorForLinux():return(false)
- win:!qtwebengine_checkErrorForWindows():return(false)
+ win32:!qtwebengine_checkErrorForWindows():return(false)
+ macos:!qtwebengine_checkErrorForMacOS():return(false)
sanitizer: !qtConfig(webengine-sanitizer) {
qtwebengine_skipBuild("Chosen sanitizer configuration is not supported for QtWebEngine. Check config.log for details or use -feature-webengine-sanitizer to force build with the chosen sanitizer configuration.")
@@ -77,8 +73,20 @@ defineReplace(qtwebengine_checkError) {
return(true)
}
+defineTest(qtwebengine_checkErrorFoMacOS) {
+ static {
+ qtwebengine_skipBuild("Static builds of QtWebEngine are not supported.")
+ return(false)
+ }
+}
+
defineTest(qtwebengine_checkErrorForLinux) {
+ static {
+ qtwebengine_skipBuild("Static builds of QtWebEngine are not supported.")
+ return(false)
+ }
+
!qtConfig(pkg-config) {
qtwebengine_skipBuild("A pkg-config support is required to build QtWebEngine.")
return(false)
@@ -114,6 +122,12 @@ defineTest(qtwebengine_checkErrorForLinux) {
}
defineTest(qtwebengine_checkErrorForWindows) {
+
+ static {
+ qtwebengine_skipBuild("Static builds of QtWebEngine are not supported.")
+ return(false)
+ }
+
!qtConfig(webengine-win-compiler64) {
qtwebengine_skipBuild("64-bit cross-building or native toolchain required to build QtWebEngine could not be found.")
return(false)
diff --git a/src/buildtools/config/windows.pri b/src/buildtools/config/windows.pri
index 5d7b7e1f2..2e5ebb876 100644
--- a/src/buildtools/config/windows.pri
+++ b/src/buildtools/config/windows.pri
@@ -89,7 +89,7 @@ msvc {
error("Qt WebEngine for Windows can only be built with a Microsoft Visual Studio C++ compatible compiler")
}
-qtConfig(webengine-spellchecker) {
+qtConfig(build-qtwebengine-core):qtConfig(webengine-spellchecker) {
qtConfig(webengine-native-spellchecker): gn_args += use_browser_spellchecker=true
else: gn_args += use_browser_spellchecker=false
} else {
diff --git a/src/buildtools/configure.json b/src/buildtools/configure.json
index f04fdae81..d6c47dfc6 100644
--- a/src/buildtools/configure.json
+++ b/src/buildtools/configure.json
@@ -9,6 +9,7 @@
"commandline": {
"options": {
"build-qtwebengine-core": "boolean",
+ "build-qtpdf": "boolean",
"webengine-jumbo-build": { "type": "optionalString", "name": "merge_limit"}
}
},
@@ -386,6 +387,23 @@
&& !config.static
&& (!config.linux || features.pkg-config)
&& (!features.xcb || features.webengine-ozone-x11)
+ && (!config.win32 || features.webengine-winversion)
+ && !config.ios",
+ "output": [ "privateFeature" ]
+ },
+ "webengine-qtpdf-support": {
+ "label": "Support Qt Pdf",
+ "condition": "module.gui
+ && features.webengine-python2
+ && features.webengine-gperf
+ && features.webengine-bison
+ && features.webengine-flex
+ && features.webengine-submodule
+ && features.webengine-nowhitespace
+ && features.webengine-arch-support
+ && !features.webengine-no-platform-support
+ && (!config.static || config.ios)
+ && (!config.linux || features.pkg-config)
&& (!config.win32 || features.webengine-winversion)",
"output": [ "privateFeature" ]
},
@@ -681,7 +699,8 @@
"report": [
{
"type": "skipBuildWarning",
- "condition": "!features.webengine-core-support && (features.build-qtwebengine-core || features.build-qtpdf)",
+ "condition": "(!features.webengine-core-support && features.build-qtwebengine-core) ||
+ (!features.webengine-qtpdf-support && features.build-qtpdf)",
"message": "qtwebengine_confCheckError"
},
{
@@ -691,7 +710,7 @@
},
{
"type": "note",
- "condition": "features.webengine-core-support && !features.build-qtpdf",
+ "condition": "features.webengine-qtpdf-support && !features.build-qtpdf",
"message": "QtPdf build is disabled by user."
},
{
@@ -701,8 +720,13 @@
},
{
"type": "warning",
- "condition": "!features.webengine-core-support && features.build-qtpdf",
+ "condition": "!features.webengine-qtpdf-support && features.build-qtpdf",
"message": "QtPdf will not be built."
+ },
+ {
+ "type": "warning",
+ "condition": "config.ios && config.simulator && config.device && features.build-qtpdf",
+ "message": "Building fat libray with device and simulator architectures will disable NEON."
}
],
"summary": [
@@ -711,14 +735,14 @@
"entries": [
"webengine-system-ninja",
"webengine-system-gn",
- {
+ {
"message": "Jumbo Build Merge Limit",
"type": "jumboBuild"
},
"webengine-developer-build",
{
"section": "QtWebEngine required system libraries",
- "condition": "config.unix && !config.macos",
+ "condition": "config.unix && !config.macos && !config.ios",
"entries": [
"webengine-system-fontconfig",
"webengine-system-dbus",
diff --git a/src/core/api/qwebengineurlrequestinfo.h b/src/core/api/qwebengineurlrequestinfo.h
index 75b6e3a81..69033cdfd 100644
--- a/src/core/api/qwebengineurlrequestinfo.h
+++ b/src/core/api/qwebengineurlrequestinfo.h
@@ -46,6 +46,7 @@
#include <QtCore/qurl.h>
namespace QtWebEngineCore {
+class InterceptedRequest;
class NetworkDelegateQt;
class URLRequestNotification;
} // namespace QtWebEngineCore
@@ -110,6 +111,7 @@ public:
private:
friend class QtWebEngineCore::NetworkDelegateQt;
friend class QtWebEngineCore::URLRequestNotification;
+ friend class QtWebEngineCore::InterceptedRequest;
Q_DISABLE_COPY(QWebEngineUrlRequestInfo)
Q_DECLARE_PRIVATE(QWebEngineUrlRequestInfo)
diff --git a/src/core/configure.json b/src/core/configure.json
index 9059575b9..9f05409ba 100644
--- a/src/core/configure.json
+++ b/src/core/configure.json
@@ -104,7 +104,6 @@
"webengine-embedded-build": {
"label": "Embedded build",
"purpose": "Enables the embedded build configuration.",
- "section": "WebEngine",
"condition": "config.unix",
"autoDetect": "tests.webengine-embedded-build",
"output": [ "privateFeature" ]
@@ -139,14 +138,12 @@
"webengine-pepper-plugins": {
"label": "Pepper Plugins",
"purpose": "Enables use of Pepper Flash plugins.",
- "section": "WebEngine",
"autoDetect": "!features.webengine-embedded-build",
"output": [ "privateFeature" ]
},
"webengine-printing-and-pdf": {
"label": "Printing and PDF",
"purpose": "Provides printing and output to PDF.",
- "section": "WebEngine",
"condition": "module.printsupport && features.printer",
"autoDetect": "!features.webengine-embedded-build",
"output": [ "privateFeature" ]
@@ -161,7 +158,6 @@
"webengine-proprietary-codecs": {
"label": "Proprietary Codecs",
"purpose": "Enables the use of proprietary codecs such as h.264/h.265 and MP3.",
- "section": "WebEngine",
"autoDetect": false,
"output": [ "privateFeature" ]
},
@@ -175,13 +171,11 @@
"webengine-spellchecker": {
"label": "Spellchecker",
"purpose": "Provides a spellchecker.",
- "section": "WebEngine",
"output": [ "publicFeature" ]
},
"webengine-native-spellchecker": {
"label": "Native Spellchecker",
"purpose": "Use the system's native spellchecking engine.",
- "section": "WebEngine",
"autoDetect": false,
"condition": "config.macos && features.webengine-spellchecker",
"output": [ "publicFeature" ]
@@ -197,7 +191,6 @@
"webengine-webrtc": {
"label": "WebRTC",
"purpose": "Provides WebRTC support.",
- "section": "WebEngine",
"autoDetect": "!features.webengine-embedded-build",
"output": [ "privateFeature" ]
},
diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp
index 29b6e09ed..baa08fa7e 100644
--- a/src/core/content_browser_client_qt.cpp
+++ b/src/core/content_browser_client_qt.cpp
@@ -41,14 +41,18 @@
#include "base/memory/ptr_util.h"
#include "base/optional.h"
+#include "base/path_service.h"
#include "base/strings/utf_string_conversions.h"
#include "base/message_loop/message_loop.h"
#include "base/task/post_task.h"
#include "base/threading/thread_restrictions.h"
+#include "chrome/browser/custom_handlers/protocol_handler_registry.h"
#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
#if QT_CONFIG(webengine_spellchecker)
#include "chrome/browser/spellchecker/spell_check_host_chrome_impl.h"
#endif
+#include "components/navigation_interception/intercept_navigation_throttle.h"
+#include "components/navigation_interception/navigation_params.h"
#include "components/guest_view/browser/guest_view_base.h"
#include "components/network_hints/browser/network_hints_message_filter.h"
#include "content/browser/renderer_host/render_view_host_delegate.h"
@@ -58,6 +62,7 @@
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/client_certificate_delegate.h"
#include "content/public/browser/media_observer.h"
+#include "content/public/browser/network_service_instance.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"
@@ -66,14 +71,18 @@
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_user_data.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/service_manager_connection.h"
#include "content/public/common/service_names.mojom.h"
#include "content/public/common/url_constants.h"
+#include "content/public/common/url_loader_throttle.h"
#include "content/public/common/user_agent.h"
#include "media/media_buildflags.h"
#include "extensions/buildflags/buildflags.h"
+#include "extensions/browser/extension_protocols.h"
+#include "extensions/browser/guest_view/web_view/web_view_guest.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "mojo/public/cpp/bindings/remote.h"
@@ -84,6 +93,8 @@
#include "qtwebengine/browser/qtwebengine_renderer_manifest.h"
#include "net/ssl/client_cert_identity.h"
#include "net/ssl/client_cert_store.h"
+#include "services/network/network_service.h"
+#include "services/network/public/cpp/features.h"
#include "services/service_manager/public/cpp/connector.h"
#include "services/service_manager/public/cpp/service.h"
#include "services/service_manager/sandbox/switches.h"
@@ -109,8 +120,12 @@
#include "devtools_manager_delegate_qt.h"
#include "login_delegate_qt.h"
#include "media_capture_devices_dispatcher.h"
-#include "net/network_delegate_qt.h"
-#include "net/url_request_context_getter_qt.h"
+#include "net/cookie_monster_delegate_qt.h"
+#include "net/custom_url_loader_factory.h"
+#include "net/proxying_restricted_cookie_manager_qt.h"
+#include "net/proxying_url_loader_factory_qt.h"
+#include "net/qrc_url_scheme_handler.h"
+#include "net/system_network_context_manager.h"
#include "platform_notification_service_qt.h"
#if QT_CONFIG(webengine_printing_and_pdf)
#include "printing/printing_message_filter_qt.h"
@@ -121,6 +136,7 @@
#include "renderer_host/user_resource_controller_host.h"
#include "service/service_qt.h"
#include "type_conversion.h"
+#include "web_contents_adapter_client.h"
#include "web_contents_delegate_qt.h"
#include "web_engine_context.h"
#include "web_engine_library_info.h"
@@ -143,13 +159,16 @@
#endif
#if BUILDFLAG(ENABLE_EXTENSIONS)
-#include "extensions/extensions_browser_client_qt.h"
+#include "content/public/browser/file_url_loader.h"
#include "extensions/browser/extension_message_filter.h"
#include "extensions/browser/guest_view/extensions_guest_view_message_filter.h"
#include "extensions/browser/io_thread_extension_message_filter.h"
#include "extensions/common/constants.h"
+
#include "common/extensions/extensions_client_qt.h"
-#include "renderer_host/resource_dispatcher_host_delegate_qt.h"
+#include "extensions/extension_web_contents_observer_qt.h"
+#include "extensions/extensions_browser_client_qt.h"
+#include "net/plugin_response_interceptor_url_loader_throttle.h"
#endif
#if BUILDFLAG(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS)
@@ -159,6 +178,7 @@
#include <QGuiApplication>
#include <QLocale>
+#include <QStandardPaths>
#if QT_CONFIG(opengl)
# include <QOpenGLContext>
# include <QOpenGLExtraFunctions>
@@ -276,22 +296,26 @@ void ContentBrowserClientQt::RenderProcessWillLaunch(content::RenderProcessHost*
{
const int id = host->GetID();
Profile *profile = Profile::FromBrowserContext(host->GetBrowserContext());
- base::PostTaskWithTraitsAndReplyWithResult(
- FROM_HERE, {content::BrowserThread::IO},
- base::BindOnce(&net::URLRequestContextGetter::GetURLRequestContext, base::Unretained(profile->GetRequestContext())),
- base::BindOnce(&ContentBrowserClientQt::AddNetworkHintsMessageFilter, base::Unretained(this), id));
+
+ host->AddFilter(new network_hints::NetworkHintsMessageFilter(id));
+
+ // Allow requesting custom schemes.
+ const auto policy = content::ChildProcessSecurityPolicy::GetInstance();
+ const auto profileAdapter = static_cast<ProfileQt *>(profile)->profileAdapter();
+ for (const QByteArray &scheme : profileAdapter->customUrlSchemes())
+ policy->GrantRequestScheme(id, scheme.toStdString());
// FIXME: Add a settings variable to enable/disable the file scheme.
- content::ChildProcessSecurityPolicy::GetInstance()->GrantRequestScheme(id, url::kFileScheme);
- static_cast<ProfileQt*>(host->GetBrowserContext())->m_profileAdapter->userResourceController()->renderProcessStartedWithHost(host);
+ policy->GrantRequestScheme(id, url::kFileScheme);
+ profileAdapter->userResourceController()->renderProcessStartedWithHost(host);
host->AddFilter(new BrowserMessageFilterQt(id, profile));
#if QT_CONFIG(webengine_printing_and_pdf)
- host->AddFilter(new PrintingMessageFilterQt(host->GetID()));
+ host->AddFilter(new PrintingMessageFilterQt(id));
#endif
#if BUILDFLAG(ENABLE_EXTENSIONS)
- host->AddFilter(new extensions::ExtensionMessageFilter(host->GetID(), host->GetBrowserContext()));
- host->AddFilter(new extensions::IOThreadExtensionMessageFilter(host->GetID(), host->GetBrowserContext()));
- host->AddFilter(new extensions::ExtensionsGuestViewMessageFilter(host->GetID(), host->GetBrowserContext()));
+ host->AddFilter(new extensions::ExtensionMessageFilter(id, profile));
+ host->AddFilter(new extensions::IOThreadExtensionMessageFilter(id, profile));
+ host->AddFilter(new extensions::ExtensionsGuestViewMessageFilter(id, profile));
#endif //ENABLE_EXTENSIONS
bool is_incognito_process = profile->IsOffTheRecord();
@@ -313,11 +337,7 @@ void ContentBrowserClientQt::RenderProcessWillLaunch(content::RenderProcessHost*
void ContentBrowserClientQt::ResourceDispatcherHostCreated()
{
-#if BUILDFLAG(ENABLE_EXTENSIONS)
- m_resourceDispatcherHostDelegate.reset(new ResourceDispatcherHostDelegateQt);
-#else
m_resourceDispatcherHostDelegate.reset(new content::ResourceDispatcherHostDelegate);
-#endif
content::ResourceDispatcherHost::Get()->SetDelegate(m_resourceDispatcherHostDelegate.get());
}
@@ -459,12 +479,20 @@ void ContentBrowserClientQt::AppendExtraCommandLineSwitches(base::CommandLine* c
void ContentBrowserClientQt::GetAdditionalWebUISchemes(std::vector<std::string>* additional_schemes)
{
+ ContentBrowserClient::GetAdditionalWebUISchemes(additional_schemes);
additional_schemes->push_back(content::kChromeDevToolsScheme);
}
void ContentBrowserClientQt::GetAdditionalViewSourceSchemes(std::vector<std::string>* additional_schemes)
{
- additional_schemes->push_back(content::kChromeDevToolsScheme);
+ ContentBrowserClient::GetAdditionalViewSourceSchemes(additional_schemes);
+}
+
+void ContentBrowserClientQt::GetAdditionalAllowedSchemesForFileSystem(std::vector<std::string>* additional_schemes)
+{
+ ContentBrowserClient::GetAdditionalAllowedSchemesForFileSystem(additional_schemes);
+ additional_schemes->push_back(content::kChromeDevToolsScheme);
+ additional_schemes->push_back(content::kChromeUIScheme);
}
#if defined(Q_OS_LINUX)
@@ -659,19 +687,6 @@ std::unique_ptr<device::LocationProvider> ContentBrowserClientQt::OverrideSystem
}
#endif
-void ContentBrowserClientQt::AddNetworkHintsMessageFilter(int render_process_id, net::URLRequestContext *context)
-{
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
- content::RenderProcessHost* host = content::RenderProcessHost::FromID(render_process_id);
- if (!host)
- return;
-
- content::BrowserMessageFilter *network_hints_message_filter =
- new network_hints::NetworkHintsMessageFilter(render_process_id);
- host->AddFilter(network_hints_message_filter);
-}
-
bool ContentBrowserClientQt::ShouldEnableStrictSiteIsolation()
{
// mirroring AwContentBrowserClient, CastContentBrowserClient and
@@ -687,13 +702,17 @@ bool ContentBrowserClientQt::WillCreateRestrictedCookieManager(network::mojom::R
int routing_id,
network::mojom::RestrictedCookieManagerRequest *request)
{
- base::PostTaskWithTraits(
- FROM_HERE, {content::BrowserThread::IO},
- base::BindOnce(&ProfileIODataQt::CreateRestrictedCookieManager,
- ProfileIODataQt::FromBrowserContext(browser_context)->getWeakPtrOnUIThread(),
- std::move(*request),
- role, origin, is_service_worker, process_id, routing_id));
- return true;
+ network::mojom::RestrictedCookieManagerRequest orig_request = std::move(*request);
+ network::mojom::RestrictedCookieManagerPtrInfo target_rcm_info;
+ *request = mojo::MakeRequest(&target_rcm_info);
+
+ ProxyingRestrictedCookieManagerQt::CreateAndBind(
+ ProfileIODataQt::FromBrowserContext(browser_context),
+ std::move(target_rcm_info),
+ is_service_worker, process_id, routing_id,
+ std::move(orig_request));
+
+ return false; // only made a proxy, still need the actual impl to be made.
}
bool ContentBrowserClientQt::AllowAppCacheOnIO(const GURL &manifest_url,
@@ -751,6 +770,14 @@ static void LaunchURL(const GURL& url,
content::WebContents* webContents = web_contents_getter.Run();
if (!webContents)
return;
+
+ ProtocolHandlerRegistry* protocolHandlerRegistry =
+ ProtocolHandlerRegistryFactory::GetForBrowserContext(
+ webContents->GetBrowserContext());
+ if (protocolHandlerRegistry &&
+ protocolHandlerRegistry->IsHandledProtocol(url.scheme()))
+ return;
+
WebContentsDelegateQt *contentsDelegate = static_cast<WebContentsDelegateQt*>(webContents->GetDelegate());
contentsDelegate->launchExternalURL(toQt(url), page_transition, is_main_frame, has_user_gesture);
}
@@ -766,7 +793,7 @@ bool ContentBrowserClientQt::HandleExternalProtocol(
bool has_user_gesture,
network::mojom::URLLoaderFactoryPtr *out_factory)
{
- Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+// Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
Q_UNUSED(child_id);
Q_UNUSED(navigation_data);
Q_UNUSED(out_factory);
@@ -822,30 +849,126 @@ private:
std::vector<std::unique_ptr<content::URLLoaderThrottle>>
ContentBrowserClientQt::CreateURLLoaderThrottlesOnIO(
- const network::ResourceRequest & /*request*/, content::ResourceContext *resource_context,
+ const network::ResourceRequest &request, content::ResourceContext *resource_context,
const base::RepeatingCallback<content::WebContents *()> & /*wc_getter*/,
- content::NavigationUIData * /*navigation_ui_data*/, int /*frame_tree_node_id*/)
+ content::NavigationUIData * /*navigation_ui_data*/, int frame_tree_node_id)
{
std::vector<std::unique_ptr<content::URLLoaderThrottle>> result;
ProfileIODataQt *ioData = ProfileIODataQt::FromResourceContext(resource_context);
result.push_back(std::make_unique<ProtocolHandlerThrottle<
scoped_refptr<ProtocolHandlerRegistry::IOThreadDelegate>>>(
- ioData->protocolHandlerRegistryIOThreadDelegate()));
+ ioData->protocolHandlerRegistryIOThreadDelegate()));
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ result.push_back(std::make_unique<PluginResponseInterceptorURLLoaderThrottle>(
+ resource_context, request.resource_type, frame_tree_node_id));
+#endif
return result;
}
std::vector<std::unique_ptr<content::URLLoaderThrottle>>
ContentBrowserClientQt::CreateURLLoaderThrottles(
const network::ResourceRequest &request, content::BrowserContext *browser_context,
- const base::RepeatingCallback<content::WebContents *()> &wc_getter,
- content::NavigationUIData *navigation_ui_data, int frame_tree_node_id)
+ const base::RepeatingCallback<content::WebContents *()> & /*wc_getter*/,
+ content::NavigationUIData * /*navigation_ui_data*/, int frame_tree_node_id)
{
std::vector<std::unique_ptr<content::URLLoaderThrottle>> result;
result.push_back(std::make_unique<ProtocolHandlerThrottle<ProtocolHandlerRegistry *>>(
- ProtocolHandlerRegistryFactory::GetForBrowserContext(browser_context)));
+ ProtocolHandlerRegistryFactory::GetForBrowserContext(browser_context)));
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ result.push_back(std::make_unique<PluginResponseInterceptorURLLoaderThrottle>(
+ browser_context, request.resource_type, frame_tree_node_id));
+#endif
return result;
}
+WebContentsAdapterClient::NavigationType pageTransitionToNavigationType(ui::PageTransition transition)
+{
+ if (ui::PageTransitionIsRedirect(transition))
+ return WebContentsAdapterClient::RedirectNavigation;
+
+ int32_t qualifier = ui::PageTransitionGetQualifier(transition);
+
+ if (qualifier & ui::PAGE_TRANSITION_FORWARD_BACK)
+ return WebContentsAdapterClient::BackForwardNavigation;
+
+ ui::PageTransition strippedTransition = ui::PageTransitionStripQualifier(transition);
+
+ switch (strippedTransition) {
+ case ui::PAGE_TRANSITION_LINK:
+ return WebContentsAdapterClient::LinkNavigation;
+ case ui::PAGE_TRANSITION_TYPED:
+ return WebContentsAdapterClient::TypedNavigation;
+ case ui::PAGE_TRANSITION_FORM_SUBMIT:
+ return WebContentsAdapterClient::FormSubmittedNavigation;
+ case ui::PAGE_TRANSITION_RELOAD:
+ return WebContentsAdapterClient::ReloadNavigation;
+ default:
+ return WebContentsAdapterClient::OtherNavigation;
+ }
+}
+
+static bool navigationThrottleCallback(content::WebContents *source,
+ const navigation_interception::NavigationParams &params)
+{
+ // We call navigationRequested later in launchExternalUrl for external protocols.
+ // The is_external_protocol parameter here is not fully accurate though,
+ // and doesn't know about profile specific custom URL schemes.
+ ProfileQt *profile = static_cast<ProfileQt *>(source->GetBrowserContext());
+ if (params.is_external_protocol() && !profile->profileAdapter()->urlSchemeHandler(toQByteArray(params.url().scheme())))
+ return false;
+ int navigationRequestAction = WebContentsAdapterClient::AcceptRequest;
+ WebContentsDelegateQt *delegate = static_cast<WebContentsDelegateQt *>(source->GetDelegate());
+ WebContentsAdapterClient *client = delegate->adapterClient();
+ client->navigationRequested(pageTransitionToNavigationType(params.transition_type()),
+ toQt(params.url()),
+ navigationRequestAction,
+ params.is_main_frame());
+ return navigationRequestAction == static_cast<int>(WebContentsAdapterClient::IgnoreRequest);
+}
+
+std::vector<std::unique_ptr<content::NavigationThrottle>> ContentBrowserClientQt::CreateThrottlesForNavigation(
+ content::NavigationHandle *navigation_handle)
+{
+ std::vector<std::unique_ptr<content::NavigationThrottle>> throttles;
+ throttles.push_back(std::make_unique<navigation_interception::InterceptNavigationThrottle>(
+ navigation_handle,
+ base::BindRepeating(&navigationThrottleCallback),
+ navigation_interception::SynchronyMode::kSync));
+ return throttles;
+}
+
+bool ContentBrowserClientQt::IsHandledURL(const GURL &url)
+{
+ static const char *const kProtocolList[] = {
+ url::kFileScheme,
+ content::kChromeDevToolsScheme,
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ extensions::kExtensionScheme,
+#endif
+ content::kChromeUIScheme,
+ url::kDataScheme,
+ url::kAboutScheme,
+#if !BUILDFLAG(DISABLE_FTP_SUPPORT)
+ url::kFtpScheme,
+#endif // !BUILDFLAG(DISABLE_FTP_SUPPORT)
+ url::kBlobScheme,
+ url::kFileSystemScheme,
+ url::kQrcScheme,
+ };
+
+ // We don't check url.IsCustom() here because we don't
+ // know if the registered protocol is installed in the
+ // profile that will be used to load the URL.
+
+ const std::string scheme = url.scheme();
+
+ for (const char *protocol : kProtocolList) {
+ if (scheme == protocol)
+ return true;
+ }
+ return net::URLRequest::IsHandledProtocol(scheme);
+}
+
std::unique_ptr<content::LoginDelegate> ContentBrowserClientQt::CreateLoginDelegate(
const net::AuthChallengeInfo &authInfo,
content::WebContents *web_contents,
@@ -875,6 +998,16 @@ bool ContentBrowserClientQt::ShouldUseProcessPerSite(content::BrowserContext* br
return ContentBrowserClient::ShouldUseProcessPerSite(browser_context, effective_url);
}
+bool ContentBrowserClientQt::DoesSiteRequireDedicatedProcess(content::BrowserOrResourceContext browser_or_resource_context,
+ const GURL &effective_site_url)
+{
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ if (effective_site_url.SchemeIs(extensions::kExtensionScheme))
+ return true;
+#endif
+ return ContentBrowserClient::DoesSiteRequireDedicatedProcess(browser_or_resource_context, effective_site_url);
+}
+
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)
@@ -887,4 +1020,194 @@ std::string ContentBrowserClientQt::GetProduct()
return productName.toStdString();
}
+scoped_refptr<network::SharedURLLoaderFactory> ContentBrowserClientQt::GetSystemSharedURLLoaderFactory()
+{
+ if (!SystemNetworkContextManager::GetInstance())
+ return nullptr;
+ return SystemNetworkContextManager::GetInstance()->GetSharedURLLoaderFactory();
+}
+
+network::mojom::NetworkContext *ContentBrowserClientQt::GetSystemNetworkContext()
+{
+ if (!SystemNetworkContextManager::GetInstance())
+ return nullptr;
+ return SystemNetworkContextManager::GetInstance()->GetContext();
+}
+
+void ContentBrowserClientQt::OnNetworkServiceCreated(network::mojom::NetworkService *network_service)
+{
+ if (!base::FeatureList::IsEnabled(network::features::kNetworkService))
+ return;
+
+ if (!SystemNetworkContextManager::GetInstance())
+ SystemNetworkContextManager::CreateInstance();
+
+ // Need to set up global NetworkService state before anything else uses it.
+ SystemNetworkContextManager::GetInstance()->OnNetworkServiceCreated(network_service);
+}
+
+network::mojom::NetworkContextPtr ContentBrowserClientQt::CreateNetworkContext(content::BrowserContext *context,
+ bool in_memory,
+ const base::FilePath &relative_partition_path)
+{
+ if (!base::FeatureList::IsEnabled(network::features::kNetworkService))
+ return nullptr;
+
+ network::mojom::NetworkContextPtr network_context;
+ // ### do we need to pass in_memory and relative_partition_path to ProfileIODataQt::CreateNetworkContextParams() ?
+ network::mojom::NetworkContextParamsPtr context_params = ProfileIODataQt::FromBrowserContext(context)->CreateNetworkContextParams();
+ content::GetNetworkService()->CreateNetworkContext(mojo::MakeRequest(&network_context), std::move(context_params));
+
+ network::mojom::CookieManagerPtrInfo cookie_manager_info;
+ network_context->GetCookieManager(mojo::MakeRequest(&cookie_manager_info));
+ ProfileIODataQt::FromBrowserContext(context)->cookieDelegate()->setMojoCookieManager(std::move(cookie_manager_info));
+
+ return network_context;
+}
+
+std::vector<base::FilePath> ContentBrowserClientQt::GetNetworkContextsParentDirectory()
+{
+ return {
+ toFilePath(QStandardPaths::writableLocation(QStandardPaths::DataLocation)),
+ toFilePath(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)) };
+}
+
+void ContentBrowserClientQt::RegisterNonNetworkNavigationURLLoaderFactories(int frame_tree_node_id,
+ NonNetworkURLLoaderFactoryMap *factories)
+{
+ DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService));
+ content::WebContents *web_contents = content::WebContents::FromFrameTreeNodeId(frame_tree_node_id);
+ Profile *profile = Profile::FromBrowserContext(web_contents->GetBrowserContext());
+ ProfileAdapter *profileAdapter = static_cast<ProfileQt *>(profile)->profileAdapter();
+
+ for (const QByteArray &scheme : profileAdapter->customUrlSchemes())
+ factories->emplace(scheme.toStdString(), CreateCustomURLLoaderFactory(profileAdapter));
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ factories->emplace(
+ extensions::kExtensionScheme,
+ extensions::CreateExtensionNavigationURLLoaderFactory(profile,
+ !!extensions::WebViewGuest::FromWebContents(web_contents)));
+#endif
+}
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+namespace {
+// The FileURLLoaderFactory provided to the extension background pages.
+// Checks with the ChildProcessSecurityPolicy to validate the file access.
+class FileURLLoaderFactory : public network::mojom::URLLoaderFactory
+{
+public:
+ explicit FileURLLoaderFactory(int child_id) : child_id_(child_id) {}
+
+private:
+ // network::mojom::URLLoaderFactory:
+ void CreateLoaderAndStart(network::mojom::URLLoaderRequest loader,
+ int32_t routing_id,
+ int32_t request_id,
+ uint32_t options,
+ const network::ResourceRequest& request,
+ network::mojom::URLLoaderClientPtr client,
+ const net::MutableNetworkTrafficAnnotationTag &traffic_annotation) override
+ {
+ if (!content::ChildProcessSecurityPolicy::GetInstance()->CanRequestURL(child_id_, request.url)) {
+ client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_ACCESS_DENIED));
+ return;
+ }
+ content::CreateFileURLLoader(request, std::move(loader), std::move(client),
+ /* observer */ nullptr,
+ /* allow_directory_listing */ false);
+ }
+
+ void Clone(network::mojom::URLLoaderFactoryRequest loader) override
+ {
+ bindings_.AddBinding(this, std::move(loader));
+ }
+
+ int child_id_;
+ mojo::BindingSet<network::mojom::URLLoaderFactory> bindings_;
+ DISALLOW_COPY_AND_ASSIGN(FileURLLoaderFactory);
+};
+} // namespace
+#endif
+
+void ContentBrowserClientQt::RegisterNonNetworkSubresourceURLLoaderFactories(int render_process_id, int render_frame_id,
+ NonNetworkURLLoaderFactoryMap *factories)
+{
+ if (!base::FeatureList::IsEnabled(network::features::kNetworkService))
+ return;
+ content::RenderProcessHost *process_host = content::RenderProcessHost::FromID(render_process_id);
+ Profile *profile = Profile::FromBrowserContext(process_host->GetBrowserContext());
+ ProfileAdapter *profileAdapter = static_cast<ProfileQt *>(profile)->profileAdapter();
+
+ for (const QByteArray &scheme : profileAdapter->customUrlSchemes())
+ factories->emplace(scheme.toStdString(), CreateCustomURLLoaderFactory(profileAdapter));
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ auto factory = extensions::CreateExtensionURLLoaderFactory(render_process_id, render_frame_id);
+ if (factory)
+ factories->emplace(extensions::kExtensionScheme, std::move(factory));
+
+ content::RenderFrameHost *frame_host = content::RenderFrameHost::FromID(render_process_id, render_frame_id);
+ content::WebContents *web_contents = content::WebContents::FromRenderFrameHost(frame_host);
+ if (!web_contents)
+ return;
+
+ extensions::ExtensionWebContentsObserverQt *web_observer =
+ extensions::ExtensionWebContentsObserverQt::FromWebContents(web_contents);
+ if (!web_observer)
+ return;
+
+ const extensions::Extension *extension = web_observer->GetExtensionFromFrame(frame_host, false);
+ if (!extension)
+ return;
+
+ std::vector<std::string> allowed_webui_hosts;
+ // Support for chrome:// scheme if appropriate.
+ if ((extension->is_extension() || extension->is_platform_app()) &&
+ extensions::Manifest::IsComponentLocation(extension->location())) {
+ // Components of chrome that are implemented as extensions or platform apps
+ // are allowed to use chrome://resources/ and chrome://theme/ URLs.
+ allowed_webui_hosts.emplace_back(content::kChromeUIResourcesHost);
+ }
+ if (!allowed_webui_hosts.empty()) {
+ factories->emplace(content::kChromeUIScheme,
+ content::CreateWebUIURLLoader(frame_host,
+ content::kChromeUIScheme,
+ std::move(allowed_webui_hosts)));
+ }
+ // Support for file:// scheme when approved by ChildProcessSecurityPolicy.
+ // FIXME: Not needed after switching to using transferable url loaders and guest views.
+ factories->emplace(url::kFileScheme, std::make_unique<FileURLLoaderFactory>(render_process_id));
+#endif
+}
+
+bool ContentBrowserClientQt::WillCreateURLLoaderFactory(
+ content::BrowserContext *browser_context,
+ content::RenderFrameHost *frame,
+ int render_process_id,
+ bool is_navigation,
+ bool is_download,
+ const url::Origin &request_initiator,
+ mojo::PendingReceiver<network::mojom::URLLoaderFactory> *factory_receiver,
+ network::mojom::TrustedURLLoaderHeaderClientPtrInfo *header_client,
+ bool *bypass_redirect_checks)
+{
+ if (!base::FeatureList::IsEnabled(network::features::kNetworkService))
+ return false;
+
+ auto proxied_receiver = std::move(*factory_receiver);
+ network::mojom::URLLoaderFactoryPtrInfo target_factory_info;
+ *factory_receiver = mojo::MakeRequest(&target_factory_info);
+ int process_id = is_navigation ? 0 : render_process_id;
+
+ base::PostTaskWithTraits(
+ FROM_HERE, {content::BrowserThread::IO},
+ base::BindOnce(&ProxyingURLLoaderFactoryQt::CreateProxy, process_id,
+ browser_context->GetResourceContext(),
+ std::move(proxied_receiver),
+ std::move(target_factory_info)));
+ return true;
+}
+
} // namespace QtWebEngineCore
diff --git a/src/core/content_browser_client_qt.h b/src/core/content_browser_client_qt.h
index f5b03a8d9..cd48d6fe5 100644
--- a/src/core/content_browser_client_qt.h
+++ b/src/core/content_browser_client_qt.h
@@ -116,6 +116,7 @@ public:
void GetAdditionalViewSourceSchemes(std::vector<std::string>* additional_schemes) override;
void GetAdditionalWebUISchemes(std::vector<std::string>* additional_schemes) override;
+ void GetAdditionalAllowedSchemesForFileSystem(std::vector<std::string>* additional_schemes) override;
void BindInterfaceRequestFromFrame(content::RenderFrameHost* render_frame_host,
const std::string& interface_name,
@@ -177,6 +178,8 @@ public:
#endif
bool ShouldIsolateErrorPage(bool in_main_frame) override;
bool ShouldUseProcessPerSite(content::BrowserContext* browser_context, const GURL& effective_url) override;
+ bool DoesSiteRequireDedicatedProcess(content::BrowserOrResourceContext browser_or_resource_contexts,
+ const GURL &effective_site_url) override;
#if defined(Q_OS_LINUX)
void GetAdditionalMappedFilesForChildProcess(const base::CommandLine& command_line, int child_process_id, content::PosixFileDescriptorInfo* mappings) override;
@@ -216,6 +219,31 @@ public:
const base::RepeatingCallback<content::WebContents *()> &wc_getter,
content::NavigationUIData *navigation_ui_data, int frame_tree_node_id) override;
+ std::vector<std::unique_ptr<content::NavigationThrottle>> CreateThrottlesForNavigation(
+ content::NavigationHandle *navigation_handle) override;
+
+ bool IsHandledURL(const GURL &url) override;
+
+ bool WillCreateURLLoaderFactory(content::BrowserContext *browser_context,
+ content::RenderFrameHost *frame,
+ int render_process_id,
+ bool is_navigation,
+ bool is_download,
+ const url::Origin &request_initiator,
+ mojo::PendingReceiver<network::mojom::URLLoaderFactory> *factory_receiver,
+ network::mojom::TrustedURLLoaderHeaderClientPtrInfo *header_client,
+ bool *bypass_redirect_checks) override;
+ scoped_refptr<network::SharedURLLoaderFactory> GetSystemSharedURLLoaderFactory() override;
+ network::mojom::NetworkContext *GetSystemNetworkContext() override;
+ void OnNetworkServiceCreated(network::mojom::NetworkService *network_service) override;
+ network::mojom::NetworkContextPtr CreateNetworkContext(content::BrowserContext *context,
+ bool in_memory,
+ const base::FilePath &relative_partition_path) override;
+ std::vector<base::FilePath> GetNetworkContextsParentDirectory() override;
+ void RegisterNonNetworkNavigationURLLoaderFactories(int frame_tree_node_id, NonNetworkURLLoaderFactoryMap *factories) override;
+ void RegisterNonNetworkSubresourceURLLoaderFactories(int render_process_id, int render_frame_id,
+ NonNetworkURLLoaderFactoryMap* factories) override;
+
static std::string getUserAgent();
std::string GetUserAgent() override { return getUserAgent(); }
@@ -223,7 +251,6 @@ public:
private:
void InitFrameInterfaces();
- void AddNetworkHintsMessageFilter(int render_process_id, net::URLRequestContext *context);
std::unique_ptr<content::ResourceDispatcherHostDelegate> m_resourceDispatcherHostDelegate;
scoped_refptr<ShareGroupQtQuick> m_shareGroupQtQuick;
diff --git a/src/core/core_chromium.pri b/src/core/core_chromium.pri
index 9eab15456..d317bf625 100644
--- a/src/core/core_chromium.pri
+++ b/src/core/core_chromium.pri
@@ -82,17 +82,16 @@ SOURCES = \
net/client_cert_override.cpp \
net/client_cert_store_data.cpp \
net/cookie_monster_delegate_qt.cpp \
- net/custom_protocol_handler.cpp \
- net/network_delegate_qt.cpp \
+ net/custom_url_loader_factory.cpp \
+ net/proxy_config_monitor.cpp \
net/proxy_config_service_qt.cpp \
+ net/proxying_url_loader_factory_qt.cpp \
+ net/proxying_restricted_cookie_manager_qt.cpp \
net/qrc_url_scheme_handler.cpp \
- net/restricted_cookie_manager_qt.cpp \
net/ssl_host_state_delegate_qt.cpp \
- net/url_request_context_getter_qt.cpp \
- net/url_request_custom_job.cpp \
+ net/system_network_context_manager.cpp \
net/url_request_custom_job_delegate.cpp \
net/url_request_custom_job_proxy.cpp \
- net/url_request_notification.cpp \
net/webui_controller_factory_qt.cpp \
ozone/gl_context_qt.cpp \
ozone/gl_ozone_egl_qt.cpp \
@@ -193,16 +192,14 @@ HEADERS = \
net/client_cert_override.h \
net/client_cert_store_data.h \
net/cookie_monster_delegate_qt.h \
- net/custom_protocol_handler.h \
- net/network_delegate_qt.h \
+ net/custom_url_loader_factory.h \
+ net/proxying_url_loader_factory_qt.h \
+ net/proxying_restricted_cookie_manager_qt.h \
net/qrc_url_scheme_handler.h \
- net/restricted_cookie_manager_qt.h \
net/ssl_host_state_delegate_qt.h \
- net/url_request_context_getter_qt.h \
- net/url_request_custom_job.h \
+ net/system_network_context_manager.h \
net/url_request_custom_job_delegate.h \
net/url_request_custom_job_proxy.h \
- net/url_request_notification.h \
net/webui_controller_factory_qt.h \
ozone/gl_context_qt.h \
ozone/gl_ozone_egl_qt.h \
@@ -219,6 +216,7 @@ HEADERS = \
profile_adapter_client.h \
profile_qt.h \
profile_io_data_qt.h \
+ proxy_config_monitor.h \
proxy_config_service_qt.h \
quota_permission_context_qt.h \
quota_request_controller.h \
@@ -339,11 +337,11 @@ qtConfig(webengine-extensions) {
extensions/extensions_browser_api_provider_qt.cpp \
extensions/extensions_browser_client_qt.cpp \
extensions/mime_handler_view_guest_delegate_qt.cpp \
+ net/plugin_response_interceptor_url_loader_throttle.cpp \
renderer/extensions/extensions_dispatcher_delegate_qt.cpp \
renderer/extensions/extensions_renderer_client_qt.cpp \
renderer/extensions/renderer_permissions_policy_delegate_qt.cpp \
- renderer/extensions/resource_request_policy_qt.cpp \
- renderer_host/resource_dispatcher_host_delegate_qt.cpp
+ renderer/extensions/resource_request_policy_qt.cpp
HEADERS += \
common/extensions/extensions_api_provider_qt.h \
@@ -356,9 +354,9 @@ qtConfig(webengine-extensions) {
extensions/extensions_browser_api_provider_qt.h \
extensions/extensions_browser_client_qt.h \
extensions/mime_handler_view_guest_delegate_qt.h \
+ net/plugin_response_interceptor_url_loader_throttle.h \
renderer/extensions/extensions_dispatcher_delegate_qt.h \
renderer/extensions/extensions_renderer_client_qt.h \
renderer/extensions/renderer_permissions_policy_delegate_qt.h \
- renderer/extensions/resource_request_policy_qt.h \
- renderer_host/resource_dispatcher_host_delegate_qt.h
+ renderer/extensions/resource_request_policy_qt.h
}
diff --git a/src/core/core_gn_config.pri b/src/core/core_gn_config.pri
index a089eecd0..2b8f2e18f 100644
--- a/src/core/core_gn_config.pri
+++ b/src/core/core_gn_config.pri
@@ -11,6 +11,7 @@ qtConfig (webengine-extensions) {
}
GN_CORE_INCLUDE_DIRS = $$PWD/service
GN_CREATE_PRI = true
+GN_PRECOMPILED_HEADERS = true
QMAKE_INTERNAL_INCLUDED_FILES = $$GN_IMPORTS $$GN_INCLUDES $$GN_FILE
diff --git a/src/core/extensions/extension_web_contents_observer_qt.cpp b/src/core/extensions/extension_web_contents_observer_qt.cpp
index 365f04e46..5b1514bb4 100644
--- a/src/core/extensions/extension_web_contents_observer_qt.cpp
+++ b/src/core/extensions/extension_web_contents_observer_qt.cpp
@@ -70,42 +70,6 @@ void ExtensionWebContentsObserverQt::CreateForWebContents(content::WebContents *
FromWebContents(web_contents)->Initialize();
}
-std::string ExtensionWebContentsObserverQt::GetExtensionIdFromFrame(content::RenderFrameHost *render_frame_host) const
-{
- const GURL &site = render_frame_host->GetSiteInstance()->GetSiteURL();
- if (!site.SchemeIs(kExtensionScheme))
- return std::string();
-
- return site.host();
-}
-
-const Extension *ExtensionWebContentsObserverQt::GetExtensionFromFrame(content::RenderFrameHost *render_frame_host, bool verify_url) const
-{
- std::string extension_id = GetExtensionIdFromFrame(render_frame_host);
- if (extension_id.empty())
- return nullptr;
-
- content::BrowserContext *browser_context =
- render_frame_host->GetProcess()->GetBrowserContext();
- const Extension *extension = ExtensionRegistry::Get(browser_context)
- ->enabled_extensions()
- .GetByID(extension_id);
- if (!extension)
- return nullptr;
-
- if (verify_url) {
- const url::Origin &origin(render_frame_host->GetLastCommittedOrigin());
- // Without site isolation, this check is needed to eliminate non-extension
- // schemes. With site isolation, this is still needed to exclude sandboxed
- // extension frames with a unique origin.
- const GURL site_url(render_frame_host->GetSiteInstance()->GetSiteURL());
- if (origin.opaque() || site_url != content::SiteInstance::GetSiteForURL(browser_context, origin.GetURL()))
- return nullptr;
- }
-
- return extension;
-}
-
void ExtensionWebContentsObserverQt::RenderFrameCreated(content::RenderFrameHost *render_frame_host)
{
ExtensionWebContentsObserver::RenderFrameCreated(render_frame_host);
diff --git a/src/core/extensions/extension_web_contents_observer_qt.h b/src/core/extensions/extension_web_contents_observer_qt.h
index a528b3856..658966b31 100644
--- a/src/core/extensions/extension_web_contents_observer_qt.h
+++ b/src/core/extensions/extension_web_contents_observer_qt.h
@@ -59,9 +59,6 @@ public:
static void CreateForWebContents(content::WebContents *web_contents);
- std::string GetExtensionIdFromFrame(content::RenderFrameHost *) const;
- const Extension *GetExtensionFromFrame(content::RenderFrameHost *, bool) const;
-
// content::WebContentsObserver overrides.
void RenderFrameCreated(content::RenderFrameHost *render_frame_host) override;
diff --git a/src/core/extensions/extensions_browser_client_qt.cpp b/src/core/extensions/extensions_browser_client_qt.cpp
index 59c15d2f5..d974155c5 100644
--- a/src/core/extensions/extensions_browser_client_qt.cpp
+++ b/src/core/extensions/extensions_browser_client_qt.cpp
@@ -66,10 +66,12 @@
#include "extensions/browser/mojo/interface_registration.h"
#include "extensions/browser/url_request_util.h"
#include "extensions/common/file_util.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
#include "net/base/completion_once_callback.h"
#include "net/base/mime_util.h"
#include "net/url_request/url_request_simple_job.h"
#include "services/network/public/mojom/url_loader.mojom.h"
+#include "third_party/zlib/google/compression_utils.h"
#include "ui/base/resource/resource_bundle.h"
#include "component_extension_resource_manager_qt.h"
@@ -165,6 +167,163 @@ private:
mutable base::WeakPtrFactory<URLRequestResourceBundleJob> weak_factory_;
};
+scoped_refptr<base::RefCountedMemory> GetResource(int resource_id, const std::string &extension_id)
+{
+ const ui::ResourceBundle &rb = ui::ResourceBundle::GetSharedInstance();
+ scoped_refptr<base::RefCountedMemory> bytes = rb.LoadDataResourceBytes(resource_id);
+ auto *replacements = extensions::ExtensionsBrowserClient::Get()->GetComponentExtensionResourceManager()
+ ? extensions::ExtensionsBrowserClient::Get()->GetComponentExtensionResourceManager()->GetTemplateReplacementsForExtension(
+ extension_id)
+ : nullptr;
+
+ bool is_gzipped = rb.IsGzipped(resource_id);
+ if (!bytes->size() || (!replacements && !is_gzipped)) {
+ return bytes;
+ }
+
+ base::StringPiece input(reinterpret_cast<const char *>(bytes->front()), bytes->size());
+
+ std::string temp_str;
+
+ base::StringPiece source = input;
+ if (is_gzipped) {
+ temp_str.resize(compression::GetUncompressedSize(input));
+ source = temp_str;
+ CHECK(compression::GzipUncompress(input, source));
+ }
+
+ if (replacements) {
+ temp_str = ui::ReplaceTemplateExpressions(source, *replacements);
+ }
+
+ DCHECK(!temp_str.empty());
+
+ return base::RefCountedString::TakeString(&temp_str);
+}
+
+// Loads an extension resource in a Chrome .pak file. These are used by
+// component extensions.
+class ResourceBundleFileLoader : public network::mojom::URLLoader
+{
+public:
+ static void CreateAndStart(const network::ResourceRequest &request, network::mojom::URLLoaderRequest loader,
+ network::mojom::URLLoaderClientPtrInfo client_info, const base::FilePath &filename,
+ int resource_id, const std::string &content_security_policy, bool send_cors_header)
+ {
+ // Owns itself. Will live as long as its URLLoader and URLLoaderClientPtr
+ // bindings are alive - essentially until either the client gives up or all
+ // file data has been sent to it.
+ auto *bundle_loader = new ResourceBundleFileLoader(content_security_policy, send_cors_header);
+ bundle_loader->Start(request, std::move(loader), std::move(client_info), filename, resource_id);
+ }
+
+ // mojom::URLLoader implementation:
+ void FollowRedirect(const std::vector<std::string> &removed_headers,
+ const net::HttpRequestHeaders &modified_headers, const base::Optional<GURL> &new_url) override
+ {
+ NOTREACHED() << "No redirects for local file loads.";
+ }
+ // Current implementation reads all resource data at start of resource
+ // load, so priority, and pausing is not currently implemented.
+ void SetPriority(net::RequestPriority priority, int32_t intra_priority_value) override {}
+ void PauseReadingBodyFromNet() override {}
+ void ResumeReadingBodyFromNet() override {}
+ void ProceedWithResponse() override {}
+
+private:
+ ResourceBundleFileLoader(const std::string &content_security_policy, bool send_cors_header) : binding_(this)
+ {
+ response_headers_ = extensions::BuildHttpHeaders(content_security_policy, send_cors_header, base::Time());
+ }
+ ~ResourceBundleFileLoader() override = default;
+
+ void Start(const network::ResourceRequest &request, network::mojom::URLLoaderRequest loader,
+ network::mojom::URLLoaderClientPtrInfo client_info, const base::FilePath &filename, int resource_id)
+ {
+ client_.Bind(std::move(client_info));
+ binding_.Bind(std::move(loader));
+ binding_.set_connection_error_handler(
+ base::BindOnce(&ResourceBundleFileLoader::OnBindingError, base::Unretained(this)));
+ client_.set_connection_error_handler(
+ base::BindOnce(&ResourceBundleFileLoader::OnConnectionError, base::Unretained(this)));
+ auto data = GetResource(resource_id, request.url.host());
+
+ std::string *read_mime_type = new std::string;
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, { base::MayBlock() },
+ base::BindOnce(&net::GetMimeTypeFromFile, filename, base::Unretained(read_mime_type)),
+ base::BindOnce(&ResourceBundleFileLoader::OnMimeTypeRead, weak_factory_.GetWeakPtr(), std::move(data),
+ base::Owned(read_mime_type)));
+ }
+
+ void OnMimeTypeRead(scoped_refptr<base::RefCountedMemory> data, std::string *read_mime_type, bool read_result)
+ {
+ network::ResourceResponseHead head;
+ head.request_start = base::TimeTicks::Now();
+ head.response_start = base::TimeTicks::Now();
+ head.content_length = data->size();
+ head.mime_type = *read_mime_type;
+ DetermineCharset(head.mime_type, data.get(), &head.charset);
+ mojo::DataPipe pipe(data->size());
+ if (!pipe.consumer_handle.is_valid()) {
+ client_->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
+ client_.reset();
+ MaybeDeleteSelf();
+ return;
+ }
+ head.headers = response_headers_;
+ head.headers->AddHeader(base::StringPrintf("%s: %s", net::HttpRequestHeaders::kContentLength,
+ base::NumberToString(head.content_length).c_str()));
+ if (!head.mime_type.empty()) {
+ head.headers->AddHeader(
+ base::StringPrintf("%s: %s", net::HttpRequestHeaders::kContentType, head.mime_type.c_str()));
+ }
+ client_->OnReceiveResponse(head);
+ client_->OnStartLoadingResponseBody(std::move(pipe.consumer_handle));
+
+ uint32_t write_size = data->size();
+ MojoResult result = pipe.producer_handle->WriteData(data->front(), &write_size, MOJO_WRITE_DATA_FLAG_NONE);
+ OnFileWritten(result);
+ }
+
+ void OnConnectionError()
+ {
+ client_.reset();
+ MaybeDeleteSelf();
+ }
+
+ void OnBindingError()
+ {
+ binding_.Close();
+ MaybeDeleteSelf();
+ }
+
+ void MaybeDeleteSelf()
+ {
+ if (!binding_.is_bound() && !client_.is_bound())
+ delete this;
+ }
+
+ void OnFileWritten(MojoResult result)
+ {
+ // All the data has been written now. The consumer will be notified that
+ // there will be no more data to read from now.
+ if (result == MOJO_RESULT_OK)
+ client_->OnComplete(network::URLLoaderCompletionStatus(net::OK));
+ else
+ client_->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
+ client_.reset();
+ MaybeDeleteSelf();
+ }
+
+ mojo::Binding<network::mojom::URLLoader> binding_;
+ network::mojom::URLLoaderClientPtr client_;
+ scoped_refptr<net::HttpResponseHeaders> response_headers_;
+ base::WeakPtrFactory<ResourceBundleFileLoader> weak_factory_{ this };
+
+ DISALLOW_COPY_AND_ASSIGN(ResourceBundleFileLoader);
+};
+
} // namespace
namespace extensions {
@@ -307,7 +466,8 @@ void ExtensionsBrowserClientQt::LoadResourceFromResourceBundle(const network::Re
network::mojom::URLLoaderClientPtr client,
bool send_cors_header)
{
- NOTIMPLEMENTED();
+ ResourceBundleFileLoader::CreateAndStart(request, std::move(loader), client.PassInterface(), resource_relative_path,
+ resource_id, content_security_policy, send_cors_header);
}
diff --git a/src/core/login_delegate_qt.cpp b/src/core/login_delegate_qt.cpp
index f63252112..80e2d9102 100644
--- a/src/core/login_delegate_qt.cpp
+++ b/src/core/login_delegate_qt.cpp
@@ -51,6 +51,8 @@
#include "content/public/browser/resource_dispatcher_host.h"
#include "content/public/browser/resource_request_info.h"
#include "extensions/buildflags/buildflags.h"
+#include "services/network/public/cpp/features.h"
+
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "extensions/browser/info_map.h"
#include "extensions/common/extension.h"
@@ -153,7 +155,9 @@ void LoginDelegateQt::sendAuthToRequester(bool success, const QString &user, con
std::move(m_auth_required_callback).Run(base::nullopt);
}
- destroy();
+ // With network service the auth callback has already deleted us.
+ if (!base::FeatureList::IsEnabled(network::features::kNetworkService))
+ destroy();
}
void LoginDelegateQt::destroy()
diff --git a/src/core/net/cookie_monster_delegate_qt.cpp b/src/core/net/cookie_monster_delegate_qt.cpp
index dad9a8db5..2ddaec526 100644
--- a/src/core/net/cookie_monster_delegate_qt.cpp
+++ b/src/core/net/cookie_monster_delegate_qt.cpp
@@ -44,7 +44,9 @@
#include "base/task/post_task.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
+#include "mojo/public/cpp/bindings/binding.h"
#include "net/cookies/cookie_util.h"
+#include "services/network/public/mojom/cookie_manager.mojom.h"
#include "api/qwebenginecookiestore.h"
#include "api/qwebenginecookiestore_p.h"
@@ -52,6 +54,25 @@
namespace QtWebEngineCore {
+class CookieChangeListener : public network::mojom::CookieChangeListener
+{
+public:
+ CookieChangeListener(CookieMonsterDelegateQt *delegate) : m_delegate(delegate) { }
+ ~CookieChangeListener() override = default;
+
+ // network::mojom::CookieChangeListener:
+ void OnCookieChange(const net::CanonicalCookie &canonical_cookie,
+ network::mojom::CookieChangeCause cause) override
+ {
+ m_delegate->OnCookieChanged(canonical_cookie, net::CookieChangeCause(cause));
+ }
+
+private:
+ CookieMonsterDelegateQt *m_delegate;
+
+ DISALLOW_COPY_AND_ASSIGN(CookieChangeListener);
+};
+
static GURL sourceUrlForCookie(const QNetworkCookie &cookie)
{
QString urlFragment = QStringLiteral("%1%2").arg(cookie.domain()).arg(cookie.path());
@@ -60,7 +81,8 @@ static GURL sourceUrlForCookie(const QNetworkCookie &cookie)
CookieMonsterDelegateQt::CookieMonsterDelegateQt()
: m_client(0)
- , m_cookieMonster(nullptr)
+ , m_listener(new CookieChangeListener(this))
+ , m_binding(m_listener.get())
{
}
@@ -81,22 +103,12 @@ void CookieMonsterDelegateQt::AddStore(net::CookieStore *store)
bool CookieMonsterDelegateQt::hasCookieMonster()
{
- return m_cookieMonster;
+ return m_mojoCookieManager.is_bound();
}
void CookieMonsterDelegateQt::getAllCookies(quint64 callbackId)
{
- net::CookieMonster::GetCookieListCallback callback =
- base::BindOnce(&CookieMonsterDelegateQt::GetAllCookiesCallbackOnIOThread, this, callbackId);
-
- base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
- base::BindOnce(&CookieMonsterDelegateQt::GetAllCookiesOnIOThread, this, std::move(callback)));
-}
-
-void CookieMonsterDelegateQt::GetAllCookiesOnIOThread(net::CookieMonster::GetCookieListCallback callback)
-{
- if (m_cookieMonster)
- m_cookieMonster->GetAllCookiesAsync(std::move(callback));
+ m_mojoCookieManager->GetAllCookies(base::BindOnce(&CookieMonsterDelegateQt::GetAllCookiesCallbackOnUIThread, this, callbackId));
}
void CookieMonsterDelegateQt::setCookie(quint64 callbackId, const QNetworkCookie &cookie, const QUrl &origin)
@@ -105,24 +117,16 @@ void CookieMonsterDelegateQt::setCookie(quint64 callbackId, const QNetworkCookie
Q_ASSERT(m_client);
net::CookieStore::SetCookiesCallback callback;
- if (callbackId != CallbackDirectory::NoCallbackId)
- callback = base::BindOnce(&CookieMonsterDelegateQt::SetCookieCallbackOnIOThread, this, callbackId);
GURL gurl = origin.isEmpty() ? sourceUrlForCookie(cookie) : toGurl(origin);
+ std::string cookie_line = cookie.toRawForm().toStdString();
- base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
- base::BindOnce(&CookieMonsterDelegateQt::SetCookieOnIOThread, this,
- gurl, cookie.toRawForm().toStdString(), std::move(callback)));
-}
-
-void CookieMonsterDelegateQt::SetCookieOnIOThread(const GURL &url, const std::string &cookie_line,
- net::CookieMonster::SetCookiesCallback callback)
-{
+ if (callbackId != CallbackDirectory::NoCallbackId)
+ callback = base::BindOnce(&CookieMonsterDelegateQt::SetCookieCallbackOnUIThread, this, callbackId);
net::CookieOptions options;
options.set_include_httponly();
-
- if (m_cookieMonster)
- m_cookieMonster->SetCookieWithOptionsAsync(url, cookie_line, options, std::move(callback));
+ auto canonCookie = net::CanonicalCookie::Create(gurl, cookie_line, base::Time::Now(), options);
+ m_mojoCookieManager->SetCanonicalCookie(*canonCookie.get(), gurl.scheme(), options, std::move(callback));
}
void CookieMonsterDelegateQt::deleteCookie(const QNetworkCookie &cookie, const QUrl &origin)
@@ -131,53 +135,23 @@ void CookieMonsterDelegateQt::deleteCookie(const QNetworkCookie &cookie, const Q
Q_ASSERT(m_client);
GURL gurl = origin.isEmpty() ? sourceUrlForCookie(cookie) : toGurl(origin);
-
- base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
- base::BindOnce(&CookieMonsterDelegateQt::DeleteCookieOnIOThread, this,
- gurl, cookie.name().toStdString()));
+ std::string cookie_name = cookie.name().toStdString();
+ auto filter = network::mojom::CookieDeletionFilter::New();
+ filter->url = gurl;
+ filter->cookie_name = cookie_name;
+ m_mojoCookieManager->DeleteCookies(std::move(filter), network::mojom::CookieManager::DeleteCookiesCallback());
}
-void CookieMonsterDelegateQt::DeleteCookieOnIOThread(const GURL &url, const std::string &cookie_name)
-{
- if (m_cookieMonster) {
- net::CookieMonster::GetCookieListCallback callback =
- base::BindOnce(&CookieMonsterDelegateQt::GetCookiesToDeleteCallback, this, cookie_name);
- m_cookieMonster->GetAllCookiesForURLAsync(url, std::move(callback));
- }
-}
-
-void CookieMonsterDelegateQt::GetCookiesToDeleteCallback(const std::string &cookie_name, const net::CookieList &cookies,
- const net::CookieStatusList &statusList)
-{
- Q_UNUSED(statusList);
- if (!m_cookieMonster)
- return;
-
- net::CookieList cookiesToDelete;
- for (auto cookie : cookies) {
- if (cookie.Name() == cookie_name)
- cookiesToDelete.push_back(cookie);
- }
- for (auto cookie : cookiesToDelete)
- m_cookieMonster->DeleteCanonicalCookieAsync(cookie, base::DoNothing());
-}
-
-
void CookieMonsterDelegateQt::deleteSessionCookies(quint64 callbackId)
{
Q_ASSERT(hasCookieMonster());
Q_ASSERT(m_client);
- net::CookieMonster::DeleteCallback callback =
- base::BindOnce(&CookieMonsterDelegateQt::DeleteCookiesCallbackOnIOThread, this, callbackId);
- base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
- base::BindOnce(&CookieMonsterDelegateQt::DeleteSessionCookiesOnIOThread, this, std::move(callback)));
-}
-
-void CookieMonsterDelegateQt::DeleteSessionCookiesOnIOThread(net::CookieMonster::DeleteCallback callback)
-{
- if (m_cookieMonster)
- m_cookieMonster->DeleteSessionCookiesAsync(std::move(callback));
+ network::mojom::CookieManager::DeleteCookiesCallback callback =
+ base::BindOnce(&CookieMonsterDelegateQt::DeleteCookiesCallbackOnUIThread, this, callbackId);
+ auto filter = network::mojom::CookieDeletionFilter::New();
+ filter->session_control = network::mojom::CookieDeletionSessionControl::SESSION_COOKIES;
+ m_mojoCookieManager->DeleteCookies(std::move(filter), std::move(callback));
}
void CookieMonsterDelegateQt::deleteAllCookies(quint64 callbackId)
@@ -185,36 +159,34 @@ void CookieMonsterDelegateQt::deleteAllCookies(quint64 callbackId)
Q_ASSERT(hasCookieMonster());
Q_ASSERT(m_client);
- net::CookieMonster::DeleteCallback callback =
- base::BindOnce(&CookieMonsterDelegateQt::DeleteCookiesCallbackOnIOThread, this, callbackId);
- base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
- base::BindOnce(&CookieMonsterDelegateQt::DeleteAllOnIOThread, this, std::move(callback)));
+ network::mojom::CookieManager::DeleteCookiesCallback callback =
+ base::BindOnce(&CookieMonsterDelegateQt::DeleteCookiesCallbackOnUIThread, this, callbackId);
+ auto filter = network::mojom::CookieDeletionFilter::New();
+ m_mojoCookieManager->DeleteCookies(std::move(filter), std::move(callback));
}
-void CookieMonsterDelegateQt::DeleteAllOnIOThread(net::CookieMonster::DeleteCallback callback)
+void CookieMonsterDelegateQt::setMojoCookieManager(network::mojom::CookieManagerPtrInfo cookie_manager_info)
{
- if (m_cookieMonster)
- m_cookieMonster->DeleteAllAsync(std::move(callback));
-}
+// DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-void CookieMonsterDelegateQt::setCookieMonster(net::CookieMonster *monster)
-{
- if (monster == m_cookieMonster)
- return;
+ if (m_binding)
+ unsetMojoCookieManager();
- m_subscriptions.clear();
- if (monster)
- AddStore(monster);
+ m_mojoCookieManager.Bind(std::move(cookie_manager_info));
- m_cookieMonster = monster;
+ network::mojom::CookieChangeListenerPtr listener_ptr;
+ m_binding.Bind(mojo::MakeRequest(&listener_ptr));
- if (!m_client)
- return;
+ m_mojoCookieManager->AddGlobalChangeListener(std::move(listener_ptr));
- if (monster)
+ if (m_client)
m_client->d_func()->processPendingUserCookies();
- else
- m_client->d_func()->rejectPendingUserCookies();
+}
+
+void CookieMonsterDelegateQt::unsetMojoCookieManager()
+{
+ m_binding.Close();
+ m_mojoCookieManager.reset();
}
void CookieMonsterDelegateQt::setClient(QWebEngineCookieStore *client)
@@ -253,32 +225,17 @@ void CookieMonsterDelegateQt::OnCookieChanged(const net::CanonicalCookie &cookie
m_client->d_func()->onCookieChanged(toQt(cookie), cause != net::CookieChangeCause::INSERTED);
}
-void CookieMonsterDelegateQt::GetAllCookiesCallbackOnIOThread(qint64 callbackId, const net::CookieList &cookies, const net::CookieStatusList &statusList)
+void CookieMonsterDelegateQt::GetAllCookiesCallbackOnUIThread(qint64 callbackId, const std::vector<net::CanonicalCookie> &cookies)
{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
QByteArray rawCookies;
for (auto &&cookie : cookies)
rawCookies += toQt(cookie).toRawForm() % QByteArrayLiteral("\n");
- base::PostTaskWithTraits(
- FROM_HERE, {content::BrowserThread::UI},
- base::BindOnce(&CookieMonsterDelegateQt::GetAllCookiesCallbackOnUIThread, this, callbackId, rawCookies));
-}
-
-void CookieMonsterDelegateQt::SetCookieCallbackOnIOThread(qint64 callbackId, net::CanonicalCookie::CookieInclusionStatus status)
-{
- base::PostTaskWithTraits(
- FROM_HERE, {content::BrowserThread::UI},
- base::BindOnce(&CookieMonsterDelegateQt::SetCookieCallbackOnUIThread, this, callbackId, status));
-}
-
-void CookieMonsterDelegateQt::DeleteCookiesCallbackOnIOThread(qint64 callbackId, uint numCookies)
-{
- base::PostTaskWithTraits(
- FROM_HERE, {content::BrowserThread::UI},
- base::BindOnce(&CookieMonsterDelegateQt::DeleteCookiesCallbackOnUIThread, this, callbackId, numCookies));
+ GetAllCookiesResultOnUIThread(callbackId, rawCookies);
}
-void CookieMonsterDelegateQt::GetAllCookiesCallbackOnUIThread(qint64 callbackId, const QByteArray &cookies)
+void CookieMonsterDelegateQt::GetAllCookiesResultOnUIThread(qint64 callbackId, const QByteArray &cookies)
{
if (m_client)
m_client->d_func()->onGetAllCallbackResult(callbackId, cookies);
diff --git a/src/core/net/cookie_monster_delegate_qt.h b/src/core/net/cookie_monster_delegate_qt.h
index 23b803790..f7ebe38c2 100644
--- a/src/core/net/cookie_monster_delegate_qt.h
+++ b/src/core/net/cookie_monster_delegate_qt.h
@@ -57,8 +57,22 @@ QT_WARNING_PUSH
// For some reason adding -Wno-unused-parameter to QMAKE_CXXFLAGS has no
// effect with clang, so use a pragma for these dirty chromium headers
QT_WARNING_DISABLE_CLANG("-Wunused-parameter")
+
+// We need to work around Chromium using 'signals' as a variable name in headers:
+#ifdef signals
+#define StAsH_signals signals
+#undef signals
+#endif
#include "base/memory/ref_counted.h"
-#include "net/cookies/cookie_monster.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "net/cookies/cookie_change_dispatcher.h"
+#include "net/cookies/cookie_store.h"
+#include "services/network/public/mojom/cookie_manager.mojom-forward.h"
+#include "services/network/public/mojom/cookie_manager.mojom.h"
+#ifdef StAsH_signals
+#define signals StAsH_signals
+#undef StAsH_signals
+#endif
QT_WARNING_POP
#include <QNetworkCookie>
@@ -68,18 +82,16 @@ QT_FORWARD_DECLARE_CLASS(QWebEngineCookieStore)
namespace QtWebEngineCore {
-// Extends net::CookieMonster::kDefaultCookieableSchemes with qrc, without enabling
-// cookies for the file:// scheme, which is disabled by default in Chromium.
-// Since qrc:// is similar to file:// and there are some unknowns about how
-// to correctly handle file:// cookies, qrc:// should only be used for testing.
-static const char *const kCookieableSchemes[] = { "http", "https", "qrc", "ws", "wss" };
+class CookieMonsterDelegateQtPrivate;
class Q_WEBENGINECORE_PRIVATE_EXPORT CookieMonsterDelegateQt : public base::RefCountedThreadSafe<CookieMonsterDelegateQt>
{
QPointer<QWebEngineCookieStore> m_client;
- net::CookieMonster *m_cookieMonster;
std::vector<std::unique_ptr<net::CookieChangeSubscription>> m_subscriptions;
+ network::mojom::CookieManagerPtr m_mojoCookieManager;
+ std::unique_ptr<network::mojom::CookieChangeListener> m_listener;
+ mojo::Binding<network::mojom::CookieChangeListener> m_binding;
public:
CookieMonsterDelegateQt();
~CookieMonsterDelegateQt();
@@ -92,8 +104,9 @@ public:
void deleteSessionCookies(quint64 callbackId);
void deleteAllCookies(quint64 callbackId);
- void setCookieMonster(net::CookieMonster *monster);
void setClient(QWebEngineCookieStore *client);
+ void setMojoCookieManager(network::mojom::CookieManagerPtrInfo cookie_manager_info);
+ void unsetMojoCookieManager();
bool canSetCookie(const QUrl &firstPartyUrl, const QByteArray &cookieLine, const QUrl &url) const;
bool canGetCookies(const QUrl &firstPartyUrl, const QUrl &url) const;
@@ -102,21 +115,8 @@ public:
void OnCookieChanged(const net::CanonicalCookie &cookie, net::CookieChangeCause cause);
private:
- void GetAllCookiesOnIOThread(net::CookieMonster::GetCookieListCallback callback);
- void SetCookieOnIOThread(const GURL &url, const std::string &cookie_line,
- net::CookieMonster::SetCookiesCallback callback);
- void DeleteCookieOnIOThread(const GURL &url, const std::string &cookie_name);
- void DeleteSessionCookiesOnIOThread(net::CookieMonster::DeleteCallback callback);
- void DeleteAllOnIOThread(net::CookieMonster::DeleteCallback callback);
-
- void GetCookiesToDeleteCallback(const std::string &cookie_name, const net::CookieList &cookies,
- const net::CookieStatusList &statusList);
- void GetAllCookiesCallbackOnIOThread(qint64 callbackId, const net::CookieList &cookies,
- const net::CookieStatusList &statusList);
- void SetCookieCallbackOnIOThread(qint64 callbackId, net::CanonicalCookie::CookieInclusionStatus status);
- void DeleteCookiesCallbackOnIOThread(qint64 callbackId, uint numCookies);
-
- void GetAllCookiesCallbackOnUIThread(qint64 callbackId, const QByteArray &cookies);
+ void GetAllCookiesCallbackOnUIThread(qint64 callbackId, const std::vector<net::CanonicalCookie> &cookies);
+ void GetAllCookiesResultOnUIThread(qint64 callbackId, const QByteArray &cookies);
void SetCookieCallbackOnUIThread(qint64 callbackId, net::CanonicalCookie::CookieInclusionStatus status);
void DeleteCookiesCallbackOnUIThread(qint64 callbackId, uint numCookies);
};
diff --git a/src/core/net/custom_protocol_handler.cpp b/src/core/net/custom_protocol_handler.cpp
deleted file mode 100644
index 7e8ee47ab..000000000
--- a/src/core/net/custom_protocol_handler.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtWebEngine module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "custom_protocol_handler.h"
-#include "url_request_custom_job.h"
-
-#include "net/base/net_errors.h"
-#include "net/url_request/url_request.h"
-#include "net/url_request/url_request_error_job.h"
-
-namespace QtWebEngineCore {
-
-CustomProtocolHandler::CustomProtocolHandler(QPointer<ProfileAdapter> profileAdapter)
- : m_profileAdapter(profileAdapter)
-{
-}
-
-net::URLRequestJob *CustomProtocolHandler::MaybeCreateJob(net::URLRequest *request, net::NetworkDelegate *networkDelegate) const
-{
- if (!networkDelegate)
- return new net::URLRequestErrorJob(request, nullptr, net::ERR_ACCESS_DENIED);
-
- return new URLRequestCustomJob(request, networkDelegate, request->url().scheme(), m_profileAdapter);
-}
-
-} // namespace
diff --git a/src/core/net/custom_protocol_handler.h b/src/core/net/custom_protocol_handler.h
deleted file mode 100644
index 625afc1d5..000000000
--- a/src/core/net/custom_protocol_handler.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtWebEngine module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//
-// 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.
-//
-
-#ifndef CUSTOM_PROTOCOL_HANDLER_H_
-#define CUSTOM_PROTOCOL_HANDLER_H_
-
-#include "qtwebenginecoreglobal_p.h"
-#include "net/url_request/url_request_job_factory.h"
-
-#include <QtCore/QByteArray>
-#include <QtCore/QObject>
-#include <QtCore/QPointer>
-
-QT_FORWARD_DECLARE_CLASS(QIODevice)
-
-namespace net {
-class NetworkDelegate;
-class URLRequestJob;
-} // namespace
-
-namespace QtWebEngineCore {
-
-class ProfileAdapter;
-
-// Implements a ProtocolHandler for custom URL schemes.
-// If |network_delegate_| is NULL then all file requests will fail with ERR_ACCESS_DENIED.
-class Q_WEBENGINECORE_PRIVATE_EXPORT CustomProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler
-{
-
-public:
- CustomProtocolHandler(QPointer<ProfileAdapter> profileAdapter);
-
- net::URLRequestJob *MaybeCreateJob(net::URLRequest *request, net::NetworkDelegate *networkDelegate) const override;
-
-private:
- DISALLOW_COPY_AND_ASSIGN(CustomProtocolHandler);
- QPointer<ProfileAdapter> m_profileAdapter;
-};
-
-} // namespace
-
-#endif // CUSTOM_PROTOCOL_HANDLER_H_
diff --git a/src/core/net/custom_url_loader_factory.cpp b/src/core/net/custom_url_loader_factory.cpp
new file mode 100644
index 000000000..239494ce9
--- /dev/null
+++ b/src/core/net/custom_url_loader_factory.cpp
@@ -0,0 +1,447 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "custom_url_loader_factory.h"
+
+#include "base/strings/stringprintf.h"
+#include "base/task/post_task.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_status_code.h"
+#include "net/http/http_util.h"
+#include "services/network/public/mojom/url_loader.mojom.h"
+#include "services/network/public/mojom/url_loader_factory.mojom.h"
+
+#include "api/qwebengineurlscheme.h"
+#include "net/url_request_custom_job_proxy.h"
+#include "profile_adapter.h"
+#include "type_conversion.h"
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/qmimedatabase.h>
+#include <QtCore/qmimedata.h>
+#include <QtCore/qurl.h>
+
+namespace QtWebEngineCore {
+
+namespace {
+
+class CustomURLLoader : public network::mojom::URLLoader
+ , private URLRequestCustomJobProxy::Client
+{
+public:
+ static void CreateAndStart(const network::ResourceRequest &request,
+ network::mojom::URLLoaderRequest loader,
+ network::mojom::URLLoaderClientPtrInfo client_info,
+ QPointer<ProfileAdapter> profileAdapter)
+ {
+ // CustomURLLoader will handle its own life-cycle, and delete when
+ // the client lets go.
+ auto *customUrlLoader = new CustomURLLoader(request, std::move(loader), std::move(client_info), profileAdapter);
+ customUrlLoader->Start();
+ }
+
+ // network::mojom::URLLoader:
+ void FollowRedirect(const std::vector<std::string> &removed_headers,
+ const net::HttpRequestHeaders &modified_headers,
+ const base::Optional<GURL> &new_url) override
+ {
+ // We can be asked for follow our own redirect
+ scoped_refptr<URLRequestCustomJobProxy> proxy = new URLRequestCustomJobProxy(this, m_proxy->m_scheme, m_proxy->m_profileAdapter);
+ m_proxy->m_client = nullptr;
+// m_taskRunner->PostTask(FROM_HERE, base::BindOnce(&URLRequestCustomJobProxy::release, m_proxy));
+ base::PostTaskWithTraits(FROM_HERE, { content::BrowserThread::UI },
+ base::BindOnce(&URLRequestCustomJobProxy::release, m_proxy));
+ m_proxy = std::move(proxy);
+ if (new_url)
+ m_request.url = *new_url;
+ else
+ m_request.url = m_redirect;
+ m_redirect = GURL();
+ for (const std::string &header: removed_headers)
+ m_request.headers.RemoveHeader(header);
+ m_request.headers.MergeFrom(modified_headers);
+ Start();
+ }
+ void SetPriority(net::RequestPriority priority, int32_t intra_priority_value) override { }
+ void PauseReadingBodyFromNet() override { }
+ void ResumeReadingBodyFromNet() override { }
+ void ProceedWithResponse() override { }
+
+private:
+ CustomURLLoader(const network::ResourceRequest &request,
+ network::mojom::URLLoaderRequest loader,
+ network::mojom::URLLoaderClientPtrInfo client_info,
+ QPointer<ProfileAdapter> profileAdapter)
+ // ### We can opt to run the url-loader on the UI thread instead
+ : m_taskRunner(base::CreateSingleThreadTaskRunner({ content::BrowserThread::IO }))
+ , m_proxy(new URLRequestCustomJobProxy(this, request.url.scheme(), profileAdapter))
+ , m_binding(this, std::move(loader))
+ , m_client(std::move(client_info))
+ , m_request(request)
+ {
+ DCHECK(m_taskRunner->RunsTasksInCurrentSequence());
+ m_binding.set_connection_error_handler(
+ base::BindOnce(&CustomURLLoader::OnConnectionError, m_weakPtrFactory.GetWeakPtr()));
+ m_firstBytePosition = 0;
+ m_device = nullptr;
+ m_error = 0;
+ QWebEngineUrlScheme scheme = QWebEngineUrlScheme::schemeByName(QByteArray::fromStdString(request.url.scheme()));
+ m_corsEnabled = scheme.flags().testFlag(QWebEngineUrlScheme::CorsEnabled);
+ }
+
+ ~CustomURLLoader() override = default;
+
+ void Start()
+ {
+ DCHECK(m_taskRunner->RunsTasksInCurrentSequence());
+ m_head.request_start = base::TimeTicks::Now();
+
+ if (!m_pipe.consumer_handle.is_valid())
+ return CompleteWithFailure(net::ERR_FAILED);
+
+ std::map<std::string, std::string> headers;
+ net::HttpRequestHeaders::Iterator it(m_request.headers);
+ while (it.GetNext())
+ headers.emplace(it.name(), it.value());
+ if (!m_request.referrer.is_empty())
+ headers.emplace("Referer", m_request.referrer.spec());
+
+ std::string rangeHeader;
+ if (m_request.headers.GetHeader(net::HttpRequestHeaders::kRange, &rangeHeader)) {
+ std::vector<net::HttpByteRange> ranges;
+ if (net::HttpUtil::ParseRangeHeader(rangeHeader, &ranges)) {
+ // Chromium doesn't support multiple range requests in one single URL request.
+ if (ranges.size() == 1)
+ m_firstBytePosition = ranges[0].first_byte_position();
+ }
+ }
+
+// m_taskRunner->PostTask(FROM_HERE,
+ base::PostTaskWithTraits(FROM_HERE, { content::BrowserThread::UI },
+ base::BindOnce(&URLRequestCustomJobProxy::initialize, m_proxy,
+ m_request.url, m_request.method, m_request.request_initiator, std::move(headers)));
+ }
+
+ void CompleteWithFailure(net::Error net_error)
+ {
+ DCHECK(m_taskRunner->RunsTasksInCurrentSequence());
+ m_client->OnComplete(network::URLLoaderCompletionStatus(net_error));
+ ClearProxyAndClient(false);
+ }
+
+ void OnConnectionError()
+ {
+ DCHECK(m_taskRunner->RunsTasksInCurrentSequence());
+ m_binding.Close();
+ if (m_client.is_bound())
+ ClearProxyAndClient(false);
+ else
+ delete this;
+ }
+
+ void OnTransferComplete(MojoResult result)
+ {
+ DCHECK(m_taskRunner->RunsTasksInCurrentSequence());
+ if (result == MOJO_RESULT_OK) {
+ network::URLLoaderCompletionStatus status(net::OK);
+ status.encoded_data_length = m_totalBytesRead + m_head.headers->raw_headers().length();
+ status.encoded_body_length = m_totalBytesRead;
+ status.decoded_body_length = m_totalBytesRead;
+ m_client->OnComplete(status);
+ } else {
+ m_client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
+ }
+ ClearProxyAndClient(false /* result == MOJO_RESULT_OK */);
+ }
+
+ void ClearProxyAndClient(bool wait_for_loader_error = false)
+ {
+ DCHECK(m_taskRunner->RunsTasksInCurrentSequence());
+ m_proxy->m_client = nullptr;
+ m_client.reset();
+ if (m_device && m_device->isOpen())
+ m_device->close();
+ m_device = nullptr;
+// m_taskRunner->PostTask(FROM_HERE, base::BindOnce(&URLRequestCustomJobProxy::release, m_proxy));
+ base::PostTaskWithTraits(FROM_HERE, { content::BrowserThread::UI },
+ base::BindOnce(&URLRequestCustomJobProxy::release, m_proxy));
+ if (!wait_for_loader_error || !m_binding.is_bound())
+ delete this;
+ }
+
+ // URLRequestCustomJobProxy::Client:
+ void notifyExpectedContentSize(qint64 size) override
+ {
+ DCHECK(m_taskRunner->RunsTasksInCurrentSequence());
+ m_head.content_length = size;
+ }
+ void notifyHeadersComplete() override
+ {
+ DCHECK(m_taskRunner->RunsTasksInCurrentSequence());
+ DCHECK(!m_error);
+ m_head.response_start = base::TimeTicks::Now();
+
+ std::string headers;
+ if (!m_redirect.is_empty()) {
+ headers += "HTTP/1.1 303 See Other\n";
+ headers += base::StringPrintf("Location: %s\n", m_redirect.spec().c_str());
+ } else {
+ headers += "HTTP/1.1 200 OK\n";
+ if (m_mimeType.size() > 0) {
+ headers += base::StringPrintf("Content-Type: %s", m_mimeType.c_str());
+ if (m_charset.size() > 0)
+ headers += base::StringPrintf("; charset=%s", m_charset.c_str());
+ headers += "\n";
+ }
+ }
+ if (m_corsEnabled) {
+ std::string origin;
+ if (m_request.headers.GetHeader("Origin", &origin)) {
+ headers += base::StringPrintf("Access-Control-Allow-Origin: %s\n", origin.c_str());
+ headers += "Access-Control-Allow-Credentials: true\n";
+ }
+ }
+ m_head.headers = base::MakeRefCounted<net::HttpResponseHeaders>(net::HttpUtil::AssembleRawHeaders(headers));
+ m_head.encoded_data_length = m_head.headers->raw_headers().length();
+
+ if (!m_redirect.is_empty()) {
+ m_head.content_length = m_head.encoded_body_length = -1;
+ net::URLRequest::FirstPartyURLPolicy first_party_url_policy =
+ m_request.update_first_party_url_on_redirect ? net::URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT
+ : net::URLRequest::NEVER_CHANGE_FIRST_PARTY_URL;
+ net::RedirectInfo redirectInfo = net::RedirectInfo::ComputeRedirectInfo(
+ m_request.method, m_request.url,
+ m_request.site_for_cookies, m_request.top_frame_origin,
+ first_party_url_policy, m_request.referrer_policy,
+ m_request.referrer.spec(), net::HTTP_SEE_OTHER,
+ m_redirect, base::nullopt, false /*insecure_scheme_was_upgraded*/);
+ m_client->OnReceiveRedirect(redirectInfo, m_head);
+ // ### should m_request be updated with RedirectInfo? (see FollowRedirect)
+ return;
+ }
+ DCHECK(m_device);
+ m_head.mime_type = m_mimeType;
+ m_head.charset = m_charset;
+ m_client->OnReceiveResponse(m_head);
+ m_client->OnStartLoadingResponseBody(std::move(m_pipe.consumer_handle));
+
+ if (readAvailableData()) // May delete this
+ return;
+
+ m_watcher = std::make_unique<mojo::SimpleWatcher>(
+ FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC, m_taskRunner);
+ m_watcher->Watch(m_pipe.producer_handle.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
+ MOJO_WATCH_CONDITION_SATISFIED,
+ base::BindRepeating(&CustomURLLoader::notifyReadyWrite,
+ m_weakPtrFactory.GetWeakPtr()));
+ }
+ void notifyCanceled() override
+ {
+ DCHECK(m_taskRunner->RunsTasksInCurrentSequence());
+ OnTransferComplete(MOJO_RESULT_CANCELLED);
+ }
+ void notifyAborted() override
+ {
+ DCHECK(m_taskRunner->RunsTasksInCurrentSequence());
+ notifyStartFailure(net::ERR_ABORTED);
+ }
+ void notifyStartFailure(int error) override
+ {
+ DCHECK(m_taskRunner->RunsTasksInCurrentSequence());
+ m_head.response_start = base::TimeTicks::Now();
+ std::string headers;
+ switch (error) {
+ case net::ERR_INVALID_URL:
+ headers = "HTTP/1.1 400 Bad Request\n";
+ break;
+ case net::ERR_FILE_NOT_FOUND:
+ headers = "HTTP/1.1 404 Not Found\n";
+ break;
+ case net::ERR_ABORTED:
+ headers = "HTTP/1.1 503 Request Aborted\n";
+ break;
+ case net::ERR_ACCESS_DENIED:
+ headers = "HTTP/1.1 403 Forbidden\n";
+ break;
+ case net::ERR_FAILED:
+ headers = "HTTP/1.1 400 Request Failed\n";
+ break;
+ default:
+ headers = "HTTP/1.1 500 Internal Error\n";
+ break;
+ }
+ m_head.headers = base::MakeRefCounted<net::HttpResponseHeaders>(net::HttpUtil::AssembleRawHeaders(headers));
+ m_head.encoded_data_length = m_head.headers->raw_headers().length();
+ m_head.content_length = m_head.encoded_body_length = -1;
+ m_client->OnReceiveResponse(m_head);
+ CompleteWithFailure(net::Error(error));
+ }
+ void notifyReadyRead() override
+ {
+ DCHECK(m_taskRunner->RunsTasksInCurrentSequence());
+ readAvailableData();
+ }
+ void notifyReadyWrite(MojoResult result, const mojo::HandleSignalsState &state)
+ {
+ DCHECK(m_taskRunner->RunsTasksInCurrentSequence());
+ if (result != MOJO_RESULT_OK) {
+ CompleteWithFailure(net::ERR_FAILED);
+ return;
+ }
+ readAvailableData();
+ }
+ bool readAvailableData()
+ {
+ DCHECK(m_taskRunner->RunsTasksInCurrentSequence());
+ for (;;) {
+ if (m_error || !m_device)
+ break;
+
+ void *buffer = nullptr;
+ uint32_t bufferSize = 0;
+ MojoResult beginResult = m_pipe.producer_handle->BeginWriteData(
+ &buffer, &bufferSize, MOJO_BEGIN_WRITE_DATA_FLAG_NONE);
+ if (beginResult == MOJO_RESULT_SHOULD_WAIT)
+ return false; // Wait for pipe watcher
+ if (beginResult != MOJO_RESULT_OK)
+ break;
+
+ int readResult = m_device->read(static_cast<char *>(buffer), bufferSize);
+ uint32_t bytesRead = std::max(readResult, 0);
+ m_pipe.producer_handle->EndWriteData(bytesRead);
+ m_totalBytesRead += bytesRead;
+ m_client->OnTransferSizeUpdated(m_totalBytesRead);
+
+ if (m_device->atEnd()) {
+ OnTransferComplete(MOJO_RESULT_OK);
+ return true; // Done with reading
+ }
+
+ if (readResult == 0)
+ return false; // Wait for readyRead
+ if (readResult < 0)
+ break;
+ }
+
+ CompleteWithFailure(m_error ? net::Error(m_error) : net::ERR_FAILED);
+ return true; // Done with reading
+ }
+ base::TaskRunner *taskRunner() override
+ {
+ DCHECK(m_taskRunner->RunsTasksInCurrentSequence());
+ return m_taskRunner.get();
+ }
+
+ scoped_refptr<base::SequencedTaskRunner> m_taskRunner;
+ scoped_refptr<URLRequestCustomJobProxy> m_proxy;
+
+ mojo::Binding<network::mojom::URLLoader> m_binding;
+ network::mojom::URLLoaderClientPtr m_client;
+ mojo::DataPipe m_pipe;
+ std::unique_ptr<mojo::SimpleWatcher> m_watcher;
+
+ network::ResourceRequest m_request;
+ network::ResourceResponseHead m_head;
+ qint64 m_totalBytesRead = 0;
+ bool m_corsEnabled;
+
+ base::WeakPtrFactory<CustomURLLoader> m_weakPtrFactory{this};
+
+ DISALLOW_COPY_AND_ASSIGN(CustomURLLoader);
+};
+
+class CustomURLLoaderFactory : public network::mojom::URLLoaderFactory {
+public:
+ CustomURLLoaderFactory(ProfileAdapter *profileAdapter)
+ : m_taskRunner(base::CreateSequencedTaskRunner({ content::BrowserThread::IO }))
+ , m_profileAdapter(profileAdapter)
+ {
+ }
+ ~CustomURLLoaderFactory() override = default;
+
+ // network::mojom::URLLoaderFactory:
+ void CreateLoaderAndStart(network::mojom::URLLoaderRequest loader,
+ int32_t routing_id,
+ int32_t request_id,
+ uint32_t options,
+ const network::ResourceRequest &request,
+ network::mojom::URLLoaderClientPtr client,
+ const net::MutableNetworkTrafficAnnotationTag &traffic_annotation) override
+ {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ Q_UNUSED(routing_id);
+ Q_UNUSED(request_id);
+ Q_UNUSED(options);
+ Q_UNUSED(traffic_annotation);
+
+ m_taskRunner->PostTask(FROM_HERE,
+ base::BindOnce(&CustomURLLoader::CreateAndStart, request,
+ std::move(loader), client.PassInterface(),
+ m_profileAdapter));
+
+ }
+
+ void Clone(network::mojom::URLLoaderFactoryRequest request) override
+ {
+ m_bindings.AddBinding(this, std::move(request));
+ }
+
+ const scoped_refptr<base::SequencedTaskRunner> m_taskRunner;
+ mojo::BindingSet<network::mojom::URLLoaderFactory> m_bindings;
+ QPointer<ProfileAdapter> m_profileAdapter;
+ DISALLOW_COPY_AND_ASSIGN(CustomURLLoaderFactory);
+};
+
+} // namespace
+
+std::unique_ptr<network::mojom::URLLoaderFactory> CreateCustomURLLoaderFactory(ProfileAdapter *profileAdapter)
+{
+ return std::make_unique<CustomURLLoaderFactory>(profileAdapter);
+}
+
+} // namespace QtWebEngineCore
+
diff --git a/src/core/net/url_request_context_getter_qt.h b/src/core/net/custom_url_loader_factory.h
index a6ef6eae0..58adf4b79 100644
--- a/src/core/net/url_request_context_getter_qt.h
+++ b/src/core/net/custom_url_loader_factory.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtWebEngine module of the Qt Toolkit.
@@ -37,27 +37,33 @@
**
****************************************************************************/
-#ifndef URL_REQUEST_CONTEXT_GETTER_QT_H
-#define URL_REQUEST_CONTEXT_GETTER_QT_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 "net/url_request/url_request_context_getter.h"
+#ifndef CUSTOM_URL_LOADER_FACTORY_H_
+#define CUSTOM_URL_LOADER_FACTORY_H_
-namespace QtWebEngineCore {
+#include <memory>
-class ProfileIODataQt;
+namespace network {
+namespace mojom {
+class URLLoaderFactory;
+} // namespace mojom
+} // namespace network
-class URLRequestContextGetterQt : public net::URLRequestContextGetter
-{
-public:
- URLRequestContextGetterQt(ProfileIODataQt *data);
- net::URLRequestContext *GetURLRequestContext() override;
- scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner() const override;
+namespace QtWebEngineCore {
+class ProfileAdapter;
-private:
- virtual ~URLRequestContextGetterQt();
- ProfileIODataQt *m_profileIOData;
-};
+std::unique_ptr<network::mojom::URLLoaderFactory> CreateCustomURLLoaderFactory(ProfileAdapter *profileAdapter);
} // namespace QtWebEngineCore
-#endif // URL_REQUEST_CONTEXT_GETTER_QT_H
+#endif // CUSTOM_URL_LOADER_FACTORY_H_
diff --git a/src/core/net/network_delegate_qt.cpp b/src/core/net/network_delegate_qt.cpp
deleted file mode 100644
index 7d3801ffe..000000000
--- a/src/core/net/network_delegate_qt.cpp
+++ /dev/null
@@ -1,305 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtWebEngine module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "network_delegate_qt.h"
-
-#include "base/task/post_task.h"
-#include "content/browser/web_contents/web_contents_impl.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/render_view_host.h"
-#include "content/public/browser/resource_request_info.h"
-#include "net/base/load_flags.h"
-#include "net/url_request/url_request.h"
-#include "ui/base/page_transition_types.h"
-
-#include "profile_adapter.h"
-#include "cookie_monster_delegate_qt.h"
-#include "profile_io_data_qt.h"
-#include "qwebengineurlrequestinfo.h"
-#include "qwebengineurlrequestinfo_p.h"
-#include "qwebengineurlrequestinterceptor.h"
-#include "type_conversion.h"
-#include "web_contents_adapter_client.h"
-#include "web_contents_view_qt.h"
-#include "url_request_notification.h"
-
-namespace QtWebEngineCore {
-
-WebContentsAdapterClient::NavigationType pageTransitionToNavigationType(ui::PageTransition transition)
-{
- if (ui::PageTransitionIsRedirect(transition))
- return WebContentsAdapterClient::RedirectNavigation;
-
- int32_t qualifier = ui::PageTransitionGetQualifier(transition);
-
- if (qualifier & ui::PAGE_TRANSITION_FORWARD_BACK)
- return WebContentsAdapterClient::BackForwardNavigation;
-
- ui::PageTransition strippedTransition = ui::PageTransitionStripQualifier(transition);
-
- switch (strippedTransition) {
- case ui::PAGE_TRANSITION_LINK:
- return WebContentsAdapterClient::LinkNavigation;
- case ui::PAGE_TRANSITION_TYPED:
- return WebContentsAdapterClient::TypedNavigation;
- case ui::PAGE_TRANSITION_FORM_SUBMIT:
- return WebContentsAdapterClient::FormSubmittedNavigation;
- case ui::PAGE_TRANSITION_RELOAD:
- return WebContentsAdapterClient::ReloadNavigation;
- default:
- return WebContentsAdapterClient::OtherNavigation;
- }
-}
-
-static QWebEngineUrlRequestInfo::ResourceType toQt(content::ResourceType resourceType)
-{
- if (resourceType >= content::ResourceType::kMainFrame && resourceType <= content::ResourceType::kMaxValue)
- return static_cast<QWebEngineUrlRequestInfo::ResourceType>(resourceType);
- return QWebEngineUrlRequestInfo::ResourceTypeUnknown;
-}
-
-static QWebEngineUrlRequestInfo::NavigationType toQt(WebContentsAdapterClient::NavigationType navigationType)
-{
- return static_cast<QWebEngineUrlRequestInfo::NavigationType>(navigationType);
-}
-
-NetworkDelegateQt::NetworkDelegateQt(ProfileIODataQt *data)
- : m_profileIOData(data)
-{
-}
-
-int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::CompletionOnceCallback callback, GURL *newUrl)
-{
- Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
- Q_ASSERT(m_profileIOData);
- content::ResourceRequestInfo *resourceInfo = content::ResourceRequestInfo::ForRequest(request);
-
- content::ResourceType resourceType = content::ResourceType::kMaxValue;
- WebContentsAdapterClient::NavigationType navigationType = WebContentsAdapterClient::OtherNavigation;
-
- if (resourceInfo) {
- resourceType = resourceInfo->GetResourceType();
- navigationType = pageTransitionToNavigationType(resourceInfo->GetPageTransition());
- }
-
- const QUrl qUrl = toQt(request->url());
-
- QUrl firstPartyUrl = QUrl();
- if (resourceType == content::ResourceType::kSubFrame)
- firstPartyUrl = toQt(request->first_party_url());
- else
- firstPartyUrl = toQt(request->site_for_cookies());
-
- const QUrl initiator = request->initiator().has_value() ? toQt(request->initiator()->GetURL()) : QUrl();
-
- QWebEngineUrlRequestInfoPrivate *infoPrivate = new QWebEngineUrlRequestInfoPrivate(toQt(resourceType),
- toQt(navigationType),
- qUrl,
- firstPartyUrl,
- initiator,
- QByteArray::fromStdString(request->method()));
- QWebEngineUrlRequestInfo requestInfo(infoPrivate);
-
- // Deprecated =begin
- // quick peek if deprecated
-
- if (m_profileIOData->isInterceptorDeprecated()) {
- QWebEngineUrlRequestInterceptor *profileInterceptor = m_profileIOData->acquireInterceptor();
- if (profileInterceptor && m_profileIOData->isInterceptorDeprecated()) {
- profileInterceptor->interceptRequest(requestInfo);
- m_profileIOData->releaseInterceptor();
- if (requestInfo.changed()) {
- int result = infoPrivate->shouldBlockRequest ? net::ERR_BLOCKED_BY_CLIENT : net::OK;
-
- if (qUrl != infoPrivate->url)
- *newUrl = toGurl(infoPrivate->url);
-
- if (!infoPrivate->extraHeaders.isEmpty()) {
- auto end = infoPrivate->extraHeaders.constEnd();
- for (auto header = infoPrivate->extraHeaders.constBegin(); header != end; ++header) {
- std::string h = header.key().toStdString();
- if (base::LowerCaseEqualsASCII(h, "referer")) {
- request->SetReferrer(header.value().toStdString());
- } else {
- request->SetExtraRequestHeaderByName(h, header.value().toStdString(), /* overwrite */ true);
- }
- }
- }
-
- if (result != net::OK)
- return result;
-
- requestInfo.resetChanged();
- }
- } else {
- m_profileIOData->releaseInterceptor();
- }
- }
- // Deprecated =cut
-
- if (!resourceInfo)
- return net::OK;
-
- // try to bail out
- if (!m_profileIOData->hasPageInterceptors() && (!m_profileIOData->requestInterceptor() || m_profileIOData->isInterceptorDeprecated()) &&
- !content::IsResourceTypeFrame(resourceType))
- return net::OK;
-
- auto webContentsGetter = resourceInfo->GetWebContentsGetterForRequest();
- new URLRequestNotification(
- request,
- resourceInfo->IsMainFrame(),
- newUrl,
- std::move(requestInfo),
- webContentsGetter,
- std::move(callback),
- m_profileIOData->profileAdapter()
- );
-
- // We'll run the callback after we notified the UI thread.
- return net::ERR_IO_PENDING;
-}
-
-void NetworkDelegateQt::OnURLRequestDestroyed(net::URLRequest *) {}
-
-void NetworkDelegateQt::OnCompleted(net::URLRequest * /*request*/, bool /*started*/, int /*net_error*/) {}
-
-bool NetworkDelegateQt::OnCanSetCookie(const net::URLRequest &request, const net::CanonicalCookie & /*cookie*/,
- net::CookieOptions *, bool allowedFromCaller)
-{
- if (!allowedFromCaller)
- return false;
- return canSetCookies(request.site_for_cookies(), request.url(), std::string());
-}
-
-bool NetworkDelegateQt::OnCanGetCookies(const net::URLRequest &request, const net::CookieList &, bool allowedFromCaller)
-{
- if (!allowedFromCaller)
- return false;
- return canGetCookies(request.site_for_cookies(), request.url());
-}
-
-bool NetworkDelegateQt::OnForcePrivacyMode(const GURL &url, const GURL &site_for_cookies) const
-{
- return false;
- // FIXME: This is what the NetworkContext implementation does (changes tst_QWebEngineCookieStore tests since 72)
- // return !canGetCookies(site_for_cookies, url);
-}
-
-bool NetworkDelegateQt::canSetCookies(const GURL &first_party, const GURL &url, const std::string &cookie_line) const
-{
- Q_ASSERT(m_profileIOData);
- return m_profileIOData->canSetCookie(toQt(first_party), QByteArray::fromStdString(cookie_line), toQt(url));
-}
-
-bool NetworkDelegateQt::canGetCookies(const GURL &first_party, const GURL &url) const
-{
- Q_ASSERT(m_profileIOData);
- return m_profileIOData->canGetCookies(toQt(first_party), toQt(url));
-}
-
-int NetworkDelegateQt::OnBeforeStartTransaction(net::URLRequest *, net::CompletionOnceCallback, net::HttpRequestHeaders *)
-{
- return net::OK;
-}
-
-void NetworkDelegateQt::OnBeforeSendHeaders(net::URLRequest *request, const net::ProxyInfo &proxy_info,
- const net::ProxyRetryInfoMap &proxy_retry_info,
- net::HttpRequestHeaders *headers)
-{}
-
-void NetworkDelegateQt::OnStartTransaction(net::URLRequest *request, const net::HttpRequestHeaders &headers) {}
-
-int NetworkDelegateQt::OnHeadersReceived(net::URLRequest *, net::CompletionOnceCallback, const net::HttpResponseHeaders *,
- scoped_refptr<net::HttpResponseHeaders> *, GURL *)
-{
- return net::OK;
-}
-
-void NetworkDelegateQt::OnBeforeRedirect(net::URLRequest *, const GURL &) {}
-
-void NetworkDelegateQt::OnResponseStarted(net::URLRequest *, int) {}
-
-void NetworkDelegateQt::OnNetworkBytesReceived(net::URLRequest *, int64_t) {}
-
-void NetworkDelegateQt::OnNetworkBytesSent(net::URLRequest *, int64_t) {}
-
-void NetworkDelegateQt::OnPACScriptError(int, const base::string16 &) {}
-
-net::NetworkDelegate::AuthRequiredResponse NetworkDelegateQt::OnAuthRequired(net::URLRequest *,
- const net::AuthChallengeInfo &,
- AuthCallback, net::AuthCredentials *)
-{
- return AUTH_REQUIRED_RESPONSE_NO_ACTION;
-}
-
-bool NetworkDelegateQt::OnCanAccessFile(const net::URLRequest &, const base::FilePath &, const base::FilePath &) const
-{
- return true;
-}
-
-bool NetworkDelegateQt::OnCancelURLRequestWithPolicyViolatingReferrerHeader(const net::URLRequest &, const GURL &,
- const GURL &) const
-{
- return false;
-}
-
-bool NetworkDelegateQt::OnCanQueueReportingReport(const url::Origin &origin) const
-{
- return false;
-}
-
-void NetworkDelegateQt::OnCanSendReportingReports(std::set<url::Origin> origins,
- base::OnceCallback<void(std::set<url::Origin>)> result_callback) const
-{
- std::move(result_callback).Run(std::set<url::Origin>());
-}
-
-bool NetworkDelegateQt::OnCanSetReportingClient(const url::Origin &origin, const GURL &endpoint) const
-{
- return false;
-}
-
-bool NetworkDelegateQt::OnCanUseReportingClient(const url::Origin &origin, const GURL &endpoint) const
-{
- return false;
-}
-
-} // namespace QtWebEngineCore
diff --git a/src/core/net/network_delegate_qt.h b/src/core/net/network_delegate_qt.h
deleted file mode 100644
index f294c6c7c..000000000
--- a/src/core/net/network_delegate_qt.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtWebEngine module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef NETWORK_DELEGATE_QT_H
-#define NETWORK_DELEGATE_QT_H
-
-#include "net/base/network_delegate.h"
-#include "net/base/net_errors.h"
-
-#include <QUrl>
-#include <QSet>
-
-namespace content {
-class WebContents;
-}
-
-namespace QtWebEngineCore {
-
-class ProfileIODataQt;
-
-class NetworkDelegateQt : public net::NetworkDelegate
-{
- ProfileIODataQt *m_profileIOData;
-
-public:
- NetworkDelegateQt(ProfileIODataQt *data);
-
- // net::NetworkDelegate implementation
- int OnBeforeURLRequest(net::URLRequest *request, net::CompletionOnceCallback callback, GURL *new_url) override;
- void OnURLRequestDestroyed(net::URLRequest *request) override;
- bool OnCanSetCookie(const net::URLRequest &request, const net::CanonicalCookie &cookie, net::CookieOptions *options,
- bool) override;
- int OnBeforeStartTransaction(net::URLRequest *request, const net::CompletionOnceCallback callback,
- net::HttpRequestHeaders *headers) override;
- void OnBeforeSendHeaders(net::URLRequest *request, const net::ProxyInfo &proxy_info,
- const net::ProxyRetryInfoMap &proxy_retry_info, net::HttpRequestHeaders *headers) override;
- void OnStartTransaction(net::URLRequest *request, const net::HttpRequestHeaders &headers) override;
- int OnHeadersReceived(net::URLRequest *, net::CompletionOnceCallback, const net::HttpResponseHeaders *,
- scoped_refptr<net::HttpResponseHeaders> *, GURL *) override;
- void OnBeforeRedirect(net::URLRequest *, const GURL &) override;
- void OnResponseStarted(net::URLRequest *, int) override;
- void OnNetworkBytesReceived(net::URLRequest *, int64_t) override;
- void OnNetworkBytesSent(net::URLRequest *, int64_t) override;
- void OnCompleted(net::URLRequest *request, bool started, int net_error) override;
- void OnPACScriptError(int, const base::string16 &) override;
- net::NetworkDelegate::AuthRequiredResponse OnAuthRequired(net::URLRequest *, const net::AuthChallengeInfo &,
- AuthCallback, net::AuthCredentials *) override;
- bool OnCanGetCookies(const net::URLRequest &, const net::CookieList &, bool) override;
- bool OnCanAccessFile(const net::URLRequest &, const base::FilePath &, const base::FilePath &) const override;
- bool OnForcePrivacyMode(const GURL &, const GURL &) const override;
- bool OnCancelURLRequestWithPolicyViolatingReferrerHeader(const net::URLRequest &, const GURL &,
- const GURL &) const override;
-
- bool OnCanQueueReportingReport(const url::Origin &origin) const override;
- void OnCanSendReportingReports(std::set<url::Origin> origins,
- base::OnceCallback<void(std::set<url::Origin>)> result_callback) const override;
- bool OnCanSetReportingClient(const url::Origin &origin, const GURL &endpoint) const override;
- bool OnCanUseReportingClient(const url::Origin &origin, const GURL &endpoint) const override;
-
- bool canSetCookies(const GURL &first_party, const GURL &url, const std::string &cookie_line) const;
- bool canGetCookies(const GURL &first_party, const GURL &url) const;
-};
-
-} // namespace QtWebEngineCore
-
-#endif // NETWORK_DELEGATE_QT_H
diff --git a/src/core/net/plugin_response_interceptor_url_loader_throttle.cpp b/src/core/net/plugin_response_interceptor_url_loader_throttle.cpp
new file mode 100644
index 000000000..cfd011ade
--- /dev/null
+++ b/src/core/net/plugin_response_interceptor_url_loader_throttle.cpp
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "plugin_response_interceptor_url_loader_throttle.h"
+
+#include "base/bind.h"
+#include "base/task/post_task.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/download_manager.h"
+#include "content/public/browser/download_request_utils.h"
+#include "content/public/browser/download_utils.h"
+#include "content/public/common/resource_type.h"
+#include "extensions/common/constants.h"
+#include "extensions/common/extension.h"
+
+#include "extensions/extension_system_qt.h"
+#include "profile_adapter.h"
+#include "profile_io_data_qt.h"
+#include "profile_qt.h"
+#include "web_contents_delegate_qt.h"
+
+#include <string>
+
+namespace QtWebEngineCore {
+
+void onPdfStreamIntercepted(const GURL &original_url, std::string extension_id, int frame_tree_node_id)
+{
+ content::WebContents *web_contents = content::WebContents::FromFrameTreeNodeId(frame_tree_node_id);
+ if (!web_contents)
+ return;
+
+ WebContentsDelegateQt *contentsDelegate = static_cast<WebContentsDelegateQt *>(web_contents->GetDelegate());
+ if (!contentsDelegate)
+ return;
+
+ WebEngineSettings *settings = contentsDelegate->webEngineSettings();
+ if (!settings->testAttribute(WebEngineSettings::PdfViewerEnabled)
+ || !settings->testAttribute(WebEngineSettings::PluginsEnabled)) {
+ // If the applications has been set up to always download PDF files to open them in an
+ // external viewer, trigger the download.
+ std::unique_ptr<download::DownloadUrlParameters> params(
+ content::DownloadRequestUtils::CreateDownloadForWebContentsMainFrame(web_contents, original_url,
+ MISSING_TRAFFIC_ANNOTATION));
+ content::BrowserContext::GetDownloadManager(web_contents->GetBrowserContext())->DownloadUrl(std::move(params));
+ return;
+ }
+
+ // The URL passes the original pdf resource url, that will be requested
+ // by the pdf viewer extension page.
+ content::NavigationController::LoadURLParams params(
+ GURL(base::StringPrintf("%s://%s/index.html?%s", extensions::kExtensionScheme,
+ extension_id.c_str(), original_url.spec().c_str())));
+
+ params.frame_tree_node_id = frame_tree_node_id;
+ web_contents->GetController().LoadURLWithParams(params);
+}
+
+
+PluginResponseInterceptorURLLoaderThrottle::PluginResponseInterceptorURLLoaderThrottle(
+ content::ResourceContext *resource_context, int resource_type, int frame_tree_node_id)
+ : m_resource_context(resource_context), m_resource_type(resource_type), m_frame_tree_node_id(frame_tree_node_id)
+{}
+
+PluginResponseInterceptorURLLoaderThrottle::PluginResponseInterceptorURLLoaderThrottle(
+ content::BrowserContext *browser_context, int resource_type, int frame_tree_node_id)
+ : m_browser_context(browser_context), m_resource_type(resource_type), m_frame_tree_node_id(frame_tree_node_id)
+{}
+
+void PluginResponseInterceptorURLLoaderThrottle::WillProcessResponse(const GURL &response_url,
+ network::ResourceResponseHead *response_head,
+ bool *defer)
+{
+ Q_UNUSED(defer);
+ if (content::download_utils::MustDownload(response_url, response_head->headers.get(), response_head->mime_type))
+ return;
+
+ if (m_resource_context) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+ } else {
+ DCHECK(m_browser_context);
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ }
+
+ std::string extension_id;
+ // FIXME: We should use extensions::InfoMap in the future:
+ if (response_head->mime_type == "application/pdf")
+ extension_id = extension_misc::kPdfExtensionId;
+ if (extension_id.empty())
+ return;
+
+ base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
+ base::BindOnce(&onPdfStreamIntercepted,
+ response_url,
+ extension_id,
+ m_frame_tree_node_id));
+}
+
+} // namespace QtWebEngineCore
diff --git a/src/core/net/url_request_notification.h b/src/core/net/plugin_response_interceptor_url_loader_throttle.h
index 673e07bf0..82384e11e 100644
--- a/src/core/net/url_request_notification.h
+++ b/src/core/net/plugin_response_interceptor_url_loader_throttle.h
@@ -37,50 +37,40 @@
**
****************************************************************************/
-#ifndef URL_REQUEST_NOTIFIACTION_H
-#define URL_REQUEST_NOTIFIACTION_H
+#ifndef PLUGIN_RESPONSE_INTERCEPTOR_URL_LOADER_THROTTLE_H_
+#define PLUGIN_RESPONSE_INTERCEPTOR_URL_LOADER_THROTTLE_H_
-#include "content/public/browser/resource_request_info.h"
-#include "net/base/completion_once_callback.h"
-#include "qwebengineurlrequestinfo.h"
-#include <QPointer>
+#include "base/macros.h"
+#include "content/public/common/url_loader_throttle.h"
-class GURL;
-
-namespace net {
-class URLRequest;
+namespace content {
+class BrowserContext;
+class ResourceContext;
}
namespace QtWebEngineCore {
-class ProfileAdapter;
-class ProfileIoDataQt;
-
-// Notifies WebContentsAdapterClient of a new URLRequest.
-class URLRequestNotification
+class PluginResponseInterceptorURLLoaderThrottle : public content::URLLoaderThrottle
{
public:
- URLRequestNotification(net::URLRequest *request,
- bool isMainFrameRequest,
- GURL *newUrl,
- QWebEngineUrlRequestInfo &&requestInfo,
- content::ResourceRequestInfo::WebContentsGetter webContentsGetter,
- net::CompletionOnceCallback callback,
- QPointer<ProfileAdapter> adapter);
- ~URLRequestNotification() = default;
- void cancel();
- void notify();
- void complete(int error);
+ PluginResponseInterceptorURLLoaderThrottle(content::ResourceContext *resource_context,
+ int resource_type, int frame_tree_node_id);
+ PluginResponseInterceptorURLLoaderThrottle(content::BrowserContext *browser_context,
+ int resource_type, int frame_tree_node_id);
+ ~PluginResponseInterceptorURLLoaderThrottle() override = default;
private:
- net::URLRequest *m_request; //used only by io thread
- bool m_isMainFrameRequest;
- GURL *m_newUrl;
- const QUrl m_originalUrl;
- QWebEngineUrlRequestInfo m_requestInfo;
- content::ResourceRequestInfo::WebContentsGetter m_webContentsGetter;
- net::CompletionOnceCallback m_callback;
- QPointer<ProfileAdapter> m_profileAdapter;
+ // content::URLLoaderThrottle overrides;
+ void WillProcessResponse(const GURL &response_url, network::ResourceResponseHead *response_head, bool *defer) override;
+
+ content::ResourceContext *m_resource_context = nullptr;
+ content::BrowserContext *m_browser_context = nullptr;
+ const int m_resource_type;
+ const int m_frame_tree_node_id;
+
+ DISALLOW_COPY_AND_ASSIGN(PluginResponseInterceptorURLLoaderThrottle);
};
-}
-#endif
+
+} // namespace QtWebEngineCore
+
+#endif // PLUGIN_RESPONSE_INTERCEPTOR_URL_LOADER_THROTTLE_H_
diff --git a/src/core/net/proxy_config_monitor.cpp b/src/core/net/proxy_config_monitor.cpp
new file mode 100644
index 000000000..818b6cb7f
--- /dev/null
+++ b/src/core/net/proxy_config_monitor.cpp
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// originally based on chrome/browser/net/proxy_config_monitor.cc
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "profile_qt.h"
+#include "proxy_config_monitor.h"
+#include "proxy_config_service_qt.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/task/post_task.h"
+#include "build/build_config.h"
+#include "components/proxy_config/pref_proxy_config_tracker_impl.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "net/proxy_resolution/proxy_resolution_service.h"
+#include "services/network/public/mojom/network_context.mojom.h"
+
+#include <utility>
+
+using content::BrowserThread;
+
+ProxyConfigMonitor::ProxyConfigMonitor(PrefService *prefs)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ proxy_config_service_.reset(
+ new ProxyConfigServiceQt(
+ prefs, base::CreateSingleThreadTaskRunnerWithTraits({ BrowserThread::UI })));
+
+ proxy_config_service_->AddObserver(this);
+}
+
+ProxyConfigMonitor::~ProxyConfigMonitor()
+{
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)
+ || !BrowserThread::IsThreadInitialized(BrowserThread::UI));
+ proxy_config_service_->RemoveObserver(this);
+}
+
+void ProxyConfigMonitor::AddToNetworkContextParams(
+ network::mojom::NetworkContextParams *network_context_params)
+{
+ network::mojom::ProxyConfigClientPtr proxy_config_client;
+ network_context_params->proxy_config_client_request = mojo::MakeRequest(&proxy_config_client);
+ proxy_config_client_set_.AddPtr(std::move(proxy_config_client));
+
+ poller_binding_set_.AddBinding(
+ this, mojo::MakeRequest(&network_context_params->proxy_config_poller_client));
+
+ net::ProxyConfigWithAnnotation proxy_config;
+ net::ProxyConfigService::ConfigAvailability availability =
+ proxy_config_service_->GetLatestProxyConfig(&proxy_config);
+ if (availability != net::ProxyConfigService::CONFIG_PENDING)
+ network_context_params->initial_proxy_config = proxy_config;
+}
+
+void ProxyConfigMonitor::OnProxyConfigChanged(
+ const net::ProxyConfigWithAnnotation &config,
+ net::ProxyConfigService::ConfigAvailability availability)
+{
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)
+ || !BrowserThread::IsThreadInitialized(BrowserThread::UI));
+ proxy_config_client_set_.ForAllPtrs(
+ [config, availability](network::mojom::ProxyConfigClient *proxy_config_client) {
+ switch (availability) {
+ case net::ProxyConfigService::CONFIG_VALID:
+ proxy_config_client->OnProxyConfigUpdated(config);
+ break;
+ case net::ProxyConfigService::CONFIG_UNSET:
+ proxy_config_client->OnProxyConfigUpdated(
+ net::ProxyConfigWithAnnotation::CreateDirect());
+ break;
+ case net::ProxyConfigService::CONFIG_PENDING:
+ NOTREACHED();
+ break;
+ }
+ });
+}
+
+void ProxyConfigMonitor::OnLazyProxyConfigPoll()
+{
+ proxy_config_service_->OnLazyPoll();
+}
diff --git a/src/core/net/proxy_config_monitor.h b/src/core/net/proxy_config_monitor.h
new file mode 100644
index 000000000..23f073a84
--- /dev/null
+++ b/src/core/net/proxy_config_monitor.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// originally based on chrome/browser/net/proxy_config_monitor.h
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PROXY_CONFIG_MONITOR_H
+#define PROXY_CONFIG_MONITOR_H
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "build/buildflag.h"
+#include "extensions/buildflags/buildflags.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
+#include "net/proxy_resolution/proxy_config_service.h"
+#include "services/network/public/mojom/network_context.mojom-forward.h"
+#include "services/network/public/mojom/network_service.mojom-forward.h"
+#include "services/network/public/mojom/proxy_config.mojom-forward.h"
+#include "services/network/public/mojom/proxy_config_with_annotation.mojom.h"
+
+namespace net {
+class ProxyConfigWithAnnotation;
+}
+
+class PrefService;
+class ProxyConfigServiceQt;
+
+// Tracks the ProxyConfig to use, and passes any updates to a NetworkContext's
+// ProxyConfigClient. This also responds to errors related to proxy settings
+// from the NetworkContext, and forwards them any listening Chrome extensions
+// associated with the profile.
+class ProxyConfigMonitor : public net::ProxyConfigService::Observer,
+ public network::mojom::ProxyConfigPollerClient
+{
+public:
+ explicit ProxyConfigMonitor(PrefService *prefs = nullptr);
+
+ ~ProxyConfigMonitor() override;
+
+ // Populates proxy-related fields of |network_context_params|. Updated
+ // ProxyConfigs will be sent to a NetworkContext created with those params
+ // whenever the configuration changes. Can be called more than once to inform
+ // multiple NetworkContexts of proxy changes.
+ void AddToNetworkContextParams(network::mojom::NetworkContextParams *network_context_params);
+
+private:
+ // net::ProxyConfigService::Observer implementation:
+ void OnProxyConfigChanged(const net::ProxyConfigWithAnnotation &config,
+ net::ProxyConfigService::ConfigAvailability availability) override;
+
+ // network::mojom::ProxyConfigPollerClient implementation:
+ void OnLazyProxyConfigPoll() override;
+
+ std::unique_ptr<ProxyConfigServiceQt> proxy_config_service_;
+
+ mojo::BindingSet<network::mojom::ProxyConfigPollerClient> poller_binding_set_;
+
+ mojo::InterfacePtrSet<network::mojom::ProxyConfigClient> proxy_config_client_set_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProxyConfigMonitor);
+};
+
+#endif // !PROXY_CONFIG_MONITOR_H
diff --git a/src/core/net/proxy_config_service_qt.cpp b/src/core/net/proxy_config_service_qt.cpp
index 8016c7e83..59884961d 100644
--- a/src/core/net/proxy_config_service_qt.cpp
+++ b/src/core/net/proxy_config_service_qt.cpp
@@ -46,8 +46,9 @@
#include "proxy_config_service_qt.h"
#include "base/bind.h"
-#include "content/public/browser/browser_thread.h"
#include "components/proxy_config/pref_proxy_config_tracker_impl.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/proxy_resolution/proxy_resolution_service.h"
using content::BrowserThread;
@@ -69,34 +70,40 @@ net::ProxyServer ProxyConfigServiceQt::fromQNetworkProxy(const QNetworkProxy &qt
}
}
-ProxyConfigServiceQt::ProxyConfigServiceQt(std::unique_ptr<ProxyConfigService> baseService,
- const net::ProxyConfigWithAnnotation &initialConfig,
- ProxyPrefs::ConfigState initialState)
- : m_baseService(baseService.release())
+ProxyConfigServiceQt::ProxyConfigServiceQt(PrefService *prefService,
+ const scoped_refptr<base::SingleThreadTaskRunner> &taskRunner)
+ : m_baseService(net::ProxyResolutionService::CreateSystemProxyConfigService(taskRunner))
, m_usesSystemConfiguration(false)
, m_registeredObserver(false)
- , m_prefConfig(initialConfig)
- , m_perfState(initialState)
-{}
+ , m_prefState(prefService
+ ? PrefProxyConfigTrackerImpl::ReadPrefConfig(prefService, &m_prefConfig)
+ : ProxyPrefs::CONFIG_UNSET)
+{
+ DETACH_FROM_SEQUENCE(m_sequenceChecker);
+}
ProxyConfigServiceQt::~ProxyConfigServiceQt()
{
+ DCHECK_CALLED_ON_VALID_SEQUENCE(m_sequenceChecker);
if (m_registeredObserver && m_baseService.get())
m_baseService->RemoveObserver(this);
}
void ProxyConfigServiceQt::AddObserver(net::ProxyConfigService::Observer *observer)
{
+ DCHECK_CALLED_ON_VALID_SEQUENCE(m_sequenceChecker);
m_observers.AddObserver(observer);
}
void ProxyConfigServiceQt::RemoveObserver(net::ProxyConfigService::Observer *observer)
{
+ DCHECK_CALLED_ON_VALID_SEQUENCE(m_sequenceChecker);
m_observers.RemoveObserver(observer);
}
net::ProxyConfigService::ConfigAvailability ProxyConfigServiceQt::GetLatestProxyConfig(net::ProxyConfigWithAnnotation *config)
{
+ DCHECK_CALLED_ON_VALID_SEQUENCE(m_sequenceChecker);
m_usesSystemConfiguration = QNetworkProxyFactory::usesSystemConfiguration();
if (m_usesSystemConfiguration) {
// Use Chromium's base service to retrieve system settings
@@ -106,7 +113,7 @@ net::ProxyConfigService::ConfigAvailability ProxyConfigServiceQt::GetLatestProxy
systemAvailability = m_baseService->GetLatestProxyConfig(&systemConfig);
ProxyPrefs::ConfigState configState;
systemAvailability = PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig(
- m_perfState, m_prefConfig, systemAvailability, systemConfig,
+ m_prefState, m_prefConfig, systemAvailability, systemConfig,
false, &configState, config);
RegisterObserver();
return systemAvailability;
@@ -151,7 +158,7 @@ net::ProxyConfigService::ConfigAvailability ProxyConfigServiceQt::GetLatestProxy
void ProxyConfigServiceQt::OnLazyPoll()
{
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CALLED_ON_VALID_SEQUENCE(m_sequenceChecker);
// We need to update if
// - setUseSystemConfiguration() was called in between
@@ -168,7 +175,7 @@ void ProxyConfigServiceQt::OnLazyPoll()
// Called when the base service changed
void ProxyConfigServiceQt::OnProxyConfigChanged(const net::ProxyConfigWithAnnotation &config, ConfigAvailability availability)
{
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CALLED_ON_VALID_SEQUENCE(m_sequenceChecker);
Q_UNUSED(config);
if (!m_usesSystemConfiguration)
@@ -180,6 +187,7 @@ void ProxyConfigServiceQt::OnProxyConfigChanged(const net::ProxyConfigWithAnnota
// Update our observers
void ProxyConfigServiceQt::Update()
{
+ DCHECK_CALLED_ON_VALID_SEQUENCE(m_sequenceChecker);
net::ProxyConfigWithAnnotation actual_config;
ConfigAvailability availability = GetLatestProxyConfig(&actual_config);
if (availability == CONFIG_PENDING)
@@ -193,7 +201,7 @@ void ProxyConfigServiceQt::Update()
// in the constructor.
void ProxyConfigServiceQt::RegisterObserver()
{
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CALLED_ON_VALID_SEQUENCE(m_sequenceChecker);
if (!m_registeredObserver && m_baseService.get()) {
m_baseService->AddObserver(this);
m_registeredObserver = true;
diff --git a/src/core/net/proxy_config_service_qt.h b/src/core/net/proxy_config_service_qt.h
index 4d8619055..c0928bc03 100644
--- a/src/core/net/proxy_config_service_qt.h
+++ b/src/core/net/proxy_config_service_qt.h
@@ -42,6 +42,7 @@
#include "base/memory/ref_counted.h"
#include "base/observer_list.h"
+#include "base/single_thread_task_runner.h"
#include "net/proxy_resolution/proxy_config.h"
#include "net/proxy_resolution/proxy_config_service.h"
@@ -50,6 +51,8 @@
#include <QNetworkProxy>
+class PrefService;
+
class ProxyConfigServiceQt
: public net::ProxyConfigService
, public net::ProxyConfigService::Observer
@@ -57,9 +60,8 @@ class ProxyConfigServiceQt
public:
static net::ProxyServer fromQNetworkProxy(const QNetworkProxy &);
- explicit ProxyConfigServiceQt(std::unique_ptr<ProxyConfigService> baseService,
- const net::ProxyConfigWithAnnotation &initialConfig,
- ProxyPrefs::ConfigState initialState);
+ explicit ProxyConfigServiceQt(PrefService *prefService,
+ const scoped_refptr<base::SingleThreadTaskRunner> &taskRunner);
~ProxyConfigServiceQt() override;
// ProxyConfigService implementation:
@@ -92,7 +94,9 @@ private:
// Configuration as defined by prefs.
net::ProxyConfigWithAnnotation m_prefConfig;
- ProxyPrefs::ConfigState m_perfState;
+ ProxyPrefs::ConfigState m_prefState;
+
+ SEQUENCE_CHECKER(m_sequenceChecker);
DISALLOW_COPY_AND_ASSIGN(ProxyConfigServiceQt);
};
diff --git a/src/core/net/proxying_restricted_cookie_manager_qt.cpp b/src/core/net/proxying_restricted_cookie_manager_qt.cpp
new file mode 100644
index 000000000..b6abeb567
--- /dev/null
+++ b/src/core/net/proxying_restricted_cookie_manager_qt.cpp
@@ -0,0 +1,231 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// originally based on android_webview/browser/network_service/aw_proxying_restricted_cookie_manager.cc:
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "proxying_restricted_cookie_manager_qt.h"
+
+#include "api/qwebenginecookiestore.h"
+#include "api/qwebenginecookiestore_p.h"
+#include "profile_adapter.h"
+#include "profile_qt.h"
+#include "type_conversion.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/task/post_task.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+namespace QtWebEngineCore {
+
+class ProxyingRestrictedCookieManagerListenerQt : public network::mojom::CookieChangeListener {
+public:
+ ProxyingRestrictedCookieManagerListenerQt(const GURL &url,
+ const GURL &site_for_cookies,
+ base::WeakPtr<ProxyingRestrictedCookieManagerQt> restricted_cookie_manager,
+ network::mojom::CookieChangeListenerPtr client_listener)
+ : url_(url)
+ , site_for_cookies_(site_for_cookies)
+ , restricted_cookie_manager_(restricted_cookie_manager)
+ , client_listener_(std::move(client_listener))
+ {}
+
+ void OnCookieChange(const net::CanonicalCookie &cookie, network::mojom::CookieChangeCause cause) override
+ {
+ if (restricted_cookie_manager_ && restricted_cookie_manager_->allowCookies(url_, site_for_cookies_))
+ client_listener_->OnCookieChange(cookie, cause);
+ }
+
+private:
+ const GURL url_;
+ const GURL site_for_cookies_;
+ base::WeakPtr<ProxyingRestrictedCookieManagerQt> restricted_cookie_manager_;
+ network::mojom::CookieChangeListenerPtr client_listener_;
+};
+
+// static
+void ProxyingRestrictedCookieManagerQt::CreateAndBind(ProfileIODataQt *profileIoData,
+ network::mojom::RestrictedCookieManagerPtrInfo underlying_rcm,
+ bool is_service_worker,
+ int process_id,
+ int frame_id,
+ network::mojom::RestrictedCookieManagerRequest request)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
+ base::BindOnce(&ProxyingRestrictedCookieManagerQt::CreateAndBindOnIoThread,
+ profileIoData,
+ std::move(underlying_rcm),
+ is_service_worker, process_id, frame_id,
+ std::move(request)));
+}
+
+
+// static
+void ProxyingRestrictedCookieManagerQt::CreateAndBindOnIoThread(ProfileIODataQt *profileIoData,
+ network::mojom::RestrictedCookieManagerPtrInfo underlying_rcm,
+ bool is_service_worker,
+ int process_id,
+ int frame_id,
+ network::mojom::RestrictedCookieManagerRequest request)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+ auto wrapper = base::WrapUnique(new ProxyingRestrictedCookieManagerQt(
+ profileIoData->getWeakPtrOnIOThread(),
+ network::mojom::RestrictedCookieManagerPtr(std::move(underlying_rcm)),
+ is_service_worker, process_id, frame_id));
+ mojo::MakeStrongBinding(std::move(wrapper), std::move(request));
+}
+
+ProxyingRestrictedCookieManagerQt::ProxyingRestrictedCookieManagerQt(base::WeakPtr<ProfileIODataQt> profileIoData,
+ network::mojom::RestrictedCookieManagerPtr underlyingRestrictedCookieManager,
+ bool is_service_worker,
+ int32_t process_id,
+ int32_t frame_id)
+ : m_profileIoData(std::move(profileIoData))
+ , underlying_restricted_cookie_manager_(std::move(underlyingRestrictedCookieManager))
+ , is_service_worker_(is_service_worker)
+ , process_id_(process_id)
+ , frame_id_(frame_id)
+ , weak_factory_(this)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+}
+
+ProxyingRestrictedCookieManagerQt::~ProxyingRestrictedCookieManagerQt()
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+}
+
+void ProxyingRestrictedCookieManagerQt::GetAllForUrl(const GURL &url,
+ const GURL &site_for_cookies,
+ network::mojom::CookieManagerGetOptionsPtr options,
+ GetAllForUrlCallback callback)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+ if (allowCookies(url, site_for_cookies)) {
+ underlying_restricted_cookie_manager_->GetAllForUrl(url, site_for_cookies, std::move(options), std::move(callback));
+ } else {
+ std::move(callback).Run(std::vector<net::CanonicalCookie>());
+ }
+}
+
+void ProxyingRestrictedCookieManagerQt::SetCanonicalCookie(const net::CanonicalCookie &cookie,
+ const GURL &url,
+ const GURL &site_for_cookies,
+ SetCanonicalCookieCallback callback)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+ if (allowCookies(url, site_for_cookies)) {
+ underlying_restricted_cookie_manager_->SetCanonicalCookie(cookie, url, site_for_cookies, std::move(callback));
+ } else {
+ std::move(callback).Run(false);
+ }
+}
+
+void ProxyingRestrictedCookieManagerQt::AddChangeListener(const GURL &url,
+ const GURL &site_for_cookies,
+ network::mojom::CookieChangeListenerPtr listener,
+ AddChangeListenerCallback callback)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+ network::mojom::CookieChangeListenerPtr proxy_listener_ptr;
+ auto proxy_listener =
+ std::make_unique<ProxyingRestrictedCookieManagerListenerQt>(
+ url, site_for_cookies, weak_factory_.GetWeakPtr(),
+ std::move(listener));
+
+ mojo::MakeStrongBinding(std::move(proxy_listener),
+ mojo::MakeRequest(&proxy_listener_ptr));
+
+ underlying_restricted_cookie_manager_->AddChangeListener(url, site_for_cookies, std::move(proxy_listener_ptr), std::move(callback));
+}
+
+void ProxyingRestrictedCookieManagerQt::SetCookieFromString(const GURL &url,
+ const GURL &site_for_cookies,
+ const std::string &cookie,
+ SetCookieFromStringCallback callback)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+ if (allowCookies(url, site_for_cookies)) {
+ underlying_restricted_cookie_manager_->SetCookieFromString(url, site_for_cookies, cookie, std::move(callback));
+ } else {
+ std::move(callback).Run();
+ }
+}
+
+void ProxyingRestrictedCookieManagerQt::GetCookiesString(const GURL &url,
+ const GURL &site_for_cookies,
+ GetCookiesStringCallback callback)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+ if (allowCookies(url, site_for_cookies)) {
+ underlying_restricted_cookie_manager_->GetCookiesString(url, site_for_cookies, std::move(callback));
+ } else {
+ std::move(callback).Run("");
+ }
+}
+
+void ProxyingRestrictedCookieManagerQt::CookiesEnabledFor(const GURL &url,
+ const GURL &site_for_cookies,
+ CookiesEnabledForCallback callback)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+ std::move(callback).Run(allowCookies(url, site_for_cookies));
+}
+
+bool ProxyingRestrictedCookieManagerQt::allowCookies(const GURL &url, const GURL &site_for_cookies) const
+{
+ if (!m_profileIoData)
+ return false;
+ return m_profileIoData->canGetCookies(toQt(site_for_cookies), toQt(url));
+}
+
+} // namespace QtWebEngineCore
diff --git a/src/core/net/restricted_cookie_manager_qt.h b/src/core/net/proxying_restricted_cookie_manager_qt.h
index 9154f671d..7a9f0e3ab 100644
--- a/src/core/net/restricted_cookie_manager_qt.h
+++ b/src/core/net/proxying_restricted_cookie_manager_qt.h
@@ -37,30 +37,30 @@
**
****************************************************************************/
-#ifndef RESTRICTED_COOKIE_MANAGER_QT_H
-#define RESTRICTED_COOKIE_MANAGER_QT_H
+#ifndef PROXYING_RESTRICTED_COOKIE_MANAGER_QT_H
+#define PROXYING_RESTRICTED_COOKIE_MANAGER_QT_H
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
-#include "services/network/restricted_cookie_manager.h"
+#include "services/network/public/mojom/restricted_cookie_manager.mojom.h"
#include "url/gurl.h"
namespace QtWebEngineCore {
class ProfileIODataQt;
-class RestrictedCookieManagerQt : public network::RestrictedCookieManager
+class ProxyingRestrictedCookieManagerQt : public network::mojom::RestrictedCookieManager
{
public:
- RestrictedCookieManagerQt(base::WeakPtr<ProfileIODataQt> profileIoData,
- network::mojom::RestrictedCookieManagerRole role,
- net::CookieStore *cookie_store,
- network::CookieSettings *cookie_settings,
- const url::Origin &origin,
+ // Expects to be called on the UI thread.
+ static void CreateAndBind(ProfileIODataQt *profileIoData,
+ network::mojom::RestrictedCookieManagerPtrInfo underlying_rcm,
bool is_service_worker,
- int32_t process_id,
- int32_t frame_id);
- ~RestrictedCookieManagerQt() override;
+ int process_id,
+ int frame_id,
+ network::mojom::RestrictedCookieManagerRequest request);
+
+ ~ProxyingRestrictedCookieManagerQt() override;
// network::mojom::RestrictedCookieManager interface:
void GetAllForUrl(const GURL &url,
@@ -75,7 +75,10 @@ public:
const GURL &site_for_cookies,
network::mojom::CookieChangeListenerPtr listener,
AddChangeListenerCallback callback) override;
-
+ void SetCookieFromString(const GURL &url,
+ const GURL &site_for_cookies,
+ const std::string &cookie,
+ SetCookieFromStringCallback callback) override;
void GetCookiesString(const GURL &url,
const GURL &site_for_cookies,
GetCookiesStringCallback callback) override;
@@ -88,13 +91,31 @@ public:
bool allowCookies(const GURL &url, const GURL &site_for_cookies) const;
private:
+ ProxyingRestrictedCookieManagerQt(base::WeakPtr<ProfileIODataQt> profileIoData,
+ network::mojom::RestrictedCookieManagerPtr underlyingRestrictedCookieManager,
+ bool is_service_worker,
+ int32_t process_id,
+ int32_t frame_id);
+
+ static void CreateAndBindOnIoThread(ProfileIODataQt *profileIoData,
+ network::mojom::RestrictedCookieManagerPtrInfo underlying_rcm,
+ bool is_service_worker,
+ int process_id,
+ int frame_id,
+ network::mojom::RestrictedCookieManagerRequest request);
+
base::WeakPtr<ProfileIODataQt> m_profileIoData;
- base::WeakPtrFactory<RestrictedCookieManagerQt> weak_factory_;
+ network::mojom::RestrictedCookieManagerPtr underlying_restricted_cookie_manager_;
+ bool is_service_worker_;
+ int process_id_;
+ int frame_id_;
+
+ base::WeakPtrFactory<ProxyingRestrictedCookieManagerQt> weak_factory_;
- DISALLOW_COPY_AND_ASSIGN(RestrictedCookieManagerQt);
+ DISALLOW_COPY_AND_ASSIGN(ProxyingRestrictedCookieManagerQt);
};
} // namespace QtWebEngineCore
-#endif // RESTRICTED_COOKIE_MANAGER_QT_H
+#endif // PROXYING_RESTRICTED_COOKIE_MANAGER_QT_H
diff --git a/src/core/net/proxying_url_loader_factory_qt.cpp b/src/core/net/proxying_url_loader_factory_qt.cpp
new file mode 100644
index 000000000..51a226117
--- /dev/null
+++ b/src/core/net/proxying_url_loader_factory_qt.cpp
@@ -0,0 +1,560 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "proxying_url_loader_factory_qt.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/task/post_task.h"
+#include "components/safe_browsing/common/safebrowsing_constants.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/global_request_id.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/resource_request_info.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_constants.h"
+#include "content/public/common/url_utils.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_status_code.h"
+#include "net/http/http_util.h"
+
+#include "api/qwebengineurlrequestinfo_p.h"
+#include "profile_io_data_qt.h"
+#include "type_conversion.h"
+#include "web_contents_adapter_client.h"
+#include "web_contents_view_qt.h"
+#include <QVariant>
+
+// originally based on aw_proxying_url_loader_factory.cc:
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+namespace QtWebEngineCore {
+
+extern WebContentsAdapterClient::NavigationType pageTransitionToNavigationType(ui::PageTransition transition);
+
+static QWebEngineUrlRequestInfo::ResourceType toQt(content::ResourceType resourceType)
+{
+ if (resourceType >= content::ResourceType::kMainFrame && resourceType <= content::ResourceType::kMaxValue)
+ return static_cast<QWebEngineUrlRequestInfo::ResourceType>(resourceType);
+ return QWebEngineUrlRequestInfo::ResourceTypeUnknown;
+}
+
+static QWebEngineUrlRequestInfo::NavigationType toQt(WebContentsAdapterClient::NavigationType navigationType)
+{
+ return static_cast<QWebEngineUrlRequestInfo::NavigationType>(navigationType);
+}
+
+// Handles intercepted, in-progress requests/responses, so that they can be
+// controlled and modified accordingly.
+class InterceptedRequest : public network::mojom::URLLoader
+ , public network::mojom::URLLoaderClient
+{
+public:
+ InterceptedRequest(int process_id, uint64_t request_id, int32_t routing_id, uint32_t options,
+ const network::ResourceRequest &request,
+ const net::MutableNetworkTrafficAnnotationTag &traffic_annotation,
+ ProfileIODataQt *profileData,
+ network::mojom::URLLoaderRequest loader_request, network::mojom::URLLoaderClientPtr client,
+ network::mojom::URLLoaderFactoryPtr target_factory);
+ ~InterceptedRequest() override;
+
+ void Restart();
+ void InterceptOnUIThread();
+
+ // network::mojom::URLLoaderClient
+ void OnReceiveResponse(const network::ResourceResponseHead &head) override;
+ void OnReceiveRedirect(const net::RedirectInfo &redirect_info, const network::ResourceResponseHead &head) override;
+ void OnUploadProgress(int64_t current_position, int64_t total_size, OnUploadProgressCallback callback) override;
+ void OnReceiveCachedMetadata(mojo_base::BigBuffer data) override;
+ void OnTransferSizeUpdated(int32_t transfer_size_diff) override;
+ void OnStartLoadingResponseBody(mojo::ScopedDataPipeConsumerHandle body) override;
+ void OnComplete(const network::URLLoaderCompletionStatus &status) override;
+
+ // network::mojom::URLLoader
+ void FollowRedirect(const std::vector<std::string> &removed_headers,
+ const net::HttpRequestHeaders &modified_headers, const base::Optional<GURL> &new_url) override;
+ void ProceedWithResponse() override;
+ void SetPriority(net::RequestPriority priority, int32_t intra_priority_value) override;
+ void PauseReadingBodyFromNet() override;
+ void ResumeReadingBodyFromNet() override;
+
+ void ContinueAfterIntercept();
+
+private:
+ // This is called when the original URLLoaderClient has a connection error.
+ void OnURLLoaderClientError();
+
+ // This is called when the original URLLoader has a connection error.
+ void OnURLLoaderError(uint32_t custom_reason, const std::string &description);
+
+ // Call OnComplete on |target_client_|. If |wait_for_loader_error| is true
+ // then this object will wait for |proxied_loader_binding_| to have a
+ // connection error before destructing.
+ void CallOnComplete(const network::URLLoaderCompletionStatus &status, bool wait_for_loader_error);
+
+ void SendErrorAndCompleteImmediately(int error_code);
+
+ const int process_id_;
+ const uint64_t request_id_;
+ const int32_t routing_id_;
+ const uint32_t options_;
+ bool input_stream_previously_failed_ = false;
+ bool request_was_redirected_ = false;
+
+ // If the |target_loader_| called OnComplete with an error this stores it.
+ // That way the destructor can send it to OnReceivedError if safe browsing
+ // error didn't occur.
+ int error_status_ = net::OK;
+ GURL m_originalUrl;
+
+ network::ResourceRequest request_;
+ network::ResourceResponseHead current_response_;
+
+ const net::MutableNetworkTrafficAnnotationTag traffic_annotation_;
+
+ ProfileIODataQt *m_profileData;
+ mojo::Binding<network::mojom::URLLoader> proxied_loader_binding_;
+ network::mojom::URLLoaderClientPtr target_client_;
+
+ mojo::Binding<network::mojom::URLLoaderClient> proxied_client_binding_;
+ network::mojom::URLLoaderPtr target_loader_;
+ network::mojom::URLLoaderFactoryPtr target_factory_;
+
+ base::WeakPtrFactory<InterceptedRequest> m_weakFactory;
+ base::WeakPtr<InterceptedRequest> m_weakPtr;
+ DISALLOW_COPY_AND_ASSIGN(InterceptedRequest);
+};
+
+InterceptedRequest::InterceptedRequest(int process_id, uint64_t request_id, int32_t routing_id, uint32_t options,
+ const network::ResourceRequest &request,
+ const net::MutableNetworkTrafficAnnotationTag &traffic_annotation,
+ ProfileIODataQt *profileData,
+ network::mojom::URLLoaderRequest loader_request,
+ network::mojom::URLLoaderClientPtr client,
+ network::mojom::URLLoaderFactoryPtr target_factory)
+ : process_id_(process_id)
+ , request_id_(request_id)
+ , routing_id_(routing_id)
+ , options_(options)
+ , request_(request)
+ , traffic_annotation_(traffic_annotation)
+ , m_profileData(profileData)
+ , proxied_loader_binding_(this, std::move(loader_request))
+ , target_client_(std::move(client))
+ , proxied_client_binding_(this)
+ , target_factory_(std::move(target_factory))
+ , m_weakFactory(this)
+ , m_weakPtr(m_weakFactory.GetWeakPtr())
+{
+ // If there is a client error, clean up the request.
+ target_client_.set_connection_error_handler(
+ base::BindOnce(&InterceptedRequest::OnURLLoaderClientError, m_weakFactory.GetWeakPtr()));
+ proxied_loader_binding_.set_connection_error_with_reason_handler(
+ base::BindOnce(&InterceptedRequest::OnURLLoaderError, m_weakFactory.GetWeakPtr()));
+}
+
+InterceptedRequest::~InterceptedRequest()
+{
+ m_weakFactory.InvalidateWeakPtrs();
+}
+
+void InterceptedRequest::Restart()
+{
+ // FIXME: Support deprecated interceptors here
+
+ // FIXME: unretained post?
+ base::PostTaskWithTraits(
+ FROM_HERE, {content::BrowserThread::UI},
+ base::BindOnce(&InterceptedRequest::InterceptOnUIThread, base::Unretained(this)));
+}
+
+void InterceptedRequest::InterceptOnUIThread()
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ content::ResourceType resourceType = content::ResourceType(request_.resource_type);
+ WebContentsAdapterClient::NavigationType navigationType =
+ pageTransitionToNavigationType(ui::PageTransition(request_.transition_type));
+
+ m_originalUrl = request_.url;
+ const QUrl qUrl = toQt(request_.url);
+
+ const QUrl initiator = request_.request_initiator.has_value() ? toQt(request_.request_initiator->GetURL()) : QUrl();
+
+ QUrl firstPartyUrl;
+ if (resourceType == content::ResourceType::kSubFrame)
+ firstPartyUrl = toQt(request_.first_party_url);
+ else
+ firstPartyUrl = toQt(request_.site_for_cookies);
+
+ QWebEngineUrlRequestInfoPrivate *infoPrivate = new QWebEngineUrlRequestInfoPrivate(toQt(resourceType),
+ toQt(navigationType),
+ qUrl,
+ firstPartyUrl,
+ initiator,
+ QByteArray::fromStdString(request_.method));
+ QWebEngineUrlRequestInfo requestInfo(infoPrivate);
+
+ content::WebContents *webContents = nullptr;
+ if (process_id_) {
+ content::RenderFrameHost *frameHost = content::RenderFrameHost::FromID(process_id_, request_.render_frame_id);
+ webContents = content::WebContents::FromRenderFrameHost(frameHost);
+ } else
+ webContents = content::WebContents::FromFrameTreeNodeId(request_.render_frame_id);
+
+ if (webContents) {
+ int result = net::OK;
+ if (m_profileData) {
+ QWebEngineUrlRequestInterceptor *interceptor = m_profileData->requestInterceptor();
+ if (interceptor && !interceptor->property("deprecated").toBool())
+ interceptor->interceptRequest(requestInfo);
+ }
+
+ WebContentsAdapterClient *client =
+ WebContentsViewQt::from(static_cast<content::WebContentsImpl*>(webContents)->GetView())->client();
+
+ if (!requestInfo.changed()) {
+ client->interceptRequest(requestInfo);
+ }
+
+ if (requestInfo.changed()) {
+ result = requestInfo.d_ptr->shouldBlockRequest ? net::ERR_BLOCKED_BY_CLIENT : net::OK;
+ // We handle the rest of the changes later when we are back in I/O thread
+ }
+
+ if (result != net::OK) {
+ base::PostTaskWithTraits(
+ FROM_HERE, {content::BrowserThread::IO},
+ base::BindOnce(&InterceptedRequest::SendErrorAndCompleteImmediately, m_weakPtr, result));
+ return;
+ }
+ if (requestInfo.changed()) {
+ if (requestInfo.requestUrl() != qUrl) {
+ net::URLRequest::FirstPartyURLPolicy first_party_url_policy =
+ request_.update_first_party_url_on_redirect ? net::URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT
+ : net::URLRequest::NEVER_CHANGE_FIRST_PARTY_URL;
+ net::RedirectInfo redirectInfo = net::RedirectInfo::ComputeRedirectInfo(request_.method, request_.url,
+ request_.site_for_cookies, request_.top_frame_origin,
+ first_party_url_policy, request_.referrer_policy,
+ request_.referrer.spec(), net::HTTP_TEMPORARY_REDIRECT,
+ toGurl(requestInfo.requestUrl()), base::nullopt,
+ false /*insecure_scheme_was_upgraded*/);
+
+ // FIXME: Should probably create a new header.
+ current_response_.encoded_data_length = 0;
+ // FIXME: unretained post.
+ base::PostTaskWithTraits(
+ FROM_HERE, {content::BrowserThread::IO},
+ base::BindOnce(&network::mojom::URLLoaderClientProxy::OnReceiveRedirect, base::Unretained(&(*target_client_)), redirectInfo, current_response_));
+ request_.method = redirectInfo.new_method;
+ request_.url = redirectInfo.new_url;
+ request_.site_for_cookies = redirectInfo.new_site_for_cookies;
+ request_.referrer = GURL(redirectInfo.new_referrer);
+ request_.referrer_policy = redirectInfo.new_referrer_policy;
+ if (request_.method == net::HttpRequestHeaders::kGetMethod)
+ request_.request_body = nullptr;
+ return;
+ }
+
+ if (!requestInfo.d_ptr->extraHeaders.isEmpty()) {
+ auto end = requestInfo.d_ptr->extraHeaders.constEnd();
+ for (auto header = requestInfo.d_ptr->extraHeaders.constBegin(); header != end; ++header) {
+ std::string h = header.key().toStdString();
+ if (base::LowerCaseEqualsASCII(h, "referer")) {
+ request_.referrer = GURL(header.value().toStdString());
+ } else {
+ request_.headers.SetHeader(h, header.value().toStdString());
+ }
+ }
+ }
+ }
+ }
+ base::PostTaskWithTraits(
+ FROM_HERE, {content::BrowserThread::IO},
+ base::BindOnce(&InterceptedRequest::ContinueAfterIntercept, m_weakPtr));
+}
+
+void InterceptedRequest::ContinueAfterIntercept()
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+ if (!target_loader_ && target_factory_) {
+ network::mojom::URLLoaderClientPtr proxied_client;
+ proxied_client_binding_.Bind(mojo::MakeRequest(&proxied_client));
+ target_factory_->CreateLoaderAndStart(mojo::MakeRequest(&target_loader_), routing_id_, request_id_, options_,
+ request_, std::move(proxied_client), traffic_annotation_);
+ }
+}
+
+// URLLoaderClient methods.
+
+void InterceptedRequest::OnReceiveResponse(const network::ResourceResponseHead &head)
+{
+ current_response_ = head;
+
+ target_client_->OnReceiveResponse(head);
+}
+
+void InterceptedRequest::OnReceiveRedirect(const net::RedirectInfo &redirect_info, const network::ResourceResponseHead &head)
+{
+ // TODO(timvolodine): handle redirect override.
+ request_was_redirected_ = true;
+ current_response_ = head;
+ target_client_->OnReceiveRedirect(redirect_info, head);
+ request_.url = redirect_info.new_url;
+ request_.method = redirect_info.new_method;
+ request_.site_for_cookies = redirect_info.new_site_for_cookies;
+ request_.referrer = GURL(redirect_info.new_referrer);
+ request_.referrer_policy = redirect_info.new_referrer_policy;
+}
+
+void InterceptedRequest::OnUploadProgress(int64_t current_position, int64_t total_size, OnUploadProgressCallback callback)
+{
+ target_client_->OnUploadProgress(current_position, total_size, std::move(callback));
+}
+
+void InterceptedRequest::OnReceiveCachedMetadata(mojo_base::BigBuffer data)
+{
+ target_client_->OnReceiveCachedMetadata(std::move(data));
+}
+
+void InterceptedRequest::OnTransferSizeUpdated(int32_t transfer_size_diff)
+{
+ target_client_->OnTransferSizeUpdated(transfer_size_diff);
+}
+
+void InterceptedRequest::OnStartLoadingResponseBody(mojo::ScopedDataPipeConsumerHandle body)
+{
+ target_client_->OnStartLoadingResponseBody(std::move(body));
+}
+
+void InterceptedRequest::OnComplete(const network::URLLoaderCompletionStatus &status)
+{
+ // Only wait for the original loader to possibly have a custom error if the
+ // target loader succeeded. If the target loader failed, then it was a race as
+ // to whether that error or the safe browsing error would be reported.
+ CallOnComplete(status, status.error_code == net::OK);
+}
+
+// URLLoader methods.
+
+void InterceptedRequest::FollowRedirect(const std::vector<std::string> &removed_headers,
+ const net::HttpRequestHeaders &modified_headers,
+ const base::Optional<GURL> &new_url)
+{
+ if (target_loader_)
+ target_loader_->FollowRedirect(removed_headers, modified_headers, new_url);
+
+ // If |OnURLLoaderClientError| was called then we're just waiting for the
+ // connection error handler of |proxied_loader_binding_|. Don't restart the
+ // job since that'll create another URLLoader
+ if (!target_client_)
+ return;
+
+ Restart();
+}
+
+void InterceptedRequest::ProceedWithResponse()
+{
+ if (target_loader_)
+ target_loader_->ProceedWithResponse();
+}
+
+void InterceptedRequest::SetPriority(net::RequestPriority priority, int32_t intra_priority_value)
+{
+ if (target_loader_)
+ target_loader_->SetPriority(priority, intra_priority_value);
+}
+
+void InterceptedRequest::PauseReadingBodyFromNet()
+{
+ if (target_loader_)
+ target_loader_->PauseReadingBodyFromNet();
+}
+
+void InterceptedRequest::ResumeReadingBodyFromNet()
+{
+ if (target_loader_)
+ target_loader_->ResumeReadingBodyFromNet();
+}
+
+void InterceptedRequest::OnURLLoaderClientError()
+{
+ // We set |wait_for_loader_error| to true because if the loader did have a
+ // custom_reason error then the client would be reset as well and it would be
+ // a race as to which connection error we saw first.
+ CallOnComplete(network::URLLoaderCompletionStatus(net::ERR_ABORTED), true /* wait_for_loader_error */);
+}
+
+void InterceptedRequest::OnURLLoaderError(uint32_t custom_reason, const std::string &description)
+{
+ // If CallOnComplete was already called, then this object is ready to be deleted.
+ if (!target_client_)
+ delete this;
+}
+
+void InterceptedRequest::CallOnComplete(const network::URLLoaderCompletionStatus &status, bool wait_for_loader_error)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+ // Save an error status so that we call onReceiveError at destruction if there
+ // was no safe browsing error.
+ if (status.error_code != net::OK)
+ error_status_ = status.error_code;
+
+ if (target_client_)
+ target_client_->OnComplete(status);
+
+ if (proxied_loader_binding_ && wait_for_loader_error) {
+ // Don't delete |this| yet, in case the |proxied_loader_binding_|'s
+ // error_handler is called with a reason to indicate an error which we want
+ // to send to the client bridge. Also reset |target_client_| so we don't
+ // get its error_handler called and then delete |this|.
+ target_client_.reset();
+
+ // Since the original client is gone no need to continue loading the
+ // request.
+ proxied_client_binding_.Close();
+ target_loader_.reset();
+
+ // In case there are pending checks as to whether this request should be
+ // intercepted, we don't want that causing |target_client_| to be used
+ // later.
+ m_weakFactory.InvalidateWeakPtrs();
+ } else {
+ delete this;
+ }
+}
+
+void InterceptedRequest::SendErrorAndCompleteImmediately(int error_code)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+ auto status = network::URLLoaderCompletionStatus(error_code);
+ target_client_->OnComplete(status);
+ delete this;
+}
+
+ProxyingURLLoaderFactoryQt::ProxyingURLLoaderFactoryQt(int process_id,
+ content::ResourceContext *resourceContext,
+ network::mojom::URLLoaderFactoryRequest loader_request,
+ network::mojom::URLLoaderFactoryPtrInfo target_factory_info)
+ : m_processId(process_id), m_resourceContext(resourceContext), m_weakFactory(this)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+ if (target_factory_info) {
+ m_targetFactory.Bind(std::move(target_factory_info));
+ m_targetFactory.set_connection_error_handler(
+ base::BindOnce(&ProxyingURLLoaderFactoryQt::OnTargetFactoryError, m_weakFactory.GetWeakPtr()));
+ }
+ m_proxyBindings.AddBinding(this, std::move(loader_request));
+ m_proxyBindings.set_connection_error_handler(
+ base::BindRepeating(&ProxyingURLLoaderFactoryQt::OnProxyBindingError, m_weakFactory.GetWeakPtr()));
+}
+
+ProxyingURLLoaderFactoryQt::~ProxyingURLLoaderFactoryQt()
+{
+ m_weakFactory.InvalidateWeakPtrs();
+}
+
+// static
+void ProxyingURLLoaderFactoryQt::CreateProxy(int process_id,
+ content::ResourceContext *resourceContext,
+ network::mojom::URLLoaderFactoryRequest loader_request,
+ network::mojom::URLLoaderFactoryPtrInfo target_factory_info)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+ // Will manage its own lifetime
+ new ProxyingURLLoaderFactoryQt(process_id, resourceContext, std::move(loader_request), std::move(target_factory_info));
+}
+
+void ProxyingURLLoaderFactoryQt::CreateLoaderAndStart(network::mojom::URLLoaderRequest loader, int32_t routing_id,
+ int32_t request_id, uint32_t options,
+ const network::ResourceRequest &request,
+ network::mojom::URLLoaderClientPtr client,
+ const net::MutableNetworkTrafficAnnotationTag &traffic_annotation)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+ ProfileIODataQt *profileIOData = ProfileIODataQt::FromResourceContext(m_resourceContext);
+
+ QWebEngineUrlRequestInterceptor *profileInterceptor = profileIOData ? profileIOData->requestInterceptor() : nullptr;
+ if (!profileIOData || !(profileInterceptor || profileIOData->hasPageInterceptors())) {
+ m_targetFactory->CreateLoaderAndStart(
+ std::move(loader), routing_id, request_id, options, request,
+ std::move(client), traffic_annotation);
+ return;
+ }
+
+ network::mojom::URLLoaderFactoryPtr target_factory_clone;
+ if (m_targetFactory)
+ m_targetFactory->Clone(mojo::MakeRequest(&target_factory_clone));
+
+
+ // Will manage its own lifetime
+ InterceptedRequest *req = new InterceptedRequest(m_processId, request_id, routing_id, options, request,
+ traffic_annotation, profileIOData,
+ std::move(loader), std::move(client),
+ std::move(target_factory_clone));
+ req->Restart();
+}
+
+void ProxyingURLLoaderFactoryQt::OnTargetFactoryError()
+{
+ delete this;
+}
+
+void ProxyingURLLoaderFactoryQt::OnProxyBindingError()
+{
+ if (m_proxyBindings.empty())
+ delete this;
+}
+
+void ProxyingURLLoaderFactoryQt::Clone(network::mojom::URLLoaderFactoryRequest loader_request)
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+ m_proxyBindings.AddBinding(this, std::move(loader_request));
+}
+
+} // namespace QtWebEngineCore
diff --git a/src/core/net/proxying_url_loader_factory_qt.h b/src/core/net/proxying_url_loader_factory_qt.h
new file mode 100644
index 000000000..a2f175885
--- /dev/null
+++ b/src/core/net/proxying_url_loader_factory_qt.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PROXYING_URL_LOADER_FACTORY_QT_H_
+#define PROXYING_URL_LOADER_FACTORY_QT_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_delete_on_sequence.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/resource_response.h"
+#include "services/network/public/mojom/url_loader.mojom.h"
+#include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "url/gurl.h"
+
+// based on aw_proxying_url_loader_factory.h:
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+namespace content {
+class ResourceContext;
+}
+
+namespace QtWebEngineCore {
+
+class ProxyingURLLoaderFactoryQt : public network::mojom::URLLoaderFactory
+{
+public:
+ ProxyingURLLoaderFactoryQt(int process_id, content::ResourceContext *resourceContext,
+ network::mojom::URLLoaderFactoryRequest loader_request,
+ network::mojom::URLLoaderFactoryPtrInfo target_factory_info);
+
+ ~ProxyingURLLoaderFactoryQt() override;
+
+ // static
+ static void CreateProxy(int process_id, content::ResourceContext *resourceContext,
+ network::mojom::URLLoaderFactoryRequest loader,
+ network::mojom::URLLoaderFactoryPtrInfo target_factory_info);
+
+ void CreateLoaderAndStart(network::mojom::URLLoaderRequest loader, int32_t routing_id, int32_t request_id,
+ uint32_t options, const network::ResourceRequest &request,
+ network::mojom::URLLoaderClientPtr client,
+ const net::MutableNetworkTrafficAnnotationTag &traffic_annotation) override;
+
+ void Clone(network::mojom::URLLoaderFactoryRequest loader_request) override;
+
+private:
+ void OnTargetFactoryError();
+ void OnProxyBindingError();
+
+ const int m_processId;
+ mojo::BindingSet<network::mojom::URLLoaderFactory> m_proxyBindings;
+ network::mojom::URLLoaderFactoryPtr m_targetFactory;
+
+ content::ResourceContext *m_resourceContext;
+
+ base::WeakPtrFactory<ProxyingURLLoaderFactoryQt> m_weakFactory;
+
+ DISALLOW_COPY_AND_ASSIGN(ProxyingURLLoaderFactoryQt);
+};
+
+} // namespace QtWebEngineCore
+
+#endif // PROXYING_URL_LOADER_FACTORY_QT_H_
diff --git a/src/core/net/restricted_cookie_manager_qt.cpp b/src/core/net/restricted_cookie_manager_qt.cpp
deleted file mode 100644
index 7f1ca163e..000000000
--- a/src/core/net/restricted_cookie_manager_qt.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtWebEngine module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-// originally based on android_webview/browser/network_service/aw_proxying_restricted_cookie_manager.cc:
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "restricted_cookie_manager_qt.h"
-
-#include "api/qwebenginecookiestore.h"
-#include "api/qwebenginecookiestore_p.h"
-#include "profile_adapter.h"
-#include "profile_qt.h"
-#include "type_conversion.h"
-
-#include "base/memory/ptr_util.h"
-#include "base/task/post_task.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
-
-namespace QtWebEngineCore {
-
-class RestrictedCookieManagerListenerQt : public network::mojom::CookieChangeListener
-{
-public:
- RestrictedCookieManagerListenerQt(const GURL &url,
- const GURL &site_for_cookies,
- base::WeakPtr<RestrictedCookieManagerQt> restricted_cookie_manager,
- network::mojom::CookieChangeListenerPtr client_listener)
- : url_(url)
- , site_for_cookies_(site_for_cookies)
- , restricted_cookie_manager_(restricted_cookie_manager)
- , client_listener_(std::move(client_listener))
- {}
-
- void OnCookieChange(const net::CanonicalCookie &cookie, network::mojom::CookieChangeCause cause) override
- {
- if (restricted_cookie_manager_ && restricted_cookie_manager_->allowCookies(url_, site_for_cookies_))
- client_listener_->OnCookieChange(cookie, cause);
- }
-
-private:
- const GURL url_;
- const GURL site_for_cookies_;
- base::WeakPtr<RestrictedCookieManagerQt> restricted_cookie_manager_;
- network::mojom::CookieChangeListenerPtr client_listener_;
-};
-
-RestrictedCookieManagerQt::RestrictedCookieManagerQt(base::WeakPtr<ProfileIODataQt> profileIoData,
- network::mojom::RestrictedCookieManagerRole role,
- net::CookieStore *cookie_store,
- network::CookieSettings *cookie_settings,
- const url::Origin &origin,
- bool is_service_worker,
- int32_t process_id,
- int32_t frame_id)
- : network::RestrictedCookieManager(role, cookie_store, cookie_settings, origin,
- nullptr, is_service_worker, process_id, frame_id)
- , m_profileIoData(profileIoData)
- , weak_factory_(this)
-{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-}
-
-RestrictedCookieManagerQt::~RestrictedCookieManagerQt()
-{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-}
-
-void RestrictedCookieManagerQt::GetAllForUrl(const GURL &url,
- const GURL &site_for_cookies,
- network::mojom::CookieManagerGetOptionsPtr options,
- GetAllForUrlCallback callback)
-{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
- if (allowCookies(url, site_for_cookies)) {
- network::RestrictedCookieManager::GetAllForUrl(url, site_for_cookies, std::move(options), std::move(callback));
- } else {
- std::move(callback).Run(std::vector<net::CanonicalCookie>());
- }
-}
-
-void RestrictedCookieManagerQt::SetCanonicalCookie(const net::CanonicalCookie &cookie,
- const GURL &url,
- const GURL &site_for_cookies,
- SetCanonicalCookieCallback callback)
-{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
- if (allowCookies(url, site_for_cookies)) {
- network::RestrictedCookieManager::SetCanonicalCookie(cookie, url, site_for_cookies, std::move(callback));
- } else {
- std::move(callback).Run(false);
- }
-}
-
-void RestrictedCookieManagerQt::AddChangeListener(const GURL &url,
- const GURL &site_for_cookies,
- network::mojom::CookieChangeListenerPtr listener,
- AddChangeListenerCallback callback)
-{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
- network::mojom::CookieChangeListenerPtr proxy_listener_ptr;
- auto proxy_listener =
- std::make_unique<RestrictedCookieManagerListenerQt>(
- url, site_for_cookies, weak_factory_.GetWeakPtr(),
- std::move(listener));
-
- mojo::MakeStrongBinding(std::move(proxy_listener),
- mojo::MakeRequest(&proxy_listener_ptr));
-
- network::RestrictedCookieManager::AddChangeListener(
- url, site_for_cookies, std::move(proxy_listener_ptr),
- std::move(callback));
-}
-
-void RestrictedCookieManagerQt::GetCookiesString(const GURL &url,
- const GURL &site_for_cookies,
- GetCookiesStringCallback callback)
-{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
- if (allowCookies(url, site_for_cookies)) {
- network::RestrictedCookieManager::GetCookiesString(url, site_for_cookies, std::move(callback));
- } else {
- std::move(callback).Run("");
- }
-}
-
-void RestrictedCookieManagerQt::CookiesEnabledFor(const GURL &url,
- const GURL &site_for_cookies,
- CookiesEnabledForCallback callback)
-{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
- std::move(callback).Run(allowCookies(url, site_for_cookies));
-}
-
-bool RestrictedCookieManagerQt::allowCookies(const GURL &url, const GURL &site_for_cookies) const
-{
- if (!m_profileIoData)
- return false;
- return m_profileIoData->canGetCookies(toQt(site_for_cookies), toQt(url));
-}
-
-} // namespace QtWebEngineCore
diff --git a/src/core/net/system_network_context_manager.cpp b/src/core/net/system_network_context_manager.cpp
new file mode 100644
index 000000000..cf84b840b
--- /dev/null
+++ b/src/core/net/system_network_context_manager.cpp
@@ -0,0 +1,351 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// based on chrome/browser/net/system_network_context_manager.cc:
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/system_network_context_manager.h"
+
+#include <set>
+#include <unordered_map>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/feature_list.h"
+#include "base/logging.h"
+#include "base/sequence_checker.h"
+#include "base/strings/string_split.h"
+#include "base/task/post_task.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/net/chrome_mojo_proxy_resolver_factory.h"
+#include "chrome/common/chrome_switches.h"
+#include "components/certificate_transparency/ct_known_logs.h"
+#include "components/network_session_configurator/common/network_features.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/cors_exempt_headers.h"
+#include "content/public/browser/network_service_instance.h"
+#include "content/public/common/content_features.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/service_names.mojom.h"
+#include "content/public/common/user_agent.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
+#include "net/dns/public/util.h"
+#include "net/net_buildflags.h"
+#include "net/third_party/uri_template/uri_template.h"
+#include "services/network/network_service.h"
+#include "services/network/public/cpp/cross_thread_shared_url_loader_factory_info.h"
+#include "services/network/public/cpp/features.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/mojom/host_resolver.mojom.h"
+#include "services/proxy_resolver/public/mojom/proxy_resolver.mojom.h"
+#include "url/gurl.h"
+
+namespace {
+
+// The global instance of the SystemNetworkContextmanager.
+SystemNetworkContextManager *g_system_network_context_manager = nullptr;
+
+network::mojom::HttpAuthStaticParamsPtr CreateHttpAuthStaticParams()
+{
+ network::mojom::HttpAuthStaticParamsPtr auth_static_params = network::mojom::HttpAuthStaticParams::New();
+
+ auth_static_params->supported_schemes = { "basic", "digest", "ntlm", "negotiate" };
+
+ return auth_static_params;
+}
+
+network::mojom::HttpAuthDynamicParamsPtr CreateHttpAuthDynamicParams()
+{
+ network::mojom::HttpAuthDynamicParamsPtr auth_dynamic_params = network::mojom::HttpAuthDynamicParams::New();
+
+ auto *command_line = base::CommandLine::ForCurrentProcess();
+ auth_dynamic_params->server_whitelist = command_line->GetSwitchValueASCII(switches::kAuthServerWhitelist);
+// auth_dynamic_params->delegate_whitelist = command_line->GetSwitchValueASCII(switches::kAuthNegotiateDelegateWhitelist);
+// auth_dynamic_params->enable_negotiate_port = command_line->HasSwitch(switches::kEnableAuthNegotiatePort);
+
+ return auth_dynamic_params;
+}
+
+} // namespace
+
+// SharedURLLoaderFactory backed by a SystemNetworkContextManager and its
+// network context. Transparently handles crashes.
+class SystemNetworkContextManager::URLLoaderFactoryForSystem : public network::SharedURLLoaderFactory
+{
+public:
+ explicit URLLoaderFactoryForSystem(SystemNetworkContextManager *manager) : manager_(manager)
+ {
+ DETACH_FROM_SEQUENCE(sequence_checker_);
+ }
+
+ // mojom::URLLoaderFactory implementation:
+
+ void CreateLoaderAndStart(network::mojom::URLLoaderRequest request, int32_t routing_id, int32_t request_id,
+ uint32_t options, const network::ResourceRequest &url_request,
+ network::mojom::URLLoaderClientPtr client,
+ const net::MutableNetworkTrafficAnnotationTag &traffic_annotation) override
+ {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (!manager_)
+ return;
+ manager_->GetURLLoaderFactory()->CreateLoaderAndStart(std::move(request), routing_id, request_id, options,
+ url_request, std::move(client), traffic_annotation);
+ }
+
+ void Clone(network::mojom::URLLoaderFactoryRequest request) override
+ {
+ if (!manager_)
+ return;
+ manager_->GetURLLoaderFactory()->Clone(std::move(request));
+ }
+
+ // SharedURLLoaderFactory implementation:
+ std::unique_ptr<network::SharedURLLoaderFactoryInfo> Clone() override
+ {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return std::make_unique<network::CrossThreadSharedURLLoaderFactoryInfo>(this);
+ }
+
+ void Shutdown() { manager_ = nullptr; }
+
+private:
+ friend class base::RefCounted<URLLoaderFactoryForSystem>;
+ ~URLLoaderFactoryForSystem() override {}
+
+ SEQUENCE_CHECKER(sequence_checker_);
+ SystemNetworkContextManager *manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(URLLoaderFactoryForSystem);
+};
+
+network::mojom::NetworkContext *SystemNetworkContextManager::GetContext()
+{
+ if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
+ // SetUp should already have been called.
+ DCHECK(io_thread_network_context_);
+ return io_thread_network_context_.get();
+ }
+
+ if (!network_service_network_context_ || network_service_network_context_.encountered_error()) {
+ // This should call into OnNetworkServiceCreated(), which will re-create
+ // the network service, if needed. There's a chance that it won't be
+ // invoked, if the NetworkContext has encountered an error but the
+ // NetworkService has not yet noticed its pipe was closed. In that case,
+ // trying to create a new NetworkContext would fail, anyways, and hopefully
+ // a new NetworkContext will be created on the next GetContext() call.
+ content::GetNetworkService();
+ DCHECK(network_service_network_context_);
+ }
+ return network_service_network_context_.get();
+}
+
+network::mojom::URLLoaderFactory *SystemNetworkContextManager::GetURLLoaderFactory()
+{
+ // Create the URLLoaderFactory as needed.
+ if (url_loader_factory_ && !url_loader_factory_.encountered_error()) {
+ return url_loader_factory_.get();
+ }
+
+ network::mojom::URLLoaderFactoryParamsPtr params = network::mojom::URLLoaderFactoryParams::New();
+ params->process_id = network::mojom::kBrowserProcessId;
+ params->is_corb_enabled = false;
+ GetContext()->CreateURLLoaderFactory(mojo::MakeRequest(&url_loader_factory_), std::move(params));
+ return url_loader_factory_.get();
+}
+
+scoped_refptr<network::SharedURLLoaderFactory> SystemNetworkContextManager::GetSharedURLLoaderFactory()
+{
+ return shared_url_loader_factory_;
+}
+
+void SystemNetworkContextManager::SetUp(
+ network::mojom::NetworkContextRequest *network_context_request,
+ network::mojom::NetworkContextParamsPtr *network_context_params, bool *stub_resolver_enabled,
+ base::Optional<std::vector<network::mojom::DnsOverHttpsServerPtr>> *dns_over_https_servers,
+ network::mojom::HttpAuthStaticParamsPtr *http_auth_static_params,
+ network::mojom::HttpAuthDynamicParamsPtr *http_auth_dynamic_params, bool *is_quic_allowed)
+{
+ if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
+ *network_context_request = mojo::MakeRequest(&io_thread_network_context_);
+ *network_context_params = CreateNetworkContextParams();
+ }
+ *is_quic_allowed = false;
+ *http_auth_static_params = CreateHttpAuthStaticParams();
+ *http_auth_dynamic_params = CreateHttpAuthDynamicParams();
+ // GetStubResolverConfig(local_state_, stub_resolver_enabled, dns_over_https_servers);
+}
+
+// static
+SystemNetworkContextManager *SystemNetworkContextManager::CreateInstance()
+{
+ DCHECK(!g_system_network_context_manager);
+ g_system_network_context_manager = new SystemNetworkContextManager();
+ return g_system_network_context_manager;
+}
+
+// static
+SystemNetworkContextManager *SystemNetworkContextManager::GetInstance()
+{
+ return g_system_network_context_manager;
+}
+
+// static
+void SystemNetworkContextManager::DeleteInstance()
+{
+ DCHECK(g_system_network_context_manager);
+ delete g_system_network_context_manager;
+}
+
+SystemNetworkContextManager::SystemNetworkContextManager()
+{
+ shared_url_loader_factory_ = new URLLoaderFactoryForSystem(this);
+}
+
+SystemNetworkContextManager::~SystemNetworkContextManager()
+{
+ shared_url_loader_factory_->Shutdown();
+}
+
+void SystemNetworkContextManager::OnNetworkServiceCreated(network::mojom::NetworkService *network_service)
+{
+ if (!base::FeatureList::IsEnabled(network::features::kNetworkService))
+ return;
+ // Disable QUIC globally
+ network_service->DisableQuic();
+
+ network_service->SetUpHttpAuth(CreateHttpAuthStaticParams());
+ network_service->ConfigureHttpAuthPrefs(CreateHttpAuthDynamicParams());
+
+ // The system NetworkContext must be created first, since it sets
+ // |primary_network_context| to true.
+ network_service->CreateNetworkContext(MakeRequest(&network_service_network_context_), CreateNetworkContextParams());
+
+ // Configure the stub resolver. This must be done after the system
+ // NetworkContext is created, but before anything has the chance to use it.
+ // bool stub_resolver_enabled;
+ // base::Optional<std::vector<network::mojom::DnsOverHttpsServerPtr>> dns_over_https_servers;
+ // GetStubResolverConfig(local_state_, &stub_resolver_enabled, &dns_over_https_servers);
+ // content::GetNetworkService()->ConfigureStubHostResolver(stub_resolver_enabled, std::move(dns_over_https_servers));
+}
+
+void SystemNetworkContextManager::AddSSLConfigToNetworkContextParams(network::mojom::NetworkContextParams *network_context_params)
+{
+ network_context_params->initial_ssl_config = network::mojom::SSLConfig::New();
+ network_context_params->initial_ssl_config->rev_checking_enabled = true;
+ network_context_params->initial_ssl_config->symantec_enforcement_disabled = true;
+}
+
+network::mojom::NetworkContextParamsPtr SystemNetworkContextManager::CreateDefaultNetworkContextParams()
+{
+ network::mojom::NetworkContextParamsPtr network_context_params = network::mojom::NetworkContextParams::New();
+ content::UpdateCorsExemptHeader(network_context_params.get());
+
+ network_context_params->enable_brotli = true;
+
+ // network_context_params->user_agent = GetUserAgent();
+
+ // Disable referrers by default. Any consumer that enables referrers should
+ // respect prefs::kEnableReferrers from the appropriate pref store.
+ network_context_params->enable_referrers = false;
+
+ // const base::CommandLine& command_line =
+ // *base::CommandLine::ForCurrentProcess();
+
+ // // TODO(eroman): Figure out why this doesn't work in single-process mode,
+ // // or if it does work, now.
+ // // Should be possible now that a private isolate is used.
+ // // http://crbug.com/474654
+ // if (!command_line.HasSwitch(switches::kWinHttpProxyResolver)) {
+ // if (command_line.HasSwitch(switches::kSingleProcess)) {
+ // LOG(ERROR) << "Cannot use V8 Proxy resolver in single process mode.";
+ // } else {
+ network_context_params->proxy_resolver_factory = ChromeMojoProxyResolverFactory::CreateWithSelfOwnedReceiver();
+ // }
+ // }
+
+ // network_context_params->pac_quick_check_enabled = local_state_->GetBoolean(prefs::kQuickCheckEnabled);
+
+ // Use the SystemNetworkContextManager to populate and update SSL
+ // configuration. The SystemNetworkContextManager is owned by the
+ // BrowserProcess itself, so will only be destroyed on shutdown, at which
+ // point, all NetworkContexts will be destroyed as well.
+ AddSSLConfigToNetworkContextParams(network_context_params.get());
+
+ // CT is only enabled on Desktop platforms for now.
+ network_context_params->enforce_chrome_ct_policy = true;
+ for (const auto &ct_log : certificate_transparency::GetKnownLogs()) {
+ // TODO(rsleevi): https://crbug.com/702062 - Remove this duplication.
+ network::mojom::CTLogInfoPtr log_info = network::mojom::CTLogInfo::New();
+ log_info->public_key = std::string(ct_log.log_key, ct_log.log_key_length);
+ log_info->name = ct_log.log_name;
+ network_context_params->ct_logs.push_back(std::move(log_info));
+ }
+
+ network_context_params->http_09_on_non_default_ports_enabled = false;
+
+ return network_context_params;
+}
+
+network::mojom::NetworkContextParamsPtr SystemNetworkContextManager::CreateNetworkContextParams()
+{
+ // TODO(mmenke): Set up parameters here (in memory cookie store, etc).
+ network::mojom::NetworkContextParamsPtr network_context_params = CreateDefaultNetworkContextParams();
+
+ network_context_params->context_name = std::string("system");
+
+ network_context_params->enable_referrers = false;
+
+ network_context_params->http_cache_enabled = false;
+
+ // These are needed for PAC scripts that use FTP URLs.
+#if !BUILDFLAG(DISABLE_FTP_SUPPORT)
+ network_context_params->enable_ftp_url_support = true;
+#endif
+
+ network_context_params->primary_network_context = true;
+
+ proxy_config_monitor_.AddToNetworkContextParams(network_context_params.get());
+
+ return network_context_params;
+}
diff --git a/src/core/net/system_network_context_manager.h b/src/core/net/system_network_context_manager.h
new file mode 100644
index 000000000..288af5195
--- /dev/null
+++ b/src/core/net/system_network_context_manager.h
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// based on chrome/browser/net/system_network_context_manager.h:
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SYSTEM_NETWORK_CONTEXT_MANAGER_H_
+#define SYSTEM_NETWORK_CONTEXT_MANAGER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/optional.h"
+#include "services/network/public/mojom/host_resolver.mojom-forward.h"
+#include "services/network/public/mojom/network_context.mojom.h"
+#include "services/network/public/mojom/network_service.mojom-forward.h"
+#include "services/network/public/mojom/ssl_config.mojom-forward.h"
+#include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
+
+#include "net/proxy_config_monitor.h"
+
+namespace network {
+namespace mojom {
+class URLLoaderFactory;
+}
+class SharedURLLoaderFactory;
+} // namespace network
+
+namespace net_log {
+class NetExportFileWriter;
+}
+
+// Responsible for creating and managing access to the system NetworkContext.
+// Lives on the UI thread. The NetworkContext this owns is intended for requests
+// not associated with a profile. It stores no data on disk, and has no HTTP
+// cache, but it does have ephemeral cookie and channel ID stores. It also does
+// not have access to HTTP proxy auth information the user has entered or that
+// comes from extensions, and similarly, has no extension-provided per-profile
+// proxy configuration information.
+//
+// This class is also responsible for configuring global NetworkService state.
+//
+// The "system" NetworkContext will either share a URLRequestContext with
+// IOThread's SystemURLRequestContext and be part of IOThread's NetworkService
+// (If the network service is disabled) or be an independent NetworkContext
+// using the actual network service.
+//
+// This class is intended to eventually replace IOThread. Handling the two cases
+// differently allows this to be used in production without breaking anything or
+// requiring two separate paths, while IOThread consumers slowly transition over
+// to being compatible with the network service.
+class SystemNetworkContextManager
+{
+public:
+ ~SystemNetworkContextManager();
+
+ // Creates the global instance of SystemNetworkContextManager. If an
+ // instance already exists, this will cause a DCHECK failure.
+ static SystemNetworkContextManager *CreateInstance();
+
+ // Gets the global SystemNetworkContextManager instance.
+ static SystemNetworkContextManager *GetInstance();
+
+ // Destroys the global SystemNetworkContextManager instance.
+ static void DeleteInstance();
+
+ // If the network service is disabled, |network_context_request| will be for
+ // the NetworkContext used by the SystemNetworkContextManager and
+ // |network_context_params| as needed to set up a system NetworkContext.
+ // Otherwise, this method can still be used to help set up the IOThread's
+ // in-process URLRequestContext.
+ //
+ // Must be called before the system NetworkContext is first used.
+ //
+ // |stub_resolver_enabled|, |dns_over_https_servers|,
+ // |http_auth_static_params|, |http_auth_dynamic_params|, and
+ // |is_quic_allowed| are used to pass initial NetworkService state to the
+ // caller, so the NetworkService can be configured appropriately. Using
+ // NetworkService's Mojo interface to set those options would lead to races
+ // with other UI->IO thread network-related tasks, since Mojo doesn't preserve
+ // execution order relative to PostTasks.
+ void SetUp(network::mojom::NetworkContextRequest *network_context_request,
+ network::mojom::NetworkContextParamsPtr *network_context_params, bool *stub_resolver_enabled,
+ base::Optional<std::vector<network::mojom::DnsOverHttpsServerPtr>> *dns_over_https_servers,
+ network::mojom::HttpAuthStaticParamsPtr *http_auth_static_params,
+ network::mojom::HttpAuthDynamicParamsPtr *http_auth_dynamic_params, bool *is_quic_allowed);
+
+ // Returns the System NetworkContext. May only be called after SetUp(). Does
+ // any initialization of the NetworkService that may be needed when first
+ // called.
+ network::mojom::NetworkContext *GetContext();
+
+ // Returns a URLLoaderFactory owned by the SystemNetworkContextManager that is
+ // backed by the SystemNetworkContext. Allows sharing of the URLLoaderFactory.
+ // Prefer this to creating a new one. Call Clone() on the value returned by
+ // this method to get a URLLoaderFactory that can be used on other threads.
+ network::mojom::URLLoaderFactory *GetURLLoaderFactory();
+
+ // Returns a SharedURLLoaderFactory owned by the SystemNetworkContextManager
+ // that is backed by the SystemNetworkContext.
+ scoped_refptr<network::SharedURLLoaderFactory> GetSharedURLLoaderFactory();
+
+ // Called when content creates a NetworkService. Creates the
+ // SystemNetworkContext, if the network service is enabled.
+ void OnNetworkServiceCreated(network::mojom::NetworkService *network_service);
+
+ // Populates |initial_ssl_config| and |ssl_config_client_request| members of
+ // |network_context_params|. As long as the SystemNetworkContextManager
+ // exists, any NetworkContext created with the params will continue to get
+ // SSL configuration updates.
+ void AddSSLConfigToNetworkContextParams(network::mojom::NetworkContextParams *network_context_params);
+
+ // Returns default set of parameters for configuring the network service.
+ network::mojom::NetworkContextParamsPtr CreateDefaultNetworkContextParams();
+
+private:
+ class URLLoaderFactoryForSystem;
+
+ explicit SystemNetworkContextManager();
+
+ // Creates parameters for the NetworkContext. May only be called once, since
+ // it initializes some class members.
+ network::mojom::NetworkContextParamsPtr CreateNetworkContextParams();
+
+ // ProxyConfigMonitor proxy_config_monitor_;
+
+ // NetworkContext using the network service, if the network service is
+ // enabled. nullptr, otherwise.
+ network::mojom::NetworkContextPtr network_service_network_context_;
+
+ // This is a NetworkContext that wraps the IOThread's SystemURLRequestContext.
+ // Always initialized in SetUp, but it's only returned by Context() when the
+ // network service is disabled.
+ network::mojom::NetworkContextPtr io_thread_network_context_;
+
+ // URLLoaderFactory backed by the NetworkContext returned by GetContext(), so
+ // consumers don't all need to create their own factory.
+ scoped_refptr<URLLoaderFactoryForSystem> shared_url_loader_factory_;
+ network::mojom::URLLoaderFactoryPtr url_loader_factory_;
+
+ ProxyConfigMonitor proxy_config_monitor_;
+
+ DISALLOW_COPY_AND_ASSIGN(SystemNetworkContextManager);
+};
+
+#endif // SYSTEM_NETWORK_CONTEXT_MANAGER_H_
diff --git a/src/core/net/url_request_context_getter_qt.cpp b/src/core/net/url_request_context_getter_qt.cpp
deleted file mode 100644
index 6081a5e9f..000000000
--- a/src/core/net/url_request_context_getter_qt.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtWebEngine module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "url_request_context_getter_qt.h"
-#include "profile_io_data_qt.h"
-
-#include "base/task/post_task.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-
-namespace QtWebEngineCore {
-
-URLRequestContextGetterQt::URLRequestContextGetterQt(ProfileIODataQt *data)
- : m_profileIOData(data)
-{
-}
-
-URLRequestContextGetterQt::~URLRequestContextGetterQt()
-{
-}
-
-net::URLRequestContext *URLRequestContextGetterQt::GetURLRequestContext()
-{
- Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
- return m_profileIOData->urlRequestContext();
-}
-
-scoped_refptr<base::SingleThreadTaskRunner> URLRequestContextGetterQt::GetNetworkTaskRunner() const
-{
- return base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO});
-}
-
-} // namespace QtWebEngineCore
diff --git a/src/core/net/url_request_custom_job.cpp b/src/core/net/url_request_custom_job.cpp
deleted file mode 100644
index 0d4ac620f..000000000
--- a/src/core/net/url_request_custom_job.cpp
+++ /dev/null
@@ -1,267 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtWebEngine module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "url_request_custom_job.h"
-#include "url_request_custom_job_proxy.h"
-
-#include "api/qwebengineurlscheme.h"
-
-#include "base/strings/stringprintf.h"
-#include "base/task/post_task.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "net/base/io_buffer.h"
-#include "net/http/http_util.h"
-
-#include <QIODevice>
-
-using namespace net;
-
-namespace QtWebEngineCore {
-
-URLRequestCustomJob::URLRequestCustomJob(URLRequest *request,
- NetworkDelegate *networkDelegate,
- const std::string &scheme,
- QPointer<ProfileAdapter> profileAdapter)
- : URLRequestJob(request, networkDelegate)
- , m_proxy(new URLRequestCustomJobProxy(this, scheme, profileAdapter))
- , m_device(nullptr)
- , m_firstBytePosition(0)
- , m_error(0)
- , m_pendingReadSize(0)
- , m_pendingReadPos(0)
- , m_pendingReadBuffer(nullptr)
- , m_corsEnabled(QWebEngineUrlScheme::schemeByName(QByteArray::fromStdString(scheme))
- .flags().testFlag(QWebEngineUrlScheme::CorsEnabled))
-{
-}
-
-URLRequestCustomJob::~URLRequestCustomJob()
-{
- m_proxy->m_job = nullptr;
- if (m_device && m_device->isOpen())
- m_device->close();
- m_device = nullptr;
- base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
- base::BindOnce(&URLRequestCustomJobProxy::release, m_proxy));
-}
-
-void URLRequestCustomJob::Start()
-{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
- HttpRequestHeaders requestHeaders = request()->extra_request_headers();
- std::map<std::string, std::string> headers;
- net::HttpRequestHeaders::Iterator it(requestHeaders);
- while (it.GetNext())
- headers.emplace(it.name(), it.value());
- if (!request()->referrer().empty())
- headers.emplace("Referer", request()->referrer());
-
- // TODO: handle UploadDataStream, for instance using a QIODevice wrapper.
-
- base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
- base::BindOnce(&URLRequestCustomJobProxy::initialize,
- m_proxy,
- request()->url(),
- request()->method(),
- request()->initiator(),
- std::move(headers)));
-}
-
-void URLRequestCustomJob::Kill()
-{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
- m_proxy->m_job = nullptr;
- if (m_device && m_device->isOpen())
- m_device->close();
- if (m_pendingReadBuffer) {
- m_pendingReadBuffer->Release();
- m_pendingReadBuffer = nullptr;
- m_pendingReadSize = 0;
- m_pendingReadPos = 0;
- }
- m_device = nullptr;
- base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
- base::BindOnce(&URLRequestCustomJobProxy::release,
- m_proxy));
- URLRequestJob::Kill();
-}
-
-bool URLRequestCustomJob::GetMimeType(std::string *mimeType) const
-{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
- if (m_mimeType.size() > 0) {
- *mimeType = m_mimeType;
- return true;
- }
- return false;
-}
-
-bool URLRequestCustomJob::GetCharset(std::string *charset)
-{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
- if (m_charset.size() > 0) {
- *charset = m_charset;
- return true;
- }
- return false;
-}
-
-void URLRequestCustomJob::GetResponseInfo(HttpResponseInfo *info)
-{
- // Based on net::URLRequestRedirectJob::StartAsync()
-
- if (m_error)
- return;
-
- std::string headers;
- if (m_redirect.is_valid()) {
- headers += "HTTP/1.1 303 See Other\n";
- headers += base::StringPrintf("Location: %s\n", m_redirect.spec().c_str());
- } else {
- headers += base::StringPrintf("HTTP/1.1 %i OK\n", 200);
- if (m_mimeType.size() > 0) {
- headers += base::StringPrintf("Content-Type: %s", m_mimeType.c_str());
- if (m_charset.size() > 0)
- headers += base::StringPrintf("; charset=%s", m_charset.c_str());
- headers += "\n";
- }
- }
- if (m_corsEnabled) {
- std::string origin;
- if (request_->extra_request_headers().GetHeader("Origin", &origin)) {
- headers += base::StringPrintf("Access-Control-Allow-Origin: %s\n", origin.c_str());
- headers += "Access-Control-Allow-Credentials: true\n";
- }
- }
-
- info->headers = new HttpResponseHeaders(HttpUtil::AssembleRawHeaders(headers));
-}
-
-bool URLRequestCustomJob::IsRedirectResponse(GURL *location, int *http_status_code, bool * /*insecure_scheme_was_upgraded*/)
-{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
- if (m_redirect.is_valid()) {
- *location = m_redirect;
- *http_status_code = 303;
- return true;
- }
- return false;
-}
-
-void URLRequestCustomJob::SetExtraRequestHeaders(const HttpRequestHeaders &headers)
-{
- std::string rangeHeader;
- if (headers.GetHeader(HttpRequestHeaders::kRange, &rangeHeader)) {
- std::vector<HttpByteRange> ranges;
- if (HttpUtil::ParseRangeHeader(rangeHeader, &ranges)) {
- // Chromium doesn't support multiple range requests in one single URL request.
- if (ranges.size() == 1)
- m_firstBytePosition = ranges[0].first_byte_position();
- }
- }
-}
-
-int URLRequestCustomJob::ReadRawData(IOBuffer *buf, int bufSize)
-{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
- if (m_error)
- return m_error;
- qint64 rv = m_device ? m_device->read(buf->data(), bufSize) : -1;
- if (rv > 0) {
- return static_cast<int>(rv);
- } else if (rv == 0) {
- // Returning zero is interpreted as EOF by Chromium, so only
- // return zero if we are the end of the file.
- if (m_device->atEnd())
- return 0;
- // Otherwise return IO_PENDING and call ReadRawDataComplete when we have data
- // for them.
- buf->AddRef();
- m_pendingReadPos = 0;
- m_pendingReadSize = bufSize;
- m_pendingReadBuffer = buf;
- return ERR_IO_PENDING;
- } else {
- // QIODevice::read might have called fail on us.
- if (m_error)
- return m_error;
- if (m_device && m_device->atEnd())
- return 0;
- return ERR_FAILED;
- }
-}
-
-void URLRequestCustomJob::notifyReadyRead()
-{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
- if (!m_device)
- return;
- if (!m_pendingReadSize)
- return;
- Q_ASSERT(m_pendingReadBuffer);
- if (!m_pendingReadBuffer)
- return;
-
- qint64 rv = m_device->read(m_pendingReadBuffer->data() + m_pendingReadPos, m_pendingReadSize - m_pendingReadPos);
- if (rv == 0)
- return;
- if (rv < 0) {
- if (m_error)
- rv = m_error;
- else if (m_device->atEnd())
- rv = 0;
- else
- rv = ERR_FAILED;
- } else {
- m_pendingReadPos += rv;
- if (m_pendingReadPos < m_pendingReadSize && !m_device->atEnd())
- return;
- rv = m_pendingReadPos;
- }
- // killJob may be called from ReadRawDataComplete
- net::IOBuffer *buf = m_pendingReadBuffer;
- m_pendingReadBuffer = nullptr;
- m_pendingReadSize = 0;
- m_pendingReadPos = 0;
- ReadRawDataComplete(rv);
- buf->Release();
-}
-
-} // namespace
diff --git a/src/core/net/url_request_custom_job.h b/src/core/net/url_request_custom_job.h
deleted file mode 100644
index db40b52bb..000000000
--- a/src/core/net/url_request_custom_job.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtWebEngine module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef URL_REQUEST_CUSTOM_JOB_H_
-#define URL_REQUEST_CUSTOM_JOB_H_
-
-#include "net/url_request/url_request_job.h"
-#include "url/gurl.h"
-#include <QtCore/QPointer>
-
-QT_FORWARD_DECLARE_CLASS(QIODevice)
-
-namespace QtWebEngineCore {
-
-class ProfileAdapter;
-class URLRequestCustomJobDelegate;
-class URLRequestCustomJobProxy;
-
-// A request job that handles reading custom URL schemes
-class URLRequestCustomJob : public net::URLRequestJob
-{
-public:
- URLRequestCustomJob(net::URLRequest *request,
- net::NetworkDelegate *networkDelegate,
- const std::string &scheme,
- QPointer<ProfileAdapter> profileAdapter);
- void Start() override;
- void Kill() override;
- int ReadRawData(net::IOBuffer *buf, int buf_size) override;
- bool GetMimeType(std::string *mimeType) const override;
- bool GetCharset(std::string *charset) override;
- void GetResponseInfo(net::HttpResponseInfo *info) override;
- bool IsRedirectResponse(GURL *location, int *http_status_code, bool *insecure_scheme_was_upgraded) override;
- void SetExtraRequestHeaders(const net::HttpRequestHeaders &headers);
-
-protected:
- virtual ~URLRequestCustomJob();
-
-private:
- void notifyReadyRead();
- scoped_refptr<URLRequestCustomJobProxy> m_proxy;
- std::string m_mimeType;
- std::string m_charset;
- GURL m_redirect;
- QIODevice *m_device;
- int64_t m_firstBytePosition;
- int m_error;
- int m_pendingReadSize;
- int m_pendingReadPos;
- net::IOBuffer *m_pendingReadBuffer;
- const bool m_corsEnabled;
-
- friend class URLRequestCustomJobProxy;
-
- DISALLOW_COPY_AND_ASSIGN(URLRequestCustomJob);
-};
-} // namespace QtWebEngineCore
-
-#endif // URL_REQUEST_CUSTOM_JOB_H_
diff --git a/src/core/net/url_request_custom_job_delegate.cpp b/src/core/net/url_request_custom_job_delegate.cpp
index f73296cf0..ff307bede 100644
--- a/src/core/net/url_request_custom_job_delegate.cpp
+++ b/src/core/net/url_request_custom_job_delegate.cpp
@@ -92,27 +92,27 @@ void URLRequestCustomJobDelegate::reply(const QByteArray &contentType, QIODevice
{
if (device)
QObject::connect(device, &QIODevice::readyRead, this, &URLRequestCustomJobDelegate::slotReadyRead);
- base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
- base::BindOnce(&URLRequestCustomJobProxy::reply,
- m_proxy,contentType.toStdString(),device));
+ m_proxy->m_ioTaskRunner->PostTask(FROM_HERE,
+ base::BindOnce(&URLRequestCustomJobProxy::reply,
+ m_proxy, contentType.toStdString(),device));
}
void URLRequestCustomJobDelegate::slotReadyRead()
{
- base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
- base::BindOnce(&URLRequestCustomJobProxy::readyRead, m_proxy));
+ m_proxy->m_ioTaskRunner->PostTask(FROM_HERE,
+ base::BindOnce(&URLRequestCustomJobProxy::readyRead, m_proxy));
}
void URLRequestCustomJobDelegate::abort()
{
- base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
- base::BindOnce(&URLRequestCustomJobProxy::abort, m_proxy));
+ m_proxy->m_ioTaskRunner->PostTask(FROM_HERE,
+ base::BindOnce(&URLRequestCustomJobProxy::abort, m_proxy));
}
void URLRequestCustomJobDelegate::redirect(const QUrl &url)
{
- base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
- base::BindOnce(&URLRequestCustomJobProxy::redirect, m_proxy, toGurl(url)));
+ m_proxy->m_ioTaskRunner->PostTask(FROM_HERE,
+ base::BindOnce(&URLRequestCustomJobProxy::redirect, m_proxy, toGurl(url)));
}
void URLRequestCustomJobDelegate::fail(Error error)
@@ -138,8 +138,8 @@ void URLRequestCustomJobDelegate::fail(Error error)
break;
}
if (net_error) {
- base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
- base::BindOnce(&URLRequestCustomJobProxy::fail, m_proxy, net_error));
+ m_proxy->m_ioTaskRunner->PostTask(FROM_HERE,
+ base::BindOnce(&URLRequestCustomJobProxy::fail, m_proxy, net_error));
}
}
diff --git a/src/core/net/url_request_custom_job_proxy.cpp b/src/core/net/url_request_custom_job_proxy.cpp
index 27fed7bf2..d2df64f2f 100644
--- a/src/core/net/url_request_custom_job_proxy.cpp
+++ b/src/core/net/url_request_custom_job_proxy.cpp
@@ -38,28 +38,29 @@
****************************************************************************/
#include "url_request_custom_job_proxy.h"
-#include "url_request_custom_job.h"
#include "url_request_custom_job_delegate.h"
+
+#include "content/public/browser/browser_thread.h"
+#include "net/base/net_errors.h"
+
#include "api/qwebengineurlrequestjob.h"
#include "profile_adapter.h"
#include "type_conversion.h"
-#include "content/public/browser/browser_thread.h"
#include "web_engine_context.h"
-using namespace net;
-
namespace QtWebEngineCore {
-URLRequestCustomJobProxy::URLRequestCustomJobProxy(URLRequestCustomJob *job,
+URLRequestCustomJobProxy::URLRequestCustomJobProxy(URLRequestCustomJobProxy::Client *client,
const std::string &scheme,
QPointer<ProfileAdapter> profileAdapter)
- : m_job(job)
+ : m_client(client)
, m_started(false)
, m_scheme(scheme)
, m_delegate(nullptr)
, m_profileAdapter(profileAdapter)
+ , m_ioTaskRunner(m_client->taskRunner())
{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+ DCHECK(m_ioTaskRunner && m_ioTaskRunner->RunsTasksInCurrentSequence());
}
URLRequestCustomJobProxy::~URLRequestCustomJobProxy()
@@ -87,74 +88,74 @@ void URLRequestCustomJobProxy::setReplyCharset(const std::string &charset)
*/
void URLRequestCustomJobProxy::reply(std::string mimeType, QIODevice *device)
{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
- if (!m_job)
+ if (!m_client)
return;
- m_job->m_mimeType = mimeType;
- m_job->m_device = device;
- if (m_job->m_device && !m_job->m_device->isReadable())
- m_job->m_device->open(QIODevice::ReadOnly);
+ DCHECK (!m_ioTaskRunner || m_ioTaskRunner->RunsTasksInCurrentSequence());
+ m_client->m_mimeType = mimeType;
+ m_client->m_device = device;
+ if (m_client->m_device && !m_client->m_device->isReadable())
+ m_client->m_device->open(QIODevice::ReadOnly);
- if (m_job->m_firstBytePosition > 0)
- m_job->m_device->seek(m_job->m_firstBytePosition);
+ if (m_client->m_firstBytePosition > 0)
+ m_client->m_device->seek(m_client->m_firstBytePosition);
- qint64 deviceSize = m_job->m_device ? m_job->m_device->size() : -1;
- qint64 remainingBytes = deviceSize - m_job->m_firstBytePosition;
+ qint64 deviceSize = m_client->m_device ? m_client->m_device->size() : -1;
+ qint64 remainingBytes = deviceSize - m_client->m_firstBytePosition;
if (remainingBytes > 0)
- m_job->set_expected_content_size(remainingBytes);
+ m_client->notifyExpectedContentSize(remainingBytes);
- if (m_job->m_device && m_job->m_device->isReadable()) {
+ if (m_client->m_device && m_client->m_device->isReadable()) {
m_started = true;
- m_job->NotifyHeadersComplete();
+ m_client->notifyHeadersComplete();
} else {
- fail(ERR_INVALID_URL);
+ fail(net::ERR_INVALID_URL);
}
}
void URLRequestCustomJobProxy::redirect(GURL url)
{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
- if (!m_job)
+ if (!m_client)
return;
- if (m_job->m_device || m_job->m_error)
+ DCHECK (!m_ioTaskRunner || m_ioTaskRunner->RunsTasksInCurrentSequence());
+ if (m_client->m_device || m_client->m_error)
return;
- m_job->m_redirect = url;
+ m_client->m_redirect = url;
m_started = true;
- m_job->NotifyHeadersComplete();
+ m_client->notifyHeadersComplete();
}
void URLRequestCustomJobProxy::abort()
{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
- if (!m_job)
+ if (!m_client)
return;
- if (m_job->m_device && m_job->m_device->isOpen())
- m_job->m_device->close();
- m_job->m_device = nullptr;
+ DCHECK (!m_ioTaskRunner || m_ioTaskRunner->RunsTasksInCurrentSequence());
+ if (m_client->m_device && m_client->m_device->isOpen())
+ m_client->m_device->close();
+ m_client->m_device = nullptr;
if (m_started)
- m_job->NotifyCanceled();
+ m_client->notifyCanceled();
else
- m_job->NotifyStartError(URLRequestStatus(URLRequestStatus::CANCELED, ERR_ABORTED));
+ m_client->notifyAborted();
}
void URLRequestCustomJobProxy::fail(int error)
{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
- if (!m_job)
+ if (!m_client)
return;
- m_job->m_error = error;
- if (m_job->m_device)
- m_job->m_device->close();
+ DCHECK (m_ioTaskRunner->RunsTasksInCurrentSequence());
+ m_client->m_error = error;
+ if (m_client->m_device)
+ m_client->m_device->close();
if (!m_started)
- m_job->NotifyStartError(URLRequestStatus::FromError(error));
+ m_client->notifyStartFailure(error);
// else we fail on the next read, or the read that might already be in progress
}
void URLRequestCustomJobProxy::readyRead()
{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
- if (m_job)
- m_job->notifyReadyRead();
+ DCHECK (m_ioTaskRunner->RunsTasksInCurrentSequence());
+ if (m_client)
+ m_client->notifyReadyRead();
}
void URLRequestCustomJobProxy::initialize(GURL url, std::string method,
diff --git a/src/core/net/url_request_custom_job_proxy.h b/src/core/net/url_request_custom_job_proxy.h
index d4cd7e208..db38083dd 100644
--- a/src/core/net/url_request_custom_job_proxy.h
+++ b/src/core/net/url_request_custom_job_proxy.h
@@ -42,6 +42,7 @@
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
+#include "base/task_runner.h"
#include "url/gurl.h"
#include "url/origin.h"
#include <QtCore/QPointer>
@@ -60,7 +61,24 @@ class URLRequestCustomJobProxy : public base::RefCountedThreadSafe<URLRequestCus
{
public:
- URLRequestCustomJobProxy(URLRequestCustomJob *job,
+ class Client {
+ public:
+ std::string m_mimeType;
+ std::string m_charset;
+ GURL m_redirect;
+ QIODevice *m_device;
+ int64_t m_firstBytePosition;
+ int m_error;
+ virtual void notifyExpectedContentSize(qint64 size) = 0;
+ virtual void notifyHeadersComplete() = 0;
+ virtual void notifyCanceled() = 0;
+ virtual void notifyAborted() = 0;
+ virtual void notifyStartFailure(int) = 0;
+ virtual void notifyReadyRead() = 0;
+ virtual base::TaskRunner *taskRunner() = 0;
+ };
+
+ URLRequestCustomJobProxy(Client *client,
const std::string &scheme,
QPointer<ProfileAdapter> profileAdapter);
~URLRequestCustomJobProxy();
@@ -76,13 +94,14 @@ public:
void readyRead();
// IO thread owned:
- URLRequestCustomJob *m_job;
+ Client *m_client;
bool m_started;
// UI thread owned:
std::string m_scheme;
URLRequestCustomJobDelegate *m_delegate;
QPointer<ProfileAdapter> m_profileAdapter;
+ scoped_refptr<base::TaskRunner> m_ioTaskRunner;
};
} // namespace QtWebEngineCore
diff --git a/src/core/net/url_request_notification.cpp b/src/core/net/url_request_notification.cpp
deleted file mode 100644
index 279bd5077..000000000
--- a/src/core/net/url_request_notification.cpp
+++ /dev/null
@@ -1,194 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtWebEngine module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "url_request_notification.h"
-
-#include "base/supports_user_data.h"
-#include "base/task/post_task.h"
-#include "content/browser/web_contents/web_contents_impl.h"
-#include "content/public/browser/browser_thread.h"
-#include "net/url_request/url_request.h"
-#include "web_contents_adapter_client.h"
-#include "web_contents_view_qt.h"
-#include "profile_io_data_qt.h"
-#include "qwebengineurlrequestinfo_p.h"
-#include "type_conversion.h"
-#include <QVariant>
-
-namespace QtWebEngineCore {
-
-// Calls cancel() when the URLRequest is destroyed.
-class UserData : public base::SupportsUserData::Data
-{
-public:
- UserData(URLRequestNotification *ptr) : m_ptr(ptr) {}
- ~UserData() { m_ptr->cancel(); }
- static const char key[];
-
-private:
- URLRequestNotification *m_ptr;
-};
-
-const char UserData::key[] = "QtWebEngineCore::URLRequestNotification";
-
-static content::ResourceType fromQt(QWebEngineUrlRequestInfo::ResourceType resourceType)
-{
- return static_cast<content::ResourceType>(resourceType);
-}
-
-URLRequestNotification::URLRequestNotification(net::URLRequest *request, bool isMainFrameRequest, GURL *newUrl,
- QWebEngineUrlRequestInfo &&requestInfo,
- content::ResourceRequestInfo::WebContentsGetter webContentsGetter,
- net::CompletionOnceCallback callback, QPointer<ProfileAdapter> adapter)
- : m_request(request)
- , m_isMainFrameRequest(isMainFrameRequest)
- , m_newUrl(newUrl)
- , m_originalUrl(requestInfo.requestUrl())
- , m_requestInfo(std::move(requestInfo))
- , m_webContentsGetter(webContentsGetter)
- , m_callback(std::move(callback))
- , m_profileAdapter(adapter)
-{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
- m_request->SetUserData(UserData::key, std::make_unique<UserData>(this));
-
- base::PostTaskWithTraits(
- FROM_HERE,
- {content::BrowserThread::UI},
- base::BindOnce(&URLRequestNotification::notify, base::Unretained(this)));
-}
-
-
-void URLRequestNotification::notify()
-{
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
- // May run concurrently with cancel() so no peeking at m_request here.
-
- int result = net::OK;
- content::WebContents *webContents = m_webContentsGetter.Run();
-
- if (webContents) {
-
- if (m_profileAdapter && m_profileAdapter->requestInterceptor()) {
- QWebEngineUrlRequestInterceptor *interceptor = m_profileAdapter->requestInterceptor();
- if (!interceptor->property("deprecated").toBool())
- interceptor->interceptRequest(m_requestInfo);
- }
-
- WebContentsAdapterClient *client =
- WebContentsViewQt::from(static_cast<content::WebContentsImpl*>(webContents)->GetView())->client();
-
- if (!m_requestInfo.changed()) {
- client->interceptRequest(m_requestInfo);
- }
-
- if (m_requestInfo.changed()) {
- result = m_requestInfo.d_ptr->shouldBlockRequest ? net::ERR_BLOCKED_BY_CLIENT : net::OK;
- // We handle the rest of the changes later when we are back in I/O thread
- }
-
- // Only do navigationRequested on MAIN_FRAME and SUB_FRAME resources
- if (result == net::OK && content::IsResourceTypeFrame(fromQt(m_requestInfo.resourceType()))) {
- int navigationRequestAction = WebContentsAdapterClient::AcceptRequest;
- client->navigationRequested(m_requestInfo.navigationType(),
- m_requestInfo.requestUrl(),
- navigationRequestAction,
- m_isMainFrameRequest);
- result = net::ERR_FAILED;
- switch (static_cast<WebContentsAdapterClient::NavigationRequestAction>(navigationRequestAction)) {
- case WebContentsAdapterClient::AcceptRequest:
- result = net::OK;
- break;
- case WebContentsAdapterClient::IgnoreRequest:
- result = net::ERR_ABORTED;
- break;
- }
- DCHECK(result != net::ERR_FAILED);
- }
- }
-
- // Run the callback on the IO thread.
- base::PostTaskWithTraits(
- FROM_HERE,
- {content::BrowserThread::IO},
- base::BindOnce(&URLRequestNotification::complete, base::Unretained(this), result));
-}
-
-void URLRequestNotification::cancel()
-{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
- // May run concurrently with notify() but we only touch m_request here.
-
- m_request = nullptr;
-}
-
-void URLRequestNotification::complete(int error)
-{
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
- if (m_request) {
- if (m_requestInfo.changed()) {
- if (m_originalUrl != m_requestInfo.d_ptr->url)
- *m_newUrl = toGurl(m_requestInfo.d_ptr->url);
-
- if (!m_requestInfo.d_ptr->extraHeaders.isEmpty()) {
- auto end = m_requestInfo.d_ptr->extraHeaders.constEnd();
- for (auto header = m_requestInfo.d_ptr->extraHeaders.constBegin(); header != end; ++header) {
- std::string h = header.key().toStdString();
- if (base::LowerCaseEqualsASCII(h, "referer")) {
- m_request->SetReferrer(header.value().toStdString());
- } else {
- m_request->SetExtraRequestHeaderByName(h, header.value().toStdString(), /* overwrite */ true);
- }
- }
- }
- }
-
- if (m_request->status().status() != net::URLRequestStatus::CANCELED)
- std::move(m_callback).Run(error);
- m_request->RemoveUserData(UserData::key);
- }
-
- delete this;
-}
-
-}
diff --git a/src/core/profile_adapter.cpp b/src/core/profile_adapter.cpp
index bccdf1ada..85577ef52 100644
--- a/src/core/profile_adapter.cpp
+++ b/src/core/profile_adapter.cpp
@@ -47,9 +47,9 @@
#include "api/qwebengineurlscheme.h"
#include "content_browser_client_qt.h"
#include "download_manager_delegate_qt.h"
-#include "net/url_request_context_getter_qt.h"
#include "permission_manager_qt.h"
#include "profile_adapter_client.h"
+#include "profile_io_data_qt.h"
#include "profile_qt.h"
#include "renderer_host/user_resource_controller_host.h"
#include "type_conversion.h"
@@ -129,8 +129,7 @@ void ProfileAdapter::setStorageName(const QString &storageName)
m_name = storageName;
if (!m_offTheRecord) {
m_profile->setupPrefService();
- if (m_profile->m_urlRequestContextGetter.get())
- m_profile->m_profileIOData->updateStorageSettings();
+ m_profile->m_profileIOData->resetNetworkContext();
if (m_visitedLinksManager)
resetVisitedLinksManager();
}
@@ -142,8 +141,7 @@ void ProfileAdapter::setOffTheRecord(bool offTheRecord)
return;
m_offTheRecord = offTheRecord;
m_profile->setupPrefService();
- if (m_profile->m_urlRequestContextGetter.get())
- m_profile->m_profileIOData->updateStorageSettings();
+ m_profile->m_profileIOData->resetNetworkContext();
if (m_visitedLinksManager)
resetVisitedLinksManager();
}
@@ -193,8 +191,7 @@ void ProfileAdapter::setRequestInterceptor(QWebEngineUrlRequestInterceptor *inte
Q_ASSERT(!m_profile->m_profileIOData->requestInterceptor());
});
- if (m_profile->m_urlRequestContextGetter.get())
- m_profile->m_profileIOData->updateRequestInterceptor();
+ m_profile->m_profileIOData->updateRequestInterceptor();
}
void ProfileAdapter::addClient(ProfileAdapterClient *adapterClient)
@@ -210,16 +207,14 @@ void ProfileAdapter::removeClient(ProfileAdapterClient *adapterClient)
void ProfileAdapter::addPageRequestInterceptor()
{
++m_pageRequestInterceptors;
- if (m_profile->m_urlRequestContextGetter.get())
- m_profile->m_profileIOData->updateRequestInterceptor();
+ m_profile->m_profileIOData->updateRequestInterceptor();
}
void ProfileAdapter::removePageRequestInterceptor()
{
Q_ASSERT(m_pageRequestInterceptors > 0);
--m_pageRequestInterceptors;
- if (m_profile->m_urlRequestContextGetter.get())
- m_profile->m_profileIOData->updateRequestInterceptor();
+ m_profile->m_profileIOData->updateRequestInterceptor();
}
@@ -277,8 +272,7 @@ void ProfileAdapter::setDataPath(const QString &path)
m_dataPath = path;
if (!m_offTheRecord) {
m_profile->setupPrefService();
- if (m_profile->m_urlRequestContextGetter.get())
- m_profile->m_profileIOData->updateStorageSettings();
+ m_profile->m_profileIOData->resetNetworkContext();
if (m_visitedLinksManager)
resetVisitedLinksManager();
}
@@ -305,8 +299,8 @@ void ProfileAdapter::setCachePath(const QString &path)
if (m_cachePath == path)
return;
m_cachePath = path;
- if (!m_offTheRecord && m_profile->m_urlRequestContextGetter.get())
- m_profile->m_profileIOData->updateHttpCache();
+ if (!m_offTheRecord)
+ m_profile->m_profileIOData->resetNetworkContext();
}
QString ProfileAdapter::cookiesPath() const
@@ -352,8 +346,7 @@ void ProfileAdapter::setHttpUserAgent(const QString &userAgent)
if (web_contents->GetBrowserContext() == m_profile.data())
web_contents->SetUserAgentOverride(m_httpUserAgent.toStdString(), true);
- if (m_profile->m_urlRequestContextGetter.get())
- m_profile->m_profileIOData->updateUserAgent();
+ m_profile->m_profileIOData->resetNetworkContext();
}
ProfileAdapter::HttpCacheType ProfileAdapter::httpCacheType() const
@@ -371,8 +364,11 @@ void ProfileAdapter::setHttpCacheType(ProfileAdapter::HttpCacheType newhttpCache
m_httpCacheType = newhttpCacheType;
if (oldCacheType == httpCacheType())
return;
- if (!m_offTheRecord && m_profile->m_urlRequestContextGetter.get())
- m_profile->m_profileIOData->updateHttpCache();
+ if (!m_offTheRecord) {
+ m_profile->m_profileIOData->resetNetworkContext();
+ if (m_httpCacheType == NoCache)
+ clearHttpCache();
+ }
}
ProfileAdapter::PersistentCookiesPolicy ProfileAdapter::persistentCookiesPolicy() const
@@ -388,8 +384,8 @@ void ProfileAdapter::setPersistentCookiesPolicy(ProfileAdapter::PersistentCookie
m_persistentCookiesPolicy = newPersistentCookiesPolicy;
if (oldPolicy == persistentCookiesPolicy())
return;
- if (!m_offTheRecord && m_profile->m_urlRequestContextGetter.get())
- m_profile->m_profileIOData->updateCookieStore();
+ if (!m_offTheRecord)
+ m_profile->m_profileIOData->resetNetworkContext();
}
ProfileAdapter::VisitedLinksPolicy ProfileAdapter::visitedLinksPolicy() const
@@ -443,8 +439,8 @@ void ProfileAdapter::setHttpCacheMaxSize(int maxSize)
if (m_httpCacheMaxSize == maxSize)
return;
m_httpCacheMaxSize = maxSize;
- if (!m_offTheRecord && m_profile->m_urlRequestContextGetter.get())
- m_profile->m_profileIOData->updateHttpCache();
+ if (!m_offTheRecord)
+ m_profile->m_profileIOData->resetNetworkContext();
}
enum class SchemeType { Protected, Overridable, Custom, Unknown };
@@ -605,8 +601,7 @@ void ProfileAdapter::setHttpAcceptLanguage(const QString &httpAcceptLanguage)
}
}
- if (m_profile->m_urlRequestContextGetter.get())
- m_profile->m_profileIOData->updateUserAgent();
+ m_profile->m_profileIOData->resetNetworkContext();
}
void ProfileAdapter::clearHttpCache()
@@ -675,6 +670,7 @@ void ProfileAdapter::setUseForGlobalCertificateVerification(bool enable)
if (enable) {
if (profileForglobalCertificateVerification) {
profileForglobalCertificateVerification->m_usedForGlobalCertificateVerification = false;
+ profileForglobalCertificateVerification->m_profile->m_profileIOData->resetNetworkContext();
for (auto *client : qAsConst(profileForglobalCertificateVerification->m_clients))
client->useForGlobalCertificateVerificationChanged();
}
@@ -685,8 +681,7 @@ void ProfileAdapter::setUseForGlobalCertificateVerification(bool enable)
profileForglobalCertificateVerification = nullptr;
}
- if (m_profile->m_urlRequestContextGetter.get())
- m_profile->m_profileIOData->updateUsedForGlobalCertificateVerification();
+ m_profile->m_profileIOData->resetNetworkContext();
}
bool ProfileAdapter::isUsedForGlobalCertificateVerification() const
diff --git a/src/core/profile_io_data_qt.cpp b/src/core/profile_io_data_qt.cpp
index 68be09ad2..00d3e768a 100644
--- a/src/core/profile_io_data_qt.cpp
+++ b/src/core/profile_io_data_qt.cpp
@@ -40,131 +40,37 @@
#include "profile_io_data_qt.h"
#include "base/task/post_task.h"
-#include "components/certificate_transparency/ct_known_logs.h"
-#include "components/network_session_configurator/common/network_features.h"
+#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
+#include "content/browser/storage_partition_impl.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/browsing_data_remover.h"
-#include "content/public/browser/cookie_store_factory.h"
+#include "content/public/browser/shared_cors_origin_access_list.h"
#include "content/public/common/content_features.h"
-#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
#include "chrome/browser/net/chrome_mojo_proxy_resolver_factory.h"
-#include "chrome/common/chrome_switches.h"
#include "components/proxy_config/pref_proxy_config_tracker_impl.h"
-#include "net/cert/cert_verifier.h"
-#include "net/cert/ct_log_verifier.h"
-#include "net/cert/ct_policy_enforcer.h"
-#include "net/cert/multi_log_ct_verifier.h"
-#include "net/cert_net/cert_net_fetcher_impl.h"
-#include "net/ftp/ftp_auth_cache.h"
-#include "net/dns/host_resolver_manager.h"
-#include "net/http/http_auth_handler_factory.h"
-#include "net/http/http_auth_scheme.h"
-#include "net/http/http_auth_preferences.h"
-#include "net/http/http_cache.h"
-#include "net/http/http_server_properties_impl.h"
-#include "net/http/http_network_session.h"
-#include "net/http/transport_security_persister.h"
-#include "net/proxy_resolution/dhcp_pac_file_fetcher_factory.h"
-#include "net/proxy_resolution/pac_file_fetcher_impl.h"
#include "net/proxy_resolution/proxy_config_service.h"
#include "net/proxy_resolution/proxy_resolution_service.h"
#include "net/ssl/ssl_config_service_defaults.h"
-#include "net/url_request/data_protocol_handler.h"
-#include "net/url_request/file_protocol_handler.h"
-#include "net/url_request/ftp_protocol_handler.h"
-#include "net/url_request/static_http_user_agent_settings.h"
-#include "net/url_request/url_request_context_storage.h"
-#include "net/url_request/url_request_job_factory_impl.h"
-#include "net/url_request/url_request_intercepting_job_factory.h"
#include "services/file/user_id_map.h"
#include "services/network/proxy_service_mojo.h"
-#include "services/network/restricted_cookie_manager.h"
+#include "services/network/public/cpp/features.h"
+#include "services/network/public/cpp/cors/origin_access_list.h"
#include "net/client_cert_override.h"
#include "net/client_cert_store_data.h"
#include "net/cookie_monster_delegate_qt.h"
-#include "net/custom_protocol_handler.h"
-#include "net/network_delegate_qt.h"
#include "net/proxy_config_service_qt.h"
-#include "net/restricted_cookie_manager_qt.h"
-#include "net/url_request_context_getter_qt.h"
+#include "net/system_network_context_manager.h"
#include "profile_qt.h"
#include "resource_context_qt.h"
#include "type_conversion.h"
-#if defined(USE_NSS_CERTS)
-#include "net/cert_net/nss_ocsp.h"
-#endif
-
-#if defined(OS_LINUX) || defined(OS_MACOSX)
-#include "net/cert/cert_net_fetcher.h"
-#include "net/cert_net/cert_net_fetcher_impl.h"
-#endif
-
#include <mutex>
+#include <QVariant>
namespace QtWebEngineCore {
-static scoped_refptr<net::CertNetFetcherImpl> s_certNetFetcher;
-
-static bool doNetworkSessionParamsMatch(const net::HttpNetworkSession::Params &first,
- const net::HttpNetworkSession::Params &second)
-{
- if (first.ignore_certificate_errors != second.ignore_certificate_errors)
- return false;
- return true;
-}
-
-static bool doNetworkSessionContextMatch(const net::HttpNetworkSession::Context &first,
- const net::HttpNetworkSession::Context &second)
-{
- if (first.transport_security_state != second.transport_security_state)
- return false;
- if (first.cert_verifier != second.cert_verifier)
- return false;
- if (first.proxy_resolution_service != second.proxy_resolution_service)
- return false;
- if (first.ssl_config_service != second.ssl_config_service)
- return false;
- if (first.http_auth_handler_factory != second.http_auth_handler_factory)
- return false;
- if (first.http_user_agent_settings != second.http_user_agent_settings)
- return false;
- if (first.http_server_properties != second.http_server_properties)
- return false;
- if (first.host_resolver != second.host_resolver)
- return false;
- if (first.cert_transparency_verifier != second.cert_transparency_verifier)
- return false;
- if (first.ct_policy_enforcer != second.ct_policy_enforcer)
- return false;
- return true;
-}
-
-static net::HttpNetworkSession::Context generateNetworkSessionContext(net::URLRequestContext *urlRequestContext)
-{
- net::HttpNetworkSession::Context network_session_context;
- network_session_context.transport_security_state = urlRequestContext->transport_security_state();
- network_session_context.cert_verifier = urlRequestContext->cert_verifier();
- network_session_context.proxy_resolution_service = urlRequestContext->proxy_resolution_service();
- network_session_context.ssl_config_service = urlRequestContext->ssl_config_service();
- network_session_context.http_auth_handler_factory = urlRequestContext->http_auth_handler_factory();
- network_session_context.http_user_agent_settings = urlRequestContext->http_user_agent_settings();
- network_session_context.http_server_properties = urlRequestContext->http_server_properties();
- network_session_context.host_resolver = urlRequestContext->host_resolver();
- network_session_context.cert_transparency_verifier = urlRequestContext->cert_transparency_verifier();
- network_session_context.ct_policy_enforcer = urlRequestContext->ct_policy_enforcer();
- return network_session_context;
-}
-
-static net::HttpNetworkSession::Params generateNetworkSessionParams(bool ignoreCertificateErrors)
-{
- net::HttpNetworkSession::Params network_session_params;
- network_session_params.ignore_certificate_errors = ignoreCertificateErrors;
- return network_session_params;
-}
-
ProfileIODataQt::ProfileIODataQt(ProfileQt *profile)
: m_profile(profile),
#if QT_CONFIG(ssl)
@@ -182,27 +88,7 @@ ProfileIODataQt::~ProfileIODataQt()
if (content::BrowserThread::IsThreadInitialized(content::BrowserThread::IO))
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
- if (m_useForGlobalCertificateVerification) {
-#if defined(USE_NSS_CERTS)
- net::SetURLRequestContextForNSSHttpIO(nullptr);
-#endif
- if (s_certNetFetcher) {
- s_certNetFetcher->Shutdown();
- s_certNetFetcher.reset();
- }
- }
-
- if (m_urlRequestContext) {
- if (m_urlRequestContext->proxy_resolution_service())
- m_urlRequestContext->proxy_resolution_service()->OnShutdown();
- m_restrictedCookieManagerBindings.CloseAllBindings();
- cancelAllUrlRequests();
- }
-
m_resourceContext.reset();
- if (m_cookieDelegate)
- m_cookieDelegate->setCookieMonster(0); // this will let CookieMonsterDelegateQt be deleted
- m_networkDelegate.reset();
delete m_proxyConfigService.fetchAndStoreAcquire(0);
}
@@ -218,6 +104,9 @@ void ProfileIODataQt::shutdownOnUIThread()
delete m_clientCertificateStoreData;
m_clientCertificateStoreData = nullptr;
#endif
+ if (m_cookieDelegate)
+ m_cookieDelegate->unsetMojoCookieManager();
+ m_proxyConfigMonitor.reset();
bool posted = content::BrowserThread::DeleteSoon(content::BrowserThread::IO, FROM_HERE, this);
if (!posted) {
qWarning() << "Could not delete ProfileIODataQt on io thread !";
@@ -230,7 +119,7 @@ net::URLRequestContext *ProfileIODataQt::urlRequestContext()
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!m_initialized)
initializeOnIOThread();
- return m_urlRequestContext.get();
+ return nullptr;
}
content::ResourceContext *ProfileIODataQt::resourceContext()
@@ -248,22 +137,23 @@ extensions::ExtensionSystemQt* ProfileIODataQt::GetExtensionSystem()
base::WeakPtr<ProfileIODataQt> ProfileIODataQt::getWeakPtrOnUIThread()
{
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ DCHECK(m_initialized);
return m_weakPtr;
}
+base::WeakPtr<ProfileIODataQt> ProfileIODataQt::getWeakPtrOnIOThread()
+{
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+ return m_weakPtrFactory.GetWeakPtr();
+}
+
void ProfileIODataQt::initializeOnIOThread()
{
- m_networkDelegate.reset(new NetworkDelegateQt(this));
- m_hostResolver = net::HostResolver::CreateStandaloneResolver(nullptr);
- m_urlRequestContext.reset(new net::URLRequestContext());
- m_urlRequestContext->set_network_delegate(m_networkDelegate.get());
- m_urlRequestContext->set_enable_brotli(true);
- m_urlRequestContext->set_host_resolver(m_hostResolver.get());
// this binds factory to io thread
m_weakPtr = m_weakPtrFactory.GetWeakPtr();
const std::lock_guard<QRecursiveMutex> lock(m_mutex);
generateAllStorage();
- generateJobFactory();
+// generateJobFactory();
setGlobalCertificateVerification();
m_initialized = true;
}
@@ -279,294 +169,22 @@ void ProfileIODataQt::initializeOnUIThread()
m_protocolHandlerRegistryIOThreadDelegate = protocolHandlerRegistry->io_thread_delegate();
m_cookieDelegate = new CookieMonsterDelegateQt();
m_cookieDelegate->setClient(m_profile->profileAdapter()->cookieStore());
- createProxyConfig();
-}
-
-void ProfileIODataQt::cancelAllUrlRequests()
-{
- Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
- Q_ASSERT(m_urlRequestContext);
-
- const std::set<const net::URLRequest*> *url_requests = m_urlRequestContext->url_requests();
- std::set<const net::URLRequest*>::const_iterator it = url_requests->begin();
- std::set<const net::URLRequest*>::const_iterator end = url_requests->end();
- for ( ; it != end; ++it) {
- net::URLRequest* request = const_cast<net::URLRequest*>(*it);
- if (request)
- request->Cancel();
- }
+ if (base::FeatureList::IsEnabled(network::features::kNetworkService))
+ m_proxyConfigMonitor.reset(new ProxyConfigMonitor(m_profile->GetPrefs()));
+ else
+ createProxyConfig();
}
void ProfileIODataQt::generateAllStorage()
{
Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
const std::lock_guard<QRecursiveMutex> lock(m_mutex);
- generateStorage();
- generateCookieStore();
- generateUserAgent();
- generateHttpCache();
m_updateAllStorage = false;
}
-void ProfileIODataQt::generateStorage()
-{
- Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
- Q_ASSERT(m_urlRequestContext);
-// Q_ASSERT(!m_mutex.tryLock()); // assert locked
-
- // We must stop all requests before deleting their backends.
- if (m_storage) {
- m_urlRequestContext->proxy_resolution_service()->OnShutdown();
- m_restrictedCookieManagerBindings.CloseAllBindings();
- m_cookieDelegate->setCookieMonster(nullptr);
- m_storage->set_cookie_store(nullptr);
- cancelAllUrlRequests();
- // we need to get rid of dangling pointer due to coming storage deletion
- m_urlRequestContext->set_http_transaction_factory(nullptr);
- m_httpNetworkSession.reset();
- m_transportSecurityPersister.reset();
- }
-
- m_storage.reset(new net::URLRequestContextStorage(m_urlRequestContext.get()));
-
- net::ProxyConfigService *proxyConfigService = m_proxyConfigService.fetchAndStoreAcquire(0);
- Q_ASSERT(proxyConfigService);
-
- std::unique_ptr<net::CertVerifier> cert_verifier = net::CertVerifier::CreateDefault(s_certNetFetcher);
- net::CertVerifier::Config config;
- // Enable revocation checking:
- config.enable_rev_checking = true;
- // Mirroring Android WebView (we have no beef with Symantec, and our users might use them):
- config.disable_symantec_enforcement = true;
- cert_verifier->SetConfig(config);
-
- m_storage->set_cert_verifier(std::move(cert_verifier));
- std::unique_ptr<net::MultiLogCTVerifier> ct_verifier(new net::MultiLogCTVerifier());
- std::vector<scoped_refptr<const net::CTLogVerifier>> ct_logs;
- for (const auto &ct_log : certificate_transparency::GetKnownLogs()) {
- scoped_refptr<const net::CTLogVerifier> log_verifier =
- net::CTLogVerifier::Create(std::string(ct_log.log_key, ct_log.log_key_length),
- ct_log.log_name);
- if (!log_verifier)
- continue;
- ct_logs.push_back(std::move(log_verifier));
- }
- ct_verifier->AddLogs(ct_logs);
- m_storage->set_cert_transparency_verifier(std::move(ct_verifier));
- m_storage->set_ct_policy_enforcer(base::WrapUnique(new net::DefaultCTPolicyEnforcer()));
- m_storage->set_ssl_config_service(std::make_unique<net::SSLConfigServiceDefaults>());
- if (!m_httpAuthPreferences) {
- m_httpAuthPreferences.reset(new net::HttpAuthPreferences());
- std::string serverWhitelist = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switches::kAuthServerWhitelist);
- m_httpAuthPreferences->SetServerWhitelist(serverWhitelist);
- }
- m_storage->set_http_auth_handler_factory(net::HttpAuthHandlerFactory::CreateDefault(
- m_httpAuthPreferences.get()));
- m_storage->set_transport_security_state(std::make_unique<net::TransportSecurityState>());
-
- if (!m_dataPath.isEmpty()) {
- scoped_refptr<base::SequencedTaskRunner> background_task_runner(
- base::CreateSequencedTaskRunnerWithTraits(
- {base::MayBlock(),
- base::TaskPriority::BEST_EFFORT,
- base::TaskShutdownBehavior::BLOCK_SHUTDOWN}));
- m_transportSecurityPersister =
- std::make_unique<net::TransportSecurityPersister>(
- m_urlRequestContext->transport_security_state(),
- toFilePath(m_dataPath),
- background_task_runner);
- };
-
- m_storage->set_http_server_properties(std::unique_ptr<net::HttpServerProperties>(
- new net::HttpServerPropertiesImpl));
-
- // The System Proxy Resolver has issues on Windows with unconfigured network cards,
- // which is why we want to use the v8 one
- if (!m_dhcpPacFileFetcherFactory)
- m_dhcpPacFileFetcherFactory.reset(new net::DhcpPacFileFetcherFactory);
-
- proxy_resolver::mojom::ProxyResolverFactoryPtr proxyResolver(std::move(m_proxyResolverFactoryInterface));
- m_storage->set_proxy_resolution_service(network::CreateProxyResolutionServiceUsingMojoFactory(
- std::move(proxyResolver),
- std::unique_ptr<net::ProxyConfigService>(proxyConfigService),
- net::PacFileFetcherImpl::CreateWithFileUrlSupport(m_urlRequestContext.get()),
- m_dhcpPacFileFetcherFactory->Create(m_urlRequestContext.get()),
- m_urlRequestContext->host_resolver(),
- nullptr /* NetLog */,
- m_urlRequestContext->network_delegate()));
-
- m_storage->set_ftp_auth_cache(std::make_unique<net::FtpAuthCache>());
-}
-
-
-void ProfileIODataQt::generateCookieStore()
-{
- Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
- Q_ASSERT(m_urlRequestContext);
-
- const std::lock_guard<QRecursiveMutex> lock(m_mutex);
-
- // FIXME: Add code to remove the old channel-id database.
-
- std::unique_ptr<net::CookieStore> cookieStore;
- switch (m_persistentCookiesPolicy) {
- case ProfileAdapter::NoPersistentCookies:
- cookieStore = content::CreateCookieStore(
- content::CookieStoreConfig(
- base::FilePath(),
- false,
- false,
- nullptr),
- nullptr);
- break;
- case ProfileAdapter::AllowPersistentCookies:
- cookieStore = content::CreateCookieStore(
- content::CookieStoreConfig(
- toFilePath(m_cookiesPath),
- false,
- true,
- nullptr),
- nullptr);
- break;
- case ProfileAdapter::ForcePersistentCookies:
- cookieStore = content::CreateCookieStore(
- content::CookieStoreConfig(
- toFilePath(m_cookiesPath),
- true,
- true,
- nullptr),
- nullptr);
- break;
- }
-
- net::CookieMonster * const cookieMonster = static_cast<net::CookieMonster*>(cookieStore.get());
- m_cookieDelegate->setCookieMonster(cookieMonster);
- m_storage->set_cookie_store(std::move(cookieStore));
-
- const std::vector<std::string> cookieableSchemes(kCookieableSchemes,
- kCookieableSchemes + base::size(kCookieableSchemes));
- cookieMonster->SetCookieableSchemes(cookieableSchemes, base::DoNothing());
-}
-
-void ProfileIODataQt::generateUserAgent()
-{
- Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
- Q_ASSERT(m_urlRequestContext);
- Q_ASSERT(m_storage);
-
- const std::lock_guard<QRecursiveMutex> lock(m_mutex);
- m_storage->set_http_user_agent_settings(std::unique_ptr<net::HttpUserAgentSettings>(
- new net::StaticHttpUserAgentSettings(m_httpAcceptLanguage.toStdString(),
- m_httpUserAgent.toStdString())));
-}
-
-void ProfileIODataQt::generateHttpCache()
-{
- Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
- Q_ASSERT(m_urlRequestContext);
- Q_ASSERT(m_storage);
-
- const std::lock_guard<QRecursiveMutex> lock(m_mutex);
-
- net::HttpCache::DefaultBackend* main_backend = 0;
- switch (m_httpCacheType) {
- case ProfileAdapter::MemoryHttpCache:
- main_backend =
- new net::HttpCache::DefaultBackend(
- net::MEMORY_CACHE,
- net::CACHE_BACKEND_DEFAULT,
- base::FilePath(),
- m_httpCacheMaxSize
- );
- break;
- case ProfileAdapter::DiskHttpCache:
- main_backend =
- new net::HttpCache::DefaultBackend(
- net::DISK_CACHE,
- net::CACHE_BACKEND_DEFAULT,
- toFilePath(m_httpCachePath),
- m_httpCacheMaxSize
- );
- break;
- case ProfileAdapter::NoCache:
- // It's safe to not create BackendFactory.
- break;
- }
-
- net::HttpCache *cache = 0;
- net::HttpNetworkSession::Context network_session_context =
- generateNetworkSessionContext(m_urlRequestContext.get());
- net::HttpNetworkSession::Params network_session_params =
- generateNetworkSessionParams(m_ignoreCertificateErrors);
-
- if (!m_httpNetworkSession
- || !doNetworkSessionParamsMatch(network_session_params, m_httpNetworkSession->params())
- || !doNetworkSessionContextMatch(network_session_context, m_httpNetworkSession->context())) {
- m_httpNetworkSession.reset(new net::HttpNetworkSession(network_session_params,
- network_session_context));
- }
-
- cache = new net::HttpCache(m_httpNetworkSession.get(),
- std::unique_ptr<net::HttpCache::DefaultBackend>(main_backend), false);
-
- m_storage->set_http_transaction_factory(std::unique_ptr<net::HttpCache>(cache));
-}
-
-void ProfileIODataQt::generateJobFactory()
-{
- Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
- Q_ASSERT(m_urlRequestContext);
- Q_ASSERT(!m_jobFactory);
-
- const std::lock_guard<QRecursiveMutex> lock(m_mutex);
- m_updateJobFactory = false;
-
- std::unique_ptr<net::URLRequestJobFactoryImpl> jobFactory(new net::URLRequestJobFactoryImpl());
- for (auto &it : m_protocolHandlers)
- jobFactory->SetProtocolHandler(it.first, base::WrapUnique(it.second.release()));
- m_protocolHandlers.clear();
-
- jobFactory->SetProtocolHandler(url::kDataScheme,
- std::unique_ptr<net::URLRequestJobFactory::ProtocolHandler>(
- new net::DataProtocolHandler()));
- scoped_refptr<base::TaskRunner> taskRunner(base::CreateTaskRunnerWithTraits({base::MayBlock(),
- base::TaskPriority::BEST_EFFORT,
- base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}));
- jobFactory->SetProtocolHandler(url::kFileScheme,
- std::make_unique<net::FileProtocolHandler>(taskRunner));
- jobFactory->SetProtocolHandler(url::kFtpScheme,
- net::FtpProtocolHandler::Create(m_urlRequestContext->host_resolver(), m_urlRequestContext->ftp_auth_cache()));
-
- m_installedCustomSchemes = m_customUrlSchemes;
- for (const QByteArray &scheme : qAsConst(m_installedCustomSchemes)) {
- jobFactory->SetProtocolHandler(scheme.toStdString(),
- std::unique_ptr<net::URLRequestJobFactory::ProtocolHandler>(
- new CustomProtocolHandler(m_profileAdapter)));
- }
-
- m_baseJobFactory = jobFactory.get();
-
- // Set up interceptors in the reverse order.
- std::unique_ptr<net::URLRequestJobFactory> topJobFactory = std::move(jobFactory);
- content::URLRequestInterceptorScopedVector::reverse_iterator i;
- for (i = m_requestInterceptors.rbegin(); i != m_requestInterceptors.rend(); ++i) {
- topJobFactory.reset(new net::URLRequestInterceptingJobFactory(std::move(topJobFactory),
- std::move(*i)));
- }
-
- m_requestInterceptors.clear();
-
- m_jobFactory = std::move(topJobFactory);
-
- m_urlRequestContext->set_job_factory(m_jobFactory.get());
-}
-
void ProfileIODataQt::regenerateJobFactory()
{
Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
- Q_ASSERT(m_urlRequestContext);
- Q_ASSERT(m_jobFactory);
- Q_ASSERT(m_baseJobFactory);
const std::lock_guard<QRecursiveMutex> lock(m_mutex);
m_updateJobFactory = false;
@@ -574,31 +192,13 @@ void ProfileIODataQt::regenerateJobFactory()
if (m_customUrlSchemes == m_installedCustomSchemes)
return;
- for (const QByteArray &scheme : qAsConst(m_installedCustomSchemes)) {
- m_baseJobFactory->SetProtocolHandler(scheme.toStdString(), nullptr);
- }
-
m_installedCustomSchemes = m_customUrlSchemes;
- for (const QByteArray &scheme : qAsConst(m_installedCustomSchemes)) {
- m_baseJobFactory->SetProtocolHandler(scheme.toStdString(),
- std::unique_ptr<net::URLRequestJobFactory::ProtocolHandler>(
- new CustomProtocolHandler(m_profileAdapter)));
- }
}
void ProfileIODataQt::setGlobalCertificateVerification()
{
Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
const std::lock_guard<QRecursiveMutex> lock(m_mutex);
- if (m_useForGlobalCertificateVerification) {
-#if defined(USE_NSS_CERTS)
- // Set request context used by NSS for OCSP requests.
- net::SetURLRequestContextForNSSHttpIO(m_urlRequestContext.get());
-#endif
- if (!s_certNetFetcher)
- s_certNetFetcher = base::MakeRefCounted<net::CertNetFetcherImpl>();
- s_certNetFetcher->SetURLRequestContext(m_urlRequestContext.get());
- }
}
void ProfileIODataQt::setRequestContextData(content::ProtocolHandlerMap *protocolHandlers,
@@ -625,12 +225,25 @@ void ProfileIODataQt::setFullConfiguration()
m_dataPath = m_profileAdapter->dataPath();
}
+void ProfileIODataQt::resetNetworkContext()
+{
+ Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ setFullConfiguration();
+ content::BrowserContext::ForEachStoragePartition(
+ m_profile, base::BindRepeating([](content::StoragePartition *storage) {
+ auto storage_impl = static_cast<content::StoragePartitionImpl *>(storage);
+ storage_impl->ResetURLLoaderFactories();
+ storage_impl->ResetNetworkContext();
+ }));
+}
+
void ProfileIODataQt::requestStorageGeneration() {
Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
const std::lock_guard<QRecursiveMutex> lock(m_mutex);
if (m_initialized && !m_updateAllStorage) {
m_updateAllStorage = true;
- createProxyConfig();
+ if (!base::FeatureList::IsEnabled(network::features::kNetworkService))
+ createProxyConfig();
base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
base::BindOnce(&ProfileIODataQt::generateAllStorage, m_weakPtr));
}
@@ -639,20 +252,17 @@ void ProfileIODataQt::requestStorageGeneration() {
// TODO(miklocek): mojofy ProxyConfigServiceQt
void ProfileIODataQt::createProxyConfig()
{
+ Q_ASSERT(!base::FeatureList::IsEnabled(network::features::kNetworkService));
Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
const std::lock_guard<QRecursiveMutex> lock(m_mutex);
// We must create the proxy config service on the UI loop on Linux because it
// must synchronously run on the glib message loop. This will be passed to
// the URLRequestContextStorage on the IO thread in GetURLRequestContext().
Q_ASSERT(m_proxyConfigService == 0);
- net::ProxyConfigWithAnnotation initialConfig;
- ProxyPrefs::ConfigState initialConfigState = PrefProxyConfigTrackerImpl::ReadPrefConfig(
- m_profileAdapter->profile()->GetPrefs(), &initialConfig);
m_proxyConfigService =
new ProxyConfigServiceQt(
- net::ProxyResolutionService::CreateSystemProxyConfigService(
- base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO})),
- initialConfig, initialConfigState);
+ m_profileAdapter->profile()->GetPrefs(),
+ base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO}));
//pass interface to io thread
m_proxyResolverFactoryInterface = ChromeMojoProxyResolverFactory::CreateWithSelfOwnedReceiver();
}
@@ -751,6 +361,7 @@ bool ProfileIODataQt::isInterceptorDeprecated() const
QWebEngineUrlRequestInterceptor *ProfileIODataQt::acquireInterceptor()
{
+ Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
m_mutex.lock();
return m_requestInterceptor;
}
@@ -769,6 +380,7 @@ bool ProfileIODataQt::hasPageInterceptors()
void ProfileIODataQt::releaseInterceptor()
{
+ Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
m_mutex.unlock();
}
@@ -811,21 +423,56 @@ std::unique_ptr<net::ClientCertStore> ProfileIODataQt::CreateClientCertStore()
#endif
}
-void ProfileIODataQt::CreateRestrictedCookieManager(network::mojom::RestrictedCookieManagerRequest request,
- network::mojom::RestrictedCookieManagerRole role,
- const url::Origin &origin,
- bool is_service_worker,
- int32_t process_id,
- int32_t routing_id)
+network::mojom::NetworkContextParamsPtr ProfileIODataQt::CreateNetworkContextParams()
{
- Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
- m_restrictedCookieManagerBindings.AddBinding(
- std::make_unique<RestrictedCookieManagerQt>(
- m_weakPtr,
- role, urlRequestContext()->cookie_store(),
- &m_cookieSettings, origin,
- is_service_worker, process_id, routing_id),
- std::move(request));
+ updateStorageSettings();
+
+ network::mojom::NetworkContextParamsPtr network_context_params =
+ SystemNetworkContextManager::GetInstance()->CreateDefaultNetworkContextParams();
+
+ network_context_params->context_name = m_profile->profileAdapter()->storageName().toStdString();
+ network_context_params->accept_language = m_httpAcceptLanguage.toStdString();
+
+ network_context_params->enable_referrers = true;
+ network_context_params->enable_encrypted_cookies = false; // ???
+// network_context_params->proxy_resolver_factory = std::move(m_proxyResolverFactoryInterface);
+
+ network_context_params->http_cache_enabled = m_httpCacheType != ProfileAdapter::NoCache;
+ network_context_params->http_cache_max_size = m_httpCacheMaxSize;
+ if (m_httpCacheType == ProfileAdapter::DiskHttpCache)
+ network_context_params->http_cache_path = toFilePath(m_httpCachePath);
+
+ if (m_persistentCookiesPolicy != ProfileAdapter::NoPersistentCookies) {
+ base::FilePath cookie_path = toFilePath(m_dataPath);
+ cookie_path = cookie_path.AppendASCII("Cookies");
+ network_context_params->cookie_path = cookie_path;
+
+ network_context_params->restore_old_session_cookies = false;
+ network_context_params->persist_session_cookies = m_persistentCookiesPolicy == ProfileAdapter::ForcePersistentCookies;
+ }
+ if (!m_dataPath.isEmpty()) {
+ network_context_params->http_server_properties_path = toFilePath(m_dataPath).AppendASCII("Network Persistent State");
+ network_context_params->transport_security_persister_path = toFilePath(m_dataPath);
+ }
+
+#if !BUILDFLAG(DISABLE_FTP_SUPPORT)
+ network_context_params->enable_ftp_url_support = true;
+#endif // !BUILDFLAG(DISABLE_FTP_SUPPORT)
+
+// network_context_params->enable_certificate_reporting = true;
+// network_context_params->enable_expect_ct_reporting = true;
+ network_context_params->enforce_chrome_ct_policy = false;
+ network_context_params->primary_network_context = m_useForGlobalCertificateVerification;
+
+ if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
+ // Should be initialized with existing per-profile CORS access lists.
+ network_context_params->cors_origin_access_list =
+ m_profile->GetSharedCorsOriginAccessList()->GetOriginAccessList().CreateCorsOriginAccessPatternsList();
+ }
+
+ m_proxyConfigMonitor->AddToNetworkContextParams(network_context_params.get());
+
+ return network_context_params;
}
// static
diff --git a/src/core/profile_io_data_qt.h b/src/core/profile_io_data_qt.h
index ec0a3dac9..a1334f412 100644
--- a/src/core/profile_io_data_qt.h
+++ b/src/core/profile_io_data_qt.h
@@ -42,13 +42,12 @@
#include "profile_adapter.h"
#include "content/public/browser/browsing_data_remover.h"
-#include "content/public/common/url_loader_throttle.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/custom_handlers/protocol_handler_registry.h"
#include "extensions/buildflags/buildflags.h"
-#include "mojo/public/cpp/bindings/strong_binding_set.h"
+#include "net/proxy_config_monitor.h"
#include "services/network/cookie_settings.h"
-#include "services/network/public/mojom/restricted_cookie_manager.mojom.h"
+#include "services/network/public/mojom/network_context.mojom.h"
#include "services/proxy_resolver/public/mojom/proxy_resolver.mojom.h"
#include <QtCore/QString>
@@ -57,16 +56,7 @@
namespace net {
class ClientCertStore;
-class DhcpPacFileFetcherFactory;
-class HttpAuthPreferences;
-class HttpNetworkSession;
-class HostResolver;
-class NetworkDelegate;
class ProxyConfigService;
-class URLRequestContext;
-class URLRequestContextStorage;
-class URLRequestJobFactoryImpl;
-class TransportSecurityPersister;
}
namespace extensions {
@@ -119,11 +109,6 @@ public:
void cancelAllUrlRequests();
void generateAllStorage();
- void generateStorage();
- void generateCookieStore();
- void generateHttpCache();
- void generateUserAgent();
- void generateJobFactory();
void regenerateJobFactory();
bool canSetCookie(const QUrl &firstPartyUrl, const QByteArray &cookieLine, const QUrl &url) const;
bool canGetCookies(const QUrl &firstPartyUrl, const QUrl &url) const;
@@ -138,6 +123,7 @@ public:
void setRequestContextData(content::ProtocolHandlerMap *protocolHandlers,
content::URLRequestInterceptorScopedVector request_interceptors);
void setFullConfiguration(); // runs on ui thread
+ void resetNetworkContext(); // runs on ui thread
void updateStorageSettings(); // runs on ui thread
void updateUserAgent(); // runs on ui thread
void updateCookieStore(); // runs on ui thread
@@ -149,12 +135,7 @@ public:
void updateUsedForGlobalCertificateVerification(); // runs on ui thread
bool hasPageInterceptors();
- void CreateRestrictedCookieManager(network::mojom::RestrictedCookieManagerRequest request,
- network::mojom::RestrictedCookieManagerRole role,
- const url::Origin &origin,
- bool is_service_worker,
- int32_t process_id,
- int32_t routing_id);
+ network::mojom::NetworkContextParamsPtr CreateNetworkContextParams();
#if QT_CONFIG(ssl)
ClientCertificateStoreData *clientCertificateStoreData();
@@ -163,34 +144,27 @@ public:
static ProfileIODataQt *FromBrowserContext(content::BrowserContext *browser_context);
static ProfileIODataQt *FromResourceContext(content::ResourceContext *resource_context);
+ base::WeakPtr<ProfileIODataQt> getWeakPtrOnIOThread();
base::WeakPtr<ProfileIODataQt> getWeakPtrOnUIThread();
+ CookieMonsterDelegateQt *cookieDelegate() const { return m_cookieDelegate.get(); }
+
private:
void removeBrowsingDataRemoverObserver();
ProfileQt *m_profile;
- std::unique_ptr<net::URLRequestContextStorage> m_storage;
- std::unique_ptr<net::NetworkDelegate> m_networkDelegate;
std::unique_ptr<content::ResourceContext> m_resourceContext;
- std::unique_ptr<net::URLRequestContext> m_urlRequestContext;
- std::unique_ptr<net::HttpNetworkSession> m_httpNetworkSession;
scoped_refptr<ProtocolHandlerRegistry::IOThreadDelegate>
m_protocolHandlerRegistryIOThreadDelegate;
- std::unique_ptr<net::DhcpPacFileFetcherFactory> m_dhcpPacFileFetcherFactory;
- std::unique_ptr<net::HttpAuthPreferences> m_httpAuthPreferences;
- std::unique_ptr<net::URLRequestJobFactory> m_jobFactory;
- std::unique_ptr<net::TransportSecurityPersister> m_transportSecurityPersister;
- std::unique_ptr<net::HostResolver> m_hostResolver;
base::WeakPtr<ProfileIODataQt> m_weakPtr;
scoped_refptr<CookieMonsterDelegateQt> m_cookieDelegate;
content::URLRequestInterceptorScopedVector m_requestInterceptors;
content::ProtocolHandlerMap m_protocolHandlers;
mojo::InterfacePtrInfo<proxy_resolver::mojom::ProxyResolverFactory> m_proxyResolverFactoryInterface;
- net::URLRequestJobFactoryImpl *m_baseJobFactory = nullptr;
QAtomicPointer<net::ProxyConfigService> m_proxyConfigService;
QPointer<ProfileAdapter> m_profileAdapter; // never dereferenced in IO thread and it is passed by qpointer
ProfileAdapter::PersistentCookiesPolicy m_persistentCookiesPolicy;
- mojo::StrongBindingSet<network::mojom::RestrictedCookieManager> m_restrictedCookieManagerBindings;
+ std::unique_ptr<ProxyConfigMonitor> m_proxyConfigMonitor;
#if QT_CONFIG(ssl)
ClientCertificateStoreData *m_clientCertificateStoreData;
diff --git a/src/core/profile_qt.cpp b/src/core/profile_qt.cpp
index be55e7c49..4d4f588fa 100644
--- a/src/core/profile_qt.cpp
+++ b/src/core/profile_qt.cpp
@@ -44,7 +44,6 @@
#include "command_line_pref_store_qt.h"
#include "download_manager_delegate_qt.h"
#include "net/ssl_host_state_delegate_qt.h"
-#include "net/url_request_context_getter_qt.h"
#include "permission_manager_qt.h"
#include "platform_notification_service_qt.h"
#include "qtwebenginecoreglobal_p.h"
@@ -225,19 +224,7 @@ net::URLRequestContextGetter *ProfileQt::CreateRequestContext(
content::ProtocolHandlerMap *protocol_handlers,
content::URLRequestInterceptorScopedVector request_interceptors)
{
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- DCHECK(!m_urlRequestContextGetter.get());
-#if BUILDFLAG(ENABLE_EXTENSIONS)
- extensions::InfoMap *extension_info_map = GetExtensionSystem()->info_map();
- (*protocol_handlers)[extensions::kExtensionScheme] =
- extensions::CreateExtensionProtocolHandler(IsOffTheRecord(), extension_info_map);
-#endif
-
- m_profileIOData->setRequestContextData(protocol_handlers, std::move(request_interceptors));
- m_profileIOData->updateStorageSettings();
- m_profileIOData->updateRequestInterceptor();
- m_urlRequestContextGetter = new URLRequestContextGetterQt(m_profileIOData.get());
- return m_urlRequestContextGetter.get();
+ return nullptr;
}
content::ClientHintsControllerDelegate *ProfileQt::GetClientHintsControllerDelegate()
diff --git a/src/core/qtwebengine.gni b/src/core/qtwebengine.gni
index 9106e4d56..f17f28495 100644
--- a/src/core/qtwebengine.gni
+++ b/src/core/qtwebengine.gni
@@ -20,6 +20,7 @@ deps = [
"//components/cdm/renderer",
"//components/error_page/common",
"//components/keyed_service/content",
+ "//components/navigation_interception",
"//components/network_hints/browser",
"//components/network_hints/common",
"//components/network_hints/renderer",
diff --git a/src/core/renderer/content_renderer_client_qt.cpp b/src/core/renderer/content_renderer_client_qt.cpp
index b9b199087..0201e5de4 100644
--- a/src/core/renderer/content_renderer_client_qt.cpp
+++ b/src/core/renderer/content_renderer_client_qt.cpp
@@ -113,8 +113,7 @@ namespace QtWebEngineCore {
static const char kHttpErrorDomain[] = "http";
-ContentRendererClientQt::ContentRendererClientQt()
- : m_serviceBinding(this)
+ContentRendererClientQt::ContentRendererClientQt() : m_serviceBinding(this)
{
#if BUILDFLAG(ENABLE_EXTENSIONS)
extensions::ExtensionsClient::Set(extensions::ExtensionsClientQt::GetInstance());
@@ -122,9 +121,7 @@ ContentRendererClientQt::ContentRendererClientQt()
#endif
}
-ContentRendererClientQt::~ContentRendererClientQt()
-{
-}
+ContentRendererClientQt::~ContentRendererClientQt() {}
void ContentRendererClientQt::RenderThreadStarted()
{
@@ -137,10 +134,9 @@ void ContentRendererClientQt::RenderThreadStarted()
m_prescientNetworkingDispatcher.reset(new network_hints::PrescientNetworkingDispatcher());
auto registry = std::make_unique<service_manager::BinderRegistry>();
- registry->AddInterface(m_visitedLinkSlave->GetBindCallback(),
- base::ThreadTaskRunnerHandle::Get());
+ registry->AddInterface(m_visitedLinkSlave->GetBindCallback(), base::ThreadTaskRunnerHandle::Get());
content::ChildThread::Get()->GetServiceManagerConnection()->AddConnectionFilter(
- std::make_unique<content::SimpleConnectionFilter>(std::move(registry)));
+ std::make_unique<content::SimpleConnectionFilter>(std::move(registry)));
renderThread->AddObserver(m_renderThreadObserver.data());
renderThread->AddObserver(UserResourceController::instance());
@@ -153,32 +149,32 @@ void ContentRendererClientQt::RenderThreadStarted()
// Allow XMLHttpRequests from qrc to file.
blink::WebURL qrc(blink::KURL("qrc:"));
blink::WebString file(blink::WebString::FromASCII("file"));
- blink::WebSecurityPolicy::AddOriginAccessAllowListEntry(qrc, file, blink::WebString(), 0,
- network::mojom::CorsDomainMatchMode::kAllowSubdomains,
- network::mojom::CorsPortMatchMode::kAllowAnyPort,
- network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
+ blink::WebSecurityPolicy::AddOriginAccessAllowListEntry(
+ qrc, file, blink::WebString(), 0, network::mojom::CorsDomainMatchMode::kAllowSubdomains,
+ network::mojom::CorsPortMatchMode::kAllowAnyPort,
+ network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
#if BUILDFLAG(ENABLE_EXTENSIONS)
// Allow the pdf viewer extension to access chrome resources
blink::WebURL pdfViewerExtension(blink::KURL("chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai"));
blink::WebString chromeResources(blink::WebString::FromASCII("chrome"));
- blink::WebSecurityPolicy::AddOriginAccessAllowListEntry(pdfViewerExtension, chromeResources, blink::WebString(), 0,
- network::mojom::CorsDomainMatchMode::kAllowSubdomains,
- network::mojom::CorsPortMatchMode::kAllowAnyPort,
- network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
+ blink::WebSecurityPolicy::AddOriginAccessAllowListEntry(
+ pdfViewerExtension, chromeResources, blink::WebString(), 0,
+ network::mojom::CorsDomainMatchMode::kAllowSubdomains, network::mojom::CorsPortMatchMode::kAllowAnyPort,
+ network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
ExtensionsRendererClientQt::GetInstance()->RenderThreadStarted();
#endif
}
-void ContentRendererClientQt::RenderViewCreated(content::RenderView* render_view)
+void ContentRendererClientQt::RenderViewCreated(content::RenderView *render_view)
{
// RenderViewObservers destroy themselves with their RenderView.
new RenderViewObserverQt(render_view);
UserResourceController::instance()->renderViewCreated(render_view);
}
-void ContentRendererClientQt::RenderFrameCreated(content::RenderFrame* render_frame)
+void ContentRendererClientQt::RenderFrameCreated(content::RenderFrame *render_frame)
{
QtWebEngineCore::RenderFrameObserverQt *render_frame_observer =
new QtWebEngineCore::RenderFrameObserverQt(render_frame, m_webCacheImpl.data());
@@ -195,9 +191,7 @@ void ContentRendererClientQt::RenderFrameCreated(content::RenderFrame* render_fr
new SpellCheckProvider(render_frame, m_spellCheck.data(), this);
#endif
#if QT_CONFIG(webengine_printing_and_pdf)
- new printing::PrintRenderFrameHelper(
- render_frame,
- base::WrapUnique(new PrintWebViewHelperDelegateQt()));
+ new printing::PrintRenderFrameHelper(render_frame, base::WrapUnique(new PrintWebViewHelperDelegateQt()));
#endif // QT_CONFIG(webengine_printing_and_pdf)
#if BUILDFLAG(ENABLE_EXTENSIONS)
auto registry = std::make_unique<service_manager::BinderRegistry>();
@@ -259,9 +253,9 @@ void ContentRendererClientQt::PrepareErrorPage(content::RenderFrame *renderFrame
std::string *errorHtml)
{
Q_UNUSED(ignoring_cache);
- GetNavigationErrorStringsInternal(renderFrame, httpMethod,
- error_page::Error::NetError(web_error.url(), web_error.reason(), web_error.has_copy_in_cache()),
- errorHtml);
+ GetNavigationErrorStringsInternal(
+ renderFrame, httpMethod,
+ error_page::Error::NetError(web_error.url(), web_error.reason(), web_error.has_copy_in_cache()), errorHtml);
}
void ContentRendererClientQt::PrepareErrorPageForHttpStatusError(content::RenderFrame *renderFrame,
@@ -277,7 +271,10 @@ void ContentRendererClientQt::PrepareErrorPageForHttpStatusError(content::Render
errorHtml);
}
-void ContentRendererClientQt::GetNavigationErrorStringsInternal(content::RenderFrame *renderFrame, const std::string &httpMethod, const error_page::Error &error, std::string *errorHtml)
+void ContentRendererClientQt::GetNavigationErrorStringsInternal(content::RenderFrame *renderFrame,
+ const std::string &httpMethod,
+ const error_page::Error &error,
+ std::string *errorHtml)
{
Q_UNUSED(renderFrame)
const bool isPost = QByteArray::fromStdString(httpMethod) == QByteArrayLiteral("POST");
@@ -322,9 +319,9 @@ blink::WebPrescientNetworking *ContentRendererClientQt::GetPrescientNetworking()
return m_prescientNetworkingDispatcher.get();
}
-bool ContentRendererClientQt::OverrideCreatePlugin(
- content::RenderFrame* render_frame,
- const blink::WebPluginParams& params, blink::WebPlugin** plugin)
+bool ContentRendererClientQt::OverrideCreatePlugin(content::RenderFrame *render_frame,
+ const blink::WebPluginParams &params,
+ blink::WebPlugin **plugin)
{
#if BUILDFLAG(ENABLE_EXTENSIONS)
if (!ExtensionsRendererClientQt::GetInstance()->OverrideCreatePlugin(render_frame, params))
@@ -333,20 +330,21 @@ bool ContentRendererClientQt::OverrideCreatePlugin(
return content::ContentRendererClient::OverrideCreatePlugin(render_frame, params, plugin);
}
-content::BrowserPluginDelegate* ContentRendererClientQt::CreateBrowserPluginDelegate(content::RenderFrame *render_frame,
+content::BrowserPluginDelegate *ContentRendererClientQt::CreateBrowserPluginDelegate(content::RenderFrame *render_frame,
const content::WebPluginInfo &info,
const std::string &mime_type,
const GURL &original_url)
{
#if BUILDFLAG(ENABLE_EXTENSIONS)
- return ExtensionsRendererClientQt::GetInstance()->CreateBrowserPluginDelegate(render_frame, info, mime_type, original_url);
+ return ExtensionsRendererClientQt::GetInstance()->CreateBrowserPluginDelegate(render_frame, info, mime_type,
+ original_url);
#else
return nullptr;
#endif
}
void ContentRendererClientQt::OnBindInterface(const service_manager::BindSourceInfo &remote_info,
- const std::string& name,
+ const std::string &name,
mojo::ScopedMessagePipeHandle handle)
{
Q_UNUSED(remote_info);
@@ -355,9 +353,8 @@ void ContentRendererClientQt::OnBindInterface(const service_manager::BindSourceI
void ContentRendererClientQt::GetInterface(const std::string &interface_name, mojo::ScopedMessagePipeHandle interface_pipe)
{
- m_serviceBinding.GetConnector()->BindInterface(
- service_manager::ServiceFilter::ByName("qtwebengine"),
- interface_name, std::move(interface_pipe));
+ m_serviceBinding.GetConnector()->BindInterface(service_manager::ServiceFilter::ByName("qtwebengine"),
+ interface_name, std::move(interface_pipe));
}
// The following is based on chrome/renderer/media/chrome_key_systems.cc:
@@ -367,7 +364,7 @@ void ContentRendererClientQt::GetInterface(const std::string &interface_name, mo
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
// External Clear Key (used for testing).
-static void AddExternalClearKey(std::vector<std::unique_ptr<media::KeySystemProperties>>* concrete_key_systems)
+static void AddExternalClearKey(std::vector<std::unique_ptr<media::KeySystemProperties>> *concrete_key_systems)
{
// TODO(xhwang): Move these into an array so we can use a for loop to add
// supported key systems below.
@@ -403,57 +400,58 @@ static void AddExternalClearKey(std::vector<std::unique_ptr<media::KeySystemProp
}
concrete_key_systems->emplace_back(
- new cdm::ExternalClearKeyProperties(kExternalClearKeyKeySystem));
+ new cdm::ExternalClearKeyProperties(kExternalClearKeyKeySystem));
// Add support of decrypt-only mode in ClearKeyCdm.
- concrete_key_systems->emplace_back(new cdm::ExternalClearKeyProperties(
- kExternalClearKeyDecryptOnlyKeySystem));
+ concrete_key_systems->emplace_back(
+ new cdm::ExternalClearKeyProperties(kExternalClearKeyDecryptOnlyKeySystem));
// A key system that triggers various types of messages in ClearKeyCdm.
- concrete_key_systems->emplace_back(new cdm::ExternalClearKeyProperties(
- kExternalClearKeyMessageTypeTestKeySystem));
+ concrete_key_systems->emplace_back(
+ new cdm::ExternalClearKeyProperties(kExternalClearKeyMessageTypeTestKeySystem));
// A key system that triggers the FileIO test in ClearKeyCdm.
- concrete_key_systems->emplace_back(new cdm::ExternalClearKeyProperties(
- kExternalClearKeyFileIOTestKeySystem));
+ concrete_key_systems->emplace_back(
+ new cdm::ExternalClearKeyProperties(kExternalClearKeyFileIOTestKeySystem));
// A key system that triggers the output protection test in ClearKeyCdm.
- concrete_key_systems->emplace_back(new cdm::ExternalClearKeyProperties(
- kExternalClearKeyOutputProtectionTestKeySystem));
+ concrete_key_systems->emplace_back(
+ new cdm::ExternalClearKeyProperties(kExternalClearKeyOutputProtectionTestKeySystem));
// A key system that triggers the platform verification test in ClearKeyCdm.
- concrete_key_systems->emplace_back(new cdm::ExternalClearKeyProperties(
- kExternalClearKeyPlatformVerificationTestKeySystem));
+ concrete_key_systems->emplace_back(
+ new cdm::ExternalClearKeyProperties(kExternalClearKeyPlatformVerificationTestKeySystem));
// A key system that Chrome thinks is supported by ClearKeyCdm, but actually
// will be refused by ClearKeyCdm. This is to test the CDM initialization
// failure case.
- concrete_key_systems->emplace_back(new cdm::ExternalClearKeyProperties(
- kExternalClearKeyInitializeFailKeySystem));
+ concrete_key_systems->emplace_back(
+ new cdm::ExternalClearKeyProperties(kExternalClearKeyInitializeFailKeySystem));
// A key system that triggers a crash in ClearKeyCdm.
- concrete_key_systems->emplace_back(new cdm::ExternalClearKeyProperties(
- kExternalClearKeyCrashKeySystem));
+ concrete_key_systems->emplace_back(
+ new cdm::ExternalClearKeyProperties(kExternalClearKeyCrashKeySystem));
// A key system that triggers the verify host files test in ClearKeyCdm.
- concrete_key_systems->emplace_back(new cdm::ExternalClearKeyProperties(
- kExternalClearKeyVerifyCdmHostTestKeySystem));
+ concrete_key_systems->emplace_back(
+ new cdm::ExternalClearKeyProperties(kExternalClearKeyVerifyCdmHostTestKeySystem));
// A key system that fetches the Storage ID in ClearKeyCdm.
- concrete_key_systems->emplace_back(new cdm::ExternalClearKeyProperties(
- kExternalClearKeyStorageIdTestKeySystem));
+ concrete_key_systems->emplace_back(
+ new cdm::ExternalClearKeyProperties(kExternalClearKeyStorageIdTestKeySystem));
// A key system that is registered with a different CDM GUID.
- concrete_key_systems->emplace_back(new cdm::ExternalClearKeyProperties(
- kExternalClearKeyDifferentGuidTestKeySystem));
+ concrete_key_systems->emplace_back(
+ new cdm::ExternalClearKeyProperties(kExternalClearKeyDifferentGuidTestKeySystem));
// A key system that triggers CDM Proxy test in ClearKeyCdm.
- concrete_key_systems->emplace_back(new cdm::ExternalClearKeyProperties(
- kExternalClearKeyCdmProxyTestKeySystem));
+ concrete_key_systems->emplace_back(
+ new cdm::ExternalClearKeyProperties(kExternalClearKeyCdmProxyTestKeySystem));
}
#if BUILDFLAG(ENABLE_WIDEVINE)
-static media::SupportedCodecs GetSupportedCodecs(const std::vector<media::VideoCodec> &supported_video_codecs, bool is_secure)
+static media::SupportedCodecs GetSupportedCodecs(const std::vector<media::VideoCodec> &supported_video_codecs,
+ bool is_secure)
{
media::SupportedCodecs supported_codecs = media::EME_CODEC_NONE;
@@ -469,7 +467,7 @@ static media::SupportedCodecs GetSupportedCodecs(const std::vector<media::VideoC
supported_codecs |= media::EME_CODEC_FLAC;
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
supported_codecs |= media::EME_CODEC_AAC;
-#endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
+#endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
}
// Video codecs are determined by what was registered for the CDM.
@@ -486,7 +484,7 @@ static media::SupportedCodecs GetSupportedCodecs(const std::vector<media::VideoC
case media::VideoCodec::kCodecH264:
supported_codecs |= media::EME_CODEC_AVC1;
break;
-#endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
+#endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
default:
DVLOG(1) << "Unexpected supported codec: " << GetCodecName(codec);
break;
@@ -505,13 +503,11 @@ static void AddWidevine(std::vector<std::unique_ptr<media::KeySystemProperties>>
}
// Codecs and encryption schemes.
- auto codecs =
- GetSupportedCodecs(capability->video_codecs, /*is_secure=*/false);
- const auto& encryption_schemes = capability->encryption_schemes;
+ auto codecs = GetSupportedCodecs(capability->video_codecs, /*is_secure=*/false);
+ const auto &encryption_schemes = capability->encryption_schemes;
auto hw_secure_codecs = GetSupportedCodecs(capability->hw_secure_video_codecs,
/*is_secure=*/true);
- const auto& hw_secure_encryption_schemes =
- capability->hw_secure_encryption_schemes;
+ const auto &hw_secure_encryption_schemes = capability->hw_secure_encryption_schemes;
// Robustness.
using Robustness = cdm::WidevineKeySystemProperties::Robustness;
@@ -574,11 +570,13 @@ void ContentRendererClientQt::WillSendRequest(blink::WebLocalFrame *frame,
bool *attach_same_site_cookies)
{
#if BUILDFLAG(ENABLE_EXTENSIONS)
- ExtensionsRendererClientQt::GetInstance()->WillSendRequest(frame, transition_type, url, initiator_origin, new_url, attach_same_site_cookies);
+ ExtensionsRendererClientQt::GetInstance()->WillSendRequest(frame, transition_type, url, initiator_origin, new_url,
+ attach_same_site_cookies);
if (!new_url->is_empty())
return;
#endif
- content::ContentRendererClient::WillSendRequest(frame, transition_type, url, initiator_origin, new_url, attach_same_site_cookies);
+ content::ContentRendererClient::WillSendRequest(frame, transition_type, url, initiator_origin, new_url,
+ attach_same_site_cookies);
}
void ContentRendererClientQt::CreateRendererService(service_manager::mojom::ServiceRequest service_request)
@@ -587,7 +585,7 @@ void ContentRendererClientQt::CreateRendererService(service_manager::mojom::Serv
m_serviceBinding.Bind(std::move(service_request));
}
-service_manager::Connector* ContentRendererClientQt::GetConnector()
+service_manager::Connector *ContentRendererClientQt::GetConnector()
{
return m_serviceBinding.GetConnector();
}
diff --git a/src/core/renderer/content_renderer_client_qt.h b/src/core/renderer/content_renderer_client_qt.h
index a13d16b5c..4b394bd3e 100644
--- a/src/core/renderer/content_renderer_client_qt.h
+++ b/src/core/renderer/content_renderer_client_qt.h
@@ -74,9 +74,10 @@ namespace QtWebEngineCore {
class RenderThreadObserverQt;
-class ContentRendererClientQt : public content::ContentRendererClient
- , public service_manager::Service
- , public service_manager::LocalInterfaceProvider
+class ContentRendererClientQt
+ : public content::ContentRendererClient
+ , public service_manager::Service
+ , public service_manager::LocalInterfaceProvider
{
public:
ContentRendererClientQt();
@@ -85,7 +86,7 @@ public:
// content::ContentRendererClient:
void RenderThreadStarted() override;
void RenderViewCreated(content::RenderView *render_view) override;
- void RenderFrameCreated(content::RenderFrame* render_frame) override;
+ void RenderFrameCreated(content::RenderFrame *render_frame) override;
bool ShouldSuppressErrorPage(content::RenderFrame *, const GURL &) override;
bool HasErrorPage(int http_status_code) override;
@@ -103,16 +104,19 @@ public:
uint64_t VisitedLinkHash(const char *canonical_url, size_t length) override;
bool IsLinkVisited(uint64_t linkHash) override;
- blink::WebPrescientNetworking* GetPrescientNetworking() override;
- void AddSupportedKeySystems(std::vector<std::unique_ptr<media::KeySystemProperties>>* key_systems) override;
+ blink::WebPrescientNetworking *GetPrescientNetworking() override;
+ void AddSupportedKeySystems(std::vector<std::unique_ptr<media::KeySystemProperties>> *key_systems) override;
void RunScriptsAtDocumentStart(content::RenderFrame *render_frame) override;
void RunScriptsAtDocumentEnd(content::RenderFrame *render_frame) override;
void RunScriptsAtDocumentIdle(content::RenderFrame *render_frame) override;
- bool OverrideCreatePlugin(content::RenderFrame* render_frame,
- const blink::WebPluginParams& params, blink::WebPlugin** plugin) override;
- content::BrowserPluginDelegate* CreateBrowserPluginDelegate(content::RenderFrame* render_frame,
- const content::WebPluginInfo& info, const std::string& mime_type, const GURL& original_url) override;
+ bool OverrideCreatePlugin(content::RenderFrame *render_frame,
+ const blink::WebPluginParams &params,
+ blink::WebPlugin **plugin) override;
+ content::BrowserPluginDelegate *CreateBrowserPluginDelegate(content::RenderFrame *render_frame,
+ const content::WebPluginInfo &info,
+ const std::string &mime_type,
+ const GURL &original_url) override;
void WillSendRequest(blink::WebLocalFrame *frame,
ui::PageTransition transition_type,
@@ -135,10 +139,10 @@ private:
mojo::ScopedMessagePipeHandle handle) override;
// service_manager::LocalInterfaceProvider:
- void GetInterface(const std::string& name, mojo::ScopedMessagePipeHandle request_handle) override;
+ void GetInterface(const std::string &name, mojo::ScopedMessagePipeHandle request_handle) override;
- void GetNavigationErrorStringsInternal(content::RenderFrame* renderFrame, const std::string &httpMethod,
- const error_page::Error& error, std::string* errorHtml);
+ void GetNavigationErrorStringsInternal(content::RenderFrame *renderFrame, const std::string &httpMethod,
+ const error_page::Error &error, std::string *errorHtml);
QScopedPointer<RenderThreadObserverQt> m_renderThreadObserver;
QScopedPointer<visitedlink::VisitedLinkSlave> m_visitedLinkSlave;
diff --git a/src/core/renderer/content_settings_observer_qt.cpp b/src/core/renderer/content_settings_observer_qt.cpp
index a9e89dfee..fc7019367 100644
--- a/src/core/renderer/content_settings_observer_qt.cpp
+++ b/src/core/renderer/content_settings_observer_qt.cpp
@@ -63,40 +63,37 @@ bool IsUniqueFrame(blink::WebFrame *frame)
frame->Top()->GetSecurityOrigin().IsUnique();
}
-} // namespace
+} // namespace
namespace QtWebEngineCore {
ContentSettingsObserverQt::ContentSettingsObserverQt(content::RenderFrame *render_frame)
- : content::RenderFrameObserver(render_frame)
- , content::RenderFrameObserverTracker<ContentSettingsObserverQt>(render_frame)
- , m_currentRequestId(0)
+ : content::RenderFrameObserver(render_frame)
+ , content::RenderFrameObserverTracker<ContentSettingsObserverQt>(render_frame)
+ , m_currentRequestId(0)
{
ClearBlockedContentSettings();
render_frame->GetWebFrame()->SetContentSettingsClient(this);
}
-ContentSettingsObserverQt::~ContentSettingsObserverQt() {
-}
+ContentSettingsObserverQt::~ContentSettingsObserverQt() {}
-bool ContentSettingsObserverQt::OnMessageReceived(const IPC::Message& message)
+bool ContentSettingsObserverQt::OnMessageReceived(const IPC::Message &message)
{
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(ContentSettingsObserverQt, message)
- IPC_MESSAGE_HANDLER(QtWebEngineMsg_RequestFileSystemAccessAsyncResponse,
- OnRequestFileSystemAccessAsyncResponse)
- IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_MESSAGE_HANDLER(QtWebEngineMsg_RequestFileSystemAccessAsyncResponse, OnRequestFileSystemAccessAsyncResponse)
+ IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
-void ContentSettingsObserverQt::DidCommitProvisionalLoad(bool is_same_document_navigation,
- ui::PageTransition /*transition*/)
+void ContentSettingsObserverQt::DidCommitProvisionalLoad(bool is_same_document_navigation, ui::PageTransition /*transition*/)
{
- blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
+ blink::WebLocalFrame *frame = render_frame()->GetWebFrame();
if (frame->Parent())
- return; // Not a top-level navigation.
+ return; // Not a top-level navigation.
if (!is_same_document_navigation)
ClearBlockedContentSettings();
@@ -104,8 +101,7 @@ void ContentSettingsObserverQt::DidCommitProvisionalLoad(bool is_same_document_n
GURL url = frame->GetDocument().Url();
// If we start failing this DCHECK, please makes sure we don't regress
// this bug: http://code.google.com/p/chromium/issues/detail?id=79304
- DCHECK(frame->GetDocument().GetSecurityOrigin().ToString() == "null" ||
- !url.SchemeIs(url::kDataScheme));
+ DCHECK(frame->GetDocument().GetSecurityOrigin().ToString() == "null" || !url.SchemeIs(url::kDataScheme));
}
void ContentSettingsObserverQt::OnDestruct()
@@ -120,10 +116,8 @@ bool ContentSettingsObserverQt::AllowDatabase()
return false;
bool result = false;
- Send(new QtWebEngineHostMsg_AllowDatabase(
- routing_id(), url::Origin(frame->GetSecurityOrigin()).GetURL(),
- url::Origin(frame->Top()->GetSecurityOrigin()).GetURL(),
- &result));
+ Send(new QtWebEngineHostMsg_AllowDatabase(routing_id(), url::Origin(frame->GetSecurityOrigin()).GetURL(),
+ url::Origin(frame->Top()->GetSecurityOrigin()).GetURL(), &result));
return result;
}
@@ -140,10 +134,9 @@ void ContentSettingsObserverQt::RequestFileSystemAccessAsync(base::OnceCallback<
// Verify there are no duplicate insertions.
DCHECK(inserted);
- Send(new QtWebEngineHostMsg_RequestFileSystemAccessAsync(
- routing_id(), m_currentRequestId,
- url::Origin(frame->GetSecurityOrigin()).GetURL(),
- url::Origin(frame->Top()->GetSecurityOrigin()).GetURL()));
+ Send(new QtWebEngineHostMsg_RequestFileSystemAccessAsync(routing_id(), m_currentRequestId,
+ url::Origin(frame->GetSecurityOrigin()).GetURL(),
+ url::Origin(frame->Top()->GetSecurityOrigin()).GetURL()));
}
bool ContentSettingsObserverQt::AllowIndexedDB(const WebSecurityOrigin &origin)
@@ -153,10 +146,8 @@ bool ContentSettingsObserverQt::AllowIndexedDB(const WebSecurityOrigin &origin)
return false;
bool result = false;
- Send(new QtWebEngineHostMsg_AllowIndexedDB(
- routing_id(), url::Origin(origin).GetURL(),
- url::Origin(frame->Top()->GetSecurityOrigin()).GetURL(),
- &result));
+ Send(new QtWebEngineHostMsg_AllowIndexedDB(routing_id(), url::Origin(origin).GetURL(),
+ url::Origin(frame->Top()->GetSecurityOrigin()).GetURL(), &result));
return result;
}
@@ -172,9 +163,8 @@ bool ContentSettingsObserverQt::AllowStorage(bool local)
return permissions->second;
bool result = false;
- Send(new QtWebEngineHostMsg_AllowDOMStorage(
- routing_id(), url::Origin(frame->GetSecurityOrigin()).GetURL(),
- url::Origin(frame->Top()->GetSecurityOrigin()).GetURL(), local, &result));
+ Send(new QtWebEngineHostMsg_AllowDOMStorage(routing_id(), url::Origin(frame->GetSecurityOrigin()).GetURL(),
+ url::Origin(frame->Top()->GetSecurityOrigin()).GetURL(), local, &result));
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 9c071aa3c..71e1fbca5 100644
--- a/src/core/renderer/content_settings_observer_qt.h
+++ b/src/core/renderer/content_settings_observer_qt.h
@@ -58,9 +58,9 @@ namespace QtWebEngineCore {
// Handles blocking content per content settings for each RenderFrame.
class ContentSettingsObserverQt
- : public content::RenderFrameObserver
- , public content::RenderFrameObserverTracker<ContentSettingsObserverQt>
- , public blink::WebContentSettingsClient
+ : public content::RenderFrameObserver
+ , public content::RenderFrameObserverTracker<ContentSettingsObserverQt>
+ , public blink::WebContentSettingsClient
{
public:
ContentSettingsObserverQt(content::RenderFrame *render_frame);
@@ -73,11 +73,9 @@ public:
bool AllowStorage(bool local) override;
private:
-
// RenderFrameObserver implementation:
bool OnMessageReceived(const IPC::Message &message) override;
- void DidCommitProvisionalLoad(bool is_same_document_navigation,
- ui::PageTransition transition) override;
+ void DidCommitProvisionalLoad(bool is_same_document_navigation, ui::PageTransition transition) override;
void OnDestruct() override;
// Message handlers.
@@ -98,4 +96,4 @@ private:
} // namespace QtWebEngineCore
-#endif // RENDERER_CONTENT_SETTINGS_OBSERVER_QT_H
+#endif // RENDERER_CONTENT_SETTINGS_OBSERVER_QT_H
diff --git a/src/core/renderer/print_web_view_helper_delegate_qt.cpp b/src/core/renderer/print_web_view_helper_delegate_qt.cpp
index 67cdd6b66..b85ff7249 100644
--- a/src/core/renderer/print_web_view_helper_delegate_qt.cpp
+++ b/src/core/renderer/print_web_view_helper_delegate_qt.cpp
@@ -52,21 +52,17 @@
#include "web_engine_library_info.h"
namespace QtWebEngineCore {
-PrintWebViewHelperDelegateQt::~PrintWebViewHelperDelegateQt()
-{
-
-}
+PrintWebViewHelperDelegateQt::~PrintWebViewHelperDelegateQt() {}
bool PrintWebViewHelperDelegateQt::CancelPrerender(content::RenderFrame *)
{
return false;
}
-blink::WebElement PrintWebViewHelperDelegateQt::GetPdfElement(blink::WebLocalFrame* frame)
+blink::WebElement PrintWebViewHelperDelegateQt::GetPdfElement(blink::WebLocalFrame *frame)
{
GURL url = frame->GetDocument().Url();
- if (url.SchemeIs(extensions::kExtensionScheme) && url.host() == extension_misc::kPdfExtensionId)
- {
+ if (url.SchemeIs(extensions::kExtensionScheme) && url.host() == extension_misc::kPdfExtensionId) {
// <object> with id="plugin" is created in
// chrome/browser/resources/pdf/pdf.js.
auto plugin_element = frame->GetDocument().GetElementById("plugin");
@@ -81,7 +77,7 @@ bool PrintWebViewHelperDelegateQt::IsPrintPreviewEnabled()
return true;
}
-bool PrintWebViewHelperDelegateQt::OverridePrint(blink::WebLocalFrame* frame)
+bool PrintWebViewHelperDelegateQt::OverridePrint(blink::WebLocalFrame *frame)
{
return false;
}
diff --git a/src/core/renderer/print_web_view_helper_delegate_qt.h b/src/core/renderer/print_web_view_helper_delegate_qt.h
index e3020922f..f02580b91 100644
--- a/src/core/renderer/print_web_view_helper_delegate_qt.h
+++ b/src/core/renderer/print_web_view_helper_delegate_qt.h
@@ -57,15 +57,14 @@ class PrintWebViewHelperDelegateQt : public printing::PrintRenderFrameHelper::De
public:
~PrintWebViewHelperDelegateQt() override;
- bool CancelPrerender(content::RenderFrame* render_frame) override;
+ bool CancelPrerender(content::RenderFrame *render_frame) override;
- blink::WebElement GetPdfElement(blink::WebLocalFrame* frame) override;
+ blink::WebElement GetPdfElement(blink::WebLocalFrame *frame) override;
bool IsPrintPreviewEnabled() override;
- bool OverridePrint(blink::WebLocalFrame* frame) override;
-}; // class PrintWebViewHelperDelegateQt
+ bool OverridePrint(blink::WebLocalFrame *frame) override;
+}; // class PrintWebViewHelperDelegateQt
}
#endif // PRINT_WEB_VIEW_HELPER_DELEGATE_QT_H
-
diff --git a/src/core/renderer/render_frame_observer_qt.cpp b/src/core/renderer/render_frame_observer_qt.cpp
index c48ef3b5c..cc01acec6 100644
--- a/src/core/renderer/render_frame_observer_qt.cpp
+++ b/src/core/renderer/render_frame_observer_qt.cpp
@@ -55,30 +55,25 @@
namespace QtWebEngineCore {
-RenderFrameObserverQt::RenderFrameObserverQt(content::RenderFrame* render_frame,
- web_cache::WebCacheImpl* web_cache_impl)
+RenderFrameObserverQt::RenderFrameObserverQt(content::RenderFrame *render_frame, web_cache::WebCacheImpl *web_cache_impl)
: RenderFrameObserver(render_frame)
, RenderFrameObserverTracker<RenderFrameObserverQt>(render_frame)
, m_isFrameDetached(false)
, m_web_cache_impl(web_cache_impl)
-{
-}
+{}
-RenderFrameObserverQt::~RenderFrameObserverQt()
-{
-}
+RenderFrameObserverQt::~RenderFrameObserverQt() {}
-void RenderFrameObserverQt::OnDestruct() {
+void RenderFrameObserverQt::OnDestruct()
+{
delete this;
}
#if QT_CONFIG(webengine_pepper_plugins)
-void RenderFrameObserverQt::DidCreatePepperPlugin(content::RendererPpapiHost* host)
+void RenderFrameObserverQt::DidCreatePepperPlugin(content::RendererPpapiHost *host)
{
- host->GetPpapiHost()->AddHostFactoryFilter(
- base::WrapUnique(new PepperRendererHostFactoryQt(host)));
- host->GetPpapiHost()->AddInstanceMessageFilter(
- base::WrapUnique(new PepperSharedMemoryMessageFilter(host)));
+ host->GetPpapiHost()->AddHostFactoryFilter(base::WrapUnique(new PepperRendererHostFactoryQt(host)));
+ host->GetPpapiHost()->AddInstanceMessageFilter(base::WrapUnique(new PepperSharedMemoryMessageFilter(host)));
}
#endif
diff --git a/src/core/renderer/render_frame_observer_qt.h b/src/core/renderer/render_frame_observer_qt.h
index fb9fd3869..6bdf4ad23 100644
--- a/src/core/renderer/render_frame_observer_qt.h
+++ b/src/core/renderer/render_frame_observer_qt.h
@@ -57,23 +57,22 @@ class WebCacheImpl;
namespace QtWebEngineCore {
class RenderFrameObserverQt
- : public content::RenderFrameObserver
- , public content::RenderFrameObserverTracker<RenderFrameObserverQt>
+ : public content::RenderFrameObserver
+ , public content::RenderFrameObserverTracker<RenderFrameObserverQt>
{
public:
- explicit RenderFrameObserverQt(content::RenderFrame* render_frame,
- web_cache::WebCacheImpl* web_cache_impl);
+ explicit RenderFrameObserverQt(content::RenderFrame *render_frame, web_cache::WebCacheImpl *web_cache_impl);
~RenderFrameObserverQt();
#if QT_CONFIG(webengine_pepper_plugins)
- void DidCreatePepperPlugin(content::RendererPpapiHost* host) override;
+ void DidCreatePepperPlugin(content::RendererPpapiHost *host) override;
#endif
void OnDestruct() override;
void FrameDetached() override;
bool isFrameDetached() const;
- service_manager::BinderRegistry* registry() { return &registry_; }
+ service_manager::BinderRegistry *registry() { return &registry_; }
private:
DISALLOW_COPY_AND_ASSIGN(RenderFrameObserverQt);
diff --git a/src/core/renderer/render_thread_observer_qt.cpp b/src/core/renderer/render_thread_observer_qt.cpp
index 64b9fd961..6012099b8 100644
--- a/src/core/renderer/render_thread_observer_qt.cpp
+++ b/src/core/renderer/render_thread_observer_qt.cpp
@@ -52,7 +52,8 @@ bool RenderThreadObserverQt::m_isIncognitoProcess = false;
void RenderThreadObserverQt::RegisterMojoInterfaces(blink::AssociatedInterfaceRegistry *associated_interfaces)
{
- associated_interfaces->AddInterface(base::Bind(&RenderThreadObserverQt::OnRendererConfigurationAssociatedRequest, base::Unretained(this)));
+ associated_interfaces->AddInterface(
+ base::Bind(&RenderThreadObserverQt::OnRendererConfigurationAssociatedRequest, base::Unretained(this)));
}
void RenderThreadObserverQt::UnregisterMojoInterfaces(blink::AssociatedInterfaceRegistry *associated_interfaces)
@@ -65,7 +66,8 @@ void RenderThreadObserverQt::SetInitialConfiguration(bool is_incognito_process)
m_isIncognitoProcess = is_incognito_process;
}
-void RenderThreadObserverQt::OnRendererConfigurationAssociatedRequest(qtwebengine::mojom::RendererConfigurationAssociatedRequest request)
+void RenderThreadObserverQt::OnRendererConfigurationAssociatedRequest(
+ qtwebengine::mojom::RendererConfigurationAssociatedRequest request)
{
m_rendererConfigurationBindings.AddBinding(this, std::move(request));
}
diff --git a/src/core/renderer/render_thread_observer_qt.h b/src/core/renderer/render_thread_observer_qt.h
index 29b842ab4..21acc6f13 100644
--- a/src/core/renderer/render_thread_observer_qt.h
+++ b/src/core/renderer/render_thread_observer_qt.h
@@ -46,10 +46,11 @@
namespace QtWebEngineCore {
-class RenderThreadObserverQt : public content::RenderThreadObserver,
- public qtwebengine::mojom::RendererConfiguration {
+class RenderThreadObserverQt
+ : public content::RenderThreadObserver
+ , public qtwebengine::mojom::RendererConfiguration
+{
public:
-
RenderThreadObserverQt() = default;
~RenderThreadObserverQt() override = default;
diff --git a/src/core/renderer/render_view_observer_qt.cpp b/src/core/renderer/render_view_observer_qt.cpp
index 7e7c7bdf8..731d8b97d 100644
--- a/src/core/renderer/render_view_observer_qt.cpp
+++ b/src/core/renderer/render_view_observer_qt.cpp
@@ -50,18 +50,15 @@
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/public/web/web_view.h"
-RenderViewObserverQt::RenderViewObserverQt(
- content::RenderView* render_view)
- : content::RenderViewObserver(render_view)
-{
-}
+RenderViewObserverQt::RenderViewObserverQt(content::RenderView *render_view) : content::RenderViewObserver(render_view)
+{}
void RenderViewObserverQt::onFetchDocumentMarkup(quint64 requestId)
{
blink::WebString markup;
if (render_view()->GetWebView()->MainFrame()->IsWebLocalFrame())
markup = blink::WebFrameContentDumper::DumpAsMarkup(
- static_cast<blink::WebLocalFrame*>(render_view()->GetWebView()->MainFrame()));
+ static_cast<blink::WebLocalFrame *>(render_view()->GetWebView()->MainFrame()));
Send(new RenderViewObserverHostQt_DidFetchDocumentMarkup(routing_id(), requestId, markup.Utf16()));
}
@@ -69,9 +66,8 @@ void RenderViewObserverQt::onFetchDocumentInnerText(quint64 requestId)
{
blink::WebString text;
if (render_view()->GetWebView()->MainFrame()->IsWebLocalFrame())
- text = blink::WebFrameContentDumper::DumpWebViewAsText(
- render_view()->GetWebView(),
- std::numeric_limits<std::size_t>::max());
+ text = blink::WebFrameContentDumper::DumpWebViewAsText(render_view()->GetWebView(),
+ std::numeric_limits<std::size_t>::max());
Send(new RenderViewObserverHostQt_DidFetchDocumentInnerText(routing_id(), requestId, text.Utf16()));
}
@@ -85,7 +81,7 @@ void RenderViewObserverQt::OnDestruct()
delete this;
}
-bool RenderViewObserverQt::OnMessageReceived(const IPC::Message& message)
+bool RenderViewObserverQt::OnMessageReceived(const IPC::Message &message)
{
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(RenderViewObserverQt, message)
diff --git a/src/core/renderer/render_view_observer_qt.h b/src/core/renderer/render_view_observer_qt.h
index a878eebe8..5c555b222 100644
--- a/src/core/renderer/render_view_observer_qt.h
+++ b/src/core/renderer/render_view_observer_qt.h
@@ -43,9 +43,10 @@
#include <QtGlobal>
-class RenderViewObserverQt : public content::RenderViewObserver {
+class RenderViewObserverQt : public content::RenderViewObserver
+{
public:
- RenderViewObserverQt(content::RenderView* render_view);
+ RenderViewObserverQt(content::RenderView *render_view);
private:
void onFetchDocumentMarkup(quint64 requestId);
@@ -54,7 +55,7 @@ private:
void OnDestruct() override;
- bool OnMessageReceived(const IPC::Message& message) override;
+ bool OnMessageReceived(const IPC::Message &message) override;
DISALLOW_COPY_AND_ASSIGN(RenderViewObserverQt);
};
diff --git a/src/core/renderer/user_resource_controller.cpp b/src/core/renderer/user_resource_controller.cpp
index 2613d262e..5c1bd301f 100644
--- a/src/core/renderer/user_resource_controller.cpp
+++ b/src/core/renderer/user_resource_controller.cpp
@@ -64,7 +64,7 @@
Q_GLOBAL_STATIC(UserResourceController, qt_webengine_userResourceController)
-static content::RenderView * const globalScriptsIndex = 0;
+static content::RenderView *const globalScriptsIndex = nullptr;
// Scripts meant to run after the load event will be run 500ms after DOMContentLoaded if the load event doesn't come within that delay.
static const int afterLoadTimeout = 500;
@@ -74,7 +74,8 @@ static int validUserScriptSchemes()
return URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS | URLPattern::SCHEME_FILE | URLPattern::SCHEME_QRC;
}
-static bool regexMatchesURL(const std::string &pat, const GURL &url) {
+static bool regexMatchesURL(const std::string &pat, const GURL &url)
+{
QRegularExpression qre(QtWebEngineCore::toQt(pat));
qre.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
if (!qre.isValid())
@@ -96,7 +97,8 @@ static bool includeRuleMatchesURL(const std::string &pat, const GURL &url)
return false;
}
-static bool scriptMatchesURL(const UserScriptData &scriptData, const GURL &url) {
+static bool scriptMatchesURL(const UserScriptData &scriptData, const GURL &url)
+{
// Logic taken from Chromium (extensions/common/user_script.cc)
bool matchFound;
if (!scriptData.urlPatterns.empty()) {
@@ -133,7 +135,7 @@ static bool scriptMatchesURL(const UserScriptData &scriptData, const GURL &url)
class UserResourceController::RenderFrameObserverHelper : public content::RenderFrameObserver
{
public:
- RenderFrameObserverHelper(content::RenderFrame* render_frame);
+ RenderFrameObserverHelper(content::RenderFrame *render_frame);
private:
// RenderFrameObserver implementation.
@@ -142,7 +144,7 @@ private:
void DidFinishLoad() override;
void FrameDetached() override;
void OnDestruct() override;
- bool OnMessageReceived(const IPC::Message& message) override;
+ bool OnMessageReceived(const IPC::Message &message) override;
void onUserScriptAdded(const UserScriptData &);
void onUserScriptRemoved(const UserScriptData &);
@@ -154,12 +156,10 @@ private:
// Helper class to create WeakPtrs so the AfterLoad tasks can be canceled and to
// avoid running scripts more than once per injection point.
-class UserResourceController::RenderFrameObserverHelper::Runner : public base::SupportsWeakPtr<Runner> {
+class UserResourceController::RenderFrameObserverHelper::Runner : public base::SupportsWeakPtr<Runner>
+{
public:
- explicit Runner(blink::WebLocalFrame *frame)
- : m_frame(frame)
- {
- }
+ explicit Runner(blink::WebLocalFrame *frame) : m_frame(frame) {}
void run(UserScriptData::InjectionPoint p)
{
@@ -179,7 +179,8 @@ private:
class UserResourceController::RenderViewObserverHelper : public content::RenderViewObserver
{
public:
- RenderViewObserverHelper(content::RenderView* render_view);
+ RenderViewObserverHelper(content::RenderView *render_view);
+
private:
// RenderViewObserver implementation.
void OnDestruct() override;
@@ -201,8 +202,7 @@ void UserResourceController::runScripts(UserScriptData::InjectionPoint p, blink:
for (uint64_t id : qAsConst(scriptsToRun)) {
const UserScriptData &script = m_scripts.value(id);
- if (script.injectionPoint != p
- || (!script.injectForSubframes && !isMainFrame))
+ if (script.injectionPoint != p || (!script.injectForSubframes && !isMainFrame))
continue;
if (!scriptMatchesURL(script, frame->GetDocument().Url()))
continue;
@@ -221,13 +221,11 @@ void UserResourceController::RunScriptsAtDocumentEnd(content::RenderFrame *rende
UserResourceController::RenderFrameObserverHelper::RenderFrameObserverHelper(content::RenderFrame *render_frame)
: content::RenderFrameObserver(render_frame)
-{
-}
+{}
UserResourceController::RenderViewObserverHelper::RenderViewObserverHelper(content::RenderView *render_view)
: content::RenderViewObserver(render_view)
-{
-}
+{}
void UserResourceController::RenderFrameObserverHelper::DidCommitProvisionalLoad(bool is_same_document_navigation,
ui::PageTransition /*transitionbool*/)
@@ -242,8 +240,7 @@ void UserResourceController::RenderFrameObserverHelper::DidCommitProvisionalLoad
m_runner.reset(new Runner(render_frame()->GetWebFrame()));
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(&Runner::run, m_runner->AsWeakPtr(), UserScriptData::DocumentElementCreation));
+ FROM_HERE, base::BindOnce(&Runner::run, m_runner->AsWeakPtr(), UserScriptData::DocumentElementCreation));
}
void UserResourceController::RenderFrameObserverHelper::DidFinishDocumentLoad()
@@ -252,18 +249,15 @@ void UserResourceController::RenderFrameObserverHelper::DidFinishDocumentLoad()
// called instead of DidCommitProvisionalLoad).
if (m_runner)
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&Runner::run, m_runner->AsWeakPtr(), UserScriptData::AfterLoad),
- base::TimeDelta::FromMilliseconds(afterLoadTimeout));
-
+ FROM_HERE, base::BindOnce(&Runner::run, m_runner->AsWeakPtr(), UserScriptData::AfterLoad),
+ base::TimeDelta::FromMilliseconds(afterLoadTimeout));
}
void UserResourceController::RenderFrameObserverHelper::DidFinishLoad()
{
if (m_runner)
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(&Runner::run, m_runner->AsWeakPtr(), UserScriptData::AfterLoad));
+ FROM_HERE, base::BindOnce(&Runner::run, m_runner->AsWeakPtr(), UserScriptData::AfterLoad));
}
void UserResourceController::RenderFrameObserverHelper::FrameDetached()
@@ -293,7 +287,7 @@ bool UserResourceController::RenderFrameObserverHelper::OnMessageReceived(const
IPC_MESSAGE_HANDLER(RenderFrameObserverHelper_ClearScripts, onScriptsCleared)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
- return handled;
+ return handled;
}
void UserResourceController::RenderFrameObserverHelper::onUserScriptAdded(const UserScriptData &script)
@@ -411,4 +405,3 @@ void UserResourceController::onClearScripts()
{
clearScriptsForView(globalScriptsIndex);
}
-
diff --git a/src/core/renderer/user_resource_controller.h b/src/core/renderer/user_resource_controller.h
index 0b5e0a0c6..3a493b9b7 100644
--- a/src/core/renderer/user_resource_controller.h
+++ b/src/core/renderer/user_resource_controller.h
@@ -56,7 +56,8 @@ class RenderFrame;
class RenderView;
}
-class UserResourceController : public content::RenderThreadObserver {
+class UserResourceController : public content::RenderThreadObserver
+{
public:
static UserResourceController *instance();
diff --git a/src/core/renderer/web_channel_ipc_transport.cpp b/src/core/renderer/web_channel_ipc_transport.cpp
index 108686068..8e961566c 100644
--- a/src/core/renderer/web_channel_ipc_transport.cpp
+++ b/src/core/renderer/web_channel_ipc_transport.cpp
@@ -61,11 +61,13 @@
namespace QtWebEngineCore {
-class WebChannelTransport : public gin::Wrappable<WebChannelTransport> {
+class WebChannelTransport : public gin::Wrappable<WebChannelTransport>
+{
public:
static gin::WrapperInfo kWrapperInfo;
static void Install(blink::WebLocalFrame *frame, uint worldId);
static void Uninstall(blink::WebLocalFrame *frame, uint worldId);
+
private:
WebChannelTransport() {}
void NativeQtSendMessage(gin::Arguments *args);
@@ -152,9 +154,7 @@ void WebChannelTransport::NativeQtSendMessage(gin::Arguments *args)
v8::Local<v8::String> jsonString = v8::Local<v8::String>::Cast(jsonValue);
QByteArray json(jsonString->Utf8Length(isolate), 0);
- jsonString->WriteUtf8(isolate,
- json.data(), json.size(),
- nullptr, v8::String::REPLACE_INVALID_UTF8);
+ jsonString->WriteUtf8(isolate, json.data(), json.size(), nullptr, v8::String::REPLACE_INVALID_UTF8);
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
@@ -172,21 +172,19 @@ void WebChannelTransport::NativeQtSendMessage(gin::Arguments *args)
gin::ObjectTemplateBuilder WebChannelTransport::GetObjectTemplateBuilder(v8::Isolate *isolate)
{
- return gin::Wrappable<WebChannelTransport>::GetObjectTemplateBuilder(isolate)
- .SetMethod("send", &WebChannelTransport::NativeQtSendMessage);
+ return gin::Wrappable<WebChannelTransport>::GetObjectTemplateBuilder(isolate).SetMethod(
+ "send", &WebChannelTransport::NativeQtSendMessage);
}
WebChannelIPCTransport::WebChannelIPCTransport(content::RenderFrame *renderFrame)
- : content::RenderFrameObserver(renderFrame)
- , m_worldId(0)
- , m_worldInitialized(false)
+ : content::RenderFrameObserver(renderFrame), m_worldId(0), m_worldInitialized(false)
{
renderFrame->GetAssociatedInterfaceRegistry()->AddInterface(
- base::Bind(&WebChannelIPCTransport::BindRequest, base::Unretained(this)));
+ base::Bind(&WebChannelIPCTransport::BindRequest, base::Unretained(this)));
}
-void WebChannelIPCTransport::BindRequest(
- qtwebchannel::mojom::WebChannelTransportRenderAssociatedRequest request) {
+void WebChannelIPCTransport::BindRequest(qtwebchannel::mojom::WebChannelTransportRenderAssociatedRequest request)
+{
m_binding.AddBinding(this, std::move(request));
}
@@ -208,7 +206,7 @@ void WebChannelIPCTransport::SetWorldId(uint32_t worldId)
void WebChannelIPCTransport::ResetWorldId()
{
- if (m_worldInitialized && m_canUseContext)
+ if (m_worldInitialized && m_canUseContext)
WebChannelTransport::Uninstall(render_frame()->GetWebFrame(), m_worldId);
m_worldInitialized = false;
@@ -222,8 +220,8 @@ void WebChannelIPCTransport::DispatchWebChannelMessage(const std::vector<uint8_t
if (!m_canUseContext)
return;
- QJsonDocument doc = QJsonDocument::fromRawData(reinterpret_cast<const char *>(binaryJson.data()),
- binaryJson.size(), QJsonDocument::BypassValidation);
+ QJsonDocument doc = QJsonDocument::fromRawData(reinterpret_cast<const char *>(binaryJson.data()), binaryJson.size(),
+ QJsonDocument::BypassValidation);
DCHECK(doc.isObject());
QByteArray json = doc.toJson(QJsonDocument::Compact);
@@ -242,22 +240,23 @@ void WebChannelIPCTransport::DispatchWebChannelMessage(const std::vector<uint8_t
if (qtObjectValue.IsEmpty() || !qtObjectValue.ToLocalChecked()->IsObject())
return;
v8::Local<v8::Object> qtObject = v8::Local<v8::Object>::Cast(qtObjectValue.ToLocalChecked());
- v8::MaybeLocal<v8::Value> webChannelObjectValue(qtObject->Get(context, gin::StringToV8(isolate, "webChannelTransport")));
+ v8::MaybeLocal<v8::Value> webChannelObjectValue(
+ qtObject->Get(context, gin::StringToV8(isolate, "webChannelTransport")));
if (webChannelObjectValue.IsEmpty() || !webChannelObjectValue.ToLocalChecked()->IsObject())
return;
v8::Local<v8::Object> webChannelObject = v8::Local<v8::Object>::Cast(webChannelObjectValue.ToLocalChecked());
v8::MaybeLocal<v8::Value> callbackValue(webChannelObject->Get(context, gin::StringToV8(isolate, "onmessage")));
if (callbackValue.IsEmpty() || !callbackValue.ToLocalChecked()->IsFunction()) {
- LOG(WARNING) << "onmessage is not a callable property of qt.webChannelTransport. Some things might not work as expected.";
+ LOG(WARNING) << "onmessage is not a callable property of qt.webChannelTransport. Some things might not work as "
+ "expected.";
return;
}
v8::Local<v8::Object> messageObject(v8::Object::New(isolate));
v8::Maybe<bool> wasSet = messageObject->DefineOwnProperty(
- context,
- v8::String::NewFromUtf8(isolate, "data").ToLocalChecked(),
- v8::String::NewFromUtf8(isolate, json.constData(), v8::NewStringType::kNormal, json.size()).ToLocalChecked(),
- v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete));
+ context, v8::String::NewFromUtf8(isolate, "data").ToLocalChecked(),
+ v8::String::NewFromUtf8(isolate, json.constData(), v8::NewStringType::kNormal, json.size()).ToLocalChecked(),
+ v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete));
DCHECK(!wasSet.IsNothing() && wasSet.FromJust());
v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(callbackValue.ToLocalChecked());
diff --git a/src/core/renderer/web_channel_ipc_transport.h b/src/core/renderer/web_channel_ipc_transport.h
index 178c20ed1..a80eecf24 100644
--- a/src/core/renderer/web_channel_ipc_transport.h
+++ b/src/core/renderer/web_channel_ipc_transport.h
@@ -49,8 +49,10 @@
namespace QtWebEngineCore {
-class WebChannelIPCTransport: private content::RenderFrameObserver,
- public qtwebchannel::mojom::WebChannelTransportRender {
+class WebChannelIPCTransport
+ : private content::RenderFrameObserver
+ , public qtwebchannel::mojom::WebChannelTransportRender
+{
public:
WebChannelIPCTransport(content::RenderFrame *);
@@ -58,7 +60,7 @@ private:
// qtwebchannel::mojom::WebChannelTransportRender
void SetWorldId(uint32_t worldId) override;
void ResetWorldId() override;
- void DispatchWebChannelMessage(const std::vector<uint8_t>& binaryJson, uint32_t worldId) override;
+ void DispatchWebChannelMessage(const std::vector<uint8_t> &binaryJson, uint32_t worldId) override;
// RenderFrameObserver
void WillReleaseScriptContext(v8::Local<v8::Context> context, int worldId) override;
diff --git a/src/core/renderer_host/render_view_observer_host_qt.cpp b/src/core/renderer_host/render_view_observer_host_qt.cpp
index c097e102d..165a9d86a 100644
--- a/src/core/renderer_host/render_view_observer_host_qt.cpp
+++ b/src/core/renderer_host/render_view_observer_host_qt.cpp
@@ -69,7 +69,7 @@ void RenderViewObserverHostQt::fetchDocumentInnerText(quint64 requestId)
web_contents()->GetRenderViewHost()->GetRoutingID(), requestId));
}
-bool RenderViewObserverHostQt::OnMessageReceived(const IPC::Message& message)
+bool RenderViewObserverHostQt::OnMessageReceived(const IPC::Message &message)
{
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(RenderViewObserverHostQt, message)
@@ -80,15 +80,14 @@ bool RenderViewObserverHostQt::OnMessageReceived(const IPC::Message& message)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
-
}
-void RenderViewObserverHostQt::onDidFetchDocumentMarkup(quint64 requestId, const base::string16& markup)
+void RenderViewObserverHostQt::onDidFetchDocumentMarkup(quint64 requestId, const base::string16 &markup)
{
m_adapterClient->didFetchDocumentMarkup(requestId, toQt(markup));
}
-void RenderViewObserverHostQt::onDidFetchDocumentInnerText(quint64 requestId, const base::string16& innerText)
+void RenderViewObserverHostQt::onDidFetchDocumentInnerText(quint64 requestId, const base::string16 &innerText)
{
m_adapterClient->didFetchDocumentInnerText(requestId, toQt(innerText));
}
diff --git a/src/core/renderer_host/render_view_observer_host_qt.h b/src/core/renderer_host/render_view_observer_host_qt.h
index a08263e07..8590ecbc5 100644
--- a/src/core/renderer_host/render_view_observer_host_qt.h
+++ b/src/core/renderer_host/render_view_observer_host_qt.h
@@ -45,7 +45,7 @@
#include <QtGlobal>
namespace content {
- class WebContents;
+class WebContents;
}
namespace QtWebEngineCore {
@@ -55,14 +55,14 @@ class WebContentsAdapterClient;
class RenderViewObserverHostQt : public content::WebContentsObserver
{
public:
- RenderViewObserverHostQt(content::WebContents*, WebContentsAdapterClient *adapterClient);
+ RenderViewObserverHostQt(content::WebContents *, WebContentsAdapterClient *adapterClient);
void fetchDocumentMarkup(quint64 requestId);
void fetchDocumentInnerText(quint64 requestId);
private:
- bool OnMessageReceived(const IPC::Message& message) override;
- void onDidFetchDocumentMarkup(quint64 requestId, const base::string16& markup);
- void onDidFetchDocumentInnerText(quint64 requestId, const base::string16& innerText);
+ bool OnMessageReceived(const IPC::Message &message) override;
+ void onDidFetchDocumentMarkup(quint64 requestId, const base::string16 &markup);
+ void onDidFetchDocumentInnerText(quint64 requestId, const base::string16 &innerText);
WebContentsAdapterClient *m_adapterClient;
};
diff --git a/src/core/renderer_host/resource_dispatcher_host_delegate_qt.cpp b/src/core/renderer_host/resource_dispatcher_host_delegate_qt.cpp
deleted file mode 100644
index a08e2cf88..000000000
--- a/src/core/renderer_host/resource_dispatcher_host_delegate_qt.cpp
+++ /dev/null
@@ -1,186 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtWebEngine module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE.Chromium file.
-
-#include "resource_dispatcher_host_delegate_qt.h"
-
-#include "base/bind.h"
-#include "base/guid.h"
-#include "base/strings/stringprintf.h"
-#include "base/task/post_task.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/download_manager.h"
-#include "content/public/browser/download_request_utils.h"
-#include "content/public/browser/navigation_controller.h"
-
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_view_host.h"
-#include "content/public/browser/resource_dispatcher_host.h"
-#include "content/public/browser/resource_request_info.h"
-#include "content/public/browser/stream_info.h"
-#include "content/public/browser/web_contents.h"
-
-#include "extensions/extension_system_qt.h"
-#include "extensions/browser/info_map.h"
-#include "extensions/common/constants.h"
-#include "extensions/common/extension.h"
-#include "extensions/common/manifest_handlers/mime_types_handler.h"
-
-#include "net/base/escape.h"
-#include "net/url_request/url_request.h"
-
-#include "profile_io_data_qt.h"
-#include "type_conversion.h"
-#include "web_contents_delegate_qt.h"
-#include "web_engine_settings.h"
-
-namespace QtWebEngineCore {
-
-void OnPdfStreamIntercepted(
- const GURL& original_url,
- std::string extension_id,
- int frame_tree_node_id,
- const content::ResourceRequestInfo::WebContentsGetter&
- web_contents_getter) {
- content::WebContents* web_contents = web_contents_getter.Run();
- if (!web_contents)
- return;
-
- WebContentsDelegateQt *contentsDelegate = static_cast<WebContentsDelegateQt*>(web_contents->GetDelegate());
- if (!contentsDelegate)
- return;
-
- WebEngineSettings *settings = contentsDelegate->webEngineSettings();
- if (!settings->testAttribute(WebEngineSettings::PdfViewerEnabled)
- || !settings->testAttribute(WebEngineSettings::PluginsEnabled)) {
- // If the applications has been set up to always download PDF files to open them in an
- // external viewer, trigger the download.
- std::unique_ptr<download::DownloadUrlParameters> params(
- content::DownloadRequestUtils::CreateDownloadForWebContentsMainFrame(
- web_contents, original_url, MISSING_TRAFFIC_ANNOTATION));
- content::BrowserContext::GetDownloadManager(web_contents->GetBrowserContext())
- ->DownloadUrl(std::move(params));
- return;
- }
-
- // The URL passes the original pdf resource url, that will be requested
- // by the pdf viewer extension page.
- content::NavigationController::LoadURLParams params(
- GURL(base::StringPrintf("%s://%s/index.html?%s", extensions::kExtensionScheme,
- extension_id.c_str(),
- original_url.spec().c_str())));
-
- params.frame_tree_node_id = frame_tree_node_id;
- web_contents->GetController().LoadURLWithParams(params);
-}
-
-bool ResourceDispatcherHostDelegateQt::ShouldInterceptResourceAsStream(net::URLRequest *request,
- const std::string &mime_type,
- GURL *origin,
- std::string *payload)
-{
- content::ResourceRequestInfo* info =
- content::ResourceRequestInfo::ForRequest(request);
-
- int render_process_host_id = -1;
- int render_frame_id = -1;
- if (!content::ResourceRequestInfo::GetRenderFrameForRequest(request, &render_process_host_id, &render_frame_id))
- return false;
-
- std::vector<std::string> whitelist = MimeTypesHandler::GetMIMETypeWhitelist();
-
- extensions::ExtensionSystemQt *extensionSystem = ProfileIODataQt::FromResourceContext(info->GetContext())->GetExtensionSystem();
- if (!extensionSystem)
- return false;
-
- const scoped_refptr<const extensions::InfoMap> extension_info_map(extensionSystem->info_map());
-
- for (const std::string &extension_id : whitelist) {
- const extensions::Extension *extension = extension_info_map->extensions().GetByID(extension_id);
- if (!extension)
- continue;
-
- MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension);
- if (!handler)
- continue;
- if (handler->CanHandleMIMEType(mime_type)) {
- StreamTargetInfo target_info;
- *origin = extensions::Extension::GetBaseURLFromExtensionId(extension_id);
- target_info.extension_id = extension_id;
- target_info.view_id = base::GenerateGUID();
- *payload = target_info.view_id;
- stream_target_info_[request] = target_info;
- return true;
- }
- }
- return false;
-}
-
-// Informs the delegate that a Stream was created. The Stream can be read from
-// the blob URL of the Stream, but can only be read once.
-void ResourceDispatcherHostDelegateQt::OnStreamCreated(net::URLRequest *request,
- std::unique_ptr<content::StreamInfo> stream)
-{
- content::ResourceRequestInfo *info = content::ResourceRequestInfo::ForRequest(request);
- std::map<net::URLRequest *, StreamTargetInfo>::iterator ix = stream_target_info_.find(request);
- CHECK(ix != stream_target_info_.end());
- int render_frame_id = -1;
- int render_process_id = -1;
- if (!content::ResourceRequestInfo::GetRenderFrameForRequest(request, &render_process_id, &render_frame_id)) {
- stream_target_info_.erase(request);
- request->Cancel();
- return;
- }
-
- base::PostTaskWithTraits(
- FROM_HERE, {content::BrowserThread::UI},
- base::BindOnce(&OnPdfStreamIntercepted,
- request->url(), ix->second.extension_id,
- info->GetFrameTreeNodeId(), info->GetWebContentsGetterForRequest()
- )
- );
- stream_target_info_.erase(request);
-}
-
-} // namespace QtWebEngineCore
diff --git a/src/core/renderer_host/resource_dispatcher_host_delegate_qt.h b/src/core/renderer_host/resource_dispatcher_host_delegate_qt.h
deleted file mode 100644
index 3039fd03e..000000000
--- a/src/core/renderer_host/resource_dispatcher_host_delegate_qt.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtWebEngine module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef RESOURCE_DISPATCHER_HOST_DELEGATE_QT_H
-#define RESOURCE_DISPATCHER_HOST_DELEGATE_QT_H
-
-#include "content/public/browser/resource_dispatcher_host_delegate.h"
-#include "extensions/buildflags/buildflags.h"
-
-#include "web_contents_adapter_client.h"
-
-namespace QtWebEngineCore {
-
-class ResourceDispatcherHostDelegateQt : public content::ResourceDispatcherHostDelegate {
-public:
- // If the stream will be rendered in a BrowserPlugin, |payload| will contain
- // the data that should be given to the old ResourceHandler to forward to the
- // renderer process.
- bool ShouldInterceptResourceAsStream(net::URLRequest *request,
- const std::string &mime_type,
- GURL *origin,
- std::string *payload) override;
-
- // Informs the delegate that a Stream was created. The Stream can be read from
- // the blob URL of the Stream, but can only be read once.
- void OnStreamCreated(net::URLRequest *request,
- std::unique_ptr<content::StreamInfo> stream) override;
-private:
-#if BUILDFLAG(ENABLE_EXTENSIONS)
- struct StreamTargetInfo {
- std::string extension_id;
- std::string view_id;
- };
- std::map<net::URLRequest *, StreamTargetInfo> stream_target_info_;
-#endif
-
-};
-
-} // namespace QtWebEngineCore
-
-#endif // RESOURCE_DISPATCHER_HOST_DELEGATE_QT_H
diff --git a/src/core/renderer_host/user_resource_controller_host.cpp b/src/core/renderer_host/user_resource_controller_host.cpp
index ed4d736f2..96625c13f 100644
--- a/src/core/renderer_host/user_resource_controller_host.cpp
+++ b/src/core/renderer_host/user_resource_controller_host.cpp
@@ -51,39 +51,37 @@
namespace QtWebEngineCore {
-class UserResourceControllerHost::WebContentsObserverHelper : public content::WebContentsObserver {
+class UserResourceControllerHost::WebContentsObserverHelper : public content::WebContentsObserver
+{
public:
WebContentsObserverHelper(UserResourceControllerHost *, content::WebContents *);
// WebContentsObserver overrides:
void RenderFrameCreated(content::RenderFrameHost *renderFrameHost) override;
- void RenderFrameHostChanged(content::RenderFrameHost *oldHost,
- content::RenderFrameHost *newHost) override;
+ void RenderFrameHostChanged(content::RenderFrameHost *oldHost, content::RenderFrameHost *newHost) override;
void WebContentsDestroyed() override;
private:
UserResourceControllerHost *m_controllerHost;
};
-UserResourceControllerHost::WebContentsObserverHelper::WebContentsObserverHelper(UserResourceControllerHost *controller, content::WebContents *contents)
- : content::WebContentsObserver(contents)
- , m_controllerHost(controller)
+UserResourceControllerHost::WebContentsObserverHelper::WebContentsObserverHelper(UserResourceControllerHost *controller,
+ content::WebContents *contents)
+ : content::WebContentsObserver(contents)
+ , m_controllerHost(controller)
{
}
-void UserResourceControllerHost::WebContentsObserverHelper::RenderFrameCreated(
- content::RenderFrameHost *renderFrameHost)
+void UserResourceControllerHost::WebContentsObserverHelper::RenderFrameCreated(content::RenderFrameHost *renderFrameHost)
{
content::WebContents *contents = web_contents();
const QList<UserScript> scripts = m_controllerHost->m_perContentsScripts.value(contents);
for (const UserScript &script : scripts)
- renderFrameHost->Send(new RenderFrameObserverHelper_AddScript(
- renderFrameHost->GetRoutingID(), script.data()));
+ renderFrameHost->Send(new RenderFrameObserverHelper_AddScript(renderFrameHost->GetRoutingID(), script.data()));
}
-void UserResourceControllerHost::WebContentsObserverHelper::RenderFrameHostChanged(
- content::RenderFrameHost *oldHost,
- content::RenderFrameHost *newHost)
+void UserResourceControllerHost::WebContentsObserverHelper::RenderFrameHostChanged(content::RenderFrameHost *oldHost,
+ content::RenderFrameHost *newHost)
{
if (oldHost)
oldHost->Send(new RenderFrameObserverHelper_ClearScripts(oldHost->GetRoutingID()));
@@ -95,10 +93,12 @@ void UserResourceControllerHost::WebContentsObserverHelper::WebContentsDestroyed
delete this;
}
-class UserResourceControllerHost::RenderProcessObserverHelper : public content::RenderProcessHostObserver {
+class UserResourceControllerHost::RenderProcessObserverHelper : public content::RenderProcessHostObserver
+{
public:
RenderProcessObserverHelper(UserResourceControllerHost *);
void RenderProcessHostDestroyed(content::RenderProcessHost *) override;
+
private:
UserResourceControllerHost *m_controllerHost;
};
@@ -142,9 +142,9 @@ void UserResourceControllerHost::addUserScript(const UserScript &script, WebCont
}
}
contents->GetRenderViewHost()->Send(
- new RenderFrameObserverHelper_AddScript(
- contents->GetRenderViewHost()->GetMainFrame()->GetRoutingID(),
- script.data()));
+ new RenderFrameObserverHelper_AddScript(
+ contents->GetRenderViewHost()->GetMainFrame()->GetRoutingID(),
+ script.data()));
}
}
@@ -165,8 +165,7 @@ bool UserResourceControllerHost::removeUserScript(const UserScript &script, WebC
return false;
const bool isProfileWideScript = !adapter;
if (isProfileWideScript) {
- QList<UserScript>::iterator it
- = std::find(m_profileWideScripts.begin(), m_profileWideScripts.end(), script);
+ QList<UserScript>::iterator it = std::find(m_profileWideScripts.begin(), m_profileWideScripts.end(), script);
if (it == m_profileWideScripts.end())
return false;
for (content::RenderProcessHost *renderer : qAsConst(m_observedProcesses))
@@ -181,9 +180,7 @@ bool UserResourceControllerHost::removeUserScript(const UserScript &script, WebC
if (it == list.end())
return false;
contents->GetRenderViewHost()->Send(
- new RenderFrameObserverHelper_RemoveScript(
- contents->GetMainFrame()->GetRoutingID(),
- (*it).data()));
+ new RenderFrameObserverHelper_RemoveScript(contents->GetMainFrame()->GetRoutingID(), (*it).data()));
list.erase(it);
}
return true;
@@ -200,7 +197,7 @@ void UserResourceControllerHost::clearAllScripts(WebContentsAdapter *adapter)
content::WebContents *contents = adapter->webContents();
m_perContentsScripts.remove(contents);
contents->GetRenderViewHost()->Send(
- new RenderFrameObserverHelper_ClearScripts(contents->GetMainFrame()->GetRoutingID()));
+ new RenderFrameObserverHelper_ClearScripts(contents->GetMainFrame()->GetRoutingID()));
}
}
diff --git a/src/core/renderer_host/user_resource_controller_host.h b/src/core/renderer_host/user_resource_controller_host.h
index 16a73f5fb..9d828feb6 100644
--- a/src/core/renderer_host/user_resource_controller_host.h
+++ b/src/core/renderer_host/user_resource_controller_host.h
@@ -66,7 +66,8 @@ namespace QtWebEngineCore {
class WebContentsAdapter;
-class Q_WEBENGINECORE_PRIVATE_EXPORT UserResourceControllerHost {
+class Q_WEBENGINECORE_PRIVATE_EXPORT UserResourceControllerHost
+{
public:
UserResourceControllerHost();
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 9758f73bf..6dbf2a370 100644
--- a/src/core/renderer_host/web_channel_ipc_transport_host.cpp
+++ b/src/core/renderer_host/web_channel_ipc_transport_host.cpp
@@ -54,11 +54,6 @@
namespace QtWebEngineCore {
-enum {
- // sizeof(QJsonPrivate::Header) + sizeof(QJsonPrivate::Base)
- MinimumBinaryJsonSize = 8 + 12
-};
-
Q_LOGGING_CATEGORY(log, "qt.webengine.webchanneltransport")
inline QDebug operator<<(QDebug stream, content::RenderFrameHost *frame)
@@ -66,7 +61,7 @@ inline QDebug operator<<(QDebug stream, content::RenderFrameHost *frame)
return stream << "frame " << frame->GetRoutingID() << " in process " << frame->GetProcess()->GetID();
}
-template <class T>
+template<class T>
inline QDebug operator<<(QDebug stream, const base::Optional<T> &opt)
{
if (opt)
@@ -145,12 +140,7 @@ void WebChannelIPCTransportHost::DispatchWebChannelMessage(const std::vector<uin
return;
}
- QJsonDocument doc;
- // QJsonDocument::fromRawData does not check the length before it starts
- // parsing the QJsonPrivate::Header and QJsonPrivate::Base structures.
- if (binaryJson.size() >= MinimumBinaryJsonSize)
- doc = QJsonDocument::fromRawData(reinterpret_cast<const char *>(binaryJson.data()),
- binaryJson.size());
+ QJsonDocument doc = QJsonDocument::fromRawData(reinterpret_cast<const char *>(binaryJson.data()), binaryJson.size());
if (!doc.isObject()) {
qCCritical(log).nospace() << "received invalid webchannel message from " << frame;
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 94891f25f..d53b24b6b 100644
--- a/src/core/renderer_host/web_channel_ipc_transport_host.h
+++ b/src/core/renderer_host/web_channel_ipc_transport_host.h
@@ -53,9 +53,11 @@ QT_FORWARD_DECLARE_CLASS(QString)
namespace QtWebEngineCore {
-class WebChannelIPCTransportHost : public QWebChannelAbstractTransport
+class WebChannelIPCTransportHost
+ : public QWebChannelAbstractTransport
, private content::WebContentsObserver
- , qtwebchannel::mojom::WebChannelTransportHost {
+ , qtwebchannel::mojom::WebChannelTransportHost
+{
public:
WebChannelIPCTransportHost(content::WebContents *webContents, uint32_t worldId = 0, QObject *parent = nullptr);
~WebChannelIPCTransportHost() override;
diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp
index a7579f916..02f52df15 100644
--- a/src/core/web_contents_adapter.cpp
+++ b/src/core/web_contents_adapter.cpp
@@ -1146,6 +1146,17 @@ bool WebContentsAdapter::recentlyAudible() const
return m_webContents->IsCurrentlyAudible();
}
+qint64 WebContentsAdapter::renderProcessPid() const
+{
+ CHECK_INITIALIZED(0);
+
+ content::RenderProcessHost *renderProcessHost = m_webContents->GetMainFrame()->GetProcess();
+ const base::Process &process = renderProcessHost->GetProcess();
+ if (!process.IsValid())
+ return 0;
+ return process.Pid();
+}
+
void WebContentsAdapter::copyImageAt(const QPoint &location)
{
CHECK_INITIALIZED();
diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h
index f8f147f9a..ef9d21b8f 100644
--- a/src/core/web_contents_adapter.h
+++ b/src/core/web_contents_adapter.h
@@ -166,6 +166,7 @@ public:
bool isAudioMuted() const;
void setAudioMuted(bool mute);
bool recentlyAudible() const;
+ qint64 renderProcessPid() const;
// Must match blink::WebMediaPlayerAction::Type.
enum MediaPlayerAction {
diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h
index e2f667358..3a75185f8 100644
--- a/src/core/web_contents_adapter_client.h
+++ b/src/core/web_contents_adapter_client.h
@@ -458,6 +458,7 @@ public:
virtual void didUpdateTargetURL(const QUrl&) = 0;
virtual void selectionChanged() = 0;
virtual void recentlyAudibleChanged(bool recentlyAudible) = 0;
+ virtual void renderProcessPidChanged(qint64 pid) = 0;
virtual QRectF viewportRect() const = 0;
virtual QColor backgroundColor() const = 0;
virtual void loadStarted(const QUrl &provisionalUrl, bool isErrorPage = false) = 0;
diff --git a/src/core/web_contents_delegate_qt.cpp b/src/core/web_contents_delegate_qt.cpp
index 7b339776b..f409d8a7c 100644
--- a/src/core/web_contents_delegate_qt.cpp
+++ b/src/core/web_contents_delegate_qt.cpp
@@ -49,7 +49,6 @@
#include "favicon_manager.h"
#include "file_picker_controller.h"
#include "media_capture_devices_dispatcher.h"
-#include "net/network_delegate_qt.h"
#include "profile_qt.h"
#include "qwebengineregisterprotocolhandlerrequest.h"
#include "register_protocol_handler_request_controller_impl.h"
@@ -316,6 +315,14 @@ void WebContentsDelegateQt::RenderFrameHostChanged(content::RenderFrameHost *old
if (new_host) {
content::FrameTreeNode *new_node = static_cast<content::RenderFrameHostImpl *>(new_host)->frame_tree_node();
m_frameFocusedObserver.addNode(new_node);
+
+ // Is this a main frame?
+ if (new_host->GetFrameOwnerElementType() == blink::FrameOwnerElementType::kNone) {
+ content::RenderProcessHost *renderProcessHost = new_host->GetProcess();
+ const base::Process &process = renderProcessHost->GetProcess();
+ if (process.IsValid())
+ m_viewClient->renderProcessPidChanged(process.Pid());
+ }
}
}
diff --git a/src/core/web_engine_context.cpp b/src/core/web_engine_context.cpp
index c34d3c612..79c093fab 100644
--- a/src/core/web_engine_context.cpp
+++ b/src/core/web_engine_context.cpp
@@ -49,6 +49,7 @@
#include "base/task/sequence_manager/thread_controller_with_message_pump_impl.h"
#include "base/threading/thread_restrictions.h"
#include "cc/base/switches.h"
+#include "chrome/common/chrome_switches.h"
#if QT_CONFIG(webengine_printing_and_pdf)
#include "chrome/browser/printing/print_job_manager.h"
#include "components/printing/browser/features.h"
@@ -73,6 +74,7 @@
#include "content/public/common/content_paths.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/main_function_params.h"
+#include "content/public/common/network_service_util.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "gpu/command_buffer/service/sync_point_manager.h"
#include "media/audio/audio_manager.h"
@@ -211,6 +213,21 @@ bool usingSoftwareDynamicGL()
#endif
}
+void setupProxyPac(base::CommandLine *commandLine){
+ if (commandLine->HasSwitch(switches::kProxyPacUrl)) {
+ QUrl pac_url(toQt(commandLine->GetSwitchValueASCII(switches::kProxyPacUrl)));
+ if (pac_url.isValid() && pac_url.isLocalFile()) {
+ QFile file(pac_url.toLocalFile());
+ if (file.exists() && file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ QByteArray ba = file.readAll();
+ commandLine->RemoveSwitch(switches::kProxyPacUrl);
+ commandLine->AppendSwitchASCII(switches::kProxyPacUrl,
+ ba.toBase64().prepend("data:application/x-javascript-config;base64,").toStdString());
+ }
+ }
+ }
+}
+
scoped_refptr<QtWebEngineCore::WebEngineContext> WebEngineContext::m_handle;
bool WebEngineContext::m_destroyed = false;
@@ -462,7 +479,7 @@ WebEngineContext::WebEngineContext()
#endif
base::CommandLine* parsedCommandLine = commandLine();
-
+ setupProxyPac(parsedCommandLine);
parsedCommandLine->AppendSwitchPath(switches::kBrowserSubprocessPath, WebEngineLibraryInfo::getPath(content::CHILD_PROCESS_EXE));
// Enable sandboxing on OS X and Linux (Desktop / Embedded) by default.
@@ -477,20 +494,6 @@ WebEngineContext::WebEngineContext()
}
parsedCommandLine->AppendSwitch(switches::kEnableThreadedCompositing);
- // These are currently only default on OS X, and we don't support them:
- parsedCommandLine->AppendSwitch(switches::kDisableZeroCopy);
- parsedCommandLine->AppendSwitch(switches::kDisableGpuMemoryBufferCompositorResources);
-
- // Enabled on OS X and Linux but currently not working. It worked in 5.7 on OS X.
- parsedCommandLine->AppendSwitch(switches::kDisableGpuMemoryBufferVideoFrames);
-
-#if defined(Q_OS_MACOS)
- // Accelerated decoding currently does not work on macOS due to issues with OpenGL Rectangle
- // texture support. See QTBUG-60002.
- parsedCommandLine->AppendSwitch(switches::kDisableAcceleratedVideoDecode);
- // Same problem with Pepper using OpenGL images.
- parsedCommandLine->AppendSwitch(switches::kDisablePepper3DImageChromium);
-#endif
#if defined(Q_OS_WIN)
// This switch is used in Chromium's gl_context_wgl.cc file to determine whether to create
@@ -539,8 +542,12 @@ WebEngineContext::WebEngineContext()
// The video-capture service is not functioning at this moment (since 69)
appendToFeatureList(disableFeatures, features::kMojoVideoCapture.name);
- // We do not yet support the network-service, but it is enabled by default since 75.
- appendToFeatureList(disableFeatures, network::features::kNetworkService.name);
+ // We do not yet fully support the network-service, but it has been enabled by default since 75.
+ bool enableNetworkService = !parsedCommandLine->HasSwitch("disable-network-service");
+ parsedCommandLine->RemoveSwitch("disable-network-service");
+ if (!enableNetworkService)
+ appendToFeatureList(disableFeatures, network::features::kNetworkService.name);
+
// BlinkGenPropertyTrees is enabled by default in 75, but causes regressions.
appendToFeatureList(disableFeatures, blink::features::kBlinkGenPropertyTrees.name);
@@ -566,6 +573,21 @@ WebEngineContext::WebEngineContext()
}
if (!enableViz) {
+ // These are currently only default on OS X, and we don't support them:
+ parsedCommandLine->AppendSwitch(switches::kDisableZeroCopy);
+ parsedCommandLine->AppendSwitch(switches::kDisableGpuMemoryBufferCompositorResources);
+
+ // Enabled on OS X and Linux but currently not working. It worked in 5.7 on OS X.
+ parsedCommandLine->AppendSwitch(switches::kDisableGpuMemoryBufferVideoFrames);
+
+#if defined(Q_OS_MACOS)
+ // Accelerated decoding currently does not work on macOS due to issues with OpenGL Rectangle
+ // texture support. See QTBUG-60002.
+ parsedCommandLine->AppendSwitch(switches::kDisableAcceleratedVideoDecode);
+ // Same problem with Pepper using OpenGL images.
+ parsedCommandLine->AppendSwitch(switches::kDisablePepper3DImageChromium);
+#endif
+
// Viz Display Compositor is enabled by default since 73. Doesn't work for us (also implies SurfaceSynchronization)
appendToFeatureList(disableFeatures, features::kVizDisplayCompositor.name);
// VideoSurfaceLayer is enabled by default since 75. We don't support it.
@@ -690,6 +712,9 @@ WebEngineContext::WebEngineContext()
m_serviceManagerEnvironment = std::make_unique<content::ServiceManagerEnvironment>(content::BrowserTaskExecutor::CreateIOThread());
m_startupData = m_serviceManagerEnvironment->CreateBrowserStartupData();
+ if (base::FeatureList::IsEnabled(network::features::kNetworkService))
+ content::ForceInProcessNetworkService(true);
+
// Once the MessageLoop has been created, attach a top-level RunLoop.
m_runLoop.reset(new base::RunLoop);
m_runLoop->BeforeRun();
diff --git a/src/pdf/api/qpdfbookmarkmodel.h b/src/pdf/api/qpdfbookmarkmodel.h
new file mode 100644
index 000000000..503a29100
--- /dev/null
+++ b/src/pdf/api/qpdfbookmarkmodel.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDFBOOKMARKMODEL_H
+#define QPDFBOOKMARKMODEL_H
+
+#include <QtPdf/qtpdfglobal.h>
+#include <QAbstractItemModel>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfDocument;
+class QPdfBookmarkModelPrivate;
+
+class Q_PDF_EXPORT QPdfBookmarkModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QPdfDocument* document READ document WRITE setDocument NOTIFY documentChanged)
+ Q_PROPERTY(StructureMode structureMode READ structureMode WRITE setStructureMode NOTIFY structureModeChanged)
+
+public:
+ enum StructureMode
+ {
+ TreeMode,
+ ListMode
+ };
+ Q_ENUM(StructureMode)
+
+ enum Role
+ {
+ TitleRole = Qt::DisplayRole,
+ LevelRole = Qt::UserRole,
+ PageNumberRole
+ };
+ Q_ENUM(Role)
+
+ explicit QPdfBookmarkModel(QObject *parent = nullptr);
+
+ QPdfDocument* document() const;
+ void setDocument(QPdfDocument *document);
+
+ StructureMode structureMode() const;
+ void setStructureMode(StructureMode mode);
+
+ QVariant data(const QModelIndex &index, int role) const override;
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
+ QModelIndex parent(const QModelIndex &index) const override;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+ QHash<int, QByteArray> roleNames() const override;
+
+Q_SIGNALS:
+ void documentChanged(QPdfDocument *document);
+ void structureModeChanged(QPdfBookmarkModel::StructureMode structureMode);
+
+private:
+ Q_DECLARE_PRIVATE(QPdfBookmarkModel)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_documentStatusChanged())
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/pdf/api/qpdfdestination.h b/src/pdf/api/qpdfdestination.h
new file mode 100644
index 000000000..325863226
--- /dev/null
+++ b/src/pdf/api/qpdfdestination.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDFDESTINATION_H
+#define QPDFDESTINATION_H
+
+#include <QtPdf/qtpdfglobal.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qshareddata.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfDestinationPrivate;
+
+class Q_PDF_EXPORT QPdfDestination
+{
+ Q_GADGET
+ Q_PROPERTY(bool valid READ isValid)
+ Q_PROPERTY(int page READ page)
+ Q_PROPERTY(QPointF location READ location)
+ Q_PROPERTY(qreal zoom READ zoom)
+
+public:
+ QPdfDestination(const QPdfDestination &other);
+ ~QPdfDestination();
+ QPdfDestination &operator=(const QPdfDestination &other);
+ inline QPdfDestination &operator=(QPdfDestination &&other) noexcept { swap(other); return *this; }
+ void swap(QPdfDestination &other) noexcept { d.swap(other.d); }
+ bool isValid() const;
+ int page() const;
+ QPointF location() const;
+ qreal zoom() const;
+
+protected:
+ QPdfDestination();
+ QPdfDestination(int page, QPointF location, qreal zoom);
+ QPdfDestination(QPdfDestinationPrivate *d);
+ friend class QPdfDocument;
+ friend class QQuickPdfNavigationStack;
+
+protected:
+ QExplicitlySharedDataPointer<QPdfDestinationPrivate> d;
+};
+
+Q_PDF_EXPORT QDebug operator<<(QDebug, const QPdfDestination &);
+
+QT_END_NAMESPACE
+
+#endif // QPDFDESTINATION_H
diff --git a/src/pdf/api/qpdfdestination_p.h b/src/pdf/api/qpdfdestination_p.h
new file mode 100644
index 000000000..3520fb795
--- /dev/null
+++ b/src/pdf/api/qpdfdestination_p.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDFDESTINATION_P_H
+#define QPDFDESTINATION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QPointF>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfDestinationPrivate : public QSharedData
+{
+public:
+ QPdfDestinationPrivate() = default;
+ QPdfDestinationPrivate(int page, QPointF location, qreal zoom)
+ : page(page),
+ location(location),
+ zoom(zoom) { }
+
+ int page = -1;
+ QPointF location;
+ qreal zoom = 1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFDESTINATION_P_H
diff --git a/src/pdf/api/qpdfdocument.h b/src/pdf/api/qpdfdocument.h
new file mode 100644
index 000000000..40df181f4
--- /dev/null
+++ b/src/pdf/api/qpdfdocument.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDFDOCUMENT_H
+#define QPDFDOCUMENT_H
+
+#include <QtPdf/qtpdfglobal.h>
+#include <QImage>
+#include <QObject>
+#include <QtPdf/QPdfDocumentRenderOptions>
+#include "qpdfselection.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPdfDocumentPrivate;
+class QNetworkReply;
+
+class Q_PDF_EXPORT QPdfDocument : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(int pageCount READ pageCount NOTIFY pageCountChanged FINAL)
+ Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged FINAL)
+ Q_PROPERTY(Status status READ status NOTIFY statusChanged FINAL)
+
+public:
+ enum Status {
+ Null,
+ Loading,
+ Ready,
+ Unloading,
+ Error
+ };
+ Q_ENUM(Status)
+
+ enum DocumentError {
+ NoError,
+ UnknownError,
+ DataNotYetAvailableError,
+ FileNotFoundError,
+ InvalidFileFormatError,
+ IncorrectPasswordError,
+ UnsupportedSecuritySchemeError
+ };
+ Q_ENUM(DocumentError)
+
+ enum MetaDataField {
+ Title,
+ Subject,
+ Author,
+ Keywords,
+ Producer,
+ Creator,
+ CreationDate,
+ ModificationDate
+ };
+ Q_ENUM(MetaDataField)
+
+ explicit QPdfDocument(QObject *parent = nullptr);
+ ~QPdfDocument();
+
+ DocumentError load(const QString &fileName);
+
+ Status status() const;
+
+ void load(QIODevice *device);
+ void setPassword(const QString &password);
+ QString password() const;
+
+ QVariant metaData(MetaDataField field) const;
+
+ DocumentError error() const;
+
+ void close();
+
+ int pageCount() const;
+
+ QSizeF pageSize(int page) const;
+
+ QImage render(int page, QSize imageSize, QPdfDocumentRenderOptions options = QPdfDocumentRenderOptions());
+
+ Q_INVOKABLE QPdfSelection getSelection(int page, QPointF start, QPointF end);
+
+Q_SIGNALS:
+ void passwordChanged();
+ void passwordRequired();
+ void statusChanged(QPdfDocument::Status status);
+ void pageCountChanged(int pageCount);
+
+private:
+ friend class QPdfBookmarkModelPrivate;
+ friend class QPdfLinkModelPrivate;
+ friend class QPdfSearchModel;
+ friend class QPdfSearchModelPrivate;
+
+ Q_PRIVATE_SLOT(d, void _q_tryLoadingWithSizeFromContentHeader())
+ Q_PRIVATE_SLOT(d, void _q_copyFromSequentialSourceDevice())
+ QScopedPointer<QPdfDocumentPrivate> d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFDOCUMENT_H
diff --git a/src/pdf/api/qpdfdocument_p.h b/src/pdf/api/qpdfdocument_p.h
new file mode 100644
index 000000000..15d8b8259
--- /dev/null
+++ b/src/pdf/api/qpdfdocument_p.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDFDOCUMENT_P_H
+#define QPDFDOCUMENT_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 "qpdfdocument.h"
+
+#include "third_party/pdfium/public/fpdfview.h"
+#include "third_party/pdfium/public/fpdf_dataavail.h"
+
+#include <qbuffer.h>
+#include <qmutex.h>
+#include <qnetworkreply.h>
+#include <qpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfMutexLocker : public QMutexLocker
+{
+public:
+ QPdfMutexLocker();
+};
+
+class QPdfDocumentPrivate: public FPDF_FILEACCESS, public FX_FILEAVAIL, public FX_DOWNLOADHINTS
+{
+public:
+ QPdfDocumentPrivate();
+ ~QPdfDocumentPrivate();
+
+ QPdfDocument *q;
+
+ FPDF_AVAIL avail;
+ FPDF_DOCUMENT doc;
+ bool loadComplete;
+
+ QPointer<QIODevice> device;
+ QScopedPointer<QIODevice> ownDevice;
+ QBuffer asyncBuffer;
+ QPointer<QIODevice> sequentialSourceDevice;
+ QByteArray password;
+
+ QPdfDocument::Status status;
+ QPdfDocument::DocumentError lastError;
+ int pageCount;
+
+ void clear();
+
+ void load(QIODevice *device, bool ownDevice);
+ void loadAsync(QIODevice *device);
+
+ void _q_tryLoadingWithSizeFromContentHeader();
+ void initiateAsyncLoadWithTotalSizeKnown(quint64 totalSize);
+ void _q_copyFromSequentialSourceDevice();
+ void tryLoadDocument();
+ void checkComplete();
+ bool checkPageComplete(int page);
+ void setStatus(QPdfDocument::Status status);
+
+ static FPDF_BOOL fpdf_IsDataAvail(struct _FX_FILEAVAIL* pThis, size_t offset, size_t size);
+ static int fpdf_GetBlock(void* param, unsigned long position, unsigned char* pBuf, unsigned long size);
+ static void fpdf_AddSegment(struct _FX_DOWNLOADHINTS* pThis, size_t offset, size_t size);
+ void updateLastError();
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFDOCUMENT_P_H
diff --git a/src/pdf/api/qpdfdocumentrenderoptions.h b/src/pdf/api/qpdfdocumentrenderoptions.h
new file mode 100644
index 000000000..873be0085
--- /dev/null
+++ b/src/pdf/api/qpdfdocumentrenderoptions.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDFDOCUMENTRENDEROPTIONS_H
+#define QPDFDOCUMENTRENDEROPTIONS_H
+
+#include <QtPdf/qpdfnamespace.h>
+#include <QtCore/QObject>
+#include <QtCore/QRect>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfDocumentRenderOptions
+{
+public:
+ Q_DECL_CONSTEXPR QPdfDocumentRenderOptions() Q_DECL_NOTHROW : data(0) {}
+
+ Q_DECL_CONSTEXPR QPdf::Rotation rotation() const Q_DECL_NOTHROW { return static_cast<QPdf::Rotation>(bits.rotation); }
+ Q_DECL_RELAXED_CONSTEXPR void setRotation(QPdf::Rotation _rotation) Q_DECL_NOTHROW { bits.rotation = _rotation; }
+
+ Q_DECL_CONSTEXPR QPdf::RenderFlags renderFlags() const Q_DECL_NOTHROW { return static_cast<QPdf::RenderFlags>(bits.renderFlags); }
+ Q_DECL_RELAXED_CONSTEXPR void setRenderFlags(QPdf::RenderFlags _renderFlags) Q_DECL_NOTHROW { bits.renderFlags = _renderFlags; }
+
+ Q_DECL_CONSTEXPR QRect scaledClipRect() const Q_DECL_NOTHROW { return m_clipRect; }
+ Q_DECL_RELAXED_CONSTEXPR void setScaledClipRect(const QRect &r) Q_DECL_NOTHROW { m_clipRect = r; }
+
+ Q_DECL_CONSTEXPR QSize scaledSize() const Q_DECL_NOTHROW { return m_scaledSize; }
+ Q_DECL_RELAXED_CONSTEXPR void setScaledSize(const QSize &s) Q_DECL_NOTHROW { m_scaledSize = s; }
+
+private:
+ friend Q_DECL_CONSTEXPR inline bool operator==(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs) Q_DECL_NOTHROW;
+
+
+ struct Bits {
+ quint32 renderFlags : 8;
+ quint32 rotation : 3;
+ quint32 reserved : 21;
+ quint32 reserved2 : 32;
+ };
+
+ union {
+ Bits bits;
+ quint64 data;
+ };
+
+ QRect m_clipRect;
+ QSize m_scaledSize;
+};
+
+Q_DECLARE_TYPEINFO(QPdfDocumentRenderOptions, Q_PRIMITIVE_TYPE);
+
+Q_DECL_CONSTEXPR inline bool operator==(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs) Q_DECL_NOTHROW
+{
+ return lhs.data == rhs.data && lhs.m_clipRect == rhs.m_clipRect && lhs.m_scaledSize == rhs.m_scaledSize;
+}
+
+Q_DECL_CONSTEXPR inline bool operator!=(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs) Q_DECL_NOTHROW
+{
+ return !operator==(lhs, rhs);
+}
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QPdfDocumentRenderOptions)
+
+#endif // QPDFDOCUMENTRENDEROPTIONS_H
diff --git a/src/pdf/api/qpdflinkmodel_p.h b/src/pdf/api/qpdflinkmodel_p.h
new file mode 100644
index 000000000..cf9c0aad4
--- /dev/null
+++ b/src/pdf/api/qpdflinkmodel_p.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDFLINKMODEL_P_H
+#define QPDFLINKMODEL_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 "qtpdfglobal.h"
+#include "qpdfdocument.h"
+
+#include <QObject>
+#include <QAbstractListModel>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfLinkModelPrivate;
+
+class Q_PDF_EXPORT QPdfLinkModel : public QAbstractListModel
+{
+ Q_OBJECT
+ Q_PROPERTY(QPdfDocument *document READ document WRITE setDocument NOTIFY documentChanged)
+ Q_PROPERTY(int page READ page WRITE setPage NOTIFY pageChanged)
+
+public:
+ enum class Role : int {
+ Rect = Qt::UserRole,
+ Url,
+ Page,
+ Location,
+ Zoom,
+ _Count
+ };
+ Q_ENUM(Role)
+ explicit QPdfLinkModel(QObject *parent = nullptr);
+ ~QPdfLinkModel();
+
+ QPdfDocument *document() const;
+
+ QHash<int, QByteArray> roleNames() const override;
+ int rowCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+
+ int page() const;
+
+public Q_SLOTS:
+ void setDocument(QPdfDocument *document);
+ void setPage(int page);
+
+Q_SIGNALS:
+ void documentChanged();
+ void pageChanged(int page);
+
+private Q_SLOTS:
+ void onStatusChanged(QPdfDocument::Status status);
+
+private:
+ QHash<int, QByteArray> m_roleNames;
+ Q_DECLARE_PRIVATE(QPdfLinkModel)
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFLINKMODEL_P_H
diff --git a/src/pdf/api/qpdflinkmodel_p_p.h b/src/pdf/api/qpdflinkmodel_p_p.h
new file mode 100644
index 000000000..3e44f1651
--- /dev/null
+++ b/src/pdf/api/qpdflinkmodel_p_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDFLINKMODEL_P_P_H
+#define QPDFLINKMODEL_P_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 "qpdflinkmodel_p.h"
+#include <private/qabstractitemmodel_p.h>
+
+#include "third_party/pdfium/public/fpdfview.h"
+
+#include <QUrl>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfLinkModelPrivate: public QAbstractItemModelPrivate
+{
+ Q_DECLARE_PUBLIC(QPdfLinkModel)
+
+public:
+ QPdfLinkModelPrivate();
+
+ void update();
+
+ struct Link {
+ // where it is on the current page
+ QRectF rect;
+ int textStart = -1;
+ int textCharCount = 0;
+ // destination inside PDF
+ int page = -1; // -1 means look at the url instead
+ QPointF location;
+ qreal zoom = 1;
+ // web destination
+ QUrl url;
+
+ QString toString() const;
+ };
+
+ QPdfDocument *document = nullptr;
+ QVector<Link> links;
+ int page = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFLINKMODEL_P_P_H
diff --git a/src/pdf/api/qpdfnamespace.h b/src/pdf/api/qpdfnamespace.h
new file mode 100644
index 000000000..c8fd8a580
--- /dev/null
+++ b/src/pdf/api/qpdfnamespace.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDFNAMESPACE_H
+#define QPDFNAMESPACE_H
+
+#include <QObject>
+
+QT_BEGIN_NAMESPACE
+
+namespace QPdf {
+ Q_NAMESPACE
+
+ enum Rotation {
+ Rotate0,
+ Rotate90,
+ Rotate180,
+ Rotate270
+ };
+ Q_ENUM_NS(Rotation)
+
+ enum RenderFlag {
+ NoRenderFlags = 0x000,
+ RenderAnnotations = 0x001,
+ RenderOptimizedForLcd = 0x002,
+ RenderGrayscale = 0x004,
+ RenderForceHalftone = 0x008,
+ RenderTextAliased = 0x010,
+ RenderImageAliased = 0x020,
+ RenderPathAliased = 0x040
+ };
+ Q_FLAG_NS(RenderFlag)
+ Q_DECLARE_FLAGS(RenderFlags, RenderFlag)
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QPdf::RenderFlags)
+
+QT_END_NAMESPACE
+#endif
diff --git a/src/pdf/api/qpdfpagenavigation.h b/src/pdf/api/qpdfpagenavigation.h
new file mode 100644
index 000000000..8da809646
--- /dev/null
+++ b/src/pdf/api/qpdfpagenavigation.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDFPAGENAVIGATION_H
+#define QPDFPAGENAVIGATION_H
+
+#include <QtPdf/qtpdfglobal.h>
+#include <QObject>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfDocument;
+class QPdfPageNavigationPrivate;
+
+class Q_PDF_EXPORT QPdfPageNavigation : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QPdfDocument* document READ document WRITE setDocument NOTIFY documentChanged)
+
+ Q_PROPERTY(int currentPage READ currentPage WRITE setCurrentPage NOTIFY currentPageChanged)
+ Q_PROPERTY(int pageCount READ pageCount NOTIFY pageCountChanged)
+ Q_PROPERTY(bool canGoToPreviousPage READ canGoToPreviousPage NOTIFY canGoToPreviousPageChanged)
+ Q_PROPERTY(bool canGoToNextPage READ canGoToNextPage NOTIFY canGoToNextPageChanged)
+
+public:
+ explicit QPdfPageNavigation(QObject *parent = nullptr);
+ ~QPdfPageNavigation();
+
+ QPdfDocument* document() const;
+ void setDocument(QPdfDocument *document);
+
+ int currentPage() const;
+ void setCurrentPage(int currentPage);
+
+ int pageCount() const;
+
+ bool canGoToPreviousPage() const;
+ bool canGoToNextPage() const;
+
+public Q_SLOTS:
+ void goToPreviousPage();
+ void goToNextPage();
+
+Q_SIGNALS:
+ void documentChanged(QPdfDocument *document);
+ void currentPageChanged(int currentPage);
+ void pageCountChanged(int pageCount);
+ void canGoToPreviousPageChanged(bool canGo);
+ void canGoToNextPageChanged(bool canGo);
+
+private:
+ Q_DECLARE_PRIVATE(QPdfPageNavigation)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/pdf/api/qpdfpagerenderer.h b/src/pdf/api/qpdfpagerenderer.h
new file mode 100644
index 000000000..de6704480
--- /dev/null
+++ b/src/pdf/api/qpdfpagerenderer.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDFPAGERENDERER_H
+#define QPDFPAGERENDERER_H
+
+#include <QtPdf/qtpdfglobal.h>
+#include <QObject>
+#include <QtPdf/QPdfDocumentRenderOptions>
+#include <QSize>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfDocument;
+class QPdfPageRendererPrivate;
+
+class Q_PDF_EXPORT QPdfPageRenderer : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QPdfDocument* document READ document WRITE setDocument NOTIFY documentChanged)
+ Q_PROPERTY(RenderMode renderMode READ renderMode WRITE setRenderMode NOTIFY renderModeChanged)
+
+public:
+ enum RenderMode
+ {
+ MultiThreadedRenderMode,
+ SingleThreadedRenderMode
+ };
+ Q_ENUM(RenderMode)
+
+ explicit QPdfPageRenderer(QObject *parent = nullptr);
+ ~QPdfPageRenderer();
+
+ RenderMode renderMode() const;
+ void setRenderMode(RenderMode mode);
+
+ QPdfDocument* document() const;
+ void setDocument(QPdfDocument *document);
+
+ quint64 requestPage(int pageNumber, QSize imageSize,
+ QPdfDocumentRenderOptions options = QPdfDocumentRenderOptions());
+
+Q_SIGNALS:
+ void documentChanged(QPdfDocument *document);
+ void renderModeChanged(RenderMode renderMode);
+
+ void pageRendered(int pageNumber, QSize imageSize, const QImage &image,
+ QPdfDocumentRenderOptions options, quint64 requestId);
+
+private:
+ Q_DECLARE_PRIVATE(QPdfPageRenderer)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/pdf/api/qpdfsearchmodel.h b/src/pdf/api/qpdfsearchmodel.h
new file mode 100644
index 000000000..cc91e214a
--- /dev/null
+++ b/src/pdf/api/qpdfsearchmodel.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDFSEARCHMODEL_H
+#define QPDFSEARCHMODEL_H
+
+#include "qtpdfglobal.h"
+#include "qpdfdocument.h"
+#include "qpdfsearchresult.h"
+
+#include <QtCore/qabstractitemmodel.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfSearchModelPrivate;
+
+class Q_PDF_EXPORT QPdfSearchModel : public QAbstractListModel
+{
+ Q_OBJECT
+ Q_PROPERTY(QPdfDocument *document READ document WRITE setDocument NOTIFY documentChanged)
+ Q_PROPERTY(QString searchString READ searchString WRITE setSearchString NOTIFY searchStringChanged)
+
+public:
+ enum class Role : int {
+ Page = Qt::UserRole,
+ IndexOnPage,
+ Location,
+ Context,
+ _Count
+ };
+ Q_ENUM(Role)
+ explicit QPdfSearchModel(QObject *parent = nullptr);
+ ~QPdfSearchModel();
+
+ QVector<QPdfSearchResult> resultsOnPage(int page) const;
+ QPdfSearchResult resultAtIndex(int index) const;
+
+ QPdfDocument *document() const;
+ QString searchString() const;
+
+ QHash<int, QByteArray> roleNames() const override;
+ int rowCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+
+public Q_SLOTS:
+ void setSearchString(QString searchString);
+ void setDocument(QPdfDocument *document);
+
+Q_SIGNALS:
+ void documentChanged();
+ void searchStringChanged();
+
+protected:
+ void updatePage(int page);
+ void timerEvent(QTimerEvent *event) override;
+
+private:
+ QHash<int, QByteArray> m_roleNames;
+ Q_DECLARE_PRIVATE(QPdfSearchModel)
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFSEARCHMODEL_H
diff --git a/src/pdf/api/qpdfsearchmodel_p.h b/src/pdf/api/qpdfsearchmodel_p.h
new file mode 100644
index 000000000..2a23706b2
--- /dev/null
+++ b/src/pdf/api/qpdfsearchmodel_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDFSEARCHMODEL_P_H
+#define QPDFSEARCHMODEL_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 "qpdfsearchmodel.h"
+#include "qpdfsearchresult_p.h"
+#include <private/qabstractitemmodel_p.h>
+
+#include "third_party/pdfium/public/fpdfview.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPdfSearchModelPrivate : public QAbstractItemModelPrivate
+{
+ Q_DECLARE_PUBLIC(QPdfSearchModel)
+
+public:
+ QPdfSearchModelPrivate();
+ void clearResults();
+ bool doSearch(int page);
+
+ struct PageAndIndex {
+ int page;
+ int index;
+ };
+ PageAndIndex pageAndIndexForResult(int resultIndex);
+ int rowsBeforePage(int page);
+
+ QPdfDocument *document = nullptr;
+ QString searchString;
+ QVector<bool> pagesSearched;
+ QVector<QVector<QPdfSearchResult>> searchResults;
+ int rowCountSoFar = 0;
+ int updateTimerId = -1;
+ int nextPageToUpdate = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFSEARCHMODEL_P_H
diff --git a/src/pdf/api/qpdfsearchresult.h b/src/pdf/api/qpdfsearchresult.h
new file mode 100644
index 000000000..db7af3dd9
--- /dev/null
+++ b/src/pdf/api/qpdfsearchresult.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDFSEARCHRESULT_H
+#define QPDFSEARCHRESULT_H
+
+#include "qpdfdestination.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qvector.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfSearchResultPrivate;
+
+class Q_PDF_EXPORT QPdfSearchResult : public QPdfDestination
+{
+ Q_GADGET
+ Q_PROPERTY(QString context READ context)
+ Q_PROPERTY(QVector<QRectF> rectangles READ rectangles)
+
+public:
+ QPdfSearchResult();
+ ~QPdfSearchResult() {}
+
+ QString context() const;
+ QVector<QRectF> rectangles() const;
+
+private:
+ QPdfSearchResult(int page, QVector<QRectF> rects, QString context);
+ QPdfSearchResult(QPdfSearchResultPrivate *d);
+ friend class QPdfDocument;
+ friend class QPdfSearchModelPrivate;
+ friend class QQuickPdfNavigationStack;
+};
+
+Q_PDF_EXPORT QDebug operator<<(QDebug, const QPdfSearchResult &);
+
+QT_END_NAMESPACE
+
+#endif // QPDFSEARCHRESULT_H
diff --git a/src/pdf/api/qpdfsearchresult_p.h b/src/pdf/api/qpdfsearchresult_p.h
new file mode 100644
index 000000000..a0f8e4457
--- /dev/null
+++ b/src/pdf/api/qpdfsearchresult_p.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDFSEARCHRESULT_P_H
+#define QPDFSEARCHRESULT_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 "qpdfdestination_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPdfSearchResultPrivate : public QPdfDestinationPrivate
+{
+public:
+ QPdfSearchResultPrivate() = default;
+ QPdfSearchResultPrivate(int page, QVector<QRectF> rects, QString context) :
+ QPdfDestinationPrivate(page, rects.first().topLeft(), 0),
+ context(context),
+ rects(rects) {}
+
+ QString context;
+ QVector<QRectF> rects;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFSEARCHRESULT_P_H
diff --git a/src/pdf/api/qpdfselection.h b/src/pdf/api/qpdfselection.h
new file mode 100644
index 000000000..900b203cf
--- /dev/null
+++ b/src/pdf/api/qpdfselection.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDFSELECTION_H
+#define QPDFSELECTION_H
+
+#include <QtPdf/qtpdfglobal.h>
+#include <QClipboard>
+#include <QExplicitlySharedDataPointer>
+#include <QObject>
+#include <QPolygonF>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfSelectionPrivate;
+
+class Q_PDF_EXPORT QPdfSelection
+{
+ Q_GADGET
+ Q_PROPERTY(bool valid READ isValid)
+ Q_PROPERTY(QVector<QPolygonF> bounds READ bounds)
+ Q_PROPERTY(QString text READ text)
+
+public:
+ QPdfSelection(const QPdfSelection &other);
+ ~QPdfSelection();
+ QPdfSelection &operator=(const QPdfSelection &other);
+ inline QPdfSelection &operator=(QPdfSelection &&other) noexcept { swap(other); return *this; }
+ void swap(QPdfSelection &other) noexcept { d.swap(other.d); }
+ bool isValid() const;
+ QVector<QPolygonF> bounds() const;
+ QString text() const;
+#if QT_CONFIG(clipboard)
+ void copyToClipboard(QClipboard::Mode mode = QClipboard::Clipboard) const;
+#endif
+
+private:
+ QPdfSelection();
+ QPdfSelection(const QString &text, QVector<QPolygonF> bounds);
+ QPdfSelection(QPdfSelectionPrivate *d);
+ friend class QPdfDocument;
+ friend class QQuickPdfSelection;
+
+private:
+ QExplicitlySharedDataPointer<QPdfSelectionPrivate> d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFSELECTION_H
diff --git a/src/pdf/api/qpdfselection_p.h b/src/pdf/api/qpdfselection_p.h
new file mode 100644
index 000000000..37145f7f9
--- /dev/null
+++ b/src/pdf/api/qpdfselection_p.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDFSELECTION_P_H
+#define QPDFSELECTION_P_H
+
+#include <QPolygonF>
+#include <QVector>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfSelectionPrivate : public QSharedData
+{
+public:
+ QPdfSelectionPrivate() = default;
+ QPdfSelectionPrivate(const QString &text, QVector<QPolygonF> bounds)
+ : text(text),
+ bounds(bounds) { }
+
+ QString text;
+ QVector<QPolygonF> bounds;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFSELECTION_P_H
diff --git a/src/pdf/api/qtpdfglobal.h b/src/pdf/api/qtpdfglobal.h
new file mode 100644
index 000000000..34cfc46d9
--- /dev/null
+++ b/src/pdf/api/qtpdfglobal.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTPDFGLOBAL_H
+#define QTPDFGLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef Q_PDF_EXPORT
+# ifndef QT_STATIC
+# if defined(QT_BUILD_PDF_LIB)
+# define Q_PDF_EXPORT Q_DECL_EXPORT
+# else
+# define Q_PDF_EXPORT Q_DECL_IMPORT
+# endif
+# else
+# define Q_PDF_EXPORT
+# endif
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QTPDFGLOBAL_H
+
diff --git a/src/pdf/configure.json b/src/pdf/configure.json
new file mode 100644
index 000000000..1f21183a2
--- /dev/null
+++ b/src/pdf/configure.json
@@ -0,0 +1,64 @@
+{
+ "module": "pdf",
+ "depends" : [ "buildtools-private" ],
+ "testDir": "../../config.tests",
+ "condition": "features.build-qtpdf",
+ "libraries": {
+ },
+ "tests": {
+ },
+ "features": {
+ "pdf-v8": {
+ "label": "Support V8",
+ "purpose": "Enables javascript support.",
+ "autoDetect": "false",
+ "condition": "!config.ios",
+ "output": ["privateFeature" ]
+ },
+ "pdf-xfa": {
+ "label": "Support XFA",
+ "purpose": "Enables XFA support.",
+ "condition": "features.pdf-v8",
+ "output": ["privateFeature" ]
+ },
+ "pdf-xfa-bmp": {
+ "label": "Support XFA-BMP",
+ "purpose": "Enables XFA-BMP support.",
+ "condition": "features.pdf-xfa",
+ "output": ["privateFeature" ]
+ },
+ "pdf-xfa-gif": {
+ "label": "Support XFA-GIF",
+ "purpose": "Enables XFA-GIF support.",
+ "condition": "features.pdf-xfa",
+ "output": ["privateFeature" ]
+ },
+ "pdf-xfa-png": {
+ "label": "Support XFA-PNG",
+ "purpose": "Enables XFA-PNG support.",
+ "condition": "features.pdf-xfa",
+ "output": ["privateFeature" ]
+ },
+ "pdf-xfa-tiff": {
+ "label": "Support XFA-TIFF",
+ "purpose": "Enables XFA-TIFF support.",
+ "condition": "features.pdf-xfa",
+ "output": ["privateFeature" ]
+ }
+ },
+ "report": [
+ ],
+ "summary": [
+ {
+ "section": "Qt PDF",
+ "entries": [
+ "pdf-v8",
+ "pdf-xfa",
+ "pdf-xfa-bmp",
+ "pdf-xfa-gif",
+ "pdf-xfa-png",
+ "pdf-xfa-tiff"
+ ]
+ }
+ ]
+}
diff --git a/src/pdf/doc/qtpdf.qdocconf b/src/pdf/doc/qtpdf.qdocconf
new file mode 100644
index 000000000..7a77105c9
--- /dev/null
+++ b/src/pdf/doc/qtpdf.qdocconf
@@ -0,0 +1,58 @@
+include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
+
+project = QtPdf
+description = Qt Pdf Reference Documentation
+version = $QT_VERSION
+
+qhp.projects = QtPdf
+
+qhp.QtPdf.file = qtpdf.qhp
+qhp.QtPdf.namespace = org.qt-project.qtpdf.$QT_VERSION_TAG
+qhp.QtPdf.virtualFolder = qtpdf
+qhp.QtPdf.indexTitle = Qt PDF
+qhp.QtPdf.indexRoot =
+
+qhp.QtPdf.filterAttributes = qtpdf $QT_VERSION qtrefdoc
+qhp.QtPdf.customFilters.Qt.name = QtPdf $QT_VERSION
+qhp.QtPdf.customFilters.Qt.filterAttributes = qtpdf $QT_VERSION
+
+qhp.QtPdf.subprojects = classes qmltypes examples
+
+qhp.QtPdf.subprojects.classes.title = C++ Classes
+qhp.QtPdf.subprojects.classes.indexTitle = Qt PDF C++ Classes
+qhp.QtPdf.subprojects.classes.selectors = class fake:headerfile
+qhp.QtPdf.subprojects.classes.sortPages = true
+
+qhp.QtPdf.subprojects.qmltypes.title = QML Types
+qhp.QtPdf.subprojects.qmltypes.indexTitle = Qt Quick PDF QML Types
+qhp.QtPdf.subprojects.qmltypes.selectors = qmltype
+qhp.QtPdf.subprojects.qmltypes.sortPages = true
+
+qhp.QtPdf.subprojects.examples.title = Examples
+qhp.QtPdf.subprojects.examples.indexTitle = Qt PDF Examples
+qhp.QtPdf.subprojects.examples.selectors = doc:example
+qhp.QtPdf.subprojects.examples.sortPages = true
+
+depends += qtcore \
+ qtwidgets \
+ qtgui \
+ qtdoc \
+ qmake \
+ qtdesigner \
+ qtquick \
+ qtcmake
+
+headerdirs += ../api \
+ ../quick
+
+sourcedirs += .. \
+ ../quick
+
+exampledirs += ../../../examples/pdfwidgets \
+ snippets/
+
+imagedirs += images
+
+navigation.landingpage = "Qt PDF"
+navigation.cppclassespage = "Qt PDF C++ Classes"
+navigation.qmltypespage = "Qt WebEngine QML Types"
diff --git a/src/pdf/doc/snippets/qtpdf-build.cmake b/src/pdf/doc/snippets/qtpdf-build.cmake
new file mode 100644
index 000000000..d46b9c3ee
--- /dev/null
+++ b/src/pdf/doc/snippets/qtpdf-build.cmake
@@ -0,0 +1,2 @@
+find_package(Qt5 COMPONENTS Pdf REQUIRED)
+target_link_libraries(mytarget Qt5::Pdf)
diff --git a/src/pdf/doc/snippets/qtpdf_build_snippet.qdoc b/src/pdf/doc/snippets/qtpdf_build_snippet.qdoc
new file mode 100644
index 000000000..25593b1ee
--- /dev/null
+++ b/src/pdf/doc/snippets/qtpdf_build_snippet.qdoc
@@ -0,0 +1,35 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [0]
+QT += pdf
+//! [0]
+
+
+//! [1]
+#include <QtPdf>
+//! [1]
diff --git a/src/pdf/doc/src/qtpdf-examples.qdoc b/src/pdf/doc/src/qtpdf-examples.qdoc
new file mode 100644
index 000000000..9daa0e7f8
--- /dev/null
+++ b/src/pdf/doc/src/qtpdf-examples.qdoc
@@ -0,0 +1,37 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \group qtpdf-examples
+ \ingroup all-examples
+
+ \title Qt PDF Examples
+ \brief Using the classes and types in the Qt PDF module.
+
+ The following examples illustrate how to use the C++ classes and QML types
+ in the \l{Qt PDF} module to render PDF documents.
+*/
diff --git a/src/pdf/doc/src/qtpdf-index.qdoc b/src/pdf/doc/src/qtpdf-index.qdoc
new file mode 100644
index 000000000..b32787eb5
--- /dev/null
+++ b/src/pdf/doc/src/qtpdf-index.qdoc
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \page qtpdf-index.html
+ \title Qt PDF
+
+ \brief Renders pages from PDF documents.
+
+ The Qt PDF module contains classes and functions for rendering
+ PDF documents. The \l QPdfDocument class loads a PDF document
+ and renders pages from it according to the options provided by
+ the \l QPdfDocumentRenderOptions class. The \l QPdfPageRenderer
+ class manages a queue that collects all render requests. The
+ \l QPdfPageNavigation class handles the navigation through a
+ PDF document.
+
+ \include module-use.qdocinc using qt module
+ \quotefile qtpdf-build.cmake
+
+ See also the \l{Build with CMake} overview.
+
+ \section2 Building with qmake
+
+ To include the definitions of the module's classes, use the
+ following directive:
+
+ \snippet qtpdf_build_snippet.qdoc 1
+
+ To link against the module, add this line to your qmake project file:
+
+ \snippet qtpdf_build_snippet.qdoc 0
+
+ \section1 Articles and Guides
+
+ \list
+ \li \l{Qt PDF Overview}
+ \endlist
+
+ \section1 Examples
+
+ \list
+ \li \l{Qt PDF Examples}
+ \endlist
+
+ \section1 API Reference
+
+ \list
+ \li \l{Qt PDF C++ Classes}
+ \li \l{Qt Quick PDF QML Types}
+ \endlist
+*/
diff --git a/src/pdf/doc/src/qtpdf-module.qdoc b/src/pdf/doc/src/qtpdf-module.qdoc
new file mode 100644
index 000000000..4170deb38
--- /dev/null
+++ b/src/pdf/doc/src/qtpdf-module.qdoc
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+/*!
+ \module QtPdf
+ \title Qt PDF C++ Classes
+ \brief Renders pages from PDF documents.
+ \since 5.14
+ \ingroup qtwebengine-modules
+ \ingroup modules
+
+ The Qt PDF module contains classes and functions for rendering
+ PDF documents.
+
+ To include the definitions of the module's classes, use the
+ following directive:
+
+ \snippet qtpdf_build_snippet.qdoc 1
+
+ \if !defined(qtforpython)
+ To link against the module, add this line to your qmake project file:
+
+ \snippet qtpdf_build_snippet.qdoc 0
+ \endif
+*/
diff --git a/src/pdf/gn_run.pro b/src/pdf/gn_run.pro
new file mode 100644
index 000000000..7fd4d4ffc
--- /dev/null
+++ b/src/pdf/gn_run.pro
@@ -0,0 +1,70 @@
+include($$QTWEBENGINE_OUT_ROOT/src/buildtools/qtbuildtools-config.pri)
+QT_FOR_CONFIG += buildtools-private
+
+TEMPLATE = aux
+
+qtConfig(debug_and_release): CONFIG += debug_and_release build_all
+
+qtConfig(webengine-system-ninja) {
+ QT_TOOL.ninja.binary = ninja
+} else {
+ QT_TOOL.ninja.binary = $$shell_quote($$shell_path($$ninjaPath()))
+}
+
+win32 {
+ # Add the gnuwin32/bin subdir of qt5.git to PATH. Needed for calling bison and friends.
+ gnuwin32path.name = PATH
+ gnuwin32path.value = $$shell_path($$clean_path($$QTWEBENGINE_ROOT/../gnuwin32/bin))
+ gnuwin32path.CONFIG += prepend
+ exists($$gnuwin32path.value): QT_TOOL_ENV = gnuwin32path
+}
+
+qtPrepareTool(NINJA, ninja)
+QT_TOOL_ENV =
+
+build_pass|!debug_and_release {
+ gn_binary = gn
+
+ runninja.target = run_ninja
+
+ # fixme: refine args
+ gn_args = $$gnArgs()
+
+ include($$QTWEBENGINE_ROOT/src/buildtools/config/pdf.pri)
+
+ # fixme: qtwebengine_target
+ gn_args += "qtwebengine_target=\"$$system_path($$OUT_PWD/$$getConfigDir()):QtPdf\""
+
+ # fixme:
+ !qtConfig(webengine-system-gn) {
+ gn_binary = $$system_quote($$system_path($$gnPath()))
+ }
+
+ gn_args = $$system_quote($$gn_args)
+ gn_src_root = $$system_quote($$system_path($$QTWEBENGINE_ROOT/$$getChromiumSrcDir()))
+ gn_build_root = $$system_quote($$system_path($$OUT_PWD/$$getConfigDir()))
+ gn_python = "--script-executable=$$pythonPathForSystem()"
+ gn_run = $$gn_binary gen $$gn_build_root $$gn_python --args=$$gn_args --root=$$gn_src_root
+
+ message("Running: $$gn_run ")
+ !system($$gn_run) {
+ error("GN run error!")
+ }
+
+ ninjaflags = $$(NINJAFLAGS)
+ isEmpty(ninjaflags):!silent: ninjaflags = "-v"
+
+ runninja.commands = $$NINJA $$ninjaflags -C $$gn_build_root QtPdf
+ QMAKE_EXTRA_TARGETS += runninja
+
+ build_pass:build_all: default_target.target = all
+ else: default_target.target = first
+ default_target.depends = runninja
+ QMAKE_EXTRA_TARGETS += default_target
+}
+
+!build_pass:debug_and_release {
+ # Special GNU make target for the meta Makefile that ensures that our debug and release Makefiles won't both run ninja in parallel.
+ notParallel.target = .NOTPARALLEL
+ QMAKE_EXTRA_TARGETS += notParallel
+}
diff --git a/src/pdf/jsbridge.cpp b/src/pdf/jsbridge.cpp
new file mode 100644
index 000000000..95b813929
--- /dev/null
+++ b/src/pdf/jsbridge.cpp
@@ -0,0 +1,38 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "fpdfsdk/javascript/JS_Runtime_Stub.cpp"
+
diff --git a/src/pdf/pdf.pro b/src/pdf/pdf.pro
new file mode 100644
index 000000000..c5513ce93
--- /dev/null
+++ b/src/pdf/pdf.pro
@@ -0,0 +1,16 @@
+TEMPLATE = subdirs
+pdfcore.file = pdfcore.pro
+pdfcore_generator.file = pdfcore_generator.pro
+gn_run.file = gn_run.pro
+
+gn_run.depends = pdfcore_generator
+pdfcore.depends = gn_run
+quick.depends = pdfcore
+
+SUBDIRS += \
+ pdfcore_generator \
+ gn_run \
+ pdfcore \
+ quick
+
+
diff --git a/src/pdf/pdfcore.pro b/src/pdf/pdfcore.pro
new file mode 100644
index 000000000..c87722b7e
--- /dev/null
+++ b/src/pdf/pdfcore.pro
@@ -0,0 +1,93 @@
+TARGET = QtPdf
+MODULE = pdf
+
+QT += gui core core-private
+QT_PRIVATE += network
+
+TEMPLATE = lib
+
+INCLUDEPATH += $$QTWEBENGINE_ROOT/src/pdf
+CHROMIUM_SRC_DIR = $$QTWEBENGINE_ROOT/$$getChromiumSrcDir()
+CHROMIUM_GEN_DIR = $$OUT_PWD/../$$getConfigDir()/gen
+INCLUDEPATH += $$QTWEBENGINE_ROOT/src/pdf \
+ $$CHROMIUM_GEN_DIR \
+ $$CHROMIUM_SRC_DIR \
+ api
+
+DEFINES += QT_BUILD_PDF_LIB
+win32: DEFINES += NOMINMAX
+
+linking_pri = $$OUT_PWD/$$getConfigDir()/$${TARGET}.pri
+!include($$linking_pri) {
+ error("Could not find the linking information that gn should have generated.")
+}
+
+isEmpty(NINJA_OBJECTS): error("Missing object files from QtPdf linking pri.")
+isEmpty(NINJA_LFLAGS): error("Missing linker flags from QtPdf linking pri")
+isEmpty(NINJA_LIBS): error("Missing library files from QtPdf linking pri")
+
+NINJA_OBJECTS = $$eval($$list($$NINJA_OBJECTS))
+RSP_FILE = $$OUT_PWD/$$getConfigDir()/$${TARGET}.rsp
+for(object, NINJA_OBJECTS): RSP_CONTENT += $$object
+write_file($$RSP_FILE, RSP_CONTENT)
+
+macos:LIBS_PRIVATE += -Wl,-filelist,$$shell_quote($$RSP_FILE)
+linux:LIBS_PRIVATE += @$$RSP_FILE
+
+# QTBUG-58710 add main rsp file on windows
+win32:QMAKE_LFLAGS += @$$RSP_FILE
+
+!isEmpty(NINJA_ARCHIVES) {
+ linux: LIBS_PRIVATE += -Wl,--start-group $$NINJA_ARCHIVES -Wl,--end-group
+ else: LIBS_PRIVATE += $$NINJA_ARCHIVES
+}
+
+LIBS_PRIVATE += $$NINJA_LIB_DIRS $$NINJA_LIBS
+
+QMAKE_DOCS = $$PWD/doc/qtpdf.qdocconf
+
+gcc {
+ QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-parameter
+}
+
+msvc {
+ QMAKE_CXXFLAGS_WARN_ON += -wd"4100"
+}
+
+ios: OBJECTS += $$NINJA_OBJECTS
+
+SOURCES += \
+ qpdfbookmarkmodel.cpp \
+ qpdfdestination.cpp \
+ qpdfdocument.cpp \
+ qpdflinkmodel.cpp \
+ qpdfpagenavigation.cpp \
+ qpdfpagerenderer.cpp \
+ qpdfsearchmodel.cpp \
+ qpdfsearchresult.cpp \
+ qpdfselection.cpp \
+
+# all "public" headers must be in "api" for sync script and to hide auto generated headers
+# by Chromium in case of in-source build
+
+HEADERS += \
+ api/qpdfbookmarkmodel.h \
+ api/qpdfdestination.h \
+ api/qpdfdestination_p.h \
+ api/qpdfdocument.h \
+ api/qpdfdocument_p.h \
+ api/qpdfdocumentrenderoptions.h \
+ api/qtpdfglobal.h \
+ api/qpdflinkmodel_p.h \
+ api/qpdflinkmodel_p_p.h \
+ api/qpdfnamespace.h \
+ api/qpdfpagenavigation.h \
+ api/qpdfpagerenderer.h \
+ api/qpdfsearchmodel.h \
+ api/qpdfsearchmodel_p.h \
+ api/qpdfsearchresult.h \
+ api/qpdfsearchresult_p.h \
+ api/qpdfselection.h \
+ api/qpdfselection_p.h \
+
+load(qt_module)
diff --git a/src/pdf/pdfcore_generator.pro b/src/pdf/pdfcore_generator.pro
new file mode 100644
index 000000000..c8eb13b81
--- /dev/null
+++ b/src/pdf/pdfcore_generator.pro
@@ -0,0 +1,15 @@
+qtConfig(debug_and_release): CONFIG += debug_and_release
+
+TARGET = QtPdf
+TEMPLATE = lib
+CONFIG = gn_generator $$CONFIG
+ios: CONFIG -=static # note we still do static on ios when linking
+GN_SRC_DIR = $$PWD
+GN_FILE = $$OUT_PWD/$$getConfigDir()/BUILD.gn
+GN_FIND_MOCABLES_SCRIPT = $$shell_path($$QTWEBENGINE_ROOT/tools/scripts/gn_find_mocables.py)
+GN_RUN_BINARY_SCRIPT = $$shell_path($$QTWEBENGINE_ROOT/tools/scripts/gn_run_binary.py)
+GN_IMPORTS = $$PWD/qtpdf.gni
+GN_CREATE_PRI = true
+QMAKE_INTERNAL_INCLUDED_FILES = $$GN_IMPORTS $$GN_INCLUDES $$GN_FILE
+
+
diff --git a/src/pdf/qpdfbookmarkmodel.cpp b/src/pdf/qpdfbookmarkmodel.cpp
new file mode 100644
index 000000000..e648657ba
--- /dev/null
+++ b/src/pdf/qpdfbookmarkmodel.cpp
@@ -0,0 +1,363 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpdfbookmarkmodel.h"
+
+#include "qpdfdocument.h"
+#include "qpdfdocument_p.h"
+
+#include "third_party/pdfium/public/fpdf_doc.h"
+#include "third_party/pdfium/public/fpdfview.h"
+
+#include <QPointer>
+#include <QScopedPointer>
+#include <private/qabstractitemmodel_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class BookmarkNode
+{
+public:
+ explicit BookmarkNode(BookmarkNode *parentNode = nullptr)
+ : m_parentNode(parentNode)
+ , m_level(0)
+ , m_pageNumber(0)
+ {
+ }
+
+ ~BookmarkNode()
+ {
+ clear();
+ }
+
+ void clear()
+ {
+ qDeleteAll(m_childNodes);
+ m_childNodes.clear();
+ }
+
+ void appendChild(BookmarkNode *child)
+ {
+ m_childNodes.append(child);
+ }
+
+ BookmarkNode *child(int row) const
+ {
+ return m_childNodes.at(row);
+ }
+
+ int childCount() const
+ {
+ return m_childNodes.count();
+ }
+
+ int row() const
+ {
+ if (m_parentNode)
+ return m_parentNode->m_childNodes.indexOf(const_cast<BookmarkNode*>(this));
+
+ return 0;
+ }
+
+ BookmarkNode *parentNode() const
+ {
+ return m_parentNode;
+ }
+
+ QString title() const
+ {
+ return m_title;
+ }
+
+ void setTitle(const QString &title)
+ {
+ m_title = title;
+ }
+
+ int level() const
+ {
+ return m_level;
+ }
+
+ void setLevel(int level)
+ {
+ m_level = level;
+ }
+
+ int pageNumber() const
+ {
+ return m_pageNumber;
+ }
+
+ void setPageNumber(int pageNumber)
+ {
+ m_pageNumber = pageNumber;
+ }
+
+private:
+ QVector<BookmarkNode*> m_childNodes;
+ BookmarkNode *m_parentNode;
+
+ QString m_title;
+ int m_level;
+ int m_pageNumber;
+};
+
+
+class QPdfBookmarkModelPrivate : public QAbstractItemModelPrivate
+{
+public:
+ QPdfBookmarkModelPrivate()
+ : QAbstractItemModelPrivate()
+ , m_rootNode(new BookmarkNode(nullptr))
+ , m_document(nullptr)
+ , m_structureMode(QPdfBookmarkModel::TreeMode)
+ {
+ }
+
+ void rebuild()
+ {
+ Q_Q(QPdfBookmarkModel);
+
+ const bool documentAvailable = (m_document && m_document->status() == QPdfDocument::Ready);
+
+ if (documentAvailable) {
+ q->beginResetModel();
+ m_rootNode->clear();
+ QPdfMutexLocker lock;
+ appendChildNode(m_rootNode.data(), nullptr, 0, m_document->d->doc);
+ lock.unlock();
+ q->endResetModel();
+ } else {
+ if (m_rootNode->childCount() == 0) {
+ return;
+ } else {
+ q->beginResetModel();
+ m_rootNode->clear();
+ q->endResetModel();
+ }
+ }
+ }
+
+ void appendChildNode(BookmarkNode *parentBookmarkNode, FPDF_BOOKMARK parentBookmark, int level, FPDF_DOCUMENT document)
+ {
+ FPDF_BOOKMARK bookmark = FPDFBookmark_GetFirstChild(document, parentBookmark);
+
+ while (bookmark) {
+ BookmarkNode *childBookmarkNode = nullptr;
+
+ if (m_structureMode == QPdfBookmarkModel::TreeMode) {
+ childBookmarkNode = new BookmarkNode(parentBookmarkNode);
+ parentBookmarkNode->appendChild(childBookmarkNode);
+ } else if (m_structureMode == QPdfBookmarkModel::ListMode) {
+ childBookmarkNode = new BookmarkNode(m_rootNode.data());
+ m_rootNode->appendChild(childBookmarkNode);
+ }
+
+ const int titleLength = int(FPDFBookmark_GetTitle(bookmark, nullptr, 0));
+
+ QVector<ushort> titleBuffer(titleLength);
+ FPDFBookmark_GetTitle(bookmark, titleBuffer.data(), quint32(titleBuffer.length()));
+
+ const FPDF_DEST dest = FPDFBookmark_GetDest(document, bookmark);
+ const int pageNumber = FPDFDest_GetDestPageIndex(document, dest);
+
+ childBookmarkNode->setTitle(QString::fromUtf16(titleBuffer.data()));
+ childBookmarkNode->setLevel(level);
+ childBookmarkNode->setPageNumber(pageNumber);
+
+ // recurse down
+ appendChildNode(childBookmarkNode, bookmark, level + 1, document);
+
+ bookmark = FPDFBookmark_GetNextSibling(document, bookmark);
+ }
+ }
+
+ void _q_documentStatusChanged()
+ {
+ rebuild();
+ }
+
+ Q_DECLARE_PUBLIC(QPdfBookmarkModel)
+
+ QScopedPointer<BookmarkNode> m_rootNode;
+ QPointer<QPdfDocument> m_document;
+ QPdfBookmarkModel::StructureMode m_structureMode;
+};
+
+
+QPdfBookmarkModel::QPdfBookmarkModel(QObject *parent)
+ : QAbstractItemModel(*new QPdfBookmarkModelPrivate, parent)
+{
+}
+
+QPdfDocument* QPdfBookmarkModel::document() const
+{
+ Q_D(const QPdfBookmarkModel);
+
+ return d->m_document;
+}
+
+void QPdfBookmarkModel::setDocument(QPdfDocument *document)
+{
+ Q_D(QPdfBookmarkModel);
+
+ if (d->m_document == document)
+ return;
+
+ if (d->m_document)
+ disconnect(d->m_document, SIGNAL(statusChanged(QPdfDocument::Status)), this, SLOT(_q_documentStatusChanged()));
+
+ d->m_document = document;
+ emit documentChanged(d->m_document);
+
+ if (d->m_document)
+ connect(d->m_document, SIGNAL(statusChanged(QPdfDocument::Status)), this, SLOT(_q_documentStatusChanged()));
+
+ d->rebuild();
+}
+
+QPdfBookmarkModel::StructureMode QPdfBookmarkModel::structureMode() const
+{
+ Q_D(const QPdfBookmarkModel);
+
+ return d->m_structureMode;
+}
+
+void QPdfBookmarkModel::setStructureMode(StructureMode mode)
+{
+ Q_D(QPdfBookmarkModel);
+
+ if (d->m_structureMode == mode)
+ return;
+
+ d->m_structureMode = mode;
+ emit structureModeChanged(d->m_structureMode);
+
+ d->rebuild();
+}
+
+int QPdfBookmarkModel::columnCount(const QModelIndex &parent) const
+{
+ return 1;
+}
+
+QHash<int, QByteArray> QPdfBookmarkModel::roleNames() const
+{
+ QHash<int, QByteArray> names;
+
+ names[TitleRole] = "title";
+ names[LevelRole] = "level";
+ names[PageNumberRole] = "pageNumber";
+
+ return names;
+}
+
+QVariant QPdfBookmarkModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ const BookmarkNode *node = static_cast<BookmarkNode*>(index.internalPointer());
+ switch (role) {
+ case TitleRole:
+ return node->title();
+ case LevelRole:
+ return node->level();
+ case PageNumberRole:
+ return node->pageNumber();
+ default:
+ return QVariant();
+ }
+}
+
+QModelIndex QPdfBookmarkModel::index(int row, int column, const QModelIndex &parent) const
+{
+ Q_D(const QPdfBookmarkModel);
+
+ if (!hasIndex(row, column, parent))
+ return QModelIndex();
+
+ BookmarkNode *parentNode;
+
+ if (!parent.isValid())
+ parentNode = d->m_rootNode.data();
+ else
+ parentNode = static_cast<BookmarkNode*>(parent.internalPointer());
+
+ BookmarkNode *childNode = parentNode->child(row);
+ if (childNode)
+ return createIndex(row, column, childNode);
+ else
+ return QModelIndex();
+}
+
+QModelIndex QPdfBookmarkModel::parent(const QModelIndex &index) const
+{
+ Q_D(const QPdfBookmarkModel);
+
+ if (!index.isValid())
+ return QModelIndex();
+
+ const BookmarkNode *childNode = static_cast<BookmarkNode*>(index.internalPointer());
+ BookmarkNode *parentNode = childNode->parentNode();
+
+ if (parentNode == d->m_rootNode.data())
+ return QModelIndex();
+
+ return createIndex(parentNode->row(), 0, parentNode);
+}
+
+int QPdfBookmarkModel::rowCount(const QModelIndex &parent) const
+{
+ Q_D(const QPdfBookmarkModel);
+
+ if (parent.column() > 0)
+ return 0;
+
+ BookmarkNode *parentNode = nullptr;
+
+ if (!parent.isValid())
+ parentNode = d->m_rootNode.data();
+ else
+ parentNode = static_cast<BookmarkNode*>(parent.internalPointer());
+
+ return parentNode->childCount();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qpdfbookmarkmodel.cpp"
diff --git a/src/pdf/qpdfdestination.cpp b/src/pdf/qpdfdestination.cpp
new file mode 100644
index 000000000..b347445e9
--- /dev/null
+++ b/src/pdf/qpdfdestination.cpp
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpdfdestination.h"
+#include "qpdfdestination_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QPdfDestination
+ \since 5.15
+ \inmodule QtPdf
+
+ \brief The QPdfDestination class defines a location on a page in a PDF
+ document, and a suggested zoom level at which it is intended to be viewed.
+*/
+
+/*!
+ Constructs an invalid Destination.
+
+ \sa valid
+*/
+QPdfDestination::QPdfDestination()
+ : d(new QPdfDestinationPrivate())
+{
+}
+
+QPdfDestination::QPdfDestination(int page, QPointF location, qreal zoom)
+ : d(new QPdfDestinationPrivate(page, location, zoom))
+{
+}
+
+QPdfDestination::QPdfDestination(QPdfDestinationPrivate *d)
+ : d(d)
+{
+}
+
+QPdfDestination::QPdfDestination(const QPdfDestination &other)
+ : d(other.d)
+{
+}
+
+QPdfDestination::~QPdfDestination()
+{
+}
+
+QPdfDestination &QPdfDestination::operator=(const QPdfDestination &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ \property QPdfDestination::valid
+
+ This property holds whether the destination is valid.
+*/
+bool QPdfDestination::isValid() const
+{
+ return d->page >= 0;
+}
+
+/*!
+ \property QPdfDestination::page
+
+ This property holds the page number.
+*/
+int QPdfDestination::page() const
+{
+ return d->page;
+}
+
+/*!
+ \property QPdfDestination::location
+
+ This property holds the location on the page, in units of points.
+*/
+QPointF QPdfDestination::location() const
+{
+ return d->location;
+}
+
+/*!
+ \property QPdfDestination::zoom
+
+ This property holds the suggested magnification level, where 1.0 means default scale
+ (1 pixel = 1 point).
+*/
+qreal QPdfDestination::zoom() const
+{
+ return d->zoom;
+}
+
+QDebug operator<<(QDebug dbg, const QPdfDestination& dest)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+ dbg << "QPdfDestination(page=" << dest.page()
+ << " location=" << dest.location()
+ << " zoom=" << dest.zoom();
+ dbg << ')';
+ return dbg;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qpdfdestination.cpp"
diff --git a/src/pdf/qpdfdocument.cpp b/src/pdf/qpdfdocument.cpp
new file mode 100644
index 000000000..1e8a0f527
--- /dev/null
+++ b/src/pdf/qpdfdocument.cpp
@@ -0,0 +1,772 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpdfdocument.h"
+#include "qpdfdocument_p.h"
+
+#include "third_party/pdfium/public/fpdf_doc.h"
+#include "third_party/pdfium/public/fpdf_text.h"
+
+#include <QDateTime>
+#include <QDebug>
+#include <QElapsedTimer>
+#include <QFile>
+#include <QHash>
+#include <QLoggingCategory>
+#include <QMutex>
+#include <QVector2D>
+
+QT_BEGIN_NAMESPACE
+
+// The library is not thread-safe at all, it has a lot of global variables.
+Q_GLOBAL_STATIC_WITH_ARGS(QMutex, pdfMutex, (QMutex::Recursive));
+static int libraryRefCount;
+static const double CharacterHitTolerance = 6.0;
+Q_LOGGING_CATEGORY(qLcDoc, "qt.pdf.document")
+
+QPdfMutexLocker::QPdfMutexLocker()
+ : QMutexLocker(pdfMutex())
+{
+}
+
+QPdfDocumentPrivate::QPdfDocumentPrivate()
+ : avail(nullptr)
+ , doc(nullptr)
+ , loadComplete(false)
+ , status(QPdfDocument::Null)
+ , lastError(QPdfDocument::NoError)
+ , pageCount(0)
+{
+ asyncBuffer.setData(QByteArray());
+ asyncBuffer.open(QIODevice::ReadWrite);
+
+ const QPdfMutexLocker lock;
+
+ if (libraryRefCount == 0)
+ FPDF_InitLibrary();
+ ++libraryRefCount;
+
+ // FPDF_FILEACCESS setup
+ m_Param = this;
+ m_GetBlock = fpdf_GetBlock;
+
+ // FX_FILEAVAIL setup
+ FX_FILEAVAIL::version = 1;
+ IsDataAvail = fpdf_IsDataAvail;
+
+ // FX_DOWNLOADHINTS setup
+ FX_DOWNLOADHINTS::version = 1;
+ AddSegment = fpdf_AddSegment;
+}
+
+QPdfDocumentPrivate::~QPdfDocumentPrivate()
+{
+ q->close();
+
+ const QPdfMutexLocker lock;
+
+ if (!--libraryRefCount)
+ FPDF_DestroyLibrary();
+}
+
+void QPdfDocumentPrivate::clear()
+{
+ QPdfMutexLocker lock;
+
+ if (doc)
+ FPDF_CloseDocument(doc);
+ doc = nullptr;
+
+ if (avail)
+ FPDFAvail_Destroy(avail);
+ avail = nullptr;
+ lock.unlock();
+
+ if (pageCount != 0) {
+ pageCount = 0;
+ emit q->pageCountChanged(pageCount);
+ }
+
+ loadComplete = false;
+
+ asyncBuffer.close();
+ asyncBuffer.setData(QByteArray());
+ asyncBuffer.open(QIODevice::ReadWrite);
+
+ if (sequentialSourceDevice)
+ sequentialSourceDevice->disconnect(q);
+}
+
+void QPdfDocumentPrivate::updateLastError()
+{
+ if (doc) {
+ lastError = QPdfDocument::NoError;
+ return;
+ }
+
+ QPdfMutexLocker lock;
+ const unsigned long error = FPDF_GetLastError();
+ lock.unlock();
+
+ switch (error) {
+ case FPDF_ERR_SUCCESS: lastError = QPdfDocument::NoError; break;
+ case FPDF_ERR_UNKNOWN: lastError = QPdfDocument::UnknownError; break;
+ case FPDF_ERR_FILE: lastError = QPdfDocument::FileNotFoundError; break;
+ case FPDF_ERR_FORMAT: lastError = QPdfDocument::InvalidFileFormatError; break;
+ case FPDF_ERR_PASSWORD: lastError = QPdfDocument::IncorrectPasswordError; break;
+ case FPDF_ERR_SECURITY: lastError = QPdfDocument::UnsupportedSecuritySchemeError; break;
+ default:
+ Q_UNREACHABLE();
+ }
+}
+
+void QPdfDocumentPrivate::load(QIODevice *newDevice, bool transferDeviceOwnership)
+{
+ if (transferDeviceOwnership)
+ ownDevice.reset(newDevice);
+ else
+ ownDevice.reset();
+
+ if (newDevice->isSequential()) {
+ sequentialSourceDevice = newDevice;
+ device = &asyncBuffer;
+ QNetworkReply *reply = qobject_cast<QNetworkReply*>(sequentialSourceDevice);
+
+ if (!reply) {
+ setStatus(QPdfDocument::Error);
+ qWarning() << "QPdfDocument: Loading from sequential devices only supported with QNetworkAccessManager.";
+ return;
+ }
+
+ if (reply->isFinished() && reply->error() != QNetworkReply::NoError) {
+ setStatus(QPdfDocument::Error);
+ return;
+ }
+
+ QObject::connect(reply, &QNetworkReply::finished, q, [this, reply](){
+ if (reply->error() != QNetworkReply::NoError || reply->bytesAvailable() == 0) {
+ this->setStatus(QPdfDocument::Error);
+ }
+ });
+
+ if (reply->header(QNetworkRequest::ContentLengthHeader).isValid())
+ _q_tryLoadingWithSizeFromContentHeader();
+ else
+ QObject::connect(reply, SIGNAL(metaDataChanged()), q, SLOT(_q_tryLoadingWithSizeFromContentHeader()));
+ } else {
+ device = newDevice;
+ initiateAsyncLoadWithTotalSizeKnown(device->size());
+ if (!avail) {
+ setStatus(QPdfDocument::Error);
+ return;
+ }
+
+ if (!doc)
+ tryLoadDocument();
+
+ if (!doc) {
+ updateLastError();
+ setStatus(QPdfDocument::Error);
+ return;
+ }
+
+ QPdfMutexLocker lock;
+ const int newPageCount = FPDF_GetPageCount(doc);
+ lock.unlock();
+ if (newPageCount != pageCount) {
+ pageCount = newPageCount;
+ emit q->pageCountChanged(pageCount);
+ }
+
+ // If it's a local file, and the first couple of pages are available,
+ // probably the whole document is available.
+ if (checkPageComplete(0) && (pageCount < 2 || checkPageComplete(1))) {
+ setStatus(QPdfDocument::Ready);
+ } else {
+ updateLastError();
+ setStatus(QPdfDocument::Error);
+ }
+ }
+}
+
+void QPdfDocumentPrivate::_q_tryLoadingWithSizeFromContentHeader()
+{
+ if (avail)
+ return;
+
+ const QNetworkReply *networkReply = qobject_cast<QNetworkReply*>(sequentialSourceDevice);
+ if (!networkReply) {
+ setStatus(QPdfDocument::Error);
+ return;
+ }
+
+ const QVariant contentLength = networkReply->header(QNetworkRequest::ContentLengthHeader);
+ if (!contentLength.isValid()) {
+ setStatus(QPdfDocument::Error);
+ return;
+ }
+
+ QObject::connect(sequentialSourceDevice, SIGNAL(readyRead()), q, SLOT(_q_copyFromSequentialSourceDevice()));
+
+ initiateAsyncLoadWithTotalSizeKnown(contentLength.toULongLong());
+
+ if (sequentialSourceDevice->bytesAvailable())
+ _q_copyFromSequentialSourceDevice();
+}
+
+void QPdfDocumentPrivate::initiateAsyncLoadWithTotalSizeKnown(quint64 totalSize)
+{
+ // FPDF_FILEACCESS setup
+ m_FileLen = totalSize;
+
+ const QPdfMutexLocker lock;
+
+ avail = FPDFAvail_Create(this, this);
+}
+
+void QPdfDocumentPrivate::_q_copyFromSequentialSourceDevice()
+{
+ if (loadComplete)
+ return;
+
+ const QByteArray data = sequentialSourceDevice->read(sequentialSourceDevice->bytesAvailable());
+ if (data.isEmpty())
+ return;
+
+ asyncBuffer.seek(asyncBuffer.size());
+ asyncBuffer.write(data);
+
+ checkComplete();
+}
+
+void QPdfDocumentPrivate::tryLoadDocument()
+{
+ QPdfMutexLocker lock;
+ switch (FPDFAvail_IsDocAvail(avail, this)) {
+ case PDF_DATA_ERROR:
+ qCDebug(qLcDoc) << "error loading";
+ break;
+ case PDF_DATA_NOTAVAIL:
+ qCDebug(qLcDoc) << "data not yet available";
+ lastError = QPdfDocument::DataNotYetAvailableError;
+ setStatus(QPdfDocument::Error);
+ break;
+ case PDF_DATA_AVAIL:
+ // all good
+ break;
+ }
+
+ Q_ASSERT(!doc);
+
+ doc = FPDFAvail_GetDocument(avail, password);
+ lock.unlock();
+
+ updateLastError();
+
+ if (lastError == QPdfDocument::IncorrectPasswordError) {
+ FPDF_CloseDocument(doc);
+ doc = nullptr;
+
+ setStatus(QPdfDocument::Error);
+ emit q->passwordRequired();
+ }
+}
+
+void QPdfDocumentPrivate::checkComplete()
+{
+ if (!avail || loadComplete)
+ return;
+
+ if (!doc)
+ tryLoadDocument();
+
+ if (!doc)
+ return;
+
+ loadComplete = true;
+
+ QPdfMutexLocker lock;
+
+ const int newPageCount = FPDF_GetPageCount(doc);
+ for (int i = 0; i < newPageCount; ++i) {
+ int result = PDF_DATA_NOTAVAIL;
+ while (result == PDF_DATA_NOTAVAIL) {
+ result = FPDFAvail_IsPageAvail(avail, i, this);
+ }
+
+ if (result == PDF_DATA_ERROR)
+ loadComplete = false;
+ }
+
+ lock.unlock();
+
+ if (loadComplete) {
+ if (newPageCount != pageCount) {
+ pageCount = newPageCount;
+ emit q->pageCountChanged(pageCount);
+ }
+
+ setStatus(QPdfDocument::Ready);
+ }
+}
+
+bool QPdfDocumentPrivate::checkPageComplete(int page)
+{
+ if (page < 0 || page >= pageCount)
+ return false;
+
+ if (loadComplete)
+ return true;
+
+ QPdfMutexLocker lock;
+ int result = PDF_DATA_NOTAVAIL;
+ while (result == PDF_DATA_NOTAVAIL)
+ result = FPDFAvail_IsPageAvail(avail, page, this);
+ lock.unlock();
+
+ if (result == PDF_DATA_ERROR)
+ updateLastError();
+
+ return (result != PDF_DATA_ERROR);
+}
+
+void QPdfDocumentPrivate::setStatus(QPdfDocument::Status documentStatus)
+{
+ if (status == documentStatus)
+ return;
+
+ status = documentStatus;
+ emit q->statusChanged(status);
+}
+
+FPDF_BOOL QPdfDocumentPrivate::fpdf_IsDataAvail(_FX_FILEAVAIL *pThis, size_t offset, size_t size)
+{
+ QPdfDocumentPrivate *d = static_cast<QPdfDocumentPrivate*>(pThis);
+ return offset + size <= static_cast<quint64>(d->device->size());
+}
+
+int QPdfDocumentPrivate::fpdf_GetBlock(void *param, unsigned long position, unsigned char *pBuf, unsigned long size)
+{
+ QPdfDocumentPrivate *d = static_cast<QPdfDocumentPrivate*>(reinterpret_cast<FPDF_FILEACCESS*>(param));
+ d->device->seek(position);
+ return qMax(qint64(0), d->device->read(reinterpret_cast<char *>(pBuf), size));
+
+}
+
+void QPdfDocumentPrivate::fpdf_AddSegment(_FX_DOWNLOADHINTS *pThis, size_t offset, size_t size)
+{
+ Q_UNUSED(pThis);
+ Q_UNUSED(offset);
+ Q_UNUSED(size);
+}
+
+/*!
+ \class QPdfDocument
+ \since 5.10
+ \inmodule QtPdf
+
+ \brief The QPdfDocument class loads a PDF document and renders pages from it.
+*/
+
+/*!
+ Constructs a new document with parent object \a parent.
+*/
+QPdfDocument::QPdfDocument(QObject *parent)
+ : QObject(parent)
+ , d(new QPdfDocumentPrivate)
+{
+ d->q = this;
+}
+
+/*!
+ Destroys the document.
+*/
+QPdfDocument::~QPdfDocument()
+{
+}
+
+QPdfDocument::DocumentError QPdfDocument::load(const QString &fileName)
+{
+ qCDebug(qLcDoc) << "loading" << fileName;
+
+ close();
+
+ d->setStatus(QPdfDocument::Loading);
+
+ QScopedPointer<QFile> f(new QFile(fileName));
+ if (!f->open(QIODevice::ReadOnly)) {
+ d->lastError = FileNotFoundError;
+ d->setStatus(QPdfDocument::Error);
+ } else {
+ d->load(f.take(), /*transfer ownership*/true);
+ }
+ return d->lastError;
+}
+
+/*!
+ \enum QPdfDocument::Status
+
+ This enum describes the current status of the document.
+
+ \value Null The initial status after the document has been created or after it has been closed.
+ \value Loading The status after load() has been called and before the document is fully loaded.
+ \value Ready The status when the document is fully loaded and its data can be accessed.
+ \value Unloading The status after close() has been called on an open document.
+ At this point the document is still valid and all its data can be accessed.
+ \value Error The status after Loading, if loading has failed.
+
+ \sa QPdfDocument::status()
+*/
+
+/*!
+ Returns the current status of the document.
+*/
+QPdfDocument::Status QPdfDocument::status() const
+{
+ return d->status;
+}
+
+void QPdfDocument::load(QIODevice *device)
+{
+ close();
+
+ d->setStatus(QPdfDocument::Loading);
+
+ d->load(device, /*transfer ownership*/false);
+}
+
+void QPdfDocument::setPassword(const QString &password)
+{
+ const QByteArray newPassword = password.toUtf8();
+
+ if (d->password == newPassword)
+ return;
+
+ d->password = newPassword;
+ emit passwordChanged();
+}
+
+QString QPdfDocument::password() const
+{
+ return QString::fromUtf8(d->password);
+}
+
+/*!
+ \enum QPdfDocument::MetaDataField
+
+ This enum describes the available fields of meta data.
+
+ \value Title The document's title as QString.
+ \value Author The name of the person who created the document as QString.
+ \value Subject The subject of the document as QString.
+ \value Keywords Keywords associated with the document as QString.
+ \value Creator If the document was converted to PDF from another format,
+ the name of the conforming product that created the original document
+ from which it was converted as QString.
+ \value Producer If the document was converted to PDF from another format,
+ the name of the conforming product that converted it to PDF as QString.
+ \value CreationDate The date and time the document was created as QDateTime.
+ \value ModificationDate The date and time the document was most recently modified as QDateTime.
+
+ \sa QPdfDocument::metaData()
+*/
+
+/*!
+ Returns the meta data of the document for the given \a field.
+*/
+QVariant QPdfDocument::metaData(MetaDataField field) const
+{
+ if (!d->doc)
+ return QString();
+
+ QByteArray fieldName;
+ switch (field) {
+ case Title:
+ fieldName = "Title";
+ break;
+ case Subject:
+ fieldName = "Subject";
+ break;
+ case Author:
+ fieldName = "Author";
+ break;
+ case Keywords:
+ fieldName = "Keywords";
+ break;
+ case Producer:
+ fieldName = "Producer";
+ break;
+ case Creator:
+ fieldName = "Creator";
+ break;
+ case CreationDate:
+ fieldName = "CreationDate";
+ break;
+ case ModificationDate:
+ fieldName = "ModDate";
+ break;
+ }
+
+ QPdfMutexLocker lock;
+ const unsigned long len = FPDF_GetMetaText(d->doc, fieldName.constData(), nullptr, 0);
+
+ QVector<ushort> buf(len);
+ FPDF_GetMetaText(d->doc, fieldName.constData(), buf.data(), buf.length());
+ lock.unlock();
+
+ QString text = QString::fromUtf16(buf.data());
+
+ switch (field) {
+ case Title: // fall through
+ case Subject:
+ case Author:
+ case Keywords:
+ case Producer:
+ case Creator:
+ return text;
+ case CreationDate: // fall through
+ case ModificationDate:
+ // convert a "D:YYYYMMDDHHmmSSOHH'mm'" into "YYYY-MM-DDTHH:mm:ss+HH:mm"
+ if (text.startsWith(QLatin1String("D:")))
+ text = text.mid(2);
+ text.insert(4, QLatin1Char('-'));
+ text.insert(7, QLatin1Char('-'));
+ text.insert(10, QLatin1Char('T'));
+ text.insert(13, QLatin1Char(':'));
+ text.insert(16, QLatin1Char(':'));
+ text.replace(QLatin1Char('\''), QLatin1Char(':'));
+ if (text.endsWith(QLatin1Char(':')))
+ text.chop(1);
+
+ return QDateTime::fromString(text, Qt::ISODate);
+ }
+
+ return QVariant();
+}
+
+QPdfDocument::DocumentError QPdfDocument::error() const
+{
+ return d->lastError;
+}
+
+/*!
+ Closes the document.
+*/
+void QPdfDocument::close()
+{
+ if (!d->doc)
+ return;
+
+ d->setStatus(Unloading);
+
+ d->clear();
+
+ if (!d->password.isEmpty()) {
+ d->password.clear();
+ emit passwordChanged();
+ }
+
+ d->setStatus(Null);
+}
+
+/*!
+ Returns the amount of pages for the loaded document or \c 0 if
+ no document is loaded.
+*/
+int QPdfDocument::pageCount() const
+{
+ return d->pageCount;
+}
+
+QSizeF QPdfDocument::pageSize(int page) const
+{
+ QSizeF result;
+ if (!d->doc || !d->checkPageComplete(page))
+ return result;
+
+ const QPdfMutexLocker lock;
+
+ FPDF_GetPageSizeByIndex(d->doc, page, &result.rwidth(), &result.rheight());
+ return result;
+}
+
+/*!
+ Renders the \a page into a QImage of size \a imageSize according to the
+ provided \a renderOptions.
+
+ Returns the rendered page or an empty image in case of an error.
+
+ Note: If the \a imageSize does not match the aspect ratio of the page in the
+ PDF document, the page is rendered scaled, so that it covers the
+ complete \a imageSize.
+*/
+QImage QPdfDocument::render(int page, QSize imageSize, QPdfDocumentRenderOptions renderOptions)
+{
+ if (!d->doc || !d->checkPageComplete(page))
+ return QImage();
+
+ const QPdfMutexLocker lock;
+
+ QElapsedTimer timer;
+ if (Q_UNLIKELY(qLcDoc().isDebugEnabled()))
+ timer.start();
+ FPDF_PAGE pdfPage = FPDF_LoadPage(d->doc, page);
+ if (!pdfPage)
+ return QImage();
+
+ QImage result(imageSize, QImage::Format_ARGB32);
+ result.fill(Qt::transparent);
+ FPDF_BITMAP bitmap = FPDFBitmap_CreateEx(result.width(), result.height(), FPDFBitmap_BGRA, result.bits(), result.bytesPerLine());
+
+ int rotation = 0;
+ switch (renderOptions.rotation()) {
+ case QPdf::Rotate0:
+ rotation = 0;
+ break;
+ case QPdf::Rotate90:
+ rotation = 1;
+ break;
+ case QPdf::Rotate180:
+ rotation = 2;
+ break;
+ case QPdf::Rotate270:
+ rotation = 3;
+ break;
+ }
+
+ const QPdf::RenderFlags renderFlags = renderOptions.renderFlags();
+ int flags = 0;
+ if (renderFlags & QPdf::RenderAnnotations)
+ flags |= FPDF_ANNOT;
+ if (renderFlags & QPdf::RenderOptimizedForLcd)
+ flags |= FPDF_LCD_TEXT;
+ if (renderFlags & QPdf::RenderGrayscale)
+ flags |= FPDF_GRAYSCALE;
+ if (renderFlags & QPdf::RenderForceHalftone)
+ flags |= FPDF_RENDER_FORCEHALFTONE;
+ if (renderFlags & QPdf::RenderTextAliased)
+ flags |= FPDF_RENDER_NO_SMOOTHTEXT;
+ if (renderFlags & QPdf::RenderImageAliased)
+ flags |= FPDF_RENDER_NO_SMOOTHIMAGE;
+ if (renderFlags & QPdf::RenderPathAliased)
+ flags |= FPDF_RENDER_NO_SMOOTHPATH;
+
+ if (renderOptions.scaledClipRect().isValid()) {
+ const QRect &clipRect = renderOptions.scaledClipRect();
+
+ // TODO take rotation into account, like cpdf_page.cpp lines 145-178
+ float x0 = clipRect.left();
+ float y0 = clipRect.top();
+ float x1 = clipRect.left();
+ float y1 = clipRect.bottom();
+ float x2 = clipRect.right();
+ float y2 = clipRect.top();
+ QSizeF origSize = pageSize(page);
+ QVector2D pageScale(1, 1);
+ if (!renderOptions.scaledSize().isNull()) {
+ pageScale = QVector2D(renderOptions.scaledSize().width() / float(origSize.width()),
+ renderOptions.scaledSize().height() / float(origSize.height()));
+ }
+ FS_MATRIX matrix {(x2 - x0) / result.width() * pageScale.x(),
+ (y2 - y0) / result.width() * pageScale.x(),
+ (x1 - x0) / result.height() * pageScale.y(),
+ (y1 - y0) / result.height() * pageScale.y(), -x0, -y0};
+
+ FS_RECTF clipRectF { 0, 0, float(imageSize.width()), float(imageSize.height()) };
+
+ FPDF_RenderPageBitmapWithMatrix(bitmap, pdfPage, &matrix, &clipRectF, flags);
+ qCDebug(qLcDoc) << "matrix" << matrix.a << matrix.b << matrix.c << matrix.d << matrix.e << matrix.f;
+ qCDebug(qLcDoc) << "page" << page << "region" << renderOptions.scaledClipRect()
+ << "size" << imageSize << "took" << timer.elapsed() << "ms";
+ } else {
+ FPDF_RenderPageBitmap(bitmap, pdfPage, 0, 0, result.width(), result.height(), rotation, flags);
+ qCDebug(qLcDoc) << "page" << page << "size" << imageSize << "took" << timer.elapsed() << "ms";
+ }
+
+ FPDFBitmap_Destroy(bitmap);
+
+ FPDF_ClosePage(pdfPage);
+ return result;
+}
+
+/*!
+ Returns information about the text on the given \a page that can be found
+ between the given \a start and \a end points, if any.
+*/
+QPdfSelection QPdfDocument::getSelection(int page, QPointF start, QPointF end)
+{
+ const QPdfMutexLocker lock;
+ FPDF_PAGE pdfPage = FPDF_LoadPage(d->doc, page);
+ double pageHeight = FPDF_GetPageHeight(pdfPage);
+ FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage);
+ int startIndex = FPDFText_GetCharIndexAtPos(textPage, start.x(), pageHeight - start.y(),
+ CharacterHitTolerance, CharacterHitTolerance);
+ int endIndex = FPDFText_GetCharIndexAtPos(textPage, end.x(), pageHeight - end.y(),
+ CharacterHitTolerance, CharacterHitTolerance);
+ if (startIndex >= 0 && endIndex != startIndex) {
+ QString text;
+ if (startIndex > endIndex)
+ qSwap(startIndex, endIndex);
+ int count = endIndex - startIndex + 1;
+ QVector<ushort> buf(count + 1);
+ // TODO is that enough space in case one unicode character is more than one in utf-16?
+ int len = FPDFText_GetText(textPage, startIndex, count, buf.data());
+ Q_ASSERT(len - 1 <= count); // len is number of characters written, including the terminator
+ text = QString::fromUtf16(buf.constData(), len - 1);
+ QVector<QPolygonF> bounds;
+ int rectCount = FPDFText_CountRects(textPage, startIndex, endIndex - startIndex);
+ for (int i = 0; i < rectCount; ++i) {
+ double l, r, b, t;
+ FPDFText_GetRect(textPage, i, &l, &t, &r, &b);
+ QPolygonF poly;
+ poly << QPointF(l, pageHeight - t);
+ poly << QPointF(r, pageHeight - t);
+ poly << QPointF(r, pageHeight - b);
+ poly << QPointF(l, pageHeight - b);
+ poly << QPointF(l, pageHeight - t);
+ bounds << poly;
+ }
+ qCDebug(qLcDoc) << page << start << "->" << end << "found" << startIndex << "->" << endIndex << text;
+ return QPdfSelection(text, bounds);
+ }
+
+ qCDebug(qLcDoc) << page << start << "->" << end << "nothing found";
+ return QPdfSelection();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qpdfdocument.cpp"
diff --git a/src/pdf/qpdfdocumentrenderoptions.qdoc b/src/pdf/qpdfdocumentrenderoptions.qdoc
new file mode 100644
index 000000000..cc5083f9d
--- /dev/null
+++ b/src/pdf/qpdfdocumentrenderoptions.qdoc
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpdfdocumentrenderoptions.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QPdfDocumentRenderOptions
+ \since 5.10
+ \inmodule QtPdf
+
+ \brief The QPdfDocumentRenderOptions class holds the options to render a page from a PDF document.
+
+ \sa QPdfDocument
+*/
+
+/*!
+ \fn QPdfDocumentRenderOptions::QPdfDocumentRenderOptions()
+
+ Constructs a QPdfDocumentRenderOptions object.
+*/
+
+/*!
+ \fn QPdf::Rotation QPdfDocumentRenderOptions::rotation() const
+
+ Returns the rotation used for rendering a page from a PDF document.
+
+ \sa setRotation()
+*/
+
+/*!
+ \fn void QPdfDocumentRenderOptions::setRotation(QPdf::Rotation rotation)
+
+ Sets the \a rotation used for rendering a page from a PDF document.
+
+ \sa rotation()
+*/
+
+/*!
+ \fn QPdf::RenderFlags QPdfDocumentRenderOptions::renderFlags() const
+
+ Returns the special flags used for rendering a page from a PDF document.
+
+ \sa setRenderFlags()
+*/
+
+/*!
+ \fn void QPdfDocumentRenderOptions::setRenderFlags(QPdf::RenderFlags flags)
+
+ Sets the special \a flags used for rendering a page from a PDF document.
+
+ \sa renderFlags()
+*/
+
+/*!
+ \fn QRect QPdfDocumentRenderOptions::scaledClipRect() const
+
+ Returns the rectangular region to be clipped from the page after having
+ been scaled to \l scaledSize().
+
+ \sa setScaledClipRect()
+*/
+
+/*!
+ \fn void QPdfDocumentRenderOptions::setScaledClipRect(QRect rect)
+
+ Sets the region \a rect to be clipped from the page after having been
+ scaled to \l scaledSize().
+
+ \sa scaledClipRect()
+*/
+
+/*!
+ \fn QRect QPdfDocumentRenderOptions::scaledSize() const
+
+ Returns the \a size of the page to be rendered, in pixels.
+
+ \sa setScaledSize()
+*/
+
+/*!
+ \fn void QPdfDocumentRenderOptions::setScaledSize(QSize size)
+
+ Sets the \a size of the page to be rendered, in pixels.
+
+ \sa scaledSize()
+*/
+
+/*!
+ \fn bool operator!=(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs)
+ \relates QPdfDocumentRenderOptions
+
+ Returns \c true if the options \a lhs and \a rhs are different, otherwise
+ returns \c false.
+*/
+
+/*!
+ \fn bool operator==(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs)
+ \relates QPdfDocumentRenderOptions
+
+ Returns \c true if the options \a lhs and \a rhs are equal,
+ otherwise returns \c false.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/pdf/qpdflinkmodel.cpp b/src/pdf/qpdflinkmodel.cpp
new file mode 100644
index 000000000..96e6ddd5c
--- /dev/null
+++ b/src/pdf/qpdflinkmodel.cpp
@@ -0,0 +1,251 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpdflinkmodel_p.h"
+#include "qpdflinkmodel_p_p.h"
+#include "qpdfdocument_p.h"
+
+#include "third_party/pdfium/public/fpdf_doc.h"
+#include "third_party/pdfium/public/fpdf_text.h"
+
+#include <QLoggingCategory>
+#include <QMetaEnum>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qLcLink, "qt.pdf.links")
+
+QPdfLinkModel::QPdfLinkModel(QObject *parent)
+ : QAbstractListModel(*(new QPdfLinkModelPrivate()), parent)
+{
+ QMetaEnum rolesMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("Role"));
+ for (int r = Qt::UserRole; r < int(Role::_Count); ++r)
+ m_roleNames.insert(r, QByteArray(rolesMetaEnum.valueToKey(r)).toLower());
+}
+
+QPdfLinkModel::~QPdfLinkModel() {}
+
+QHash<int, QByteArray> QPdfLinkModel::roleNames() const
+{
+ return m_roleNames;
+}
+
+int QPdfLinkModel::rowCount(const QModelIndex &parent) const
+{
+ Q_D(const QPdfLinkModel);
+ Q_UNUSED(parent)
+ return d->links.count();
+}
+
+QVariant QPdfLinkModel::data(const QModelIndex &index, int role) const
+{
+ Q_D(const QPdfLinkModel);
+ const QPdfLinkModelPrivate::Link &link = d->links.at(index.row());
+ switch (Role(role)) {
+ case Role::Rect:
+ return link.rect;
+ case Role::Url:
+ return link.url;
+ case Role::Page:
+ return link.page;
+ case Role::Location:
+ return link.location;
+ case Role::Zoom:
+ return link.zoom;
+ case Role::_Count:
+ break;
+ }
+ if (role == Qt::DisplayRole)
+ return link.toString();
+ return QVariant();
+}
+
+QPdfDocument *QPdfLinkModel::document() const
+{
+ Q_D(const QPdfLinkModel);
+ return d->document;
+}
+
+void QPdfLinkModel::setDocument(QPdfDocument *document)
+{
+ Q_D(QPdfLinkModel);
+ if (d->document == document)
+ return;
+ disconnect(d->document, &QPdfDocument::statusChanged, this, &QPdfLinkModel::onStatusChanged);
+ connect(document, &QPdfDocument::statusChanged, this, &QPdfLinkModel::onStatusChanged);
+ d->document = document;
+ emit documentChanged();
+ if (page())
+ setPage(0);
+ else
+ d->update();
+}
+
+int QPdfLinkModel::page() const
+{
+ Q_D(const QPdfLinkModel);
+ return d->page;
+}
+
+void QPdfLinkModel::setPage(int page)
+{
+ Q_D(QPdfLinkModel);
+ if (d->page == page)
+ return;
+
+ d->page = page;
+ emit pageChanged(page);
+ d->update();
+}
+
+QPdfLinkModelPrivate::QPdfLinkModelPrivate() : QAbstractItemModelPrivate()
+{
+}
+
+void QPdfLinkModelPrivate::update()
+{
+ Q_Q(QPdfLinkModel);
+ if (!document || !document->d->doc)
+ return;
+ auto doc = document->d->doc;
+ const QPdfMutexLocker lock;
+ FPDF_PAGE pdfPage = FPDF_LoadPage(doc, page);
+ if (!pdfPage) {
+ qWarning() << "failed to load page" << page;
+ return;
+ }
+ double pageHeight = FPDF_GetPageHeight(pdfPage);
+ q->beginResetModel();
+ links.clear();
+
+ // Iterate the ordinary links
+ int linkStart = 0;
+ bool ok = true;
+ while (ok) {
+ FPDF_LINK linkAnnot;
+ ok = FPDFLink_Enumerate(pdfPage, &linkStart, &linkAnnot);
+ if (!ok)
+ break;
+ FS_RECTF rect;
+ ok = FPDFLink_GetAnnotRect(linkAnnot, &rect);
+ if (!ok)
+ break;
+ Link linkData;
+ linkData.rect = QRectF(rect.left, pageHeight - rect.top,
+ rect.right - rect.left, rect.top - rect.bottom);
+ FPDF_DEST dest = FPDFLink_GetDest(doc, linkAnnot);
+ FPDF_ACTION action = FPDFLink_GetAction(linkAnnot);
+ if (FPDFAction_GetType(action) != PDFACTION_GOTO) {
+ qWarning() << "link action type" << FPDFAction_GetType(action) << "is not yet supported";
+ continue;
+ }
+ linkData.page = FPDFDest_GetDestPageIndex(doc, dest);
+ FPDF_BOOL hasX, hasY, hasZoom;
+ FS_FLOAT x, y, zoom;
+ ok = FPDFDest_GetLocationInPage(dest, &hasX, &hasY, &hasZoom, &x, &y, &zoom);
+ if (!ok)
+ break;
+ if (hasX && hasY)
+ linkData.location = QPointF(x, pageHeight - y);
+ if (hasZoom)
+ linkData.zoom = zoom;
+ links << linkData;
+ }
+
+ // Iterate the web links
+ FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage);
+ if (textPage) {
+ FPDF_PAGELINK webLinks = FPDFLink_LoadWebLinks(textPage);
+ if (webLinks) {
+ int count = FPDFLink_CountWebLinks(webLinks);
+ for (int i = 0; i < count; ++i) {
+ Link linkData;
+ int len = FPDFLink_GetURL(webLinks, i, nullptr, 0);
+ if (len < 1) {
+ qWarning() << "URL" << i << "has length" << len;
+ } else {
+ QVector<unsigned short> buf(len);
+ int got = FPDFLink_GetURL(webLinks, i, buf.data(), len);
+ Q_ASSERT(got == len);
+ linkData.url = QString::fromUtf16(buf.data(), got - 1);
+ }
+ FPDFLink_GetTextRange(webLinks, i, &linkData.textStart, &linkData.textCharCount);
+ len = FPDFLink_CountRects(webLinks, i);
+ for (int r = 0; r < len; ++r) {
+ double left, top, right, bottom;
+ bool success = FPDFLink_GetRect(webLinks, i, r, &left, &top, &right, &bottom);
+ if (success) {
+ linkData.rect = QRectF(left, pageHeight - top, right - left, top - bottom);
+ links << linkData;
+ }
+ }
+ }
+ FPDFLink_CloseWebLinks(webLinks);
+ }
+ FPDFText_ClosePage(textPage);
+ }
+
+ // All done
+ FPDF_ClosePage(pdfPage);
+ if (Q_UNLIKELY(qLcLink().isDebugEnabled())) {
+ for (const Link &l : links)
+ qCDebug(qLcLink) << l.rect << l.toString();
+ }
+ q->endResetModel();
+}
+
+void QPdfLinkModel::onStatusChanged(QPdfDocument::Status status)
+{
+ Q_D(QPdfLinkModel);
+ qCDebug(qLcLink) << "sees document statusChanged" << status;
+ if (status == QPdfDocument::Ready)
+ d->update();
+}
+
+QString QPdfLinkModelPrivate::Link::toString() const
+{
+ QString ret;
+ if (page >= 0)
+ return QLatin1String("page ") + QString::number(page) +
+ QLatin1String(" location ") + QString::number(location.x()) + QLatin1Char(',') + QString::number(location.y()) +
+ QLatin1String(" zoom ") + QString::number(zoom);
+ else
+ return url.toString();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qpdflinkmodel_p.cpp"
diff --git a/src/pdf/qpdfnamespace.qdoc b/src/pdf/qpdfnamespace.qdoc
new file mode 100644
index 000000000..96bb090e9
--- /dev/null
+++ b/src/pdf/qpdfnamespace.qdoc
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \namespace QPdf
+ \inmodule QtPdf
+ \keyword QPdf Namespace
+
+ \brief The QPdf namespace contains miscellaneous identifiers
+ used throughout the QtPdf module.
+*/
+
+/*!
+ \enum QPdf::Rotation
+
+ This enum describes the rotation of the page for rendering.
+
+ \value Rotate0 Do not rotate (the default)
+ \value Rotate90 Rotate 90 degrees clockwise
+ \value Rotate180 Rotate 180 degrees
+ \value Rotate270 Rotate 270 degrees clockwise
+
+ \sa QPdfDocument::render()
+*/
+/*!
+ \enum QPdf::RenderFlag
+
+ This enum is used to describe how a page should be rendered.
+
+ \value NoRenderFlags The default value, representing no flags.
+ \value RenderAnnotations The page is rendered with annotations.
+ \value RenderOptimizedForLcd The text of the page is rendered optimized for LCD display.
+ \value RenderGrayscale The page is rendered grayscale.
+ \value RenderForceHalftone Always use halftones for rendering if the output image is stretched.
+ \value RenderTextAliased Anti-aliasing is disabled for rendering text.
+ \value RenderImageAliased Anti-aliasing is disabled for rendering images.
+ \value RenderPathAliased Anti-aliasing is disabled for rendering paths.
+
+ \sa QPdfDocument::render()
+*/
+
diff --git a/src/pdf/qpdfpagenavigation.cpp b/src/pdf/qpdfpagenavigation.cpp
new file mode 100644
index 000000000..497c1c2eb
--- /dev/null
+++ b/src/pdf/qpdfpagenavigation.cpp
@@ -0,0 +1,314 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpdfpagenavigation.h"
+
+#include "qpdfdocument.h"
+
+#include <private/qobject_p.h>
+
+#include <QPointer>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfPageNavigationPrivate : public QObjectPrivate
+{
+public:
+ QPdfPageNavigationPrivate()
+ : QObjectPrivate()
+ {
+ }
+
+ void update()
+ {
+ Q_Q(QPdfPageNavigation);
+
+ const bool documentAvailable = m_document && m_document->status() == QPdfDocument::Ready;
+
+ if (documentAvailable) {
+ const int newPageCount = m_document->pageCount();
+ if (m_pageCount != newPageCount) {
+ m_pageCount = newPageCount;
+ emit q->pageCountChanged(m_pageCount);
+ }
+ } else {
+ if (m_pageCount != 0) {
+ m_pageCount = 0;
+ emit q->pageCountChanged(m_pageCount);
+ }
+ }
+
+ if (m_currentPage != 0) {
+ m_currentPage = 0;
+ emit q->currentPageChanged(m_currentPage);
+ }
+
+ updatePrevNext();
+ }
+
+ void updatePrevNext()
+ {
+ Q_Q(QPdfPageNavigation);
+
+ const bool hasPreviousPage = m_currentPage > 0;
+ const bool hasNextPage = m_currentPage < (m_pageCount - 1);
+
+ if (m_canGoToPreviousPage != hasPreviousPage) {
+ m_canGoToPreviousPage = hasPreviousPage;
+ emit q->canGoToPreviousPageChanged(m_canGoToPreviousPage);
+ }
+
+ if (m_canGoToNextPage != hasNextPage) {
+ m_canGoToNextPage = hasNextPage;
+ emit q->canGoToNextPageChanged(m_canGoToNextPage);
+ }
+ }
+
+ void documentStatusChanged()
+ {
+ update();
+ }
+
+ Q_DECLARE_PUBLIC(QPdfPageNavigation)
+
+ QPointer<QPdfDocument> m_document = nullptr;
+ int m_currentPage = 0;
+ int m_pageCount = 0;
+ bool m_canGoToPreviousPage = false;
+ bool m_canGoToNextPage = false;
+
+ QMetaObject::Connection m_documentStatusChangedConnection;
+};
+
+/*!
+ \class QPdfPageNavigation
+ \since 5.10
+ \inmodule QtPdf
+
+ \brief The QPdfPageNavigation class handles the navigation through a PDF document.
+
+ \sa QPdfDocument
+*/
+
+
+/*!
+ Constructs a page navigation object with parent object \a parent.
+*/
+QPdfPageNavigation::QPdfPageNavigation(QObject *parent)
+ : QObject(*new QPdfPageNavigationPrivate, parent)
+{
+}
+
+/*!
+ Destroys the page navigation object.
+*/
+QPdfPageNavigation::~QPdfPageNavigation()
+{
+}
+
+/*!
+ \property QPdfPageNavigation::document
+ \brief The document instance on which this object navigates.
+
+ By default, this property is \c nullptr.
+
+ \sa document(), setDocument(), QPdfDocument
+*/
+
+/*!
+ Returns the document on which this object navigates, or a \c nullptr
+ if none has set before.
+
+ \sa QPdfDocument
+*/
+QPdfDocument* QPdfPageNavigation::document() const
+{
+ Q_D(const QPdfPageNavigation);
+
+ return d->m_document;
+}
+
+/*!
+ Sets the \a document this object navigates on.
+
+ After a new document has been set, the currentPage will be \c 0.
+
+ \sa QPdfDocument
+*/
+void QPdfPageNavigation::setDocument(QPdfDocument *document)
+{
+ Q_D(QPdfPageNavigation);
+
+ if (d->m_document == document)
+ return;
+
+ if (d->m_document)
+ disconnect(d->m_documentStatusChangedConnection);
+
+ d->m_document = document;
+ emit documentChanged(d->m_document);
+
+ if (d->m_document)
+ d->m_documentStatusChangedConnection = connect(d->m_document.data(), &QPdfDocument::statusChanged, this, [d](){ d->documentStatusChanged(); });
+
+ d->update();
+}
+
+/*!
+ \property QPdfPageNavigation::currentPage
+ \brief The current page number in the document.
+
+ \sa currentPage(), setCurrentPage()
+*/
+
+/*!
+ Returns the current page number or \c 0 if there is no document set.
+
+ After a document has been loaded, the currentPage will always be \c 0.
+*/
+int QPdfPageNavigation::currentPage() const
+{
+ Q_D(const QPdfPageNavigation);
+
+ return d->m_currentPage;
+}
+
+/*!
+ \fn void QPdfPageNavigation::setCurrentPage(int page)
+
+ Sets the current \a page number.
+*/
+void QPdfPageNavigation::setCurrentPage(int newPage)
+{
+ Q_D(QPdfPageNavigation);
+
+ if (newPage < 0 || newPage >= d->m_pageCount)
+ return;
+
+ if (d->m_currentPage == newPage)
+ return;
+
+ d->m_currentPage = newPage;
+ emit currentPageChanged(d->m_currentPage);
+
+ d->updatePrevNext();
+}
+
+/*!
+ \property QPdfPageNavigation::pageCount
+ \brief The number of pages in the document.
+
+ \sa pageCount()
+*/
+
+/*!
+ Returns the number of pages in the document or \c 0 if there
+ is no document set.
+*/
+int QPdfPageNavigation::pageCount() const
+{
+ Q_D(const QPdfPageNavigation);
+
+ return d->m_pageCount;
+}
+
+/*!
+ \property QPdfPageNavigation::canGoToPreviousPage
+ \brief Indicates whether there is a page before the current page.
+
+ \sa canGoToPreviousPage(), goToPreviousPage()
+*/
+
+/*!
+ Returns whether there is a page before the current one.
+*/
+bool QPdfPageNavigation::canGoToPreviousPage() const
+{
+ Q_D(const QPdfPageNavigation);
+
+ return d->m_canGoToPreviousPage;
+}
+
+/*!
+ \property QPdfPageNavigation::canGoToNextPage
+ \brief Indicates whether there is a page after the current page.
+
+ \sa canGoToNextPage(), goToNextPage()
+*/
+
+/*!
+ Returns whether there is a page after the current one.
+*/
+bool QPdfPageNavigation::canGoToNextPage() const
+{
+ Q_D(const QPdfPageNavigation);
+
+ return d->m_canGoToNextPage;
+}
+
+/*!
+ Changes the current page to the previous page.
+
+ If there is no previous page in the document, nothing happens.
+
+ \sa canGoToPreviousPage
+*/
+void QPdfPageNavigation::goToPreviousPage()
+{
+ Q_D(QPdfPageNavigation);
+
+ if (d->m_currentPage > 0)
+ setCurrentPage(d->m_currentPage - 1);
+}
+
+/*!
+ Changes the current page to the next page.
+
+ If there is no next page in the document, nothing happens.
+
+ \sa canGoToNextPage
+*/
+void QPdfPageNavigation::goToNextPage()
+{
+ Q_D(QPdfPageNavigation);
+
+ if (d->m_currentPage < d->m_pageCount - 1)
+ setCurrentPage(d->m_currentPage + 1);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qpdfpagenavigation.cpp"
diff --git a/src/pdf/qpdfpagerenderer.cpp b/src/pdf/qpdfpagerenderer.cpp
new file mode 100644
index 000000000..e90f66700
--- /dev/null
+++ b/src/pdf/qpdfpagerenderer.cpp
@@ -0,0 +1,359 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpdfpagerenderer.h"
+
+#include <private/qobject_p.h>
+#include <QMutex>
+#include <QPdfDocument>
+#include <QPointer>
+#include <QThread>
+
+QT_BEGIN_NAMESPACE
+
+class RenderWorker : public QObject
+{
+ Q_OBJECT
+
+public:
+ RenderWorker();
+ ~RenderWorker();
+
+ void setDocument(QPdfDocument *document);
+
+public Q_SLOTS:
+ void requestPage(quint64 requestId, int page, QSize imageSize,
+ QPdfDocumentRenderOptions options);
+
+Q_SIGNALS:
+ void pageRendered(int page, QSize imageSize, const QImage &image,
+ QPdfDocumentRenderOptions options, quint64 requestId);
+
+private:
+ QPointer<QPdfDocument> m_document;
+ QMutex m_mutex;
+};
+
+class QPdfPageRendererPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QPdfPageRenderer)
+
+public:
+ QPdfPageRendererPrivate();
+ ~QPdfPageRendererPrivate();
+
+ void handleNextRequest();
+ void requestFinished(int page, QSize imageSize, const QImage &image,
+ QPdfDocumentRenderOptions options, quint64 requestId);
+
+ QPdfPageRenderer::RenderMode m_renderMode = QPdfPageRenderer::SingleThreadedRenderMode;
+ QPointer<QPdfDocument> m_document;
+
+ struct PageRequest
+ {
+ quint64 id;
+ int pageNumber;
+ QSize imageSize;
+ QPdfDocumentRenderOptions options;
+ };
+
+ QVector<PageRequest> m_requests;
+ QVector<PageRequest> m_pendingRequests;
+ quint64 m_requestIdCounter = 1;
+
+ QThread *m_renderThread = nullptr;
+ QScopedPointer<RenderWorker> m_renderWorker;
+};
+
+Q_DECLARE_TYPEINFO(QPdfPageRendererPrivate::PageRequest, Q_PRIMITIVE_TYPE);
+
+
+RenderWorker::RenderWorker()
+ : m_document(nullptr)
+{
+}
+
+RenderWorker::~RenderWorker()
+{
+}
+
+void RenderWorker::setDocument(QPdfDocument *document)
+{
+ const QMutexLocker locker(&m_mutex);
+
+ if (m_document == document)
+ return;
+
+ m_document = document;
+}
+
+void RenderWorker::requestPage(quint64 requestId, int pageNumber, QSize imageSize,
+ QPdfDocumentRenderOptions options)
+{
+ const QMutexLocker locker(&m_mutex);
+
+ if (!m_document || m_document->status() != QPdfDocument::Ready)
+ return;
+
+ const QImage image = m_document->render(pageNumber, imageSize, options);
+
+ emit pageRendered(pageNumber, imageSize, image, options, requestId);
+}
+
+
+QPdfPageRendererPrivate::QPdfPageRendererPrivate()
+ : QObjectPrivate()
+ , m_renderWorker(new RenderWorker)
+{
+}
+
+QPdfPageRendererPrivate::~QPdfPageRendererPrivate()
+{
+ if (m_renderThread) {
+ m_renderThread->quit();
+ m_renderThread->wait();
+ }
+}
+
+void QPdfPageRendererPrivate::handleNextRequest()
+{
+ if (m_requests.isEmpty())
+ return;
+
+ const PageRequest request = m_requests.takeFirst();
+ m_pendingRequests.append(request);
+
+ QMetaObject::invokeMethod(m_renderWorker.data(), "requestPage", Qt::QueuedConnection,
+ Q_ARG(quint64, request.id), Q_ARG(int, request.pageNumber),
+ Q_ARG(QSize, request.imageSize), Q_ARG(QPdfDocumentRenderOptions,
+ request.options));
+}
+
+void QPdfPageRendererPrivate::requestFinished(int page, QSize imageSize, const QImage &image, QPdfDocumentRenderOptions options, quint64 requestId)
+{
+ const auto it = std::find_if(m_pendingRequests.begin(), m_pendingRequests.end(),
+ [page, imageSize, options](const PageRequest &request){ return request.pageNumber == page && request.imageSize == imageSize && request.options == options; });
+
+ if (it != m_pendingRequests.end())
+ m_pendingRequests.erase(it);
+}
+
+/*!
+ \class QPdfPageRenderer
+ \since 5.11
+ \inmodule QtPdf
+
+ \brief The QPdfPageRenderer class encapsulates the rendering of pages of a PDF document.
+
+ The QPdfPageRenderer contains a queue that collects all render requests that are invoked through
+ requestPage(). Depending on the configured RenderMode the QPdfPageRenderer processes this queue
+ in the main UI thread on next event loop invocation (SingleThreadedRenderMode) or in a separate worker thread
+ (MultiThreadedRenderMode) and emits the result through the pageRendered() signal for each request once
+ the rendering is done.
+
+ \sa QPdfDocument
+*/
+
+
+/*!
+ Constructs a page renderer object with parent object \a parent.
+*/
+QPdfPageRenderer::QPdfPageRenderer(QObject *parent)
+ : QObject(*new QPdfPageRendererPrivate(), parent)
+{
+ Q_D(QPdfPageRenderer);
+
+ qRegisterMetaType<QPdfDocumentRenderOptions>();
+
+ connect(d->m_renderWorker.data(), &RenderWorker::pageRendered, this,
+ [this,d](int page, QSize imageSize, const QImage &image, QPdfDocumentRenderOptions options, quint64 requestId) {
+ d->requestFinished(page, imageSize, image, options, requestId);
+ emit pageRendered(page, imageSize, image, options, requestId);
+ d->handleNextRequest();
+ });
+}
+
+/*!
+ Destroys the page renderer object.
+*/
+QPdfPageRenderer::~QPdfPageRenderer()
+{
+}
+
+/*!
+ \enum QPdfPageRenderer::RenderMode
+
+ This enum describes how the pages are rendered.
+
+ \value MultiThreadedRenderMode All pages are rendered in a separate worker thread.
+ \value SingleThreadedRenderMode All pages are rendered in the main UI thread (default).
+
+ \sa renderMode(), setRenderMode()
+*/
+
+/*!
+ \property QPdfPageRenderer::renderMode
+ \brief The mode the renderer uses to render the pages.
+
+ By default, this property is \c QPdfPageRenderer::SingleThreaded.
+
+ \sa setRenderMode(), RenderMode
+*/
+
+/*!
+ Returns the mode of how the pages are rendered.
+
+ \sa RenderMode
+*/
+QPdfPageRenderer::RenderMode QPdfPageRenderer::renderMode() const
+{
+ Q_D(const QPdfPageRenderer);
+
+ return d->m_renderMode;
+}
+
+/*!
+ Sets the mode of how the pages are rendered to \a mode.
+
+ \sa RenderMode
+*/
+void QPdfPageRenderer::setRenderMode(RenderMode mode)
+{
+ Q_D(QPdfPageRenderer);
+
+ if (d->m_renderMode == mode)
+ return;
+
+ d->m_renderMode = mode;
+ emit renderModeChanged(d->m_renderMode);
+
+ if (d->m_renderMode == MultiThreadedRenderMode) {
+ d->m_renderThread = new QThread;
+ d->m_renderWorker->moveToThread(d->m_renderThread);
+ d->m_renderThread->start();
+ } else {
+ d->m_renderThread->quit();
+ d->m_renderThread->wait();
+ delete d->m_renderThread;
+ d->m_renderThread = nullptr;
+
+ // pulling the object from another thread should be fine, once that thread is deleted
+ d->m_renderWorker->moveToThread(this->thread());
+ }
+}
+
+/*!
+ \property QPdfPageRenderer::document
+ \brief The document instance this object renders the pages from.
+
+ By default, this property is \c nullptr.
+
+ \sa document(), setDocument(), QPdfDocument
+*/
+
+/*!
+ Returns the document this objects renders the pages from, or a \c nullptr
+ if none has been set before.
+
+ \sa QPdfDocument
+*/
+QPdfDocument* QPdfPageRenderer::document() const
+{
+ Q_D(const QPdfPageRenderer);
+
+ return d->m_document;
+}
+
+/*!
+ Sets the \a document this object renders the pages from.
+
+ \sa QPdfDocument
+*/
+void QPdfPageRenderer::setDocument(QPdfDocument *document)
+{
+ Q_D(QPdfPageRenderer);
+
+ if (d->m_document == document)
+ return;
+
+ d->m_document = document;
+ emit documentChanged(d->m_document);
+
+ d->m_renderWorker->setDocument(d->m_document);
+}
+
+/*!
+ Requests the renderer to render the page \a pageNumber into a QImage of size \a imageSize
+ according to the provided \a options.
+
+ Once the rendering is done the pageRendered() signal is emitted with the result as parameters.
+
+ The return value is an ID that uniquely identifies the render request. If a request with the
+ same parameters is still in the queue, the ID of that queued request is returned.
+*/
+quint64 QPdfPageRenderer::requestPage(int pageNumber, QSize imageSize,
+ QPdfDocumentRenderOptions options)
+{
+ Q_D(QPdfPageRenderer);
+
+ if (!d->m_document || d->m_document->status() != QPdfDocument::Ready)
+ return 0;
+
+ for (const auto request : qAsConst(d->m_pendingRequests)) {
+ if (request.pageNumber == pageNumber
+ && request.imageSize == imageSize
+ && request.options == options)
+ return request.id;
+ }
+
+ const auto id = d->m_requestIdCounter++;
+
+ QPdfPageRendererPrivate::PageRequest request;
+ request.id = id;
+ request.pageNumber = pageNumber;
+ request.imageSize = imageSize;
+ request.options = options;
+
+ d->m_requests.append(request);
+
+ d->handleNextRequest();
+
+ return id;
+}
+
+QT_END_NAMESPACE
+
+#include "qpdfpagerenderer.moc"
diff --git a/src/pdf/qpdfsearchmodel.cpp b/src/pdf/qpdfsearchmodel.cpp
new file mode 100644
index 000000000..4129c7cb7
--- /dev/null
+++ b/src/pdf/qpdfsearchmodel.cpp
@@ -0,0 +1,310 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpdfdestination.h"
+#include "qpdfdocument_p.h"
+#include "qpdfsearchmodel.h"
+#include "qpdfsearchmodel_p.h"
+#include "qpdfsearchresult_p.h"
+
+#include "third_party/pdfium/public/fpdf_doc.h"
+#include "third_party/pdfium/public/fpdf_text.h"
+
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/QMetaEnum>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qLcS, "qt.pdf.search")
+
+static const int UpdateTimerInterval = 100;
+static const int ContextChars = 20;
+static const double CharacterHitTolerance = 6.0;
+
+QPdfSearchModel::QPdfSearchModel(QObject *parent)
+ : QAbstractListModel(*(new QPdfSearchModelPrivate()), parent)
+{
+ QMetaEnum rolesMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("Role"));
+ for (int r = Qt::UserRole; r < int(Role::_Count); ++r) {
+ QByteArray roleName = QByteArray(rolesMetaEnum.valueToKey(r));
+ if (roleName.isEmpty())
+ continue;
+ roleName[0] = QChar::toLower(roleName[0]);
+ m_roleNames.insert(r, roleName);
+ }
+}
+
+QPdfSearchModel::~QPdfSearchModel() {}
+
+QHash<int, QByteArray> QPdfSearchModel::roleNames() const
+{
+ return m_roleNames;
+}
+
+int QPdfSearchModel::rowCount(const QModelIndex &parent) const
+{
+ Q_D(const QPdfSearchModel);
+ Q_UNUSED(parent)
+ return d->rowCountSoFar;
+}
+
+QVariant QPdfSearchModel::data(const QModelIndex &index, int role) const
+{
+ Q_D(const QPdfSearchModel);
+ const auto pi = const_cast<QPdfSearchModelPrivate*>(d)->pageAndIndexForResult(index.row());
+ if (pi.page < 0)
+ return QVariant();
+ switch (Role(role)) {
+ case Role::Page:
+ return pi.page;
+ case Role::IndexOnPage:
+ return pi.index;
+ case Role::Location:
+ return d->searchResults[pi.page][pi.index].location();
+ case Role::Context:
+ return d->searchResults[pi.page][pi.index].context();
+ case Role::_Count:
+ break;
+ }
+ if (role == Qt::DisplayRole)
+ return d->searchResults[pi.page][pi.index].context();
+ return QVariant();
+}
+
+void QPdfSearchModel::updatePage(int page)
+{
+ Q_D(QPdfSearchModel);
+ d->doSearch(page);
+}
+
+QString QPdfSearchModel::searchString() const
+{
+ Q_D(const QPdfSearchModel);
+ return d->searchString;
+}
+
+void QPdfSearchModel::setSearchString(QString searchString)
+{
+ Q_D(QPdfSearchModel);
+ if (d->searchString == searchString)
+ return;
+
+ d->searchString = searchString;
+ emit searchStringChanged();
+ beginResetModel();
+ d->clearResults();
+ endResetModel();
+}
+
+QVector<QPdfSearchResult> QPdfSearchModel::resultsOnPage(int page) const
+{
+ Q_D(const QPdfSearchModel);
+ const_cast<QPdfSearchModelPrivate *>(d)->doSearch(page);
+ if (d->searchResults.count() <= page)
+ return {};
+ return d->searchResults[page];
+}
+
+QPdfSearchResult QPdfSearchModel::resultAtIndex(int index) const
+{
+ Q_D(const QPdfSearchModel);
+ const auto pi = const_cast<QPdfSearchModelPrivate*>(d)->pageAndIndexForResult(index);
+ if (pi.page < 0)
+ return QPdfSearchResult();
+ return d->searchResults[pi.page][pi.index];
+}
+
+QPdfDocument *QPdfSearchModel::document() const
+{
+ Q_D(const QPdfSearchModel);
+ return d->document;
+}
+
+void QPdfSearchModel::setDocument(QPdfDocument *document)
+{
+ Q_D(QPdfSearchModel);
+ if (d->document == document)
+ return;
+
+ d->document = document;
+ emit documentChanged();
+ d->clearResults();
+}
+
+void QPdfSearchModel::timerEvent(QTimerEvent *event)
+{
+ Q_D(QPdfSearchModel);
+ if (event->timerId() != d->updateTimerId)
+ return;
+ if (!d->document || d->nextPageToUpdate >= d->document->pageCount()) {
+ if (d->document)
+ qCDebug(qLcS, "done updating search results on %d pages", d->searchResults.count());
+ killTimer(d->updateTimerId);
+ d->updateTimerId = -1;
+ }
+ d->doSearch(d->nextPageToUpdate++);
+}
+
+QPdfSearchModelPrivate::QPdfSearchModelPrivate()
+{
+}
+
+void QPdfSearchModelPrivate::clearResults()
+{
+ Q_Q(QPdfSearchModel);
+ rowCountSoFar = 0;
+ searchResults.clear();
+ pagesSearched.clear();
+ if (document) {
+ searchResults.resize(document->pageCount());
+ pagesSearched.resize(document->pageCount());
+ } else {
+ searchResults.resize(0);
+ pagesSearched.resize(0);
+ }
+ nextPageToUpdate = 0;
+ updateTimerId = q->startTimer(UpdateTimerInterval);
+}
+
+bool QPdfSearchModelPrivate::doSearch(int page)
+{
+ if (page < 0 || page >= pagesSearched.count() || searchString.isEmpty())
+ return false;
+ if (pagesSearched[page])
+ return true;
+ Q_Q(QPdfSearchModel);
+
+ const QPdfMutexLocker lock;
+ QElapsedTimer timer;
+ timer.start();
+ FPDF_PAGE pdfPage = FPDF_LoadPage(document->d->doc, page);
+ if (!pdfPage) {
+ qWarning() << "failed to load page" << page;
+ return false;
+ }
+ double pageHeight = FPDF_GetPageHeight(pdfPage);
+ FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage);
+ if (!textPage) {
+ qWarning() << "failed to load text of page" << page;
+ FPDF_ClosePage(pdfPage);
+ return false;
+ }
+ FPDF_SCHHANDLE sh = FPDFText_FindStart(textPage, searchString.utf16(), 0, 0);
+ QVector<QPdfSearchResult> newSearchResults;
+ while (FPDFText_FindNext(sh)) {
+ int idx = FPDFText_GetSchResultIndex(sh);
+ int count = FPDFText_GetSchCount(sh);
+ int rectCount = FPDFText_CountRects(textPage, idx, count);
+ QVector<QRectF> rects;
+ int startIndex = -1;
+ int endIndex = -1;
+ for (int r = 0; r < rectCount; ++r) {
+ double left, top, right, bottom;
+ FPDFText_GetRect(textPage, r, &left, &top, &right, &bottom);
+ rects << QRectF(left, pageHeight - top, right - left, top - bottom);
+ if (r == 0) {
+ startIndex = FPDFText_GetCharIndexAtPos(textPage, left, top,
+ CharacterHitTolerance, CharacterHitTolerance);
+ }
+ if (r == rectCount - 1) {
+ endIndex = FPDFText_GetCharIndexAtPos(textPage, right, top,
+ CharacterHitTolerance, CharacterHitTolerance);
+ }
+ qCDebug(qLcS) << rects.last() << "char idx" << startIndex << "->" << endIndex;
+ }
+ QString context;
+ if (startIndex >= 0 || endIndex >= 0) {
+ startIndex = qMax(0, startIndex - ContextChars);
+ endIndex += ContextChars;
+ int count = endIndex - startIndex + 1;
+ if (count > 0) {
+ QVector<ushort> buf(count + 1);
+ int len = FPDFText_GetText(textPage, startIndex, count, buf.data());
+ Q_ASSERT(len - 1 <= count); // len is number of characters written, including the terminator
+ context = QString::fromUtf16(buf.constData(), len - 1);
+ context = context.replace(QLatin1Char('\n'), QLatin1Char(' '));
+ context = context.replace(searchString,
+ QLatin1String("<b>") + searchString + QLatin1String("</b>"));
+ }
+ }
+ newSearchResults << QPdfSearchResult(page, rects, context);
+ }
+ FPDFText_FindClose(sh);
+ FPDFText_ClosePage(textPage);
+ FPDF_ClosePage(pdfPage);
+ qCDebug(qLcS) << searchString << "took" << timer.elapsed() << "ms to find"
+ << newSearchResults.count() << "results on page" << page;
+
+ pagesSearched[page] = true;
+ searchResults[page] = newSearchResults;
+ if (newSearchResults.count() > 0) {
+ int rowsBefore = rowsBeforePage(page);
+ qCDebug(qLcS) << "from row" << rowsBefore << "rowCount" << rowCountSoFar << "increasing by" << newSearchResults.count();
+ rowCountSoFar += newSearchResults.count();
+ q->beginInsertRows(QModelIndex(), rowsBefore, rowsBefore + newSearchResults.count() - 1);
+ q->endInsertRows();
+ }
+ return true;
+}
+
+QPdfSearchModelPrivate::PageAndIndex QPdfSearchModelPrivate::pageAndIndexForResult(int resultIndex)
+{
+ const int pageCount = document->pageCount();
+ int totalSoFar = 0;
+ int previousTotalSoFar = 0;
+ for (int page = 0; page < pageCount; ++page) {
+ if (!pagesSearched[page])
+ doSearch(page);
+ totalSoFar += searchResults[page].count();
+ if (totalSoFar > resultIndex)
+ return {page, resultIndex - previousTotalSoFar};
+ previousTotalSoFar = totalSoFar;
+ }
+ return {-1, -1};
+}
+
+int QPdfSearchModelPrivate::rowsBeforePage(int page)
+{
+ int ret = 0;
+ for (int i = 0; i < page; ++i)
+ ret += searchResults[i].count();
+ return ret;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qpdfsearchmodel.cpp"
diff --git a/src/pdf/qpdfsearchresult.cpp b/src/pdf/qpdfsearchresult.cpp
new file mode 100644
index 000000000..1164a1d43
--- /dev/null
+++ b/src/pdf/qpdfsearchresult.cpp
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpdfsearchresult.h"
+#include "qpdfsearchresult_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QPdfSearchResult::QPdfSearchResult() :
+ QPdfSearchResult(new QPdfSearchResultPrivate()) { }
+
+QPdfSearchResult::QPdfSearchResult(int page, QVector<QRectF> rects, QString context) :
+ QPdfSearchResult(new QPdfSearchResultPrivate(page, rects, context)) { }
+
+QPdfSearchResult::QPdfSearchResult(QPdfSearchResultPrivate *d) :
+ QPdfDestination(static_cast<QPdfDestinationPrivate *>(d)) { }
+
+QString QPdfSearchResult::context() const
+{
+ return static_cast<QPdfSearchResultPrivate *>(d.data())->context;
+}
+
+QVector<QRectF> QPdfSearchResult::rectangles() const
+{
+ return static_cast<QPdfSearchResultPrivate *>(d.data())->rects;
+}
+
+QDebug operator<<(QDebug dbg, const QPdfSearchResult &searchResult)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+ dbg << "QPdfSearchResult(page=" << searchResult.page()
+ << " context=" << searchResult.context()
+ << " rects=" << searchResult.rectangles();
+ dbg << ')';
+ return dbg;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qpdfsearchresult.cpp"
diff --git a/src/pdf/qpdfselection.cpp b/src/pdf/qpdfselection.cpp
new file mode 100644
index 000000000..8c3d6fde0
--- /dev/null
+++ b/src/pdf/qpdfselection.cpp
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpdfselection.h"
+#include "qpdfselection_p.h"
+#include <QGuiApplication>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QPdfSelection
+ \since 5.15
+ \inmodule QtPdf
+
+ \brief The QPdfSelection class defines a range of text that has been selected
+ on one page in a PDF document, and its geometric boundaries.
+
+ \sa QPdfDocument::getSelection()
+*/
+
+/*!
+ Constructs an invalid selection.
+
+ \sa valid
+*/
+QPdfSelection::QPdfSelection()
+ : d(new QPdfSelectionPrivate())
+{
+}
+
+/*!
+ \internal
+ Constructs a selection including the range of characters that make up the
+ \a text string, and which take up space on the page within the polygon
+ regions given in \a bounds.
+*/
+QPdfSelection::QPdfSelection(const QString &text, QVector<QPolygonF> bounds)
+ : d(new QPdfSelectionPrivate(text, bounds))
+{
+}
+
+QPdfSelection::QPdfSelection(QPdfSelectionPrivate *d)
+ : d(d)
+{
+}
+
+QPdfSelection::QPdfSelection(const QPdfSelection &other)
+ : d(other.d)
+{
+}
+
+QPdfSelection::~QPdfSelection()
+{
+}
+
+/*!
+ \property QPdfSelection::valid
+
+ This property holds whether the selection is valid.
+*/
+bool QPdfSelection::isValid() const
+{
+ return !d->bounds.isEmpty();
+}
+
+/*!
+ \property QPdfSelection::bounds
+
+ This property holds a set of regions that the selected text occupies on the
+ page, represented as polygons. The coordinate system for the polygons has
+ the origin at the upper-left corner of the page, and the units are
+ \l {https://en.wikipedia.org/wiki/Point_(typography)}{points}.
+
+ \note For now, the polygons returned from \l QPdfDocument::getSelection()
+ are always rectangles; but in the future it may be possible to represent
+ more complex regions.
+*/
+QVector<QPolygonF> QPdfSelection::bounds() const
+{
+ return d->bounds;
+}
+
+/*!
+ \property QPdfSelection::text
+
+ This property holds the selected text.
+*/
+QString QPdfSelection::text() const
+{
+ return d->text;
+}
+
+#if QT_CONFIG(clipboard)
+/*!
+ Copies \l text to the \l {QGuiApplication::clipboard()}{system clipboard}.
+*/
+void QPdfSelection::copyToClipboard(QClipboard::Mode mode) const
+{
+ QGuiApplication::clipboard()->setText(d->text, mode);
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_qpdfselection.cpp"
diff --git a/src/pdf/qtpdf.gni b/src/pdf/qtpdf.gni
new file mode 100644
index 000000000..c31f3e9a0
--- /dev/null
+++ b/src/pdf/qtpdf.gni
@@ -0,0 +1,7 @@
+include_dirs = [
+]
+
+deps = [
+ "//third_party/pdfium"
+]
+
diff --git a/src/pdf/quick/plugin.cpp b/src/pdf/quick/plugin.cpp
new file mode 100644
index 000000000..bb68a817e
--- /dev/null
+++ b/src/pdf/quick/plugin.cpp
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtQml/qqml.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlextensionplugin.h>
+#include "qquickpdfdocument_p.h"
+#include "qquickpdflinkmodel_p.h"
+#include "qquickpdfnavigationstack_p.h"
+#include "qquickpdfsearchmodel_p.h"
+#include "qquickpdfselection_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlmodule QtQuick.Pdf 5.15
+ \title Qt Quick PDF QML Types
+ \ingroup qmlmodules
+ \brief Provides QML types for handling PDF documents.
+
+ This QML module contains types for handling PDF documents.
+
+ To use the types in this module, import the module with the following line:
+
+ \code
+ import QtQuick.Pdf 5.15
+ \endcode
+*/
+
+class QtQuick2PdfPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
+
+public:
+ QtQuick2PdfPlugin() : QQmlExtensionPlugin() { }
+
+ void initializeEngine(QQmlEngine *engine, const char *uri) override {
+ Q_UNUSED(uri);
+#ifndef QT_STATIC
+ engine->addImportPath(QStringLiteral("qrc:/"));
+#endif
+ }
+
+ void registerTypes(const char *uri) override {
+ Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.Pdf"));
+
+ // Register the latest version, even if there are no new types or new revisions for existing types yet.
+ qmlRegisterModule(uri, 2, QT_VERSION_MINOR);
+
+ qmlRegisterType<QQuickPdfDocument>(uri, 5, 15, "PdfDocument");
+ qmlRegisterType<QQuickPdfLinkModel>(uri, 5, 15, "PdfLinkModel");
+ qmlRegisterType<QQuickPdfNavigationStack>(uri, 5, 15, "PdfNavigationStack");
+ qmlRegisterType<QQuickPdfSearchModel>(uri, 5, 15, "PdfSearchModel");
+ qmlRegisterType<QQuickPdfSelection>(uri, 5, 15, "PdfSelection");
+
+ qmlRegisterType(QUrl("qrc:/qt-project.org/qtpdf/qml/PdfPageView.qml"), uri, 5, 15, "PdfPageView");
+ qmlRegisterType(QUrl("qrc:/qt-project.org/qtpdf/qml/PdfMultiPageView.qml"), uri, 5, 15, "PdfMultiPageView");
+ qmlRegisterType(QUrl("qrc:/qt-project.org/qtpdf/qml/PdfScrollablePageView.qml"), uri, 5, 15, "PdfScrollablePageView");
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "plugin.moc"
diff --git a/src/pdf/quick/plugins.qmltypes b/src/pdf/quick/plugins.qmltypes
new file mode 100644
index 000000000..a30361d33
--- /dev/null
+++ b/src/pdf/quick/plugins.qmltypes
@@ -0,0 +1,52 @@
+import QtQuick.tooling 1.2
+
+// This file describes the plugin-supplied types contained in the library.
+// It is used for QML tooling purposes only.
+//
+// This file was auto-generated by:
+// 'qmlplugindump -nonrelocatable QtQuick.Pdf 5.14'
+
+Module {
+ dependencies: [
+ "QtGraphicalEffects 1.12",
+ "QtQuick 2.14",
+ "QtQuick.Controls 2.14",
+ "QtQuick.Controls.Fusion 2.14",
+ "QtQuick.Controls.Fusion.impl 2.14",
+ "QtQuick.Controls.Imagine 2.14",
+ "QtQuick.Controls.Imagine.impl 2.14",
+ "QtQuick.Controls.Material 2.14",
+ "QtQuick.Controls.Material.impl 2.14",
+ "QtQuick.Controls.Universal 2.14",
+ "QtQuick.Controls.Universal.impl 2.12",
+ "QtQuick.Controls.impl 2.14",
+ "QtQuick.Shapes 1.14",
+ "QtQuick.Templates 2.14",
+ "QtQuick.Window 2.2"
+ ]
+ Component {
+ name: "QQuickPdfDocument"
+ prototype: "QObject"
+ exports: ["QtQuick.Pdf/PdfDocument 5.14"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "source"; type: "QUrl" }
+ Property { name: "pageCount"; type: "int"; isReadonly: true }
+ Property { name: "password"; type: "string" }
+ Property { name: "status"; type: "QPdfDocument::Status"; isReadonly: true }
+ Property { name: "title"; type: "string"; isReadonly: true }
+ Property { name: "subject"; type: "string"; isReadonly: true }
+ Property { name: "author"; type: "string"; isReadonly: true }
+ Property { name: "keywords"; type: "string"; isReadonly: true }
+ Property { name: "producer"; type: "string"; isReadonly: true }
+ Property { name: "creator"; type: "string"; isReadonly: true }
+ Property { name: "creationDate"; type: "QDateTime"; isReadonly: true }
+ Property { name: "modificationDate"; type: "QDateTime"; isReadonly: true }
+ Signal { name: "passwordRequired" }
+ Signal { name: "metaDataLoaded" }
+ Method {
+ name: "pagePointSize"
+ type: "QSizeF"
+ Parameter { name: "page"; type: "int" }
+ }
+ }
+}
diff --git a/src/pdf/quick/qml/PdfMultiPageView.qml b/src/pdf/quick/qml/PdfMultiPageView.qml
new file mode 100644
index 000000000..e8eccaf3b
--- /dev/null
+++ b/src/pdf/quick/qml/PdfMultiPageView.qml
@@ -0,0 +1,342 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Layouts 1.14
+import QtQuick.Pdf 5.15
+import QtQuick.Shapes 1.14
+import QtQuick.Window 2.14
+
+Item {
+ // public API
+ // TODO 5.15: required property
+ property var document: undefined
+ property bool debug: false
+
+ property string selectedText
+ function copySelectionToClipboard() {
+ var currentItem = tableView.itemAtPos(0, tableView.contentY + root.height / 2)
+ if (debug)
+ console.log("currentItem", currentItem, "sel", currentItem.selection.text)
+ if (currentItem !== null)
+ currentItem.selection.copyToClipboard()
+ }
+
+ // page navigation
+ property alias currentPage: navigationStack.currentPage
+ property alias backEnabled: navigationStack.backAvailable
+ property alias forwardEnabled: navigationStack.forwardAvailable
+ function back() { navigationStack.back() }
+ function forward() { navigationStack.forward() }
+ function goToPage(page) {
+ if (page === navigationStack.currentPage)
+ return
+ goToLocation(page, Qt.point(0, 0), 0)
+ }
+ function goToLocation(page, location, zoom) {
+ if (zoom > 0)
+ root.renderScale = zoom
+ navigationStack.push(page, location, zoom)
+ }
+
+ // page scaling
+ property real renderScale: 1
+ property real pageRotation: 0
+ function resetScale() { root.renderScale = 1 }
+ function scaleToWidth(width, height) {
+ root.renderScale = width / (tableView.rot90 ? tableView.firstPagePointSize.height : tableView.firstPagePointSize.width)
+ }
+ function scaleToPage(width, height) {
+ var windowAspect = width / height
+ var pageAspect = tableView.firstPagePointSize.width / tableView.firstPagePointSize.height
+ if (tableView.rot90) {
+ if (windowAspect > pageAspect) {
+ root.renderScale = height / tableView.firstPagePointSize.width
+ } else {
+ root.renderScale = width / tableView.firstPagePointSize.height
+ }
+ } else {
+ if (windowAspect > pageAspect) {
+ root.renderScale = height / tableView.firstPagePointSize.height
+ } else {
+ root.renderScale = width / tableView.firstPagePointSize.width
+ }
+ }
+ }
+
+ // text search
+ property alias searchModel: searchModel
+ property alias searchString: searchModel.searchString
+ function searchBack() { --searchModel.currentResult }
+ function searchForward() { ++searchModel.currentResult }
+
+ id: root
+ TableView {
+ id: tableView
+ anchors.fill: parent
+ model: root.document === undefined ? 0 : root.document.pageCount
+ rowSpacing: 6
+ property real rotationModulus: Math.abs(root.pageRotation % 180)
+ property bool rot90: rotationModulus > 45 && rotationModulus < 135
+ onRot90Changed: forceLayout()
+ property size firstPagePointSize: document === undefined ? Qt.size(0, 0) : document.pagePointSize(0)
+ contentWidth: document === undefined ? 0 : document.maxPageWidth * root.renderScale
+ // workaround for missing function (see https://codereview.qt-project.org/c/qt/qtdeclarative/+/248464)
+ function itemAtPos(x, y, includeSpacing) {
+ // we don't care about x (assume col 0), and assume includeSpacing is true
+ var ret = null
+ for (var i = 0; i < contentItem.children.length; ++i) {
+ var child = contentItem.children[i];
+ if (root.debug)
+ console.log(child, "@y", child.y)
+ if (child.y < y && (!ret || child.y > ret.y))
+ ret = child
+ }
+ if (root.debug)
+ console.log("given y", y, "found", ret, "@", ret.y)
+ return ret // the delegate with the largest y that is less than the given y
+ }
+ rowHeightProvider: function(row) { return (rot90 ? document.pagePointSize(row).width : document.pagePointSize(row).height) * root.renderScale }
+ delegate: Rectangle {
+ id: pageHolder
+ color: root.debug ? "beige" : "transparent"
+ Text {
+ visible: root.debug
+ anchors { right: parent.right; verticalCenter: parent.verticalCenter }
+ rotation: -90; text: pageHolder.width.toFixed(1) + "x" + pageHolder.height.toFixed(1) + "\n" +
+ image.width.toFixed(1) + "x" + image.height.toFixed(1)
+ }
+ implicitWidth: Math.max(root.width, (tableView.rot90 ? document.maxPageHeight : document.maxPageWidth) * root.renderScale)
+ implicitHeight: tableView.rot90 ? image.width : image.height
+ onImplicitWidthChanged: tableView.forceLayout()
+ objectName: "page " + index
+ property int delegateIndex: row // expose the context property for JS outside of the delegate
+ property alias selection: selection
+ Rectangle {
+ id: paper
+ width: image.width
+ height: image.height
+ rotation: root.pageRotation
+ anchors.centerIn: parent
+ property size pagePointSize: document.pagePointSize(index)
+ property real pageScale: image.paintedWidth / pagePointSize.width
+ Image {
+ id: image
+ source: document.source
+ currentFrame: index
+ asynchronous: true
+ fillMode: Image.PreserveAspectFit
+ width: paper.pagePointSize.width * root.renderScale
+ height: paper.pagePointSize.height * root.renderScale
+ property real renderScale: root.renderScale
+ property real oldRenderScale: 1
+ onRenderScaleChanged: {
+ image.sourceSize.width = paper.pagePointSize.width * renderScale
+ image.sourceSize.height = 0
+ paper.scale = 1
+ }
+ }
+ Shape {
+ anchors.fill: parent
+ opacity: 0.25
+ visible: image.status === Image.Ready
+ ShapePath {
+ strokeWidth: 1
+ strokeColor: "cyan"
+ fillColor: "steelblue"
+ scale: Qt.size(paper.pageScale, paper.pageScale)
+ PathMultiline {
+ paths: searchModel.boundingPolygonsOnPage(index)
+ }
+ }
+ ShapePath {
+ fillColor: "orange"
+ scale: Qt.size(paper.pageScale, paper.pageScale)
+ PathMultiline {
+ id: selectionBoundaries
+ paths: selection.geometry
+ }
+ }
+ }
+ Shape {
+ anchors.fill: parent
+ opacity: 0.5
+ visible: image.status === Image.Ready && searchModel.currentPage === index
+ ShapePath {
+ strokeWidth: 1
+ strokeColor: "blue"
+ fillColor: "cyan"
+ scale: Qt.size(paper.pageScale, paper.pageScale)
+ PathMultiline {
+ paths: searchModel.currentResultBoundingPolygons
+ }
+ }
+ }
+ PinchHandler {
+ id: pinch
+ minimumScale: 0.1
+ maximumScale: root.renderScale < 4 ? 2 : 1
+ minimumRotation: 0
+ maximumRotation: 0
+ enabled: image.sourceSize.width < 5000
+ onActiveChanged:
+ if (active) {
+ paper.z = 10
+ } else {
+ paper.z = 0
+ var newSourceWidth = image.sourceSize.width * paper.scale
+ var ratio = newSourceWidth / image.sourceSize.width
+ if (ratio > 1.1 || ratio < 0.9) {
+ paper.scale = 1
+ root.renderScale *= ratio
+ }
+ }
+ grabPermissions: PointerHandler.CanTakeOverFromAnything
+ }
+ DragHandler {
+ id: textSelectionDrag
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ target: null
+ }
+ TapHandler {
+ id: tapHandler
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ }
+ Repeater {
+ model: PdfLinkModel {
+ id: linkModel
+ document: root.document
+ page: image.currentFrame
+ }
+ delegate: Rectangle {
+ color: "transparent"
+ border.color: "lightgrey"
+ x: rect.x * paper.pageScale
+ y: rect.y * paper.pageScale
+ width: rect.width * paper.pageScale
+ height: rect.height * paper.pageScale
+ MouseArea { // TODO switch to TapHandler / HoverHandler in 5.15
+ id: linkMA
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ hoverEnabled: true
+ onClicked: {
+ if (page >= 0)
+ root.goToLocation(page, location, zoom)
+ else
+ Qt.openUrlExternally(url)
+ }
+ }
+ ToolTip {
+ visible: linkMA.containsMouse
+ delay: 1000
+ text: page >= 0 ?
+ ("page " + (page + 1) +
+ " location " + location.x.toFixed(1) + ", " + location.y.toFixed(1) +
+ " zoom " + zoom) : url
+ }
+ }
+ }
+ }
+ PdfSelection {
+ id: selection
+ document: root.document
+ page: image.currentFrame
+ fromPoint: Qt.point(textSelectionDrag.centroid.pressPosition.x / paper.pageScale,
+ textSelectionDrag.centroid.pressPosition.y / paper.pageScale)
+ toPoint: Qt.point(textSelectionDrag.centroid.position.x / paper.pageScale,
+ textSelectionDrag.centroid.position.y / paper.pageScale)
+ hold: !textSelectionDrag.active && !tapHandler.pressed
+ onTextChanged: root.selectedText = text
+ }
+ }
+ ScrollBar.vertical: ScrollBar {
+ property bool moved: false
+ onPositionChanged: moved = true
+ onActiveChanged: {
+ var currentItem = tableView.itemAtPos(0, tableView.contentY + root.height / 2)
+ var currentPage = currentItem.delegateIndex
+ var currentLocation = Qt.point((tableView.contentX - currentItem.x + root.width / 2) / root.renderScale,
+ (tableView.contentY - currentItem.y + root.height / 2) / root.renderScale)
+ if (active) {
+ moved = false
+ navigationStack.push(currentPage, currentLocation, root.renderScale)
+ } else if (moved) {
+ navigationStack.update(currentPage, currentLocation, root.renderScale)
+ }
+ }
+ }
+ ScrollBar.horizontal: ScrollBar { }
+ }
+ onRenderScaleChanged: {
+ tableView.forceLayout()
+ var currentItem = tableView.itemAtPos(tableView.contentX + root.width / 2, tableView.contentY + root.height / 2)
+ if (currentItem !== undefined)
+ navigationStack.update(currentItem.delegateIndex, Qt.point(currentItem.x / renderScale, currentItem.y / renderScale), renderScale)
+ }
+ PdfNavigationStack {
+ id: navigationStack
+ onJumped: {
+ root.renderScale = zoom
+ tableView.contentX = Math.max(0, location.x - root.width / 2) * root.renderScale
+ tableView.contentY = tableView.originY + root.document.heightSumBeforePage(page, tableView.rowSpacing / root.renderScale) * root.renderScale
+ if (root.debug) {
+ console.log("going to page", page,
+ "@y", root.document.heightSumBeforePage(page, tableView.rowSpacing / root.renderScale) * root.renderScale,
+ "ended up @", tableView.contentY, "originY is", tableView.originY)
+ }
+ }
+ }
+ PdfSearchModel {
+ id: searchModel
+ document: root.document === undefined ? null : root.document
+ onCurrentPageChanged: root.goToPage(currentPage)
+ }
+}
diff --git a/src/pdf/quick/qml/PdfPageView.qml b/src/pdf/quick/qml/PdfPageView.qml
new file mode 100644
index 000000000..dfd00a1a8
--- /dev/null
+++ b/src/pdf/quick/qml/PdfPageView.qml
@@ -0,0 +1,273 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Pdf 5.15
+import QtQuick.Shapes 1.14
+import Qt.labs.animation 1.0
+
+Rectangle {
+ // public API
+ // TODO 5.15: required property
+ property var document: undefined
+ property alias status: image.status
+
+ property alias selectedText: selection.text
+ function copySelectionToClipboard() {
+ selection.copyToClipboard()
+ }
+
+ // page navigation
+ property alias currentPage: navigationStack.currentPage
+ property alias backEnabled: navigationStack.backAvailable
+ property alias forwardEnabled: navigationStack.forwardAvailable
+ function back() { navigationStack.back() }
+ function forward() { navigationStack.forward() }
+ function goToPage(page) { goToLocation(page, Qt.point(0, 0), 0) }
+ function goToLocation(page, location, zoom) {
+ if (zoom > 0)
+ root.renderScale = zoom
+ navigationStack.push(page, location, zoom)
+ }
+
+ // page scaling
+ property real renderScale: 1
+ property alias sourceSize: image.sourceSize
+ function resetScale() {
+ image.sourceSize.width = 0
+ image.sourceSize.height = 0
+ root.x = 0
+ root.y = 0
+ root.scale = 1
+ }
+ function scaleToWidth(width, height) {
+ var halfRotation = Math.abs(root.rotation % 180)
+ image.sourceSize = Qt.size((halfRotation > 45 && halfRotation < 135) ? height : width, 0)
+ root.x = 0
+ root.y = 0
+ image.centerInSize = Qt.size(width, height)
+ image.centerOnLoad = true
+ image.vCenterOnLoad = (halfRotation > 45 && halfRotation < 135)
+ root.scale = 1
+ }
+ function scaleToPage(width, height) {
+ var windowAspect = width / height
+ var halfRotation = Math.abs(root.rotation % 180)
+ var pagePointSize = document.pagePointSize(navigationStack.currentPage)
+ if (halfRotation > 45 && halfRotation < 135) {
+ // rotated 90 or 270º
+ var pageAspect = pagePointSize.height / pagePointSize.width
+ if (windowAspect > pageAspect) {
+ image.sourceSize = Qt.size(height, 0)
+ } else {
+ image.sourceSize = Qt.size(0, width)
+ }
+ } else {
+ var pageAspect = pagePointSize.width / pagePointSize.height
+ if (windowAspect > pageAspect) {
+ image.sourceSize = Qt.size(0, height)
+ } else {
+ image.sourceSize = Qt.size(width, 0)
+ }
+ }
+ image.centerInSize = Qt.size(width, height)
+ image.centerOnLoad = true
+ image.vCenterOnLoad = true
+ root.scale = 1
+ }
+
+ // text search
+ property alias searchModel: searchModel
+ property alias searchString: searchModel.searchString
+ function searchBack() { --searchModel.currentResult }
+ function searchForward() { ++searchModel.currentResult }
+
+ // implementation
+ id: root
+ width: image.width
+ height: image.height
+
+ PdfSelection {
+ id: selection
+ document: root.document
+ page: navigationStack.currentPage
+ fromPoint: Qt.point(textSelectionDrag.centroid.pressPosition.x / image.pageScale, textSelectionDrag.centroid.pressPosition.y / image.pageScale)
+ toPoint: Qt.point(textSelectionDrag.centroid.position.x / image.pageScale, textSelectionDrag.centroid.position.y / image.pageScale)
+ hold: !textSelectionDrag.active && !tapHandler.pressed
+ }
+
+ PdfSearchModel {
+ id: searchModel
+ document: root.document === undefined ? null : root.document
+ onCurrentPageChanged: root.goToPage(currentPage)
+ }
+
+ PdfNavigationStack {
+ id: navigationStack
+ onCurrentPageChanged: searchModel.currentPage = currentPage
+ // TODO onCurrentLocationChanged: position currentLocation.x and .y in middle // currentPageChanged() MUST occur first!
+ onCurrentZoomChanged: root.renderScale = currentZoom
+ // TODO deal with horizontal location (need WheelHandler or Flickable probably)
+ }
+
+ Image {
+ id: image
+ currentFrame: navigationStack.currentPage
+ source: document.status === PdfDocument.Ready ? document.source : ""
+ asynchronous: true
+ fillMode: Image.PreserveAspectFit
+ property bool centerOnLoad: false
+ property bool vCenterOnLoad: false
+ property size centerInSize
+ property real pageScale: image.paintedWidth / document.pagePointSize(navigationStack.currentPage).width
+ function reRenderIfNecessary() {
+ var newSourceWidth = image.sourceSize.width * root.scale
+ var ratio = newSourceWidth / image.sourceSize.width
+ if (ratio > 1.1 || ratio < 0.9) {
+ image.sourceSize.width = newSourceWidth
+ image.sourceSize.height = 0
+ root.scale = 1
+ }
+ }
+ onStatusChanged:
+ if (status == Image.Ready && centerOnLoad) {
+ root.x = (centerInSize.width - image.implicitWidth) / 2
+ root.y = vCenterOnLoad ? (centerInSize.height - image.implicitHeight) / 2 : 0
+ centerOnLoad = false
+ vCenterOnLoad = false
+ }
+ }
+ onRenderScaleChanged: {
+ image.sourceSize.width = document.pagePointSize(navigationStack.currentPage).width * renderScale
+ image.sourceSize.height = 0
+ root.scale = 1
+ }
+
+ Shape {
+ anchors.fill: parent
+ opacity: 0.25
+ visible: image.status === Image.Ready
+ ShapePath {
+ strokeWidth: 1
+ strokeColor: "cyan"
+ fillColor: "steelblue"
+ scale: Qt.size(image.pageScale, image.pageScale)
+ PathMultiline {
+ paths: searchModel.currentPageBoundingPolygons
+ }
+ }
+ ShapePath {
+ strokeWidth: 1
+ strokeColor: "orange"
+ fillColor: "cyan"
+ scale: Qt.size(image.pageScale, image.pageScale)
+ PathMultiline {
+ paths: searchModel.currentResultBoundingPolygons
+ }
+ }
+ ShapePath {
+ fillColor: "orange"
+ scale: Qt.size(image.pageScale, image.pageScale)
+ PathMultiline {
+ paths: selection.geometry
+ }
+ }
+ }
+
+ Repeater {
+ model: PdfLinkModel {
+ id: linkModel
+ document: root.document
+ page: navigationStack.currentPage
+ }
+ delegate: Rectangle {
+ color: "transparent"
+ border.color: "lightgrey"
+ x: rect.x * image.pageScale
+ y: rect.y * image.pageScale
+ width: rect.width * image.pageScale
+ height: rect.height * image.pageScale
+ MouseArea { // TODO switch to TapHandler / HoverHandler in 5.15
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ onClicked: {
+ if (page >= 0)
+ navigationStack.push(page, Qt.point(0, 0), root.renderScale)
+ else
+ Qt.openUrlExternally(url)
+ }
+ }
+ }
+ }
+
+ PinchHandler {
+ id: pinch
+ minimumScale: 0.1
+ maximumScale: 10
+ minimumRotation: 0
+ maximumRotation: 0
+ onActiveChanged: if (!active) image.reRenderIfNecessary()
+ grabPermissions: PinchHandler.TakeOverForbidden // don't allow takeover if pinch has started
+ }
+ DragHandler {
+ id: pageMovingTouchDrag
+ acceptedDevices: PointerDevice.TouchScreen
+ }
+ DragHandler {
+ id: pageMovingMiddleMouseDrag
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ acceptedButtons: Qt.MiddleButton
+ snapMode: DragHandler.NoSnap
+ }
+ DragHandler {
+ id: textSelectionDrag
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ target: null
+ }
+ TapHandler {
+ id: tapHandler
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ }
+ // prevent it from being scrolled out of view
+ BoundaryRule on x {
+ minimum: 100 - root.width
+ maximum: root.parent.width - 100
+ }
+ BoundaryRule on y {
+ minimum: 100 - root.height
+ maximum: root.parent.height - 100
+ }
+}
diff --git a/src/pdf/quick/qml/PdfScrollablePageView.qml b/src/pdf/quick/qml/PdfScrollablePageView.qml
new file mode 100644
index 000000000..55aa44bbf
--- /dev/null
+++ b/src/pdf/quick/qml/PdfScrollablePageView.qml
@@ -0,0 +1,274 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Pdf 5.15
+import QtQuick.Shapes 1.14
+import Qt.labs.animation 1.0
+
+Flickable {
+ // public API
+ // TODO 5.15: required property
+ property var document: undefined
+ property bool debug: false
+ property alias status: image.status
+
+ property alias selectedText: selection.text
+ function copySelectionToClipboard() {
+ selection.copyToClipboard()
+ }
+
+ // page navigation
+ property alias currentPage: navigationStack.currentPage
+ property alias backEnabled: navigationStack.backAvailable
+ property alias forwardEnabled: navigationStack.forwardAvailable
+ function back() { navigationStack.back() }
+ function forward() { navigationStack.forward() }
+ function goToPage(page) {
+ if (page === navigationStack.currentPage)
+ return
+ goToLocation(page, Qt.point(0, 0), 0)
+ }
+ function goToLocation(page, location, zoom) {
+ if (zoom > 0)
+ root.renderScale = zoom
+ navigationStack.push(page, location, zoom)
+ }
+
+ // page scaling
+ property real renderScale: 1
+ property real pageRotation: 0
+ property alias sourceSize: image.sourceSize
+ function resetScale() {
+ paper.scale = 1
+ root.renderScale = 1
+ }
+ function scaleToWidth(width, height) {
+ var pagePointSize = document.pagePointSize(navigationStack.currentPage)
+ root.renderScale = root.width / (paper.rot90 ? pagePointSize.height : pagePointSize.width)
+ if (debug)
+ console.log("scaling", pagePointSize, "to fit", root.width, "rotated?", paper.rot90, "scale", root.renderScale)
+ root.contentX = 0
+ root.contentY = 0
+ }
+ function scaleToPage(width, height) {
+ var pagePointSize = document.pagePointSize(navigationStack.currentPage)
+ root.renderScale = Math.min(
+ root.width / (paper.rot90 ? pagePointSize.height : pagePointSize.width),
+ root.height / (paper.rot90 ? pagePointSize.width : pagePointSize.height) )
+ root.contentX = 0
+ root.contentY = 0
+ }
+
+ // text search
+ property alias searchModel: searchModel
+ property alias searchString: searchModel.searchString
+ function searchBack() { --searchModel.currentResult }
+ function searchForward() { ++searchModel.currentResult }
+
+ // implementation
+ id: root
+ contentWidth: paper.width
+ contentHeight: paper.height
+ ScrollBar.vertical: ScrollBar {
+ onActiveChanged:
+ if (!active ) {
+ var currentLocation = Qt.point((root.contentX + root.width / 2) / root.renderScale,
+ (root.contentY + root.height / 2) / root.renderScale)
+ navigationStack.update(navigationStack.currentPage, currentLocation, root.renderScale)
+ }
+ }
+ ScrollBar.horizontal: ScrollBar {
+ onActiveChanged:
+ if (!active ) {
+ var currentLocation = Qt.point((root.contentX + root.width / 2) / root.renderScale,
+ (root.contentY + root.height / 2) / root.renderScale)
+ navigationStack.update(navigationStack.currentPage, currentLocation, root.renderScale)
+ }
+ }
+
+ onRenderScaleChanged: {
+ image.sourceSize.width = document.pagePointSize(navigationStack.currentPage).width * renderScale
+ image.sourceSize.height = 0
+ paper.scale = 1
+ var currentLocation = Qt.point((root.contentX + root.width / 2) / root.renderScale,
+ (root.contentY + root.height / 2) / root.renderScale)
+ navigationStack.update(navigationStack.currentPage, currentLocation, root.renderScale)
+ }
+
+ PdfSelection {
+ id: selection
+ document: root.document
+ page: navigationStack.currentPage
+ fromPoint: Qt.point(textSelectionDrag.centroid.pressPosition.x / image.pageScale,
+ textSelectionDrag.centroid.pressPosition.y / image.pageScale)
+ toPoint: Qt.point(textSelectionDrag.centroid.position.x / image.pageScale,
+ textSelectionDrag.centroid.position.y / image.pageScale)
+ hold: !textSelectionDrag.active && !tapHandler.pressed
+ }
+
+ PdfSearchModel {
+ id: searchModel
+ document: root.document === undefined ? null : root.document
+ onCurrentPageChanged: root.goToPage(currentPage)
+ }
+
+ PdfNavigationStack {
+ id: navigationStack
+ onJumped: {
+ root.renderScale = zoom
+ root.contentX = Math.max(0, location.x * root.renderScale - root.width / 2)
+ root.contentY = Math.max(0, location.y * root.renderScale - root.height / 2)
+ if (root.debug)
+ console.log("going to zoom", zoom, "loc", location,
+ "on page", page, "ended up @", root.contentX + ", " + root.contentY)
+ }
+ onCurrentPageChanged: searchModel.currentPage = currentPage
+ }
+
+ Rectangle {
+ id: paper
+ width: rot90 ? image.height : image.width
+ height: rot90 ? image.width : image.height
+ property real rotationModulus: Math.abs(root.pageRotation % 180)
+ property bool rot90: rotationModulus > 45 && rotationModulus < 135
+
+ Image {
+ id: image
+ currentFrame: navigationStack.currentPage
+ source: document.status === PdfDocument.Ready ? document.source : ""
+ asynchronous: true
+ fillMode: Image.PreserveAspectFit
+ rotation: root.pageRotation
+ anchors.centerIn: parent
+ property real pageScale: image.paintedWidth / document.pagePointSize(navigationStack.currentPage).width
+ }
+
+ Shape {
+ anchors.fill: parent
+ opacity: 0.25
+ visible: image.status === Image.Ready
+ ShapePath {
+ strokeWidth: 1
+ strokeColor: "cyan"
+ fillColor: "steelblue"
+ scale: Qt.size(image.pageScale, image.pageScale)
+ PathMultiline {
+ paths: searchModel.currentPageBoundingPolygons
+ }
+ }
+ ShapePath {
+ strokeWidth: 1
+ strokeColor: "orange"
+ fillColor: "cyan"
+ scale: Qt.size(image.pageScale, image.pageScale)
+ PathMultiline {
+ paths: searchModel.currentResultBoundingPolygons
+ }
+ }
+ ShapePath {
+ fillColor: "orange"
+ scale: Qt.size(image.pageScale, image.pageScale)
+ PathMultiline {
+ paths: selection.geometry
+ }
+ }
+ }
+
+ Repeater {
+ model: PdfLinkModel {
+ id: linkModel
+ document: root.document
+ page: navigationStack.currentPage
+ }
+ delegate: Rectangle {
+ color: "transparent"
+ border.color: "lightgrey"
+ x: rect.x * image.pageScale
+ y: rect.y * image.pageScale
+ width: rect.width * image.pageScale
+ height: rect.height * image.pageScale
+ MouseArea { // TODO switch to TapHandler / HoverHandler in 5.15
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ onClicked: {
+ if (page >= 0)
+ navigationStack.push(page, Qt.point(0, 0), root.renderScale)
+ else
+ Qt.openUrlExternally(url)
+ }
+ }
+ }
+ }
+
+ PinchHandler {
+ id: pinch
+ minimumScale: 0.1
+ maximumScale: root.renderScale < 4 ? 2 : 1
+ minimumRotation: 0
+ maximumRotation: 0
+ enabled: image.sourceSize.width < 5000
+ onActiveChanged:
+ if (!active) {
+ var newSourceWidth = image.sourceSize.width * paper.scale
+ var ratio = newSourceWidth / image.sourceSize.width
+ if (ratio > 1.1 || ratio < 0.9) {
+ paper.scale = 1
+ root.renderScale *= ratio
+ }
+ // TODO adjust contentX/Y to position the page so the same region is visible
+ paper.x = 0
+ paper.y = 0
+ }
+ grabPermissions: PointerHandler.CanTakeOverFromAnything
+ }
+ DragHandler {
+ id: pageMovingMiddleMouseDrag
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ acceptedButtons: Qt.MiddleButton
+ snapMode: DragHandler.NoSnap
+ }
+ DragHandler {
+ id: textSelectionDrag
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ target: null
+ }
+ TapHandler {
+ id: tapHandler
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ }
+ }
+}
diff --git a/src/pdf/quick/qmldir b/src/pdf/quick/qmldir
new file mode 100644
index 000000000..65fa95cda
--- /dev/null
+++ b/src/pdf/quick/qmldir
@@ -0,0 +1,4 @@
+module QtQuick.Pdf
+plugin pdfplugin
+classname QtQuick2PdfPlugin
+typeinfo plugins.qmltypes
diff --git a/src/pdf/quick/qquickpdfdocument.cpp b/src/pdf/quick/qquickpdfdocument.cpp
new file mode 100644
index 000000000..3d5f0fa10
--- /dev/null
+++ b/src/pdf/quick/qquickpdfdocument.cpp
@@ -0,0 +1,306 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickpdfdocument_p.h"
+#include <QQuickItem>
+#include <QQmlEngine>
+#include <QStandardPaths>
+#include <private/qguiapplication_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype PdfDocument
+ \instantiates QQuickPdfDocument
+ \inqmlmodule QtQuick.Pdf
+ \ingroup pdf
+ \brief A representation of a PDF document.
+ \since 5.15
+
+ PdfDocument provides access to PDF document meta-information.
+ It is not necessary for rendering, as it is enough to use an
+ \l Image with source set to the URL of the PDF.
+*/
+
+/*!
+ Constructs a PDF document.
+*/
+QQuickPdfDocument::QQuickPdfDocument(QObject *parent)
+ : QObject(parent)
+{
+ connect(&m_doc, &QPdfDocument::passwordChanged, this, &QQuickPdfDocument::passwordChanged);
+ connect(&m_doc, &QPdfDocument::passwordRequired, this, &QQuickPdfDocument::passwordRequired);
+ connect(&m_doc, &QPdfDocument::statusChanged, [=] (QPdfDocument::Status status) {
+ emit statusChanged();
+ if (status == QPdfDocument::Ready)
+ emit metaDataChanged();
+ });
+ connect(&m_doc, &QPdfDocument::pageCountChanged, this, &QQuickPdfDocument::pageCountChanged);
+}
+
+void QQuickPdfDocument::componentComplete()
+{
+ if (m_doc.error() == QPdfDocument::IncorrectPasswordError)
+ emit passwordRequired();
+}
+
+/*!
+ \qmlproperty url PdfDocument::source
+
+ This property holds a URL pointing to the PDF file to be loaded.
+
+ \note At this time, only local filesystem URLs are supported.
+*/
+void QQuickPdfDocument::setSource(QUrl source)
+{
+ if (m_source == source)
+ return;
+
+ m_source = source;
+ m_maxPageWidthHeight = QSizeF();
+ emit sourceChanged();
+ if (source.scheme() == QLatin1String("qrc"))
+ m_doc.load(QLatin1Char(':') + source.path());
+ else
+ m_doc.load(source.path());
+}
+
+/*!
+ \qmlproperty string PdfDocument::error
+
+ This property holds a translated string representation of the current
+ error, if any.
+
+ \sa status
+*/
+QString QQuickPdfDocument::error() const
+{
+ switch (m_doc.error()) {
+ case QPdfDocument::NoError:
+ return tr("no error");
+ break;
+ case QPdfDocument::UnknownError:
+ break;
+ case QPdfDocument::DataNotYetAvailableError:
+ return tr("data not yet available");
+ break;
+ case QPdfDocument::FileNotFoundError:
+ return tr("file not found");
+ break;
+ case QPdfDocument::InvalidFileFormatError:
+ return tr("invalid file format");
+ break;
+ case QPdfDocument::IncorrectPasswordError:
+ return tr("incorrect password");
+ break;
+ case QPdfDocument::UnsupportedSecuritySchemeError:
+ return tr("unsupported security scheme");
+ break;
+ }
+ return tr("unknown error");
+}
+
+/*!
+ \qmlproperty bool PdfDocument::password
+
+ This property holds the document password. If the passwordRequired()
+ signal is emitted, the UI should prompt the user and then set this
+ property so that document opening can continue.
+*/
+void QQuickPdfDocument::setPassword(const QString &password)
+{
+ if (m_doc.password() == password)
+ return;
+ m_doc.setPassword(password);
+ if (source().isValid() && source().isLocalFile())
+ m_doc.load(source().path());
+}
+
+/*!
+ \qmlproperty int PdfDocument::pageCount
+
+ This property holds the number of pages the PDF contains.
+*/
+
+/*!
+ \qmlsignal PdfDocument::passwordRequired()
+
+ This signal is emitted when the PDF requires a password in order to open.
+ The UI in a typical PDF viewer should prompt the user for the password
+ and then set the password property when the user has provided it.
+*/
+
+/*!
+ \qmlmethod size PdfDocument::pagePointSize(int page)
+
+ Returns the size of the given \a page in points.
+*/
+QSizeF QQuickPdfDocument::pagePointSize(int page) const
+{
+ return m_doc.pageSize(page);
+}
+
+qreal QQuickPdfDocument::maxPageWidth() const
+{
+ const_cast<QQuickPdfDocument *>(this)->updateMaxPageSize();
+ return m_maxPageWidthHeight.width();
+}
+
+qreal QQuickPdfDocument::maxPageHeight() const
+{
+ const_cast<QQuickPdfDocument *>(this)->updateMaxPageSize();
+ return m_maxPageWidthHeight.height();
+}
+
+/*!
+ \internal
+ \qmlmethod size PdfDocument::heightSumBeforePage(int page)
+
+ Returns the sum of the heights, in points, of all sets of \a facingPages
+ pages from 0 to the given \a page, exclusive.
+
+ That is, if the pages were laid out end-to-end in adjacent sets of
+ \a facingPages, what would be the distance in points from the top of the
+ first page to the top of the given page.
+*/
+// Workaround for lack of something analogous to ListView.positionViewAtIndex() in TableView
+qreal QQuickPdfDocument::heightSumBeforePage(int page, qreal spacing, int facingPages) const
+{
+ qreal ret = 0;
+ for (int i = 0; i < page; i+= facingPages) {
+ if (i + facingPages > page)
+ break;
+ qreal facingPagesHeight = 0;
+ for (int j = i; j < i + facingPages; ++j)
+ facingPagesHeight = qMax(facingPagesHeight, pagePointSize(j).height());
+ ret += facingPagesHeight + spacing;
+ }
+ return ret;
+}
+
+void QQuickPdfDocument::updateMaxPageSize()
+{
+ if (m_maxPageWidthHeight.isValid())
+ return;
+ qreal w = 0;
+ qreal h = 0;
+ const int count = pageCount();
+ for (int i = 0; i < count; ++i) {
+ auto size = pagePointSize(i);
+ w = qMax(w, size.width());
+ h = qMax(w, size.height());
+ }
+ m_maxPageWidthHeight = QSizeF(w, h);
+}
+
+/*!
+ \qmlproperty real PdfDocument::maxPageWidth
+
+ This property holds the width of the widest page in the document, in points.
+*/
+
+/*!
+ \qmlproperty real PdfDocument::maxPageHeight
+
+ This property holds the height of the tallest page in the document, in points.
+*/
+
+/*!
+ \qmlproperty string PdfDocument::title
+
+ This property holds the document's title. A typical viewer UI can bind this
+ to the \c Window.title property.
+*/
+
+/*!
+ \qmlproperty string PdfDocument::author
+
+ This property holds the name of the person who created the document.
+*/
+
+/*!
+ \qmlproperty string PdfDocument::subject
+
+ This property holds the subject of the document.
+*/
+
+/*!
+ \qmlproperty string PdfDocument::keywords
+
+ This property holds the keywords associated with the document.
+*/
+
+/*!
+ \qmlproperty string PdfDocument::creator
+
+ If the document was converted to PDF from another format, this property
+ holds the name of the software that created the original document.
+*/
+
+/*!
+ \qmlproperty string PdfDocument::producer
+
+ If the document was converted to PDF from another format, this property
+ holds the name of the software that converted it to PDF.
+*/
+
+/*!
+ \qmlproperty string PdfDocument::creationDate
+
+ This property holds the date and time the document was created.
+*/
+
+/*!
+ \qmlproperty string PdfDocument::modificationDate
+
+ This property holds the date and time the document was most recently
+ modified.
+*/
+
+/*!
+ \qmlproperty enum PdfDocument::status
+
+ This property tells the current status of the document. The possible values are:
+
+ \value PdfDocument.Null The initial status after the document has been created or after it has been closed.
+ \value PdfDocument.Loading The status after load() has been called and before the document is fully loaded.
+ \value PdfDocument.Ready The status when the document is fully loaded and its data can be accessed.
+ \value PdfDocument.Unloading The status after close() has been called on an open document.
+ At this point the document is still valid and all its data can be accessed.
+ \value PdfDocument.Error The status after Loading, if loading has failed.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/pdf/quick/qquickpdfdocument_p.h b/src/pdf/quick/qquickpdfdocument_p.h
new file mode 100644
index 000000000..cefa4f756
--- /dev/null
+++ b/src/pdf/quick/qquickpdfdocument_p.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPDFDOCUMENT_P_H
+#define QQUICKPDFDOCUMENT_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 <QtPdf/QPdfDocument>
+#include <QDateTime>
+#include <QJSValue>
+#include <QQmlParserStatus>
+#include <QUrl>
+#include <QVariant>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickPdfDocument : public QObject, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
+ Q_PROPERTY(int pageCount READ pageCount NOTIFY pageCountChanged FINAL)
+ Q_PROPERTY(qreal maxPageWidth READ maxPageWidth NOTIFY metaDataChanged)
+ Q_PROPERTY(qreal maxPageHeight READ maxPageHeight NOTIFY metaDataChanged)
+ Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged FINAL)
+ Q_PROPERTY(QPdfDocument::Status status READ status NOTIFY statusChanged FINAL)
+ Q_PROPERTY(QString error READ error NOTIFY statusChanged FINAL)
+
+ Q_PROPERTY(QString title READ title NOTIFY metaDataChanged)
+ Q_PROPERTY(QString subject READ subject NOTIFY metaDataChanged)
+ Q_PROPERTY(QString author READ author NOTIFY metaDataChanged)
+ Q_PROPERTY(QString keywords READ keywords NOTIFY metaDataChanged)
+ Q_PROPERTY(QString producer READ producer NOTIFY metaDataChanged)
+ Q_PROPERTY(QString creator READ creator NOTIFY metaDataChanged)
+ Q_PROPERTY(QDateTime creationDate READ creationDate NOTIFY metaDataChanged)
+ Q_PROPERTY(QDateTime modificationDate READ modificationDate NOTIFY metaDataChanged)
+
+public:
+ explicit QQuickPdfDocument(QObject *parent = nullptr);
+
+ void classBegin() override {}
+ void componentComplete() override;
+
+ QUrl source() const { return m_source; }
+ void setSource(QUrl source);
+
+ int pageCount() const { return m_doc.pageCount(); }
+ QPdfDocument::Status status() const { return m_doc.status(); }
+
+ QString error() const;
+
+ QString password() const { return m_doc.password(); }
+ void setPassword(const QString &password);
+
+ QString title() { return m_doc.metaData(QPdfDocument::Title).toString(); }
+ QString author() { return m_doc.metaData(QPdfDocument::Author).toString(); }
+ QString subject() { return m_doc.metaData(QPdfDocument::Subject).toString(); }
+ QString keywords() { return m_doc.metaData(QPdfDocument::Keywords).toString(); }
+ QString producer() { return m_doc.metaData(QPdfDocument::Producer).toString(); }
+ QString creator() { return m_doc.metaData(QPdfDocument::Creator).toString(); }
+ QDateTime creationDate() { return m_doc.metaData(QPdfDocument::CreationDate).toDateTime(); }
+ QDateTime modificationDate() { return m_doc.metaData(QPdfDocument::ModificationDate).toDateTime(); }
+
+ Q_INVOKABLE QSizeF pagePointSize(int page) const;
+ qreal maxPageWidth() const;
+ qreal maxPageHeight() const;
+ Q_INVOKABLE qreal heightSumBeforePage(int page, qreal spacing = 0, int facingPages = 1) const;
+
+Q_SIGNALS:
+ void sourceChanged();
+ void passwordChanged();
+ void passwordRequired();
+ void statusChanged();
+ void pageCountChanged();
+ void metaDataChanged();
+
+private:
+ QPdfDocument &document() { return m_doc; }
+ void updateMaxPageSize();
+
+private:
+ QUrl m_source;
+ QPdfDocument m_doc;
+ QSizeF m_maxPageWidthHeight;
+
+ friend class QQuickPdfLinkModel;
+ friend class QQuickPdfSearchModel;
+ friend class QQuickPdfSelection;
+
+ Q_DISABLE_COPY(QQuickPdfDocument)
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPDFDOCUMENT_P_H
diff --git a/src/pdf/quick/qquickpdflinkmodel.cpp b/src/pdf/quick/qquickpdflinkmodel.cpp
new file mode 100644
index 000000000..f2ff3fd22
--- /dev/null
+++ b/src/pdf/quick/qquickpdflinkmodel.cpp
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickpdflinkmodel_p.h"
+#include <QQuickItem>
+#include <QQmlEngine>
+#include <QStandardPaths>
+#include <private/qguiapplication_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype PdfLinkModel
+ \instantiates QQuickPdfLinkModel
+ \inqmlmodule QtQuick.Pdf
+ \ingroup pdf
+ \brief A representation of links within a PDF document.
+ \since 5.15
+
+ PdfLinkModel provides the geometry and the destination for each link
+ that the specified \l page contains.
+
+ The available model roles are:
+
+ \value rect
+ Bounding rectangle around the link.
+ \value url
+ If the link is a web link, the URL for that; otherwise an empty URL.
+ \value page
+ If the link is an internal link, the page number to which the link should jump; otherwise \c {-1}.
+ \value location
+ If the link is an internal link, the location on the page to which the link should jump.
+ \value zoom
+ If the link is an internal link, the intended zoom level on the destination page.
+
+ Normally it will be used with \l {QtQuick::Repeater}{Repeater} to visualize
+ the links and provide the ability to click them:
+
+ \qml
+ Repeater {
+ model: PdfLinkModel {
+ document: root.document
+ page: image.currentFrame
+ }
+ delegate: Rectangle {
+ color: "transparent"
+ border.color: "lightgrey"
+ x: rect.x
+ y: rect.y
+ width: rect.width
+ height: rect.height
+ HoverHandler { cursorShape: Qt.PointingHandCursor }
+ TapHandler {
+ onTapped: {
+ if (page >= 0)
+ image.currentFrame = page
+ else
+ Qt.openUrlExternally(url)
+ }
+ }
+ }
+ }
+ \endqml
+
+ \note General-purpose PDF viewing capabilities are provided by
+ \l PdfScrollablePageView and \l PdfMultiPageView. PdfLinkModel is only needed
+ when building PDF view components from scratch.
+*/
+
+QQuickPdfLinkModel::QQuickPdfLinkModel(QObject *parent)
+ : QPdfLinkModel(parent)
+{
+}
+
+/*!
+ \qmlproperty PdfDocument PdfLinkModel::document
+
+ This property holds the PDF document in which links are to be found.
+*/
+QQuickPdfDocument *QQuickPdfLinkModel::document() const
+{
+ return m_quickDocument;
+}
+
+void QQuickPdfLinkModel::setDocument(QQuickPdfDocument *document)
+{
+ if (document == m_quickDocument)
+ return;
+ m_quickDocument = document;
+ QPdfLinkModel::setDocument(&document->m_doc);
+}
+
+/*!
+ \qmlproperty int PdfLinkModel::page
+
+ This property holds the page number on which links are to be found.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/pdf/quick/qquickpdflinkmodel_p.h b/src/pdf/quick/qquickpdflinkmodel_p.h
new file mode 100644
index 000000000..23ad6c8c1
--- /dev/null
+++ b/src/pdf/quick/qquickpdflinkmodel_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPDFLINKMODEL_P_H
+#define QQUICKPDFLINKMODEL_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 "qquickpdfdocument_p.h"
+#include "../api/qpdflinkmodel_p.h"
+
+#include <QVariant>
+#include <QtQml/qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickPdfLinkModel : public QPdfLinkModel
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickPdfDocument *document READ document WRITE setDocument NOTIFY documentChanged)
+
+public:
+ explicit QQuickPdfLinkModel(QObject *parent = nullptr);
+
+ QQuickPdfDocument *document() const;
+ void setDocument(QQuickPdfDocument *document);
+
+signals:
+ void documentChanged();
+
+private:
+ void updateResults();
+
+private:
+ QQuickPdfDocument *m_quickDocument;
+ QVector<QPolygonF> m_linksGeometry;
+
+ Q_DISABLE_COPY(QQuickPdfLinkModel)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPdfLinkModel)
+
+#endif // QQUICKPDFLINKMODEL_P_H
diff --git a/src/pdf/quick/qquickpdfnavigationstack.cpp b/src/pdf/quick/qquickpdfnavigationstack.cpp
new file mode 100644
index 000000000..7ba317557
--- /dev/null
+++ b/src/pdf/quick/qquickpdfnavigationstack.cpp
@@ -0,0 +1,266 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickpdfnavigationstack_p.h"
+#include <QLoggingCategory>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qLcNav, "qt.pdf.navigationstack")
+
+/*!
+ \qmltype PdfNavigationStack
+ \instantiates QQuickPdfNavigationStack
+ \inqmlmodule QtQuick.Pdf
+ \ingroup pdf
+ \brief History of the destinations visited within a PDF Document.
+ \since 5.15
+
+ PdfNavigationStack remembers which destinations the user has visited in a PDF
+ document, and provides the ability to traverse backward and forward.
+*/
+
+QQuickPdfNavigationStack::QQuickPdfNavigationStack(QObject *parent)
+ : QObject(parent)
+{
+ push(0, QPointF(), 1);
+}
+
+/*!
+ \qmlmethod void PdfNavigationStack::forward()
+
+ Goes back to the page, location and zoom level that was being viewed before
+ back() was called, and then emits the \l jumped() signal.
+
+ If a new destination was pushed since the last time \l back() was called,
+ the forward() function does nothing, because there is a branch in the
+ timeline which causes the "future" to be lost.
+*/
+void QQuickPdfNavigationStack::forward()
+{
+ if (m_currentHistoryIndex >= m_pageHistory.count() - 1)
+ return;
+ bool backAvailableWas = backAvailable();
+ bool forwardAvailableWas = forwardAvailable();
+ QPointF currentLocationWas = currentLocation();
+ qreal currentZoomWas = currentZoom();
+ ++m_currentHistoryIndex;
+ m_changing = true;
+ emit jumped(currentPage(), currentLocation(), currentZoom());
+ if (currentZoomWas != currentZoom())
+ emit currentZoomChanged();
+ emit currentPageChanged();
+ if (currentLocationWas != currentLocation())
+ emit currentLocationChanged();
+ if (!backAvailableWas)
+ emit backAvailableChanged();
+ if (forwardAvailableWas != forwardAvailable())
+ emit forwardAvailableChanged();
+ m_changing = false;
+}
+
+/*!
+ \qmlmethod void PdfNavigationStack::back()
+
+ Pops the stack, updates the \l currentPage, \l currentLocation and
+ \l currentZoom properties to the most-recently-viewed destination, and then
+ emits the \l jumped() signal.
+*/
+void QQuickPdfNavigationStack::back()
+{
+ if (m_currentHistoryIndex <= 0)
+ return;
+ bool backAvailableWas = backAvailable();
+ bool forwardAvailableWas = forwardAvailable();
+ QPointF currentLocationWas = currentLocation();
+ qreal currentZoomWas = currentZoom();
+ --m_currentHistoryIndex;
+ m_changing = true;
+ emit jumped(currentPage(), currentLocation(), currentZoom());
+ if (currentZoomWas != currentZoom())
+ emit currentZoomChanged();
+ emit currentPageChanged();
+ if (currentLocationWas != currentLocation())
+ emit currentLocationChanged();
+ if (backAvailableWas != backAvailable())
+ emit backAvailableChanged();
+ if (!forwardAvailableWas)
+ emit forwardAvailableChanged();
+ m_changing = false;
+}
+
+/*!
+ \qmlproperty int PdfNavigationStack::currentPage
+
+ This property holds the current page that is being viewed.
+ If there is no current page, it holds \c -1.
+*/
+int QQuickPdfNavigationStack::currentPage() const
+{
+ if (m_currentHistoryIndex < 0 || m_currentHistoryIndex >= m_pageHistory.count())
+ return -1;
+ return m_pageHistory.at(m_currentHistoryIndex)->page;
+}
+
+/*!
+ \qmlproperty point PdfNavigationStack::currentLocation
+
+ This property holds the current location on the page that is being viewed.
+*/
+QPointF QQuickPdfNavigationStack::currentLocation() const
+{
+ if (m_currentHistoryIndex < 0 || m_currentHistoryIndex >= m_pageHistory.count())
+ return QPointF();
+ return m_pageHistory.at(m_currentHistoryIndex)->location;
+}
+
+/*!
+ \qmlproperty real PdfNavigationStack::currentZoom
+
+ This property holds the magnification scale on the page that is being viewed.
+*/
+qreal QQuickPdfNavigationStack::currentZoom() const
+{
+ if (m_currentHistoryIndex < 0 || m_currentHistoryIndex >= m_pageHistory.count())
+ return 1;
+ return m_pageHistory.at(m_currentHistoryIndex)->zoom;
+}
+
+/*!
+ \qmlmethod void PdfNavigationStack::push(int page, point location, qreal zoom)
+
+ Adds the given destination, consisting of \a page, \a location and \a zoom,
+ to the history of visited locations.
+
+ If forwardAvailable is \c true, calling this function represents a branch
+ in the timeline which causes the "future" to be lost, and therefore
+ forwardAvailable will change to \c false.
+*/
+void QQuickPdfNavigationStack::push(int page, QPointF location, qreal zoom)
+{
+ if (page == currentPage() && location == currentLocation() && zoom == currentZoom())
+ return;
+ if (qFuzzyIsNull(zoom))
+ zoom = currentZoom();
+ bool backAvailableWas = backAvailable();
+ bool forwardAvailableWas = forwardAvailable();
+ if (!m_changing) {
+ if (m_currentHistoryIndex >= 0 && forwardAvailableWas)
+ m_pageHistory.remove(m_currentHistoryIndex + 1, m_pageHistory.count() - m_currentHistoryIndex - 1);
+ m_pageHistory.append(QExplicitlySharedDataPointer<QPdfDestinationPrivate>(new QPdfDestinationPrivate(page, location, zoom)));
+ m_currentHistoryIndex = m_pageHistory.count() - 1;
+ }
+ emit currentZoomChanged();
+ emit currentPageChanged();
+ emit currentLocationChanged();
+ if (m_changing)
+ return;
+ if (!backAvailableWas)
+ emit backAvailableChanged();
+ if (forwardAvailableWas)
+ emit forwardAvailableChanged();
+ emit jumped(page, location, zoom);
+ qCDebug(qLcNav) << "push: index" << m_currentHistoryIndex << "page" << page
+ << "@" << location << "zoom" << zoom << "-> history" <<
+ [this]() {
+ QStringList ret;
+ for (auto d : m_pageHistory)
+ ret << QString::number(d->page);
+ return ret.join(',');
+ }();
+}
+
+/*!
+ \qmlmethod void PdfNavigationStack::update(int page, point location, qreal zoom)
+
+ Modifies the current destination, consisting of \a page, \a location and \a zoom.
+
+ This can be called periodically while the user is manually moving around
+ the document, so that after back() is called, forward() will jump back to
+ the most-recently-viewed destination rather than the destination that was
+ last specified by push().
+
+ The \c currentZoomChanged, \c currentPageChanged and \c currentLocationChanged
+ signals will be emitted if the respective properties are actually changed.
+ The \l jumped signal is not emitted, because this operation
+ represents smooth movement rather than a navigational jump.
+*/
+void QQuickPdfNavigationStack::update(int page, QPointF location, qreal zoom)
+{
+ if (m_currentHistoryIndex < 0 || m_currentHistoryIndex >= m_pageHistory.count())
+ return;
+ int currentPageWas = currentPage();
+ QPointF currentLocationWas = currentLocation();
+ qreal currentZoomWas = currentZoom();
+ if (page == currentPageWas && location == currentLocationWas && zoom == currentZoomWas)
+ return;
+ m_pageHistory[m_currentHistoryIndex]->page = page;
+ m_pageHistory[m_currentHistoryIndex]->location = location;
+ m_pageHistory[m_currentHistoryIndex]->zoom = zoom;
+ if (currentZoomWas != zoom)
+ emit currentZoomChanged();
+ if (currentPageWas != page)
+ emit currentPageChanged();
+ if (currentLocationWas != location)
+ emit currentLocationChanged();
+ qCDebug(qLcNav) << "update: index" << m_currentHistoryIndex << "page" << page
+ << "@" << location << "zoom" << zoom << "-> history" <<
+ [this]() {
+ QStringList ret;
+ for (auto d : m_pageHistory)
+ ret << QString::number(d->page);
+ return ret.join(',');
+ }();
+}
+
+bool QQuickPdfNavigationStack::backAvailable() const
+{
+ return m_currentHistoryIndex > 0;
+}
+
+bool QQuickPdfNavigationStack::forwardAvailable() const
+{
+ return m_currentHistoryIndex < m_pageHistory.count() - 1;
+}
+
+/*!
+ \qmlsignal PdfNavigationStack::jumped(int page, point location, qreal zoom)
+
+ This signal is emitted when forward(), back() or push() is called, but not
+ when update() is called.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/pdf/quick/qquickpdfnavigationstack_p.h b/src/pdf/quick/qquickpdfnavigationstack_p.h
new file mode 100644
index 000000000..8d7102fb1
--- /dev/null
+++ b/src/pdf/quick/qquickpdfnavigationstack_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPDFNAVIGATIONSTACK_P_H
+#define QQUICKPDFNAVIGATIONSTACK_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 "qquickpdfdocument_p.h"
+#include "../api/qpdfdestination_p.h"
+
+#include <QtQml/qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickPdfNavigationStack : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int currentPage READ currentPage NOTIFY currentPageChanged)
+ Q_PROPERTY(QPointF currentLocation READ currentLocation NOTIFY currentLocationChanged)
+ Q_PROPERTY(qreal currentZoom READ currentZoom NOTIFY currentZoomChanged)
+ Q_PROPERTY(bool backAvailable READ backAvailable NOTIFY backAvailableChanged)
+ Q_PROPERTY(bool forwardAvailable READ forwardAvailable NOTIFY forwardAvailableChanged)
+
+public:
+ explicit QQuickPdfNavigationStack(QObject *parent = nullptr);
+
+ Q_INVOKABLE void push(int page, QPointF location, qreal zoom);
+ Q_INVOKABLE void update(int page, QPointF location, qreal zoom);
+ Q_INVOKABLE void forward();
+ Q_INVOKABLE void back();
+
+ int currentPage() const;
+ QPointF currentLocation() const;
+ qreal currentZoom() const;
+
+ bool backAvailable() const;
+ bool forwardAvailable() const;
+
+Q_SIGNALS:
+ void currentPageChanged();
+ void currentLocationChanged();
+ void currentZoomChanged();
+ void backAvailableChanged();
+ void forwardAvailableChanged();
+ void jumped(int page, QPointF location, qreal zoom);
+
+private:
+ QVector<QExplicitlySharedDataPointer<QPdfDestinationPrivate>> m_pageHistory;
+ int m_currentHistoryIndex = 0;
+ bool m_changing = false;
+
+ Q_DISABLE_COPY(QQuickPdfNavigationStack)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPdfNavigationStack)
+
+#endif // QQUICKPDFNAVIGATIONSTACK_P_H
diff --git a/src/pdf/quick/qquickpdfsearchmodel.cpp b/src/pdf/quick/qquickpdfsearchmodel.cpp
new file mode 100644
index 000000000..a4b457841
--- /dev/null
+++ b/src/pdf/quick/qquickpdfsearchmodel.cpp
@@ -0,0 +1,277 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickpdfsearchmodel_p.h"
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qLcS, "qt.pdf.search")
+
+/*!
+ \qmltype PdfSearchModel
+ \instantiates QQuickPdfSearchModel
+ \inqmlmodule QtQuick.Pdf
+ \ingroup pdf
+ \brief A representation of text search results within a PDF Document.
+ \since 5.15
+
+ PdfSearchModel provides the ability to search for text strings within a
+ document and get the geometric locations of matches on each page.
+*/
+
+QQuickPdfSearchModel::QQuickPdfSearchModel(QObject *parent)
+ : QPdfSearchModel(parent)
+{
+ connect(this, &QPdfSearchModel::searchStringChanged,
+ this, &QQuickPdfSearchModel::onResultsChanged);
+}
+
+QQuickPdfDocument *QQuickPdfSearchModel::document() const
+{
+ return m_quickDocument;
+}
+
+void QQuickPdfSearchModel::setDocument(QQuickPdfDocument *document)
+{
+ if (document == m_quickDocument || !document)
+ return;
+
+ m_quickDocument = document;
+ QPdfSearchModel::setDocument(&document->m_doc);
+}
+
+/*!
+ \qmlproperty list<list<point>> PdfSearchModel::currentResultBoundingPolygons
+
+ A set of paths in a form that can be bound to the \c paths property of a
+ \l {QtQuick::PathMultiline}{PathMultiline} instance to render a batch of
+ rectangles around the regions comprising the search result \l currentResult
+ on \l currentPage. This is normally used to highlight one search result
+ at a time, in a UI that allows stepping through the results:
+
+ \qml
+ PdfDocument {
+ id: doc
+ }
+ PdfSearchModel {
+ id: searchModel
+ document: doc
+ currentPage: view.currentPage
+ currentResult: ...
+ }
+ Shape {
+ ShapePath {
+ PathMultiline {
+ paths: searchModel.currentResultBoundingPolygons
+ }
+ }
+ }
+ \endqml
+
+ \sa PathMultiline
+*/
+QVector<QPolygonF> QQuickPdfSearchModel::currentResultBoundingPolygons() const
+{
+ QVector<QPolygonF> ret;
+ const auto &results = const_cast<QQuickPdfSearchModel *>(this)->resultsOnPage(m_currentPage);
+ if (m_currentResult < 0 || m_currentResult >= results.count())
+ return ret;
+ const auto result = results[m_currentResult];
+ for (auto rect : result.rectangles())
+ ret << QPolygonF(rect);
+ return ret;
+}
+
+void QQuickPdfSearchModel::onResultsChanged()
+{
+ emit currentPageBoundingPolygonsChanged();
+ emit currentResultBoundingPolygonsChanged();
+}
+
+/*!
+ \qmlproperty list<list<point>> PdfSearchModel::currentPageBoundingPolygons
+
+ A set of paths in a form that can be bound to the \c paths property of a
+ \l {QtQuick::PathMultiline}{PathMultiline} instance to render a batch of
+ rectangles around all the regions where search results are found on
+ \l currentPage:
+
+ \qml
+ PdfDocument {
+ id: doc
+ }
+ PdfSearchModel {
+ id: searchModel
+ document: doc
+ }
+ Shape {
+ ShapePath {
+ PathMultiline {
+ paths: searchModel.matchGeometry(view.currentPage)
+ }
+ }
+ }
+ \endqml
+
+ \sa PathMultiline
+*/
+QVector<QPolygonF> QQuickPdfSearchModel::currentPageBoundingPolygons() const
+{
+ return const_cast<QQuickPdfSearchModel *>(this)->boundingPolygonsOnPage(m_currentPage);
+}
+
+/*!
+ \qmlfunction list<list<point>> PdfSearchModel::boundingPolygonsOnPage(int page)
+
+ Returns a set of paths in a form that can be bound to the \c paths property of a
+ \l {QtQuick::PathMultiline}{PathMultiline} instance to render a batch of
+ rectangles around all the locations where search results are found:
+
+ \qml
+ PdfDocument {
+ id: doc
+ }
+ PdfSearchModel {
+ id: searchModel
+ document: doc
+ }
+ Shape {
+ ShapePath {
+ PathMultiline {
+ paths: searchModel.matchGeometry(view.currentPage)
+ }
+ }
+ }
+ \endqml
+
+ \sa PathMultiline
+*/
+QVector<QPolygonF> QQuickPdfSearchModel::boundingPolygonsOnPage(int page)
+{
+ if (!document() || searchString().isEmpty() || page < 0 || page > document()->pageCount())
+ return {};
+
+ updatePage(page);
+
+ QVector<QPolygonF> ret;
+ auto m = QPdfSearchModel::resultsOnPage(page);
+ for (auto result : m) {
+ for (auto rect : result.rectangles())
+ ret << QPolygonF(rect);
+ }
+
+ return ret;
+}
+
+/*!
+ \qmlproperty int PdfSearchModel::currentPage
+
+ The page on which \l currentMatchGeometry should provide filtered search results.
+*/
+void QQuickPdfSearchModel::setCurrentPage(int currentPage)
+{
+ if (m_currentPage == currentPage)
+ return;
+
+ if (currentPage < 0)
+ currentPage = document()->pageCount() - 1;
+ else if (currentPage >= document()->pageCount())
+ currentPage = 0;
+
+ m_currentPage = currentPage;
+ if (!m_suspendSignals) {
+ emit currentPageChanged();
+ onResultsChanged();
+ }
+}
+
+/*!
+ \qmlproperty int PdfSearchModel::currentResult
+
+ The result index on \l currentPage for which \l currentResultBoundingPolygons
+ should provide the regions to highlight.
+*/
+void QQuickPdfSearchModel::setCurrentResult(int currentResult)
+{
+ if (m_currentResult == currentResult)
+ return;
+
+ int currentResultWas = currentResult;
+ int currentPageWas = m_currentPage;
+ if (currentResult < 0) {
+ setCurrentPage(m_currentPage - 1);
+ while (resultsOnPage(m_currentPage).count() == 0 && m_currentPage != currentPageWas) {
+ m_suspendSignals = true;
+ setCurrentPage(m_currentPage - 1);
+ }
+ if (m_suspendSignals) {
+ emit currentPageChanged();
+ m_suspendSignals = false;
+ }
+ const auto results = resultsOnPage(m_currentPage);
+ currentResult = results.count() - 1;
+ } else {
+ const auto results = resultsOnPage(m_currentPage);
+ if (currentResult >= results.count()) {
+ setCurrentPage(m_currentPage + 1);
+ while (resultsOnPage(m_currentPage).count() == 0 && m_currentPage != currentPageWas) {
+ m_suspendSignals = true;
+ setCurrentPage(m_currentPage + 1);
+ }
+ if (m_suspendSignals) {
+ emit currentPageChanged();
+ m_suspendSignals = false;
+ }
+ currentResult = 0;
+ }
+ }
+ qCDebug(qLcS) << "currentResult was" << m_currentResult
+ << "requested" << currentResultWas << "on page" << currentPageWas
+ << "->" << currentResult << "on page" << m_currentPage;
+
+ m_currentResult = currentResult;
+ emit currentResultChanged();
+ emit currentResultBoundingPolygonsChanged();
+}
+
+/*!
+ \qmlproperty string PdfSearchModel::searchString
+
+ The string to search for.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/pdf/quick/qquickpdfsearchmodel_p.h b/src/pdf/quick/qquickpdfsearchmodel_p.h
new file mode 100644
index 000000000..3e05f80e3
--- /dev/null
+++ b/src/pdf/quick/qquickpdfsearchmodel_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPDFSEARCHMODEL_P_H
+#define QQUICKPDFSEARCHMODEL_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 "qquickpdfdocument_p.h"
+#include "../api/qpdfsearchmodel.h"
+
+#include <QtCore/qvariant.h>
+#include <QtQml/qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickPdfSearchModel : public QPdfSearchModel
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickPdfDocument *document READ document WRITE setDocument NOTIFY documentChanged)
+ Q_PROPERTY(int currentPage READ currentPage WRITE setCurrentPage NOTIFY currentPageChanged)
+ Q_PROPERTY(int currentResult READ currentResult WRITE setCurrentResult NOTIFY currentResultChanged)
+ Q_PROPERTY(QVector<QPolygonF> currentPageBoundingPolygons READ currentPageBoundingPolygons NOTIFY currentPageBoundingPolygonsChanged)
+ Q_PROPERTY(QVector<QPolygonF> currentResultBoundingPolygons READ currentResultBoundingPolygons NOTIFY currentResultBoundingPolygonsChanged)
+
+public:
+ explicit QQuickPdfSearchModel(QObject *parent = nullptr);
+
+ QQuickPdfDocument *document() const;
+ void setDocument(QQuickPdfDocument * document);
+
+ Q_INVOKABLE QVector<QPolygonF> boundingPolygonsOnPage(int page);
+
+ int currentPage() const { return m_currentPage; }
+ void setCurrentPage(int currentPage);
+
+ int currentResult() const { return m_currentResult; }
+ void setCurrentResult(int currentResult);
+
+ QVector<QPolygonF> currentPageBoundingPolygons() const;
+ QVector<QPolygonF> currentResultBoundingPolygons() const;
+
+signals:
+ void documentChanged();
+ void currentPageChanged();
+ void currentResultChanged();
+ void currentPageBoundingPolygonsChanged();
+ void currentResultBoundingPolygonsChanged();
+
+private:
+ void updateResults();
+ void onResultsChanged();
+
+private:
+ QQuickPdfDocument *m_quickDocument = nullptr;
+ int m_currentPage = 0;
+ int m_currentResult = 0;
+ bool m_suspendSignals = false;
+
+ Q_DISABLE_COPY(QQuickPdfSearchModel)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPdfSearchModel)
+QML_DECLARE_TYPE(QPdfSelection)
+
+#endif // QQUICKPDFSEARCHMODEL_P_H
diff --git a/src/pdf/quick/qquickpdfselection.cpp b/src/pdf/quick/qquickpdfselection.cpp
new file mode 100644
index 000000000..d313820ba
--- /dev/null
+++ b/src/pdf/quick/qquickpdfselection.cpp
@@ -0,0 +1,268 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickpdfselection_p.h"
+#include "qquickpdfdocument_p.h"
+#include <QClipboard>
+#include <QQuickItem>
+#include <QQmlEngine>
+#include <QStandardPaths>
+#include <private/qguiapplication_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype PdfSelection
+ \instantiates QQuickPdfSelection
+ \inqmlmodule QtQuick.Pdf
+ \ingroup pdf
+ \brief A representation of a text selection within a PDF Document.
+ \since 5.15
+
+ PdfSelection provides the text string and its geometry within a bounding box
+ from one point to another.
+*/
+
+/*!
+ Constructs a SearchModel.
+*/
+QQuickPdfSelection::QQuickPdfSelection(QObject *parent)
+ : QObject(parent)
+{
+}
+
+QQuickPdfDocument *QQuickPdfSelection::document() const
+{
+ return m_document;
+}
+
+void QQuickPdfSelection::setDocument(QQuickPdfDocument *document)
+{
+ if (m_document == document)
+ return;
+
+ if (m_document) {
+ disconnect(m_document, &QQuickPdfDocument::sourceChanged,
+ this, &QQuickPdfSelection::resetPoints);
+ }
+ m_document = document;
+ emit documentChanged();
+ resetPoints();
+ connect(m_document, &QQuickPdfDocument::sourceChanged,
+ this, &QQuickPdfSelection::resetPoints);
+}
+
+/*!
+ \qmlproperty list<list<point>> PdfSelection::geometry
+
+ A set of paths in a form that can be bound to the \c paths property of a
+ \l {QtQuick::PathMultiline}{PathMultiline} instance to render a batch of
+ rectangles around the text regions that are included in the selection:
+
+ \qml
+ PdfDocument {
+ id: doc
+ }
+ PdfSelection {
+ id: selection
+ document: doc
+ fromPoint: textSelectionDrag.centroid.pressPosition
+ toPoint: textSelectionDrag.centroid.position
+ hold: !textSelectionDrag.active
+ }
+ Shape {
+ ShapePath {
+ PathMultiline {
+ paths: selection.geometry
+ }
+ }
+ }
+ DragHandler {
+ id: textSelectionDrag
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ target: null
+ }
+ \endqml
+
+ \sa PathMultiline
+*/
+QVector<QPolygonF> QQuickPdfSelection::geometry() const
+{
+ return m_geometry;
+}
+
+void QQuickPdfSelection::resetPoints()
+{
+ bool wasHolding = m_hold;
+ m_hold = false;
+ setFromPoint(QPointF());
+ setToPoint(QPointF());
+ m_hold = wasHolding;
+}
+
+/*!
+ \qmlproperty int PdfSelection::page
+
+ The page number on which to search.
+
+ \sa QtQuick::Image::currentFrame
+*/
+int QQuickPdfSelection::page() const
+{
+ return m_page;
+}
+
+void QQuickPdfSelection::setPage(int page)
+{
+ if (m_page == page)
+ return;
+
+ m_page = page;
+ emit pageChanged();
+ resetPoints();
+}
+
+/*!
+ \qmlproperty point PdfSelection::fromPoint
+
+ The beginning location, in \l {https://en.wikipedia.org/wiki/Point_(typography)}{points}
+ from the upper-left corner of the page, from which to find selected text.
+ This can be bound to a scaled version of the \c centroid.pressPosition
+ of a \l DragHandler to begin selecting text from the position where the user
+ presses the mouse button and begins dragging, for example.
+*/
+QPointF QQuickPdfSelection::fromPoint() const
+{
+ return m_fromPoint;
+}
+
+void QQuickPdfSelection::setFromPoint(QPointF fromPoint)
+{
+ if (m_hold || m_fromPoint == fromPoint)
+ return;
+
+ m_fromPoint = fromPoint;
+ emit fromPointChanged();
+ updateResults();
+}
+
+/*!
+ \qmlproperty point PdfSelection::toPoint
+
+ The ending location, in \l {https://en.wikipedia.org/wiki/Point_(typography)}{points}
+ from the upper-left corner of the page, from which to find selected text.
+ This can be bound to a scaled version of the \c centroid.position
+ of a \l DragHandler to end selection of text at the position where the user
+ is currently dragging the mouse, for example.
+*/
+QPointF QQuickPdfSelection::toPoint() const
+{
+ return m_toPoint;
+}
+
+void QQuickPdfSelection::setToPoint(QPointF toPoint)
+{
+ if (m_hold || m_toPoint == toPoint)
+ return;
+
+ m_toPoint = toPoint;
+ emit toPointChanged();
+ updateResults();
+}
+
+/*!
+ \qmlproperty bool PdfSelection::hold
+
+ Controls whether to hold the existing selection regardless of changes to
+ \l fromPoint and \l toPoint. This property can be set to \c true when the mouse
+ or touchpoint is released, so that the selection is not lost due to the
+ point bindings changing.
+*/
+bool QQuickPdfSelection::hold() const
+{
+ return m_hold;
+}
+
+void QQuickPdfSelection::setHold(bool hold)
+{
+ if (m_hold == hold)
+ return;
+
+ m_hold = hold;
+ emit holdChanged();
+}
+
+/*!
+ \qmlproperty string PdfSelection::string
+
+ The string found.
+*/
+QString QQuickPdfSelection::text() const
+{
+ return m_text;
+}
+
+#if QT_CONFIG(clipboard)
+/*!
+ \qmlmethod void PdfSelection::copyToClipboard()
+
+ Copies plain text from the \l string property to the system clipboard.
+*/
+void QQuickPdfSelection::copyToClipboard() const
+{
+ QGuiApplication::clipboard()->setText(m_text);
+}
+#endif
+
+void QQuickPdfSelection::updateResults()
+{
+ if (!m_document)
+ return;
+ QPdfSelection sel = m_document->document().getSelection(m_page, m_fromPoint, m_toPoint);
+ if (sel.text() != m_text) {
+ m_text = sel.text();
+ if (QGuiApplication::clipboard()->supportsSelection())
+ sel.copyToClipboard(QClipboard::Selection);
+ emit textChanged();
+ }
+
+ if (sel.bounds() != m_geometry) {
+ m_geometry = sel.bounds();
+ emit geometryChanged();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/pdf/quick/qquickpdfselection_p.h b/src/pdf/quick/qquickpdfselection_p.h
new file mode 100644
index 000000000..a0e6d1a8d
--- /dev/null
+++ b/src/pdf/quick/qquickpdfselection_p.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPDFSELECTION_P_H
+#define QQUICKPDFSELECTION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QPointF>
+#include <QPolygonF>
+#include <QVariant>
+#include <QtQml/qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickPdfDocument;
+
+class QQuickPdfSelection : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickPdfDocument *document READ document WRITE setDocument NOTIFY documentChanged)
+ Q_PROPERTY(int page READ page WRITE setPage NOTIFY pageChanged)
+ Q_PROPERTY(QPointF fromPoint READ fromPoint WRITE setFromPoint NOTIFY fromPointChanged)
+ Q_PROPERTY(QPointF toPoint READ toPoint WRITE setToPoint NOTIFY toPointChanged)
+ Q_PROPERTY(bool hold READ hold WRITE setHold NOTIFY holdChanged)
+
+ Q_PROPERTY(QString text READ text NOTIFY textChanged)
+ Q_PROPERTY(QVector<QPolygonF> geometry READ geometry NOTIFY geometryChanged)
+
+public:
+ explicit QQuickPdfSelection(QObject *parent = nullptr);
+
+ QQuickPdfDocument *document() const;
+ void setDocument(QQuickPdfDocument * document);
+ int page() const;
+ void setPage(int page);
+ QPointF fromPoint() const;
+ void setFromPoint(QPointF fromPoint);
+ QPointF toPoint() const;
+ void setToPoint(QPointF toPoint);
+ bool hold() const;
+ void setHold(bool hold);
+
+ QString text() const;
+ QVector<QPolygonF> geometry() const;
+
+#if QT_CONFIG(clipboard)
+ Q_INVOKABLE void copyToClipboard() const;
+#endif
+
+signals:
+ void documentChanged();
+ void pageChanged();
+ void fromPointChanged();
+ void toPointChanged();
+ void holdChanged();
+ void textChanged();
+ void geometryChanged();
+
+private:
+ void resetPoints();
+ void updateResults();
+
+private:
+ QQuickPdfDocument *m_document = nullptr;
+ QPointF m_fromPoint;
+ QPointF m_toPoint;
+ QString m_text;
+ QVector<QPolygonF> m_geometry;
+ int m_page = 0;
+ bool m_hold = false;
+
+ Q_DISABLE_COPY(QQuickPdfSelection)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPdfSelection)
+\
+#endif // QQUICKPDFSELECTION_P_H
diff --git a/src/pdf/quick/quick.pro b/src/pdf/quick/quick.pro
new file mode 100644
index 000000000..b62b80346
--- /dev/null
+++ b/src/pdf/quick/quick.pro
@@ -0,0 +1,34 @@
+CXX_MODULE = qml
+TARGET = pdfplugin
+TARGETPATH = QtQuick/Pdf
+IMPORT_VERSION = 1.0
+
+#QMAKE_DOCS = $$PWD/doc/qtquickpdf.qdocconf
+
+PDF_QML_FILES = \
+ qml/PdfMultiPageView.qml \
+ qml/PdfPageView.qml \
+ qml/PdfScrollablePageView.qml \
+
+QML_FILES += $$PDF_QML_FILES qmldir
+
+RESOURCES += resources.qrc
+
+SOURCES += \
+ plugin.cpp \
+ qquickpdfdocument.cpp \
+ qquickpdflinkmodel.cpp \
+ qquickpdfnavigationstack.cpp \
+ qquickpdfsearchmodel.cpp \
+ qquickpdfselection.cpp \
+
+HEADERS += \
+ qquickpdfdocument_p.h \
+ qquickpdflinkmodel_p.h \
+ qquickpdfnavigationstack_p.h \
+ qquickpdfsearchmodel_p.h \
+ qquickpdfselection_p.h \
+
+QT += pdf quick-private gui gui-private core core-private qml qml-private
+
+load(qml_plugin)
diff --git a/src/pdf/quick/resources.qrc b/src/pdf/quick/resources.qrc
new file mode 100644
index 000000000..20cac4827
--- /dev/null
+++ b/src/pdf/quick/resources.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/qt-project.org/qtpdf">
+ <file>qml/PdfMultiPageView.qml</file>
+ <file>qml/PdfPageView.qml</file>
+ <file>qml/PdfScrollablePageView.qml</file>
+ </qresource>
+</RCC>
diff --git a/src/pdfwidgets/configure.json b/src/pdfwidgets/configure.json
new file mode 100644
index 000000000..b33f08555
--- /dev/null
+++ b/src/pdfwidgets/configure.json
@@ -0,0 +1,28 @@
+{
+ "module": "pdfwidgets",
+ "condition": "module.pdf && features.pdf-widgets",
+ "depends": [
+ "pdf-private"
+ ],
+ "commandline": {
+ "options": {
+ "pdf-widgets": "boolean"
+ }
+ },
+ "features": {
+ "pdf-widgets": {
+ "label": "Support Qt PDF Widgets",
+ "purpose": "Provides Qt PDF Widgets support.",
+ "condition": "module.widgets",
+ "output": [ "privateFeature" ]
+ }
+ },
+ "summary": [
+ {
+ "section": "Qt PDF Widgets",
+ "entries": [
+ "pdf-widgets"
+ ]
+ }
+ ]
+}
diff --git a/src/pdfwidgets/pdfwidgets.pro b/src/pdfwidgets/pdfwidgets.pro
new file mode 100644
index 000000000..cf221be03
--- /dev/null
+++ b/src/pdfwidgets/pdfwidgets.pro
@@ -0,0 +1,12 @@
+TARGET = QtPdfWidgets
+QT = core gui widgets widgets-private pdf
+
+SOURCES += \
+ qpdfview.cpp
+
+HEADERS += \
+ qpdfview.h \
+ qpdfview_p.h \
+ qtpdfwidgetsglobal.h
+
+load(qt_module)
diff --git a/src/pdfwidgets/qpdfview.cpp b/src/pdfwidgets/qpdfview.cpp
new file mode 100644
index 000000000..19e5b719a
--- /dev/null
+++ b/src/pdfwidgets/qpdfview.cpp
@@ -0,0 +1,493 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpdfview.h"
+#include "qpdfview_p.h"
+
+#include "qpdfpagerenderer.h"
+
+#include <QGuiApplication>
+#include <QPdfDocument>
+#include <QPdfPageNavigation>
+#include <QScreen>
+#include <QScrollBar>
+#include <QScroller>
+
+QT_BEGIN_NAMESPACE
+
+QPdfViewPrivate::QPdfViewPrivate()
+ : QAbstractScrollAreaPrivate()
+ , m_document(nullptr)
+ , m_pageNavigation(nullptr)
+ , m_pageRenderer(nullptr)
+ , m_pageMode(QPdfView::SinglePage)
+ , m_zoomMode(QPdfView::CustomZoom)
+ , m_zoomFactor(1.0)
+ , m_pageSpacing(3)
+ , m_documentMargins(6, 6, 6, 6)
+ , m_blockPageScrolling(false)
+ , m_pageCacheLimit(20)
+ , m_screenResolution(QGuiApplication::primaryScreen()->logicalDotsPerInch() / 72.0)
+{
+}
+
+void QPdfViewPrivate::init()
+{
+ Q_Q(QPdfView);
+
+ m_pageNavigation = new QPdfPageNavigation(q);
+ m_pageRenderer = new QPdfPageRenderer(q);
+ m_pageRenderer->setRenderMode(QPdfPageRenderer::MultiThreadedRenderMode);
+}
+
+void QPdfViewPrivate::documentStatusChanged()
+{
+ updateDocumentLayout();
+ invalidatePageCache();
+}
+
+void QPdfViewPrivate::currentPageChanged(int currentPage)
+{
+ Q_Q(QPdfView);
+
+ if (m_blockPageScrolling)
+ return;
+
+ q->verticalScrollBar()->setValue(yPositionForPage(currentPage));
+
+ if (m_pageMode == QPdfView::SinglePage)
+ invalidateDocumentLayout();
+}
+
+void QPdfViewPrivate::calculateViewport()
+{
+ Q_Q(QPdfView);
+
+ const int x = q->horizontalScrollBar()->value();
+ const int y = q->verticalScrollBar()->value();
+ const int width = q->viewport()->width();
+ const int height = q->viewport()->height();
+
+ setViewport(QRect(x, y, width, height));
+}
+
+void QPdfViewPrivate::setViewport(QRect viewport)
+{
+ if (m_viewport == viewport)
+ return;
+
+ const QSize oldSize = m_viewport.size();
+
+ m_viewport = viewport;
+
+ if (oldSize != m_viewport.size()) {
+ updateDocumentLayout();
+
+ if (m_zoomMode != QPdfView::CustomZoom) {
+ invalidatePageCache();
+ }
+ }
+
+ if (m_pageMode == QPdfView::MultiPage) {
+ // An imaginary, 2px height line at the upper half of the viewport, which is used to
+ // determine which page is currently located there -> we propagate that as 'current' page
+ // to the QPdfPageNavigation object
+ const QRect currentPageLine(m_viewport.x(), m_viewport.y() + m_viewport.height() * 0.4, m_viewport.width(), 2);
+
+ int currentPage = 0;
+ for (auto it = m_documentLayout.pageGeometries.cbegin(); it != m_documentLayout.pageGeometries.cend(); ++it) {
+ const QRect pageGeometry = it.value();
+ if (pageGeometry.intersects(currentPageLine)) {
+ currentPage = it.key();
+ break;
+ }
+ }
+
+ if (currentPage != m_pageNavigation->currentPage()) {
+ m_blockPageScrolling = true;
+ m_pageNavigation->setCurrentPage(currentPage);
+ m_blockPageScrolling = false;
+ }
+ }
+}
+
+void QPdfViewPrivate::updateScrollBars()
+{
+ Q_Q(QPdfView);
+
+ const QSize p = q->viewport()->size();
+ const QSize v = m_documentLayout.documentSize;
+
+ q->horizontalScrollBar()->setRange(0, v.width() - p.width());
+ q->horizontalScrollBar()->setPageStep(p.width());
+ q->verticalScrollBar()->setRange(0, v.height() - p.height());
+ q->verticalScrollBar()->setPageStep(p.height());
+}
+
+void QPdfViewPrivate::pageRendered(int pageNumber, QSize imageSize, const QImage &image, quint64 requestId)
+{
+ Q_Q(QPdfView);
+
+ Q_UNUSED(imageSize)
+ Q_UNUSED(requestId)
+
+ if (!m_cachedPagesLRU.contains(pageNumber)) {
+ if (m_cachedPagesLRU.length() > m_pageCacheLimit)
+ m_pageCache.remove(m_cachedPagesLRU.takeFirst());
+
+ m_cachedPagesLRU.append(pageNumber);
+ }
+
+ m_pageCache.insert(pageNumber, image);
+
+ q->viewport()->update();
+}
+
+void QPdfViewPrivate::invalidateDocumentLayout()
+{
+ updateDocumentLayout();
+ invalidatePageCache();
+}
+
+void QPdfViewPrivate::invalidatePageCache()
+{
+ Q_Q(QPdfView);
+
+ m_pageCache.clear();
+ q->viewport()->update();
+}
+
+QPdfViewPrivate::DocumentLayout QPdfViewPrivate::calculateDocumentLayout() const
+{
+ // The DocumentLayout describes a virtual layout where all pages are positioned inside
+ // - For SinglePage mode, this is just an area as large as the current page surrounded
+ // by the m_documentMargins.
+ // - For MultiPage mode, this is the area that is covered by all pages which are placed
+ // below each other, with m_pageSpacing inbetween and surrounded by m_documentMargins
+
+ DocumentLayout documentLayout;
+
+ if (!m_document || m_document->status() != QPdfDocument::Ready)
+ return documentLayout;
+
+ QHash<int, QRect> pageGeometries;
+
+ const int pageCount = m_document->pageCount();
+
+ int totalWidth = 0;
+
+ const int startPage = (m_pageMode == QPdfView::SinglePage ? m_pageNavigation->currentPage() : 0);
+ const int endPage = (m_pageMode == QPdfView::SinglePage ? m_pageNavigation->currentPage() + 1 : pageCount);
+
+ // calculate page sizes
+ for (int page = startPage; page < endPage; ++page) {
+ QSize pageSize;
+ if (m_zoomMode == QPdfView::CustomZoom) {
+ pageSize = QSizeF(m_document->pageSize(page) * m_screenResolution * m_zoomFactor).toSize();
+ } else if (m_zoomMode == QPdfView::FitToWidth) {
+ pageSize = QSizeF(m_document->pageSize(page) * m_screenResolution).toSize();
+ const qreal factor = (qreal(m_viewport.width() - m_documentMargins.left() - m_documentMargins.right()) / qreal(pageSize.width()));
+ pageSize *= factor;
+ } else if (m_zoomMode == QPdfView::FitInView) {
+ const QSize viewportSize(m_viewport.size() + QSize(-m_documentMargins.left() - m_documentMargins.right(), -m_pageSpacing));
+
+ pageSize = QSizeF(m_document->pageSize(page) * m_screenResolution).toSize();
+ pageSize = pageSize.scaled(viewportSize, Qt::KeepAspectRatio);
+ }
+
+ totalWidth = qMax(totalWidth, pageSize.width());
+
+ pageGeometries[page] = QRect(QPoint(0, 0), pageSize);
+ }
+
+ totalWidth += m_documentMargins.left() + m_documentMargins.right();
+
+ int pageY = m_documentMargins.top();
+
+ // calculate page positions
+ for (int page = startPage; page < endPage; ++page) {
+ const QSize pageSize = pageGeometries[page].size();
+
+ // center horizontal inside the viewport
+ const int pageX = (qMax(totalWidth, m_viewport.width()) - pageSize.width()) / 2;
+
+ pageGeometries[page].moveTopLeft(QPoint(pageX, pageY));
+
+ pageY += pageSize.height() + m_pageSpacing;
+ }
+
+ pageY += m_documentMargins.bottom();
+
+ documentLayout.pageGeometries = pageGeometries;
+
+ // calculate overall document size
+ documentLayout.documentSize = QSize(totalWidth, pageY);
+
+ return documentLayout;
+}
+
+qreal QPdfViewPrivate::yPositionForPage(int pageNumber) const
+{
+ const auto it = m_documentLayout.pageGeometries.constFind(pageNumber);
+ if (it == m_documentLayout.pageGeometries.cend())
+ return 0.0;
+
+ return (*it).y();
+}
+
+void QPdfViewPrivate::updateDocumentLayout()
+{
+ m_documentLayout = calculateDocumentLayout();
+
+ updateScrollBars();
+}
+
+
+QPdfView::QPdfView(QWidget *parent)
+ : QAbstractScrollArea(*new QPdfViewPrivate(), parent)
+{
+ Q_D(QPdfView);
+
+ d->init();
+
+ connect(d->m_pageNavigation, &QPdfPageNavigation::currentPageChanged, this, [d](int page){ d->currentPageChanged(page); });
+
+ connect(d->m_pageRenderer, &QPdfPageRenderer::pageRendered,
+ this, [d](int pageNumber, QSize imageSize, const QImage &image, QPdfDocumentRenderOptions, quint64 requestId){ d->pageRendered(pageNumber, imageSize, image, requestId); });
+
+ verticalScrollBar()->setSingleStep(20);
+ horizontalScrollBar()->setSingleStep(20);
+
+ QScroller::grabGesture(this);
+
+ d->calculateViewport();
+}
+
+/*!
+ \internal
+*/
+QPdfView::QPdfView(QPdfViewPrivate &dd, QWidget *parent)
+ : QAbstractScrollArea(dd, parent)
+{
+}
+
+QPdfView::~QPdfView()
+{
+}
+
+void QPdfView::setDocument(QPdfDocument *document)
+{
+ Q_D(QPdfView);
+
+ if (d->m_document == document)
+ return;
+
+ if (d->m_document)
+ disconnect(d->m_documentStatusChangedConnection);
+
+ d->m_document = document;
+ emit documentChanged(d->m_document);
+
+ if (d->m_document)
+ d->m_documentStatusChangedConnection = connect(d->m_document.data(), &QPdfDocument::statusChanged, this, [d](){ d->documentStatusChanged(); });
+
+ d->m_pageNavigation->setDocument(d->m_document);
+ d->m_pageRenderer->setDocument(d->m_document);
+
+ d->documentStatusChanged();
+}
+
+QPdfDocument *QPdfView::document() const
+{
+ Q_D(const QPdfView);
+
+ return d->m_document;
+}
+
+QPdfPageNavigation *QPdfView::pageNavigation() const
+{
+ Q_D(const QPdfView);
+
+ return d->m_pageNavigation;
+}
+
+QPdfView::PageMode QPdfView::pageMode() const
+{
+ Q_D(const QPdfView);
+
+ return d->m_pageMode;
+}
+
+void QPdfView::setPageMode(PageMode mode)
+{
+ Q_D(QPdfView);
+
+ if (d->m_pageMode == mode)
+ return;
+
+ d->m_pageMode = mode;
+ d->invalidateDocumentLayout();
+
+ emit pageModeChanged(d->m_pageMode);
+}
+
+QPdfView::ZoomMode QPdfView::zoomMode() const
+{
+ Q_D(const QPdfView);
+
+ return d->m_zoomMode;
+}
+
+void QPdfView::setZoomMode(ZoomMode mode)
+{
+ Q_D(QPdfView);
+
+ if (d->m_zoomMode == mode)
+ return;
+
+ d->m_zoomMode = mode;
+ d->invalidateDocumentLayout();
+
+ emit zoomModeChanged(d->m_zoomMode);
+}
+
+qreal QPdfView::zoomFactor() const
+{
+ Q_D(const QPdfView);
+
+ return d->m_zoomFactor;
+}
+
+void QPdfView::setZoomFactor(qreal factor)
+{
+ Q_D(QPdfView);
+
+ if (d->m_zoomFactor == factor)
+ return;
+
+ d->m_zoomFactor = factor;
+ d->invalidateDocumentLayout();
+
+ emit zoomFactorChanged(d->m_zoomFactor);
+}
+
+int QPdfView::pageSpacing() const
+{
+ Q_D(const QPdfView);
+
+ return d->m_pageSpacing;
+}
+
+void QPdfView::setPageSpacing(int spacing)
+{
+ Q_D(QPdfView);
+
+ if (d->m_pageSpacing == spacing)
+ return;
+
+ d->m_pageSpacing = spacing;
+ d->invalidateDocumentLayout();
+
+ emit pageSpacingChanged(d->m_pageSpacing);
+}
+
+QMargins QPdfView::documentMargins() const
+{
+ Q_D(const QPdfView);
+
+ return d->m_documentMargins;
+}
+
+void QPdfView::setDocumentMargins(QMargins margins)
+{
+ Q_D(QPdfView);
+
+ if (d->m_documentMargins == margins)
+ return;
+
+ d->m_documentMargins = margins;
+ d->invalidateDocumentLayout();
+
+ emit documentMarginsChanged(d->m_documentMargins);
+}
+
+void QPdfView::paintEvent(QPaintEvent *event)
+{
+ Q_D(QPdfView);
+
+ QPainter painter(viewport());
+ painter.fillRect(event->rect(), palette().brush(QPalette::Dark));
+ painter.translate(-d->m_viewport.x(), -d->m_viewport.y());
+
+ for (auto it = d->m_documentLayout.pageGeometries.cbegin(); it != d->m_documentLayout.pageGeometries.cend(); ++it) {
+ const QRect pageGeometry = it.value();
+ if (pageGeometry.intersects(d->m_viewport)) { // page needs to be painted
+ painter.fillRect(pageGeometry, Qt::white);
+
+ const int page = it.key();
+ const auto pageIt = d->m_pageCache.constFind(page);
+ if (pageIt != d->m_pageCache.cend()) {
+ const QImage &img = pageIt.value();
+ painter.drawImage(pageGeometry.topLeft(), img);
+ } else {
+ d->m_pageRenderer->requestPage(page, pageGeometry.size());
+ }
+ }
+ }
+}
+
+void QPdfView::resizeEvent(QResizeEvent *event)
+{
+ Q_D(QPdfView);
+
+ QAbstractScrollArea::resizeEvent(event);
+
+ d->updateScrollBars();
+ d->calculateViewport();
+}
+
+void QPdfView::scrollContentsBy(int dx, int dy)
+{
+ Q_D(QPdfView);
+
+ QAbstractScrollArea::scrollContentsBy(dx, dy);
+
+ d->calculateViewport();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qpdfview.cpp"
diff --git a/src/pdfwidgets/qpdfview.h b/src/pdfwidgets/qpdfview.h
new file mode 100644
index 000000000..cee1cb64c
--- /dev/null
+++ b/src/pdfwidgets/qpdfview.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDFVIEW_H
+#define QPDFVIEW_H
+
+#include <QtPdfWidgets/qtpdfwidgetsglobal.h>
+#include <QtWidgets/qabstractscrollarea.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfDocument;
+class QPdfPageNavigation;
+class QPdfViewPrivate;
+
+class Q_PDF_WIDGETS_EXPORT QPdfView : public QAbstractScrollArea
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QPdfDocument* document READ document WRITE setDocument NOTIFY documentChanged)
+
+ Q_PROPERTY(PageMode pageMode READ pageMode WRITE setPageMode NOTIFY pageModeChanged)
+ Q_PROPERTY(ZoomMode zoomMode READ zoomMode WRITE setZoomMode NOTIFY zoomModeChanged)
+ Q_PROPERTY(qreal zoomFactor READ zoomFactor WRITE setZoomFactor NOTIFY zoomFactorChanged)
+
+ Q_PROPERTY(int pageSpacing READ pageSpacing WRITE setPageSpacing NOTIFY pageSpacingChanged)
+ Q_PROPERTY(QMargins documentMargins READ documentMargins WRITE setDocumentMargins NOTIFY documentMarginsChanged)
+
+public:
+ enum PageMode
+ {
+ SinglePage,
+ MultiPage
+ };
+ Q_ENUM(PageMode)
+
+ enum ZoomMode
+ {
+ CustomZoom,
+ FitToWidth,
+ FitInView
+ };
+ Q_ENUM(ZoomMode)
+
+ explicit QPdfView(QWidget *parent = nullptr);
+ ~QPdfView();
+
+ void setDocument(QPdfDocument *document);
+ QPdfDocument *document() const;
+
+ QPdfPageNavigation *pageNavigation() const;
+
+ PageMode pageMode() const;
+ ZoomMode zoomMode() const;
+ qreal zoomFactor() const;
+
+ int pageSpacing() const;
+ void setPageSpacing(int spacing);
+
+ QMargins documentMargins() const;
+ void setDocumentMargins(QMargins margins);
+
+public Q_SLOTS:
+ void setPageMode(PageMode mode);
+ void setZoomMode(ZoomMode mode);
+ void setZoomFactor(qreal factor);
+
+Q_SIGNALS:
+ void documentChanged(QPdfDocument *document);
+ void pageModeChanged(PageMode pageMode);
+ void zoomModeChanged(ZoomMode zoomMode);
+ void zoomFactorChanged(qreal zoomFactor);
+ void pageSpacingChanged(int pageSpacing);
+ void documentMarginsChanged(QMargins documentMargins);
+
+protected:
+ explicit QPdfView(QPdfViewPrivate &, QWidget *);
+
+ void paintEvent(QPaintEvent *event) override;
+ void resizeEvent(QResizeEvent *event) override;
+ void scrollContentsBy(int dx, int dy) override;
+
+private:
+ Q_DECLARE_PRIVATE(QPdfView)
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFVIEW_H
diff --git a/src/pdfwidgets/qpdfview_p.h b/src/pdfwidgets/qpdfview_p.h
new file mode 100644
index 000000000..9fd54b4b6
--- /dev/null
+++ b/src/pdfwidgets/qpdfview_p.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDFVIEW_P_H
+#define QPDFVIEW_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 "qpdfview.h"
+
+#include <QPointer>
+#include <QtWidgets/private/qabstractscrollarea_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfPageRenderer;
+
+class QPdfViewPrivate : public QAbstractScrollAreaPrivate
+{
+ Q_DECLARE_PUBLIC(QPdfView)
+
+public:
+ QPdfViewPrivate();
+ void init();
+
+ void documentStatusChanged();
+ void currentPageChanged(int currentPage);
+ void calculateViewport();
+ void setViewport(QRect viewport);
+ void updateScrollBars();
+
+ void pageRendered(int pageNumber, QSize imageSize, const QImage &image, quint64 requestId);
+ void invalidateDocumentLayout();
+ void invalidatePageCache();
+
+ qreal yPositionForPage(int page) const;
+
+ struct DocumentLayout
+ {
+ QSize documentSize;
+ QHash<int, QRect> pageGeometries;
+ };
+
+ DocumentLayout calculateDocumentLayout() const;
+ void updateDocumentLayout();
+
+ QPointer<QPdfDocument> m_document;
+ QPdfPageNavigation* m_pageNavigation;
+ QPdfPageRenderer *m_pageRenderer;
+
+ QPdfView::PageMode m_pageMode;
+ QPdfView::ZoomMode m_zoomMode;
+ qreal m_zoomFactor;
+
+ int m_pageSpacing;
+ QMargins m_documentMargins;
+
+ bool m_blockPageScrolling;
+
+ QMetaObject::Connection m_documentStatusChangedConnection;
+
+ QRect m_viewport;
+
+ QHash<int, QImage> m_pageCache;
+ QVector<int> m_cachedPagesLRU;
+ int m_pageCacheLimit;
+
+ DocumentLayout m_documentLayout;
+
+ qreal m_screenResolution; // pixels per point
+};
+
+Q_DECLARE_TYPEINFO(QPdfViewPrivate::DocumentLayout, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif // QPDFVIEW_P_H
diff --git a/src/pdfwidgets/qtpdfwidgetsglobal.h b/src/pdfwidgets/qtpdfwidgetsglobal.h
new file mode 100644
index 000000000..6c73a34f6
--- /dev/null
+++ b/src/pdfwidgets/qtpdfwidgetsglobal.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTPDFWIDGETSGLOBAL_H
+#define QTPDFWIDGETSGLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef Q_PDF_WIDGETS_EXPORT
+# ifndef QT_STATIC
+# if defined(QT_BUILD_PDFWIDGETS_LIB)
+# define Q_PDF_WIDGETS_EXPORT Q_DECL_EXPORT
+# else
+# define Q_PDF_WIDGETS_EXPORT Q_DECL_IMPORT
+# endif
+# else
+# define Q_PDF_WIDGETS_EXPORT
+# endif
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QTPDFWIDGETSGLOBAL_H
+
diff --git a/src/plugins/imageformats/imageformats.pro b/src/plugins/imageformats/imageformats.pro
new file mode 100644
index 000000000..c3b9cb3a4
--- /dev/null
+++ b/src/plugins/imageformats/imageformats.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS += pdf
diff --git a/src/plugins/imageformats/pdf/main.cpp b/src/plugins/imageformats/pdf/main.cpp
new file mode 100644
index 000000000..b4d59353c
--- /dev/null
+++ b/src/plugins/imageformats/pdf/main.cpp
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpdfiohandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPdfPlugin : public QImageIOPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QImageIOHandlerFactoryInterface_iid FILE "pdf.json")
+
+public:
+ Capabilities capabilities(QIODevice *device, const QByteArray &format) const override;
+ QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const override;
+};
+
+QImageIOPlugin::Capabilities QPdfPlugin::capabilities(QIODevice *device, const QByteArray &format) const
+{
+ if (format == "pdf")
+ return Capabilities(CanRead);
+ if (!format.isEmpty())
+ return {};
+
+ Capabilities cap;
+ if (device->isReadable() && QPdfIOHandler::canRead(device))
+ cap |= CanRead;
+ return cap;
+}
+
+QImageIOHandler *QPdfPlugin::create(QIODevice *device, const QByteArray &format) const
+{
+ QPdfIOHandler *hand = new QPdfIOHandler();
+ hand->setDevice(device);
+ hand->setFormat(format);
+ return hand;
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/imageformats/pdf/pdf.json b/src/plugins/imageformats/pdf/pdf.json
new file mode 100644
index 000000000..1f5268ca1
--- /dev/null
+++ b/src/plugins/imageformats/pdf/pdf.json
@@ -0,0 +1,4 @@
+{
+ "Keys": [ "pdf" ],
+ "MimeTypes": [ "application/pdf" ]
+}
diff --git a/src/plugins/imageformats/pdf/pdf.pro b/src/plugins/imageformats/pdf/pdf.pro
new file mode 100644
index 000000000..b820ae7d5
--- /dev/null
+++ b/src/plugins/imageformats/pdf/pdf.pro
@@ -0,0 +1,11 @@
+TARGET = qpdf
+
+PLUGIN_TYPE = imageformats
+PLUGIN_EXTENDS = pdf
+PLUGIN_CLASS_NAME = QPdfPlugin
+load(qt_plugin)
+
+HEADERS += qpdfiohandler_p.h
+SOURCES += main.cpp \
+ qpdfiohandler.cpp
+QT += pdf
diff --git a/src/plugins/imageformats/pdf/qpdfiohandler.cpp b/src/plugins/imageformats/pdf/qpdfiohandler.cpp
new file mode 100644
index 000000000..739e8b34c
--- /dev/null
+++ b/src/plugins/imageformats/pdf/qpdfiohandler.cpp
@@ -0,0 +1,244 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpdfiohandler_p.h"
+#include <QLoggingCategory>
+#include <QPainter>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qLcPdf, "qt.imageformat.pdf")
+
+QPdfIOHandler::QPdfIOHandler()
+{
+}
+
+bool QPdfIOHandler::canRead() const
+{
+ if (!device())
+ return false;
+ if (m_loaded)
+ return true;
+ if (QPdfIOHandler::canRead(device())) {
+ setFormat("pdf");
+ return true;
+ }
+ return false;
+}
+
+bool QPdfIOHandler::canRead(QIODevice *device)
+{
+ char buf[6];
+ device->peek(buf, 6);
+ return (!qstrncmp(buf, "%PDF-", 5) || Q_UNLIKELY(!qstrncmp(buf, "\012%PDF-", 6)));
+}
+
+int QPdfIOHandler::currentImageNumber() const
+{
+ return m_page;
+}
+
+QRect QPdfIOHandler::currentImageRect() const
+{
+ return QRect(QPoint(0, 0), m_doc.pageSize(m_page).toSize());
+}
+
+int QPdfIOHandler::imageCount() const
+{
+ int ret = 0;
+ if (const_cast<QPdfIOHandler *>(this)->load(device()))
+ ret = m_doc.pageCount();
+ qCDebug(qLcPdf) << "imageCount" << ret;
+ return ret;
+}
+
+QByteArray QPdfIOHandler::name() const
+{
+ return m_doc.metaData(QPdfDocument::MetaDataField::Title).toString().toLocal8Bit();
+}
+
+bool QPdfIOHandler::read(QImage *image)
+{
+ if (load(device())) {
+ if (m_page >= m_doc.pageCount())
+ return false;
+ if (m_page < 0)
+ m_page = 0;
+ const bool xform = (m_clipRect.isValid() || m_scaledSize.isValid() || m_scaledClipRect.isValid());
+ QSize pageSize = m_doc.pageSize(m_page).toSize();
+ QSize finalSize = pageSize;
+ QRectF bounds;
+ if (xform && !finalSize.isEmpty()) {
+ bounds = QRectF(QPointF(0,0), QSizeF(finalSize));
+ QPoint tr1, tr2;
+ QSizeF sc(1, 1);
+ if (m_clipRect.isValid()) {
+ tr1 = -m_clipRect.topLeft();
+ finalSize = m_clipRect.size();
+ }
+ if (m_scaledSize.isValid()) {
+ sc = QSizeF(qreal(m_scaledSize.width()) / finalSize.width(),
+ qreal(m_scaledSize.height()) / finalSize.height());
+ finalSize = m_scaledSize;
+ pageSize = m_scaledSize;
+ }
+ if (m_scaledClipRect.isValid()) {
+ tr2 = -m_scaledClipRect.topLeft();
+ finalSize = m_scaledClipRect.size();
+ }
+ QTransform t;
+ t.translate(tr2.x(), tr2.y());
+ t.scale(sc.width(), sc.height());
+ t.translate(tr1.x(), tr1.y());
+ bounds = t.mapRect(bounds);
+ }
+ qCDebug(qLcPdf) << Q_FUNC_INFO << m_page << finalSize;
+ if (image->size() != finalSize || !image->reinterpretAsFormat(QImage::Format_ARGB32_Premultiplied)) {
+ *image = QImage(finalSize, QImage::Format_ARGB32_Premultiplied);
+ if (!finalSize.isEmpty() && image->isNull()) {
+ // avoid QTBUG-68229
+ qWarning("QPdfIOHandler: QImage allocation failed (size %i x %i)", finalSize.width(), finalSize.height());
+ return false;
+ }
+ }
+ if (!finalSize.isEmpty()) {
+ QPdfDocumentRenderOptions options;
+ if (m_scaledClipRect.isValid())
+ options.setScaledClipRect(m_scaledClipRect);
+ options.setScaledSize(pageSize);
+ image->fill(m_backColor.rgba());
+ QPainter p(image);
+ QImage pageImage = m_doc.render(m_page, finalSize, options);
+ p.drawImage(0, 0, pageImage);
+ p.end();
+ }
+ return true;
+ }
+
+ return false;
+}
+
+QVariant QPdfIOHandler::option(ImageOption option) const
+{
+ switch (option) {
+ case ImageFormat:
+ return QImage::Format_ARGB32_Premultiplied;
+ case Size:
+ const_cast<QPdfIOHandler *>(this)->load(device());
+ return m_doc.pageSize(qMax(0, m_page));
+ case ClipRect:
+ return m_clipRect;
+ case ScaledSize:
+ return m_scaledSize;
+ case ScaledClipRect:
+ return m_scaledClipRect;
+ case BackgroundColor:
+ return m_backColor;
+ case Name:
+ return m_doc.metaData(QPdfDocument::Title);
+ default:
+ break;
+ }
+ return QVariant();
+}
+
+void QPdfIOHandler::setOption(ImageOption option, const QVariant & value)
+{
+ switch (option) {
+ case ClipRect:
+ m_clipRect = value.toRect();
+ break;
+ case ScaledSize:
+ m_scaledSize = value.toSize();
+ break;
+ case ScaledClipRect:
+ m_scaledClipRect = value.toRect();
+ break;
+ case BackgroundColor:
+ m_backColor = value.value<QColor>();
+ break;
+ default:
+ break;
+ }
+}
+
+bool QPdfIOHandler::supportsOption(ImageOption option) const
+{
+ switch (option)
+ {
+ case ImageFormat:
+ case Size:
+ case ClipRect:
+ case ScaledSize:
+ case ScaledClipRect:
+ case BackgroundColor:
+ case Name:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+bool QPdfIOHandler::jumpToImage(int frame)
+{
+ qCDebug(qLcPdf) << Q_FUNC_INFO << frame;
+ if (frame < 0 || frame >= m_doc.pageCount())
+ return false;
+ m_page = frame;
+ return true;
+}
+
+bool QPdfIOHandler::jumpToNextImage()
+{
+ return jumpToImage(m_page + 1);
+}
+
+bool QPdfIOHandler::load(QIODevice *device)
+{
+ if (m_loaded)
+ return true;
+ if (format().isEmpty())
+ if (!canRead())
+ return false;
+
+ m_doc.load(device);
+ m_loaded = (m_doc.error() == QPdfDocument::DocumentError::NoError);
+
+ return m_loaded;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/imageformats/pdf/qpdfiohandler_p.h b/src/plugins/imageformats/pdf/qpdfiohandler_p.h
new file mode 100644
index 000000000..ca0a27581
--- /dev/null
+++ b/src/plugins/imageformats/pdf/qpdfiohandler_p.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDFIOHANDLER_H
+#define QPDFIOHANDLER_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 <QtGui/qimageiohandler.h>
+#include <QtPdf/QPdfDocument>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfIOHandler : public QImageIOHandler
+{
+public:
+ QPdfIOHandler();
+ bool canRead() const override;
+ static bool canRead(QIODevice *device);
+ int currentImageNumber() const override;
+ QRect currentImageRect() const override;
+ int imageCount() const override;
+ QByteArray name() const override;
+ bool read(QImage *image) override;
+ QVariant option(ImageOption option) const override;
+ void setOption(ImageOption option, const QVariant & value) override;
+ bool supportsOption(ImageOption option) const override;
+ bool jumpToImage(int frame) override;
+ bool jumpToNextImage() override;
+
+private:
+ bool load(QIODevice *device);
+
+private:
+ QPdfDocument m_doc;
+ int m_page = -1;
+
+ QRect m_clipRect;
+ QSize m_scaledSize;
+ QRect m_scaledClipRect;
+ QColor m_backColor = Qt::transparent;
+ bool m_loaded = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFIOHANDLER_H
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro
index 6698a9736..50181aa19 100644
--- a/src/plugins/plugins.pro
+++ b/src/plugins/plugins.pro
@@ -1,2 +1,3 @@
TEMPLATE = subdirs
-qtHaveModule(designer): SUBDIRS += qwebengineview
+qtHaveModule(webenginewidgets): qtHaveModule(designer): SUBDIRS += qwebengineview
+qtHaveModule(pdf): qtConfig(imageformatplugin): SUBDIRS += imageformats
diff --git a/src/process/Entitlements_mac.plist b/src/process/QtWebEngineProcess.entitlements
index f2fbabddb..f2fbabddb 100644
--- a/src/process/Entitlements_mac.plist
+++ b/src/process/QtWebEngineProcess.entitlements
diff --git a/src/process/process.pro b/src/process/process.pro
index e861af182..72aad52a5 100644
--- a/src/process/process.pro
+++ b/src/process/process.pro
@@ -46,6 +46,11 @@ qtConfig(build_all): CONFIG += build_all
qtConfig(framework) {
# Deploy the QtWebEngineProcess app bundle into the QtWebEngineCore framework.
DESTDIR = $$MODULE_BASE_OUTDIR/lib/QtWebEngineCore.framework/Versions/5/Helpers
+
+ # Deploy the entitlements file so macdeployqt can use it.
+ entitlements.files = QtWebEngineProcess.entitlements
+ entitlements.path = Contents/Resources/
+ QMAKE_BUNDLE_DATA += entitlements
} else {
CONFIG -= app_bundle
win32: DESTDIR = $$MODULE_BASE_OUTDIR/bin
diff --git a/src/src.pro b/src/src.pro
index de88878a6..063c148b5 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -4,9 +4,11 @@ include($$QTWEBENGINE_OUT_ROOT/src/buildtools/qtbuildtools-config.pri)
include($$QTWEBENGINE_OUT_ROOT/src/core/qtwebenginecore-config.pri)
include($$QTWEBENGINE_OUT_ROOT/src/webengine/qtwebengine-config.pri)
include($$QTWEBENGINE_OUT_ROOT/src/webenginewidgets/qtwebenginewidgets-config.pri)
+include($$QTWEBENGINE_OUT_ROOT/src/pdf/qtpdf-config.pri)
+include($$QTWEBENGINE_OUT_ROOT/src/pdfwidgets/qtpdfwidgets-config.pri)
QT_FOR_CONFIG += buildtools-private webenginecore webenginecore-private webengine-private \
- webenginewidgets-private
+ webenginewidgets-private pdf-private pdfwidgets-private
TEMPLATE = subdirs
@@ -38,7 +40,20 @@ qtConfig(build-qtwebengine-core):qtConfig(webengine-core-support) {
}
}
-!qtConfig(webengine-core-support): qtConfig(build-qtwebengine-core) {
+qtConfig(build-qtpdf):qtConfig(webengine-qtpdf-support) {
+ pdf.depends = buildtools
+ qtConfig(build-qtwebengine-core):qtConfig(webengine-core-support): pdf.depends += core
+ SUBDIRS += pdf
+ !contains(SUBDIRS, buildtools): SUBDIRS += buildtools
+ !contains(SUBDIRS, plugins): SUBDIRS += plugins
+ plugins.depends += pdf
+ qtConfig(pdf-widgets) {
+ pdfwidgets.depends = pdf
+ SUBDIRS += pdfwidgets
+ }
+}
+
+!qtConfig(webengine-core-support):if(qtConfig(build-qtwebengine-core)|qtConfig(build-qtpdf)) {
!qtwebengine_makeCheckError():!isEmpty(skipBuildReason):!build_pass {
errorbuild.commands = @echo Modules will not be built. $${skipBuildReason}
errorbuild.CONFIG = phony
diff --git a/src/tools/qwebengine_convert_dict/main.cpp b/src/tools/qwebengine_convert_dict/main.cpp
index 1694dbcef..0ebba96b1 100644
--- a/src/tools/qwebengine_convert_dict/main.cpp
+++ b/src/tools/qwebengine_convert_dict/main.cpp
@@ -84,7 +84,7 @@ inline bool VerifyWords(const convert_dict::DicReader::WordList& org_words,
hunspell::BDictReader reader;
if (!reader.Init(reinterpret_cast<const unsigned char*>(serialized.data()),
serialized.size())) {
- out << "BDict is invalid" << endl;
+ out << "BDict is invalid" << Qt::endl;
return false;
}
hunspell::WordIterator iter = reader.GetAllWordIterator();
@@ -96,7 +96,7 @@ inline bool VerifyWords(const convert_dict::DicReader::WordList& org_words,
for (size_t i = 0; i < org_words.size(); i++) {
int affix_matches = iter.Advance(buf, buf_size, affix_ids);
if (affix_matches == 0) {
- out << "Found the end before we expected" << endl;
+ out << "Found the end before we expected" << Qt::endl;
return false;
}
@@ -104,7 +104,7 @@ inline bool VerifyWords(const convert_dict::DicReader::WordList& org_words,
out << "Word does not match!\n"
<< " Index: " << i << "\n"
<< " Expected: " << QString::fromStdString(org_words[i].first) << "\n"
- << " Actual: " << QString::fromUtf8(buf) << endl;
+ << " Actual: " << QString::fromUtf8(buf) << Qt::endl;
return false;
}
@@ -118,7 +118,7 @@ inline bool VerifyWords(const convert_dict::DicReader::WordList& org_words,
<< " Index: " << i << "\n"
<< " Word: " << QString::fromUtf8(buf) << "\n"
<< " Expected: " << expectedAffixes << "\n"
- << " Actual: " << actualAffixes << endl;
+ << " Actual: " << actualAffixes << Qt::endl;
return false;
}
}
@@ -148,7 +148,7 @@ int main(int argc, char *argv[])
out << "Usage: qwebengine_convert_dict <dic file> <bdic file>\n\nExample:\n"
"qwebengine_convert_dict ./en-US.dic ./en-US.bdic\nwill read en-US.dic, "
"en-US.dic_delta, and en-US.aff from the current directory and generate "
- "en-US.bdic\n" << endl;
+ "en-US.bdic\n" << Qt::endl;
return 1;
}
@@ -184,7 +184,7 @@ int main(int argc, char *argv[])
out << "Couldn't find ICU data directory. Please check that the following path exists: "
<< icuDataDir
<< "\nAlternatively provide the directory path via the QT_WEBENGINE_ICU_DAT_DIR "
- "environment variable.\n" << endl;
+ "environment variable.\n" << Qt::endl;
return 1;
}
@@ -196,21 +196,21 @@ int main(int argc, char *argv[])
base::FilePath file_out_path = toFilePath(argv[2]);
base::FilePath aff_path = file_in_path.ReplaceExtension(FILE_PATH_LITERAL(".aff"));
- out << "Reading " << toQt(aff_path.value()) << endl;
+ out << "Reading " << toQt(aff_path.value()) << Qt::endl;
convert_dict::AffReader aff_reader(aff_path);
if (!aff_reader.Read()) {
- out << "Unable to read the aff file." << endl;
+ out << "Unable to read the aff file." << Qt::endl;
return 1;
}
base::FilePath dic_path = file_in_path.ReplaceExtension(FILE_PATH_LITERAL(".dic"));
- out << "Reading " << toQt(dic_path.value()) << endl;
+ out << "Reading " << toQt(dic_path.value()) << Qt::endl;
// DicReader will also read the .dic_delta file.
convert_dict::DicReader dic_reader(dic_path);
if (!dic_reader.Read(&aff_reader)) {
- out << "Unable to read the dic file." << endl;
+ out << "Unable to read the dic file." << Qt::endl;
return 1;
}
@@ -222,27 +222,27 @@ int main(int argc, char *argv[])
writer.SetOtherCommands(aff_reader.other_commands());
writer.SetWords(dic_reader.words());
- out << "Serializing..." << endl;
+ out << "Serializing..." << Qt::endl;
std::string serialized = writer.GetBDict();
- out << "Verifying..." << endl;
+ out << "Verifying..." << Qt::endl;
if (!VerifyWords(dic_reader.words(), serialized, out)) {
- out << "ERROR converting, the dictionary does not check out OK." << endl;
+ out << "ERROR converting, the dictionary does not check out OK." << Qt::endl;
return 1;
}
- out << "Writing " << toQt(file_out_path.value()) << endl;
+ out << "Writing " << toQt(file_out_path.value()) << Qt::endl;
FILE *out_file = base::OpenFile(file_out_path, "wb");
if (!out_file) {
- out << "ERROR writing file" << endl;
+ out << "ERROR writing file" << Qt::endl;
return 1;
}
size_t written = fwrite(&serialized[0], 1, serialized.size(), out_file);
Q_ASSERT(written == serialized.size());
base::CloseFile(out_file);
- out << "Success. Dictionary converted." << endl;
+ out << "Success. Dictionary converted." << Qt::endl;
return 0;
}
diff --git a/src/webengine/api/qquickwebenginehistory.cpp b/src/webengine/api/qquickwebenginehistory.cpp
index d3c4a0026..e77974a0f 100644
--- a/src/webengine/api/qquickwebenginehistory.cpp
+++ b/src/webengine/api/qquickwebenginehistory.cpp
@@ -327,6 +327,20 @@ QQuickWebEngineHistoryListModel *QQuickWebEngineHistory::forwardItems() const
return d->m_forwardNavigationModel.data();
}
+/*!
+ \qmlmethod void WebEngineHistory::clear()
+ \since QtWebEngine 1.11
+
+ Clears the history.
+*/
+void QQuickWebEngineHistory::clear()
+{
+ Q_D(QQuickWebEngineHistory);
+ d->m_view->adapter->clearNavigationHistory();
+ d->m_view->updateNavigationActions();
+ reset();
+}
+
void QQuickWebEngineHistory::reset()
{
Q_D(QQuickWebEngineHistory);
diff --git a/src/webengine/api/qquickwebenginehistory_p.h b/src/webengine/api/qquickwebenginehistory_p.h
index bf049b2a6..5d4783e96 100644
--- a/src/webengine/api/qquickwebenginehistory_p.h
+++ b/src/webengine/api/qquickwebenginehistory_p.h
@@ -107,6 +107,7 @@ public:
QQuickWebEngineHistoryListModel *items() const;
QQuickWebEngineHistoryListModel *backItems() const;
QQuickWebEngineHistoryListModel *forwardItems() const;
+ Q_REVISION(1) Q_INVOKABLE void clear();
void reset();
diff --git a/src/webengine/api/qquickwebenginetestsupport.cpp b/src/webengine/api/qquickwebenginetestsupport.cpp
index b7b863125..bef87160e 100644
--- a/src/webengine/api/qquickwebenginetestsupport.cpp
+++ b/src/webengine/api/qquickwebenginetestsupport.cpp
@@ -157,7 +157,7 @@ void QQuickWebEngineTestEvent::mouseEvent(QEvent::Type type, QWindow *window, QO
if (sgitem)
pos = sgitem->mapToScene(_pos).toPoint();
- QMouseEvent me(type, pos, window->mapFromGlobal(pos), Qt::LeftButton, Qt::LeftButton, 0);
+ QMouseEvent me(type, pos, window->mapFromGlobal(pos), Qt::LeftButton, Qt::LeftButton, {});
me.setTimestamp(++QTest::lastMouseTimestamp);
QSpontaneKeyEvent::setSpontaneous(&me);
diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp
index 3ce202695..8f5313a9d 100644
--- a/src/webengine/api/qquickwebengineview.cpp
+++ b/src/webengine/api/qquickwebengineview.cpp
@@ -428,6 +428,12 @@ void QQuickWebEngineViewPrivate::recentlyAudibleChanged(bool recentlyAudible)
Q_EMIT q->recentlyAudibleChanged(recentlyAudible);
}
+void QQuickWebEngineViewPrivate::renderProcessPidChanged(qint64 pid)
+{
+ Q_Q(QQuickWebEngineView);
+ Q_EMIT q->renderProcessPidChanged(pid);
+}
+
QRectF QQuickWebEngineViewPrivate::viewportRect() const
{
Q_Q(const QQuickWebEngineView);
@@ -1418,6 +1424,12 @@ bool QQuickWebEngineView::recentlyAudible() const
return d->adapter->recentlyAudible();
}
+qint64 QQuickWebEngineView::renderProcessPid() const
+{
+ const Q_D(QQuickWebEngineView);
+ return d->adapter->renderProcessPid();
+}
+
void QQuickWebEngineView::printToPdf(const QString& filePath, PrintedPageSizeId pageSizeId, PrintedPageOrientation orientation)
{
#if QT_CONFIG(webengine_printing_and_pdf)
diff --git a/src/webengine/api/qquickwebengineview_p.h b/src/webengine/api/qquickwebengineview_p.h
index 618f9407e..ab84b2600 100644
--- a/src/webengine/api/qquickwebengineview_p.h
+++ b/src/webengine/api/qquickwebengineview_p.h
@@ -142,6 +142,8 @@ class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineView : public QQuickItem {
Q_PROPERTY(LifecycleState lifecycleState READ lifecycleState WRITE setLifecycleState NOTIFY lifecycleStateChanged REVISION 10 FINAL)
Q_PROPERTY(LifecycleState recommendedState READ recommendedState NOTIFY recommendedStateChanged REVISION 10 FINAL)
+ Q_PROPERTY(qint64 renderProcessPid READ renderProcessPid NOTIFY renderProcessPidChanged FINAL REVISION 11)
+
public:
QQuickWebEngineView(QQuickItem *parent = 0);
~QQuickWebEngineView();
@@ -493,6 +495,8 @@ public:
void setAudioMuted(bool muted);
bool recentlyAudible() const;
+ qint64 renderProcessPid() const;
+
#if QT_CONFIG(webengine_testsupport)
QQuickWebEngineTestSupport *testSupport() const;
void setTestSupport(QQuickWebEngineTestSupport *testSupport);
@@ -520,7 +524,7 @@ public Q_SLOTS:
void reload();
Q_REVISION(1) void reloadAndBypassCache();
void stop();
- Q_REVISION(1) void findText(const QString &subString, FindFlags options = 0, const QJSValue &callback = QJSValue());
+ Q_REVISION(1) void findText(const QString &subString, FindFlags options = { }, const QJSValue &callback = QJSValue());
Q_REVISION(1) void fullScreenCancelled();
Q_REVISION(1) void grantFeaturePermission(const QUrl &securityOrigin, Feature, bool granted);
Q_REVISION(2) void setActiveFocusOnPress(bool arg);
@@ -576,6 +580,7 @@ Q_SIGNALS:
Q_REVISION(10) void lifecycleStateChanged(LifecycleState state);
Q_REVISION(10) void recommendedStateChanged(LifecycleState state);
Q_REVISION(10) void findTextFinished(const QWebEngineFindTextResult &result);
+ Q_REVISION(11) void renderProcessPidChanged(qint64 pid);
#if QT_CONFIG(webengine_testsupport)
void testSupportChanged();
diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h
index 6408767b3..1f024167f 100644
--- a/src/webengine/api/qquickwebengineview_p_p.h
+++ b/src/webengine/api/qquickwebengineview_p_p.h
@@ -112,6 +112,7 @@ public:
void didUpdateTargetURL(const QUrl&) override;
void selectionChanged() override;
void recentlyAudibleChanged(bool recentlyAudible) override;
+ void renderProcessPidChanged(qint64 pid) override;
QRectF viewportRect() const override;
QColor backgroundColor() const override;
void loadStarted(const QUrl &provisionalUrl, bool isErrorPage = false) override;
diff --git a/src/webengine/doc/src/webengineview_lgpl.qdoc b/src/webengine/doc/src/webengineview_lgpl.qdoc
index 6e349dfb3..0cd8441cf 100644
--- a/src/webengine/doc/src/webengineview_lgpl.qdoc
+++ b/src/webengine/doc/src/webengineview_lgpl.qdoc
@@ -1614,3 +1614,27 @@
\sa findText(), FindTextResult
*/
+
+/*!
+ \qmlproperty qint64 WebEngineView::renderProcessPid
+ \brief Returns the process ID (PID) of the render process assigned to the
+ current page's main frame.
+ \since QtWebEngine 1.11
+ \readonly
+
+ If no render process is available yet, \c 0 is returned.
+
+ \sa renderProcessPidChanged
+*/
+
+/*!
+ \qmlsignal WebEngineView::renderProcessPidChanged(qint64 pid)
+ \since QtWebEngine 1.11
+ \readonly
+
+ If no render process is available yet, \c 0 is returned.
+ This signal is emitted when the PID (process ID) of the page's underlying
+ render process changed.
+
+ \sa renderProcessPid
+*/
diff --git a/src/webengine/plugin/plugin.cpp b/src/webengine/plugin/plugin.cpp
index 3acf7d058..a74373b23 100644
--- a/src/webengine/plugin/plugin.cpp
+++ b/src/webengine/plugin/plugin.cpp
@@ -97,6 +97,7 @@ public:
qmlRegisterType<QQuickWebEngineView, 8>(uri, 1, 8, "WebEngineView");
qmlRegisterType<QQuickWebEngineView, 9>(uri, 1, 9, "WebEngineView");
qmlRegisterType<QQuickWebEngineView, 10>(uri, 1, 10, "WebEngineView");
+ qmlRegisterType<QQuickWebEngineView, 11>(uri, 1, 11, "WebEngineView");
qmlRegisterType<QQuickWebEngineProfile>(uri, 1, 1, "WebEngineProfile");
qmlRegisterType<QQuickWebEngineProfile, 1>(uri, 1, 2, "WebEngineProfile");
qmlRegisterType<QQuickWebEngineProfile, 2>(uri, 1, 3, "WebEngineProfile");
@@ -137,6 +138,7 @@ public:
qmlRegisterSingletonType<QQuickWebEngineSingleton>(uri, 1, 1, "WebEngine", webEngineSingletonProvider);
qmlRegisterUncreatableType<QQuickWebEngineHistory>(uri, 1, 1, "NavigationHistory",
msgUncreatableType("NavigationHistory"));
+ qmlRegisterUncreatableType<QQuickWebEngineHistory, 1>(uri, 1, 11, "NavigationHistory", msgUncreatableType("NavigationHistory"));
qmlRegisterUncreatableType<QQuickWebEngineHistoryListModel>(uri, 1, 1, "NavigationHistoryListModel",
msgUncreatableType("NavigationHistory"));
qmlRegisterUncreatableType<QQuickWebEngineFullScreenRequest>(uri, 1, 1, "FullScreenRequest",
diff --git a/src/webengine/plugin/plugins.qmltypes b/src/webengine/plugin/plugins.qmltypes
index a23d1c3c0..24b073290 100644
--- a/src/webengine/plugin/plugins.qmltypes
+++ b/src/webengine/plugin/plugins.qmltypes
@@ -1348,6 +1348,7 @@ Module {
Property { name: "devToolsView"; revision: 7; type: "QQuickWebEngineView"; isPointer: true }
Property { name: "lifecycleState"; revision: 10; type: "LifecycleState" }
Property { name: "recommendedState"; revision: 10; type: "LifecycleState"; isReadonly: true }
+ Property { name: "renderProcessId"; revision: 11; type: "qint64"; isReadonly: true }
Signal {
name: "loadingChanged"
Parameter { name: "loadRequest"; type: "QQuickWebEngineLoadRequest"; isPointer: true }
@@ -1526,6 +1527,11 @@ Module {
revision: 10
Parameter { name: "result"; type: "QWebEngineFindTextResult" }
}
+ Signal {
+ name: "renderProcessPidChanged"
+ revision: 11
+ Parameter { name: "pid"; type: "qint64" }
+ }
Method {
name: "runJavaScript"
Parameter { type: "string" }
diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp
index 850a29a16..ad6196850 100644
--- a/src/webenginewidgets/api/qwebenginepage.cpp
+++ b/src/webenginewidgets/api/qwebenginepage.cpp
@@ -277,6 +277,12 @@ void QWebEnginePagePrivate::recentlyAudibleChanged(bool recentlyAudible)
Q_EMIT q->recentlyAudibleChanged(recentlyAudible);
}
+void QWebEnginePagePrivate::renderProcessPidChanged(qint64 pid)
+{
+ Q_Q(QWebEnginePage);
+ Q_EMIT q->renderProcessPidChanged(pid);
+}
+
QRectF QWebEnginePagePrivate::viewportRect() const
{
return view ? view->rect() : QRectF();
@@ -937,6 +943,13 @@ QWebEnginePage::QWebEnginePage(QObject* parent)
*/
/*!
+ \fn void QWebEnginePage::renderProcessPidChanged(qint64 pid);
+ \since 5.15
+
+ This signal is emitted when the underlying render process PID, \a renderProcessPid, changes.
+*/
+
+/*!
\fn void QWebEnginePage::iconUrlChanged(const QUrl &url)
This signal is emitted when the URL of the icon ("favicon") associated with the
@@ -1149,6 +1162,20 @@ bool QWebEnginePage::recentlyAudible() const
return d->adapter->isInitialized() && d->adapter->recentlyAudible();
}
+/*!
+ \property QWebEnginePage::renderProcessPid
+ \brief The process ID (PID) of the render process assigned to the current
+ page's main frame.
+ \since 5.15
+
+ If no render process is available yet, \c 0 is returned.
+*/
+qint64 QWebEnginePage::renderProcessPid() const
+{
+ Q_D(const QWebEnginePage);
+ return d->adapter->renderProcessPid();
+}
+
void QWebEnginePage::setView(QWidget *newViewBase)
{
QWebEnginePagePrivate::bindPageAndView(this, qobject_cast<QWebEngineView *>(newViewBase));
@@ -2312,7 +2339,7 @@ QStringList QWebEnginePage::chooseFiles(FileSelectionMode mode, const QStringLis
QString str;
switch (static_cast<FilePickerController::FileChooserMode>(mode)) {
case FilePickerController::OpenMultiple:
- ret = QFileDialog::getOpenFileNames(view(), QString(), QString(), filter.join(";;"), nullptr, QFileDialog::HideNameFilterDetails);
+ ret = QFileDialog::getOpenFileNames(view(), QString(), QString(), filter.join(QStringLiteral(";;")), nullptr, QFileDialog::HideNameFilterDetails);
break;
// Chromium extension, not exposed as part of the public API for now.
case FilePickerController::UploadFolder:
@@ -2326,7 +2353,7 @@ QStringList QWebEnginePage::chooseFiles(FileSelectionMode mode, const QStringLis
ret << str;
break;
case FilePickerController::Open:
- str = QFileDialog::getOpenFileName(view(), QString(), oldFiles.first(), filter.join(";;"), nullptr, QFileDialog::HideNameFilterDetails);
+ str = QFileDialog::getOpenFileName(view(), QString(), oldFiles.first(), filter.join(QStringLiteral(";;")), nullptr, QFileDialog::HideNameFilterDetails);
if (!str.isNull())
ret << str;
break;
diff --git a/src/webenginewidgets/api/qwebenginepage.h b/src/webenginewidgets/api/qwebenginepage.h
index cd012ea70..3cf6a9f8b 100644
--- a/src/webenginewidgets/api/qwebenginepage.h
+++ b/src/webenginewidgets/api/qwebenginepage.h
@@ -92,6 +92,7 @@ class QWEBENGINEWIDGETS_EXPORT QWebEnginePage : public QObject {
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged)
Q_PROPERTY(LifecycleState lifecycleState READ lifecycleState WRITE setLifecycleState NOTIFY lifecycleStateChanged)
Q_PROPERTY(LifecycleState recommendedState READ recommendedState NOTIFY recommendedStateChanged)
+ Q_PROPERTY(qint64 renderProcessPid READ renderProcessPid NOTIFY renderProcessPidChanged)
public:
enum WebAction {
@@ -305,6 +306,7 @@ public:
bool isAudioMuted() const;
void setAudioMuted(bool muted);
bool recentlyAudible() const;
+ qint64 renderProcessPid() const;
void printToPdf(const QString &filePath, const QPageLayout &layout = QPageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF()));
void printToPdf(const QWebEngineCallback<const QByteArray&> &resultCallback, const QPageLayout &layout = QPageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF()));
@@ -361,6 +363,7 @@ Q_SIGNALS:
void contentsSizeChanged(const QSizeF &size);
void audioMutedChanged(bool muted);
void recentlyAudibleChanged(bool recentlyAudible);
+ void renderProcessPidChanged(qint64 pid);
void pdfPrintingFinished(const QString &filePath, bool success);
void printRequested();
diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h
index e78b0f926..7fdc811e2 100644
--- a/src/webenginewidgets/api/qwebenginepage_p.h
+++ b/src/webenginewidgets/api/qwebenginepage_p.h
@@ -103,6 +103,7 @@ public:
void didUpdateTargetURL(const QUrl&) override;
void selectionChanged() override;
void recentlyAudibleChanged(bool recentlyAudible) override;
+ void renderProcessPidChanged(qint64 pid) override;
QRectF viewportRect() const override;
QColor backgroundColor() const override;
void loadStarted(const QUrl &provisionalUrl, bool isErrorPage = false) override;
diff --git a/sync.profile b/sync.profile
index 4cc549106..a1e3c8ded 100644
--- a/sync.profile
+++ b/sync.profile
@@ -2,11 +2,14 @@
"QtWebEngine" => "$basedir/src/webengine",
"QtWebEngineWidgets" => "$basedir/src/webenginewidgets",
"QtWebEngineCore" => "$basedir/src/core",
+ "QtPdf" => "$basedir/src/pdf",
+ "QtPdfWidgets" => "$basedir/src/pdfwidgets",
);
%moduleheaders = ( # restrict the module headers to those found in relative path
"QtWebEngine" => "api",
"QtWebEngineWidgets" => "api",
"QtWebEngineCore" => "api",
+ "QtPdf" => "api"
);
%classnames = (
);
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 59bcd5aef..9b71e1183 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -7,3 +7,8 @@ qtHaveModule(webengine) {
qtHaveModule(webenginewidgets) {
SUBDIRS += core widgets
}
+
+qtHaveModule(pdf) {
+ SUBDIRS += pdf
+}
+
diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp
index a7b44602f..5effb2abf 100644
--- a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp
+++ b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp
@@ -290,18 +290,21 @@ void tst_QWebEngineUrlRequestInterceptor::requestedUrl()
page.setUrl(QUrl("qrc:///resources/__placeholder__"));
QVERIFY(spy.wait());
QTRY_COMPARE(spy.count(), 1);
+ QVERIFY(interceptor.requestInfos.count() >= 1);
QCOMPARE(interceptor.requestInfos.at(0).requestUrl, QUrl("qrc:///resources/content.html"));
QCOMPARE(page.requestedUrl(), QUrl("qrc:///resources/__placeholder__"));
QCOMPARE(page.url(), QUrl("qrc:///resources/content.html"));
page.setUrl(QUrl("qrc:/non-existent.html"));
QTRY_COMPARE(spy.count(), 2);
+ QVERIFY(interceptor.requestInfos.count() >= 3);
QCOMPARE(interceptor.requestInfos.at(2).requestUrl, QUrl("qrc:/non-existent.html"));
QCOMPARE(page.requestedUrl(), QUrl("qrc:///resources/__placeholder__"));
QCOMPARE(page.url(), QUrl("qrc:///resources/content.html"));
page.setUrl(QUrl("http://abcdef.abcdef"));
QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 3, 15000);
+ QVERIFY(interceptor.requestInfos.count() >= 4);
QCOMPARE(interceptor.requestInfos.at(3).requestUrl, QUrl("http://abcdef.abcdef/"));
QCOMPARE(page.requestedUrl(), QUrl("qrc:///resources/__placeholder__"));
QCOMPARE(page.url(), QUrl("qrc:///resources/content.html"));
@@ -359,6 +362,7 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrl()
page.setUrl(QUrl("qrc:///resources/firstparty.html"));
QVERIFY(spy.wait());
+ QVERIFY(interceptor.requestInfos.count() >= 2);
QCOMPARE(interceptor.requestInfos.at(0).requestUrl, QUrl("qrc:///resources/firstparty.html"));
QCOMPARE(interceptor.requestInfos.at(1).requestUrl, QUrl("qrc:///resources/content.html"));
QCOMPARE(interceptor.requestInfos.at(0).firstPartyUrl, QUrl("qrc:///resources/firstparty.html"));
@@ -393,16 +397,19 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrlNestedIframes()
page.setUrl(requestUrl);
QTRY_COMPARE(loadSpy.count(), 1);
+ QVERIFY(interceptor.requestInfos.count() >= 1);
RequestInfo info = interceptor.requestInfos.at(0);
QCOMPARE(info.requestUrl, requestUrl);
QCOMPARE(info.firstPartyUrl, requestUrl);
QCOMPARE(info.resourceType, QWebEngineUrlRequestInfo::ResourceTypeMainFrame);
+ QVERIFY(interceptor.requestInfos.count() >= 2);
info = interceptor.requestInfos.at(1);
QCOMPARE(info.requestUrl, QUrl(adjustedUrl + "iframe2.html"));
QCOMPARE(info.firstPartyUrl, requestUrl);
QCOMPARE(info.resourceType, QWebEngineUrlRequestInfo::ResourceTypeSubFrame);
+ QVERIFY(interceptor.requestInfos.count() >= 3);
info = interceptor.requestInfos.at(2);
QCOMPARE(info.requestUrl, QUrl(adjustedUrl + "iframe3.html"));
QCOMPARE(info.firstPartyUrl, requestUrl);
@@ -456,6 +463,7 @@ void tst_QWebEngineUrlRequestInterceptor::requestInterceptorByResourceType()
QTRY_COMPARE(interceptor.getUrlRequestForType(static_cast<QWebEngineUrlRequestInfo::ResourceType>(resourceType)).count(), 1);
QList<RequestInfo> infos = interceptor.getUrlRequestForType(static_cast<QWebEngineUrlRequestInfo::ResourceType>(resourceType));
+ QVERIFY(infos.count() >= 1);
QCOMPARE(infos.at(0).requestUrl, requestUrl);
QCOMPARE(infos.at(0).firstPartyUrl, firstPartyUrl);
QCOMPARE(infos.at(0).resourceType, resourceType);
diff --git a/tests/auto/pdf/pdf.pro b/tests/auto/pdf/pdf.pro
new file mode 100644
index 000000000..a2b3fcff2
--- /dev/null
+++ b/tests/auto/pdf/pdf.pro
@@ -0,0 +1,8 @@
+TEMPLATE = subdirs
+
+SUBDIRS = \
+ qpdfbookmarkmodel \
+ qpdfpagenavigation \
+ qpdfpagerenderer
+
+qtHaveModule(printsupport): SUBDIRS += qpdfdocument
diff --git a/tests/auto/pdf/qpdfbookmarkmodel/pdf-sample.bookmarks.pdf b/tests/auto/pdf/qpdfbookmarkmodel/pdf-sample.bookmarks.pdf
new file mode 100644
index 000000000..bd27c18b6
--- /dev/null
+++ b/tests/auto/pdf/qpdfbookmarkmodel/pdf-sample.bookmarks.pdf
Binary files differ
diff --git a/tests/auto/pdf/qpdfbookmarkmodel/pdf-sample.bookmarks_pages.pdf b/tests/auto/pdf/qpdfbookmarkmodel/pdf-sample.bookmarks_pages.pdf
new file mode 100644
index 000000000..c4e1aa36e
--- /dev/null
+++ b/tests/auto/pdf/qpdfbookmarkmodel/pdf-sample.bookmarks_pages.pdf
Binary files differ
diff --git a/tests/auto/pdf/qpdfbookmarkmodel/qpdfbookmarkmodel.pro b/tests/auto/pdf/qpdfbookmarkmodel/qpdfbookmarkmodel.pro
new file mode 100644
index 000000000..11a010637
--- /dev/null
+++ b/tests/auto/pdf/qpdfbookmarkmodel/qpdfbookmarkmodel.pro
@@ -0,0 +1,5 @@
+CONFIG += testcase
+TARGET = tst_qpdfbookmarkmodel
+QT += pdf testlib network
+macos:CONFIG -= app_bundle
+SOURCES += tst_qpdfbookmarkmodel.cpp
diff --git a/tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp b/tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp
new file mode 100644
index 000000000..fddc98011
--- /dev/null
+++ b/tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp
@@ -0,0 +1,287 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <QPdfDocument>
+#include <QPdfBookmarkModel>
+
+class tst_QPdfBookmarkModel: public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QPdfBookmarkModel()
+ {
+ qRegisterMetaType<QPdfDocument::Status>();
+ }
+
+private slots:
+ void emptyModel();
+ void setEmptyDocument();
+ void setEmptyDocumentAndLoad();
+ void setLoadedDocument();
+ void unloadDocument();
+ void testTreeStructure();
+ void testListStructure();
+ void testPageNumberRole();
+};
+
+void tst_QPdfBookmarkModel::emptyModel()
+{
+ QPdfBookmarkModel model;
+
+ QVERIFY(!model.document());
+ QCOMPARE(model.structureMode(), QPdfBookmarkModel::TreeMode);
+ QCOMPARE(model.rowCount(), 0);
+ QCOMPARE(model.columnCount(), 1);
+ QCOMPARE(model.index(0, 0).isValid(), false);
+}
+
+void tst_QPdfBookmarkModel::setEmptyDocument()
+{
+ QPdfDocument document;
+ QPdfBookmarkModel model;
+
+ model.setDocument(&document);
+
+ QCOMPARE(model.document(), &document);
+ QCOMPARE(model.structureMode(), QPdfBookmarkModel::TreeMode);
+ QCOMPARE(model.rowCount(), 0);
+ QCOMPARE(model.columnCount(), 1);
+ QCOMPARE(model.index(0, 0).isValid(), false);
+}
+
+void tst_QPdfBookmarkModel::setEmptyDocumentAndLoad()
+{
+ QPdfDocument document;
+ QPdfBookmarkModel model;
+
+ model.setDocument(&document);
+
+ QSignalSpy modelAboutToBeResetSpy(&model, SIGNAL(modelAboutToBeReset()));
+ QSignalSpy modelResetSpy(&model, SIGNAL(modelReset()));
+
+ QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks.pdf")), QPdfDocument::NoError);
+
+ QCOMPARE(modelAboutToBeResetSpy.count(), 1);
+ QCOMPARE(modelResetSpy.count(), 1);
+
+ QCOMPARE(model.rowCount(), 3);
+}
+
+void tst_QPdfBookmarkModel::setLoadedDocument()
+{
+ QPdfDocument document;
+ QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks.pdf")), QPdfDocument::NoError);
+
+ QPdfBookmarkModel model;
+
+ QSignalSpy modelAboutToBeResetSpy(&model, SIGNAL(modelAboutToBeReset()));
+ QSignalSpy modelResetSpy(&model, SIGNAL(modelReset()));
+
+ model.setDocument(&document);
+
+ QCOMPARE(modelAboutToBeResetSpy.count(), 1);
+ QCOMPARE(modelResetSpy.count(), 1);
+
+ QCOMPARE(model.rowCount(), 3);
+}
+
+void tst_QPdfBookmarkModel::unloadDocument()
+{
+ QPdfDocument document;
+ QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks.pdf")), QPdfDocument::NoError);
+
+ QPdfBookmarkModel model;
+ model.setDocument(&document);
+
+ QCOMPARE(model.rowCount(), 3);
+
+ QSignalSpy modelAboutToBeResetSpy(&model, SIGNAL(modelAboutToBeReset()));
+ QSignalSpy modelResetSpy(&model, SIGNAL(modelReset()));
+
+ document.close();
+
+ QCOMPARE(modelAboutToBeResetSpy.count(), 1);
+ QCOMPARE(modelResetSpy.count(), 1);
+
+ QCOMPARE(model.rowCount(), 0);
+}
+
+void tst_QPdfBookmarkModel::testTreeStructure()
+{
+ QPdfDocument document;
+ QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks.pdf")), QPdfDocument::NoError);
+
+ QPdfBookmarkModel model;
+ model.setDocument(&document);
+
+ QCOMPARE(model.rowCount(), 3);
+
+ const QModelIndex index1 = model.index(0, 0);
+ QCOMPARE(index1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 1"));
+ QCOMPARE(index1.data(QPdfBookmarkModel::LevelRole).toInt(), 0);
+ QCOMPARE(model.rowCount(index1), 2);
+
+ const QModelIndex index1_1 = model.index(0, 0, index1);
+ QCOMPARE(index1_1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 1.1"));
+ QCOMPARE(index1_1.data(QPdfBookmarkModel::LevelRole).toInt(), 1);
+ QCOMPARE(model.rowCount(index1_1), 0);
+
+ const QModelIndex index1_2 = model.index(1, 0, index1);
+ QCOMPARE(index1_2.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 1.2"));
+ QCOMPARE(index1_2.data(QPdfBookmarkModel::LevelRole).toInt(), 1);
+ QCOMPARE(model.rowCount(index1_2), 0);
+
+ const QModelIndex index2 = model.index(1, 0);
+ QCOMPARE(index2.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2"));
+ QCOMPARE(index2.data(QPdfBookmarkModel::LevelRole).toInt(), 0);
+ QCOMPARE(model.rowCount(index2), 2);
+
+ const QModelIndex index2_1 = model.index(0, 0, index2);
+ QCOMPARE(index2_1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2.1"));
+ QCOMPARE(index2_1.data(QPdfBookmarkModel::LevelRole).toInt(), 1);
+ QCOMPARE(model.rowCount(index2_1), 1);
+
+ const QModelIndex index2_1_1 = model.index(0, 0, index2_1);
+ QCOMPARE(index2_1_1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2.1.1"));
+ QCOMPARE(index2_1_1.data(QPdfBookmarkModel::LevelRole).toInt(), 2);
+ QCOMPARE(model.rowCount(index2_1_1), 0);
+
+ const QModelIndex index2_2 = model.index(1, 0, index2);
+ QCOMPARE(index2_2.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2.2"));
+ QCOMPARE(index2_2.data(QPdfBookmarkModel::LevelRole).toInt(), 1);
+ QCOMPARE(model.rowCount(index2_2), 0);
+
+ const QModelIndex index3 = model.index(2, 0);
+ QCOMPARE(index3.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 3"));
+ QCOMPARE(index3.data(QPdfBookmarkModel::LevelRole).toInt(), 0);
+ QCOMPARE(model.rowCount(index3), 0);
+
+ const QModelIndex index4 = model.index(3, 0);
+ QCOMPARE(index4, QModelIndex());
+}
+
+void tst_QPdfBookmarkModel::testListStructure()
+{
+ QPdfDocument document;
+ QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks.pdf")), QPdfDocument::NoError);
+
+ QPdfBookmarkModel model;
+ model.setDocument(&document);
+
+ QSignalSpy modelAboutToBeResetSpy(&model, SIGNAL(modelAboutToBeReset()));
+ QSignalSpy modelResetSpy(&model, SIGNAL(modelReset()));
+
+ model.setStructureMode(QPdfBookmarkModel::ListMode);
+
+ QCOMPARE(modelAboutToBeResetSpy.count(), 1);
+ QCOMPARE(modelResetSpy.count(), 1);
+
+ QCOMPARE(model.rowCount(), 8);
+
+ const QModelIndex index1 = model.index(0, 0);
+ QCOMPARE(index1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 1"));
+ QCOMPARE(index1.data(QPdfBookmarkModel::LevelRole).toInt(), 0);
+ QCOMPARE(model.rowCount(index1), 0);
+
+ const QModelIndex index1_1 = model.index(1, 0);
+ QCOMPARE(index1_1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 1.1"));
+ QCOMPARE(index1_1.data(QPdfBookmarkModel::LevelRole).toInt(), 1);
+ QCOMPARE(model.rowCount(index1_1), 0);
+
+ const QModelIndex index1_2 = model.index(2, 0);
+ QCOMPARE(index1_2.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 1.2"));
+ QCOMPARE(index1_2.data(QPdfBookmarkModel::LevelRole).toInt(), 1);
+ QCOMPARE(model.rowCount(index1_2), 0);
+
+ const QModelIndex index2 = model.index(3, 0);
+ QCOMPARE(index2.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2"));
+ QCOMPARE(index2.data(QPdfBookmarkModel::LevelRole).toInt(), 0);
+ QCOMPARE(model.rowCount(index2), 0);
+
+ const QModelIndex index2_1 = model.index(4, 0);
+ QCOMPARE(index2_1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2.1"));
+ QCOMPARE(index2_1.data(QPdfBookmarkModel::LevelRole).toInt(), 1);
+ QCOMPARE(model.rowCount(index2_1), 0);
+
+ const QModelIndex index2_1_1 = model.index(5, 0);
+ QCOMPARE(index2_1_1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2.1.1"));
+ QCOMPARE(index2_1_1.data(QPdfBookmarkModel::LevelRole).toInt(), 2);
+ QCOMPARE(model.rowCount(index2_1_1), 0);
+
+ const QModelIndex index2_2 = model.index(6, 0);
+ QCOMPARE(index2_2.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2.2"));
+ QCOMPARE(index2_2.data(QPdfBookmarkModel::LevelRole).toInt(), 1);
+ QCOMPARE(model.rowCount(index2_2), 0);
+
+ const QModelIndex index3 = model.index(7, 0);
+ QCOMPARE(index3.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 3"));
+ QCOMPARE(index3.data(QPdfBookmarkModel::LevelRole).toInt(), 0);
+ QCOMPARE(model.rowCount(index3), 0);
+
+ const QModelIndex index4 = model.index(8, 0);
+ QCOMPARE(index4, QModelIndex());
+}
+
+void tst_QPdfBookmarkModel::testPageNumberRole()
+{
+ QPdfDocument document;
+ QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks_pages.pdf")), QPdfDocument::NoError);
+
+ QPdfBookmarkModel model;
+ model.setDocument(&document);
+
+ QCOMPARE(model.rowCount(), 3);
+
+ const QModelIndex index1 = model.index(0, 0);
+ QCOMPARE(index1.data(QPdfBookmarkModel::PageNumberRole).toInt(), 0);
+
+ const QModelIndex index2 = model.index(1, 0);
+ QCOMPARE(index2.data(QPdfBookmarkModel::PageNumberRole).toInt(), 1);
+
+ const QModelIndex index2_1 = model.index(0, 0, index2);
+ QCOMPARE(index2_1.data(QPdfBookmarkModel::PageNumberRole).toInt(), 1);
+
+ const QModelIndex index3 = model.index(2, 0);
+ QCOMPARE(index3.data(QPdfBookmarkModel::PageNumberRole).toInt(), 2);
+}
+
+QTEST_MAIN(tst_QPdfBookmarkModel)
+
+#include "tst_qpdfbookmarkmodel.moc"
diff --git a/tests/auto/pdf/qpdfdocument/BLACKLIST b/tests/auto/pdf/qpdfdocument/BLACKLIST
new file mode 100644
index 000000000..b8db556d6
--- /dev/null
+++ b/tests/auto/pdf/qpdfdocument/BLACKLIST
@@ -0,0 +1,6 @@
+[password]
+*
+
+[passwordClearedOnClose]
+*
+
diff --git a/tests/auto/pdf/qpdfdocument/pdf-sample.metadata.pdf b/tests/auto/pdf/qpdfdocument/pdf-sample.metadata.pdf
new file mode 100644
index 000000000..c3350ba5f
--- /dev/null
+++ b/tests/auto/pdf/qpdfdocument/pdf-sample.metadata.pdf
Binary files differ
diff --git a/tests/auto/pdf/qpdfdocument/pdf-sample.protected.pdf b/tests/auto/pdf/qpdfdocument/pdf-sample.protected.pdf
new file mode 100644
index 000000000..d76fdd1a6
--- /dev/null
+++ b/tests/auto/pdf/qpdfdocument/pdf-sample.protected.pdf
Binary files differ
diff --git a/tests/auto/pdf/qpdfdocument/qpdfdocument.pro b/tests/auto/pdf/qpdfdocument/qpdfdocument.pro
new file mode 100644
index 000000000..8382a25e3
--- /dev/null
+++ b/tests/auto/pdf/qpdfdocument/qpdfdocument.pro
@@ -0,0 +1,6 @@
+CONFIG += testcase
+TARGET = tst_qpdfdocument
+QT += pdf printsupport testlib network
+macx:CONFIG -= app_bundle
+SOURCES += tst_qpdfdocument.cpp
+
diff --git a/tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp b/tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp
new file mode 100644
index 000000000..29b85fc89
--- /dev/null
+++ b/tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp
@@ -0,0 +1,386 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <QPainter>
+#include <QPdfDocument>
+#include <QPrinter>
+#include <QTemporaryFile>
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+
+class tst_QPdfDocument: public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QPdfDocument()
+ {
+ qRegisterMetaType<QPdfDocument::Status>();
+ }
+
+private slots:
+ void pageCount();
+ void loadFromIODevice();
+ void loadAsync();
+ void password();
+ void close();
+ void loadAfterClose();
+ void closeOnDestroy();
+ void status();
+ void passwordClearedOnClose();
+ void metaData();
+};
+
+struct TemporaryPdf: public QTemporaryFile
+{
+ TemporaryPdf();
+ QPageLayout pageLayout;
+};
+
+
+TemporaryPdf::TemporaryPdf()
+{
+ open();
+ pageLayout = QPageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF());
+
+ {
+ QPrinter printer;
+ printer.setOutputFormat(QPrinter::PdfFormat);
+ printer.setOutputFileName(fileName());
+ printer.setPageLayout(pageLayout);
+
+ {
+ QPainter painter(&printer);
+ painter.drawText(100, 100, QStringLiteral("Hello Page 1"));
+ printer.newPage();
+ painter.drawText(100, 100, QStringLiteral("Hello Page 2"));
+ }
+ }
+
+ seek(0);
+}
+
+void tst_QPdfDocument::pageCount()
+{
+ TemporaryPdf tempPdf;
+
+ QPdfDocument doc;
+ QSignalSpy pageCountChangedSpy(&doc, SIGNAL(pageCountChanged(int)));
+
+ QCOMPARE(doc.pageCount(), 0);
+ QCOMPARE(doc.load(tempPdf.fileName()), QPdfDocument::NoError);
+ QCOMPARE(doc.pageCount(), 2);
+ QCOMPARE(pageCountChangedSpy.count(), 1);
+ QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount());
+
+ QCOMPARE(doc.pageSize(0).toSize(), tempPdf.pageLayout.fullRectPoints().size());
+}
+
+void tst_QPdfDocument::loadFromIODevice()
+{
+ TemporaryPdf tempPdf;
+ QPdfDocument doc;
+ QSignalSpy statusChangedSpy(&doc, SIGNAL(statusChanged(QPdfDocument::Status)));
+ QSignalSpy pageCountChangedSpy(&doc, SIGNAL(pageCountChanged(int)));
+ doc.load(&tempPdf);
+ QCOMPARE(statusChangedSpy.count(), 2);
+ QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading);
+ QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Ready);
+ QCOMPARE(doc.error(), QPdfDocument::NoError);
+ QCOMPARE(doc.pageCount(), 2);
+ QCOMPARE(pageCountChangedSpy.count(), 1);
+ QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount());
+}
+
+void tst_QPdfDocument::loadAsync()
+{
+ TemporaryPdf tempPdf;
+
+ QNetworkAccessManager nam;
+
+ QUrl url = QUrl::fromLocalFile(tempPdf.fileName());
+ QScopedPointer<QNetworkReply> reply(nam.get(QNetworkRequest(url)));
+
+ QPdfDocument doc;
+ QSignalSpy statusChangedSpy(&doc, SIGNAL(statusChanged(QPdfDocument::Status)));
+ QSignalSpy pageCountChangedSpy(&doc, SIGNAL(pageCountChanged(int)));
+
+ doc.load(reply.data());
+
+ QCOMPARE(statusChangedSpy.count(), 2);
+ QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading);
+ QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Ready);
+ QCOMPARE(doc.pageCount(), 2);
+ QCOMPARE(pageCountChangedSpy.count(), 1);
+ QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount());
+}
+
+void tst_QPdfDocument::password()
+{
+ QPdfDocument doc;
+ QSignalSpy passwordChangedSpy(&doc, SIGNAL(passwordChanged()));
+
+ QCOMPARE(doc.pageCount(), 0);
+ QCOMPARE(doc.load(QFINDTESTDATA("pdf-sample.protected.pdf")), QPdfDocument::IncorrectPasswordError);
+ QCOMPARE(passwordChangedSpy.count(), 0);
+ doc.setPassword(QStringLiteral("WrongPassword"));
+ QCOMPARE(passwordChangedSpy.count(), 1);
+ QCOMPARE(doc.load(QFINDTESTDATA("pdf-sample.protected.pdf")), QPdfDocument::IncorrectPasswordError);
+ QCOMPARE(doc.status(), QPdfDocument::Error);
+ doc.setPassword(QStringLiteral("Qt"));
+ QCOMPARE(passwordChangedSpy.count(), 2);
+ QCOMPARE(doc.load(QFINDTESTDATA("pdf-sample.protected.pdf")), QPdfDocument::NoError);
+ QCOMPARE(doc.pageCount(), 1);
+}
+
+void tst_QPdfDocument::close()
+{
+ TemporaryPdf tempPdf;
+ QPdfDocument doc;
+
+ QSignalSpy statusChangedSpy(&doc, SIGNAL(statusChanged(QPdfDocument::Status)));
+ QSignalSpy pageCountChangedSpy(&doc, SIGNAL(pageCountChanged(int)));
+
+ doc.load(&tempPdf);
+
+ QCOMPARE(statusChangedSpy.count(), 2);
+ QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading);
+ QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Ready);
+ QCOMPARE(pageCountChangedSpy.count(), 1);
+ QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount());
+
+ statusChangedSpy.clear();
+ pageCountChangedSpy.clear();
+
+ doc.close();
+ QCOMPARE(statusChangedSpy.count(), 2);
+ QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Unloading);
+ QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Null);
+ QCOMPARE(doc.pageCount(), 0);
+ QCOMPARE(pageCountChangedSpy.count(), 1);
+ QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount());
+}
+
+void tst_QPdfDocument::loadAfterClose()
+{
+ TemporaryPdf tempPdf;
+ QPdfDocument doc;
+
+ QSignalSpy statusChangedSpy(&doc, SIGNAL(statusChanged(QPdfDocument::Status)));
+ QSignalSpy pageCountChangedSpy(&doc, SIGNAL(pageCountChanged(int)));
+
+ doc.load(&tempPdf);
+ QCOMPARE(statusChangedSpy.count(), 2);
+ QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading);
+ QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Ready);
+ QCOMPARE(pageCountChangedSpy.count(), 1);
+ QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount());
+ statusChangedSpy.clear();
+ pageCountChangedSpy.clear();
+
+ doc.close();
+ QCOMPARE(statusChangedSpy.count(), 2);
+ QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Unloading);
+ QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Null);
+ QCOMPARE(pageCountChangedSpy.count(), 1);
+ QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount());
+ statusChangedSpy.clear();
+ pageCountChangedSpy.clear();
+
+ doc.load(&tempPdf);
+ QCOMPARE(statusChangedSpy.count(), 2);
+ QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading);
+ QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Ready);
+ QCOMPARE(doc.error(), QPdfDocument::NoError);
+ QCOMPARE(doc.pageCount(), 2);
+ QCOMPARE(pageCountChangedSpy.count(), 1);
+ QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount());
+}
+
+void tst_QPdfDocument::closeOnDestroy()
+{
+ TemporaryPdf tempPdf;
+
+ // deleting an open document should automatically close it
+ {
+ QPdfDocument *doc = new QPdfDocument;
+
+ doc->load(&tempPdf);
+
+ QSignalSpy statusChangedSpy(doc, SIGNAL(statusChanged(QPdfDocument::Status)));
+ QSignalSpy pageCountChangedSpy(doc, SIGNAL(pageCountChanged(int)));
+
+ delete doc;
+
+ QCOMPARE(statusChangedSpy.count(), 2);
+ QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Unloading);
+ QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Null);
+ QCOMPARE(pageCountChangedSpy.count(), 1);
+ QCOMPARE(pageCountChangedSpy[0][0].toInt(), 0);
+ }
+
+ // deleting a closed document should not emit any signal
+ {
+ QPdfDocument *doc = new QPdfDocument;
+ doc->load(&tempPdf);
+ doc->close();
+
+ QSignalSpy statusChangedSpy(doc, SIGNAL(statusChanged(QPdfDocument::Status)));
+ QSignalSpy pageCountChangedSpy(doc, SIGNAL(pageCountChanged(int)));
+
+ delete doc;
+
+ QCOMPARE(statusChangedSpy.count(), 0);
+ QCOMPARE(pageCountChangedSpy.count(), 0);
+ }
+}
+
+void tst_QPdfDocument::status()
+{
+ TemporaryPdf tempPdf;
+
+ QPdfDocument doc;
+ QCOMPARE(doc.status(), QPdfDocument::Null);
+
+ QSignalSpy statusChangedSpy(&doc, SIGNAL(statusChanged(QPdfDocument::Status)));
+
+ // open existing document
+ doc.load(&tempPdf);
+ QCOMPARE(statusChangedSpy.count(), 2);
+ QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading);
+ QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Ready);
+ statusChangedSpy.clear();
+
+ QCOMPARE(doc.status(), QPdfDocument::Ready);
+
+ // close document
+ doc.close();
+
+ QCOMPARE(statusChangedSpy.count(), 2);
+ QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Unloading);
+ QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Null);
+ statusChangedSpy.clear();
+
+ QCOMPARE(doc.status(), QPdfDocument::Null);
+
+ // try to open non-existing document
+ doc.load(QFINDTESTDATA("does-not-exist.pdf"));
+ QCOMPARE(statusChangedSpy.count(), 2);
+ QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading);
+ QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Error);
+ QCOMPARE(doc.status(), QPdfDocument::Error);
+ statusChangedSpy.clear();
+
+ // try to open non-existing document asynchronously
+ QNetworkAccessManager accessManager;
+
+ const QUrl url("http://doesnotexist.qt.io");
+ QScopedPointer<QNetworkReply> reply(accessManager.get(QNetworkRequest(url)));
+
+ doc.load(reply.data());
+
+ QElapsedTimer stopWatch;
+ stopWatch.start();
+ forever {
+ QCoreApplication::instance()->processEvents();
+ if (statusChangedSpy.count() == 2)
+ break;
+ if (stopWatch.elapsed() >= 30000)
+ break;
+ }
+
+ QCOMPARE(statusChangedSpy.count(), 2);
+ QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading);
+ QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Error);
+ statusChangedSpy.clear();
+}
+
+void tst_QPdfDocument::passwordClearedOnClose()
+{
+ TemporaryPdf tempPdf;
+ QPdfDocument doc;
+
+ QSignalSpy passwordChangedSpy(&doc, SIGNAL(passwordChanged()));
+
+ doc.setPassword(QStringLiteral("Qt"));
+ QCOMPARE(passwordChangedSpy.count(), 1);
+ QCOMPARE(doc.load(QFINDTESTDATA("pdf-sample.protected.pdf")), QPdfDocument::NoError);
+ passwordChangedSpy.clear();
+
+ doc.close(); // password is cleared on close
+ QCOMPARE(passwordChangedSpy.count(), 1);
+ passwordChangedSpy.clear();
+
+ doc.load(&tempPdf);
+ doc.close(); // signal is not emitted if password didn't change
+ QCOMPARE(passwordChangedSpy.count(), 0);
+}
+
+void tst_QPdfDocument::metaData()
+{
+ QPdfDocument doc;
+
+ // a closed document does not return any meta data
+ QCOMPARE(doc.metaData(QPdfDocument::Title).toString(), QString());
+ QCOMPARE(doc.metaData(QPdfDocument::Subject).toString(), QString());
+ QCOMPARE(doc.metaData(QPdfDocument::Author).toString(), QString());
+ QCOMPARE(doc.metaData(QPdfDocument::Keywords).toString(), QString());
+ QCOMPARE(doc.metaData(QPdfDocument::Producer).toString(), QString());
+ QCOMPARE(doc.metaData(QPdfDocument::Creator).toString(), QString());
+ QCOMPARE(doc.metaData(QPdfDocument::CreationDate).toDateTime(), QDateTime());
+ QCOMPARE(doc.metaData(QPdfDocument::ModificationDate).toDateTime(), QDateTime());
+
+ QCOMPARE(doc.load(QFINDTESTDATA("pdf-sample.metadata.pdf")), QPdfDocument::NoError);
+
+ // check for proper meta data from sample document
+ QCOMPARE(doc.metaData(QPdfDocument::Title).toString(), QString::fromLatin1("Qt PDF Unit Test Document"));
+ QCOMPARE(doc.metaData(QPdfDocument::Subject).toString(), QString::fromLatin1("A test for meta data access"));
+ QCOMPARE(doc.metaData(QPdfDocument::Author).toString(), QString::fromLatin1("John Doe"));
+ QCOMPARE(doc.metaData(QPdfDocument::Keywords).toString(), QString::fromLatin1("meta data keywords"));
+ QCOMPARE(doc.metaData(QPdfDocument::Producer).toString(), QString::fromLatin1("LibreOffice 5.1"));
+ QCOMPARE(doc.metaData(QPdfDocument::Creator).toString(), QString::fromLatin1("Writer"));
+ QCOMPARE(doc.metaData(QPdfDocument::CreationDate).toDateTime(), QDateTime(QDate(2016, 8, 7), QTime(7, 3, 6), Qt::UTC));
+ QCOMPARE(doc.metaData(QPdfDocument::ModificationDate).toDateTime(), QDateTime(QDate(2016, 8, 8), QTime(8, 3, 6), Qt::UTC));
+}
+
+QTEST_MAIN(tst_QPdfDocument)
+
+#include "tst_qpdfdocument.moc"
+
diff --git a/tests/auto/pdf/qpdfpagenavigation/pdf-sample.pagenavigation.pdf b/tests/auto/pdf/qpdfpagenavigation/pdf-sample.pagenavigation.pdf
new file mode 100644
index 000000000..c4e1aa36e
--- /dev/null
+++ b/tests/auto/pdf/qpdfpagenavigation/pdf-sample.pagenavigation.pdf
Binary files differ
diff --git a/tests/auto/pdf/qpdfpagenavigation/qpdfpagenavigation.pro b/tests/auto/pdf/qpdfpagenavigation/qpdfpagenavigation.pro
new file mode 100644
index 000000000..8de99543f
--- /dev/null
+++ b/tests/auto/pdf/qpdfpagenavigation/qpdfpagenavigation.pro
@@ -0,0 +1,5 @@
+CONFIG += testcase
+TARGET = tst_qpdfpagenavigation
+QT += pdf testlib network
+macos:CONFIG -= app_bundle
+SOURCES += tst_qpdfpagenavigation.cpp
diff --git a/tests/auto/pdf/qpdfpagenavigation/tst_qpdfpagenavigation.cpp b/tests/auto/pdf/qpdfpagenavigation/tst_qpdfpagenavigation.cpp
new file mode 100644
index 000000000..ff6a02750
--- /dev/null
+++ b/tests/auto/pdf/qpdfpagenavigation/tst_qpdfpagenavigation.cpp
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <QPdfDocument>
+#include <QPdfPageNavigation>
+
+class tst_QPdfPageNavigation: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void defaultValues();
+ void setEmptyDocument();
+ void setEmptyDocumentAndLoad();
+ void setLoadedDocument();
+ void unloadDocument();
+ void navigate();
+};
+
+void tst_QPdfPageNavigation::defaultValues()
+{
+ QPdfPageNavigation pageNavigation;
+
+ QCOMPARE(pageNavigation.document(), nullptr);
+ QCOMPARE(pageNavigation.currentPage(), 0);
+ QCOMPARE(pageNavigation.pageCount(), 0);
+ QCOMPARE(pageNavigation.canGoToPreviousPage(), false);
+ QCOMPARE(pageNavigation.canGoToNextPage(), false);
+}
+
+void tst_QPdfPageNavigation::setEmptyDocument()
+{
+ QPdfDocument document;
+ QPdfPageNavigation pageNavigation;
+
+ pageNavigation.setDocument(&document);
+
+ QCOMPARE(pageNavigation.document(), &document);
+ QCOMPARE(pageNavigation.currentPage(), 0);
+ QCOMPARE(pageNavigation.pageCount(), 0);
+ QCOMPARE(pageNavigation.canGoToPreviousPage(), false);
+ QCOMPARE(pageNavigation.canGoToNextPage(), false);
+}
+
+void tst_QPdfPageNavigation::setEmptyDocumentAndLoad()
+{
+ QPdfDocument document;
+ QPdfPageNavigation pageNavigation;
+
+ pageNavigation.setDocument(&document);
+
+ QSignalSpy currentPageChangedSpy(&pageNavigation, &QPdfPageNavigation::currentPageChanged);
+ QSignalSpy pageCountChangedSpy(&pageNavigation, &QPdfPageNavigation::pageCountChanged);
+ QSignalSpy canGoToPreviousPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToPreviousPageChanged);
+ QSignalSpy canGoToNextPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToNextPageChanged);
+
+ QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagenavigation.pdf")), QPdfDocument::NoError);
+
+ QCOMPARE(currentPageChangedSpy.count(), 0); // current page stays '0'
+ QCOMPARE(pageCountChangedSpy.count(), 1);
+ QCOMPARE(pageCountChangedSpy[0][0].toInt(), 3);
+ QCOMPARE(canGoToPreviousPageChangedSpy.count(), 0); // still no previous page available
+ QCOMPARE(canGoToNextPageChangedSpy.count(), 1);
+ QCOMPARE(canGoToNextPageChangedSpy[0][0].toBool(), true);
+}
+
+void tst_QPdfPageNavigation::setLoadedDocument()
+{
+ QPdfDocument document;
+ QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagenavigation.pdf")), QPdfDocument::NoError);
+
+ QPdfPageNavigation pageNavigation;
+
+ QSignalSpy currentPageChangedSpy(&pageNavigation, &QPdfPageNavigation::currentPageChanged);
+ QSignalSpy pageCountChangedSpy(&pageNavigation, &QPdfPageNavigation::pageCountChanged);
+ QSignalSpy canGoToPreviousPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToPreviousPageChanged);
+ QSignalSpy canGoToNextPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToNextPageChanged);
+
+ pageNavigation.setDocument(&document);
+
+ QCOMPARE(currentPageChangedSpy.count(), 0); // current page stays '0'
+ QCOMPARE(pageCountChangedSpy.count(), 1);
+ QCOMPARE(pageCountChangedSpy[0][0].toInt(), 3);
+ QCOMPARE(canGoToPreviousPageChangedSpy.count(), 0); // still no previous page available
+ QCOMPARE(canGoToNextPageChangedSpy.count(), 1);
+ QCOMPARE(canGoToNextPageChangedSpy[0][0].toBool(), true);
+}
+
+void tst_QPdfPageNavigation::unloadDocument()
+{
+ QPdfDocument document;
+ QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagenavigation.pdf")), QPdfDocument::NoError);
+
+ QPdfPageNavigation pageNavigation;
+ pageNavigation.setDocument(&document);
+
+ QSignalSpy currentPageChangedSpy(&pageNavigation, &QPdfPageNavigation::currentPageChanged);
+ QSignalSpy pageCountChangedSpy(&pageNavigation, &QPdfPageNavigation::pageCountChanged);
+ QSignalSpy canGoToPreviousPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToPreviousPageChanged);
+ QSignalSpy canGoToNextPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToNextPageChanged);
+
+ document.close();
+
+ QCOMPARE(currentPageChangedSpy.count(), 0); // current page stays '0'
+ QCOMPARE(pageCountChangedSpy.count(), 1);
+ QCOMPARE(pageCountChangedSpy[0][0].toInt(), 0);
+ QCOMPARE(canGoToPreviousPageChangedSpy.count(), 0); // still no previous page available
+ QCOMPARE(canGoToNextPageChangedSpy.count(), 1);
+ QCOMPARE(canGoToNextPageChangedSpy[0][0].toBool(), false);
+}
+
+void tst_QPdfPageNavigation::navigate()
+{
+ QPdfDocument document;
+ QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagenavigation.pdf")), QPdfDocument::NoError);
+
+ QPdfPageNavigation pageNavigation;
+ pageNavigation.setDocument(&document);
+
+ QSignalSpy currentPageChangedSpy(&pageNavigation, &QPdfPageNavigation::currentPageChanged);
+ QSignalSpy canGoToPreviousPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToPreviousPageChanged);
+ QSignalSpy canGoToNextPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToNextPageChanged);
+
+ QCOMPARE(pageNavigation.currentPage(), 0);
+
+ // try to go to previous page while there is none
+ QCOMPARE(pageNavigation.canGoToPreviousPage(), false);
+ pageNavigation.goToPreviousPage();
+ QCOMPARE(canGoToPreviousPageChangedSpy.count(), 0);
+ QCOMPARE(pageNavigation.currentPage(), 0);
+ QCOMPARE(pageNavigation.canGoToPreviousPage(), false);
+
+ // try to go to next page
+ QCOMPARE(pageNavigation.canGoToNextPage(), true);
+ pageNavigation.goToNextPage();
+ QCOMPARE(canGoToPreviousPageChangedSpy.count(), 1);
+ QCOMPARE(canGoToNextPageChangedSpy.count(), 0);
+ QCOMPARE(currentPageChangedSpy.count(), 1);
+ QCOMPARE(pageNavigation.currentPage(), 1);
+ QCOMPARE(pageNavigation.canGoToPreviousPage(), true);
+
+ currentPageChangedSpy.clear();
+ canGoToPreviousPageChangedSpy.clear();
+ canGoToNextPageChangedSpy.clear();
+
+ // try to go to last page
+ pageNavigation.setCurrentPage(2);
+ QCOMPARE(canGoToPreviousPageChangedSpy.count(), 0);
+ QCOMPARE(canGoToNextPageChangedSpy.count(), 1);
+ QCOMPARE(currentPageChangedSpy.count(), 1);
+ QCOMPARE(pageNavigation.currentPage(), 2);
+ QCOMPARE(pageNavigation.canGoToNextPage(), false);
+
+ // check that invalid requests are ignored
+ pageNavigation.setCurrentPage(-1);
+ QCOMPARE(pageNavigation.currentPage(), 2);
+
+ pageNavigation.setCurrentPage(3);
+ QCOMPARE(pageNavigation.currentPage(), 2);
+}
+
+QTEST_MAIN(tst_QPdfPageNavigation)
+
+#include "tst_qpdfpagenavigation.moc"
diff --git a/tests/auto/pdf/qpdfpagerenderer/pdf-sample.pagerenderer.pdf b/tests/auto/pdf/qpdfpagerenderer/pdf-sample.pagerenderer.pdf
new file mode 100644
index 000000000..c4e1aa36e
--- /dev/null
+++ b/tests/auto/pdf/qpdfpagerenderer/pdf-sample.pagerenderer.pdf
Binary files differ
diff --git a/tests/auto/pdf/qpdfpagerenderer/qpdfpagerenderer.pro b/tests/auto/pdf/qpdfpagerenderer/qpdfpagerenderer.pro
new file mode 100644
index 000000000..9ccb4e82c
--- /dev/null
+++ b/tests/auto/pdf/qpdfpagerenderer/qpdfpagerenderer.pro
@@ -0,0 +1,5 @@
+CONFIG += testcase
+TARGET = tst_qpdfpagerenderer
+QT += pdf testlib network
+macos:CONFIG -= app_bundle
+SOURCES += tst_qpdfpagerenderer.cpp
diff --git a/tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp b/tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp
new file mode 100644
index 000000000..8eaef7c6e
--- /dev/null
+++ b/tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QPdfDocument>
+#include <QPdfPageRenderer>
+
+#include <QtTest/QtTest>
+
+class tst_QPdfPageRenderer: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void defaultValues();
+ void withNoDocument();
+ void withEmptyDocument();
+ void withLoadedDocumentSingleThreaded();
+ void withLoadedDocumentMultiThreaded();
+ void switchingRenderMode();
+};
+
+void tst_QPdfPageRenderer::defaultValues()
+{
+ QPdfPageRenderer pageRenderer;
+
+ QCOMPARE(pageRenderer.document(), nullptr);
+ QCOMPARE(pageRenderer.renderMode(), QPdfPageRenderer::SingleThreadedRenderMode);
+}
+
+void tst_QPdfPageRenderer::withNoDocument()
+{
+ QPdfPageRenderer pageRenderer;
+
+ const QSize imageSize(100, 100);
+ const quint64 requestId = pageRenderer.requestPage(0, imageSize);
+
+ QCOMPARE(requestId, quint64(0));
+}
+
+void tst_QPdfPageRenderer::withEmptyDocument()
+{
+ QPdfDocument document;
+ QPdfPageRenderer pageRenderer;
+
+ pageRenderer.setDocument(&document);
+
+ const QSize imageSize(100, 100);
+ const quint64 requestId = pageRenderer.requestPage(0, imageSize);
+
+ QCOMPARE(requestId, quint64(0));
+}
+
+void tst_QPdfPageRenderer::withLoadedDocumentSingleThreaded()
+{
+ QPdfDocument document;
+ QPdfPageRenderer pageRenderer;
+ pageRenderer.setDocument(&document);
+
+ QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagerenderer.pdf")), QPdfDocument::NoError);
+
+ QSignalSpy pageRenderedSpy(&pageRenderer, &QPdfPageRenderer::pageRendered);
+
+ const QSize imageSize(100, 100);
+ const quint64 requestId = pageRenderer.requestPage(0, imageSize);
+
+ QCOMPARE(requestId, quint64(1));
+ QTRY_COMPARE(pageRenderedSpy.count(), 1);
+ QCOMPARE(pageRenderedSpy[0][0].toInt(), 0);
+ QCOMPARE(pageRenderedSpy[0][1].toSize(), imageSize);
+ QCOMPARE(pageRenderedSpy[0][2].value<QImage>().size(), imageSize);
+ QCOMPARE(pageRenderedSpy[0][4].toULongLong(), requestId);
+}
+
+void tst_QPdfPageRenderer::withLoadedDocumentMultiThreaded()
+{
+ QPdfDocument document;
+
+ QPdfPageRenderer pageRenderer;
+ pageRenderer.setDocument(&document);
+ pageRenderer.setRenderMode(QPdfPageRenderer::MultiThreadedRenderMode);
+
+ QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagerenderer.pdf")), QPdfDocument::NoError);
+
+ QSignalSpy pageRenderedSpy(&pageRenderer, &QPdfPageRenderer::pageRendered);
+
+ const QSize imageSize(100, 100);
+ const quint64 requestId = pageRenderer.requestPage(0, imageSize);
+
+ QCOMPARE(requestId, quint64(1));
+ QTRY_COMPARE(pageRenderedSpy.count(), 1);
+ QCOMPARE(pageRenderedSpy[0][0].toInt(), 0);
+ QCOMPARE(pageRenderedSpy[0][1].toSize(), imageSize);
+ QCOMPARE(pageRenderedSpy[0][2].value<QImage>().size(), imageSize);
+ QCOMPARE(pageRenderedSpy[0][4].toULongLong(), requestId);
+}
+
+void tst_QPdfPageRenderer::switchingRenderMode()
+{
+ QPdfDocument document;
+ QPdfPageRenderer pageRenderer;
+ pageRenderer.setDocument(&document);
+
+ QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagerenderer.pdf")), QPdfDocument::NoError);
+
+ QSignalSpy pageRenderedSpy(&pageRenderer, &QPdfPageRenderer::pageRendered);
+
+ // render single threaded
+ const QSize imageSize(100, 100);
+ const quint64 firstRequestId = pageRenderer.requestPage(0, imageSize);
+
+ QTRY_COMPARE(pageRenderedSpy.count(), 1);
+ QCOMPARE(pageRenderedSpy[0][0].toInt(), 0);
+ QCOMPARE(pageRenderedSpy[0][1].toSize(), imageSize);
+ QCOMPARE(pageRenderedSpy[0][2].value<QImage>().size(), imageSize);
+ QCOMPARE(pageRenderedSpy[0][4].toULongLong(), firstRequestId);
+
+ const QImage image = pageRenderedSpy[0][2].value<QImage>();
+
+ pageRenderedSpy.clear();
+
+ // switch to multi threaded
+ pageRenderer.setRenderMode(QPdfPageRenderer::MultiThreadedRenderMode);
+
+ const quint64 secondRequestId = pageRenderer.requestPage(0, imageSize);
+
+ QVERIFY(firstRequestId != secondRequestId);
+ QTRY_COMPARE(pageRenderedSpy.count(), 1);
+ QCOMPARE(pageRenderedSpy[0][0].toInt(), 0);
+ QCOMPARE(pageRenderedSpy[0][1].toSize(), imageSize);
+ QCOMPARE(pageRenderedSpy[0][2].value<QImage>(), image);
+ QCOMPARE(pageRenderedSpy[0][2].value<QImage>().size(), imageSize);
+ QCOMPARE(pageRenderedSpy[0][4].toULongLong(), secondRequestId);
+
+ pageRenderedSpy.clear();
+
+ // switch back to single threaded
+ pageRenderer.setRenderMode(QPdfPageRenderer::SingleThreadedRenderMode);
+
+ const quint64 thirdRequestId = pageRenderer.requestPage(0, imageSize);
+
+ QTRY_COMPARE(pageRenderedSpy.count(), 1);
+ QCOMPARE(pageRenderedSpy[0][0].toInt(), 0);
+ QCOMPARE(pageRenderedSpy[0][1].toSize(), imageSize);
+ QCOMPARE(pageRenderedSpy[0][2].value<QImage>(), image);
+ QCOMPARE(pageRenderedSpy[0][2].value<QImage>().size(), imageSize);
+ QCOMPARE(pageRenderedSpy[0][4].toULongLong(), thirdRequestId);
+}
+
+QTEST_MAIN(tst_QPdfPageRenderer)
+
+#include "tst_qpdfpagerenderer.moc"
diff --git a/tests/auto/pdf/qpdfsearchmodel/qpdfsearchmodel.pro b/tests/auto/pdf/qpdfsearchmodel/qpdfsearchmodel.pro
new file mode 100644
index 000000000..205fef175
--- /dev/null
+++ b/tests/auto/pdf/qpdfsearchmodel/qpdfsearchmodel.pro
@@ -0,0 +1,5 @@
+CONFIG += testcase
+TARGET = tst_qpdfsearchmodel
+QT += pdf testlib network
+macos:CONFIG -= app_bundle
+SOURCES += tst_qpdfsearchmodel.cpp
diff --git a/tests/auto/pdf/qpdfsearchmodel/test.pdf b/tests/auto/pdf/qpdfsearchmodel/test.pdf
new file mode 100644
index 000000000..a9dc1bc29
--- /dev/null
+++ b/tests/auto/pdf/qpdfsearchmodel/test.pdf
Binary files differ
diff --git a/tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp b/tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp
new file mode 100644
index 000000000..e690bfc11
--- /dev/null
+++ b/tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <QPdfDocument>
+#include <QPdfSearchModel>
+
+class tst_QPdfSearchModel: public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QPdfSearchModel() {}
+
+private slots:
+ void findText();
+};
+
+void tst_QPdfSearchModel::findText()
+{
+ QPdfDocument document;
+ QCOMPARE(document.load(QFINDTESTDATA("test.pdf")), QPdfDocument::NoError);
+
+ QPdfSearchModel model;
+ model.setDocument(&document);
+ QVector<QRectF> matches = model.matches(1, "ai");
+
+ qDebug() << matches;
+ QCOMPARE(matches.count(), 3);
+}
+
+QTEST_MAIN(tst_QPdfSearchModel)
+
+#include "tst_qpdfsearchmodel.moc"
diff --git a/tests/auto/quick/publicapi/tst_publicapi.cpp b/tests/auto/quick/publicapi/tst_publicapi.cpp
index 321972057..aaca7997b 100644
--- a/tests/auto/quick/publicapi/tst_publicapi.cpp
+++ b/tests/auto/quick/publicapi/tst_publicapi.cpp
@@ -300,6 +300,7 @@ static const QStringList expectedAPI = QStringList()
<< "QQuickWebEngineFullScreenRequest.reject() --> void"
<< "QQuickWebEngineFullScreenRequest.toggleOn --> bool"
<< "QQuickWebEngineHistory.backItems --> QQuickWebEngineHistoryListModel*"
+ << "QQuickWebEngineHistory.clear() --> void"
<< "QQuickWebEngineHistory.forwardItems --> QQuickWebEngineHistoryListModel*"
<< "QQuickWebEngineHistory.items --> QQuickWebEngineHistoryListModel*"
<< "QQuickWebEngineJavaScriptDialogRequest.DialogTypeAlert --> DialogType"
@@ -740,6 +741,8 @@ static const QStringList expectedAPI = QStringList()
<< "QQuickWebEngineView.quotaRequested(QWebEngineQuotaRequest) --> void"
<< "QQuickWebEngineView.recentlyAudible --> bool"
<< "QQuickWebEngineView.recentlyAudibleChanged(bool) --> void"
+ << "QQuickWebEngineView.renderProcessPid --> qlonglong"
+ << "QQuickWebEngineView.renderProcessPidChanged(qlonglong) --> void"
<< "QQuickWebEngineView.recommendedState --> LifecycleState"
<< "QQuickWebEngineView.recommendedStateChanged(LifecycleState) --> void"
<< "QQuickWebEngineView.registerProtocolHandlerRequested(QWebEngineRegisterProtocolHandlerRequest) --> void"
diff --git a/tests/auto/quick/qmltests/data/tst_navigationHistory.qml b/tests/auto/quick/qmltests/data/tst_navigationHistory.qml
index 77664e645..6ed232589 100644
--- a/tests/auto/quick/qmltests/data/tst_navigationHistory.qml
+++ b/tests/auto/quick/qmltests/data/tst_navigationHistory.qml
@@ -134,6 +134,13 @@ TestWebEngineView {
compare(forwardItemsList.count, 1)
compare(backItemsList.currentItem.text, Qt.resolvedUrl("test1.html"))
compare(forwardItemsList.currentItem.text, Qt.resolvedUrl("javascript.html"))
+
+ webEngineView.navigationHistory.clear()
+ compare(webEngineView.url, Qt.resolvedUrl("test2.html"))
+ compare(webEngineView.canGoBack, false)
+ compare(webEngineView.canGoForward, false)
+ compare(backItemsList.count, 0)
+ compare(forwardItemsList.count, 0)
}
}
}
diff --git a/tests/auto/quick/qquickwebengineview/BLACKLIST b/tests/auto/quick/qquickwebengineview/BLACKLIST
index d4d5c9844..322c6f561 100644
--- a/tests/auto/quick/qquickwebengineview/BLACKLIST
+++ b/tests/auto/quick/qquickwebengineview/BLACKLIST
@@ -1,2 +1,6 @@
[transparentWebEngineViews]
windows
+
+# until qt5.git is updated with new qtdeclarative
+[focusChild]
+*
diff --git a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp
index 6f9cb9c69..7798f07fc 100644
--- a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp
+++ b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp
@@ -584,7 +584,7 @@ void tst_QQuickWebEngineView::interruptImeTextComposition()
QFETCH(QString, eventType);
if (eventType == "MouseButton") {
QPoint textInputCenter = elementCenter(view, QStringLiteral("input2"));
- QTest::mouseClick(view->window(), Qt::LeftButton, 0, textInputCenter);
+ QTest::mouseClick(view->window(), Qt::LeftButton, {}, textInputCenter);
} else if (eventType == "Touch") {
QPoint textInputCenter = elementCenter(view, QStringLiteral("input2"));
QTouchDevice *touchDevice = QTest::createTouchDevice();
@@ -621,7 +621,7 @@ void tst_QQuickWebEngineView::inputContextQueryInput()
// Set focus on an input field.
QPoint textInputCenter = elementCenter(view, "input1");
- QTest::mouseClick(view->window(), Qt::LeftButton, 0, textInputCenter);
+ QTest::mouseClick(view->window(), Qt::LeftButton, {}, textInputCenter);
QTRY_COMPARE(testContext.infos.count(), 2);
QCOMPARE(evaluateJavaScriptSync(view, "document.activeElement.id").toString(), QStringLiteral("input1"));
foreach (const InputMethodInfo &info, testContext.infos) {
diff --git a/tests/auto/widgets/accessibility/tst_accessibility.cpp b/tests/auto/widgets/accessibility/tst_accessibility.cpp
index 117a9e573..5f472332f 100644
--- a/tests/auto/widgets/accessibility/tst_accessibility.cpp
+++ b/tests/auto/widgets/accessibility/tst_accessibility.cpp
@@ -339,7 +339,7 @@ void tst_Accessibility::roles_data()
QTest::newRow("AX_ROLE_ABBR") << QString("<abbr>a</abbr>") << false << QAccessible::StaticText;
QTest::newRow("AX_ROLE_ALERT") << QString("<div role='alert'>alert</div>") << true << QAccessible::AlertMessage;
QTest::newRow("AX_ROLE_ALERT_DIALOG") << QString("<div role='alertdialog'>alert</div>") << true << QAccessible::AlertMessage;
- //QTest::newRow("AX_ROLE_ANCHOR") << QString("<a>target</a>") << false << QAccessible::Link; // FIXME: The test case might be wrong (see https://codereview.chromium.org/2713193003)
+ QTest::newRow("AX_ROLE_ANCHOR") << QString("<a id='a'>Chapter a</a>") << false << QAccessible::Link;
QTest::newRow("AX_ROLE_ANNOTATION") << QString("<rt>a</rt>") << false << QAccessible::StaticText;
QTest::newRow("AX_ROLE_APPLICATION") << QString("<div role='application'>landmark</div>") << true << QAccessible::Document;
QTest::newRow("AX_ROLE_ARTICLE") << QString("<article>a</article>") << true << QAccessible::Section;
diff --git a/tests/auto/widgets/origins/BLACKLIST b/tests/auto/widgets/origins/BLACKLIST
new file mode 100644
index 000000000..db858f11e
--- /dev/null
+++ b/tests/auto/widgets/origins/BLACKLIST
@@ -0,0 +1,3 @@
+# QTBUG-81556
+[mixedXHR]
+*
diff --git a/tests/auto/widgets/origins/tst_origins.cpp b/tests/auto/widgets/origins/tst_origins.cpp
index c63f4d690..e3927f763 100644
--- a/tests/auto/widgets/origins/tst_origins.cpp
+++ b/tests/auto/widgets/origins/tst_origins.cpp
@@ -481,6 +481,8 @@ void tst_Origins::subdirWithoutAccess()
{
ScopedAttribute sa(m_page->settings(), QWebEngineSettings::LocalContentCanAccessFileUrls, false);
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
QVERIFY(verifyLoad(QSL("file:" THIS_DIR "resources/subdir/index.html")));
QCOMPARE(eval(QSL("msg[0]")), QVariant());
QCOMPARE(eval(QSL("msg[1]")), QVariant());
@@ -507,22 +509,28 @@ void tst_Origins::mixedSchemes()
QVERIFY(verifyLoad(QSL("file:" THIS_DIR "resources/mixedSchemes.html")));
eval(QSL("setIFrameUrl('file:" THIS_DIR "resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess")));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('qrc:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('tst:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
QVERIFY(verifyLoad(QSL("qrc:/resources/mixedSchemes.html")));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('file:" THIS_DIR "resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
eval(QSL("setIFrameUrl('qrc:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess")));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('tst:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
QVERIFY(verifyLoad(QSL("tst:/resources/mixedSchemes.html")));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Not allowed to load local resource")));
eval(QSL("setIFrameUrl('file:" THIS_DIR "resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("cannotLoad")));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('qrc:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
eval(QSL("setIFrameUrl('tst:/resources/mixedSchemes_frame.html')"));
@@ -531,36 +539,47 @@ void tst_Origins::mixedSchemes()
QVERIFY(verifyLoad(QSL("PathSyntax:/resources/mixedSchemes.html")));
eval(QSL("setIFrameUrl('PathSyntax:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess")));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Not allowed to load local resource")));
eval(QSL("setIFrameUrl('PathSyntax-Local:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("cannotLoad")));
eval(QSL("setIFrameUrl('PathSyntax-LocalAccessAllowed:/resources/mixedSchemes_frame.html')"));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('PathSyntax-NoAccessAllowed:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
QVERIFY(verifyLoad(QSL("PathSyntax-LocalAccessAllowed:/resources/mixedSchemes.html")));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('PathSyntax:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('PathSyntax-Local:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
eval(QSL("setIFrameUrl('PathSyntax-LocalAccessAllowed:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess")));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('PathSyntax-NoAccessAllowed:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
QVERIFY(verifyLoad(QSL("PathSyntax-NoAccessAllowed:/resources/mixedSchemes.html")));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('PathSyntax:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Not allowed to load local resource")));
eval(QSL("setIFrameUrl('PathSyntax-Local:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("cannotLoad")));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('PathSyntax-LocalAccessAllowed:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('PathSyntax-NoAccessAllowed:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
QVERIFY(verifyLoad(QSL("HostSyntax://a/resources/mixedSchemes.html")));
eval(QSL("setIFrameUrl('HostSyntax://a/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess")));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('HostSyntax://b/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
}
@@ -569,14 +588,17 @@ void tst_Origins::mixedSchemes()
void tst_Origins::mixedSchemesWithCsp()
{
QVERIFY(verifyLoad(QSL("HostSyntax://a/resources/mixedSchemesWithCsp.html")));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("violates the following Content Security Policy")));
eval(QSL("setIFrameUrl('HostSyntax://a/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("violates the following Content Security Policy")));
eval(QSL("setIFrameUrl('HostSyntax://b/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
QVERIFY(verifyLoad(QSL("HostSyntax-ContentSecurityPolicyIgnored://a/resources/mixedSchemesWithCsp.html")));
eval(QSL("setIFrameUrl('HostSyntax-ContentSecurityPolicyIgnored://a/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess")));
+ QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('HostSyntax-ContentSecurityPolicyIgnored://b/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
}
diff --git a/tests/auto/widgets/proxypac/proxypac.pro b/tests/auto/widgets/proxypac/proxypac.pro
index 4dbcd9365..2aacb4366 100644
--- a/tests/auto/widgets/proxypac/proxypac.pro
+++ b/tests/auto/widgets/proxypac/proxypac.pro
@@ -4,8 +4,10 @@ HEADERS += proxyserver.h
SOURCES += proxyserver.cpp
proxy_pac.name = QTWEBENGINE_CHROMIUM_FLAGS
+
+win32:proxy_pac.value = --proxy-pac-url="file:///$$PWD/proxy.pac"
+else:proxy_pac.value = --proxy-pac-url="file://$$PWD/proxy.pac"
boot2qt:proxy_pac.value = "--single-process --no-sandbox --proxy-pac-url=file://$$PWD/proxy.pac"
-else: proxy_pac.value = --proxy-pac-url="file://$$PWD/proxy.pac"
QT_TOOL_ENV += proxy_pac
diff --git a/tests/auto/widgets/qwebenginepage/BLACKLIST b/tests/auto/widgets/qwebenginepage/BLACKLIST
index 2498ed765..3053876d4 100644
--- a/tests/auto/widgets/qwebenginepage/BLACKLIST
+++ b/tests/auto/widgets/qwebenginepage/BLACKLIST
@@ -4,3 +4,13 @@ osx
[mouseMovementProperties]
windows
macos # Can't move cursor (QTBUG-76312)
+
+# QTBUG-81614
+[setHtmlWithBaseURL]
+*
+
+[devTools]
+msvc-2019
+
+[setLifecycleStateWithDevTools]
+msvc-2019
diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
index 94b3f16c1..f500652c7 100644
--- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
+++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
@@ -226,6 +226,7 @@ private Q_SLOTS:
void customUserAgentInNewTab();
void renderProcessCrashed();
+ void renderProcessPid();
private:
static QPoint elementCenter(QWebEnginePage *page, const QString &id);
@@ -980,7 +981,7 @@ void tst_QWebEnginePage::findText()
{
CallbackSpy<bool> callbackSpy;
QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished);
- m_view->findText("", 0, callbackSpy.ref());
+ m_view->findText("", {}, callbackSpy.ref());
QVERIFY(callbackSpy.wasCalled());
QCOMPARE(signalSpy.count(), 1);
QTRY_COMPARE(m_view->selectedText(), QString("foo bar"));
@@ -991,7 +992,7 @@ void tst_QWebEnginePage::findText()
{
CallbackSpy<bool> callbackSpy;
QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished);
- m_view->findText("Will not be found", 0, callbackSpy.ref());
+ m_view->findText("Will not be found", {}, callbackSpy.ref());
QCOMPARE(callbackSpy.waitForResult(), false);
QTRY_COMPARE(signalSpy.count(), 1);
auto result = signalSpy.takeFirst().value(0).value<QWebEngineFindTextResult>();
@@ -1008,7 +1009,7 @@ void tst_QWebEnginePage::findText()
{
CallbackSpy<bool> callbackSpy;
QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished);
- m_view->findText("foo", 0, callbackSpy.ref());
+ m_view->findText("foo", {}, callbackSpy.ref());
QVERIFY(callbackSpy.waitForResult());
QTRY_COMPARE(signalSpy.count(), 1);
QTRY_VERIFY(m_view->selectedText().isEmpty());
@@ -1019,7 +1020,7 @@ void tst_QWebEnginePage::findText()
{
CallbackSpy<bool> callbackSpy;
QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished);
- m_view->findText("", 0, callbackSpy.ref());
+ m_view->findText("", {}, callbackSpy.ref());
QTRY_VERIFY(callbackSpy.wasCalled());
QTRY_COMPARE(signalSpy.count(), 1);
QTRY_COMPARE(m_view->selectedText(), QString("foo"));
@@ -1029,8 +1030,8 @@ void tst_QWebEnginePage::findText()
// should interrupt the first one.
{
QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished);
- m_view->findText("foo", 0);
- m_view->findText("foo", 0);
+ m_view->findText("foo", {});
+ m_view->findText("foo", {});
QTRY_COMPARE(signalSpy.count(), 2);
QTRY_VERIFY(m_view->selectedText().isEmpty());
@@ -1085,11 +1086,11 @@ void tst_QWebEnginePage::findTextSuccessiveShouldCallAllCallbacks()
QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
m_view->setHtml(QString("<html><head></head><body><div>abcdefg abcdefg abcdefg abcdefg abcdefg</div></body></html>"));
QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000);
- m_page->findText("abcde", 0, spy1.ref());
- m_page->findText("abcd", 0, spy2.ref());
- m_page->findText("abc", 0, spy3.ref());
- m_page->findText("ab", 0, spy4.ref());
- m_page->findText("a", 0, spy5.ref());
+ m_page->findText("abcde", {}, spy1.ref());
+ m_page->findText("abcd", {}, spy2.ref());
+ m_page->findText("abc", {}, spy3.ref());
+ m_page->findText("ab", {}, spy4.ref());
+ m_page->findText("a", {}, spy5.ref());
spy5.waitForResult();
QVERIFY(spy1.wasCalled());
QVERIFY(spy2.wasCalled());
@@ -1110,10 +1111,10 @@ void tst_QWebEnginePage::findTextCalledOnMatch()
// CALLBACK
bool callbackCalled = false;
- m_view->page()->findText("foo", 0, [this, &callbackCalled](bool found) {
+ m_view->page()->findText("foo", {}, [this, &callbackCalled](bool found) {
QVERIFY(found);
- m_view->page()->findText("bar", 0, [&callbackCalled](bool found) {
+ m_view->page()->findText("bar", {}, [&callbackCalled](bool found) {
QVERIFY(found);
callbackCalled = true;
});
@@ -1147,7 +1148,7 @@ void tst_QWebEnginePage::findTextActiveMatchOrdinal()
// Iterate over all "foo" matches.
for (int i = 1; i <= 3; ++i) {
- m_view->page()->findText("foo", 0);
+ m_view->page()->findText("foo", {});
QTRY_COMPARE(findTextSpy.count(), 1);
result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>();
QCOMPARE(result.numberOfMatches(), 3);
@@ -1155,7 +1156,7 @@ void tst_QWebEnginePage::findTextActiveMatchOrdinal()
}
// The last match is followed by the fist one.
- m_view->page()->findText("foo", 0);
+ m_view->page()->findText("foo", {});
QTRY_COMPARE(findTextSpy.count(), 1);
result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>();
QCOMPARE(result.numberOfMatches(), 3);
@@ -1169,14 +1170,14 @@ void tst_QWebEnginePage::findTextActiveMatchOrdinal()
QCOMPARE(result.activeMatch(), 3);
// Finding another word resets the activeMatch.
- m_view->page()->findText("bar", 0);
+ m_view->page()->findText("bar", {});
QTRY_COMPARE(findTextSpy.count(), 1);
result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>();
QCOMPARE(result.numberOfMatches(), 2);
QCOMPARE(result.activeMatch(), 1);
// If no match activeMatch is 0.
- m_view->page()->findText("bla", 0);
+ m_view->page()->findText("bla", {});
QTRY_COMPARE(findTextSpy.count(), 1);
result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>();
QCOMPARE(result.numberOfMatches(), 0);
@@ -2024,7 +2025,7 @@ private Q_SLOTS:
void continueError()
{
- emit error(this->error());
+ emit error(this->networkError());
emit finished();
}
};
@@ -3290,7 +3291,7 @@ void tst_QWebEnginePage::dataURLFragment()
QTRY_COMPARE(loadFinishedSpy.count(), 1);
QSignalSpy urlChangedSpy(m_page, SIGNAL(urlChanged(QUrl)));
- QTest::mouseClick(m_view->focusProxy(), Qt::LeftButton, 0, elementCenter(m_page, "link"));
+ QTest::mouseClick(m_view->focusProxy(), Qt::LeftButton, {}, elementCenter(m_page, "link"));
QVERIFY(urlChangedSpy.wait());
QCOMPARE(m_page->url().fragment(), QStringLiteral("anchor"));
@@ -3300,7 +3301,7 @@ void tst_QWebEnginePage::dataURLFragment()
"</body></html>", QUrl("http://test.qt.io/mytest.html"));
QTRY_COMPARE(loadFinishedSpy.count(), 2);
- QTest::mouseClick(m_view->focusProxy(), Qt::LeftButton, 0, elementCenter(m_page, "link"));
+ QTest::mouseClick(m_view->focusProxy(), Qt::LeftButton, {}, elementCenter(m_page, "link"));
QVERIFY(urlChangedSpy.wait());
QCOMPARE(m_page->url(), QUrl("http://test.qt.io/mytest.html#anchor"));
}
@@ -3322,7 +3323,7 @@ void tst_QWebEnginePage::devTools()
QCOMPARE(devToolsPage.devToolsPage(), nullptr);
QCOMPARE(devToolsPage.inspectedPage(), &inspectedPage1);
- QTRY_COMPARE(spy.count(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 30000);
QVERIFY(spy.takeFirst().value(0).toBool());
devToolsPage.setInspectedPage(&inspectedPage2);
@@ -3334,7 +3335,7 @@ void tst_QWebEnginePage::devTools()
QCOMPARE(devToolsPage.devToolsPage(), nullptr);
QCOMPARE(devToolsPage.inspectedPage(), &inspectedPage2);
- QTRY_COMPARE(spy.count(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 30000);
QVERIFY(spy.takeFirst().value(0).toBool());
devToolsPage.setInspectedPage(nullptr);
@@ -3370,7 +3371,7 @@ void tst_QWebEnginePage::openLinkInDifferentProfile()
QTRY_COMPARE(spy1.count(), 1);
QVERIFY(spy1.takeFirst().value(0).toBool());
page1.targetPage = &page2;
- QTest::mouseClick(view.focusProxy(), Qt::MiddleButton, 0, elementCenter(&page1, "link"));
+ QTest::mouseClick(view.focusProxy(), Qt::MiddleButton, {}, elementCenter(&page1, "link"));
QTRY_COMPARE(spy2.count(), 1);
QVERIFY(spy2.takeFirst().value(0).toBool());
}
@@ -3814,9 +3815,9 @@ void tst_QWebEnginePage::setLifecycleStateWithDevTools()
// Ensure pages are initialized
inspectedPage.load(QStringLiteral("about:blank"));
devToolsPage.load(QStringLiteral("about:blank"));
- QTRY_COMPARE(inspectedSpy.count(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(inspectedSpy.count(), 1, 30000);
QCOMPARE(inspectedSpy.takeFirst().value(0), QVariant(true));
- QTRY_COMPARE(devToolsSpy.count(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.count(), 1, 30000);
QCOMPARE(devToolsSpy.takeFirst().value(0), QVariant(true));
// Open DevTools with Frozen inspectedPage
@@ -4327,7 +4328,7 @@ void tst_QWebEnginePage::customUserAgentInNewTab()
QTRY_COMPARE(spy.count(), 1);
QVERIFY(spy.takeFirst().value(0).toBool());
QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("navigator.userAgent")).toString(), profile1.httpUserAgent());
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, elementCenter(&page, "link"));
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, elementCenter(&page, "link"));
QTRY_VERIFY(page.newPage);
QTRY_VERIFY(!lastUserAgent.isEmpty());
QCOMPARE(lastUserAgent, profile1.httpUserAgent().toUtf8());
@@ -4342,7 +4343,7 @@ void tst_QWebEnginePage::customUserAgentInNewTab()
QString("'>link</a></body></html>"));
QTRY_COMPARE(spy.count(), 1);
QVERIFY(spy.takeFirst().value(0).toBool());
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, elementCenter(&page, "link"));
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, elementCenter(&page, "link"));
QTRY_VERIFY(page.newPage);
QTRY_VERIFY(!lastUserAgent.isEmpty());
QCOMPARE(lastUserAgent, profile2.httpUserAgent().toUtf8());
@@ -4368,6 +4369,24 @@ void tst_QWebEnginePage::renderProcessCrashed()
status == QWebEnginePage::AbnormalTerminationStatus);
}
+void tst_QWebEnginePage::renderProcessPid()
+{
+ QCOMPARE(m_page->renderProcessPid(), 0);
+
+ m_page->load(QUrl("about:blank"));
+ QSignalSpy spyFinished(m_page, &QWebEnginePage::loadFinished);
+ QVERIFY(spyFinished.wait());
+
+ QVERIFY(m_page->renderProcessPid() > 1);
+
+ bool crashed = false;
+ connect(m_page, &QWebEnginePage::renderProcessTerminated, [&]() { crashed = true; });
+ m_page->load(QUrl("chrome://crash"));
+ QTRY_VERIFY_WITH_TIMEOUT(crashed, 20000);
+
+ QCOMPARE(m_page->renderProcessPid(), 0);
+}
+
static QByteArrayList params = {QByteArrayLiteral("--use-fake-device-for-media-stream")};
W_QTEST_MAIN(tst_QWebEnginePage, params)
diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
index 128cf7ab9..1f8162941 100644
--- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
+++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
@@ -71,6 +71,7 @@ private Q_SLOTS:
void urlSchemeHandlerInstallation();
void urlSchemeHandlerXhrStatus();
void urlSchemeHandlerScriptModule();
+ void urlSchemeHandlerLongReply();
void customUserAgent();
void httpAcceptLanguage();
void downloadItem();
@@ -172,7 +173,6 @@ public:
~AutoDir() { removeRecursively(); }
};
-
qint64 totalSize(QDir dir)
{
qint64 sum = 0;
@@ -355,7 +355,7 @@ protected:
memcpy(data, m_data.constData() + m_bytesRead, len);
m_bytesAvailable -= len;
m_bytesRead += len;
- } else if (m_data.size() > 0)
+ } else if (atEnd())
return -1;
return len;
@@ -760,6 +760,31 @@ void tst_QWebEngineProfile::urlSchemeHandlerScriptModule()
QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("test")).toString(), QStringLiteral("SUCCESS"));
}
+class LongReplyUrlSchemeHandler : public QWebEngineUrlSchemeHandler
+{
+public:
+ LongReplyUrlSchemeHandler(QObject *parent = nullptr) : QWebEngineUrlSchemeHandler(parent) {}
+ ~LongReplyUrlSchemeHandler() {}
+
+ void requestStarted(QWebEngineUrlRequestJob *job)
+ {
+ QBuffer *buffer = new QBuffer(job);
+ buffer->setData(QByteArray(128 * 1024, ' ') +
+ "<html><head><title>Minify this!</title></head></html>");
+ job->reply("text/html", buffer);
+ }
+};
+
+void tst_QWebEngineProfile::urlSchemeHandlerLongReply()
+{
+ LongReplyUrlSchemeHandler handler;
+ QWebEngineProfile profile;
+ profile.installUrlSchemeHandler("aviancarrier", &handler);
+ QWebEnginePage page(&profile);
+ page.load(QUrl("aviancarrier:/"));
+ QTRY_COMPARE(page.title(), QString("Minify this!"));
+}
+
void tst_QWebEngineProfile::customUserAgent()
{
QString defaultUserAgent = QWebEngineProfile::defaultProfile()->httpUserAgent();
@@ -965,26 +990,32 @@ void tst_QWebEngineProfile::initiator()
InitiatorSpy handler;
QWebEngineProfile profile;
profile.installUrlSchemeHandler("foo", &handler);
- QWebEnginePage page(&profile);
+ QWebEnginePage page(&profile, nullptr);
QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool)));
+ page.load(QUrl("about:blank"));
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
+ loadFinishedSpy.clear();
// about:blank has a unique origin, so initiator should be QUrl("null")
evaluateJavaScriptSync(&page, "window.location = 'foo:bar'");
- QVERIFY(loadFinishedSpy.wait());
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
+ loadFinishedSpy.clear();
QCOMPARE(handler.initiator, QUrl("null"));
page.setHtml("", QUrl("http://test:123/foo%20bar"));
- QVERIFY(loadFinishedSpy.wait());
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
+ loadFinishedSpy.clear();
// baseUrl determines the origin, so QUrl("http://test:123")
evaluateJavaScriptSync(&page, "window.location = 'foo:bar'");
- QVERIFY(loadFinishedSpy.wait());
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
+ loadFinishedSpy.clear();
QCOMPARE(handler.initiator, QUrl("http://test:123"));
// Directly calling load/setUrl should have initiator QUrl(), meaning
// browser-initiated, trusted.
page.load(QUrl("foo:bar"));
- QVERIFY(loadFinishedSpy.wait());
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
QCOMPARE(handler.initiator, QUrl());
}
diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
index 2862be1dd..b733e509c 100644
--- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
+++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
@@ -538,7 +538,7 @@ void tst_QWebEngineView::focusInputTypes()
// 'text' field
QPoint textInputCenter = elementCenter(webView.page(), "textInput");
- QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, 0, textInputCenter);
+ QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, {}, textInputCenter);
QTRY_COMPARE(evaluateJavaScriptSync(webView.page(), "document.activeElement.id").toString(), QStringLiteral("textInput"));
VERIFY_INPUTMETHOD_HINTS(webView.focusProxy()->inputMethodHints(), Qt::ImhPreferLowercase);
QVERIFY(webView.focusProxy()->testAttribute(Qt::WA_InputMethodEnabled));
@@ -546,7 +546,7 @@ void tst_QWebEngineView::focusInputTypes()
// 'password' field
QPoint passwordInputCenter = elementCenter(webView.page(), "passwordInput");
- QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, 0, passwordInputCenter);
+ QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, {}, passwordInputCenter);
QTRY_COMPARE(evaluateJavaScriptSync(webView.page(), "document.activeElement.id").toString(), QStringLiteral("passwordInput"));
VERIFY_INPUTMETHOD_HINTS(webView.focusProxy()->inputMethodHints(), (Qt::ImhSensitiveData | Qt::ImhNoPredictiveText | Qt::ImhNoAutoUppercase | Qt::ImhHiddenText));
QVERIFY(!webView.focusProxy()->testAttribute(Qt::WA_InputMethodEnabled));
@@ -554,7 +554,7 @@ void tst_QWebEngineView::focusInputTypes()
// 'tel' field
QPoint telInputCenter = elementCenter(webView.page(), "telInput");
- QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, 0, telInputCenter);
+ QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, {}, telInputCenter);
QTRY_COMPARE(evaluateJavaScriptSync(webView.page(), "document.activeElement.id").toString(), QStringLiteral("telInput"));
VERIFY_INPUTMETHOD_HINTS(webView.focusProxy()->inputMethodHints(), Qt::ImhDialableCharactersOnly);
QVERIFY(webView.focusProxy()->testAttribute(Qt::WA_InputMethodEnabled));
@@ -562,7 +562,7 @@ void tst_QWebEngineView::focusInputTypes()
// 'number' field
QPoint numberInputCenter = elementCenter(webView.page(), "numberInput");
- QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, 0, numberInputCenter);
+ QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, {}, numberInputCenter);
QTRY_COMPARE(evaluateJavaScriptSync(webView.page(), "document.activeElement.id").toString(), QStringLiteral("numberInput"));
VERIFY_INPUTMETHOD_HINTS(webView.focusProxy()->inputMethodHints(), Qt::ImhFormattedNumbersOnly);
QVERIFY(webView.focusProxy()->testAttribute(Qt::WA_InputMethodEnabled));
@@ -570,7 +570,7 @@ void tst_QWebEngineView::focusInputTypes()
// 'email' field
QPoint emailInputCenter = elementCenter(webView.page(), "emailInput");
- QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, 0, emailInputCenter);
+ QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, {}, emailInputCenter);
QTRY_COMPARE(evaluateJavaScriptSync(webView.page(), "document.activeElement.id").toString(), QStringLiteral("emailInput"));
VERIFY_INPUTMETHOD_HINTS(webView.focusProxy()->inputMethodHints(), Qt::ImhEmailCharactersOnly);
QVERIFY(webView.focusProxy()->testAttribute(Qt::WA_InputMethodEnabled));
@@ -578,28 +578,28 @@ void tst_QWebEngineView::focusInputTypes()
// 'url' field
QPoint urlInputCenter = elementCenter(webView.page(), "urlInput");
- QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, 0, urlInputCenter);
+ QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, {}, urlInputCenter);
QTRY_COMPARE(evaluateJavaScriptSync(webView.page(), "document.activeElement.id").toString(), QStringLiteral("urlInput"));
VERIFY_INPUTMETHOD_HINTS(webView.focusProxy()->inputMethodHints(), (Qt::ImhUrlCharactersOnly | Qt::ImhNoPredictiveText | Qt::ImhNoAutoUppercase));
QVERIFY(webView.focusProxy()->testAttribute(Qt::WA_InputMethodEnabled));
QTRY_VERIFY(inputMethodQuery(Qt::ImEnabled).toBool());
// 'password' field
- QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, 0, passwordInputCenter);
+ QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, {}, passwordInputCenter);
QTRY_COMPARE(evaluateJavaScriptSync(webView.page(), "document.activeElement.id").toString(), QStringLiteral("passwordInput"));
VERIFY_INPUTMETHOD_HINTS(webView.focusProxy()->inputMethodHints(), (Qt::ImhSensitiveData | Qt::ImhNoPredictiveText | Qt::ImhNoAutoUppercase | Qt::ImhHiddenText));
QVERIFY(!webView.focusProxy()->testAttribute(Qt::WA_InputMethodEnabled));
QTRY_COMPARE(inputMethodQuery(Qt::ImEnabled).toBool(), imeHasHiddenTextCapability);
// 'text' type
- QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, 0, textInputCenter);
+ QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, {}, textInputCenter);
QTRY_COMPARE(evaluateJavaScriptSync(webView.page(), "document.activeElement.id").toString(), QStringLiteral("textInput"));
VERIFY_INPUTMETHOD_HINTS(webView.focusProxy()->inputMethodHints(), Qt::ImhPreferLowercase);
QVERIFY(webView.focusProxy()->testAttribute(Qt::WA_InputMethodEnabled));
QTRY_VERIFY(inputMethodQuery(Qt::ImEnabled).toBool());
// 'password' field
- QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, 0, passwordInputCenter);
+ QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, {}, passwordInputCenter);
QTRY_COMPARE(evaluateJavaScriptSync(webView.page(), "document.activeElement.id").toString(), QStringLiteral("passwordInput"));
VERIFY_INPUTMETHOD_HINTS(webView.focusProxy()->inputMethodHints(), (Qt::ImhSensitiveData | Qt::ImhNoPredictiveText | Qt::ImhNoAutoUppercase | Qt::ImhHiddenText));
QVERIFY(!webView.focusProxy()->testAttribute(Qt::WA_InputMethodEnabled));
@@ -607,7 +607,7 @@ void tst_QWebEngineView::focusInputTypes()
// 'text area' field
QPoint textAreaCenter = elementCenter(webView.page(), "textArea");
- QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, 0, textAreaCenter);
+ QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, {}, textAreaCenter);
QTRY_COMPARE(evaluateJavaScriptSync(webView.page(), "document.activeElement.id").toString(), QStringLiteral("textArea"));
VERIFY_INPUTMETHOD_HINTS(webView.focusProxy()->inputMethodHints(), (Qt::ImhMultiLine | Qt::ImhPreferLowercase));
QVERIFY(webView.focusProxy()->testAttribute(Qt::WA_InputMethodEnabled));
@@ -696,12 +696,12 @@ void tst_QWebEngineView::horizontalScrollbarTest()
QSignalSpy scrollSpy(view.page(), SIGNAL(scrollPositionChanged(QPointF)));
// Note: The test below assumes that the layout direction is Qt::LeftToRight.
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, QPoint(550, 595));
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, QPoint(550, 595));
scrollSpy.wait();
QVERIFY(view.page()->scrollPosition().x() > 0);
// Note: The test below assumes that the layout direction is Qt::LeftToRight.
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, QPoint(20, 595));
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, QPoint(20, 595));
scrollSpy.wait();
QVERIFY(view.page()->scrollPosition() == QPoint(0, 0));
}
@@ -1472,7 +1472,7 @@ void tst_QWebEngineView::mouseClick()
QVERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty());
textInputCenter = elementCenter(view.page(), "input");
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, textInputCenter);
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter);
QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input"));
QCOMPARE(selectionChangedSpy.count(), 0);
QVERIFY(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString().isEmpty());
@@ -1493,7 +1493,7 @@ void tst_QWebEngineView::mouseClick()
QCOMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input"));
QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QStringLiteral("Company"));
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, textInputCenter);
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter);
QVERIFY(selectionChangedSpy.wait());
QCOMPARE(selectionChangedSpy.count(), 2);
QVERIFY(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString().isEmpty());
@@ -1514,7 +1514,7 @@ void tst_QWebEngineView::mouseClick()
QCOMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input"));
QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QStringLiteral("The Qt Company"));
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, textInputCenter);
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter);
QVERIFY(selectionChangedSpy.wait());
QCOMPARE(selectionChangedSpy.count(), 3);
QVERIFY(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString().isEmpty());
@@ -1991,7 +1991,7 @@ void tst_QWebEngineView::softwareInputPanel()
QVERIFY(loadFinishedSpy.wait());
QPoint textInputCenter = elementCenter(view.page(), "input1");
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, textInputCenter);
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter);
QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input1"));
// This part of the test checks if the SIP (Software Input Panel) is triggered,
@@ -2010,7 +2010,7 @@ void tst_QWebEngineView::softwareInputPanel()
QTRY_VERIFY(!testContext.isInputPanelVisible());
testContext.hideInputPanel();
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, textInputCenter);
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter);
QTRY_VERIFY(testContext.isInputPanelVisible());
view.setHtml("<html><body><p id='para'>nothing to input here</p></body></html>");
@@ -2018,7 +2018,7 @@ void tst_QWebEngineView::softwareInputPanel()
testContext.hideInputPanel();
QPoint paraCenter = elementCenter(view.page(), "para");
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, paraCenter);
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, paraCenter);
QVERIFY(!testContext.isInputPanelVisible());
@@ -2030,7 +2030,7 @@ void tst_QWebEngineView::softwareInputPanel()
QVERIFY(loadFinishedSpy.wait());
QPoint btnDivCenter = elementCenter(view.page(), "btnDiv");
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, btnDivCenter);
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, btnDivCenter);
QVERIFY(!testContext.isInputPanelVisible());
}
@@ -2055,7 +2055,7 @@ void tst_QWebEngineView::inputContextQueryInput()
// Set focus on an input field.
QPoint textInputCenter = elementCenter(view.page(), "input1");
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, textInputCenter);
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter);
QTRY_COMPARE(testContext.infos.count(), 2);
QCOMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input1"));
foreach (const InputMethodInfo &info, testContext.infos) {
@@ -2204,7 +2204,7 @@ void tst_QWebEngineView::inputMethods()
QTRY_COMPARE(loadFinishedSpy.size(), 1);
QPoint textInputCenter = elementCenter(view.page(), "input1");
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, textInputCenter);
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter);
QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input1"));
// ImCursorRectangle
@@ -2305,7 +2305,7 @@ void tst_QWebEngineView::textSelectionInInputField()
// LEFT to RIGHT selection
// Mouse click event moves the current cursor to the end of the text
QPoint textInputCenter = elementCenter(view.page(), "input1");
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, textInputCenter);
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter);
QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input1"));
QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 11);
QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 11);
@@ -2347,7 +2347,7 @@ void tst_QWebEngineView::textSelectionInInputField()
// RIGHT to LEFT selection
// Deselect the selection (this moves the current cursor to the end of the text)
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, textInputCenter);
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter);
QVERIFY(selectionChangedSpy.wait());
QCOMPARE(selectionChangedSpy.count(), 2);
@@ -2387,7 +2387,7 @@ void tst_QWebEngineView::textSelectionOutOfInputField()
QVERIFY(view.page()->selectedText().isEmpty());
// Simple click should not update text selection, however it updates selection bounds in Chromium
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, view.geometry().center());
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, view.geometry().center());
QCOMPARE(selectionChangedSpy.count(), 0);
QVERIFY(!view.hasSelection());
QVERIFY(view.page()->selectedText().isEmpty());
@@ -2400,7 +2400,7 @@ void tst_QWebEngineView::textSelectionOutOfInputField()
QCOMPARE(view.page()->selectedText(), QString("This is a text"));
// Deselect text by mouse click
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, view.geometry().center());
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, view.geometry().center());
QVERIFY(selectionChangedSpy.wait());
QCOMPARE(selectionChangedSpy.count(), 2);
QVERIFY(!view.hasSelection());
@@ -2447,7 +2447,7 @@ void tst_QWebEngineView::textSelectionOutOfInputField()
// Remove selection by clicking into an input field
QPoint textInputCenter = elementCenter(view.page(), "input1");
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, textInputCenter);
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter);
QVERIFY(selectionChangedSpy.wait());
QCOMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input1"));
QCOMPARE(selectionChangedSpy.count(), 2);
@@ -2462,7 +2462,7 @@ void tst_QWebEngineView::textSelectionOutOfInputField()
QCOMPARE(view.page()->selectedText(), QString("QtWebEngine"));
// Deselect input field's text by mouse click
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, view.geometry().center());
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, view.geometry().center());
QVERIFY(selectionChangedSpy.wait());
QCOMPARE(selectionChangedSpy.count(), 4);
QVERIFY(!view.hasSelection());
@@ -2483,14 +2483,14 @@ void tst_QWebEngineView::hiddenText()
QVERIFY(loadFinishedSpy.wait());
QPoint passwordInputCenter = elementCenter(view.page(), "password1");
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, passwordInputCenter);
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, passwordInputCenter);
QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("password1"));
QVERIFY(!view.focusProxy()->testAttribute(Qt::WA_InputMethodEnabled));
QVERIFY(view.focusProxy()->inputMethodHints() & Qt::ImhHiddenText);
QPoint textInputCenter = elementCenter(view.page(), "input1");
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, textInputCenter);
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter);
QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input1"));
QVERIFY(!(view.focusProxy()->inputMethodHints() & Qt::ImhHiddenText));
}
@@ -3108,7 +3108,7 @@ void tst_QWebEngineView::globalMouseSelection()
// Deselect the selection (this moves the current cursor to the end of the text)
QPoint textInputCenter = elementCenter(view.page(), "input1");
- QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, textInputCenter);
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter);
QVERIFY(selectionChangedSpy.wait());
QCOMPARE(selectionChangedSpy.count(), 2);
QVERIFY(QApplication::clipboard()->text(QClipboard::Selection).isEmpty());
@@ -3149,36 +3149,55 @@ void tst_QWebEngineView::noContextMenu()
void tst_QWebEngineView::contextMenu_data()
{
QTest::addColumn<int>("childrenCount");
+ QTest::addColumn<bool>("isCustomMenu");
QTest::addColumn<Qt::ContextMenuPolicy>("contextMenuPolicy");
- QTest::newRow("defaultContextMenu") << 1 << Qt::DefaultContextMenu;
- QTest::newRow("customContextMenu") << 1 << Qt::CustomContextMenu;
- QTest::newRow("preventContextMenu") << 0 << Qt::PreventContextMenu;
+ QTest::newRow("defaultContextMenu") << 1 << false << Qt::DefaultContextMenu;
+ QTest::newRow("customContextMenu") << 1 << true << Qt::CustomContextMenu;
+ QTest::newRow("preventContextMenu") << 0 << false << Qt::PreventContextMenu;
}
void tst_QWebEngineView::contextMenu()
{
QFETCH(int, childrenCount);
+ QFETCH(bool, isCustomMenu);
QFETCH(Qt::ContextMenuPolicy, contextMenuPolicy);
QWebEngineView view;
+ QMenu *customMenu = nullptr;
if (contextMenuPolicy == Qt::CustomContextMenu) {
- connect(&view, &QWebEngineView::customContextMenuRequested, [&view](const QPoint &pt) {
- QMenu* menu = new QMenu(&view);
- menu->addAction("Action1");
- menu->addAction("Action2");
- menu->popup(pt);
+ connect(&view, &QWebEngineView::customContextMenuRequested, [&view, &customMenu] (const QPoint &pt) {
+ Q_ASSERT(!customMenu);
+ customMenu = new QMenu(&view);
+ customMenu->addAction("Action1");
+ customMenu->addAction("Action2");
+ customMenu->popup(pt);
});
}
view.setContextMenuPolicy(contextMenuPolicy);
+
+ // input is supposed to be skipped before first real navigation in >= 79
+ QSignalSpy loadSpy(&view, &QWebEngineView::loadFinished);
+ view.load(QUrl("about:blank"));
view.resize(640, 480);
view.show();
+ QTRY_COMPARE(loadSpy.count(), 1);
QVERIFY(view.findChildren<QMenu *>().isEmpty());
QTest::mouseMove(view.windowHandle(), QPoint(10,10));
QTest::mouseClick(view.windowHandle(), Qt::RightButton);
- QTRY_COMPARE(view.findChildren<QMenu *>().count(), childrenCount);
+
+ // verify for zero children will always succeed, so should be tested with at least minor timeout
+ if (childrenCount <= 0) {
+ QVERIFY(!QTest::qWaitFor([&view] () { return view.findChildren<QMenu *>().count() > 0; }, 500));
+ } else {
+ QTRY_COMPARE(view.findChildren<QMenu *>().count(), childrenCount);
+ if (isCustomMenu) {
+ QCOMPARE(view.findChildren<QMenu *>().first(), customMenu);
+ }
+ }
+ QCOMPARE(!!customMenu, isCustomMenu);
}
void tst_QWebEngineView::mouseLeave()
diff --git a/tests/auto/widgets/schemes/tst_schemes.cpp b/tests/auto/widgets/schemes/tst_schemes.cpp
index 1b6093571..a4a0e34ff 100644
--- a/tests/auto/widgets/schemes/tst_schemes.cpp
+++ b/tests/auto/widgets/schemes/tst_schemes.cpp
@@ -38,6 +38,7 @@ class tst_Schemes : public QObject
Q_OBJECT
private Q_SLOTS:
+ void unknownUrlSchemePolicy_data();
void unknownUrlSchemePolicy();
};
@@ -58,8 +59,27 @@ public:
}
};
+Q_DECLARE_METATYPE(QWebEngineSettings::UnknownUrlSchemePolicy)
+
+void tst_Schemes::unknownUrlSchemePolicy_data()
+{
+ QTest::addColumn<QWebEngineSettings::UnknownUrlSchemePolicy>("policy");
+ QTest::addColumn<bool>("userAction");
+ QTest::newRow("DisallowUnknownUrlSchemes, script") << QWebEngineSettings::DisallowUnknownUrlSchemes << false;
+ QTest::newRow("DisallowUnknownUrlSchemes, user") << QWebEngineSettings::DisallowUnknownUrlSchemes << true;
+ QTest::newRow("AllowUnknownUrlSchemesFromUserInteraction, script") << QWebEngineSettings::AllowUnknownUrlSchemesFromUserInteraction << false;
+ QTest::newRow("AllowUnknownUrlSchemesFromUserInteraction, user") << QWebEngineSettings::AllowUnknownUrlSchemesFromUserInteraction << true;
+ QTest::newRow("AllowAllUnknownUrlSchemes, script") << QWebEngineSettings::AllowAllUnknownUrlSchemes << false;
+ QTest::newRow("AllowAllUnknownUrlSchemes, user") << QWebEngineSettings::AllowAllUnknownUrlSchemes << true;
+ QTest::newRow("default UnknownUrlSchemePolicy, script") << QWebEngineSettings::UnknownUrlSchemePolicy(0) << false;
+ QTest::newRow("default UnknownUrlSchemePolicy, user") << QWebEngineSettings::UnknownUrlSchemePolicy(0) << true;
+}
+
void tst_Schemes::unknownUrlSchemePolicy()
{
+ QFETCH(QWebEngineSettings::UnknownUrlSchemePolicy, policy);
+ QFETCH(bool, userAction);
+
QWebEngineView view;
AcceptNavigationRequestHandler page;
QSignalSpy loadFinishedSpy(&page, &QWebEnginePage::loadFinished);
@@ -71,41 +91,31 @@ void tst_Schemes::unknownUrlSchemePolicy()
settings->setAttribute(QWebEngineSettings::ErrorPageEnabled, true);
settings->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true);
- QWebEngineSettings::UnknownUrlSchemePolicy policies[6] = {QWebEngineSettings::DisallowUnknownUrlSchemes,
- QWebEngineSettings::DisallowUnknownUrlSchemes,
- QWebEngineSettings::AllowUnknownUrlSchemesFromUserInteraction,
- QWebEngineSettings::AllowUnknownUrlSchemesFromUserInteraction,
- QWebEngineSettings::AllowAllUnknownUrlSchemes,
- QWebEngineSettings::AllowAllUnknownUrlSchemes};
- // even iterations are for navigation-requests from javascript,
- // odd iterations are for navigations-requests from user-interaction
- for (int i = 0; i < 8; i++) {
- if (i <= 5)
- settings->setUnknownUrlSchemePolicy(policies[i]);
- else
- settings->resetUnknownUrlSchemePolicy();
- loadFinishedSpy.clear();
- page.acceptNavigationRequestCalls = 0;
- bool shouldAccept;
-
- if (i % 2 == 0) { // navigation request coming from javascript
- shouldAccept = (4 <= i && i <= 5); // only case AllowAllUnknownUrlSchemes
- view.setHtml("<html><script>setTimeout(function(){ window.location.href='nonexistentscheme://somewhere'; }, 10);</script><body>testing...</body></html>");
- } else { // navigation request coming from user interaction
- shouldAccept = (2 <= i); // all cases except DisallowUnknownUrlSchemes
- view.setHtml("<html><body><a id='nonexlink' href='nonexistentscheme://somewhere'>nonexistentscheme://somewhere</a></body></html>");
- QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 15000);
- // focus and trigger the link
- view.page()->runJavaScript("document.getElementById('nonexlink').focus();", [&view](const QVariant &result) {
- Q_UNUSED(result);
- QTest::sendKeyEvent(QTest::Press, view.focusProxy(), Qt::Key_Return, QString("\r"), Qt::NoModifier);
- QTest::sendKeyEvent(QTest::Release, view.focusProxy(), Qt::Key_Return, QString("\r"), Qt::NoModifier);
- });
- }
+ if (policy > 0)
+ settings->setUnknownUrlSchemePolicy(policy);
+ else
+ settings->resetUnknownUrlSchemePolicy();
+ loadFinishedSpy.clear();
+ page.acceptNavigationRequestCalls = 0;
+ bool shouldAccept;
- QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 2, 60000);
- QCOMPARE(page.acceptNavigationRequestCalls, shouldAccept ? 1 : 0);
+ if (!userAction) { // navigation request coming from javascript
+ shouldAccept = (policy == QWebEngineSettings::AllowAllUnknownUrlSchemes);
+ view.setHtml("<html><script>setTimeout(function(){ window.location.href='nonexistentscheme://somewhere'; }, 10);</script><body>testing...</body></html>");
+ } else { // navigation request coming from user interaction
+ shouldAccept = (policy != QWebEngineSettings::DisallowUnknownUrlSchemes);
+ view.setHtml("<html><body><a id='nonexlink' href='nonexistentscheme://somewhere'>nonexistentscheme://somewhere</a></body></html>");
+ QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 15000);
+ // focus and trigger the link
+ view.page()->runJavaScript("document.getElementById('nonexlink').focus();", [&view](const QVariant &result) {
+ Q_UNUSED(result);
+ QTest::sendKeyEvent(QTest::Press, view.focusProxy(), Qt::Key_Return, QString("\r"), Qt::NoModifier);
+ QTest::sendKeyEvent(QTest::Release, view.focusProxy(), Qt::Key_Return, QString("\r"), Qt::NoModifier);
+ });
}
+
+ QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 2, 60000);
+ QCOMPARE(page.acceptNavigationRequestCalls, shouldAccept ? 1 : 0);
}
QTEST_MAIN(tst_Schemes)
diff --git a/tests/auto/widgets/spellchecking/tst_spellchecking.cpp b/tests/auto/widgets/spellchecking/tst_spellchecking.cpp
index 64df05d89..801e2a76c 100644
--- a/tests/auto/widgets/spellchecking/tst_spellchecking.cpp
+++ b/tests/auto/widgets/spellchecking/tst_spellchecking.cpp
@@ -41,10 +41,10 @@ public:
void activateMenu(QWidget *widget, const QPoint &position)
{
QTest::mouseMove(widget, position);
- QTest::mousePress(widget, Qt::RightButton, 0, position);
+ QTest::mousePress(widget, Qt::RightButton, {}, position);
QContextMenuEvent evcont(QContextMenuEvent::Mouse, position, mapToGlobal(position));
event(&evcont);
- QTest::mouseRelease(widget, Qt::RightButton, 0, position);
+ QTest::mouseRelease(widget, Qt::RightButton, {}, position);
}
const QWebEngineContextMenuData& data()
@@ -175,8 +175,8 @@ void tst_Spellchecking::spellcheck()
//type text, spellchecker needs time
QTest::mouseMove(m_view->focusWidget(), QPoint(20,20));
- QTest::mousePress(m_view->focusWidget(), Qt::LeftButton, 0, QPoint(20,20));
- QTest::mouseRelease(m_view->focusWidget(), Qt::LeftButton, 0, QPoint(20,20));
+ QTest::mousePress(m_view->focusWidget(), Qt::LeftButton, {}, QPoint(20,20));
+ QTest::mouseRelease(m_view->focusWidget(), Qt::LeftButton, {}, QPoint(20,20));
QString text("I lowe Qt ....");
for (int i = 0; i < text.length(); i++) {
QTest::keyClicks(m_view->focusWidget(), text.at(i));
diff --git a/tests/auto/widgets/util.h b/tests/auto/widgets/util.h
index eba974f33..20241be8b 100644
--- a/tests/auto/widgets/util.h
+++ b/tests/auto/widgets/util.h
@@ -132,7 +132,7 @@ static inline QString toHtmlSync(QWebEnginePage *page)
static inline bool findTextSync(QWebEnginePage *page, const QString &subString)
{
CallbackSpy<bool> spy;
- page->findText(subString, 0, spy.ref());
+ page->findText(subString, {}, spy.ref());
return spy.waitForResult();
}
diff --git a/tests/manual/quick/pdf/listview.qml b/tests/manual/quick/pdf/listview.qml
new file mode 100644
index 000000000..361ae7d89
--- /dev/null
+++ b/tests/manual/quick/pdf/listview.qml
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.14
+import QtQuick.Window 2.14
+import QtQuick.Pdf 5.15
+
+Window {
+ width: 600
+ height: 440
+ color: "lightgrey"
+ title: doc.source
+ visible: true
+
+ PdfDocument {
+ id: doc
+ source: "test.pdf"
+ }
+
+ ListView {
+ id: listView
+ anchors.fill: parent
+ model: doc.pageCount
+ spacing: 6
+ delegate: Column {
+ Rectangle {
+ id: paper
+ width: image.width
+ height: image.height
+ Image {
+ id: image
+ objectName: "PDF page " + index
+ source: doc.source
+ currentFrame: index
+ asynchronous: true
+ }
+ }
+ Text {
+ text: "Page " + (image.currentFrame + 1)
+ }
+ }
+ }
+
+ Text {
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+ text: "page " + Math.max(1, (listView.indexAt(0, listView.contentY) + 1)) + " of " + doc.pageCount
+ }
+}
diff --git a/tests/manual/quick/pdf/pessimizedListView.qml b/tests/manual/quick/pdf/pessimizedListView.qml
new file mode 100644
index 000000000..4ae0edabe
--- /dev/null
+++ b/tests/manual/quick/pdf/pessimizedListView.qml
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Layouts 1.14
+import QtQuick.Pdf 5.15
+import Qt.labs.platform 1.1 as P
+
+ApplicationWindow {
+ width: 900
+ height: 1000
+ color: "lightgrey"
+ title: doc.source + " scale " + imageScale.toFixed(2)
+ visible: true
+ property real imageScale: 1
+
+ header: ToolBar {
+ RowLayout {
+ anchors.fill: parent
+ anchors.rightMargin: 6
+ ToolButton {
+ action: Action {
+ text: "🗁"
+ shortcut: StandardKey.Open
+ onTriggered: fileDialog.open()
+ }
+ }
+ ToolButton {
+ action: Action {
+ text: "⊕"
+ shortcut: StandardKey.ZoomIn
+ onTriggered: imageScale *= Math.sqrt(2)
+ }
+ }
+ ToolButton {
+ action: Action {
+ text: "⊖"
+ shortcut: StandardKey.ZoomOut
+ onTriggered: imageScale /= Math.sqrt(2)
+ }
+ }
+ ToolButton {
+ action: Action {
+ text: "1x"
+ shortcut: "Ctrl+0"
+ onTriggered: imageScale = 1
+ }
+ }
+
+ Label {
+ text: "Pixels/point:"
+ }
+ SpinBox {
+ id: oversamplingSB
+ from: 1; to: 8; value: 4
+ }
+
+ Label {
+ text: "cacheBuffer:"
+ }
+ SpinBox {
+ id: cacheBufferSB
+ from: 0; to: 1000; stepSize: 50; value: 100
+ }
+
+ CheckBox {
+ id: asyncCB
+ text: "async"
+ }
+
+ CheckBox {
+ id: cacheCB
+ text: "cache"
+ }
+ }
+ }
+
+ PdfDocument {
+ id: doc
+ source: "test.pdf"
+ }
+
+ P.FileDialog {
+ id: fileDialog
+ title: "Open a PDF file"
+ nameFilters: [ "PDF files (*.pdf)" ]
+ onAccepted: doc.source = file
+ }
+
+ ListView {
+ id: listView
+ anchors.fill: parent
+ anchors.margins: 10
+ model: doc.pageCount
+ spacing: 6
+ cacheBuffer: cacheBufferSB.value
+ ScrollBar.vertical: ScrollBar { }
+ delegate: Rectangle {
+ id: paper
+ width: Math.max(60, image.width) * imageScale
+ height: 100 * imageScale
+ BusyIndicator {
+ anchors.centerIn: parent
+ running: image.status === Image.Loading
+ }
+ Image {
+ id: image
+ scale: imageScale
+ anchors.centerIn: parent
+ sourceSize.width: doc.pagePointSize(index).width * oversamplingSB.value
+ height: 100
+ fillMode: Image.PreserveAspectFit
+ objectName: "PDF page " + index
+ source: doc.source
+ currentFrame: index
+ asynchronous: asyncCB.checked
+ cache: cacheCB.checked
+ }
+ }
+ }
+
+ Text {
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+ text: "page " + Math.max(1, (listView.indexAt(0, listView.contentY) + 1)) + " of " + doc.pageCount
+ }
+}
diff --git a/tests/manual/quick/pdf/simplest.qml b/tests/manual/quick/pdf/simplest.qml
new file mode 100644
index 000000000..0571493af
--- /dev/null
+++ b/tests/manual/quick/pdf/simplest.qml
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.14
+
+Image {
+ id: image
+ source: "test.pdf"
+ fillMode: Image.PreserveAspectFit
+ Shortcut {
+ sequence: StandardKey.MoveToNextPage
+ enabled: image.currentFrame < image.frameCount - 1
+ onActivated: image.currentFrame++
+ }
+ Shortcut {
+ sequence: StandardKey.MoveToPreviousPage
+ enabled: image.currentFrame > 0
+ onActivated: image.currentFrame--
+ }
+}
diff --git a/tests/manual/quick/pdf/test.pdf b/tests/manual/quick/pdf/test.pdf
new file mode 100644
index 000000000..a9dc1bc29
--- /dev/null
+++ b/tests/manual/quick/pdf/test.pdf
Binary files differ
diff --git a/tests/manual/quick/pdf/withdoc.qml b/tests/manual/quick/pdf/withdoc.qml
new file mode 100644
index 000000000..2d82a6abf
--- /dev/null
+++ b/tests/manual/quick/pdf/withdoc.qml
@@ -0,0 +1,206 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import Qt.labs.platform 1.1 as Platform
+import QtQuick.Pdf 5.15
+import QtQuick.Shapes 1.14
+import QtQuick.Window 2.14
+
+Window {
+ width: 800
+ height: 940
+ color: "lightgrey"
+ title: doc.source
+ visible: true
+
+ PdfDocument {
+ id: doc
+ source: "test.pdf"
+ }
+
+ Platform.FileDialog {
+ id: fileDialog
+ title: "Open a PDF file"
+ nameFilters: [ "PDF files (*.pdf)" ]
+ onAccepted: doc.source = file
+ }
+
+ PdfSelection {
+ id: selection
+ document: doc
+ page: image.currentFrame
+ fromPoint: dragHandler.centroid.pressPosition
+ toPoint: dragHandler.centroid.position
+ hold: !dragHandler.active
+ }
+
+ Column {
+ id: column
+ anchors.fill: parent
+ anchors.margins: 6
+ spacing: 6
+ Text { text: "title: " + doc.title; visible: doc.title !== "" }
+ Text { text: "author: " + doc.author; visible: doc.author !== "" }
+ Text { text: "subject: " + doc.subject; visible: doc.subject !== "" }
+ Text { text: "keywords: " + doc.keywords; visible: doc.keywords !== "" }
+ Text { text: "producer: " + doc.producer; visible: doc.producer !== "" }
+ Text { text: "creator: " + doc.creator; visible: doc.creator !== "" }
+ Text { text: "creationDate: " + doc.creationDate; visible: doc.creationDate !== "" }
+ Text { text: "modificationDate: " + doc.modificationDate; visible: doc.modificationDate !== "" }
+ Text { text: "implicit size: " + image.implicitWidth + "x" + image.implicitHeight }
+ Text { text: "source size: " + image.sourceSize.width + "x" + image.sourceSize.height }
+ Text { text: "painted size: " + image.paintedWidth + "x" + image.paintedHeight }
+
+ Flickable {
+ width: column.width
+ height: width
+ contentWidth: paper.width
+ contentHeight: paper.height
+ z: -1
+ Rectangle {
+ id: paper
+ width: image.width
+ height: image.height
+ Image {
+ id: image
+ source: doc.status === PdfDocument.Ready ? doc.source : ""
+
+ property real zoomFactor: Math.sqrt(2)
+
+ DragHandler {
+ id: dragHandler
+ target: null
+ }
+
+ Shortcut {
+ sequence: StandardKey.MoveToNextPage
+ enabled: image.currentFrame < image.frameCount - 1
+ onActivated: image.currentFrame++
+ }
+ Shortcut {
+ sequence: StandardKey.MoveToPreviousPage
+ enabled: image.currentFrame > 0
+ onActivated: image.currentFrame--
+ }
+ Shortcut {
+ sequence: StandardKey.ZoomIn
+ enabled: image.sourceSize.width < 5000
+ onActivated: {
+ image.sourceSize.width = image.implicitWidth * image.zoomFactor
+ image.sourceSize.height = image.implicitHeight * image.zoomFactor
+ }
+ }
+ Shortcut {
+ sequence: StandardKey.ZoomOut
+ enabled: image.width > 50
+ onActivated: {
+ image.sourceSize.width = image.implicitWidth / image.zoomFactor
+ image.sourceSize.height = image.implicitHeight / image.zoomFactor
+ }
+ }
+ Shortcut {
+ sequence: "Ctrl+0"
+ onActivated: image.sourceSize = undefined
+ }
+ Shortcut {
+ sequence: StandardKey.Open
+ onActivated: fileDialog.open()
+ }
+ Shortcut {
+ sequence: StandardKey.Quit
+ onActivated: Qt.quit()
+ }
+ }
+
+ Shape {
+ anchors.fill: parent
+ opacity: 0.25
+ ShapePath {
+ fillColor: "cyan"
+ PathMultiline {
+ id: selectionBoundaries
+ paths: selection.geometry
+ }
+ }
+ }
+
+ Repeater {
+ model: PdfLinkModel {
+ id: linkModel
+ document: doc
+ page: image.currentFrame
+ }
+ delegate: Rectangle {
+ color: "transparent"
+ border.color: "lightgrey"
+ x: rect.x
+ y: rect.y
+ width: rect.width
+ height: rect.height
+// HoverHandler { cursorShape: Qt.PointingHandCursor } // 5.15 onward (QTBUG-68073)
+ TapHandler {
+ onTapped: {
+ if (page >= 0)
+ image.currentFrame = page
+ else
+ Qt.openUrlExternally(url)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ Text {
+ anchors.bottom: parent.bottom
+ text: "page " + (image.currentFrame + 1) + " of " + doc.pageCount
+ }
+}
diff --git a/tools/scripts/take_snapshot.py b/tools/scripts/take_snapshot.py
index 741979a9e..b7c2c4bd9 100755
--- a/tools/scripts/take_snapshot.py
+++ b/tools/scripts/take_snapshot.py
@@ -179,7 +179,6 @@ def isInChromiumBlacklist(file_path):
or file_path.startswith('third_party/icu/android')
or file_path.startswith('third_party/icu/cast')
or file_path.startswith('third_party/icu/chromeos')
- or file_path.startswith('third_party/icu/ios')
or file_path.startswith('third_party/instrumented_libraries')
or file_path.startswith('third_party/jsr-305')
or file_path.startswith('third_party/junit')